keep history after reset to ToT of master
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d2f212e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# Treat all files in this repo as binary, with no git magic updating
+# line endings. Windows users contributing to Go will need to use a
+# modern version of git and editors capable of LF line endings.
+#
+# We'll prevent accidental CRLF line endings from entering the repo
+# via the git-review gofmt checks.
+#
+# See golang.org/issue/9281
+
+* -text
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8339fd6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+# Add no patterns to .hgignore except for files generated by the build.
+last-change
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..88dff59
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+
+## Filing issues
+
+When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+**We do not accept GitHub pull requests**
+(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
+
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/PATENTS b/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/README b/README
new file mode 100644
index 0000000..6b13d8e
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+This repository holds supplementary Go networking libraries.
+
+To submit changes to this repository, see http://golang.org/doc/contribute.html.
diff --git a/codereview.cfg b/codereview.cfg
new file mode 100644
index 0000000..3f8b14b
--- /dev/null
+++ b/codereview.cfg
@@ -0,0 +1 @@
+issuerepo: golang/go
diff --git a/context/context.go b/context/context.go
new file mode 100644
index 0000000..e7ee376
--- /dev/null
+++ b/context/context.go
@@ -0,0 +1,447 @@
+// Copyright 2014 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.
+
+// Package context defines the Context type, which carries deadlines,
+// cancelation signals, and other request-scoped values across API boundaries
+// and between processes.
+//
+// Incoming requests to a server should create a Context, and outgoing calls to
+// servers should accept a Context. The chain of function calls between must
+// propagate the Context, optionally replacing it with a modified copy created
+// using WithDeadline, WithTimeout, WithCancel, or WithValue.
+//
+// Programs that use Contexts should follow these rules to keep interfaces
+// consistent across packages and enable static analysis tools to check context
+// propagation:
+//
+// Do not store Contexts inside a struct type; instead, pass a Context
+// explicitly to each function that needs it. The Context should be the first
+// parameter, typically named ctx:
+//
+// func DoSomething(ctx context.Context, arg Arg) error {
+// // ... use ctx ...
+// }
+//
+// Do not pass a nil Context, even if a function permits it. Pass context.TODO
+// if you are unsure about which Context to use.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+//
+// The same Context may be passed to functions running in different goroutines;
+// Contexts are safe for simultaneous use by multiple goroutines.
+//
+// See http://blog.golang.org/context for example code for a server that uses
+// Contexts.
+package context // import "golang.org/x/net/context"
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+)
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context interface {
+ // Deadline returns the time when work done on behalf of this context
+ // should be canceled. Deadline returns ok==false when no deadline is
+ // set. Successive calls to Deadline return the same results.
+ Deadline() (deadline time.Time, ok bool)
+
+ // Done returns a channel that's closed when work done on behalf of this
+ // context should be canceled. Done may return nil if this context can
+ // never be canceled. Successive calls to Done return the same value.
+ //
+ // WithCancel arranges for Done to be closed when cancel is called;
+ // WithDeadline arranges for Done to be closed when the deadline
+ // expires; WithTimeout arranges for Done to be closed when the timeout
+ // elapses.
+ //
+ // Done is provided for use in select statements:
+ //
+ // // Stream generates values with DoSomething and sends them to out
+ // // until DoSomething returns an error or ctx.Done is closed.
+ // func Stream(ctx context.Context, out <-chan Value) error {
+ // for {
+ // v, err := DoSomething(ctx)
+ // if err != nil {
+ // return err
+ // }
+ // select {
+ // case <-ctx.Done():
+ // return ctx.Err()
+ // case out <- v:
+ // }
+ // }
+ // }
+ //
+ // See http://blog.golang.org/pipelines for more examples of how to use
+ // a Done channel for cancelation.
+ Done() <-chan struct{}
+
+ // Err returns a non-nil error value after Done is closed. Err returns
+ // Canceled if the context was canceled or DeadlineExceeded if the
+ // context's deadline passed. No other values for Err are defined.
+ // After Done is closed, successive calls to Err return the same value.
+ Err() error
+
+ // Value returns the value associated with this context for key, or nil
+ // if no value is associated with key. Successive calls to Value with
+ // the same key returns the same result.
+ //
+ // Use context values only for request-scoped data that transits
+ // processes and API boundaries, not for passing optional parameters to
+ // functions.
+ //
+ // A key identifies a specific value in a Context. Functions that wish
+ // to store values in Context typically allocate a key in a global
+ // variable then use that key as the argument to context.WithValue and
+ // Context.Value. A key can be any type that supports equality;
+ // packages should define keys as an unexported type to avoid
+ // collisions.
+ //
+ // Packages that define a Context key should provide type-safe accessors
+ // for the values stores using that key:
+ //
+ // // Package user defines a User type that's stored in Contexts.
+ // package user
+ //
+ // import "golang.org/x/net/context"
+ //
+ // // User is the type of value stored in the Contexts.
+ // type User struct {...}
+ //
+ // // key is an unexported type for keys defined in this package.
+ // // This prevents collisions with keys defined in other packages.
+ // type key int
+ //
+ // // userKey is the key for user.User values in Contexts. It is
+ // // unexported; clients use user.NewContext and user.FromContext
+ // // instead of using this key directly.
+ // var userKey key = 0
+ //
+ // // NewContext returns a new Context that carries value u.
+ // func NewContext(ctx context.Context, u *User) context.Context {
+ // return context.WithValue(ctx, userKey, u)
+ // }
+ //
+ // // FromContext returns the User value stored in ctx, if any.
+ // func FromContext(ctx context.Context) (*User, bool) {
+ // u, ok := ctx.Value(userKey).(*User)
+ // return u, ok
+ // }
+ Value(key interface{}) interface{}
+}
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = errors.New("context canceled")
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = errors.New("context deadline exceeded")
+
+// An emptyCtx is never canceled, has no values, and has no deadline. It is not
+// struct{}, since vars of this type must have distinct addresses.
+type emptyCtx int
+
+func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+func (*emptyCtx) Done() <-chan struct{} {
+ return nil
+}
+
+func (*emptyCtx) Err() error {
+ return nil
+}
+
+func (*emptyCtx) Value(key interface{}) interface{} {
+ return nil
+}
+
+func (e *emptyCtx) String() string {
+ switch e {
+ case background:
+ return "context.Background"
+ case todo:
+ return "context.TODO"
+ }
+ return "unknown empty Context"
+}
+
+var (
+ background = new(emptyCtx)
+ todo = new(emptyCtx)
+)
+
+// Background returns a non-nil, empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming
+// requests.
+func Background() Context {
+ return background
+}
+
+// TODO returns a non-nil, empty Context. Code should use context.TODO when
+// it's unclear which Context to use or it's is not yet available (because the
+// surrounding function has not yet been extended to accept a Context
+// parameter). TODO is recognized by static analysis tools that determine
+// whether Contexts are propagated correctly in a program.
+func TODO() Context {
+ return todo
+}
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc func()
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+ c := newCancelCtx(parent)
+ propagateCancel(parent, &c)
+ return &c, func() { c.cancel(true, Canceled) }
+}
+
+// newCancelCtx returns an initialized cancelCtx.
+func newCancelCtx(parent Context) cancelCtx {
+ return cancelCtx{
+ Context: parent,
+ done: make(chan struct{}),
+ }
+}
+
+// propagateCancel arranges for child to be canceled when parent is.
+func propagateCancel(parent Context, child canceler) {
+ if parent.Done() == nil {
+ return // parent is never canceled
+ }
+ if p, ok := parentCancelCtx(parent); ok {
+ p.mu.Lock()
+ if p.err != nil {
+ // parent has already been canceled
+ child.cancel(false, p.err)
+ } else {
+ if p.children == nil {
+ p.children = make(map[canceler]bool)
+ }
+ p.children[child] = true
+ }
+ p.mu.Unlock()
+ } else {
+ go func() {
+ select {
+ case <-parent.Done():
+ child.cancel(false, parent.Err())
+ case <-child.Done():
+ }
+ }()
+ }
+}
+
+// parentCancelCtx follows a chain of parent references until it finds a
+// *cancelCtx. This function understands how each of the concrete types in this
+// package represents its parent.
+func parentCancelCtx(parent Context) (*cancelCtx, bool) {
+ for {
+ switch c := parent.(type) {
+ case *cancelCtx:
+ return c, true
+ case *timerCtx:
+ return &c.cancelCtx, true
+ case *valueCtx:
+ parent = c.Context
+ default:
+ return nil, false
+ }
+ }
+}
+
+// removeChild removes a context from its parent.
+func removeChild(parent Context, child canceler) {
+ p, ok := parentCancelCtx(parent)
+ if !ok {
+ return
+ }
+ p.mu.Lock()
+ if p.children != nil {
+ delete(p.children, child)
+ }
+ p.mu.Unlock()
+}
+
+// A canceler is a context type that can be canceled directly. The
+// implementations are *cancelCtx and *timerCtx.
+type canceler interface {
+ cancel(removeFromParent bool, err error)
+ Done() <-chan struct{}
+}
+
+// A cancelCtx can be canceled. When canceled, it also cancels any children
+// that implement canceler.
+type cancelCtx struct {
+ Context
+
+ done chan struct{} // closed by the first cancel call.
+
+ mu sync.Mutex
+ children map[canceler]bool // set to nil by the first cancel call
+ err error // set to non-nil by the first cancel call
+}
+
+func (c *cancelCtx) Done() <-chan struct{} {
+ return c.done
+}
+
+func (c *cancelCtx) Err() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.err
+}
+
+func (c *cancelCtx) String() string {
+ return fmt.Sprintf("%v.WithCancel", c.Context)
+}
+
+// cancel closes c.done, cancels each of c's children, and, if
+// removeFromParent is true, removes c from its parent's children.
+func (c *cancelCtx) cancel(removeFromParent bool, err error) {
+ if err == nil {
+ panic("context: internal error: missing cancel error")
+ }
+ c.mu.Lock()
+ if c.err != nil {
+ c.mu.Unlock()
+ return // already canceled
+ }
+ c.err = err
+ close(c.done)
+ for child := range c.children {
+ // NOTE: acquiring the child's lock while holding parent's lock.
+ child.cancel(false, err)
+ }
+ c.children = nil
+ c.mu.Unlock()
+
+ if removeFromParent {
+ removeChild(c.Context, c)
+ }
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+ if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
+ // The current deadline is already sooner than the new one.
+ return WithCancel(parent)
+ }
+ c := &timerCtx{
+ cancelCtx: newCancelCtx(parent),
+ deadline: deadline,
+ }
+ propagateCancel(parent, c)
+ d := deadline.Sub(time.Now())
+ if d <= 0 {
+ c.cancel(true, DeadlineExceeded) // deadline has already passed
+ return c, func() { c.cancel(true, Canceled) }
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.err == nil {
+ c.timer = time.AfterFunc(d, func() {
+ c.cancel(true, DeadlineExceeded)
+ })
+ }
+ return c, func() { c.cancel(true, Canceled) }
+}
+
+// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
+// implement Done and Err. It implements cancel by stopping its timer then
+// delegating to cancelCtx.cancel.
+type timerCtx struct {
+ cancelCtx
+ timer *time.Timer // Under cancelCtx.mu.
+
+ deadline time.Time
+}
+
+func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
+ return c.deadline, true
+}
+
+func (c *timerCtx) String() string {
+ return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
+}
+
+func (c *timerCtx) cancel(removeFromParent bool, err error) {
+ c.cancelCtx.cancel(false, err)
+ if removeFromParent {
+ // Remove this timerCtx from its parent cancelCtx's children.
+ removeChild(c.cancelCtx.Context, c)
+ }
+ c.mu.Lock()
+ if c.timer != nil {
+ c.timer.Stop()
+ c.timer = nil
+ }
+ c.mu.Unlock()
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// defer cancel() // releases resources if slowOperation completes before timeout elapses
+// return slowOperation(ctx)
+// }
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+ return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+ return &valueCtx{parent, key, val}
+}
+
+// A valueCtx carries a key-value pair. It implements Value for that key and
+// delegates all other calls to the embedded Context.
+type valueCtx struct {
+ Context
+ key, val interface{}
+}
+
+func (c *valueCtx) String() string {
+ return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
+}
+
+func (c *valueCtx) Value(key interface{}) interface{} {
+ if c.key == key {
+ return c.val
+ }
+ return c.Context.Value(key)
+}
diff --git a/context/context_test.go b/context/context_test.go
new file mode 100644
index 0000000..e64afa6
--- /dev/null
+++ b/context/context_test.go
@@ -0,0 +1,575 @@
+// Copyright 2014 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.
+
+package context
+
+import (
+ "fmt"
+ "math/rand"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+// otherContext is a Context that's not one of the types defined in context.go.
+// This lets us test code paths that differ based on the underlying type of the
+// Context.
+type otherContext struct {
+ Context
+}
+
+func TestBackground(t *testing.T) {
+ c := Background()
+ if c == nil {
+ t.Fatalf("Background returned nil")
+ }
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if got, want := fmt.Sprint(c), "context.Background"; got != want {
+ t.Errorf("Background().String() = %q want %q", got, want)
+ }
+}
+
+func TestTODO(t *testing.T) {
+ c := TODO()
+ if c == nil {
+ t.Fatalf("TODO returned nil")
+ }
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if got, want := fmt.Sprint(c), "context.TODO"; got != want {
+ t.Errorf("TODO().String() = %q want %q", got, want)
+ }
+}
+
+func TestWithCancel(t *testing.T) {
+ c1, cancel := WithCancel(Background())
+
+ if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
+ t.Errorf("c1.String() = %q want %q", got, want)
+ }
+
+ o := otherContext{c1}
+ c2, _ := WithCancel(o)
+ contexts := []Context{c1, o, c2}
+
+ for i, c := range contexts {
+ if d := c.Done(); d == nil {
+ t.Errorf("c[%d].Done() == %v want non-nil", i, d)
+ }
+ if e := c.Err(); e != nil {
+ t.Errorf("c[%d].Err() == %v want nil", i, e)
+ }
+
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ }
+
+ cancel()
+ time.Sleep(100 * time.Millisecond) // let cancelation propagate
+
+ for i, c := range contexts {
+ select {
+ case <-c.Done():
+ default:
+ t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
+ }
+ if e := c.Err(); e != Canceled {
+ t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
+ }
+ }
+}
+
+func TestParentFinishesChild(t *testing.T) {
+ // Context tree:
+ // parent -> cancelChild
+ // parent -> valueChild -> timerChild
+ parent, cancel := WithCancel(Background())
+ cancelChild, stop := WithCancel(parent)
+ defer stop()
+ valueChild := WithValue(parent, "key", "value")
+ timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
+ defer stop()
+
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ case x := <-cancelChild.Done():
+ t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
+ case x := <-timerChild.Done():
+ t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
+ case x := <-valueChild.Done():
+ t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+
+ // The parent's children should contain the two cancelable children.
+ pc := parent.(*cancelCtx)
+ cc := cancelChild.(*cancelCtx)
+ tc := timerChild.(*timerCtx)
+ pc.mu.Lock()
+ if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
+ t.Errorf("bad linkage: pc.children = %v, want %v and %v",
+ pc.children, cc, tc)
+ }
+ pc.mu.Unlock()
+
+ if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
+ t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
+ }
+ if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
+ t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
+ }
+
+ cancel()
+
+ pc.mu.Lock()
+ if len(pc.children) != 0 {
+ t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
+ }
+ pc.mu.Unlock()
+
+ // parent and children should all be finished.
+ check := func(ctx Context, name string) {
+ select {
+ case <-ctx.Done():
+ default:
+ t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
+ }
+ if e := ctx.Err(); e != Canceled {
+ t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
+ }
+ }
+ check(parent, "parent")
+ check(cancelChild, "cancelChild")
+ check(valueChild, "valueChild")
+ check(timerChild, "timerChild")
+
+ // WithCancel should return a canceled context on a canceled parent.
+ precanceledChild := WithValue(parent, "key", "value")
+ select {
+ case <-precanceledChild.Done():
+ default:
+ t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
+ }
+ if e := precanceledChild.Err(); e != Canceled {
+ t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
+ }
+}
+
+func TestChildFinishesFirst(t *testing.T) {
+ cancelable, stop := WithCancel(Background())
+ defer stop()
+ for _, parent := range []Context{Background(), cancelable} {
+ child, cancel := WithCancel(parent)
+
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ case x := <-child.Done():
+ t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+
+ cc := child.(*cancelCtx)
+ pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
+ if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
+ t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
+ }
+
+ if pcok {
+ pc.mu.Lock()
+ if len(pc.children) != 1 || !pc.children[cc] {
+ t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
+ }
+ pc.mu.Unlock()
+ }
+
+ cancel()
+
+ if pcok {
+ pc.mu.Lock()
+ if len(pc.children) != 0 {
+ t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
+ }
+ pc.mu.Unlock()
+ }
+
+ // child should be finished.
+ select {
+ case <-child.Done():
+ default:
+ t.Errorf("<-child.Done() blocked, but shouldn't have")
+ }
+ if e := child.Err(); e != Canceled {
+ t.Errorf("child.Err() == %v want %v", e, Canceled)
+ }
+
+ // parent should not be finished.
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if e := parent.Err(); e != nil {
+ t.Errorf("parent.Err() == %v want nil", e)
+ }
+ }
+}
+
+func testDeadline(c Context, wait time.Duration, t *testing.T) {
+ select {
+ case <-time.After(wait):
+ t.Fatalf("context should have timed out")
+ case <-c.Done():
+ }
+ if e := c.Err(); e != DeadlineExceeded {
+ t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
+ }
+}
+
+func TestDeadline(t *testing.T) {
+ c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+ t.Errorf("c.String() = %q want prefix %q", got, prefix)
+ }
+ testDeadline(c, 200*time.Millisecond, t)
+
+ c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ o := otherContext{c}
+ testDeadline(o, 200*time.Millisecond, t)
+
+ c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ o = otherContext{c}
+ c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
+ testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestTimeout(t *testing.T) {
+ c, _ := WithTimeout(Background(), 100*time.Millisecond)
+ if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+ t.Errorf("c.String() = %q want prefix %q", got, prefix)
+ }
+ testDeadline(c, 200*time.Millisecond, t)
+
+ c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ o := otherContext{c}
+ testDeadline(o, 200*time.Millisecond, t)
+
+ c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ o = otherContext{c}
+ c, _ = WithTimeout(o, 300*time.Millisecond)
+ testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestCanceledTimeout(t *testing.T) {
+ c, _ := WithTimeout(Background(), 200*time.Millisecond)
+ o := otherContext{c}
+ c, cancel := WithTimeout(o, 400*time.Millisecond)
+ cancel()
+ time.Sleep(100 * time.Millisecond) // let cancelation propagate
+ select {
+ case <-c.Done():
+ default:
+ t.Errorf("<-c.Done() blocked, but shouldn't have")
+ }
+ if e := c.Err(); e != Canceled {
+ t.Errorf("c.Err() == %v want %v", e, Canceled)
+ }
+}
+
+type key1 int
+type key2 int
+
+var k1 = key1(1)
+var k2 = key2(1) // same int as k1, different type
+var k3 = key2(3) // same type as k2, different int
+
+func TestValues(t *testing.T) {
+ check := func(c Context, nm, v1, v2, v3 string) {
+ if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
+ t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
+ }
+ if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
+ t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
+ }
+ if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
+ t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
+ }
+ }
+
+ c0 := Background()
+ check(c0, "c0", "", "", "")
+
+ c1 := WithValue(Background(), k1, "c1k1")
+ check(c1, "c1", "c1k1", "", "")
+
+ if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
+ t.Errorf("c.String() = %q want %q", got, want)
+ }
+
+ c2 := WithValue(c1, k2, "c2k2")
+ check(c2, "c2", "c1k1", "c2k2", "")
+
+ c3 := WithValue(c2, k3, "c3k3")
+ check(c3, "c2", "c1k1", "c2k2", "c3k3")
+
+ c4 := WithValue(c3, k1, nil)
+ check(c4, "c4", "", "c2k2", "c3k3")
+
+ o0 := otherContext{Background()}
+ check(o0, "o0", "", "", "")
+
+ o1 := otherContext{WithValue(Background(), k1, "c1k1")}
+ check(o1, "o1", "c1k1", "", "")
+
+ o2 := WithValue(o1, k2, "o2k2")
+ check(o2, "o2", "c1k1", "o2k2", "")
+
+ o3 := otherContext{c4}
+ check(o3, "o3", "", "c2k2", "c3k3")
+
+ o4 := WithValue(o3, k3, nil)
+ check(o4, "o4", "", "c2k2", "")
+}
+
+func TestAllocs(t *testing.T) {
+ bg := Background()
+ for _, test := range []struct {
+ desc string
+ f func()
+ limit float64
+ gccgoLimit float64
+ }{
+ {
+ desc: "Background()",
+ f: func() { Background() },
+ limit: 0,
+ gccgoLimit: 0,
+ },
+ {
+ desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
+ f: func() {
+ c := WithValue(bg, k1, nil)
+ c.Value(k1)
+ },
+ limit: 3,
+ gccgoLimit: 3,
+ },
+ {
+ desc: "WithTimeout(bg, 15*time.Millisecond)",
+ f: func() {
+ c, _ := WithTimeout(bg, 15*time.Millisecond)
+ <-c.Done()
+ },
+ limit: 8,
+ gccgoLimit: 15,
+ },
+ {
+ desc: "WithCancel(bg)",
+ f: func() {
+ c, cancel := WithCancel(bg)
+ cancel()
+ <-c.Done()
+ },
+ limit: 5,
+ gccgoLimit: 8,
+ },
+ {
+ desc: "WithTimeout(bg, 100*time.Millisecond)",
+ f: func() {
+ c, cancel := WithTimeout(bg, 100*time.Millisecond)
+ cancel()
+ <-c.Done()
+ },
+ limit: 8,
+ gccgoLimit: 25,
+ },
+ } {
+ limit := test.limit
+ if runtime.Compiler == "gccgo" {
+ // gccgo does not yet do escape analysis.
+ // TOOD(iant): Remove this when gccgo does do escape analysis.
+ limit = test.gccgoLimit
+ }
+ if n := testing.AllocsPerRun(100, test.f); n > limit {
+ t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
+ }
+ }
+}
+
+func TestSimultaneousCancels(t *testing.T) {
+ root, cancel := WithCancel(Background())
+ m := map[Context]CancelFunc{root: cancel}
+ q := []Context{root}
+ // Create a tree of contexts.
+ for len(q) != 0 && len(m) < 100 {
+ parent := q[0]
+ q = q[1:]
+ for i := 0; i < 4; i++ {
+ ctx, cancel := WithCancel(parent)
+ m[ctx] = cancel
+ q = append(q, ctx)
+ }
+ }
+ // Start all the cancels in a random order.
+ var wg sync.WaitGroup
+ wg.Add(len(m))
+ for _, cancel := range m {
+ go func(cancel CancelFunc) {
+ cancel()
+ wg.Done()
+ }(cancel)
+ }
+ // Wait on all the contexts in a random order.
+ for ctx := range m {
+ select {
+ case <-ctx.Done():
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
+ }
+ }
+ // Wait for all the cancel functions to return.
+ done := make(chan struct{})
+ go func() {
+ wg.Wait()
+ close(done)
+ }()
+ select {
+ case <-done:
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
+ }
+}
+
+func TestInterlockedCancels(t *testing.T) {
+ parent, cancelParent := WithCancel(Background())
+ child, cancelChild := WithCancel(parent)
+ go func() {
+ parent.Done()
+ cancelChild()
+ }()
+ cancelParent()
+ select {
+ case <-child.Done():
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
+ }
+}
+
+func TestLayersCancel(t *testing.T) {
+ testLayers(t, time.Now().UnixNano(), false)
+}
+
+func TestLayersTimeout(t *testing.T) {
+ testLayers(t, time.Now().UnixNano(), true)
+}
+
+func testLayers(t *testing.T, seed int64, testTimeout bool) {
+ rand.Seed(seed)
+ errorf := func(format string, a ...interface{}) {
+ t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
+ }
+ const (
+ timeout = 200 * time.Millisecond
+ minLayers = 30
+ )
+ type value int
+ var (
+ vals []*value
+ cancels []CancelFunc
+ numTimers int
+ ctx = Background()
+ )
+ for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
+ switch rand.Intn(3) {
+ case 0:
+ v := new(value)
+ ctx = WithValue(ctx, v, v)
+ vals = append(vals, v)
+ case 1:
+ var cancel CancelFunc
+ ctx, cancel = WithCancel(ctx)
+ cancels = append(cancels, cancel)
+ case 2:
+ var cancel CancelFunc
+ ctx, cancel = WithTimeout(ctx, timeout)
+ cancels = append(cancels, cancel)
+ numTimers++
+ }
+ }
+ checkValues := func(when string) {
+ for _, key := range vals {
+ if val := ctx.Value(key).(*value); key != val {
+ errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
+ }
+ }
+ }
+ select {
+ case <-ctx.Done():
+ errorf("ctx should not be canceled yet")
+ default:
+ }
+ if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
+ t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
+ }
+ t.Log(ctx)
+ checkValues("before cancel")
+ if testTimeout {
+ select {
+ case <-ctx.Done():
+ case <-time.After(timeout + timeout/10):
+ errorf("ctx should have timed out")
+ }
+ checkValues("after timeout")
+ } else {
+ cancel := cancels[rand.Intn(len(cancels))]
+ cancel()
+ select {
+ case <-ctx.Done():
+ default:
+ errorf("ctx should be canceled")
+ }
+ checkValues("after cancel")
+ }
+}
+
+func TestCancelRemoves(t *testing.T) {
+ checkChildren := func(when string, ctx Context, want int) {
+ if got := len(ctx.(*cancelCtx).children); got != want {
+ t.Errorf("%s: context has %d children, want %d", when, got, want)
+ }
+ }
+
+ ctx, _ := WithCancel(Background())
+ checkChildren("after creation", ctx, 0)
+ _, cancel := WithCancel(ctx)
+ checkChildren("with WithCancel child ", ctx, 1)
+ cancel()
+ checkChildren("after cancelling WithCancel child", ctx, 0)
+
+ ctx, _ = WithCancel(Background())
+ checkChildren("after creation", ctx, 0)
+ _, cancel = WithTimeout(ctx, 60*time.Minute)
+ checkChildren("with WithTimeout child ", ctx, 1)
+ cancel()
+ checkChildren("after cancelling WithTimeout child", ctx, 0)
+}
diff --git a/context/ctxhttp/cancelreq.go b/context/ctxhttp/cancelreq.go
new file mode 100644
index 0000000..48610e3
--- /dev/null
+++ b/context/ctxhttp/cancelreq.go
@@ -0,0 +1,18 @@
+// Copyright 2015 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.
+
+// +build go1.5
+
+package ctxhttp
+
+import "net/http"
+
+func canceler(client *http.Client, req *http.Request) func() {
+ ch := make(chan struct{})
+ req.Cancel = ch
+
+ return func() {
+ close(ch)
+ }
+}
diff --git a/context/ctxhttp/cancelreq_go14.go b/context/ctxhttp/cancelreq_go14.go
new file mode 100644
index 0000000..56bcbad
--- /dev/null
+++ b/context/ctxhttp/cancelreq_go14.go
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+// +build !go1.5
+
+package ctxhttp
+
+import "net/http"
+
+type requestCanceler interface {
+ CancelRequest(*http.Request)
+}
+
+func canceler(client *http.Client, req *http.Request) func() {
+ rc, ok := client.Transport.(requestCanceler)
+ if !ok {
+ return func() {}
+ }
+ return func() {
+ rc.CancelRequest(req)
+ }
+}
diff --git a/context/ctxhttp/ctxhttp.go b/context/ctxhttp/ctxhttp.go
new file mode 100644
index 0000000..504dd63
--- /dev/null
+++ b/context/ctxhttp/ctxhttp.go
@@ -0,0 +1,79 @@
+// Copyright 2015 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.
+
+// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
+package ctxhttp // import "golang.org/x/net/context/ctxhttp"
+
+import (
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "golang.org/x/net/context"
+)
+
+// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
+// If the client is nil, http.DefaultClient is used.
+// If the context is canceled or times out, ctx.Err() will be returned.
+func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
+ if client == nil {
+ client = http.DefaultClient
+ }
+
+ // Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go.
+ cancel := canceler(client, req)
+
+ type responseAndError struct {
+ resp *http.Response
+ err error
+ }
+ result := make(chan responseAndError, 1)
+
+ go func() {
+ resp, err := client.Do(req)
+ result <- responseAndError{resp, err}
+ }()
+
+ select {
+ case <-ctx.Done():
+ cancel()
+ return nil, ctx.Err()
+ case r := <-result:
+ return r.resp, r.err
+ }
+}
+
+// Get issues a GET request via the Do function.
+func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Head issues a HEAD request via the Do function.
+func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("HEAD", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Post issues a POST request via the Do function.
+func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
+ req, err := http.NewRequest("POST", url, body)
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", bodyType)
+ return Do(ctx, client, req)
+}
+
+// PostForm issues a POST request via the Do function.
+func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
+ return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+}
diff --git a/context/ctxhttp/ctxhttp_test.go b/context/ctxhttp/ctxhttp_test.go
new file mode 100644
index 0000000..47b53d7
--- /dev/null
+++ b/context/ctxhttp/ctxhttp_test.go
@@ -0,0 +1,72 @@
+// Copyright 2015 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.
+
+package ctxhttp
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+const (
+ requestDuration = 100 * time.Millisecond
+ requestBody = "ok"
+)
+
+func TestNoTimeout(t *testing.T) {
+ ctx := context.Background()
+ resp, err := doRequest(ctx)
+
+ if resp == nil || err != nil {
+ t.Fatalf("error received from client: %v %v", err, resp)
+ }
+}
+func TestCancel(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ time.Sleep(requestDuration / 2)
+ cancel()
+ }()
+
+ resp, err := doRequest(ctx)
+
+ if resp != nil || err == nil {
+ t.Fatalf("expected error, didn't get one. resp: %v", resp)
+ }
+ if err != ctx.Err() {
+ t.Fatalf("expected error from context but got: %v", err)
+ }
+}
+
+func TestCancelAfterRequest(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+
+ resp, err := doRequest(ctx)
+
+ // Cancel before reading the body.
+ // Request.Body should still be readable after the context is canceled.
+ cancel()
+
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil || string(b) != requestBody {
+ t.Fatalf("could not read body: %q %v", b, err)
+ }
+}
+
+func doRequest(ctx context.Context) (*http.Response, error) {
+ var okHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(requestDuration)
+ w.Write([]byte(requestBody))
+ })
+
+ serv := httptest.NewServer(okHandler)
+ defer serv.Close()
+
+ return Get(ctx, nil, serv.URL)
+}
diff --git a/context/withtimeout_test.go b/context/withtimeout_test.go
new file mode 100644
index 0000000..a6754dc
--- /dev/null
+++ b/context/withtimeout_test.go
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+package context_test
+
+import (
+ "fmt"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+func ExampleWithTimeout() {
+ // Pass a context with a timeout to tell a blocking function that it
+ // should abandon its work after the timeout elapses.
+ ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
+ select {
+ case <-time.After(200 * time.Millisecond):
+ fmt.Println("overslept")
+ case <-ctx.Done():
+ fmt.Println(ctx.Err()) // prints "context deadline exceeded"
+ }
+ // Output:
+ // context deadline exceeded
+}
diff --git a/dict/dict.go b/dict/dict.go
new file mode 100644
index 0000000..58fef89
--- /dev/null
+++ b/dict/dict.go
@@ -0,0 +1,210 @@
+// Copyright 2010 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.
+
+// Package dict implements the Dictionary Server Protocol
+// as defined in RFC 2229.
+package dict // import "golang.org/x/net/dict"
+
+import (
+ "net/textproto"
+ "strconv"
+ "strings"
+)
+
+// A Client represents a client connection to a dictionary server.
+type Client struct {
+ text *textproto.Conn
+}
+
+// Dial returns a new client connected to a dictionary server at
+// addr on the given network.
+func Dial(network, addr string) (*Client, error) {
+ text, err := textproto.Dial(network, addr)
+ if err != nil {
+ return nil, err
+ }
+ _, _, err = text.ReadCodeLine(220)
+ if err != nil {
+ text.Close()
+ return nil, err
+ }
+ return &Client{text: text}, nil
+}
+
+// Close closes the connection to the dictionary server.
+func (c *Client) Close() error {
+ return c.text.Close()
+}
+
+// A Dict represents a dictionary available on the server.
+type Dict struct {
+ Name string // short name of dictionary
+ Desc string // long description
+}
+
+// Dicts returns a list of the dictionaries available on the server.
+func (c *Client) Dicts() ([]Dict, error) {
+ id, err := c.text.Cmd("SHOW DB")
+ if err != nil {
+ return nil, err
+ }
+
+ c.text.StartResponse(id)
+ defer c.text.EndResponse(id)
+
+ _, _, err = c.text.ReadCodeLine(110)
+ if err != nil {
+ return nil, err
+ }
+ lines, err := c.text.ReadDotLines()
+ if err != nil {
+ return nil, err
+ }
+ _, _, err = c.text.ReadCodeLine(250)
+
+ dicts := make([]Dict, len(lines))
+ for i := range dicts {
+ d := &dicts[i]
+ a, _ := fields(lines[i])
+ if len(a) < 2 {
+ return nil, textproto.ProtocolError("invalid dictionary: " + lines[i])
+ }
+ d.Name = a[0]
+ d.Desc = a[1]
+ }
+ return dicts, err
+}
+
+// A Defn represents a definition.
+type Defn struct {
+ Dict Dict // Dict where definition was found
+ Word string // Word being defined
+ Text []byte // Definition text, typically multiple lines
+}
+
+// Define requests the definition of the given word.
+// The argument dict names the dictionary to use,
+// the Name field of a Dict returned by Dicts.
+//
+// The special dictionary name "*" means to look in all the
+// server's dictionaries.
+// The special dictionary name "!" means to look in all the
+// server's dictionaries in turn, stopping after finding the word
+// in one of them.
+func (c *Client) Define(dict, word string) ([]*Defn, error) {
+ id, err := c.text.Cmd("DEFINE %s %q", dict, word)
+ if err != nil {
+ return nil, err
+ }
+
+ c.text.StartResponse(id)
+ defer c.text.EndResponse(id)
+
+ _, line, err := c.text.ReadCodeLine(150)
+ if err != nil {
+ return nil, err
+ }
+ a, _ := fields(line)
+ if len(a) < 1 {
+ return nil, textproto.ProtocolError("malformed response: " + line)
+ }
+ n, err := strconv.Atoi(a[0])
+ if err != nil {
+ return nil, textproto.ProtocolError("invalid definition count: " + a[0])
+ }
+ def := make([]*Defn, n)
+ for i := 0; i < n; i++ {
+ _, line, err = c.text.ReadCodeLine(151)
+ if err != nil {
+ return nil, err
+ }
+ a, _ := fields(line)
+ if len(a) < 3 {
+ // skip it, to keep protocol in sync
+ i--
+ n--
+ def = def[0:n]
+ continue
+ }
+ d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}}
+ d.Text, err = c.text.ReadDotBytes()
+ if err != nil {
+ return nil, err
+ }
+ def[i] = d
+ }
+ _, _, err = c.text.ReadCodeLine(250)
+ return def, err
+}
+
+// Fields returns the fields in s.
+// Fields are space separated unquoted words
+// or quoted with single or double quote.
+func fields(s string) ([]string, error) {
+ var v []string
+ i := 0
+ for {
+ for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
+ i++
+ }
+ if i >= len(s) {
+ break
+ }
+ if s[i] == '"' || s[i] == '\'' {
+ q := s[i]
+ // quoted string
+ var j int
+ for j = i + 1; ; j++ {
+ if j >= len(s) {
+ return nil, textproto.ProtocolError("malformed quoted string")
+ }
+ if s[j] == '\\' {
+ j++
+ continue
+ }
+ if s[j] == q {
+ j++
+ break
+ }
+ }
+ v = append(v, unquote(s[i+1:j-1]))
+ i = j
+ } else {
+ // atom
+ var j int
+ for j = i; j < len(s); j++ {
+ if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' {
+ break
+ }
+ }
+ v = append(v, s[i:j])
+ i = j
+ }
+ if i < len(s) {
+ c := s[i]
+ if c != ' ' && c != '\t' {
+ return nil, textproto.ProtocolError("quotes not on word boundaries")
+ }
+ }
+ }
+ return v, nil
+}
+
+func unquote(s string) string {
+ if strings.Index(s, "\\") < 0 {
+ return s
+ }
+ b := []byte(s)
+ w := 0
+ for r := 0; r < len(b); r++ {
+ c := b[r]
+ if c == '\\' {
+ r++
+ c = b[r]
+ }
+ b[w] = c
+ w++
+ }
+ return string(b[0:w])
+}
diff --git a/html/atom/atom.go b/html/atom/atom.go
new file mode 100644
index 0000000..cd0a8ac
--- /dev/null
+++ b/html/atom/atom.go
@@ -0,0 +1,78 @@
+// Copyright 2012 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.
+
+// Package atom provides integer codes (also known as atoms) for a fixed set of
+// frequently occurring HTML strings: tag names and attribute keys such as "p"
+// and "id".
+//
+// Sharing an atom's name between all elements with the same tag can result in
+// fewer string allocations when tokenizing and parsing HTML. Integer
+// comparisons are also generally faster than string comparisons.
+//
+// The value of an atom's particular code is not guaranteed to stay the same
+// between versions of this package. Neither is any ordering guaranteed:
+// whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to
+// be dense. The only guarantees are that e.g. looking up "div" will yield
+// atom.Div, calling atom.Div.String will return "div", and atom.Div != 0.
+package atom // import "golang.org/x/net/html/atom"
+
+// Atom is an integer code for a string. The zero value maps to "".
+type Atom uint32
+
+// String returns the atom's name.
+func (a Atom) String() string {
+ start := uint32(a >> 8)
+ n := uint32(a & 0xff)
+ if start+n > uint32(len(atomText)) {
+ return ""
+ }
+ return atomText[start : start+n]
+}
+
+func (a Atom) string() string {
+ return atomText[a>>8 : a>>8+a&0xff]
+}
+
+// fnv computes the FNV hash with an arbitrary starting value h.
+func fnv(h uint32, s []byte) uint32 {
+ for i := range s {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+func match(s string, t []byte) bool {
+ for i, c := range t {
+ if s[i] != c {
+ return false
+ }
+ }
+ return true
+}
+
+// Lookup returns the atom whose name is s. It returns zero if there is no
+// such atom. The lookup is case sensitive.
+func Lookup(s []byte) Atom {
+ if len(s) == 0 || len(s) > maxAtomLen {
+ return 0
+ }
+ h := fnv(hash0, s)
+ if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
+ return a
+ }
+ if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
+ return a
+ }
+ return 0
+}
+
+// String returns a string whose contents are equal to s. In that sense, it is
+// equivalent to string(s) but may be more efficient.
+func String(s []byte) string {
+ if a := Lookup(s); a != 0 {
+ return a.String()
+ }
+ return string(s)
+}
diff --git a/html/atom/atom_test.go b/html/atom/atom_test.go
new file mode 100644
index 0000000..6e33704
--- /dev/null
+++ b/html/atom/atom_test.go
@@ -0,0 +1,109 @@
+// Copyright 2012 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.
+
+package atom
+
+import (
+ "sort"
+ "testing"
+)
+
+func TestKnown(t *testing.T) {
+ for _, s := range testAtomList {
+ if atom := Lookup([]byte(s)); atom.String() != s {
+ t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String())
+ }
+ }
+}
+
+func TestHits(t *testing.T) {
+ for _, a := range table {
+ if a == 0 {
+ continue
+ }
+ got := Lookup([]byte(a.String()))
+ if got != a {
+ t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a))
+ }
+ }
+}
+
+func TestMisses(t *testing.T) {
+ testCases := []string{
+ "",
+ "\x00",
+ "\xff",
+ "A",
+ "DIV",
+ "Div",
+ "dIV",
+ "aa",
+ "a\x00",
+ "ab",
+ "abb",
+ "abbr0",
+ "abbr ",
+ " abbr",
+ " a",
+ "acceptcharset",
+ "acceptCharset",
+ "accept_charset",
+ "h0",
+ "h1h2",
+ "h7",
+ "onClick",
+ "λ",
+ // The following string has the same hash (0xa1d7fab7) as "onmouseover".
+ "\x00\x00\x00\x00\x00\x50\x18\xae\x38\xd0\xb7",
+ }
+ for _, tc := range testCases {
+ got := Lookup([]byte(tc))
+ if got != 0 {
+ t.Errorf("Lookup(%q): got %d, want 0", tc, got)
+ }
+ }
+}
+
+func TestForeignObject(t *testing.T) {
+ const (
+ afo = Foreignobject
+ afO = ForeignObject
+ sfo = "foreignobject"
+ sfO = "foreignObject"
+ )
+ if got := Lookup([]byte(sfo)); got != afo {
+ t.Errorf("Lookup(%q): got %#v, want %#v", sfo, got, afo)
+ }
+ if got := Lookup([]byte(sfO)); got != afO {
+ t.Errorf("Lookup(%q): got %#v, want %#v", sfO, got, afO)
+ }
+ if got := afo.String(); got != sfo {
+ t.Errorf("Atom(%#v).String(): got %q, want %q", afo, got, sfo)
+ }
+ if got := afO.String(); got != sfO {
+ t.Errorf("Atom(%#v).String(): got %q, want %q", afO, got, sfO)
+ }
+}
+
+func BenchmarkLookup(b *testing.B) {
+ sortedTable := make([]string, 0, len(table))
+ for _, a := range table {
+ if a != 0 {
+ sortedTable = append(sortedTable, a.String())
+ }
+ }
+ sort.Strings(sortedTable)
+
+ x := make([][]byte, 1000)
+ for i := range x {
+ x[i] = []byte(sortedTable[i%len(sortedTable)])
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, s := range x {
+ Lookup(s)
+ }
+ }
+}
diff --git a/html/atom/gen.go b/html/atom/gen.go
new file mode 100644
index 0000000..6bfa866
--- /dev/null
+++ b/html/atom/gen.go
@@ -0,0 +1,648 @@
+// Copyright 2012 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.
+
+// +build ignore
+
+package main
+
+// This program generates table.go and table_test.go.
+// Invoke as
+//
+// go run gen.go |gofmt >table.go
+// go run gen.go -test |gofmt >table_test.go
+
+import (
+ "flag"
+ "fmt"
+ "math/rand"
+ "os"
+ "sort"
+ "strings"
+)
+
+// identifier converts s to a Go exported identifier.
+// It converts "div" to "Div" and "accept-charset" to "AcceptCharset".
+func identifier(s string) string {
+ b := make([]byte, 0, len(s))
+ cap := true
+ for _, c := range s {
+ if c == '-' {
+ cap = true
+ continue
+ }
+ if cap && 'a' <= c && c <= 'z' {
+ c -= 'a' - 'A'
+ }
+ cap = false
+ b = append(b, byte(c))
+ }
+ return string(b)
+}
+
+var test = flag.Bool("test", false, "generate table_test.go")
+
+func main() {
+ flag.Parse()
+
+ var all []string
+ all = append(all, elements...)
+ all = append(all, attributes...)
+ all = append(all, eventHandlers...)
+ all = append(all, extra...)
+ sort.Strings(all)
+
+ if *test {
+ fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n")
+ fmt.Printf("package atom\n\n")
+ fmt.Printf("var testAtomList = []string{\n")
+ for _, s := range all {
+ fmt.Printf("\t%q,\n", s)
+ }
+ fmt.Printf("}\n")
+ return
+ }
+
+ // uniq - lists have dups
+ // compute max len too
+ maxLen := 0
+ w := 0
+ for _, s := range all {
+ if w == 0 || all[w-1] != s {
+ if maxLen < len(s) {
+ maxLen = len(s)
+ }
+ all[w] = s
+ w++
+ }
+ }
+ all = all[:w]
+
+ // Find hash that minimizes table size.
+ var best *table
+ for i := 0; i < 1000000; i++ {
+ if best != nil && 1<<(best.k-1) < len(all) {
+ break
+ }
+ h := rand.Uint32()
+ for k := uint(0); k <= 16; k++ {
+ if best != nil && k >= best.k {
+ break
+ }
+ var t table
+ if t.init(h, k, all) {
+ best = &t
+ break
+ }
+ }
+ }
+ if best == nil {
+ fmt.Fprintf(os.Stderr, "failed to construct string table\n")
+ os.Exit(1)
+ }
+
+ // Lay out strings, using overlaps when possible.
+ layout := append([]string{}, all...)
+
+ // Remove strings that are substrings of other strings
+ for changed := true; changed; {
+ changed = false
+ for i, s := range layout {
+ if s == "" {
+ continue
+ }
+ for j, t := range layout {
+ if i != j && t != "" && strings.Contains(s, t) {
+ changed = true
+ layout[j] = ""
+ }
+ }
+ }
+ }
+
+ // Join strings where one suffix matches another prefix.
+ for {
+ // Find best i, j, k such that layout[i][len-k:] == layout[j][:k],
+ // maximizing overlap length k.
+ besti := -1
+ bestj := -1
+ bestk := 0
+ for i, s := range layout {
+ if s == "" {
+ continue
+ }
+ for j, t := range layout {
+ if i == j {
+ continue
+ }
+ for k := bestk + 1; k <= len(s) && k <= len(t); k++ {
+ if s[len(s)-k:] == t[:k] {
+ besti = i
+ bestj = j
+ bestk = k
+ }
+ }
+ }
+ }
+ if bestk > 0 {
+ layout[besti] += layout[bestj][bestk:]
+ layout[bestj] = ""
+ continue
+ }
+ break
+ }
+
+ text := strings.Join(layout, "")
+
+ atom := map[string]uint32{}
+ for _, s := range all {
+ off := strings.Index(text, s)
+ if off < 0 {
+ panic("lost string " + s)
+ }
+ atom[s] = uint32(off<<8 | len(s))
+ }
+
+ // Generate the Go code.
+ fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n")
+ fmt.Printf("package atom\n\nconst (\n")
+ for _, s := range all {
+ fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s])
+ }
+ fmt.Printf(")\n\n")
+
+ fmt.Printf("const hash0 = %#x\n\n", best.h0)
+ fmt.Printf("const maxAtomLen = %d\n\n", maxLen)
+
+ fmt.Printf("var table = [1<<%d]Atom{\n", best.k)
+ for i, s := range best.tab {
+ if s == "" {
+ continue
+ }
+ fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s)
+ }
+ fmt.Printf("}\n")
+ datasize := (1 << best.k) * 4
+
+ fmt.Printf("const atomText =\n")
+ textsize := len(text)
+ for len(text) > 60 {
+ fmt.Printf("\t%q +\n", text[:60])
+ text = text[60:]
+ }
+ fmt.Printf("\t%q\n\n", text)
+
+ fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize)
+}
+
+type byLen []string
+
+func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) }
+func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byLen) Len() int { return len(x) }
+
+// fnv computes the FNV hash with an arbitrary starting value h.
+func fnv(h uint32, s string) uint32 {
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+// A table represents an attempt at constructing the lookup table.
+// The lookup table uses cuckoo hashing, meaning that each string
+// can be found in one of two positions.
+type table struct {
+ h0 uint32
+ k uint
+ mask uint32
+ tab []string
+}
+
+// hash returns the two hashes for s.
+func (t *table) hash(s string) (h1, h2 uint32) {
+ h := fnv(t.h0, s)
+ h1 = h & t.mask
+ h2 = (h >> 16) & t.mask
+ return
+}
+
+// init initializes the table with the given parameters.
+// h0 is the initial hash value,
+// k is the number of bits of hash value to use, and
+// x is the list of strings to store in the table.
+// init returns false if the table cannot be constructed.
+func (t *table) init(h0 uint32, k uint, x []string) bool {
+ t.h0 = h0
+ t.k = k
+ t.tab = make([]string, 1<<k)
+ t.mask = 1<<k - 1
+ for _, s := range x {
+ if !t.insert(s) {
+ return false
+ }
+ }
+ return true
+}
+
+// insert inserts s in the table.
+func (t *table) insert(s string) bool {
+ h1, h2 := t.hash(s)
+ if t.tab[h1] == "" {
+ t.tab[h1] = s
+ return true
+ }
+ if t.tab[h2] == "" {
+ t.tab[h2] = s
+ return true
+ }
+ if t.push(h1, 0) {
+ t.tab[h1] = s
+ return true
+ }
+ if t.push(h2, 0) {
+ t.tab[h2] = s
+ return true
+ }
+ return false
+}
+
+// push attempts to push aside the entry in slot i.
+func (t *table) push(i uint32, depth int) bool {
+ if depth > len(t.tab) {
+ return false
+ }
+ s := t.tab[i]
+ h1, h2 := t.hash(s)
+ j := h1 + h2 - i
+ if t.tab[j] != "" && !t.push(j, depth+1) {
+ return false
+ }
+ t.tab[j] = s
+ return true
+}
+
+// The lists of element names and attribute keys were taken from
+// https://html.spec.whatwg.org/multipage/indices.html#index
+// as of the "HTML Living Standard - Last Updated 21 February 2015" version.
+
+var elements = []string{
+ "a",
+ "abbr",
+ "address",
+ "area",
+ "article",
+ "aside",
+ "audio",
+ "b",
+ "base",
+ "bdi",
+ "bdo",
+ "blockquote",
+ "body",
+ "br",
+ "button",
+ "canvas",
+ "caption",
+ "cite",
+ "code",
+ "col",
+ "colgroup",
+ "command",
+ "data",
+ "datalist",
+ "dd",
+ "del",
+ "details",
+ "dfn",
+ "dialog",
+ "div",
+ "dl",
+ "dt",
+ "em",
+ "embed",
+ "fieldset",
+ "figcaption",
+ "figure",
+ "footer",
+ "form",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "head",
+ "header",
+ "hgroup",
+ "hr",
+ "html",
+ "i",
+ "iframe",
+ "img",
+ "input",
+ "ins",
+ "kbd",
+ "keygen",
+ "label",
+ "legend",
+ "li",
+ "link",
+ "map",
+ "mark",
+ "menu",
+ "menuitem",
+ "meta",
+ "meter",
+ "nav",
+ "noscript",
+ "object",
+ "ol",
+ "optgroup",
+ "option",
+ "output",
+ "p",
+ "param",
+ "pre",
+ "progress",
+ "q",
+ "rp",
+ "rt",
+ "ruby",
+ "s",
+ "samp",
+ "script",
+ "section",
+ "select",
+ "small",
+ "source",
+ "span",
+ "strong",
+ "style",
+ "sub",
+ "summary",
+ "sup",
+ "table",
+ "tbody",
+ "td",
+ "template",
+ "textarea",
+ "tfoot",
+ "th",
+ "thead",
+ "time",
+ "title",
+ "tr",
+ "track",
+ "u",
+ "ul",
+ "var",
+ "video",
+ "wbr",
+}
+
+// https://html.spec.whatwg.org/multipage/indices.html#attributes-3
+
+var attributes = []string{
+ "abbr",
+ "accept",
+ "accept-charset",
+ "accesskey",
+ "action",
+ "alt",
+ "async",
+ "autocomplete",
+ "autofocus",
+ "autoplay",
+ "challenge",
+ "charset",
+ "checked",
+ "cite",
+ "class",
+ "cols",
+ "colspan",
+ "command",
+ "content",
+ "contenteditable",
+ "contextmenu",
+ "controls",
+ "coords",
+ "crossorigin",
+ "data",
+ "datetime",
+ "default",
+ "defer",
+ "dir",
+ "dirname",
+ "disabled",
+ "download",
+ "draggable",
+ "dropzone",
+ "enctype",
+ "for",
+ "form",
+ "formaction",
+ "formenctype",
+ "formmethod",
+ "formnovalidate",
+ "formtarget",
+ "headers",
+ "height",
+ "hidden",
+ "high",
+ "href",
+ "hreflang",
+ "http-equiv",
+ "icon",
+ "id",
+ "inputmode",
+ "ismap",
+ "itemid",
+ "itemprop",
+ "itemref",
+ "itemscope",
+ "itemtype",
+ "keytype",
+ "kind",
+ "label",
+ "lang",
+ "list",
+ "loop",
+ "low",
+ "manifest",
+ "max",
+ "maxlength",
+ "media",
+ "mediagroup",
+ "method",
+ "min",
+ "minlength",
+ "multiple",
+ "muted",
+ "name",
+ "novalidate",
+ "open",
+ "optimum",
+ "pattern",
+ "ping",
+ "placeholder",
+ "poster",
+ "preload",
+ "radiogroup",
+ "readonly",
+ "rel",
+ "required",
+ "reversed",
+ "rows",
+ "rowspan",
+ "sandbox",
+ "spellcheck",
+ "scope",
+ "scoped",
+ "seamless",
+ "selected",
+ "shape",
+ "size",
+ "sizes",
+ "sortable",
+ "sorted",
+ "span",
+ "src",
+ "srcdoc",
+ "srclang",
+ "start",
+ "step",
+ "style",
+ "tabindex",
+ "target",
+ "title",
+ "translate",
+ "type",
+ "typemustmatch",
+ "usemap",
+ "value",
+ "width",
+ "wrap",
+}
+
+var eventHandlers = []string{
+ "onabort",
+ "onautocomplete",
+ "onautocompleteerror",
+ "onafterprint",
+ "onbeforeprint",
+ "onbeforeunload",
+ "onblur",
+ "oncancel",
+ "oncanplay",
+ "oncanplaythrough",
+ "onchange",
+ "onclick",
+ "onclose",
+ "oncontextmenu",
+ "oncuechange",
+ "ondblclick",
+ "ondrag",
+ "ondragend",
+ "ondragenter",
+ "ondragleave",
+ "ondragover",
+ "ondragstart",
+ "ondrop",
+ "ondurationchange",
+ "onemptied",
+ "onended",
+ "onerror",
+ "onfocus",
+ "onhashchange",
+ "oninput",
+ "oninvalid",
+ "onkeydown",
+ "onkeypress",
+ "onkeyup",
+ "onlanguagechange",
+ "onload",
+ "onloadeddata",
+ "onloadedmetadata",
+ "onloadstart",
+ "onmessage",
+ "onmousedown",
+ "onmousemove",
+ "onmouseout",
+ "onmouseover",
+ "onmouseup",
+ "onmousewheel",
+ "onoffline",
+ "ononline",
+ "onpagehide",
+ "onpageshow",
+ "onpause",
+ "onplay",
+ "onplaying",
+ "onpopstate",
+ "onprogress",
+ "onratechange",
+ "onreset",
+ "onresize",
+ "onscroll",
+ "onseeked",
+ "onseeking",
+ "onselect",
+ "onshow",
+ "onsort",
+ "onstalled",
+ "onstorage",
+ "onsubmit",
+ "onsuspend",
+ "ontimeupdate",
+ "ontoggle",
+ "onunload",
+ "onvolumechange",
+ "onwaiting",
+}
+
+// extra are ad-hoc values not covered by any of the lists above.
+var extra = []string{
+ "align",
+ "annotation",
+ "annotation-xml",
+ "applet",
+ "basefont",
+ "bgsound",
+ "big",
+ "blink",
+ "center",
+ "color",
+ "desc",
+ "face",
+ "font",
+ "foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive.
+ "foreignobject",
+ "frame",
+ "frameset",
+ "image",
+ "isindex",
+ "listing",
+ "malignmark",
+ "marquee",
+ "math",
+ "mglyph",
+ "mi",
+ "mn",
+ "mo",
+ "ms",
+ "mtext",
+ "nobr",
+ "noembed",
+ "noframes",
+ "plaintext",
+ "prompt",
+ "public",
+ "spacer",
+ "strike",
+ "svg",
+ "system",
+ "tt",
+ "xmp",
+}
diff --git a/html/atom/table.go b/html/atom/table.go
new file mode 100644
index 0000000..2605ba3
--- /dev/null
+++ b/html/atom/table.go
@@ -0,0 +1,713 @@
+// generated by go run gen.go; DO NOT EDIT
+
+package atom
+
+const (
+ A Atom = 0x1
+ Abbr Atom = 0x4
+ Accept Atom = 0x2106
+ AcceptCharset Atom = 0x210e
+ Accesskey Atom = 0x3309
+ Action Atom = 0x1f606
+ Address Atom = 0x4f307
+ Align Atom = 0x1105
+ Alt Atom = 0x4503
+ Annotation Atom = 0x1670a
+ AnnotationXml Atom = 0x1670e
+ Applet Atom = 0x2b306
+ Area Atom = 0x2fa04
+ Article Atom = 0x38807
+ Aside Atom = 0x8305
+ Async Atom = 0x7b05
+ Audio Atom = 0xa605
+ Autocomplete Atom = 0x1fc0c
+ Autofocus Atom = 0xb309
+ Autoplay Atom = 0xce08
+ B Atom = 0x101
+ Base Atom = 0xd604
+ Basefont Atom = 0xd608
+ Bdi Atom = 0x1a03
+ Bdo Atom = 0xe703
+ Bgsound Atom = 0x11807
+ Big Atom = 0x12403
+ Blink Atom = 0x12705
+ Blockquote Atom = 0x12c0a
+ Body Atom = 0x2f04
+ Br Atom = 0x202
+ Button Atom = 0x13606
+ Canvas Atom = 0x7f06
+ Caption Atom = 0x1bb07
+ Center Atom = 0x5b506
+ Challenge Atom = 0x21f09
+ Charset Atom = 0x2807
+ Checked Atom = 0x32807
+ Cite Atom = 0x3c804
+ Class Atom = 0x4de05
+ Code Atom = 0x14904
+ Col Atom = 0x15003
+ Colgroup Atom = 0x15008
+ Color Atom = 0x15d05
+ Cols Atom = 0x16204
+ Colspan Atom = 0x16207
+ Command Atom = 0x17507
+ Content Atom = 0x42307
+ Contenteditable Atom = 0x4230f
+ Contextmenu Atom = 0x3310b
+ Controls Atom = 0x18808
+ Coords Atom = 0x19406
+ Crossorigin Atom = 0x19f0b
+ Data Atom = 0x44a04
+ Datalist Atom = 0x44a08
+ Datetime Atom = 0x23c08
+ Dd Atom = 0x26702
+ Default Atom = 0x8607
+ Defer Atom = 0x14b05
+ Del Atom = 0x3ef03
+ Desc Atom = 0x4db04
+ Details Atom = 0x4807
+ Dfn Atom = 0x6103
+ Dialog Atom = 0x1b06
+ Dir Atom = 0x6903
+ Dirname Atom = 0x6907
+ Disabled Atom = 0x10c08
+ Div Atom = 0x11303
+ Dl Atom = 0x11e02
+ Download Atom = 0x40008
+ Draggable Atom = 0x17b09
+ Dropzone Atom = 0x39108
+ Dt Atom = 0x50902
+ Em Atom = 0x6502
+ Embed Atom = 0x6505
+ Enctype Atom = 0x21107
+ Face Atom = 0x5b304
+ Fieldset Atom = 0x1b008
+ Figcaption Atom = 0x1b80a
+ Figure Atom = 0x1cc06
+ Font Atom = 0xda04
+ Footer Atom = 0x8d06
+ For Atom = 0x1d803
+ ForeignObject Atom = 0x1d80d
+ Foreignobject Atom = 0x1e50d
+ Form Atom = 0x1f204
+ Formaction Atom = 0x1f20a
+ Formenctype Atom = 0x20d0b
+ Formmethod Atom = 0x2280a
+ Formnovalidate Atom = 0x2320e
+ Formtarget Atom = 0x2470a
+ Frame Atom = 0x9a05
+ Frameset Atom = 0x9a08
+ H1 Atom = 0x26e02
+ H2 Atom = 0x29402
+ H3 Atom = 0x2a702
+ H4 Atom = 0x2e902
+ H5 Atom = 0x2f302
+ H6 Atom = 0x50b02
+ Head Atom = 0x2d504
+ Header Atom = 0x2d506
+ Headers Atom = 0x2d507
+ Height Atom = 0x25106
+ Hgroup Atom = 0x25906
+ Hidden Atom = 0x26506
+ High Atom = 0x26b04
+ Hr Atom = 0x27002
+ Href Atom = 0x27004
+ Hreflang Atom = 0x27008
+ Html Atom = 0x25504
+ HttpEquiv Atom = 0x2780a
+ I Atom = 0x601
+ Icon Atom = 0x42204
+ Id Atom = 0x8502
+ Iframe Atom = 0x29606
+ Image Atom = 0x29c05
+ Img Atom = 0x2a103
+ Input Atom = 0x3e805
+ Inputmode Atom = 0x3e809
+ Ins Atom = 0x1a803
+ Isindex Atom = 0x2a907
+ Ismap Atom = 0x2b005
+ Itemid Atom = 0x33c06
+ Itemprop Atom = 0x3c908
+ Itemref Atom = 0x5ad07
+ Itemscope Atom = 0x2b909
+ Itemtype Atom = 0x2c308
+ Kbd Atom = 0x1903
+ Keygen Atom = 0x3906
+ Keytype Atom = 0x53707
+ Kind Atom = 0x10904
+ Label Atom = 0xf005
+ Lang Atom = 0x27404
+ Legend Atom = 0x18206
+ Li Atom = 0x1202
+ Link Atom = 0x12804
+ List Atom = 0x44e04
+ Listing Atom = 0x44e07
+ Loop Atom = 0xf404
+ Low Atom = 0x11f03
+ Malignmark Atom = 0x100a
+ Manifest Atom = 0x5f108
+ Map Atom = 0x2b203
+ Mark Atom = 0x1604
+ Marquee Atom = 0x2cb07
+ Math Atom = 0x2d204
+ Max Atom = 0x2e103
+ Maxlength Atom = 0x2e109
+ Media Atom = 0x6e05
+ Mediagroup Atom = 0x6e0a
+ Menu Atom = 0x33804
+ Menuitem Atom = 0x33808
+ Meta Atom = 0x45d04
+ Meter Atom = 0x24205
+ Method Atom = 0x22c06
+ Mglyph Atom = 0x2a206
+ Mi Atom = 0x2eb02
+ Min Atom = 0x2eb03
+ Minlength Atom = 0x2eb09
+ Mn Atom = 0x23502
+ Mo Atom = 0x3ed02
+ Ms Atom = 0x2bc02
+ Mtext Atom = 0x2f505
+ Multiple Atom = 0x30308
+ Muted Atom = 0x30b05
+ Name Atom = 0x6c04
+ Nav Atom = 0x3e03
+ Nobr Atom = 0x5704
+ Noembed Atom = 0x6307
+ Noframes Atom = 0x9808
+ Noscript Atom = 0x3d208
+ Novalidate Atom = 0x2360a
+ Object Atom = 0x1ec06
+ Ol Atom = 0xc902
+ Onabort Atom = 0x13a07
+ Onafterprint Atom = 0x1c00c
+ Onautocomplete Atom = 0x1fa0e
+ Onautocompleteerror Atom = 0x1fa13
+ Onbeforeprint Atom = 0x6040d
+ Onbeforeunload Atom = 0x4e70e
+ Onblur Atom = 0xaa06
+ Oncancel Atom = 0xe908
+ Oncanplay Atom = 0x28509
+ Oncanplaythrough Atom = 0x28510
+ Onchange Atom = 0x3a708
+ Onclick Atom = 0x31007
+ Onclose Atom = 0x31707
+ Oncontextmenu Atom = 0x32f0d
+ Oncuechange Atom = 0x3420b
+ Ondblclick Atom = 0x34d0a
+ Ondrag Atom = 0x35706
+ Ondragend Atom = 0x35709
+ Ondragenter Atom = 0x3600b
+ Ondragleave Atom = 0x36b0b
+ Ondragover Atom = 0x3760a
+ Ondragstart Atom = 0x3800b
+ Ondrop Atom = 0x38f06
+ Ondurationchange Atom = 0x39f10
+ Onemptied Atom = 0x39609
+ Onended Atom = 0x3af07
+ Onerror Atom = 0x3b607
+ Onfocus Atom = 0x3bd07
+ Onhashchange Atom = 0x3da0c
+ Oninput Atom = 0x3e607
+ Oninvalid Atom = 0x3f209
+ Onkeydown Atom = 0x3fb09
+ Onkeypress Atom = 0x4080a
+ Onkeyup Atom = 0x41807
+ Onlanguagechange Atom = 0x43210
+ Onload Atom = 0x44206
+ Onloadeddata Atom = 0x4420c
+ Onloadedmetadata Atom = 0x45510
+ Onloadstart Atom = 0x46b0b
+ Onmessage Atom = 0x47609
+ Onmousedown Atom = 0x47f0b
+ Onmousemove Atom = 0x48a0b
+ Onmouseout Atom = 0x4950a
+ Onmouseover Atom = 0x4a20b
+ Onmouseup Atom = 0x4ad09
+ Onmousewheel Atom = 0x4b60c
+ Onoffline Atom = 0x4c209
+ Ononline Atom = 0x4cb08
+ Onpagehide Atom = 0x4d30a
+ Onpageshow Atom = 0x4fe0a
+ Onpause Atom = 0x50d07
+ Onplay Atom = 0x51706
+ Onplaying Atom = 0x51709
+ Onpopstate Atom = 0x5200a
+ Onprogress Atom = 0x52a0a
+ Onratechange Atom = 0x53e0c
+ Onreset Atom = 0x54a07
+ Onresize Atom = 0x55108
+ Onscroll Atom = 0x55f08
+ Onseeked Atom = 0x56708
+ Onseeking Atom = 0x56f09
+ Onselect Atom = 0x57808
+ Onshow Atom = 0x58206
+ Onsort Atom = 0x58b06
+ Onstalled Atom = 0x59509
+ Onstorage Atom = 0x59e09
+ Onsubmit Atom = 0x5a708
+ Onsuspend Atom = 0x5bb09
+ Ontimeupdate Atom = 0xdb0c
+ Ontoggle Atom = 0x5c408
+ Onunload Atom = 0x5cc08
+ Onvolumechange Atom = 0x5d40e
+ Onwaiting Atom = 0x5e209
+ Open Atom = 0x3cf04
+ Optgroup Atom = 0xf608
+ Optimum Atom = 0x5eb07
+ Option Atom = 0x60006
+ Output Atom = 0x49c06
+ P Atom = 0xc01
+ Param Atom = 0xc05
+ Pattern Atom = 0x5107
+ Ping Atom = 0x7704
+ Placeholder Atom = 0xc30b
+ Plaintext Atom = 0xfd09
+ Poster Atom = 0x15706
+ Pre Atom = 0x25e03
+ Preload Atom = 0x25e07
+ Progress Atom = 0x52c08
+ Prompt Atom = 0x5fa06
+ Public Atom = 0x41e06
+ Q Atom = 0x13101
+ Radiogroup Atom = 0x30a
+ Readonly Atom = 0x2fb08
+ Rel Atom = 0x25f03
+ Required Atom = 0x1d008
+ Reversed Atom = 0x5a08
+ Rows Atom = 0x9204
+ Rowspan Atom = 0x9207
+ Rp Atom = 0x1c602
+ Rt Atom = 0x13f02
+ Ruby Atom = 0xaf04
+ S Atom = 0x2c01
+ Samp Atom = 0x4e04
+ Sandbox Atom = 0xbb07
+ Scope Atom = 0x2bd05
+ Scoped Atom = 0x2bd06
+ Script Atom = 0x3d406
+ Seamless Atom = 0x31c08
+ Section Atom = 0x4e207
+ Select Atom = 0x57a06
+ Selected Atom = 0x57a08
+ Shape Atom = 0x4f905
+ Size Atom = 0x55504
+ Sizes Atom = 0x55505
+ Small Atom = 0x18f05
+ Sortable Atom = 0x58d08
+ Sorted Atom = 0x19906
+ Source Atom = 0x1aa06
+ Spacer Atom = 0x2db06
+ Span Atom = 0x9504
+ Spellcheck Atom = 0x3230a
+ Src Atom = 0x3c303
+ Srcdoc Atom = 0x3c306
+ Srclang Atom = 0x41107
+ Start Atom = 0x38605
+ Step Atom = 0x5f704
+ Strike Atom = 0x53306
+ Strong Atom = 0x55906
+ Style Atom = 0x61105
+ Sub Atom = 0x5a903
+ Summary Atom = 0x61607
+ Sup Atom = 0x61d03
+ Svg Atom = 0x62003
+ System Atom = 0x62306
+ Tabindex Atom = 0x46308
+ Table Atom = 0x42d05
+ Target Atom = 0x24b06
+ Tbody Atom = 0x2e05
+ Td Atom = 0x4702
+ Template Atom = 0x62608
+ Textarea Atom = 0x2f608
+ Tfoot Atom = 0x8c05
+ Th Atom = 0x22e02
+ Thead Atom = 0x2d405
+ Time Atom = 0xdd04
+ Title Atom = 0xa105
+ Tr Atom = 0x10502
+ Track Atom = 0x10505
+ Translate Atom = 0x14009
+ Tt Atom = 0x5302
+ Type Atom = 0x21404
+ Typemustmatch Atom = 0x2140d
+ U Atom = 0xb01
+ Ul Atom = 0x8a02
+ Usemap Atom = 0x51106
+ Value Atom = 0x4005
+ Var Atom = 0x11503
+ Video Atom = 0x28105
+ Wbr Atom = 0x12103
+ Width Atom = 0x50705
+ Wrap Atom = 0x58704
+ Xmp Atom = 0xc103
+)
+
+const hash0 = 0xc17da63e
+
+const maxAtomLen = 19
+
+var table = [1 << 9]Atom{
+ 0x1: 0x48a0b, // onmousemove
+ 0x2: 0x5e209, // onwaiting
+ 0x3: 0x1fa13, // onautocompleteerror
+ 0x4: 0x5fa06, // prompt
+ 0x7: 0x5eb07, // optimum
+ 0x8: 0x1604, // mark
+ 0xa: 0x5ad07, // itemref
+ 0xb: 0x4fe0a, // onpageshow
+ 0xc: 0x57a06, // select
+ 0xd: 0x17b09, // draggable
+ 0xe: 0x3e03, // nav
+ 0xf: 0x17507, // command
+ 0x11: 0xb01, // u
+ 0x14: 0x2d507, // headers
+ 0x15: 0x44a08, // datalist
+ 0x17: 0x4e04, // samp
+ 0x1a: 0x3fb09, // onkeydown
+ 0x1b: 0x55f08, // onscroll
+ 0x1c: 0x15003, // col
+ 0x20: 0x3c908, // itemprop
+ 0x21: 0x2780a, // http-equiv
+ 0x22: 0x61d03, // sup
+ 0x24: 0x1d008, // required
+ 0x2b: 0x25e07, // preload
+ 0x2c: 0x6040d, // onbeforeprint
+ 0x2d: 0x3600b, // ondragenter
+ 0x2e: 0x50902, // dt
+ 0x2f: 0x5a708, // onsubmit
+ 0x30: 0x27002, // hr
+ 0x31: 0x32f0d, // oncontextmenu
+ 0x33: 0x29c05, // image
+ 0x34: 0x50d07, // onpause
+ 0x35: 0x25906, // hgroup
+ 0x36: 0x7704, // ping
+ 0x37: 0x57808, // onselect
+ 0x3a: 0x11303, // div
+ 0x3b: 0x1fa0e, // onautocomplete
+ 0x40: 0x2eb02, // mi
+ 0x41: 0x31c08, // seamless
+ 0x42: 0x2807, // charset
+ 0x43: 0x8502, // id
+ 0x44: 0x5200a, // onpopstate
+ 0x45: 0x3ef03, // del
+ 0x46: 0x2cb07, // marquee
+ 0x47: 0x3309, // accesskey
+ 0x49: 0x8d06, // footer
+ 0x4a: 0x44e04, // list
+ 0x4b: 0x2b005, // ismap
+ 0x51: 0x33804, // menu
+ 0x52: 0x2f04, // body
+ 0x55: 0x9a08, // frameset
+ 0x56: 0x54a07, // onreset
+ 0x57: 0x12705, // blink
+ 0x58: 0xa105, // title
+ 0x59: 0x38807, // article
+ 0x5b: 0x22e02, // th
+ 0x5d: 0x13101, // q
+ 0x5e: 0x3cf04, // open
+ 0x5f: 0x2fa04, // area
+ 0x61: 0x44206, // onload
+ 0x62: 0xda04, // font
+ 0x63: 0xd604, // base
+ 0x64: 0x16207, // colspan
+ 0x65: 0x53707, // keytype
+ 0x66: 0x11e02, // dl
+ 0x68: 0x1b008, // fieldset
+ 0x6a: 0x2eb03, // min
+ 0x6b: 0x11503, // var
+ 0x6f: 0x2d506, // header
+ 0x70: 0x13f02, // rt
+ 0x71: 0x15008, // colgroup
+ 0x72: 0x23502, // mn
+ 0x74: 0x13a07, // onabort
+ 0x75: 0x3906, // keygen
+ 0x76: 0x4c209, // onoffline
+ 0x77: 0x21f09, // challenge
+ 0x78: 0x2b203, // map
+ 0x7a: 0x2e902, // h4
+ 0x7b: 0x3b607, // onerror
+ 0x7c: 0x2e109, // maxlength
+ 0x7d: 0x2f505, // mtext
+ 0x7e: 0xbb07, // sandbox
+ 0x7f: 0x58b06, // onsort
+ 0x80: 0x100a, // malignmark
+ 0x81: 0x45d04, // meta
+ 0x82: 0x7b05, // async
+ 0x83: 0x2a702, // h3
+ 0x84: 0x26702, // dd
+ 0x85: 0x27004, // href
+ 0x86: 0x6e0a, // mediagroup
+ 0x87: 0x19406, // coords
+ 0x88: 0x41107, // srclang
+ 0x89: 0x34d0a, // ondblclick
+ 0x8a: 0x4005, // value
+ 0x8c: 0xe908, // oncancel
+ 0x8e: 0x3230a, // spellcheck
+ 0x8f: 0x9a05, // frame
+ 0x91: 0x12403, // big
+ 0x94: 0x1f606, // action
+ 0x95: 0x6903, // dir
+ 0x97: 0x2fb08, // readonly
+ 0x99: 0x42d05, // table
+ 0x9a: 0x61607, // summary
+ 0x9b: 0x12103, // wbr
+ 0x9c: 0x30a, // radiogroup
+ 0x9d: 0x6c04, // name
+ 0x9f: 0x62306, // system
+ 0xa1: 0x15d05, // color
+ 0xa2: 0x7f06, // canvas
+ 0xa3: 0x25504, // html
+ 0xa5: 0x56f09, // onseeking
+ 0xac: 0x4f905, // shape
+ 0xad: 0x25f03, // rel
+ 0xae: 0x28510, // oncanplaythrough
+ 0xaf: 0x3760a, // ondragover
+ 0xb0: 0x62608, // template
+ 0xb1: 0x1d80d, // foreignObject
+ 0xb3: 0x9204, // rows
+ 0xb6: 0x44e07, // listing
+ 0xb7: 0x49c06, // output
+ 0xb9: 0x3310b, // contextmenu
+ 0xbb: 0x11f03, // low
+ 0xbc: 0x1c602, // rp
+ 0xbd: 0x5bb09, // onsuspend
+ 0xbe: 0x13606, // button
+ 0xbf: 0x4db04, // desc
+ 0xc1: 0x4e207, // section
+ 0xc2: 0x52a0a, // onprogress
+ 0xc3: 0x59e09, // onstorage
+ 0xc4: 0x2d204, // math
+ 0xc5: 0x4503, // alt
+ 0xc7: 0x8a02, // ul
+ 0xc8: 0x5107, // pattern
+ 0xc9: 0x4b60c, // onmousewheel
+ 0xca: 0x35709, // ondragend
+ 0xcb: 0xaf04, // ruby
+ 0xcc: 0xc01, // p
+ 0xcd: 0x31707, // onclose
+ 0xce: 0x24205, // meter
+ 0xcf: 0x11807, // bgsound
+ 0xd2: 0x25106, // height
+ 0xd4: 0x101, // b
+ 0xd5: 0x2c308, // itemtype
+ 0xd8: 0x1bb07, // caption
+ 0xd9: 0x10c08, // disabled
+ 0xdb: 0x33808, // menuitem
+ 0xdc: 0x62003, // svg
+ 0xdd: 0x18f05, // small
+ 0xde: 0x44a04, // data
+ 0xe0: 0x4cb08, // ononline
+ 0xe1: 0x2a206, // mglyph
+ 0xe3: 0x6505, // embed
+ 0xe4: 0x10502, // tr
+ 0xe5: 0x46b0b, // onloadstart
+ 0xe7: 0x3c306, // srcdoc
+ 0xeb: 0x5c408, // ontoggle
+ 0xed: 0xe703, // bdo
+ 0xee: 0x4702, // td
+ 0xef: 0x8305, // aside
+ 0xf0: 0x29402, // h2
+ 0xf1: 0x52c08, // progress
+ 0xf2: 0x12c0a, // blockquote
+ 0xf4: 0xf005, // label
+ 0xf5: 0x601, // i
+ 0xf7: 0x9207, // rowspan
+ 0xfb: 0x51709, // onplaying
+ 0xfd: 0x2a103, // img
+ 0xfe: 0xf608, // optgroup
+ 0xff: 0x42307, // content
+ 0x101: 0x53e0c, // onratechange
+ 0x103: 0x3da0c, // onhashchange
+ 0x104: 0x4807, // details
+ 0x106: 0x40008, // download
+ 0x109: 0x14009, // translate
+ 0x10b: 0x4230f, // contenteditable
+ 0x10d: 0x36b0b, // ondragleave
+ 0x10e: 0x2106, // accept
+ 0x10f: 0x57a08, // selected
+ 0x112: 0x1f20a, // formaction
+ 0x113: 0x5b506, // center
+ 0x115: 0x45510, // onloadedmetadata
+ 0x116: 0x12804, // link
+ 0x117: 0xdd04, // time
+ 0x118: 0x19f0b, // crossorigin
+ 0x119: 0x3bd07, // onfocus
+ 0x11a: 0x58704, // wrap
+ 0x11b: 0x42204, // icon
+ 0x11d: 0x28105, // video
+ 0x11e: 0x4de05, // class
+ 0x121: 0x5d40e, // onvolumechange
+ 0x122: 0xaa06, // onblur
+ 0x123: 0x2b909, // itemscope
+ 0x124: 0x61105, // style
+ 0x127: 0x41e06, // public
+ 0x129: 0x2320e, // formnovalidate
+ 0x12a: 0x58206, // onshow
+ 0x12c: 0x51706, // onplay
+ 0x12d: 0x3c804, // cite
+ 0x12e: 0x2bc02, // ms
+ 0x12f: 0xdb0c, // ontimeupdate
+ 0x130: 0x10904, // kind
+ 0x131: 0x2470a, // formtarget
+ 0x135: 0x3af07, // onended
+ 0x136: 0x26506, // hidden
+ 0x137: 0x2c01, // s
+ 0x139: 0x2280a, // formmethod
+ 0x13a: 0x3e805, // input
+ 0x13c: 0x50b02, // h6
+ 0x13d: 0xc902, // ol
+ 0x13e: 0x3420b, // oncuechange
+ 0x13f: 0x1e50d, // foreignobject
+ 0x143: 0x4e70e, // onbeforeunload
+ 0x144: 0x2bd05, // scope
+ 0x145: 0x39609, // onemptied
+ 0x146: 0x14b05, // defer
+ 0x147: 0xc103, // xmp
+ 0x148: 0x39f10, // ondurationchange
+ 0x149: 0x1903, // kbd
+ 0x14c: 0x47609, // onmessage
+ 0x14d: 0x60006, // option
+ 0x14e: 0x2eb09, // minlength
+ 0x14f: 0x32807, // checked
+ 0x150: 0xce08, // autoplay
+ 0x152: 0x202, // br
+ 0x153: 0x2360a, // novalidate
+ 0x156: 0x6307, // noembed
+ 0x159: 0x31007, // onclick
+ 0x15a: 0x47f0b, // onmousedown
+ 0x15b: 0x3a708, // onchange
+ 0x15e: 0x3f209, // oninvalid
+ 0x15f: 0x2bd06, // scoped
+ 0x160: 0x18808, // controls
+ 0x161: 0x30b05, // muted
+ 0x162: 0x58d08, // sortable
+ 0x163: 0x51106, // usemap
+ 0x164: 0x1b80a, // figcaption
+ 0x165: 0x35706, // ondrag
+ 0x166: 0x26b04, // high
+ 0x168: 0x3c303, // src
+ 0x169: 0x15706, // poster
+ 0x16b: 0x1670e, // annotation-xml
+ 0x16c: 0x5f704, // step
+ 0x16d: 0x4, // abbr
+ 0x16e: 0x1b06, // dialog
+ 0x170: 0x1202, // li
+ 0x172: 0x3ed02, // mo
+ 0x175: 0x1d803, // for
+ 0x176: 0x1a803, // ins
+ 0x178: 0x55504, // size
+ 0x179: 0x43210, // onlanguagechange
+ 0x17a: 0x8607, // default
+ 0x17b: 0x1a03, // bdi
+ 0x17c: 0x4d30a, // onpagehide
+ 0x17d: 0x6907, // dirname
+ 0x17e: 0x21404, // type
+ 0x17f: 0x1f204, // form
+ 0x181: 0x28509, // oncanplay
+ 0x182: 0x6103, // dfn
+ 0x183: 0x46308, // tabindex
+ 0x186: 0x6502, // em
+ 0x187: 0x27404, // lang
+ 0x189: 0x39108, // dropzone
+ 0x18a: 0x4080a, // onkeypress
+ 0x18b: 0x23c08, // datetime
+ 0x18c: 0x16204, // cols
+ 0x18d: 0x1, // a
+ 0x18e: 0x4420c, // onloadeddata
+ 0x190: 0xa605, // audio
+ 0x192: 0x2e05, // tbody
+ 0x193: 0x22c06, // method
+ 0x195: 0xf404, // loop
+ 0x196: 0x29606, // iframe
+ 0x198: 0x2d504, // head
+ 0x19e: 0x5f108, // manifest
+ 0x19f: 0xb309, // autofocus
+ 0x1a0: 0x14904, // code
+ 0x1a1: 0x55906, // strong
+ 0x1a2: 0x30308, // multiple
+ 0x1a3: 0xc05, // param
+ 0x1a6: 0x21107, // enctype
+ 0x1a7: 0x5b304, // face
+ 0x1a8: 0xfd09, // plaintext
+ 0x1a9: 0x26e02, // h1
+ 0x1aa: 0x59509, // onstalled
+ 0x1ad: 0x3d406, // script
+ 0x1ae: 0x2db06, // spacer
+ 0x1af: 0x55108, // onresize
+ 0x1b0: 0x4a20b, // onmouseover
+ 0x1b1: 0x5cc08, // onunload
+ 0x1b2: 0x56708, // onseeked
+ 0x1b4: 0x2140d, // typemustmatch
+ 0x1b5: 0x1cc06, // figure
+ 0x1b6: 0x4950a, // onmouseout
+ 0x1b7: 0x25e03, // pre
+ 0x1b8: 0x50705, // width
+ 0x1b9: 0x19906, // sorted
+ 0x1bb: 0x5704, // nobr
+ 0x1be: 0x5302, // tt
+ 0x1bf: 0x1105, // align
+ 0x1c0: 0x3e607, // oninput
+ 0x1c3: 0x41807, // onkeyup
+ 0x1c6: 0x1c00c, // onafterprint
+ 0x1c7: 0x210e, // accept-charset
+ 0x1c8: 0x33c06, // itemid
+ 0x1c9: 0x3e809, // inputmode
+ 0x1cb: 0x53306, // strike
+ 0x1cc: 0x5a903, // sub
+ 0x1cd: 0x10505, // track
+ 0x1ce: 0x38605, // start
+ 0x1d0: 0xd608, // basefont
+ 0x1d6: 0x1aa06, // source
+ 0x1d7: 0x18206, // legend
+ 0x1d8: 0x2d405, // thead
+ 0x1da: 0x8c05, // tfoot
+ 0x1dd: 0x1ec06, // object
+ 0x1de: 0x6e05, // media
+ 0x1df: 0x1670a, // annotation
+ 0x1e0: 0x20d0b, // formenctype
+ 0x1e2: 0x3d208, // noscript
+ 0x1e4: 0x55505, // sizes
+ 0x1e5: 0x1fc0c, // autocomplete
+ 0x1e6: 0x9504, // span
+ 0x1e7: 0x9808, // noframes
+ 0x1e8: 0x24b06, // target
+ 0x1e9: 0x38f06, // ondrop
+ 0x1ea: 0x2b306, // applet
+ 0x1ec: 0x5a08, // reversed
+ 0x1f0: 0x2a907, // isindex
+ 0x1f3: 0x27008, // hreflang
+ 0x1f5: 0x2f302, // h5
+ 0x1f6: 0x4f307, // address
+ 0x1fa: 0x2e103, // max
+ 0x1fb: 0xc30b, // placeholder
+ 0x1fc: 0x2f608, // textarea
+ 0x1fe: 0x4ad09, // onmouseup
+ 0x1ff: 0x3800b, // ondragstart
+}
+
+const atomText = "abbradiogrouparamalignmarkbdialogaccept-charsetbodyaccesskey" +
+ "genavaluealtdetailsampatternobreversedfnoembedirnamediagroup" +
+ "ingasyncanvasidefaultfooterowspanoframesetitleaudionblurubya" +
+ "utofocusandboxmplaceholderautoplaybasefontimeupdatebdoncance" +
+ "labelooptgrouplaintextrackindisabledivarbgsoundlowbrbigblink" +
+ "blockquotebuttonabortranslatecodefercolgroupostercolorcolspa" +
+ "nnotation-xmlcommandraggablegendcontrolsmallcoordsortedcross" +
+ "originsourcefieldsetfigcaptionafterprintfigurequiredforeignO" +
+ "bjectforeignobjectformactionautocompleteerrorformenctypemust" +
+ "matchallengeformmethodformnovalidatetimeterformtargetheightm" +
+ "lhgroupreloadhiddenhigh1hreflanghttp-equivideoncanplaythroug" +
+ "h2iframeimageimglyph3isindexismappletitemscopeditemtypemarqu" +
+ "eematheaderspacermaxlength4minlength5mtextareadonlymultiplem" +
+ "utedonclickoncloseamlesspellcheckedoncontextmenuitemidoncuec" +
+ "hangeondblclickondragendondragenterondragleaveondragoverondr" +
+ "agstarticleondropzonemptiedondurationchangeonendedonerroronf" +
+ "ocusrcdocitempropenoscriptonhashchangeoninputmodeloninvalido" +
+ "nkeydownloadonkeypressrclangonkeyupublicontenteditableonlang" +
+ "uagechangeonloadeddatalistingonloadedmetadatabindexonloadsta" +
+ "rtonmessageonmousedownonmousemoveonmouseoutputonmouseoveronm" +
+ "ouseuponmousewheelonofflineononlineonpagehidesclassectionbef" +
+ "oreunloaddresshapeonpageshowidth6onpausemaponplayingonpopsta" +
+ "teonprogresstrikeytypeonratechangeonresetonresizestrongonscr" +
+ "ollonseekedonseekingonselectedonshowraponsortableonstalledon" +
+ "storageonsubmitemrefacenteronsuspendontoggleonunloadonvolume" +
+ "changeonwaitingoptimumanifestepromptoptionbeforeprintstylesu" +
+ "mmarysupsvgsystemplate"
diff --git a/html/atom/table_test.go b/html/atom/table_test.go
new file mode 100644
index 0000000..0f2ecce
--- /dev/null
+++ b/html/atom/table_test.go
@@ -0,0 +1,351 @@
+// generated by go run gen.go -test; DO NOT EDIT
+
+package atom
+
+var testAtomList = []string{
+ "a",
+ "abbr",
+ "abbr",
+ "accept",
+ "accept-charset",
+ "accesskey",
+ "action",
+ "address",
+ "align",
+ "alt",
+ "annotation",
+ "annotation-xml",
+ "applet",
+ "area",
+ "article",
+ "aside",
+ "async",
+ "audio",
+ "autocomplete",
+ "autofocus",
+ "autoplay",
+ "b",
+ "base",
+ "basefont",
+ "bdi",
+ "bdo",
+ "bgsound",
+ "big",
+ "blink",
+ "blockquote",
+ "body",
+ "br",
+ "button",
+ "canvas",
+ "caption",
+ "center",
+ "challenge",
+ "charset",
+ "checked",
+ "cite",
+ "cite",
+ "class",
+ "code",
+ "col",
+ "colgroup",
+ "color",
+ "cols",
+ "colspan",
+ "command",
+ "command",
+ "content",
+ "contenteditable",
+ "contextmenu",
+ "controls",
+ "coords",
+ "crossorigin",
+ "data",
+ "data",
+ "datalist",
+ "datetime",
+ "dd",
+ "default",
+ "defer",
+ "del",
+ "desc",
+ "details",
+ "dfn",
+ "dialog",
+ "dir",
+ "dirname",
+ "disabled",
+ "div",
+ "dl",
+ "download",
+ "draggable",
+ "dropzone",
+ "dt",
+ "em",
+ "embed",
+ "enctype",
+ "face",
+ "fieldset",
+ "figcaption",
+ "figure",
+ "font",
+ "footer",
+ "for",
+ "foreignObject",
+ "foreignobject",
+ "form",
+ "form",
+ "formaction",
+ "formenctype",
+ "formmethod",
+ "formnovalidate",
+ "formtarget",
+ "frame",
+ "frameset",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "head",
+ "header",
+ "headers",
+ "height",
+ "hgroup",
+ "hidden",
+ "high",
+ "hr",
+ "href",
+ "hreflang",
+ "html",
+ "http-equiv",
+ "i",
+ "icon",
+ "id",
+ "iframe",
+ "image",
+ "img",
+ "input",
+ "inputmode",
+ "ins",
+ "isindex",
+ "ismap",
+ "itemid",
+ "itemprop",
+ "itemref",
+ "itemscope",
+ "itemtype",
+ "kbd",
+ "keygen",
+ "keytype",
+ "kind",
+ "label",
+ "label",
+ "lang",
+ "legend",
+ "li",
+ "link",
+ "list",
+ "listing",
+ "loop",
+ "low",
+ "malignmark",
+ "manifest",
+ "map",
+ "mark",
+ "marquee",
+ "math",
+ "max",
+ "maxlength",
+ "media",
+ "mediagroup",
+ "menu",
+ "menuitem",
+ "meta",
+ "meter",
+ "method",
+ "mglyph",
+ "mi",
+ "min",
+ "minlength",
+ "mn",
+ "mo",
+ "ms",
+ "mtext",
+ "multiple",
+ "muted",
+ "name",
+ "nav",
+ "nobr",
+ "noembed",
+ "noframes",
+ "noscript",
+ "novalidate",
+ "object",
+ "ol",
+ "onabort",
+ "onafterprint",
+ "onautocomplete",
+ "onautocompleteerror",
+ "onbeforeprint",
+ "onbeforeunload",
+ "onblur",
+ "oncancel",
+ "oncanplay",
+ "oncanplaythrough",
+ "onchange",
+ "onclick",
+ "onclose",
+ "oncontextmenu",
+ "oncuechange",
+ "ondblclick",
+ "ondrag",
+ "ondragend",
+ "ondragenter",
+ "ondragleave",
+ "ondragover",
+ "ondragstart",
+ "ondrop",
+ "ondurationchange",
+ "onemptied",
+ "onended",
+ "onerror",
+ "onfocus",
+ "onhashchange",
+ "oninput",
+ "oninvalid",
+ "onkeydown",
+ "onkeypress",
+ "onkeyup",
+ "onlanguagechange",
+ "onload",
+ "onloadeddata",
+ "onloadedmetadata",
+ "onloadstart",
+ "onmessage",
+ "onmousedown",
+ "onmousemove",
+ "onmouseout",
+ "onmouseover",
+ "onmouseup",
+ "onmousewheel",
+ "onoffline",
+ "ononline",
+ "onpagehide",
+ "onpageshow",
+ "onpause",
+ "onplay",
+ "onplaying",
+ "onpopstate",
+ "onprogress",
+ "onratechange",
+ "onreset",
+ "onresize",
+ "onscroll",
+ "onseeked",
+ "onseeking",
+ "onselect",
+ "onshow",
+ "onsort",
+ "onstalled",
+ "onstorage",
+ "onsubmit",
+ "onsuspend",
+ "ontimeupdate",
+ "ontoggle",
+ "onunload",
+ "onvolumechange",
+ "onwaiting",
+ "open",
+ "optgroup",
+ "optimum",
+ "option",
+ "output",
+ "p",
+ "param",
+ "pattern",
+ "ping",
+ "placeholder",
+ "plaintext",
+ "poster",
+ "pre",
+ "preload",
+ "progress",
+ "prompt",
+ "public",
+ "q",
+ "radiogroup",
+ "readonly",
+ "rel",
+ "required",
+ "reversed",
+ "rows",
+ "rowspan",
+ "rp",
+ "rt",
+ "ruby",
+ "s",
+ "samp",
+ "sandbox",
+ "scope",
+ "scoped",
+ "script",
+ "seamless",
+ "section",
+ "select",
+ "selected",
+ "shape",
+ "size",
+ "sizes",
+ "small",
+ "sortable",
+ "sorted",
+ "source",
+ "spacer",
+ "span",
+ "span",
+ "spellcheck",
+ "src",
+ "srcdoc",
+ "srclang",
+ "start",
+ "step",
+ "strike",
+ "strong",
+ "style",
+ "style",
+ "sub",
+ "summary",
+ "sup",
+ "svg",
+ "system",
+ "tabindex",
+ "table",
+ "target",
+ "tbody",
+ "td",
+ "template",
+ "textarea",
+ "tfoot",
+ "th",
+ "thead",
+ "time",
+ "title",
+ "title",
+ "tr",
+ "track",
+ "translate",
+ "tt",
+ "type",
+ "typemustmatch",
+ "u",
+ "ul",
+ "usemap",
+ "value",
+ "var",
+ "video",
+ "wbr",
+ "width",
+ "wrap",
+ "xmp",
+}
diff --git a/html/charset/charset.go b/html/charset/charset.go
new file mode 100644
index 0000000..464c821
--- /dev/null
+++ b/html/charset/charset.go
@@ -0,0 +1,244 @@
+// Copyright 2013 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.
+
+// Package charset provides common text encodings for HTML documents.
+//
+// The mapping from encoding labels to encodings is defined at
+// https://encoding.spec.whatwg.org/.
+package charset // import "golang.org/x/net/html/charset"
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "mime"
+ "strings"
+ "unicode/utf8"
+
+ "golang.org/x/net/html"
+ "golang.org/x/text/encoding"
+ "golang.org/x/text/encoding/charmap"
+ "golang.org/x/text/transform"
+)
+
+// Lookup returns the encoding with the specified label, and its canonical
+// name. It returns nil and the empty string if label is not one of the
+// standard encodings for HTML. Matching is case-insensitive and ignores
+// leading and trailing whitespace.
+func Lookup(label string) (e encoding.Encoding, name string) {
+ label = strings.ToLower(strings.Trim(label, "\t\n\r\f "))
+ enc := encodings[label]
+ return enc.e, enc.name
+}
+
+// DetermineEncoding determines the encoding of an HTML document by examining
+// up to the first 1024 bytes of content and the declared Content-Type.
+//
+// See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding
+func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) {
+ if len(content) > 1024 {
+ content = content[:1024]
+ }
+
+ for _, b := range boms {
+ if bytes.HasPrefix(content, b.bom) {
+ e, name = Lookup(b.enc)
+ return e, name, true
+ }
+ }
+
+ if _, params, err := mime.ParseMediaType(contentType); err == nil {
+ if cs, ok := params["charset"]; ok {
+ if e, name = Lookup(cs); e != nil {
+ return e, name, true
+ }
+ }
+ }
+
+ if len(content) > 0 {
+ e, name = prescan(content)
+ if e != nil {
+ return e, name, false
+ }
+ }
+
+ // Try to detect UTF-8.
+ // First eliminate any partial rune at the end.
+ for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- {
+ b := content[i]
+ if b < 0x80 {
+ break
+ }
+ if utf8.RuneStart(b) {
+ content = content[:i]
+ break
+ }
+ }
+ hasHighBit := false
+ for _, c := range content {
+ if c >= 0x80 {
+ hasHighBit = true
+ break
+ }
+ }
+ if hasHighBit && utf8.Valid(content) {
+ return encoding.Nop, "utf-8", false
+ }
+
+ // TODO: change default depending on user's locale?
+ return charmap.Windows1252, "windows-1252", false
+}
+
+// NewReader returns an io.Reader that converts the content of r to UTF-8.
+// It calls DetermineEncoding to find out what r's encoding is.
+func NewReader(r io.Reader, contentType string) (io.Reader, error) {
+ preview := make([]byte, 1024)
+ n, err := io.ReadFull(r, preview)
+ switch {
+ case err == io.ErrUnexpectedEOF:
+ preview = preview[:n]
+ r = bytes.NewReader(preview)
+ case err != nil:
+ return nil, err
+ default:
+ r = io.MultiReader(bytes.NewReader(preview), r)
+ }
+
+ if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop {
+ r = transform.NewReader(r, e.NewDecoder())
+ }
+ return r, nil
+}
+
+// NewReaderLabel returns a reader that converts from the specified charset to
+// UTF-8. It uses Lookup to find the encoding that corresponds to label, and
+// returns an error if Lookup returns nil. It is suitable for use as
+// encoding/xml.Decoder's CharsetReader function.
+func NewReaderLabel(label string, input io.Reader) (io.Reader, error) {
+ e, _ := Lookup(label)
+ if e == nil {
+ return nil, fmt.Errorf("unsupported charset: %q", label)
+ }
+ return transform.NewReader(input, e.NewDecoder()), nil
+}
+
+func prescan(content []byte) (e encoding.Encoding, name string) {
+ z := html.NewTokenizer(bytes.NewReader(content))
+ for {
+ switch z.Next() {
+ case html.ErrorToken:
+ return nil, ""
+
+ case html.StartTagToken, html.SelfClosingTagToken:
+ tagName, hasAttr := z.TagName()
+ if !bytes.Equal(tagName, []byte("meta")) {
+ continue
+ }
+ attrList := make(map[string]bool)
+ gotPragma := false
+
+ const (
+ dontKnow = iota
+ doNeedPragma
+ doNotNeedPragma
+ )
+ needPragma := dontKnow
+
+ name = ""
+ e = nil
+ for hasAttr {
+ var key, val []byte
+ key, val, hasAttr = z.TagAttr()
+ ks := string(key)
+ if attrList[ks] {
+ continue
+ }
+ attrList[ks] = true
+ for i, c := range val {
+ if 'A' <= c && c <= 'Z' {
+ val[i] = c + 0x20
+ }
+ }
+
+ switch ks {
+ case "http-equiv":
+ if bytes.Equal(val, []byte("content-type")) {
+ gotPragma = true
+ }
+
+ case "content":
+ if e == nil {
+ name = fromMetaElement(string(val))
+ if name != "" {
+ e, name = Lookup(name)
+ if e != nil {
+ needPragma = doNeedPragma
+ }
+ }
+ }
+
+ case "charset":
+ e, name = Lookup(string(val))
+ needPragma = doNotNeedPragma
+ }
+ }
+
+ if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma {
+ continue
+ }
+
+ if strings.HasPrefix(name, "utf-16") {
+ name = "utf-8"
+ e = encoding.Nop
+ }
+
+ if e != nil {
+ return e, name
+ }
+ }
+ }
+}
+
+func fromMetaElement(s string) string {
+ for s != "" {
+ csLoc := strings.Index(s, "charset")
+ if csLoc == -1 {
+ return ""
+ }
+ s = s[csLoc+len("charset"):]
+ s = strings.TrimLeft(s, " \t\n\f\r")
+ if !strings.HasPrefix(s, "=") {
+ continue
+ }
+ s = s[1:]
+ s = strings.TrimLeft(s, " \t\n\f\r")
+ if s == "" {
+ return ""
+ }
+ if q := s[0]; q == '"' || q == '\'' {
+ s = s[1:]
+ closeQuote := strings.IndexRune(s, rune(q))
+ if closeQuote == -1 {
+ return ""
+ }
+ return s[:closeQuote]
+ }
+
+ end := strings.IndexAny(s, "; \t\n\f\r")
+ if end == -1 {
+ end = len(s)
+ }
+ return s[:end]
+ }
+ return ""
+}
+
+var boms = []struct {
+ bom []byte
+ enc string
+}{
+ {[]byte{0xfe, 0xff}, "utf-16be"},
+ {[]byte{0xff, 0xfe}, "utf-16le"},
+ {[]byte{0xef, 0xbb, 0xbf}, "utf-8"},
+}
diff --git a/html/charset/charset_test.go b/html/charset/charset_test.go
new file mode 100644
index 0000000..8b10399
--- /dev/null
+++ b/html/charset/charset_test.go
@@ -0,0 +1,236 @@
+// Copyright 2013 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.
+
+package charset
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io/ioutil"
+ "runtime"
+ "strings"
+ "testing"
+
+ "golang.org/x/text/transform"
+)
+
+func transformString(t transform.Transformer, s string) (string, error) {
+ r := transform.NewReader(strings.NewReader(s), t)
+ b, err := ioutil.ReadAll(r)
+ return string(b), err
+}
+
+var testCases = []struct {
+ utf8, other, otherEncoding string
+}{
+ {"Résumé", "Résumé", "utf8"},
+ {"Résumé", "R\xe9sum\xe9", "latin1"},
+ {"これは漢字です。", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"},
+ {"これは漢字です。", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"},
+ {"Hello, world", "Hello, world", "ASCII"},
+ {"Gdańsk", "Gda\xf1sk", "ISO-8859-2"},
+ {"Ââ Čč Đđ Ŋŋ Õõ Šš Žž Åå Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"},
+ {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"},
+ {"latviešu", "latvie\xf0u", "ISO-8859-13"},
+ {"Seònaid", "Se\xf2naid", "ISO-8859-14"},
+ {"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"},
+ {"românește", "rom\xe2ne\xbate", "ISO-8859-16"},
+ {"nutraĵo", "nutra\xbco", "ISO-8859-3"},
+ {"Kalâdlit", "Kal\xe2dlit", "ISO-8859-4"},
+ {"русский", "\xe0\xe3\xe1\xe1\xda\xd8\xd9", "ISO-8859-5"},
+ {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "ISO-8859-7"},
+ {"Kağan", "Ka\xf0an", "ISO-8859-9"},
+ {"Résumé", "R\x8esum\x8e", "macintosh"},
+ {"Gdańsk", "Gda\xf1sk", "windows-1250"},
+ {"русский", "\xf0\xf3\xf1\xf1\xea\xe8\xe9", "windows-1251"},
+ {"Résumé", "R\xe9sum\xe9", "windows-1252"},
+ {"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "windows-1253"},
+ {"Kağan", "Ka\xf0an", "windows-1254"},
+ {"עִבְרִית", "\xf2\xc4\xe1\xc0\xf8\xc4\xe9\xfa", "windows-1255"},
+ {"العربية", "\xc7\xe1\xda\xd1\xc8\xed\xc9", "windows-1256"},
+ {"latviešu", "latvie\xf0u", "windows-1257"},
+ {"Việt", "Vi\xea\xf2t", "windows-1258"},
+ {"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "windows-874"},
+ {"русский", "\xd2\xd5\xd3\xd3\xcb\xc9\xca", "KOI8-R"},
+ {"українська", "\xd5\xcb\xd2\xc1\xa7\xce\xd3\xd8\xcb\xc1", "KOI8-U"},
+ {"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
+ {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
+ {"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
+ {"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"},
+ {"㧯", "\x82\x31\x89\x38", "gb18030"},
+ {"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"},
+ {"Hello, 世界!", "Hello, \x90\xa2\x8aE!", "SJIS"},
+ {"イウエオカ", "\xb2\xb3\xb4\xb5\xb6", "SJIS"},
+ {"これは漢字です。", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"},
+ {"Hello, 世界!", "Hello, \x1b$B@$3&\x1b(B!", "ISO-2022-JP"},
+ {"네이트 | 즐거움의 시작, 슈파스(Spaβ) NATE", "\xb3\xd7\xc0\xcc\xc6\xae | \xc1\xf1\xb0\xc5\xbf\xf2\xc0\xc7 \xbd\xc3\xc0\xdb, \xbd\xb4\xc6\xc4\xbd\xba(Spa\xa5\xe2) NATE", "EUC-KR"},
+}
+
+func TestDecode(t *testing.T) {
+ for _, tc := range testCases {
+ e, _ := Lookup(tc.otherEncoding)
+ if e == nil {
+ t.Errorf("%s: not found", tc.otherEncoding)
+ continue
+ }
+ s, err := transformString(e.NewDecoder(), tc.other)
+ if err != nil {
+ t.Errorf("%s: decode %q: %v", tc.otherEncoding, tc.other, err)
+ continue
+ }
+ if s != tc.utf8 {
+ t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.utf8)
+ }
+ }
+}
+
+func TestEncode(t *testing.T) {
+ for _, tc := range testCases {
+ e, _ := Lookup(tc.otherEncoding)
+ if e == nil {
+ t.Errorf("%s: not found", tc.otherEncoding)
+ continue
+ }
+ s, err := transformString(e.NewEncoder(), tc.utf8)
+ if err != nil {
+ t.Errorf("%s: encode %q: %s", tc.otherEncoding, tc.utf8, err)
+ continue
+ }
+ if s != tc.other {
+ t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.other)
+ }
+ }
+}
+
+// TestNames verifies that you can pass an encoding's name to Lookup and get
+// the same encoding back (except for "replacement").
+func TestNames(t *testing.T) {
+ for _, e := range encodings {
+ if e.name == "replacement" {
+ continue
+ }
+ _, got := Lookup(e.name)
+ if got != e.name {
+ t.Errorf("got %q, want %q", got, e.name)
+ continue
+ }
+ }
+}
+
+var sniffTestCases = []struct {
+ filename, declared, want string
+}{
+ {"HTTP-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
+ {"UTF-16LE-BOM.html", "", "utf-16le"},
+ {"UTF-16BE-BOM.html", "", "utf-16be"},
+ {"meta-content-attribute.html", "text/html", "iso-8859-15"},
+ {"meta-charset-attribute.html", "text/html", "iso-8859-15"},
+ {"No-encoding-declaration.html", "text/html", "utf-8"},
+ {"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", "utf-8"},
+ {"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
+ {"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
+ {"UTF-8-BOM-vs-meta-content.html", "text/html", "utf-8"},
+ {"UTF-8-BOM-vs-meta-charset.html", "text/html", "utf-8"},
+}
+
+func TestSniff(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl": // platforms that don't permit direct file system access
+ t.Skipf("not supported on %q", runtime.GOOS)
+ }
+
+ for _, tc := range sniffTestCases {
+ content, err := ioutil.ReadFile("testdata/" + tc.filename)
+ if err != nil {
+ t.Errorf("%s: error reading file: %v", tc.filename, err)
+ continue
+ }
+
+ _, name, _ := DetermineEncoding(content, tc.declared)
+ if name != tc.want {
+ t.Errorf("%s: got %q, want %q", tc.filename, name, tc.want)
+ continue
+ }
+ }
+}
+
+func TestReader(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl": // platforms that don't permit direct file system access
+ t.Skipf("not supported on %q", runtime.GOOS)
+ }
+
+ for _, tc := range sniffTestCases {
+ content, err := ioutil.ReadFile("testdata/" + tc.filename)
+ if err != nil {
+ t.Errorf("%s: error reading file: %v", tc.filename, err)
+ continue
+ }
+
+ r, err := NewReader(bytes.NewReader(content), tc.declared)
+ if err != nil {
+ t.Errorf("%s: error creating reader: %v", tc.filename, err)
+ continue
+ }
+
+ got, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Errorf("%s: error reading from charset.NewReader: %v", tc.filename, err)
+ continue
+ }
+
+ e, _ := Lookup(tc.want)
+ want, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader(content), e.NewDecoder()))
+ if err != nil {
+ t.Errorf("%s: error decoding with hard-coded charset name: %v", tc.filename, err)
+ continue
+ }
+
+ if !bytes.Equal(got, want) {
+ t.Errorf("%s: got %q, want %q", tc.filename, got, want)
+ continue
+ }
+ }
+}
+
+var metaTestCases = []struct {
+ meta, want string
+}{
+ {"", ""},
+ {"text/html", ""},
+ {"text/html; charset utf-8", ""},
+ {"text/html; charset=latin-2", "latin-2"},
+ {"text/html; charset; charset = utf-8", "utf-8"},
+ {`charset="big5"`, "big5"},
+ {"charset='shift_jis'", "shift_jis"},
+}
+
+func TestFromMeta(t *testing.T) {
+ for _, tc := range metaTestCases {
+ got := fromMetaElement(tc.meta)
+ if got != tc.want {
+ t.Errorf("%q: got %q, want %q", tc.meta, got, tc.want)
+ }
+ }
+}
+
+func TestXML(t *testing.T) {
+ const s = "<?xml version=\"1.0\" encoding=\"windows-1252\"?><a><Word>r\xe9sum\xe9</Word></a>"
+
+ d := xml.NewDecoder(strings.NewReader(s))
+ d.CharsetReader = NewReaderLabel
+
+ var a struct {
+ Word string
+ }
+ err := d.Decode(&a)
+ if err != nil {
+ t.Fatalf("Decode: %v", err)
+ }
+
+ want := "résumé"
+ if a.Word != want {
+ t.Errorf("got %q, want %q", a.Word, want)
+ }
+}
diff --git a/html/charset/gen.go b/html/charset/gen.go
new file mode 100644
index 0000000..828347f
--- /dev/null
+++ b/html/charset/gen.go
@@ -0,0 +1,111 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+package main
+
+// Download https://encoding.spec.whatwg.org/encodings.json and use it to
+// generate table.go.
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "strings"
+)
+
+type enc struct {
+ Name string
+ Labels []string
+}
+
+type group struct {
+ Encodings []enc
+ Heading string
+}
+
+const specURL = "https://encoding.spec.whatwg.org/encodings.json"
+
+func main() {
+ resp, err := http.Get(specURL)
+ if err != nil {
+ log.Fatalf("error fetching %s: %s", specURL, err)
+ }
+ if resp.StatusCode != 200 {
+ log.Fatalf("error fetching %s: HTTP status %s", specURL, resp.Status)
+ }
+ defer resp.Body.Close()
+
+ var groups []group
+ d := json.NewDecoder(resp.Body)
+ err = d.Decode(&groups)
+ if err != nil {
+ log.Fatalf("error reading encodings.json: %s", err)
+ }
+
+ fmt.Println("// generated by go run gen.go; DO NOT EDIT")
+ fmt.Println()
+ fmt.Println("package charset")
+ fmt.Println()
+
+ fmt.Println("import (")
+ fmt.Println(`"golang.org/x/text/encoding"`)
+ for _, pkg := range []string{"charmap", "japanese", "korean", "simplifiedchinese", "traditionalchinese", "unicode"} {
+ fmt.Printf("\"golang.org/x/text/encoding/%s\"\n", pkg)
+ }
+ fmt.Println(")")
+ fmt.Println()
+
+ fmt.Println("var encodings = map[string]struct{e encoding.Encoding; name string} {")
+ for _, g := range groups {
+ for _, e := range g.Encodings {
+ goName, ok := miscNames[e.Name]
+ if !ok {
+ for k, v := range prefixes {
+ if strings.HasPrefix(e.Name, k) {
+ goName = v + e.Name[len(k):]
+ break
+ }
+ }
+ if goName == "" {
+ log.Fatalf("unrecognized encoding name: %s", e.Name)
+ }
+ }
+
+ for _, label := range e.Labels {
+ fmt.Printf("%q: {%s, %q},\n", label, goName, e.Name)
+ }
+ }
+ }
+ fmt.Println("}")
+}
+
+var prefixes = map[string]string{
+ "iso-8859-": "charmap.ISO8859_",
+ "windows-": "charmap.Windows",
+}
+
+var miscNames = map[string]string{
+ "utf-8": "encoding.Nop",
+ "ibm866": "charmap.CodePage866",
+ "iso-8859-8-i": "charmap.ISO8859_8",
+ "koi8-r": "charmap.KOI8R",
+ "koi8-u": "charmap.KOI8U",
+ "macintosh": "charmap.Macintosh",
+ "x-mac-cyrillic": "charmap.MacintoshCyrillic",
+ "gbk": "simplifiedchinese.GBK",
+ "gb18030": "simplifiedchinese.GB18030",
+ "hz-gb-2312": "simplifiedchinese.HZGB2312",
+ "big5": "traditionalchinese.Big5",
+ "euc-jp": "japanese.EUCJP",
+ "iso-2022-jp": "japanese.ISO2022JP",
+ "shift_jis": "japanese.ShiftJIS",
+ "euc-kr": "korean.EUCKR",
+ "replacement": "encoding.Replacement",
+ "utf-16be": "unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)",
+ "utf-16le": "unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)",
+ "x-user-defined": "charmap.XUserDefined",
+}
diff --git a/html/charset/table.go b/html/charset/table.go
new file mode 100644
index 0000000..aa0d948
--- /dev/null
+++ b/html/charset/table.go
@@ -0,0 +1,235 @@
+// generated by go run gen.go; DO NOT EDIT
+
+package charset
+
+import (
+ "golang.org/x/text/encoding"
+ "golang.org/x/text/encoding/charmap"
+ "golang.org/x/text/encoding/japanese"
+ "golang.org/x/text/encoding/korean"
+ "golang.org/x/text/encoding/simplifiedchinese"
+ "golang.org/x/text/encoding/traditionalchinese"
+ "golang.org/x/text/encoding/unicode"
+)
+
+var encodings = map[string]struct {
+ e encoding.Encoding
+ name string
+}{
+ "unicode-1-1-utf-8": {encoding.Nop, "utf-8"},
+ "utf-8": {encoding.Nop, "utf-8"},
+ "utf8": {encoding.Nop, "utf-8"},
+ "866": {charmap.CodePage866, "ibm866"},
+ "cp866": {charmap.CodePage866, "ibm866"},
+ "csibm866": {charmap.CodePage866, "ibm866"},
+ "ibm866": {charmap.CodePage866, "ibm866"},
+ "csisolatin2": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso-8859-2": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso-ir-101": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso8859-2": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso88592": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso_8859-2": {charmap.ISO8859_2, "iso-8859-2"},
+ "iso_8859-2:1987": {charmap.ISO8859_2, "iso-8859-2"},
+ "l2": {charmap.ISO8859_2, "iso-8859-2"},
+ "latin2": {charmap.ISO8859_2, "iso-8859-2"},
+ "csisolatin3": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso-8859-3": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso-ir-109": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso8859-3": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso88593": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso_8859-3": {charmap.ISO8859_3, "iso-8859-3"},
+ "iso_8859-3:1988": {charmap.ISO8859_3, "iso-8859-3"},
+ "l3": {charmap.ISO8859_3, "iso-8859-3"},
+ "latin3": {charmap.ISO8859_3, "iso-8859-3"},
+ "csisolatin4": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso-8859-4": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso-ir-110": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso8859-4": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso88594": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso_8859-4": {charmap.ISO8859_4, "iso-8859-4"},
+ "iso_8859-4:1988": {charmap.ISO8859_4, "iso-8859-4"},
+ "l4": {charmap.ISO8859_4, "iso-8859-4"},
+ "latin4": {charmap.ISO8859_4, "iso-8859-4"},
+ "csisolatincyrillic": {charmap.ISO8859_5, "iso-8859-5"},
+ "cyrillic": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso-8859-5": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso-ir-144": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso8859-5": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso88595": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso_8859-5": {charmap.ISO8859_5, "iso-8859-5"},
+ "iso_8859-5:1988": {charmap.ISO8859_5, "iso-8859-5"},
+ "arabic": {charmap.ISO8859_6, "iso-8859-6"},
+ "asmo-708": {charmap.ISO8859_6, "iso-8859-6"},
+ "csiso88596e": {charmap.ISO8859_6, "iso-8859-6"},
+ "csiso88596i": {charmap.ISO8859_6, "iso-8859-6"},
+ "csisolatinarabic": {charmap.ISO8859_6, "iso-8859-6"},
+ "ecma-114": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso-8859-6": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso-8859-6-e": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso-8859-6-i": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso-ir-127": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso8859-6": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso88596": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso_8859-6": {charmap.ISO8859_6, "iso-8859-6"},
+ "iso_8859-6:1987": {charmap.ISO8859_6, "iso-8859-6"},
+ "csisolatingreek": {charmap.ISO8859_7, "iso-8859-7"},
+ "ecma-118": {charmap.ISO8859_7, "iso-8859-7"},
+ "elot_928": {charmap.ISO8859_7, "iso-8859-7"},
+ "greek": {charmap.ISO8859_7, "iso-8859-7"},
+ "greek8": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso-8859-7": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso-ir-126": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso8859-7": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso88597": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso_8859-7": {charmap.ISO8859_7, "iso-8859-7"},
+ "iso_8859-7:1987": {charmap.ISO8859_7, "iso-8859-7"},
+ "sun_eu_greek": {charmap.ISO8859_7, "iso-8859-7"},
+ "csiso88598e": {charmap.ISO8859_8, "iso-8859-8"},
+ "csisolatinhebrew": {charmap.ISO8859_8, "iso-8859-8"},
+ "hebrew": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso-8859-8": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso-8859-8-e": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso-ir-138": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso8859-8": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso88598": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso_8859-8": {charmap.ISO8859_8, "iso-8859-8"},
+ "iso_8859-8:1988": {charmap.ISO8859_8, "iso-8859-8"},
+ "visual": {charmap.ISO8859_8, "iso-8859-8"},
+ "csiso88598i": {charmap.ISO8859_8, "iso-8859-8-i"},
+ "iso-8859-8-i": {charmap.ISO8859_8, "iso-8859-8-i"},
+ "logical": {charmap.ISO8859_8, "iso-8859-8-i"},
+ "csisolatin6": {charmap.ISO8859_10, "iso-8859-10"},
+ "iso-8859-10": {charmap.ISO8859_10, "iso-8859-10"},
+ "iso-ir-157": {charmap.ISO8859_10, "iso-8859-10"},
+ "iso8859-10": {charmap.ISO8859_10, "iso-8859-10"},
+ "iso885910": {charmap.ISO8859_10, "iso-8859-10"},
+ "l6": {charmap.ISO8859_10, "iso-8859-10"},
+ "latin6": {charmap.ISO8859_10, "iso-8859-10"},
+ "iso-8859-13": {charmap.ISO8859_13, "iso-8859-13"},
+ "iso8859-13": {charmap.ISO8859_13, "iso-8859-13"},
+ "iso885913": {charmap.ISO8859_13, "iso-8859-13"},
+ "iso-8859-14": {charmap.ISO8859_14, "iso-8859-14"},
+ "iso8859-14": {charmap.ISO8859_14, "iso-8859-14"},
+ "iso885914": {charmap.ISO8859_14, "iso-8859-14"},
+ "csisolatin9": {charmap.ISO8859_15, "iso-8859-15"},
+ "iso-8859-15": {charmap.ISO8859_15, "iso-8859-15"},
+ "iso8859-15": {charmap.ISO8859_15, "iso-8859-15"},
+ "iso885915": {charmap.ISO8859_15, "iso-8859-15"},
+ "iso_8859-15": {charmap.ISO8859_15, "iso-8859-15"},
+ "l9": {charmap.ISO8859_15, "iso-8859-15"},
+ "iso-8859-16": {charmap.ISO8859_16, "iso-8859-16"},
+ "cskoi8r": {charmap.KOI8R, "koi8-r"},
+ "koi": {charmap.KOI8R, "koi8-r"},
+ "koi8": {charmap.KOI8R, "koi8-r"},
+ "koi8-r": {charmap.KOI8R, "koi8-r"},
+ "koi8_r": {charmap.KOI8R, "koi8-r"},
+ "koi8-u": {charmap.KOI8U, "koi8-u"},
+ "csmacintosh": {charmap.Macintosh, "macintosh"},
+ "mac": {charmap.Macintosh, "macintosh"},
+ "macintosh": {charmap.Macintosh, "macintosh"},
+ "x-mac-roman": {charmap.Macintosh, "macintosh"},
+ "dos-874": {charmap.Windows874, "windows-874"},
+ "iso-8859-11": {charmap.Windows874, "windows-874"},
+ "iso8859-11": {charmap.Windows874, "windows-874"},
+ "iso885911": {charmap.Windows874, "windows-874"},
+ "tis-620": {charmap.Windows874, "windows-874"},
+ "windows-874": {charmap.Windows874, "windows-874"},
+ "cp1250": {charmap.Windows1250, "windows-1250"},
+ "windows-1250": {charmap.Windows1250, "windows-1250"},
+ "x-cp1250": {charmap.Windows1250, "windows-1250"},
+ "cp1251": {charmap.Windows1251, "windows-1251"},
+ "windows-1251": {charmap.Windows1251, "windows-1251"},
+ "x-cp1251": {charmap.Windows1251, "windows-1251"},
+ "ansi_x3.4-1968": {charmap.Windows1252, "windows-1252"},
+ "ascii": {charmap.Windows1252, "windows-1252"},
+ "cp1252": {charmap.Windows1252, "windows-1252"},
+ "cp819": {charmap.Windows1252, "windows-1252"},
+ "csisolatin1": {charmap.Windows1252, "windows-1252"},
+ "ibm819": {charmap.Windows1252, "windows-1252"},
+ "iso-8859-1": {charmap.Windows1252, "windows-1252"},
+ "iso-ir-100": {charmap.Windows1252, "windows-1252"},
+ "iso8859-1": {charmap.Windows1252, "windows-1252"},
+ "iso88591": {charmap.Windows1252, "windows-1252"},
+ "iso_8859-1": {charmap.Windows1252, "windows-1252"},
+ "iso_8859-1:1987": {charmap.Windows1252, "windows-1252"},
+ "l1": {charmap.Windows1252, "windows-1252"},
+ "latin1": {charmap.Windows1252, "windows-1252"},
+ "us-ascii": {charmap.Windows1252, "windows-1252"},
+ "windows-1252": {charmap.Windows1252, "windows-1252"},
+ "x-cp1252": {charmap.Windows1252, "windows-1252"},
+ "cp1253": {charmap.Windows1253, "windows-1253"},
+ "windows-1253": {charmap.Windows1253, "windows-1253"},
+ "x-cp1253": {charmap.Windows1253, "windows-1253"},
+ "cp1254": {charmap.Windows1254, "windows-1254"},
+ "csisolatin5": {charmap.Windows1254, "windows-1254"},
+ "iso-8859-9": {charmap.Windows1254, "windows-1254"},
+ "iso-ir-148": {charmap.Windows1254, "windows-1254"},
+ "iso8859-9": {charmap.Windows1254, "windows-1254"},
+ "iso88599": {charmap.Windows1254, "windows-1254"},
+ "iso_8859-9": {charmap.Windows1254, "windows-1254"},
+ "iso_8859-9:1989": {charmap.Windows1254, "windows-1254"},
+ "l5": {charmap.Windows1254, "windows-1254"},
+ "latin5": {charmap.Windows1254, "windows-1254"},
+ "windows-1254": {charmap.Windows1254, "windows-1254"},
+ "x-cp1254": {charmap.Windows1254, "windows-1254"},
+ "cp1255": {charmap.Windows1255, "windows-1255"},
+ "windows-1255": {charmap.Windows1255, "windows-1255"},
+ "x-cp1255": {charmap.Windows1255, "windows-1255"},
+ "cp1256": {charmap.Windows1256, "windows-1256"},
+ "windows-1256": {charmap.Windows1256, "windows-1256"},
+ "x-cp1256": {charmap.Windows1256, "windows-1256"},
+ "cp1257": {charmap.Windows1257, "windows-1257"},
+ "windows-1257": {charmap.Windows1257, "windows-1257"},
+ "x-cp1257": {charmap.Windows1257, "windows-1257"},
+ "cp1258": {charmap.Windows1258, "windows-1258"},
+ "windows-1258": {charmap.Windows1258, "windows-1258"},
+ "x-cp1258": {charmap.Windows1258, "windows-1258"},
+ "x-mac-cyrillic": {charmap.MacintoshCyrillic, "x-mac-cyrillic"},
+ "x-mac-ukrainian": {charmap.MacintoshCyrillic, "x-mac-cyrillic"},
+ "chinese": {simplifiedchinese.GBK, "gbk"},
+ "csgb2312": {simplifiedchinese.GBK, "gbk"},
+ "csiso58gb231280": {simplifiedchinese.GBK, "gbk"},
+ "gb2312": {simplifiedchinese.GBK, "gbk"},
+ "gb_2312": {simplifiedchinese.GBK, "gbk"},
+ "gb_2312-80": {simplifiedchinese.GBK, "gbk"},
+ "gbk": {simplifiedchinese.GBK, "gbk"},
+ "iso-ir-58": {simplifiedchinese.GBK, "gbk"},
+ "x-gbk": {simplifiedchinese.GBK, "gbk"},
+ "gb18030": {simplifiedchinese.GB18030, "gb18030"},
+ "hz-gb-2312": {simplifiedchinese.HZGB2312, "hz-gb-2312"},
+ "big5": {traditionalchinese.Big5, "big5"},
+ "big5-hkscs": {traditionalchinese.Big5, "big5"},
+ "cn-big5": {traditionalchinese.Big5, "big5"},
+ "csbig5": {traditionalchinese.Big5, "big5"},
+ "x-x-big5": {traditionalchinese.Big5, "big5"},
+ "cseucpkdfmtjapanese": {japanese.EUCJP, "euc-jp"},
+ "euc-jp": {japanese.EUCJP, "euc-jp"},
+ "x-euc-jp": {japanese.EUCJP, "euc-jp"},
+ "csiso2022jp": {japanese.ISO2022JP, "iso-2022-jp"},
+ "iso-2022-jp": {japanese.ISO2022JP, "iso-2022-jp"},
+ "csshiftjis": {japanese.ShiftJIS, "shift_jis"},
+ "ms_kanji": {japanese.ShiftJIS, "shift_jis"},
+ "shift-jis": {japanese.ShiftJIS, "shift_jis"},
+ "shift_jis": {japanese.ShiftJIS, "shift_jis"},
+ "sjis": {japanese.ShiftJIS, "shift_jis"},
+ "windows-31j": {japanese.ShiftJIS, "shift_jis"},
+ "x-sjis": {japanese.ShiftJIS, "shift_jis"},
+ "cseuckr": {korean.EUCKR, "euc-kr"},
+ "csksc56011987": {korean.EUCKR, "euc-kr"},
+ "euc-kr": {korean.EUCKR, "euc-kr"},
+ "iso-ir-149": {korean.EUCKR, "euc-kr"},
+ "korean": {korean.EUCKR, "euc-kr"},
+ "ks_c_5601-1987": {korean.EUCKR, "euc-kr"},
+ "ks_c_5601-1989": {korean.EUCKR, "euc-kr"},
+ "ksc5601": {korean.EUCKR, "euc-kr"},
+ "ksc_5601": {korean.EUCKR, "euc-kr"},
+ "windows-949": {korean.EUCKR, "euc-kr"},
+ "csiso2022kr": {encoding.Replacement, "replacement"},
+ "iso-2022-kr": {encoding.Replacement, "replacement"},
+ "iso-2022-cn": {encoding.Replacement, "replacement"},
+ "iso-2022-cn-ext": {encoding.Replacement, "replacement"},
+ "utf-16be": {unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM), "utf-16be"},
+ "utf-16": {unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), "utf-16le"},
+ "utf-16le": {unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), "utf-16le"},
+ "x-user-defined": {charmap.XUserDefined, "x-user-defined"},
+}
diff --git a/html/charset/testdata/HTTP-charset.html b/html/charset/testdata/HTTP-charset.html
new file mode 100644
index 0000000..9915fa0
--- /dev/null
+++ b/html/charset/testdata/HTTP-charset.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>HTTP charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="The character encoding of a page can be set using the HTTP header charset declaration.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
+</head>
+<body>
+<p class='title'>HTTP charset</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">The character encoding of a page can be set using the HTTP header charset declaration.</p>
+<div class="notes"><p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ÜÀÚ</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p><p>The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-003">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-001<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-001" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/HTTP-vs-UTF-8-BOM.html b/html/charset/testdata/HTTP-vs-UTF-8-BOM.html
new file mode 100644
index 0000000..26e5d8b
--- /dev/null
+++ b/html/charset/testdata/HTTP-vs-UTF-8-BOM.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>HTTP vs UTF-8 BOM</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
+</head>
+<body>
+<p class='title'>HTTP vs UTF-8 BOM</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.</p>
+<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ýäè</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p><p>If the test is unsuccessful, the characters  should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-022">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-034<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-034" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/HTTP-vs-meta-charset.html b/html/charset/testdata/HTTP-vs-meta-charset.html
new file mode 100644
index 0000000..2f07e95
--- /dev/null
+++ b/html/charset/testdata/HTTP-vs-meta-charset.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-1" > <title>HTTP vs meta charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.">
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
+</head>
+<body>
+<p class='title'>HTTP vs meta charset</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.</p>
+<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ÜÀÚ</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-037">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-018<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-018" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/HTTP-vs-meta-content.html b/html/charset/testdata/HTTP-vs-meta-content.html
new file mode 100644
index 0000000..6853cdd
--- /dev/null
+++ b/html/charset/testdata/HTTP-vs-meta-content.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html;charset=iso-8859-1" > <title>HTTP vs meta content</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.">
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
+</head>
+<body>
+<p class='title'>HTTP vs meta content</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.</p>
+<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ÜÀÚ</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-018">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-016<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-016" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/No-encoding-declaration.html b/html/charset/testdata/No-encoding-declaration.html
new file mode 100644
index 0000000..612e26c
--- /dev/null
+++ b/html/charset/testdata/No-encoding-declaration.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <title>No encoding declaration</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
+</head>
+<body>
+<p class='title'>No encoding declaration</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.</p>
+<div class="notes"><p><p>The test on this page contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ýäè</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-034">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-015<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-015" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/README b/html/charset/testdata/README
new file mode 100644
index 0000000..38ef0f9
--- /dev/null
+++ b/html/charset/testdata/README
@@ -0,0 +1,9 @@
+These test cases come from
+http://www.w3.org/International/tests/repository/html5/the-input-byte-stream/results-basics
+
+Distributed under both the W3C Test Suite License
+(http://www.w3.org/Consortium/Legal/2008/04-testsuite-license)
+and the W3C 3-clause BSD License
+(http://www.w3.org/Consortium/Legal/2008/03-bsd-license).
+To contribute to a W3C Test Suite, see the policies and contribution
+forms (http://www.w3.org/2004/10/27-testcases).
diff --git a/html/charset/testdata/UTF-16BE-BOM.html b/html/charset/testdata/UTF-16BE-BOM.html
new file mode 100644
index 0000000..3abf7a9
--- /dev/null
+++ b/html/charset/testdata/UTF-16BE-BOM.html
Binary files differ
diff --git a/html/charset/testdata/UTF-16LE-BOM.html b/html/charset/testdata/UTF-16LE-BOM.html
new file mode 100644
index 0000000..76254c9
--- /dev/null
+++ b/html/charset/testdata/UTF-16LE-BOM.html
Binary files differ
diff --git a/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html b/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html
new file mode 100644
index 0000000..83de433
--- /dev/null
+++ b/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-15"> <title>UTF-8 BOM vs meta charset</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.">
+<style type='text/css'>
+.test div { width: 50px; }.test div { width: 90px; }
+</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
+</head>
+<body>
+<p class='title'>UTF-8 BOM vs meta charset</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.</p>
+<div class="notes"><p><p>The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ýäè</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-024">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-038<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-038" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/UTF-8-BOM-vs-meta-content.html b/html/charset/testdata/UTF-8-BOM-vs-meta-content.html
new file mode 100644
index 0000000..501aac2
--- /dev/null
+++ b/html/charset/testdata/UTF-8-BOM-vs-meta-content.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>UTF-8 BOM vs meta content</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
+</head>
+<body>
+<p class='title'>UTF-8 BOM vs meta content</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.</p>
+<div class="notes"><p><p>The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ýäè</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-038">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-037<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-037" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/meta-charset-attribute.html b/html/charset/testdata/meta-charset-attribute.html
new file mode 100644
index 0000000..2d7d25a
--- /dev/null
+++ b/html/charset/testdata/meta-charset-attribute.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta charset="iso-8859-15"> <title>meta charset attribute</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="The character encoding of the page can be set by a meta element with charset attribute.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
+</head>
+<body>
+<p class='title'>meta charset attribute</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with charset attribute.</p>
+<div class="notes"><p><p>The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ÜÀÚ</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-015">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-009<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-009" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/charset/testdata/meta-content-attribute.html b/html/charset/testdata/meta-content-attribute.html
new file mode 100644
index 0000000..1c3f228
--- /dev/null
+++ b/html/charset/testdata/meta-content-attribute.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>meta content attribute</title>
+<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
+<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
+<link rel="stylesheet" type="text/css" href="./generatedtests.css">
+<script src="http://w3c-test.org/resources/testharness.js"></script>
+<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
+<meta name='flags' content='http'>
+<meta name="assert" content="The character encoding of the page can be set by a meta element with http-equiv and content attributes.">
+<style type='text/css'>
+.test div { width: 50px; }</style>
+<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
+</head>
+<body>
+<p class='title'>meta content attribute</p>
+
+
+<div id='log'></div>
+
+
+<div class='test'><div id='box' class='ýäè'> </div></div>
+
+
+
+
+
+<div class='description'>
+<p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with http-equiv and content attributes.</p>
+<div class="notes"><p><p>The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.ÜÀÚ</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
+</div>
+</div>
+<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-009">Next test</a></div><div class="doctype">HTML5</div>
+<p class="jump">the-input-byte-stream-007<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary & related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-007" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
+<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
+ <li>The test is read from a server that supports HTTP.</li></ul></div>
+</div>
+<script>
+test(function() {
+assert_equals(document.getElementById('box').offsetWidth, 100);
+}, " ");
+</script>
+
+</body>
+</html>
+
+
diff --git a/html/const.go b/html/const.go
new file mode 100644
index 0000000..52f651f
--- /dev/null
+++ b/html/const.go
@@ -0,0 +1,102 @@
+// Copyright 2011 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.
+
+package html
+
+// Section 12.2.3.2 of the HTML5 specification says "The following elements
+// have varying levels of special parsing rules".
+// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
+var isSpecialElementMap = map[string]bool{
+ "address": true,
+ "applet": true,
+ "area": true,
+ "article": true,
+ "aside": true,
+ "base": true,
+ "basefont": true,
+ "bgsound": true,
+ "blockquote": true,
+ "body": true,
+ "br": true,
+ "button": true,
+ "caption": true,
+ "center": true,
+ "col": true,
+ "colgroup": true,
+ "dd": true,
+ "details": true,
+ "dir": true,
+ "div": true,
+ "dl": true,
+ "dt": true,
+ "embed": true,
+ "fieldset": true,
+ "figcaption": true,
+ "figure": true,
+ "footer": true,
+ "form": true,
+ "frame": true,
+ "frameset": true,
+ "h1": true,
+ "h2": true,
+ "h3": true,
+ "h4": true,
+ "h5": true,
+ "h6": true,
+ "head": true,
+ "header": true,
+ "hgroup": true,
+ "hr": true,
+ "html": true,
+ "iframe": true,
+ "img": true,
+ "input": true,
+ "isindex": true,
+ "li": true,
+ "link": true,
+ "listing": true,
+ "marquee": true,
+ "menu": true,
+ "meta": true,
+ "nav": true,
+ "noembed": true,
+ "noframes": true,
+ "noscript": true,
+ "object": true,
+ "ol": true,
+ "p": true,
+ "param": true,
+ "plaintext": true,
+ "pre": true,
+ "script": true,
+ "section": true,
+ "select": true,
+ "source": true,
+ "style": true,
+ "summary": true,
+ "table": true,
+ "tbody": true,
+ "td": true,
+ "template": true,
+ "textarea": true,
+ "tfoot": true,
+ "th": true,
+ "thead": true,
+ "title": true,
+ "tr": true,
+ "track": true,
+ "ul": true,
+ "wbr": true,
+ "xmp": true,
+}
+
+func isSpecialElement(element *Node) bool {
+ switch element.Namespace {
+ case "", "html":
+ return isSpecialElementMap[element.Data]
+ case "svg":
+ return element.Data == "foreignObject"
+ }
+ return false
+}
diff --git a/html/doc.go b/html/doc.go
new file mode 100644
index 0000000..94f4968
--- /dev/null
+++ b/html/doc.go
@@ -0,0 +1,106 @@
+// Copyright 2010 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.
+
+/*
+Package html implements an HTML5-compliant tokenizer and parser.
+
+Tokenization is done by creating a Tokenizer for an io.Reader r. It is the
+caller's responsibility to ensure that r provides UTF-8 encoded HTML.
+
+ z := html.NewTokenizer(r)
+
+Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(),
+which parses the next token and returns its type, or an error:
+
+ for {
+ tt := z.Next()
+ if tt == html.ErrorToken {
+ // ...
+ return ...
+ }
+ // Process the current token.
+ }
+
+There are two APIs for retrieving the current token. The high-level API is to
+call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs
+allow optionally calling Raw after Next but before Token, Text, TagName, or
+TagAttr. In EBNF notation, the valid call sequence per token is:
+
+ Next {Raw} [ Token | Text | TagName {TagAttr} ]
+
+Token returns an independent data structure that completely describes a token.
+Entities (such as "<") are unescaped, tag names and attribute keys are
+lower-cased, and attributes are collected into a []Attribute. For example:
+
+ for {
+ if z.Next() == html.ErrorToken {
+ // Returning io.EOF indicates success.
+ return z.Err()
+ }
+ emitToken(z.Token())
+ }
+
+The low-level API performs fewer allocations and copies, but the contents of
+the []byte values returned by Text, TagName and TagAttr may change on the next
+call to Next. For example, to extract an HTML page's anchor text:
+
+ depth := 0
+ for {
+ tt := z.Next()
+ switch tt {
+ case ErrorToken:
+ return z.Err()
+ case TextToken:
+ if depth > 0 {
+ // emitBytes should copy the []byte it receives,
+ // if it doesn't process it immediately.
+ emitBytes(z.Text())
+ }
+ case StartTagToken, EndTagToken:
+ tn, _ := z.TagName()
+ if len(tn) == 1 && tn[0] == 'a' {
+ if tt == StartTagToken {
+ depth++
+ } else {
+ depth--
+ }
+ }
+ }
+ }
+
+Parsing is done by calling Parse with an io.Reader, which returns the root of
+the parse tree (the document element) as a *Node. It is the caller's
+responsibility to ensure that the Reader provides UTF-8 encoded HTML. For
+example, to process each anchor node in depth-first order:
+
+ doc, err := html.Parse(r)
+ if err != nil {
+ // ...
+ }
+ var f func(*html.Node)
+ f = func(n *html.Node) {
+ if n.Type == html.ElementNode && n.Data == "a" {
+ // Do something with n...
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ f(c)
+ }
+ }
+ f(doc)
+
+The relevant specifications include:
+https://html.spec.whatwg.org/multipage/syntax.html and
+https://html.spec.whatwg.org/multipage/syntax.html#tokenization
+*/
+package html // import "golang.org/x/net/html"
+
+// The tokenization algorithm implemented by this package is not a line-by-line
+// transliteration of the relatively verbose state-machine in the WHATWG
+// specification. A more direct approach is used instead, where the program
+// counter implies the state, such as whether it is tokenizing a tag or a text
+// node. Specification compliance is verified by checking expected and actual
+// outputs over a test suite rather than aiming for algorithmic fidelity.
+
+// TODO(nigeltao): Does a DOM API belong in this package or a separate one?
+// TODO(nigeltao): How does parsing interact with a JavaScript engine?
diff --git a/html/doctype.go b/html/doctype.go
new file mode 100644
index 0000000..c484e5a
--- /dev/null
+++ b/html/doctype.go
@@ -0,0 +1,156 @@
+// Copyright 2011 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.
+
+package html
+
+import (
+ "strings"
+)
+
+// parseDoctype parses the data from a DoctypeToken into a name,
+// public identifier, and system identifier. It returns a Node whose Type
+// is DoctypeNode, whose Data is the name, and which has attributes
+// named "system" and "public" for the two identifiers if they were present.
+// quirks is whether the document should be parsed in "quirks mode".
+func parseDoctype(s string) (n *Node, quirks bool) {
+ n = &Node{Type: DoctypeNode}
+
+ // Find the name.
+ space := strings.IndexAny(s, whitespace)
+ if space == -1 {
+ space = len(s)
+ }
+ n.Data = s[:space]
+ // The comparison to "html" is case-sensitive.
+ if n.Data != "html" {
+ quirks = true
+ }
+ n.Data = strings.ToLower(n.Data)
+ s = strings.TrimLeft(s[space:], whitespace)
+
+ if len(s) < 6 {
+ // It can't start with "PUBLIC" or "SYSTEM".
+ // Ignore the rest of the string.
+ return n, quirks || s != ""
+ }
+
+ key := strings.ToLower(s[:6])
+ s = s[6:]
+ for key == "public" || key == "system" {
+ s = strings.TrimLeft(s, whitespace)
+ if s == "" {
+ break
+ }
+ quote := s[0]
+ if quote != '"' && quote != '\'' {
+ break
+ }
+ s = s[1:]
+ q := strings.IndexRune(s, rune(quote))
+ var id string
+ if q == -1 {
+ id = s
+ s = ""
+ } else {
+ id = s[:q]
+ s = s[q+1:]
+ }
+ n.Attr = append(n.Attr, Attribute{Key: key, Val: id})
+ if key == "public" {
+ key = "system"
+ } else {
+ key = ""
+ }
+ }
+
+ if key != "" || s != "" {
+ quirks = true
+ } else if len(n.Attr) > 0 {
+ if n.Attr[0].Key == "public" {
+ public := strings.ToLower(n.Attr[0].Val)
+ switch public {
+ case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html":
+ quirks = true
+ default:
+ for _, q := range quirkyIDs {
+ if strings.HasPrefix(public, q) {
+ quirks = true
+ break
+ }
+ }
+ }
+ // The following two public IDs only cause quirks mode if there is no system ID.
+ if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") ||
+ strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) {
+ quirks = true
+ }
+ }
+ if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
+ strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
+ quirks = true
+ }
+ }
+
+ return n, quirks
+}
+
+// quirkyIDs is a list of public doctype identifiers that cause a document
+// to be interpreted in quirks mode. The identifiers should be in lower case.
+var quirkyIDs = []string{
+ "+//silmaril//dtd html pro v0r11 19970101//",
+ "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
+ "-//as//dtd html 3.0 aswedit + extensions//",
+ "-//ietf//dtd html 2.0 level 1//",
+ "-//ietf//dtd html 2.0 level 2//",
+ "-//ietf//dtd html 2.0 strict level 1//",
+ "-//ietf//dtd html 2.0 strict level 2//",
+ "-//ietf//dtd html 2.0 strict//",
+ "-//ietf//dtd html 2.0//",
+ "-//ietf//dtd html 2.1e//",
+ "-//ietf//dtd html 3.0//",
+ "-//ietf//dtd html 3.2 final//",
+ "-//ietf//dtd html 3.2//",
+ "-//ietf//dtd html 3//",
+ "-//ietf//dtd html level 0//",
+ "-//ietf//dtd html level 1//",
+ "-//ietf//dtd html level 2//",
+ "-//ietf//dtd html level 3//",
+ "-//ietf//dtd html strict level 0//",
+ "-//ietf//dtd html strict level 1//",
+ "-//ietf//dtd html strict level 2//",
+ "-//ietf//dtd html strict level 3//",
+ "-//ietf//dtd html strict//",
+ "-//ietf//dtd html//",
+ "-//metrius//dtd metrius presentational//",
+ "-//microsoft//dtd internet explorer 2.0 html strict//",
+ "-//microsoft//dtd internet explorer 2.0 html//",
+ "-//microsoft//dtd internet explorer 2.0 tables//",
+ "-//microsoft//dtd internet explorer 3.0 html strict//",
+ "-//microsoft//dtd internet explorer 3.0 html//",
+ "-//microsoft//dtd internet explorer 3.0 tables//",
+ "-//netscape comm. corp.//dtd html//",
+ "-//netscape comm. corp.//dtd strict html//",
+ "-//o'reilly and associates//dtd html 2.0//",
+ "-//o'reilly and associates//dtd html extended 1.0//",
+ "-//o'reilly and associates//dtd html extended relaxed 1.0//",
+ "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
+ "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
+ "-//spyglass//dtd html 2.0 extended//",
+ "-//sq//dtd html 2.0 hotmetal + extensions//",
+ "-//sun microsystems corp.//dtd hotjava html//",
+ "-//sun microsystems corp.//dtd hotjava strict html//",
+ "-//w3c//dtd html 3 1995-03-24//",
+ "-//w3c//dtd html 3.2 draft//",
+ "-//w3c//dtd html 3.2 final//",
+ "-//w3c//dtd html 3.2//",
+ "-//w3c//dtd html 3.2s draft//",
+ "-//w3c//dtd html 4.0 frameset//",
+ "-//w3c//dtd html 4.0 transitional//",
+ "-//w3c//dtd html experimental 19960712//",
+ "-//w3c//dtd html experimental 970421//",
+ "-//w3c//dtd w3 html//",
+ "-//w3o//dtd w3 html 3.0//",
+ "-//webtechs//dtd mozilla html 2.0//",
+ "-//webtechs//dtd mozilla html//",
+}
diff --git a/html/entity.go b/html/entity.go
new file mode 100644
index 0000000..a50c04c
--- /dev/null
+++ b/html/entity.go
@@ -0,0 +1,2253 @@
+// Copyright 2010 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.
+
+package html
+
+// All entities that do not end with ';' are 6 or fewer bytes long.
+const longestEntityWithoutSemicolon = 6
+
+// entity is a map from HTML entity names to their values. The semicolon matters:
+// https://html.spec.whatwg.org/multipage/syntax.html#named-character-references
+// lists both "amp" and "amp;" as two separate entries.
+//
+// Note that the HTML5 list is larger than the HTML4 list at
+// http://www.w3.org/TR/html4/sgml/entities.html
+var entity = map[string]rune{
+ "AElig;": '\U000000C6',
+ "AMP;": '\U00000026',
+ "Aacute;": '\U000000C1',
+ "Abreve;": '\U00000102',
+ "Acirc;": '\U000000C2',
+ "Acy;": '\U00000410',
+ "Afr;": '\U0001D504',
+ "Agrave;": '\U000000C0',
+ "Alpha;": '\U00000391',
+ "Amacr;": '\U00000100',
+ "And;": '\U00002A53',
+ "Aogon;": '\U00000104',
+ "Aopf;": '\U0001D538',
+ "ApplyFunction;": '\U00002061',
+ "Aring;": '\U000000C5',
+ "Ascr;": '\U0001D49C',
+ "Assign;": '\U00002254',
+ "Atilde;": '\U000000C3',
+ "Auml;": '\U000000C4',
+ "Backslash;": '\U00002216',
+ "Barv;": '\U00002AE7',
+ "Barwed;": '\U00002306',
+ "Bcy;": '\U00000411',
+ "Because;": '\U00002235',
+ "Bernoullis;": '\U0000212C',
+ "Beta;": '\U00000392',
+ "Bfr;": '\U0001D505',
+ "Bopf;": '\U0001D539',
+ "Breve;": '\U000002D8',
+ "Bscr;": '\U0000212C',
+ "Bumpeq;": '\U0000224E',
+ "CHcy;": '\U00000427',
+ "COPY;": '\U000000A9',
+ "Cacute;": '\U00000106',
+ "Cap;": '\U000022D2',
+ "CapitalDifferentialD;": '\U00002145',
+ "Cayleys;": '\U0000212D',
+ "Ccaron;": '\U0000010C',
+ "Ccedil;": '\U000000C7',
+ "Ccirc;": '\U00000108',
+ "Cconint;": '\U00002230',
+ "Cdot;": '\U0000010A',
+ "Cedilla;": '\U000000B8',
+ "CenterDot;": '\U000000B7',
+ "Cfr;": '\U0000212D',
+ "Chi;": '\U000003A7',
+ "CircleDot;": '\U00002299',
+ "CircleMinus;": '\U00002296',
+ "CirclePlus;": '\U00002295',
+ "CircleTimes;": '\U00002297',
+ "ClockwiseContourIntegral;": '\U00002232',
+ "CloseCurlyDoubleQuote;": '\U0000201D',
+ "CloseCurlyQuote;": '\U00002019',
+ "Colon;": '\U00002237',
+ "Colone;": '\U00002A74',
+ "Congruent;": '\U00002261',
+ "Conint;": '\U0000222F',
+ "ContourIntegral;": '\U0000222E',
+ "Copf;": '\U00002102',
+ "Coproduct;": '\U00002210',
+ "CounterClockwiseContourIntegral;": '\U00002233',
+ "Cross;": '\U00002A2F',
+ "Cscr;": '\U0001D49E',
+ "Cup;": '\U000022D3',
+ "CupCap;": '\U0000224D',
+ "DD;": '\U00002145',
+ "DDotrahd;": '\U00002911',
+ "DJcy;": '\U00000402',
+ "DScy;": '\U00000405',
+ "DZcy;": '\U0000040F',
+ "Dagger;": '\U00002021',
+ "Darr;": '\U000021A1',
+ "Dashv;": '\U00002AE4',
+ "Dcaron;": '\U0000010E',
+ "Dcy;": '\U00000414',
+ "Del;": '\U00002207',
+ "Delta;": '\U00000394',
+ "Dfr;": '\U0001D507',
+ "DiacriticalAcute;": '\U000000B4',
+ "DiacriticalDot;": '\U000002D9',
+ "DiacriticalDoubleAcute;": '\U000002DD',
+ "DiacriticalGrave;": '\U00000060',
+ "DiacriticalTilde;": '\U000002DC',
+ "Diamond;": '\U000022C4',
+ "DifferentialD;": '\U00002146',
+ "Dopf;": '\U0001D53B',
+ "Dot;": '\U000000A8',
+ "DotDot;": '\U000020DC',
+ "DotEqual;": '\U00002250',
+ "DoubleContourIntegral;": '\U0000222F',
+ "DoubleDot;": '\U000000A8',
+ "DoubleDownArrow;": '\U000021D3',
+ "DoubleLeftArrow;": '\U000021D0',
+ "DoubleLeftRightArrow;": '\U000021D4',
+ "DoubleLeftTee;": '\U00002AE4',
+ "DoubleLongLeftArrow;": '\U000027F8',
+ "DoubleLongLeftRightArrow;": '\U000027FA',
+ "DoubleLongRightArrow;": '\U000027F9',
+ "DoubleRightArrow;": '\U000021D2',
+ "DoubleRightTee;": '\U000022A8',
+ "DoubleUpArrow;": '\U000021D1',
+ "DoubleUpDownArrow;": '\U000021D5',
+ "DoubleVerticalBar;": '\U00002225',
+ "DownArrow;": '\U00002193',
+ "DownArrowBar;": '\U00002913',
+ "DownArrowUpArrow;": '\U000021F5',
+ "DownBreve;": '\U00000311',
+ "DownLeftRightVector;": '\U00002950',
+ "DownLeftTeeVector;": '\U0000295E',
+ "DownLeftVector;": '\U000021BD',
+ "DownLeftVectorBar;": '\U00002956',
+ "DownRightTeeVector;": '\U0000295F',
+ "DownRightVector;": '\U000021C1',
+ "DownRightVectorBar;": '\U00002957',
+ "DownTee;": '\U000022A4',
+ "DownTeeArrow;": '\U000021A7',
+ "Downarrow;": '\U000021D3',
+ "Dscr;": '\U0001D49F',
+ "Dstrok;": '\U00000110',
+ "ENG;": '\U0000014A',
+ "ETH;": '\U000000D0',
+ "Eacute;": '\U000000C9',
+ "Ecaron;": '\U0000011A',
+ "Ecirc;": '\U000000CA',
+ "Ecy;": '\U0000042D',
+ "Edot;": '\U00000116',
+ "Efr;": '\U0001D508',
+ "Egrave;": '\U000000C8',
+ "Element;": '\U00002208',
+ "Emacr;": '\U00000112',
+ "EmptySmallSquare;": '\U000025FB',
+ "EmptyVerySmallSquare;": '\U000025AB',
+ "Eogon;": '\U00000118',
+ "Eopf;": '\U0001D53C',
+ "Epsilon;": '\U00000395',
+ "Equal;": '\U00002A75',
+ "EqualTilde;": '\U00002242',
+ "Equilibrium;": '\U000021CC',
+ "Escr;": '\U00002130',
+ "Esim;": '\U00002A73',
+ "Eta;": '\U00000397',
+ "Euml;": '\U000000CB',
+ "Exists;": '\U00002203',
+ "ExponentialE;": '\U00002147',
+ "Fcy;": '\U00000424',
+ "Ffr;": '\U0001D509',
+ "FilledSmallSquare;": '\U000025FC',
+ "FilledVerySmallSquare;": '\U000025AA',
+ "Fopf;": '\U0001D53D',
+ "ForAll;": '\U00002200',
+ "Fouriertrf;": '\U00002131',
+ "Fscr;": '\U00002131',
+ "GJcy;": '\U00000403',
+ "GT;": '\U0000003E',
+ "Gamma;": '\U00000393',
+ "Gammad;": '\U000003DC',
+ "Gbreve;": '\U0000011E',
+ "Gcedil;": '\U00000122',
+ "Gcirc;": '\U0000011C',
+ "Gcy;": '\U00000413',
+ "Gdot;": '\U00000120',
+ "Gfr;": '\U0001D50A',
+ "Gg;": '\U000022D9',
+ "Gopf;": '\U0001D53E',
+ "GreaterEqual;": '\U00002265',
+ "GreaterEqualLess;": '\U000022DB',
+ "GreaterFullEqual;": '\U00002267',
+ "GreaterGreater;": '\U00002AA2',
+ "GreaterLess;": '\U00002277',
+ "GreaterSlantEqual;": '\U00002A7E',
+ "GreaterTilde;": '\U00002273',
+ "Gscr;": '\U0001D4A2',
+ "Gt;": '\U0000226B',
+ "HARDcy;": '\U0000042A',
+ "Hacek;": '\U000002C7',
+ "Hat;": '\U0000005E',
+ "Hcirc;": '\U00000124',
+ "Hfr;": '\U0000210C',
+ "HilbertSpace;": '\U0000210B',
+ "Hopf;": '\U0000210D',
+ "HorizontalLine;": '\U00002500',
+ "Hscr;": '\U0000210B',
+ "Hstrok;": '\U00000126',
+ "HumpDownHump;": '\U0000224E',
+ "HumpEqual;": '\U0000224F',
+ "IEcy;": '\U00000415',
+ "IJlig;": '\U00000132',
+ "IOcy;": '\U00000401',
+ "Iacute;": '\U000000CD',
+ "Icirc;": '\U000000CE',
+ "Icy;": '\U00000418',
+ "Idot;": '\U00000130',
+ "Ifr;": '\U00002111',
+ "Igrave;": '\U000000CC',
+ "Im;": '\U00002111',
+ "Imacr;": '\U0000012A',
+ "ImaginaryI;": '\U00002148',
+ "Implies;": '\U000021D2',
+ "Int;": '\U0000222C',
+ "Integral;": '\U0000222B',
+ "Intersection;": '\U000022C2',
+ "InvisibleComma;": '\U00002063',
+ "InvisibleTimes;": '\U00002062',
+ "Iogon;": '\U0000012E',
+ "Iopf;": '\U0001D540',
+ "Iota;": '\U00000399',
+ "Iscr;": '\U00002110',
+ "Itilde;": '\U00000128',
+ "Iukcy;": '\U00000406',
+ "Iuml;": '\U000000CF',
+ "Jcirc;": '\U00000134',
+ "Jcy;": '\U00000419',
+ "Jfr;": '\U0001D50D',
+ "Jopf;": '\U0001D541',
+ "Jscr;": '\U0001D4A5',
+ "Jsercy;": '\U00000408',
+ "Jukcy;": '\U00000404',
+ "KHcy;": '\U00000425',
+ "KJcy;": '\U0000040C',
+ "Kappa;": '\U0000039A',
+ "Kcedil;": '\U00000136',
+ "Kcy;": '\U0000041A',
+ "Kfr;": '\U0001D50E',
+ "Kopf;": '\U0001D542',
+ "Kscr;": '\U0001D4A6',
+ "LJcy;": '\U00000409',
+ "LT;": '\U0000003C',
+ "Lacute;": '\U00000139',
+ "Lambda;": '\U0000039B',
+ "Lang;": '\U000027EA',
+ "Laplacetrf;": '\U00002112',
+ "Larr;": '\U0000219E',
+ "Lcaron;": '\U0000013D',
+ "Lcedil;": '\U0000013B',
+ "Lcy;": '\U0000041B',
+ "LeftAngleBracket;": '\U000027E8',
+ "LeftArrow;": '\U00002190',
+ "LeftArrowBar;": '\U000021E4',
+ "LeftArrowRightArrow;": '\U000021C6',
+ "LeftCeiling;": '\U00002308',
+ "LeftDoubleBracket;": '\U000027E6',
+ "LeftDownTeeVector;": '\U00002961',
+ "LeftDownVector;": '\U000021C3',
+ "LeftDownVectorBar;": '\U00002959',
+ "LeftFloor;": '\U0000230A',
+ "LeftRightArrow;": '\U00002194',
+ "LeftRightVector;": '\U0000294E',
+ "LeftTee;": '\U000022A3',
+ "LeftTeeArrow;": '\U000021A4',
+ "LeftTeeVector;": '\U0000295A',
+ "LeftTriangle;": '\U000022B2',
+ "LeftTriangleBar;": '\U000029CF',
+ "LeftTriangleEqual;": '\U000022B4',
+ "LeftUpDownVector;": '\U00002951',
+ "LeftUpTeeVector;": '\U00002960',
+ "LeftUpVector;": '\U000021BF',
+ "LeftUpVectorBar;": '\U00002958',
+ "LeftVector;": '\U000021BC',
+ "LeftVectorBar;": '\U00002952',
+ "Leftarrow;": '\U000021D0',
+ "Leftrightarrow;": '\U000021D4',
+ "LessEqualGreater;": '\U000022DA',
+ "LessFullEqual;": '\U00002266',
+ "LessGreater;": '\U00002276',
+ "LessLess;": '\U00002AA1',
+ "LessSlantEqual;": '\U00002A7D',
+ "LessTilde;": '\U00002272',
+ "Lfr;": '\U0001D50F',
+ "Ll;": '\U000022D8',
+ "Lleftarrow;": '\U000021DA',
+ "Lmidot;": '\U0000013F',
+ "LongLeftArrow;": '\U000027F5',
+ "LongLeftRightArrow;": '\U000027F7',
+ "LongRightArrow;": '\U000027F6',
+ "Longleftarrow;": '\U000027F8',
+ "Longleftrightarrow;": '\U000027FA',
+ "Longrightarrow;": '\U000027F9',
+ "Lopf;": '\U0001D543',
+ "LowerLeftArrow;": '\U00002199',
+ "LowerRightArrow;": '\U00002198',
+ "Lscr;": '\U00002112',
+ "Lsh;": '\U000021B0',
+ "Lstrok;": '\U00000141',
+ "Lt;": '\U0000226A',
+ "Map;": '\U00002905',
+ "Mcy;": '\U0000041C',
+ "MediumSpace;": '\U0000205F',
+ "Mellintrf;": '\U00002133',
+ "Mfr;": '\U0001D510',
+ "MinusPlus;": '\U00002213',
+ "Mopf;": '\U0001D544',
+ "Mscr;": '\U00002133',
+ "Mu;": '\U0000039C',
+ "NJcy;": '\U0000040A',
+ "Nacute;": '\U00000143',
+ "Ncaron;": '\U00000147',
+ "Ncedil;": '\U00000145',
+ "Ncy;": '\U0000041D',
+ "NegativeMediumSpace;": '\U0000200B',
+ "NegativeThickSpace;": '\U0000200B',
+ "NegativeThinSpace;": '\U0000200B',
+ "NegativeVeryThinSpace;": '\U0000200B',
+ "NestedGreaterGreater;": '\U0000226B',
+ "NestedLessLess;": '\U0000226A',
+ "NewLine;": '\U0000000A',
+ "Nfr;": '\U0001D511',
+ "NoBreak;": '\U00002060',
+ "NonBreakingSpace;": '\U000000A0',
+ "Nopf;": '\U00002115',
+ "Not;": '\U00002AEC',
+ "NotCongruent;": '\U00002262',
+ "NotCupCap;": '\U0000226D',
+ "NotDoubleVerticalBar;": '\U00002226',
+ "NotElement;": '\U00002209',
+ "NotEqual;": '\U00002260',
+ "NotExists;": '\U00002204',
+ "NotGreater;": '\U0000226F',
+ "NotGreaterEqual;": '\U00002271',
+ "NotGreaterLess;": '\U00002279',
+ "NotGreaterTilde;": '\U00002275',
+ "NotLeftTriangle;": '\U000022EA',
+ "NotLeftTriangleEqual;": '\U000022EC',
+ "NotLess;": '\U0000226E',
+ "NotLessEqual;": '\U00002270',
+ "NotLessGreater;": '\U00002278',
+ "NotLessTilde;": '\U00002274',
+ "NotPrecedes;": '\U00002280',
+ "NotPrecedesSlantEqual;": '\U000022E0',
+ "NotReverseElement;": '\U0000220C',
+ "NotRightTriangle;": '\U000022EB',
+ "NotRightTriangleEqual;": '\U000022ED',
+ "NotSquareSubsetEqual;": '\U000022E2',
+ "NotSquareSupersetEqual;": '\U000022E3',
+ "NotSubsetEqual;": '\U00002288',
+ "NotSucceeds;": '\U00002281',
+ "NotSucceedsSlantEqual;": '\U000022E1',
+ "NotSupersetEqual;": '\U00002289',
+ "NotTilde;": '\U00002241',
+ "NotTildeEqual;": '\U00002244',
+ "NotTildeFullEqual;": '\U00002247',
+ "NotTildeTilde;": '\U00002249',
+ "NotVerticalBar;": '\U00002224',
+ "Nscr;": '\U0001D4A9',
+ "Ntilde;": '\U000000D1',
+ "Nu;": '\U0000039D',
+ "OElig;": '\U00000152',
+ "Oacute;": '\U000000D3',
+ "Ocirc;": '\U000000D4',
+ "Ocy;": '\U0000041E',
+ "Odblac;": '\U00000150',
+ "Ofr;": '\U0001D512',
+ "Ograve;": '\U000000D2',
+ "Omacr;": '\U0000014C',
+ "Omega;": '\U000003A9',
+ "Omicron;": '\U0000039F',
+ "Oopf;": '\U0001D546',
+ "OpenCurlyDoubleQuote;": '\U0000201C',
+ "OpenCurlyQuote;": '\U00002018',
+ "Or;": '\U00002A54',
+ "Oscr;": '\U0001D4AA',
+ "Oslash;": '\U000000D8',
+ "Otilde;": '\U000000D5',
+ "Otimes;": '\U00002A37',
+ "Ouml;": '\U000000D6',
+ "OverBar;": '\U0000203E',
+ "OverBrace;": '\U000023DE',
+ "OverBracket;": '\U000023B4',
+ "OverParenthesis;": '\U000023DC',
+ "PartialD;": '\U00002202',
+ "Pcy;": '\U0000041F',
+ "Pfr;": '\U0001D513',
+ "Phi;": '\U000003A6',
+ "Pi;": '\U000003A0',
+ "PlusMinus;": '\U000000B1',
+ "Poincareplane;": '\U0000210C',
+ "Popf;": '\U00002119',
+ "Pr;": '\U00002ABB',
+ "Precedes;": '\U0000227A',
+ "PrecedesEqual;": '\U00002AAF',
+ "PrecedesSlantEqual;": '\U0000227C',
+ "PrecedesTilde;": '\U0000227E',
+ "Prime;": '\U00002033',
+ "Product;": '\U0000220F',
+ "Proportion;": '\U00002237',
+ "Proportional;": '\U0000221D',
+ "Pscr;": '\U0001D4AB',
+ "Psi;": '\U000003A8',
+ "QUOT;": '\U00000022',
+ "Qfr;": '\U0001D514',
+ "Qopf;": '\U0000211A',
+ "Qscr;": '\U0001D4AC',
+ "RBarr;": '\U00002910',
+ "REG;": '\U000000AE',
+ "Racute;": '\U00000154',
+ "Rang;": '\U000027EB',
+ "Rarr;": '\U000021A0',
+ "Rarrtl;": '\U00002916',
+ "Rcaron;": '\U00000158',
+ "Rcedil;": '\U00000156',
+ "Rcy;": '\U00000420',
+ "Re;": '\U0000211C',
+ "ReverseElement;": '\U0000220B',
+ "ReverseEquilibrium;": '\U000021CB',
+ "ReverseUpEquilibrium;": '\U0000296F',
+ "Rfr;": '\U0000211C',
+ "Rho;": '\U000003A1',
+ "RightAngleBracket;": '\U000027E9',
+ "RightArrow;": '\U00002192',
+ "RightArrowBar;": '\U000021E5',
+ "RightArrowLeftArrow;": '\U000021C4',
+ "RightCeiling;": '\U00002309',
+ "RightDoubleBracket;": '\U000027E7',
+ "RightDownTeeVector;": '\U0000295D',
+ "RightDownVector;": '\U000021C2',
+ "RightDownVectorBar;": '\U00002955',
+ "RightFloor;": '\U0000230B',
+ "RightTee;": '\U000022A2',
+ "RightTeeArrow;": '\U000021A6',
+ "RightTeeVector;": '\U0000295B',
+ "RightTriangle;": '\U000022B3',
+ "RightTriangleBar;": '\U000029D0',
+ "RightTriangleEqual;": '\U000022B5',
+ "RightUpDownVector;": '\U0000294F',
+ "RightUpTeeVector;": '\U0000295C',
+ "RightUpVector;": '\U000021BE',
+ "RightUpVectorBar;": '\U00002954',
+ "RightVector;": '\U000021C0',
+ "RightVectorBar;": '\U00002953',
+ "Rightarrow;": '\U000021D2',
+ "Ropf;": '\U0000211D',
+ "RoundImplies;": '\U00002970',
+ "Rrightarrow;": '\U000021DB',
+ "Rscr;": '\U0000211B',
+ "Rsh;": '\U000021B1',
+ "RuleDelayed;": '\U000029F4',
+ "SHCHcy;": '\U00000429',
+ "SHcy;": '\U00000428',
+ "SOFTcy;": '\U0000042C',
+ "Sacute;": '\U0000015A',
+ "Sc;": '\U00002ABC',
+ "Scaron;": '\U00000160',
+ "Scedil;": '\U0000015E',
+ "Scirc;": '\U0000015C',
+ "Scy;": '\U00000421',
+ "Sfr;": '\U0001D516',
+ "ShortDownArrow;": '\U00002193',
+ "ShortLeftArrow;": '\U00002190',
+ "ShortRightArrow;": '\U00002192',
+ "ShortUpArrow;": '\U00002191',
+ "Sigma;": '\U000003A3',
+ "SmallCircle;": '\U00002218',
+ "Sopf;": '\U0001D54A',
+ "Sqrt;": '\U0000221A',
+ "Square;": '\U000025A1',
+ "SquareIntersection;": '\U00002293',
+ "SquareSubset;": '\U0000228F',
+ "SquareSubsetEqual;": '\U00002291',
+ "SquareSuperset;": '\U00002290',
+ "SquareSupersetEqual;": '\U00002292',
+ "SquareUnion;": '\U00002294',
+ "Sscr;": '\U0001D4AE',
+ "Star;": '\U000022C6',
+ "Sub;": '\U000022D0',
+ "Subset;": '\U000022D0',
+ "SubsetEqual;": '\U00002286',
+ "Succeeds;": '\U0000227B',
+ "SucceedsEqual;": '\U00002AB0',
+ "SucceedsSlantEqual;": '\U0000227D',
+ "SucceedsTilde;": '\U0000227F',
+ "SuchThat;": '\U0000220B',
+ "Sum;": '\U00002211',
+ "Sup;": '\U000022D1',
+ "Superset;": '\U00002283',
+ "SupersetEqual;": '\U00002287',
+ "Supset;": '\U000022D1',
+ "THORN;": '\U000000DE',
+ "TRADE;": '\U00002122',
+ "TSHcy;": '\U0000040B',
+ "TScy;": '\U00000426',
+ "Tab;": '\U00000009',
+ "Tau;": '\U000003A4',
+ "Tcaron;": '\U00000164',
+ "Tcedil;": '\U00000162',
+ "Tcy;": '\U00000422',
+ "Tfr;": '\U0001D517',
+ "Therefore;": '\U00002234',
+ "Theta;": '\U00000398',
+ "ThinSpace;": '\U00002009',
+ "Tilde;": '\U0000223C',
+ "TildeEqual;": '\U00002243',
+ "TildeFullEqual;": '\U00002245',
+ "TildeTilde;": '\U00002248',
+ "Topf;": '\U0001D54B',
+ "TripleDot;": '\U000020DB',
+ "Tscr;": '\U0001D4AF',
+ "Tstrok;": '\U00000166',
+ "Uacute;": '\U000000DA',
+ "Uarr;": '\U0000219F',
+ "Uarrocir;": '\U00002949',
+ "Ubrcy;": '\U0000040E',
+ "Ubreve;": '\U0000016C',
+ "Ucirc;": '\U000000DB',
+ "Ucy;": '\U00000423',
+ "Udblac;": '\U00000170',
+ "Ufr;": '\U0001D518',
+ "Ugrave;": '\U000000D9',
+ "Umacr;": '\U0000016A',
+ "UnderBar;": '\U0000005F',
+ "UnderBrace;": '\U000023DF',
+ "UnderBracket;": '\U000023B5',
+ "UnderParenthesis;": '\U000023DD',
+ "Union;": '\U000022C3',
+ "UnionPlus;": '\U0000228E',
+ "Uogon;": '\U00000172',
+ "Uopf;": '\U0001D54C',
+ "UpArrow;": '\U00002191',
+ "UpArrowBar;": '\U00002912',
+ "UpArrowDownArrow;": '\U000021C5',
+ "UpDownArrow;": '\U00002195',
+ "UpEquilibrium;": '\U0000296E',
+ "UpTee;": '\U000022A5',
+ "UpTeeArrow;": '\U000021A5',
+ "Uparrow;": '\U000021D1',
+ "Updownarrow;": '\U000021D5',
+ "UpperLeftArrow;": '\U00002196',
+ "UpperRightArrow;": '\U00002197',
+ "Upsi;": '\U000003D2',
+ "Upsilon;": '\U000003A5',
+ "Uring;": '\U0000016E',
+ "Uscr;": '\U0001D4B0',
+ "Utilde;": '\U00000168',
+ "Uuml;": '\U000000DC',
+ "VDash;": '\U000022AB',
+ "Vbar;": '\U00002AEB',
+ "Vcy;": '\U00000412',
+ "Vdash;": '\U000022A9',
+ "Vdashl;": '\U00002AE6',
+ "Vee;": '\U000022C1',
+ "Verbar;": '\U00002016',
+ "Vert;": '\U00002016',
+ "VerticalBar;": '\U00002223',
+ "VerticalLine;": '\U0000007C',
+ "VerticalSeparator;": '\U00002758',
+ "VerticalTilde;": '\U00002240',
+ "VeryThinSpace;": '\U0000200A',
+ "Vfr;": '\U0001D519',
+ "Vopf;": '\U0001D54D',
+ "Vscr;": '\U0001D4B1',
+ "Vvdash;": '\U000022AA',
+ "Wcirc;": '\U00000174',
+ "Wedge;": '\U000022C0',
+ "Wfr;": '\U0001D51A',
+ "Wopf;": '\U0001D54E',
+ "Wscr;": '\U0001D4B2',
+ "Xfr;": '\U0001D51B',
+ "Xi;": '\U0000039E',
+ "Xopf;": '\U0001D54F',
+ "Xscr;": '\U0001D4B3',
+ "YAcy;": '\U0000042F',
+ "YIcy;": '\U00000407',
+ "YUcy;": '\U0000042E',
+ "Yacute;": '\U000000DD',
+ "Ycirc;": '\U00000176',
+ "Ycy;": '\U0000042B',
+ "Yfr;": '\U0001D51C',
+ "Yopf;": '\U0001D550',
+ "Yscr;": '\U0001D4B4',
+ "Yuml;": '\U00000178',
+ "ZHcy;": '\U00000416',
+ "Zacute;": '\U00000179',
+ "Zcaron;": '\U0000017D',
+ "Zcy;": '\U00000417',
+ "Zdot;": '\U0000017B',
+ "ZeroWidthSpace;": '\U0000200B',
+ "Zeta;": '\U00000396',
+ "Zfr;": '\U00002128',
+ "Zopf;": '\U00002124',
+ "Zscr;": '\U0001D4B5',
+ "aacute;": '\U000000E1',
+ "abreve;": '\U00000103',
+ "ac;": '\U0000223E',
+ "acd;": '\U0000223F',
+ "acirc;": '\U000000E2',
+ "acute;": '\U000000B4',
+ "acy;": '\U00000430',
+ "aelig;": '\U000000E6',
+ "af;": '\U00002061',
+ "afr;": '\U0001D51E',
+ "agrave;": '\U000000E0',
+ "alefsym;": '\U00002135',
+ "aleph;": '\U00002135',
+ "alpha;": '\U000003B1',
+ "amacr;": '\U00000101',
+ "amalg;": '\U00002A3F',
+ "amp;": '\U00000026',
+ "and;": '\U00002227',
+ "andand;": '\U00002A55',
+ "andd;": '\U00002A5C',
+ "andslope;": '\U00002A58',
+ "andv;": '\U00002A5A',
+ "ang;": '\U00002220',
+ "ange;": '\U000029A4',
+ "angle;": '\U00002220',
+ "angmsd;": '\U00002221',
+ "angmsdaa;": '\U000029A8',
+ "angmsdab;": '\U000029A9',
+ "angmsdac;": '\U000029AA',
+ "angmsdad;": '\U000029AB',
+ "angmsdae;": '\U000029AC',
+ "angmsdaf;": '\U000029AD',
+ "angmsdag;": '\U000029AE',
+ "angmsdah;": '\U000029AF',
+ "angrt;": '\U0000221F',
+ "angrtvb;": '\U000022BE',
+ "angrtvbd;": '\U0000299D',
+ "angsph;": '\U00002222',
+ "angst;": '\U000000C5',
+ "angzarr;": '\U0000237C',
+ "aogon;": '\U00000105',
+ "aopf;": '\U0001D552',
+ "ap;": '\U00002248',
+ "apE;": '\U00002A70',
+ "apacir;": '\U00002A6F',
+ "ape;": '\U0000224A',
+ "apid;": '\U0000224B',
+ "apos;": '\U00000027',
+ "approx;": '\U00002248',
+ "approxeq;": '\U0000224A',
+ "aring;": '\U000000E5',
+ "ascr;": '\U0001D4B6',
+ "ast;": '\U0000002A',
+ "asymp;": '\U00002248',
+ "asympeq;": '\U0000224D',
+ "atilde;": '\U000000E3',
+ "auml;": '\U000000E4',
+ "awconint;": '\U00002233',
+ "awint;": '\U00002A11',
+ "bNot;": '\U00002AED',
+ "backcong;": '\U0000224C',
+ "backepsilon;": '\U000003F6',
+ "backprime;": '\U00002035',
+ "backsim;": '\U0000223D',
+ "backsimeq;": '\U000022CD',
+ "barvee;": '\U000022BD',
+ "barwed;": '\U00002305',
+ "barwedge;": '\U00002305',
+ "bbrk;": '\U000023B5',
+ "bbrktbrk;": '\U000023B6',
+ "bcong;": '\U0000224C',
+ "bcy;": '\U00000431',
+ "bdquo;": '\U0000201E',
+ "becaus;": '\U00002235',
+ "because;": '\U00002235',
+ "bemptyv;": '\U000029B0',
+ "bepsi;": '\U000003F6',
+ "bernou;": '\U0000212C',
+ "beta;": '\U000003B2',
+ "beth;": '\U00002136',
+ "between;": '\U0000226C',
+ "bfr;": '\U0001D51F',
+ "bigcap;": '\U000022C2',
+ "bigcirc;": '\U000025EF',
+ "bigcup;": '\U000022C3',
+ "bigodot;": '\U00002A00',
+ "bigoplus;": '\U00002A01',
+ "bigotimes;": '\U00002A02',
+ "bigsqcup;": '\U00002A06',
+ "bigstar;": '\U00002605',
+ "bigtriangledown;": '\U000025BD',
+ "bigtriangleup;": '\U000025B3',
+ "biguplus;": '\U00002A04',
+ "bigvee;": '\U000022C1',
+ "bigwedge;": '\U000022C0',
+ "bkarow;": '\U0000290D',
+ "blacklozenge;": '\U000029EB',
+ "blacksquare;": '\U000025AA',
+ "blacktriangle;": '\U000025B4',
+ "blacktriangledown;": '\U000025BE',
+ "blacktriangleleft;": '\U000025C2',
+ "blacktriangleright;": '\U000025B8',
+ "blank;": '\U00002423',
+ "blk12;": '\U00002592',
+ "blk14;": '\U00002591',
+ "blk34;": '\U00002593',
+ "block;": '\U00002588',
+ "bnot;": '\U00002310',
+ "bopf;": '\U0001D553',
+ "bot;": '\U000022A5',
+ "bottom;": '\U000022A5',
+ "bowtie;": '\U000022C8',
+ "boxDL;": '\U00002557',
+ "boxDR;": '\U00002554',
+ "boxDl;": '\U00002556',
+ "boxDr;": '\U00002553',
+ "boxH;": '\U00002550',
+ "boxHD;": '\U00002566',
+ "boxHU;": '\U00002569',
+ "boxHd;": '\U00002564',
+ "boxHu;": '\U00002567',
+ "boxUL;": '\U0000255D',
+ "boxUR;": '\U0000255A',
+ "boxUl;": '\U0000255C',
+ "boxUr;": '\U00002559',
+ "boxV;": '\U00002551',
+ "boxVH;": '\U0000256C',
+ "boxVL;": '\U00002563',
+ "boxVR;": '\U00002560',
+ "boxVh;": '\U0000256B',
+ "boxVl;": '\U00002562',
+ "boxVr;": '\U0000255F',
+ "boxbox;": '\U000029C9',
+ "boxdL;": '\U00002555',
+ "boxdR;": '\U00002552',
+ "boxdl;": '\U00002510',
+ "boxdr;": '\U0000250C',
+ "boxh;": '\U00002500',
+ "boxhD;": '\U00002565',
+ "boxhU;": '\U00002568',
+ "boxhd;": '\U0000252C',
+ "boxhu;": '\U00002534',
+ "boxminus;": '\U0000229F',
+ "boxplus;": '\U0000229E',
+ "boxtimes;": '\U000022A0',
+ "boxuL;": '\U0000255B',
+ "boxuR;": '\U00002558',
+ "boxul;": '\U00002518',
+ "boxur;": '\U00002514',
+ "boxv;": '\U00002502',
+ "boxvH;": '\U0000256A',
+ "boxvL;": '\U00002561',
+ "boxvR;": '\U0000255E',
+ "boxvh;": '\U0000253C',
+ "boxvl;": '\U00002524',
+ "boxvr;": '\U0000251C',
+ "bprime;": '\U00002035',
+ "breve;": '\U000002D8',
+ "brvbar;": '\U000000A6',
+ "bscr;": '\U0001D4B7',
+ "bsemi;": '\U0000204F',
+ "bsim;": '\U0000223D',
+ "bsime;": '\U000022CD',
+ "bsol;": '\U0000005C',
+ "bsolb;": '\U000029C5',
+ "bsolhsub;": '\U000027C8',
+ "bull;": '\U00002022',
+ "bullet;": '\U00002022',
+ "bump;": '\U0000224E',
+ "bumpE;": '\U00002AAE',
+ "bumpe;": '\U0000224F',
+ "bumpeq;": '\U0000224F',
+ "cacute;": '\U00000107',
+ "cap;": '\U00002229',
+ "capand;": '\U00002A44',
+ "capbrcup;": '\U00002A49',
+ "capcap;": '\U00002A4B',
+ "capcup;": '\U00002A47',
+ "capdot;": '\U00002A40',
+ "caret;": '\U00002041',
+ "caron;": '\U000002C7',
+ "ccaps;": '\U00002A4D',
+ "ccaron;": '\U0000010D',
+ "ccedil;": '\U000000E7',
+ "ccirc;": '\U00000109',
+ "ccups;": '\U00002A4C',
+ "ccupssm;": '\U00002A50',
+ "cdot;": '\U0000010B',
+ "cedil;": '\U000000B8',
+ "cemptyv;": '\U000029B2',
+ "cent;": '\U000000A2',
+ "centerdot;": '\U000000B7',
+ "cfr;": '\U0001D520',
+ "chcy;": '\U00000447',
+ "check;": '\U00002713',
+ "checkmark;": '\U00002713',
+ "chi;": '\U000003C7',
+ "cir;": '\U000025CB',
+ "cirE;": '\U000029C3',
+ "circ;": '\U000002C6',
+ "circeq;": '\U00002257',
+ "circlearrowleft;": '\U000021BA',
+ "circlearrowright;": '\U000021BB',
+ "circledR;": '\U000000AE',
+ "circledS;": '\U000024C8',
+ "circledast;": '\U0000229B',
+ "circledcirc;": '\U0000229A',
+ "circleddash;": '\U0000229D',
+ "cire;": '\U00002257',
+ "cirfnint;": '\U00002A10',
+ "cirmid;": '\U00002AEF',
+ "cirscir;": '\U000029C2',
+ "clubs;": '\U00002663',
+ "clubsuit;": '\U00002663',
+ "colon;": '\U0000003A',
+ "colone;": '\U00002254',
+ "coloneq;": '\U00002254',
+ "comma;": '\U0000002C',
+ "commat;": '\U00000040',
+ "comp;": '\U00002201',
+ "compfn;": '\U00002218',
+ "complement;": '\U00002201',
+ "complexes;": '\U00002102',
+ "cong;": '\U00002245',
+ "congdot;": '\U00002A6D',
+ "conint;": '\U0000222E',
+ "copf;": '\U0001D554',
+ "coprod;": '\U00002210',
+ "copy;": '\U000000A9',
+ "copysr;": '\U00002117',
+ "crarr;": '\U000021B5',
+ "cross;": '\U00002717',
+ "cscr;": '\U0001D4B8',
+ "csub;": '\U00002ACF',
+ "csube;": '\U00002AD1',
+ "csup;": '\U00002AD0',
+ "csupe;": '\U00002AD2',
+ "ctdot;": '\U000022EF',
+ "cudarrl;": '\U00002938',
+ "cudarrr;": '\U00002935',
+ "cuepr;": '\U000022DE',
+ "cuesc;": '\U000022DF',
+ "cularr;": '\U000021B6',
+ "cularrp;": '\U0000293D',
+ "cup;": '\U0000222A',
+ "cupbrcap;": '\U00002A48',
+ "cupcap;": '\U00002A46',
+ "cupcup;": '\U00002A4A',
+ "cupdot;": '\U0000228D',
+ "cupor;": '\U00002A45',
+ "curarr;": '\U000021B7',
+ "curarrm;": '\U0000293C',
+ "curlyeqprec;": '\U000022DE',
+ "curlyeqsucc;": '\U000022DF',
+ "curlyvee;": '\U000022CE',
+ "curlywedge;": '\U000022CF',
+ "curren;": '\U000000A4',
+ "curvearrowleft;": '\U000021B6',
+ "curvearrowright;": '\U000021B7',
+ "cuvee;": '\U000022CE',
+ "cuwed;": '\U000022CF',
+ "cwconint;": '\U00002232',
+ "cwint;": '\U00002231',
+ "cylcty;": '\U0000232D',
+ "dArr;": '\U000021D3',
+ "dHar;": '\U00002965',
+ "dagger;": '\U00002020',
+ "daleth;": '\U00002138',
+ "darr;": '\U00002193',
+ "dash;": '\U00002010',
+ "dashv;": '\U000022A3',
+ "dbkarow;": '\U0000290F',
+ "dblac;": '\U000002DD',
+ "dcaron;": '\U0000010F',
+ "dcy;": '\U00000434',
+ "dd;": '\U00002146',
+ "ddagger;": '\U00002021',
+ "ddarr;": '\U000021CA',
+ "ddotseq;": '\U00002A77',
+ "deg;": '\U000000B0',
+ "delta;": '\U000003B4',
+ "demptyv;": '\U000029B1',
+ "dfisht;": '\U0000297F',
+ "dfr;": '\U0001D521',
+ "dharl;": '\U000021C3',
+ "dharr;": '\U000021C2',
+ "diam;": '\U000022C4',
+ "diamond;": '\U000022C4',
+ "diamondsuit;": '\U00002666',
+ "diams;": '\U00002666',
+ "die;": '\U000000A8',
+ "digamma;": '\U000003DD',
+ "disin;": '\U000022F2',
+ "div;": '\U000000F7',
+ "divide;": '\U000000F7',
+ "divideontimes;": '\U000022C7',
+ "divonx;": '\U000022C7',
+ "djcy;": '\U00000452',
+ "dlcorn;": '\U0000231E',
+ "dlcrop;": '\U0000230D',
+ "dollar;": '\U00000024',
+ "dopf;": '\U0001D555',
+ "dot;": '\U000002D9',
+ "doteq;": '\U00002250',
+ "doteqdot;": '\U00002251',
+ "dotminus;": '\U00002238',
+ "dotplus;": '\U00002214',
+ "dotsquare;": '\U000022A1',
+ "doublebarwedge;": '\U00002306',
+ "downarrow;": '\U00002193',
+ "downdownarrows;": '\U000021CA',
+ "downharpoonleft;": '\U000021C3',
+ "downharpoonright;": '\U000021C2',
+ "drbkarow;": '\U00002910',
+ "drcorn;": '\U0000231F',
+ "drcrop;": '\U0000230C',
+ "dscr;": '\U0001D4B9',
+ "dscy;": '\U00000455',
+ "dsol;": '\U000029F6',
+ "dstrok;": '\U00000111',
+ "dtdot;": '\U000022F1',
+ "dtri;": '\U000025BF',
+ "dtrif;": '\U000025BE',
+ "duarr;": '\U000021F5',
+ "duhar;": '\U0000296F',
+ "dwangle;": '\U000029A6',
+ "dzcy;": '\U0000045F',
+ "dzigrarr;": '\U000027FF',
+ "eDDot;": '\U00002A77',
+ "eDot;": '\U00002251',
+ "eacute;": '\U000000E9',
+ "easter;": '\U00002A6E',
+ "ecaron;": '\U0000011B',
+ "ecir;": '\U00002256',
+ "ecirc;": '\U000000EA',
+ "ecolon;": '\U00002255',
+ "ecy;": '\U0000044D',
+ "edot;": '\U00000117',
+ "ee;": '\U00002147',
+ "efDot;": '\U00002252',
+ "efr;": '\U0001D522',
+ "eg;": '\U00002A9A',
+ "egrave;": '\U000000E8',
+ "egs;": '\U00002A96',
+ "egsdot;": '\U00002A98',
+ "el;": '\U00002A99',
+ "elinters;": '\U000023E7',
+ "ell;": '\U00002113',
+ "els;": '\U00002A95',
+ "elsdot;": '\U00002A97',
+ "emacr;": '\U00000113',
+ "empty;": '\U00002205',
+ "emptyset;": '\U00002205',
+ "emptyv;": '\U00002205',
+ "emsp;": '\U00002003',
+ "emsp13;": '\U00002004',
+ "emsp14;": '\U00002005',
+ "eng;": '\U0000014B',
+ "ensp;": '\U00002002',
+ "eogon;": '\U00000119',
+ "eopf;": '\U0001D556',
+ "epar;": '\U000022D5',
+ "eparsl;": '\U000029E3',
+ "eplus;": '\U00002A71',
+ "epsi;": '\U000003B5',
+ "epsilon;": '\U000003B5',
+ "epsiv;": '\U000003F5',
+ "eqcirc;": '\U00002256',
+ "eqcolon;": '\U00002255',
+ "eqsim;": '\U00002242',
+ "eqslantgtr;": '\U00002A96',
+ "eqslantless;": '\U00002A95',
+ "equals;": '\U0000003D',
+ "equest;": '\U0000225F',
+ "equiv;": '\U00002261',
+ "equivDD;": '\U00002A78',
+ "eqvparsl;": '\U000029E5',
+ "erDot;": '\U00002253',
+ "erarr;": '\U00002971',
+ "escr;": '\U0000212F',
+ "esdot;": '\U00002250',
+ "esim;": '\U00002242',
+ "eta;": '\U000003B7',
+ "eth;": '\U000000F0',
+ "euml;": '\U000000EB',
+ "euro;": '\U000020AC',
+ "excl;": '\U00000021',
+ "exist;": '\U00002203',
+ "expectation;": '\U00002130',
+ "exponentiale;": '\U00002147',
+ "fallingdotseq;": '\U00002252',
+ "fcy;": '\U00000444',
+ "female;": '\U00002640',
+ "ffilig;": '\U0000FB03',
+ "fflig;": '\U0000FB00',
+ "ffllig;": '\U0000FB04',
+ "ffr;": '\U0001D523',
+ "filig;": '\U0000FB01',
+ "flat;": '\U0000266D',
+ "fllig;": '\U0000FB02',
+ "fltns;": '\U000025B1',
+ "fnof;": '\U00000192',
+ "fopf;": '\U0001D557',
+ "forall;": '\U00002200',
+ "fork;": '\U000022D4',
+ "forkv;": '\U00002AD9',
+ "fpartint;": '\U00002A0D',
+ "frac12;": '\U000000BD',
+ "frac13;": '\U00002153',
+ "frac14;": '\U000000BC',
+ "frac15;": '\U00002155',
+ "frac16;": '\U00002159',
+ "frac18;": '\U0000215B',
+ "frac23;": '\U00002154',
+ "frac25;": '\U00002156',
+ "frac34;": '\U000000BE',
+ "frac35;": '\U00002157',
+ "frac38;": '\U0000215C',
+ "frac45;": '\U00002158',
+ "frac56;": '\U0000215A',
+ "frac58;": '\U0000215D',
+ "frac78;": '\U0000215E',
+ "frasl;": '\U00002044',
+ "frown;": '\U00002322',
+ "fscr;": '\U0001D4BB',
+ "gE;": '\U00002267',
+ "gEl;": '\U00002A8C',
+ "gacute;": '\U000001F5',
+ "gamma;": '\U000003B3',
+ "gammad;": '\U000003DD',
+ "gap;": '\U00002A86',
+ "gbreve;": '\U0000011F',
+ "gcirc;": '\U0000011D',
+ "gcy;": '\U00000433',
+ "gdot;": '\U00000121',
+ "ge;": '\U00002265',
+ "gel;": '\U000022DB',
+ "geq;": '\U00002265',
+ "geqq;": '\U00002267',
+ "geqslant;": '\U00002A7E',
+ "ges;": '\U00002A7E',
+ "gescc;": '\U00002AA9',
+ "gesdot;": '\U00002A80',
+ "gesdoto;": '\U00002A82',
+ "gesdotol;": '\U00002A84',
+ "gesles;": '\U00002A94',
+ "gfr;": '\U0001D524',
+ "gg;": '\U0000226B',
+ "ggg;": '\U000022D9',
+ "gimel;": '\U00002137',
+ "gjcy;": '\U00000453',
+ "gl;": '\U00002277',
+ "glE;": '\U00002A92',
+ "gla;": '\U00002AA5',
+ "glj;": '\U00002AA4',
+ "gnE;": '\U00002269',
+ "gnap;": '\U00002A8A',
+ "gnapprox;": '\U00002A8A',
+ "gne;": '\U00002A88',
+ "gneq;": '\U00002A88',
+ "gneqq;": '\U00002269',
+ "gnsim;": '\U000022E7',
+ "gopf;": '\U0001D558',
+ "grave;": '\U00000060',
+ "gscr;": '\U0000210A',
+ "gsim;": '\U00002273',
+ "gsime;": '\U00002A8E',
+ "gsiml;": '\U00002A90',
+ "gt;": '\U0000003E',
+ "gtcc;": '\U00002AA7',
+ "gtcir;": '\U00002A7A',
+ "gtdot;": '\U000022D7',
+ "gtlPar;": '\U00002995',
+ "gtquest;": '\U00002A7C',
+ "gtrapprox;": '\U00002A86',
+ "gtrarr;": '\U00002978',
+ "gtrdot;": '\U000022D7',
+ "gtreqless;": '\U000022DB',
+ "gtreqqless;": '\U00002A8C',
+ "gtrless;": '\U00002277',
+ "gtrsim;": '\U00002273',
+ "hArr;": '\U000021D4',
+ "hairsp;": '\U0000200A',
+ "half;": '\U000000BD',
+ "hamilt;": '\U0000210B',
+ "hardcy;": '\U0000044A',
+ "harr;": '\U00002194',
+ "harrcir;": '\U00002948',
+ "harrw;": '\U000021AD',
+ "hbar;": '\U0000210F',
+ "hcirc;": '\U00000125',
+ "hearts;": '\U00002665',
+ "heartsuit;": '\U00002665',
+ "hellip;": '\U00002026',
+ "hercon;": '\U000022B9',
+ "hfr;": '\U0001D525',
+ "hksearow;": '\U00002925',
+ "hkswarow;": '\U00002926',
+ "hoarr;": '\U000021FF',
+ "homtht;": '\U0000223B',
+ "hookleftarrow;": '\U000021A9',
+ "hookrightarrow;": '\U000021AA',
+ "hopf;": '\U0001D559',
+ "horbar;": '\U00002015',
+ "hscr;": '\U0001D4BD',
+ "hslash;": '\U0000210F',
+ "hstrok;": '\U00000127',
+ "hybull;": '\U00002043',
+ "hyphen;": '\U00002010',
+ "iacute;": '\U000000ED',
+ "ic;": '\U00002063',
+ "icirc;": '\U000000EE',
+ "icy;": '\U00000438',
+ "iecy;": '\U00000435',
+ "iexcl;": '\U000000A1',
+ "iff;": '\U000021D4',
+ "ifr;": '\U0001D526',
+ "igrave;": '\U000000EC',
+ "ii;": '\U00002148',
+ "iiiint;": '\U00002A0C',
+ "iiint;": '\U0000222D',
+ "iinfin;": '\U000029DC',
+ "iiota;": '\U00002129',
+ "ijlig;": '\U00000133',
+ "imacr;": '\U0000012B',
+ "image;": '\U00002111',
+ "imagline;": '\U00002110',
+ "imagpart;": '\U00002111',
+ "imath;": '\U00000131',
+ "imof;": '\U000022B7',
+ "imped;": '\U000001B5',
+ "in;": '\U00002208',
+ "incare;": '\U00002105',
+ "infin;": '\U0000221E',
+ "infintie;": '\U000029DD',
+ "inodot;": '\U00000131',
+ "int;": '\U0000222B',
+ "intcal;": '\U000022BA',
+ "integers;": '\U00002124',
+ "intercal;": '\U000022BA',
+ "intlarhk;": '\U00002A17',
+ "intprod;": '\U00002A3C',
+ "iocy;": '\U00000451',
+ "iogon;": '\U0000012F',
+ "iopf;": '\U0001D55A',
+ "iota;": '\U000003B9',
+ "iprod;": '\U00002A3C',
+ "iquest;": '\U000000BF',
+ "iscr;": '\U0001D4BE',
+ "isin;": '\U00002208',
+ "isinE;": '\U000022F9',
+ "isindot;": '\U000022F5',
+ "isins;": '\U000022F4',
+ "isinsv;": '\U000022F3',
+ "isinv;": '\U00002208',
+ "it;": '\U00002062',
+ "itilde;": '\U00000129',
+ "iukcy;": '\U00000456',
+ "iuml;": '\U000000EF',
+ "jcirc;": '\U00000135',
+ "jcy;": '\U00000439',
+ "jfr;": '\U0001D527',
+ "jmath;": '\U00000237',
+ "jopf;": '\U0001D55B',
+ "jscr;": '\U0001D4BF',
+ "jsercy;": '\U00000458',
+ "jukcy;": '\U00000454',
+ "kappa;": '\U000003BA',
+ "kappav;": '\U000003F0',
+ "kcedil;": '\U00000137',
+ "kcy;": '\U0000043A',
+ "kfr;": '\U0001D528',
+ "kgreen;": '\U00000138',
+ "khcy;": '\U00000445',
+ "kjcy;": '\U0000045C',
+ "kopf;": '\U0001D55C',
+ "kscr;": '\U0001D4C0',
+ "lAarr;": '\U000021DA',
+ "lArr;": '\U000021D0',
+ "lAtail;": '\U0000291B',
+ "lBarr;": '\U0000290E',
+ "lE;": '\U00002266',
+ "lEg;": '\U00002A8B',
+ "lHar;": '\U00002962',
+ "lacute;": '\U0000013A',
+ "laemptyv;": '\U000029B4',
+ "lagran;": '\U00002112',
+ "lambda;": '\U000003BB',
+ "lang;": '\U000027E8',
+ "langd;": '\U00002991',
+ "langle;": '\U000027E8',
+ "lap;": '\U00002A85',
+ "laquo;": '\U000000AB',
+ "larr;": '\U00002190',
+ "larrb;": '\U000021E4',
+ "larrbfs;": '\U0000291F',
+ "larrfs;": '\U0000291D',
+ "larrhk;": '\U000021A9',
+ "larrlp;": '\U000021AB',
+ "larrpl;": '\U00002939',
+ "larrsim;": '\U00002973',
+ "larrtl;": '\U000021A2',
+ "lat;": '\U00002AAB',
+ "latail;": '\U00002919',
+ "late;": '\U00002AAD',
+ "lbarr;": '\U0000290C',
+ "lbbrk;": '\U00002772',
+ "lbrace;": '\U0000007B',
+ "lbrack;": '\U0000005B',
+ "lbrke;": '\U0000298B',
+ "lbrksld;": '\U0000298F',
+ "lbrkslu;": '\U0000298D',
+ "lcaron;": '\U0000013E',
+ "lcedil;": '\U0000013C',
+ "lceil;": '\U00002308',
+ "lcub;": '\U0000007B',
+ "lcy;": '\U0000043B',
+ "ldca;": '\U00002936',
+ "ldquo;": '\U0000201C',
+ "ldquor;": '\U0000201E',
+ "ldrdhar;": '\U00002967',
+ "ldrushar;": '\U0000294B',
+ "ldsh;": '\U000021B2',
+ "le;": '\U00002264',
+ "leftarrow;": '\U00002190',
+ "leftarrowtail;": '\U000021A2',
+ "leftharpoondown;": '\U000021BD',
+ "leftharpoonup;": '\U000021BC',
+ "leftleftarrows;": '\U000021C7',
+ "leftrightarrow;": '\U00002194',
+ "leftrightarrows;": '\U000021C6',
+ "leftrightharpoons;": '\U000021CB',
+ "leftrightsquigarrow;": '\U000021AD',
+ "leftthreetimes;": '\U000022CB',
+ "leg;": '\U000022DA',
+ "leq;": '\U00002264',
+ "leqq;": '\U00002266',
+ "leqslant;": '\U00002A7D',
+ "les;": '\U00002A7D',
+ "lescc;": '\U00002AA8',
+ "lesdot;": '\U00002A7F',
+ "lesdoto;": '\U00002A81',
+ "lesdotor;": '\U00002A83',
+ "lesges;": '\U00002A93',
+ "lessapprox;": '\U00002A85',
+ "lessdot;": '\U000022D6',
+ "lesseqgtr;": '\U000022DA',
+ "lesseqqgtr;": '\U00002A8B',
+ "lessgtr;": '\U00002276',
+ "lesssim;": '\U00002272',
+ "lfisht;": '\U0000297C',
+ "lfloor;": '\U0000230A',
+ "lfr;": '\U0001D529',
+ "lg;": '\U00002276',
+ "lgE;": '\U00002A91',
+ "lhard;": '\U000021BD',
+ "lharu;": '\U000021BC',
+ "lharul;": '\U0000296A',
+ "lhblk;": '\U00002584',
+ "ljcy;": '\U00000459',
+ "ll;": '\U0000226A',
+ "llarr;": '\U000021C7',
+ "llcorner;": '\U0000231E',
+ "llhard;": '\U0000296B',
+ "lltri;": '\U000025FA',
+ "lmidot;": '\U00000140',
+ "lmoust;": '\U000023B0',
+ "lmoustache;": '\U000023B0',
+ "lnE;": '\U00002268',
+ "lnap;": '\U00002A89',
+ "lnapprox;": '\U00002A89',
+ "lne;": '\U00002A87',
+ "lneq;": '\U00002A87',
+ "lneqq;": '\U00002268',
+ "lnsim;": '\U000022E6',
+ "loang;": '\U000027EC',
+ "loarr;": '\U000021FD',
+ "lobrk;": '\U000027E6',
+ "longleftarrow;": '\U000027F5',
+ "longleftrightarrow;": '\U000027F7',
+ "longmapsto;": '\U000027FC',
+ "longrightarrow;": '\U000027F6',
+ "looparrowleft;": '\U000021AB',
+ "looparrowright;": '\U000021AC',
+ "lopar;": '\U00002985',
+ "lopf;": '\U0001D55D',
+ "loplus;": '\U00002A2D',
+ "lotimes;": '\U00002A34',
+ "lowast;": '\U00002217',
+ "lowbar;": '\U0000005F',
+ "loz;": '\U000025CA',
+ "lozenge;": '\U000025CA',
+ "lozf;": '\U000029EB',
+ "lpar;": '\U00000028',
+ "lparlt;": '\U00002993',
+ "lrarr;": '\U000021C6',
+ "lrcorner;": '\U0000231F',
+ "lrhar;": '\U000021CB',
+ "lrhard;": '\U0000296D',
+ "lrm;": '\U0000200E',
+ "lrtri;": '\U000022BF',
+ "lsaquo;": '\U00002039',
+ "lscr;": '\U0001D4C1',
+ "lsh;": '\U000021B0',
+ "lsim;": '\U00002272',
+ "lsime;": '\U00002A8D',
+ "lsimg;": '\U00002A8F',
+ "lsqb;": '\U0000005B',
+ "lsquo;": '\U00002018',
+ "lsquor;": '\U0000201A',
+ "lstrok;": '\U00000142',
+ "lt;": '\U0000003C',
+ "ltcc;": '\U00002AA6',
+ "ltcir;": '\U00002A79',
+ "ltdot;": '\U000022D6',
+ "lthree;": '\U000022CB',
+ "ltimes;": '\U000022C9',
+ "ltlarr;": '\U00002976',
+ "ltquest;": '\U00002A7B',
+ "ltrPar;": '\U00002996',
+ "ltri;": '\U000025C3',
+ "ltrie;": '\U000022B4',
+ "ltrif;": '\U000025C2',
+ "lurdshar;": '\U0000294A',
+ "luruhar;": '\U00002966',
+ "mDDot;": '\U0000223A',
+ "macr;": '\U000000AF',
+ "male;": '\U00002642',
+ "malt;": '\U00002720',
+ "maltese;": '\U00002720',
+ "map;": '\U000021A6',
+ "mapsto;": '\U000021A6',
+ "mapstodown;": '\U000021A7',
+ "mapstoleft;": '\U000021A4',
+ "mapstoup;": '\U000021A5',
+ "marker;": '\U000025AE',
+ "mcomma;": '\U00002A29',
+ "mcy;": '\U0000043C',
+ "mdash;": '\U00002014',
+ "measuredangle;": '\U00002221',
+ "mfr;": '\U0001D52A',
+ "mho;": '\U00002127',
+ "micro;": '\U000000B5',
+ "mid;": '\U00002223',
+ "midast;": '\U0000002A',
+ "midcir;": '\U00002AF0',
+ "middot;": '\U000000B7',
+ "minus;": '\U00002212',
+ "minusb;": '\U0000229F',
+ "minusd;": '\U00002238',
+ "minusdu;": '\U00002A2A',
+ "mlcp;": '\U00002ADB',
+ "mldr;": '\U00002026',
+ "mnplus;": '\U00002213',
+ "models;": '\U000022A7',
+ "mopf;": '\U0001D55E',
+ "mp;": '\U00002213',
+ "mscr;": '\U0001D4C2',
+ "mstpos;": '\U0000223E',
+ "mu;": '\U000003BC',
+ "multimap;": '\U000022B8',
+ "mumap;": '\U000022B8',
+ "nLeftarrow;": '\U000021CD',
+ "nLeftrightarrow;": '\U000021CE',
+ "nRightarrow;": '\U000021CF',
+ "nVDash;": '\U000022AF',
+ "nVdash;": '\U000022AE',
+ "nabla;": '\U00002207',
+ "nacute;": '\U00000144',
+ "nap;": '\U00002249',
+ "napos;": '\U00000149',
+ "napprox;": '\U00002249',
+ "natur;": '\U0000266E',
+ "natural;": '\U0000266E',
+ "naturals;": '\U00002115',
+ "nbsp;": '\U000000A0',
+ "ncap;": '\U00002A43',
+ "ncaron;": '\U00000148',
+ "ncedil;": '\U00000146',
+ "ncong;": '\U00002247',
+ "ncup;": '\U00002A42',
+ "ncy;": '\U0000043D',
+ "ndash;": '\U00002013',
+ "ne;": '\U00002260',
+ "neArr;": '\U000021D7',
+ "nearhk;": '\U00002924',
+ "nearr;": '\U00002197',
+ "nearrow;": '\U00002197',
+ "nequiv;": '\U00002262',
+ "nesear;": '\U00002928',
+ "nexist;": '\U00002204',
+ "nexists;": '\U00002204',
+ "nfr;": '\U0001D52B',
+ "nge;": '\U00002271',
+ "ngeq;": '\U00002271',
+ "ngsim;": '\U00002275',
+ "ngt;": '\U0000226F',
+ "ngtr;": '\U0000226F',
+ "nhArr;": '\U000021CE',
+ "nharr;": '\U000021AE',
+ "nhpar;": '\U00002AF2',
+ "ni;": '\U0000220B',
+ "nis;": '\U000022FC',
+ "nisd;": '\U000022FA',
+ "niv;": '\U0000220B',
+ "njcy;": '\U0000045A',
+ "nlArr;": '\U000021CD',
+ "nlarr;": '\U0000219A',
+ "nldr;": '\U00002025',
+ "nle;": '\U00002270',
+ "nleftarrow;": '\U0000219A',
+ "nleftrightarrow;": '\U000021AE',
+ "nleq;": '\U00002270',
+ "nless;": '\U0000226E',
+ "nlsim;": '\U00002274',
+ "nlt;": '\U0000226E',
+ "nltri;": '\U000022EA',
+ "nltrie;": '\U000022EC',
+ "nmid;": '\U00002224',
+ "nopf;": '\U0001D55F',
+ "not;": '\U000000AC',
+ "notin;": '\U00002209',
+ "notinva;": '\U00002209',
+ "notinvb;": '\U000022F7',
+ "notinvc;": '\U000022F6',
+ "notni;": '\U0000220C',
+ "notniva;": '\U0000220C',
+ "notnivb;": '\U000022FE',
+ "notnivc;": '\U000022FD',
+ "npar;": '\U00002226',
+ "nparallel;": '\U00002226',
+ "npolint;": '\U00002A14',
+ "npr;": '\U00002280',
+ "nprcue;": '\U000022E0',
+ "nprec;": '\U00002280',
+ "nrArr;": '\U000021CF',
+ "nrarr;": '\U0000219B',
+ "nrightarrow;": '\U0000219B',
+ "nrtri;": '\U000022EB',
+ "nrtrie;": '\U000022ED',
+ "nsc;": '\U00002281',
+ "nsccue;": '\U000022E1',
+ "nscr;": '\U0001D4C3',
+ "nshortmid;": '\U00002224',
+ "nshortparallel;": '\U00002226',
+ "nsim;": '\U00002241',
+ "nsime;": '\U00002244',
+ "nsimeq;": '\U00002244',
+ "nsmid;": '\U00002224',
+ "nspar;": '\U00002226',
+ "nsqsube;": '\U000022E2',
+ "nsqsupe;": '\U000022E3',
+ "nsub;": '\U00002284',
+ "nsube;": '\U00002288',
+ "nsubseteq;": '\U00002288',
+ "nsucc;": '\U00002281',
+ "nsup;": '\U00002285',
+ "nsupe;": '\U00002289',
+ "nsupseteq;": '\U00002289',
+ "ntgl;": '\U00002279',
+ "ntilde;": '\U000000F1',
+ "ntlg;": '\U00002278',
+ "ntriangleleft;": '\U000022EA',
+ "ntrianglelefteq;": '\U000022EC',
+ "ntriangleright;": '\U000022EB',
+ "ntrianglerighteq;": '\U000022ED',
+ "nu;": '\U000003BD',
+ "num;": '\U00000023',
+ "numero;": '\U00002116',
+ "numsp;": '\U00002007',
+ "nvDash;": '\U000022AD',
+ "nvHarr;": '\U00002904',
+ "nvdash;": '\U000022AC',
+ "nvinfin;": '\U000029DE',
+ "nvlArr;": '\U00002902',
+ "nvrArr;": '\U00002903',
+ "nwArr;": '\U000021D6',
+ "nwarhk;": '\U00002923',
+ "nwarr;": '\U00002196',
+ "nwarrow;": '\U00002196',
+ "nwnear;": '\U00002927',
+ "oS;": '\U000024C8',
+ "oacute;": '\U000000F3',
+ "oast;": '\U0000229B',
+ "ocir;": '\U0000229A',
+ "ocirc;": '\U000000F4',
+ "ocy;": '\U0000043E',
+ "odash;": '\U0000229D',
+ "odblac;": '\U00000151',
+ "odiv;": '\U00002A38',
+ "odot;": '\U00002299',
+ "odsold;": '\U000029BC',
+ "oelig;": '\U00000153',
+ "ofcir;": '\U000029BF',
+ "ofr;": '\U0001D52C',
+ "ogon;": '\U000002DB',
+ "ograve;": '\U000000F2',
+ "ogt;": '\U000029C1',
+ "ohbar;": '\U000029B5',
+ "ohm;": '\U000003A9',
+ "oint;": '\U0000222E',
+ "olarr;": '\U000021BA',
+ "olcir;": '\U000029BE',
+ "olcross;": '\U000029BB',
+ "oline;": '\U0000203E',
+ "olt;": '\U000029C0',
+ "omacr;": '\U0000014D',
+ "omega;": '\U000003C9',
+ "omicron;": '\U000003BF',
+ "omid;": '\U000029B6',
+ "ominus;": '\U00002296',
+ "oopf;": '\U0001D560',
+ "opar;": '\U000029B7',
+ "operp;": '\U000029B9',
+ "oplus;": '\U00002295',
+ "or;": '\U00002228',
+ "orarr;": '\U000021BB',
+ "ord;": '\U00002A5D',
+ "order;": '\U00002134',
+ "orderof;": '\U00002134',
+ "ordf;": '\U000000AA',
+ "ordm;": '\U000000BA',
+ "origof;": '\U000022B6',
+ "oror;": '\U00002A56',
+ "orslope;": '\U00002A57',
+ "orv;": '\U00002A5B',
+ "oscr;": '\U00002134',
+ "oslash;": '\U000000F8',
+ "osol;": '\U00002298',
+ "otilde;": '\U000000F5',
+ "otimes;": '\U00002297',
+ "otimesas;": '\U00002A36',
+ "ouml;": '\U000000F6',
+ "ovbar;": '\U0000233D',
+ "par;": '\U00002225',
+ "para;": '\U000000B6',
+ "parallel;": '\U00002225',
+ "parsim;": '\U00002AF3',
+ "parsl;": '\U00002AFD',
+ "part;": '\U00002202',
+ "pcy;": '\U0000043F',
+ "percnt;": '\U00000025',
+ "period;": '\U0000002E',
+ "permil;": '\U00002030',
+ "perp;": '\U000022A5',
+ "pertenk;": '\U00002031',
+ "pfr;": '\U0001D52D',
+ "phi;": '\U000003C6',
+ "phiv;": '\U000003D5',
+ "phmmat;": '\U00002133',
+ "phone;": '\U0000260E',
+ "pi;": '\U000003C0',
+ "pitchfork;": '\U000022D4',
+ "piv;": '\U000003D6',
+ "planck;": '\U0000210F',
+ "planckh;": '\U0000210E',
+ "plankv;": '\U0000210F',
+ "plus;": '\U0000002B',
+ "plusacir;": '\U00002A23',
+ "plusb;": '\U0000229E',
+ "pluscir;": '\U00002A22',
+ "plusdo;": '\U00002214',
+ "plusdu;": '\U00002A25',
+ "pluse;": '\U00002A72',
+ "plusmn;": '\U000000B1',
+ "plussim;": '\U00002A26',
+ "plustwo;": '\U00002A27',
+ "pm;": '\U000000B1',
+ "pointint;": '\U00002A15',
+ "popf;": '\U0001D561',
+ "pound;": '\U000000A3',
+ "pr;": '\U0000227A',
+ "prE;": '\U00002AB3',
+ "prap;": '\U00002AB7',
+ "prcue;": '\U0000227C',
+ "pre;": '\U00002AAF',
+ "prec;": '\U0000227A',
+ "precapprox;": '\U00002AB7',
+ "preccurlyeq;": '\U0000227C',
+ "preceq;": '\U00002AAF',
+ "precnapprox;": '\U00002AB9',
+ "precneqq;": '\U00002AB5',
+ "precnsim;": '\U000022E8',
+ "precsim;": '\U0000227E',
+ "prime;": '\U00002032',
+ "primes;": '\U00002119',
+ "prnE;": '\U00002AB5',
+ "prnap;": '\U00002AB9',
+ "prnsim;": '\U000022E8',
+ "prod;": '\U0000220F',
+ "profalar;": '\U0000232E',
+ "profline;": '\U00002312',
+ "profsurf;": '\U00002313',
+ "prop;": '\U0000221D',
+ "propto;": '\U0000221D',
+ "prsim;": '\U0000227E',
+ "prurel;": '\U000022B0',
+ "pscr;": '\U0001D4C5',
+ "psi;": '\U000003C8',
+ "puncsp;": '\U00002008',
+ "qfr;": '\U0001D52E',
+ "qint;": '\U00002A0C',
+ "qopf;": '\U0001D562',
+ "qprime;": '\U00002057',
+ "qscr;": '\U0001D4C6',
+ "quaternions;": '\U0000210D',
+ "quatint;": '\U00002A16',
+ "quest;": '\U0000003F',
+ "questeq;": '\U0000225F',
+ "quot;": '\U00000022',
+ "rAarr;": '\U000021DB',
+ "rArr;": '\U000021D2',
+ "rAtail;": '\U0000291C',
+ "rBarr;": '\U0000290F',
+ "rHar;": '\U00002964',
+ "racute;": '\U00000155',
+ "radic;": '\U0000221A',
+ "raemptyv;": '\U000029B3',
+ "rang;": '\U000027E9',
+ "rangd;": '\U00002992',
+ "range;": '\U000029A5',
+ "rangle;": '\U000027E9',
+ "raquo;": '\U000000BB',
+ "rarr;": '\U00002192',
+ "rarrap;": '\U00002975',
+ "rarrb;": '\U000021E5',
+ "rarrbfs;": '\U00002920',
+ "rarrc;": '\U00002933',
+ "rarrfs;": '\U0000291E',
+ "rarrhk;": '\U000021AA',
+ "rarrlp;": '\U000021AC',
+ "rarrpl;": '\U00002945',
+ "rarrsim;": '\U00002974',
+ "rarrtl;": '\U000021A3',
+ "rarrw;": '\U0000219D',
+ "ratail;": '\U0000291A',
+ "ratio;": '\U00002236',
+ "rationals;": '\U0000211A',
+ "rbarr;": '\U0000290D',
+ "rbbrk;": '\U00002773',
+ "rbrace;": '\U0000007D',
+ "rbrack;": '\U0000005D',
+ "rbrke;": '\U0000298C',
+ "rbrksld;": '\U0000298E',
+ "rbrkslu;": '\U00002990',
+ "rcaron;": '\U00000159',
+ "rcedil;": '\U00000157',
+ "rceil;": '\U00002309',
+ "rcub;": '\U0000007D',
+ "rcy;": '\U00000440',
+ "rdca;": '\U00002937',
+ "rdldhar;": '\U00002969',
+ "rdquo;": '\U0000201D',
+ "rdquor;": '\U0000201D',
+ "rdsh;": '\U000021B3',
+ "real;": '\U0000211C',
+ "realine;": '\U0000211B',
+ "realpart;": '\U0000211C',
+ "reals;": '\U0000211D',
+ "rect;": '\U000025AD',
+ "reg;": '\U000000AE',
+ "rfisht;": '\U0000297D',
+ "rfloor;": '\U0000230B',
+ "rfr;": '\U0001D52F',
+ "rhard;": '\U000021C1',
+ "rharu;": '\U000021C0',
+ "rharul;": '\U0000296C',
+ "rho;": '\U000003C1',
+ "rhov;": '\U000003F1',
+ "rightarrow;": '\U00002192',
+ "rightarrowtail;": '\U000021A3',
+ "rightharpoondown;": '\U000021C1',
+ "rightharpoonup;": '\U000021C0',
+ "rightleftarrows;": '\U000021C4',
+ "rightleftharpoons;": '\U000021CC',
+ "rightrightarrows;": '\U000021C9',
+ "rightsquigarrow;": '\U0000219D',
+ "rightthreetimes;": '\U000022CC',
+ "ring;": '\U000002DA',
+ "risingdotseq;": '\U00002253',
+ "rlarr;": '\U000021C4',
+ "rlhar;": '\U000021CC',
+ "rlm;": '\U0000200F',
+ "rmoust;": '\U000023B1',
+ "rmoustache;": '\U000023B1',
+ "rnmid;": '\U00002AEE',
+ "roang;": '\U000027ED',
+ "roarr;": '\U000021FE',
+ "robrk;": '\U000027E7',
+ "ropar;": '\U00002986',
+ "ropf;": '\U0001D563',
+ "roplus;": '\U00002A2E',
+ "rotimes;": '\U00002A35',
+ "rpar;": '\U00000029',
+ "rpargt;": '\U00002994',
+ "rppolint;": '\U00002A12',
+ "rrarr;": '\U000021C9',
+ "rsaquo;": '\U0000203A',
+ "rscr;": '\U0001D4C7',
+ "rsh;": '\U000021B1',
+ "rsqb;": '\U0000005D',
+ "rsquo;": '\U00002019',
+ "rsquor;": '\U00002019',
+ "rthree;": '\U000022CC',
+ "rtimes;": '\U000022CA',
+ "rtri;": '\U000025B9',
+ "rtrie;": '\U000022B5',
+ "rtrif;": '\U000025B8',
+ "rtriltri;": '\U000029CE',
+ "ruluhar;": '\U00002968',
+ "rx;": '\U0000211E',
+ "sacute;": '\U0000015B',
+ "sbquo;": '\U0000201A',
+ "sc;": '\U0000227B',
+ "scE;": '\U00002AB4',
+ "scap;": '\U00002AB8',
+ "scaron;": '\U00000161',
+ "sccue;": '\U0000227D',
+ "sce;": '\U00002AB0',
+ "scedil;": '\U0000015F',
+ "scirc;": '\U0000015D',
+ "scnE;": '\U00002AB6',
+ "scnap;": '\U00002ABA',
+ "scnsim;": '\U000022E9',
+ "scpolint;": '\U00002A13',
+ "scsim;": '\U0000227F',
+ "scy;": '\U00000441',
+ "sdot;": '\U000022C5',
+ "sdotb;": '\U000022A1',
+ "sdote;": '\U00002A66',
+ "seArr;": '\U000021D8',
+ "searhk;": '\U00002925',
+ "searr;": '\U00002198',
+ "searrow;": '\U00002198',
+ "sect;": '\U000000A7',
+ "semi;": '\U0000003B',
+ "seswar;": '\U00002929',
+ "setminus;": '\U00002216',
+ "setmn;": '\U00002216',
+ "sext;": '\U00002736',
+ "sfr;": '\U0001D530',
+ "sfrown;": '\U00002322',
+ "sharp;": '\U0000266F',
+ "shchcy;": '\U00000449',
+ "shcy;": '\U00000448',
+ "shortmid;": '\U00002223',
+ "shortparallel;": '\U00002225',
+ "shy;": '\U000000AD',
+ "sigma;": '\U000003C3',
+ "sigmaf;": '\U000003C2',
+ "sigmav;": '\U000003C2',
+ "sim;": '\U0000223C',
+ "simdot;": '\U00002A6A',
+ "sime;": '\U00002243',
+ "simeq;": '\U00002243',
+ "simg;": '\U00002A9E',
+ "simgE;": '\U00002AA0',
+ "siml;": '\U00002A9D',
+ "simlE;": '\U00002A9F',
+ "simne;": '\U00002246',
+ "simplus;": '\U00002A24',
+ "simrarr;": '\U00002972',
+ "slarr;": '\U00002190',
+ "smallsetminus;": '\U00002216',
+ "smashp;": '\U00002A33',
+ "smeparsl;": '\U000029E4',
+ "smid;": '\U00002223',
+ "smile;": '\U00002323',
+ "smt;": '\U00002AAA',
+ "smte;": '\U00002AAC',
+ "softcy;": '\U0000044C',
+ "sol;": '\U0000002F',
+ "solb;": '\U000029C4',
+ "solbar;": '\U0000233F',
+ "sopf;": '\U0001D564',
+ "spades;": '\U00002660',
+ "spadesuit;": '\U00002660',
+ "spar;": '\U00002225',
+ "sqcap;": '\U00002293',
+ "sqcup;": '\U00002294',
+ "sqsub;": '\U0000228F',
+ "sqsube;": '\U00002291',
+ "sqsubset;": '\U0000228F',
+ "sqsubseteq;": '\U00002291',
+ "sqsup;": '\U00002290',
+ "sqsupe;": '\U00002292',
+ "sqsupset;": '\U00002290',
+ "sqsupseteq;": '\U00002292',
+ "squ;": '\U000025A1',
+ "square;": '\U000025A1',
+ "squarf;": '\U000025AA',
+ "squf;": '\U000025AA',
+ "srarr;": '\U00002192',
+ "sscr;": '\U0001D4C8',
+ "ssetmn;": '\U00002216',
+ "ssmile;": '\U00002323',
+ "sstarf;": '\U000022C6',
+ "star;": '\U00002606',
+ "starf;": '\U00002605',
+ "straightepsilon;": '\U000003F5',
+ "straightphi;": '\U000003D5',
+ "strns;": '\U000000AF',
+ "sub;": '\U00002282',
+ "subE;": '\U00002AC5',
+ "subdot;": '\U00002ABD',
+ "sube;": '\U00002286',
+ "subedot;": '\U00002AC3',
+ "submult;": '\U00002AC1',
+ "subnE;": '\U00002ACB',
+ "subne;": '\U0000228A',
+ "subplus;": '\U00002ABF',
+ "subrarr;": '\U00002979',
+ "subset;": '\U00002282',
+ "subseteq;": '\U00002286',
+ "subseteqq;": '\U00002AC5',
+ "subsetneq;": '\U0000228A',
+ "subsetneqq;": '\U00002ACB',
+ "subsim;": '\U00002AC7',
+ "subsub;": '\U00002AD5',
+ "subsup;": '\U00002AD3',
+ "succ;": '\U0000227B',
+ "succapprox;": '\U00002AB8',
+ "succcurlyeq;": '\U0000227D',
+ "succeq;": '\U00002AB0',
+ "succnapprox;": '\U00002ABA',
+ "succneqq;": '\U00002AB6',
+ "succnsim;": '\U000022E9',
+ "succsim;": '\U0000227F',
+ "sum;": '\U00002211',
+ "sung;": '\U0000266A',
+ "sup;": '\U00002283',
+ "sup1;": '\U000000B9',
+ "sup2;": '\U000000B2',
+ "sup3;": '\U000000B3',
+ "supE;": '\U00002AC6',
+ "supdot;": '\U00002ABE',
+ "supdsub;": '\U00002AD8',
+ "supe;": '\U00002287',
+ "supedot;": '\U00002AC4',
+ "suphsol;": '\U000027C9',
+ "suphsub;": '\U00002AD7',
+ "suplarr;": '\U0000297B',
+ "supmult;": '\U00002AC2',
+ "supnE;": '\U00002ACC',
+ "supne;": '\U0000228B',
+ "supplus;": '\U00002AC0',
+ "supset;": '\U00002283',
+ "supseteq;": '\U00002287',
+ "supseteqq;": '\U00002AC6',
+ "supsetneq;": '\U0000228B',
+ "supsetneqq;": '\U00002ACC',
+ "supsim;": '\U00002AC8',
+ "supsub;": '\U00002AD4',
+ "supsup;": '\U00002AD6',
+ "swArr;": '\U000021D9',
+ "swarhk;": '\U00002926',
+ "swarr;": '\U00002199',
+ "swarrow;": '\U00002199',
+ "swnwar;": '\U0000292A',
+ "szlig;": '\U000000DF',
+ "target;": '\U00002316',
+ "tau;": '\U000003C4',
+ "tbrk;": '\U000023B4',
+ "tcaron;": '\U00000165',
+ "tcedil;": '\U00000163',
+ "tcy;": '\U00000442',
+ "tdot;": '\U000020DB',
+ "telrec;": '\U00002315',
+ "tfr;": '\U0001D531',
+ "there4;": '\U00002234',
+ "therefore;": '\U00002234',
+ "theta;": '\U000003B8',
+ "thetasym;": '\U000003D1',
+ "thetav;": '\U000003D1',
+ "thickapprox;": '\U00002248',
+ "thicksim;": '\U0000223C',
+ "thinsp;": '\U00002009',
+ "thkap;": '\U00002248',
+ "thksim;": '\U0000223C',
+ "thorn;": '\U000000FE',
+ "tilde;": '\U000002DC',
+ "times;": '\U000000D7',
+ "timesb;": '\U000022A0',
+ "timesbar;": '\U00002A31',
+ "timesd;": '\U00002A30',
+ "tint;": '\U0000222D',
+ "toea;": '\U00002928',
+ "top;": '\U000022A4',
+ "topbot;": '\U00002336',
+ "topcir;": '\U00002AF1',
+ "topf;": '\U0001D565',
+ "topfork;": '\U00002ADA',
+ "tosa;": '\U00002929',
+ "tprime;": '\U00002034',
+ "trade;": '\U00002122',
+ "triangle;": '\U000025B5',
+ "triangledown;": '\U000025BF',
+ "triangleleft;": '\U000025C3',
+ "trianglelefteq;": '\U000022B4',
+ "triangleq;": '\U0000225C',
+ "triangleright;": '\U000025B9',
+ "trianglerighteq;": '\U000022B5',
+ "tridot;": '\U000025EC',
+ "trie;": '\U0000225C',
+ "triminus;": '\U00002A3A',
+ "triplus;": '\U00002A39',
+ "trisb;": '\U000029CD',
+ "tritime;": '\U00002A3B',
+ "trpezium;": '\U000023E2',
+ "tscr;": '\U0001D4C9',
+ "tscy;": '\U00000446',
+ "tshcy;": '\U0000045B',
+ "tstrok;": '\U00000167',
+ "twixt;": '\U0000226C',
+ "twoheadleftarrow;": '\U0000219E',
+ "twoheadrightarrow;": '\U000021A0',
+ "uArr;": '\U000021D1',
+ "uHar;": '\U00002963',
+ "uacute;": '\U000000FA',
+ "uarr;": '\U00002191',
+ "ubrcy;": '\U0000045E',
+ "ubreve;": '\U0000016D',
+ "ucirc;": '\U000000FB',
+ "ucy;": '\U00000443',
+ "udarr;": '\U000021C5',
+ "udblac;": '\U00000171',
+ "udhar;": '\U0000296E',
+ "ufisht;": '\U0000297E',
+ "ufr;": '\U0001D532',
+ "ugrave;": '\U000000F9',
+ "uharl;": '\U000021BF',
+ "uharr;": '\U000021BE',
+ "uhblk;": '\U00002580',
+ "ulcorn;": '\U0000231C',
+ "ulcorner;": '\U0000231C',
+ "ulcrop;": '\U0000230F',
+ "ultri;": '\U000025F8',
+ "umacr;": '\U0000016B',
+ "uml;": '\U000000A8',
+ "uogon;": '\U00000173',
+ "uopf;": '\U0001D566',
+ "uparrow;": '\U00002191',
+ "updownarrow;": '\U00002195',
+ "upharpoonleft;": '\U000021BF',
+ "upharpoonright;": '\U000021BE',
+ "uplus;": '\U0000228E',
+ "upsi;": '\U000003C5',
+ "upsih;": '\U000003D2',
+ "upsilon;": '\U000003C5',
+ "upuparrows;": '\U000021C8',
+ "urcorn;": '\U0000231D',
+ "urcorner;": '\U0000231D',
+ "urcrop;": '\U0000230E',
+ "uring;": '\U0000016F',
+ "urtri;": '\U000025F9',
+ "uscr;": '\U0001D4CA',
+ "utdot;": '\U000022F0',
+ "utilde;": '\U00000169',
+ "utri;": '\U000025B5',
+ "utrif;": '\U000025B4',
+ "uuarr;": '\U000021C8',
+ "uuml;": '\U000000FC',
+ "uwangle;": '\U000029A7',
+ "vArr;": '\U000021D5',
+ "vBar;": '\U00002AE8',
+ "vBarv;": '\U00002AE9',
+ "vDash;": '\U000022A8',
+ "vangrt;": '\U0000299C',
+ "varepsilon;": '\U000003F5',
+ "varkappa;": '\U000003F0',
+ "varnothing;": '\U00002205',
+ "varphi;": '\U000003D5',
+ "varpi;": '\U000003D6',
+ "varpropto;": '\U0000221D',
+ "varr;": '\U00002195',
+ "varrho;": '\U000003F1',
+ "varsigma;": '\U000003C2',
+ "vartheta;": '\U000003D1',
+ "vartriangleleft;": '\U000022B2',
+ "vartriangleright;": '\U000022B3',
+ "vcy;": '\U00000432',
+ "vdash;": '\U000022A2',
+ "vee;": '\U00002228',
+ "veebar;": '\U000022BB',
+ "veeeq;": '\U0000225A',
+ "vellip;": '\U000022EE',
+ "verbar;": '\U0000007C',
+ "vert;": '\U0000007C',
+ "vfr;": '\U0001D533',
+ "vltri;": '\U000022B2',
+ "vopf;": '\U0001D567',
+ "vprop;": '\U0000221D',
+ "vrtri;": '\U000022B3',
+ "vscr;": '\U0001D4CB',
+ "vzigzag;": '\U0000299A',
+ "wcirc;": '\U00000175',
+ "wedbar;": '\U00002A5F',
+ "wedge;": '\U00002227',
+ "wedgeq;": '\U00002259',
+ "weierp;": '\U00002118',
+ "wfr;": '\U0001D534',
+ "wopf;": '\U0001D568',
+ "wp;": '\U00002118',
+ "wr;": '\U00002240',
+ "wreath;": '\U00002240',
+ "wscr;": '\U0001D4CC',
+ "xcap;": '\U000022C2',
+ "xcirc;": '\U000025EF',
+ "xcup;": '\U000022C3',
+ "xdtri;": '\U000025BD',
+ "xfr;": '\U0001D535',
+ "xhArr;": '\U000027FA',
+ "xharr;": '\U000027F7',
+ "xi;": '\U000003BE',
+ "xlArr;": '\U000027F8',
+ "xlarr;": '\U000027F5',
+ "xmap;": '\U000027FC',
+ "xnis;": '\U000022FB',
+ "xodot;": '\U00002A00',
+ "xopf;": '\U0001D569',
+ "xoplus;": '\U00002A01',
+ "xotime;": '\U00002A02',
+ "xrArr;": '\U000027F9',
+ "xrarr;": '\U000027F6',
+ "xscr;": '\U0001D4CD',
+ "xsqcup;": '\U00002A06',
+ "xuplus;": '\U00002A04',
+ "xutri;": '\U000025B3',
+ "xvee;": '\U000022C1',
+ "xwedge;": '\U000022C0',
+ "yacute;": '\U000000FD',
+ "yacy;": '\U0000044F',
+ "ycirc;": '\U00000177',
+ "ycy;": '\U0000044B',
+ "yen;": '\U000000A5',
+ "yfr;": '\U0001D536',
+ "yicy;": '\U00000457',
+ "yopf;": '\U0001D56A',
+ "yscr;": '\U0001D4CE',
+ "yucy;": '\U0000044E',
+ "yuml;": '\U000000FF',
+ "zacute;": '\U0000017A',
+ "zcaron;": '\U0000017E',
+ "zcy;": '\U00000437',
+ "zdot;": '\U0000017C',
+ "zeetrf;": '\U00002128',
+ "zeta;": '\U000003B6',
+ "zfr;": '\U0001D537',
+ "zhcy;": '\U00000436',
+ "zigrarr;": '\U000021DD',
+ "zopf;": '\U0001D56B',
+ "zscr;": '\U0001D4CF',
+ "zwj;": '\U0000200D',
+ "zwnj;": '\U0000200C',
+ "AElig": '\U000000C6',
+ "AMP": '\U00000026',
+ "Aacute": '\U000000C1',
+ "Acirc": '\U000000C2',
+ "Agrave": '\U000000C0',
+ "Aring": '\U000000C5',
+ "Atilde": '\U000000C3',
+ "Auml": '\U000000C4',
+ "COPY": '\U000000A9',
+ "Ccedil": '\U000000C7',
+ "ETH": '\U000000D0',
+ "Eacute": '\U000000C9',
+ "Ecirc": '\U000000CA',
+ "Egrave": '\U000000C8',
+ "Euml": '\U000000CB',
+ "GT": '\U0000003E',
+ "Iacute": '\U000000CD',
+ "Icirc": '\U000000CE',
+ "Igrave": '\U000000CC',
+ "Iuml": '\U000000CF',
+ "LT": '\U0000003C',
+ "Ntilde": '\U000000D1',
+ "Oacute": '\U000000D3',
+ "Ocirc": '\U000000D4',
+ "Ograve": '\U000000D2',
+ "Oslash": '\U000000D8',
+ "Otilde": '\U000000D5',
+ "Ouml": '\U000000D6',
+ "QUOT": '\U00000022',
+ "REG": '\U000000AE',
+ "THORN": '\U000000DE',
+ "Uacute": '\U000000DA',
+ "Ucirc": '\U000000DB',
+ "Ugrave": '\U000000D9',
+ "Uuml": '\U000000DC',
+ "Yacute": '\U000000DD',
+ "aacute": '\U000000E1',
+ "acirc": '\U000000E2',
+ "acute": '\U000000B4',
+ "aelig": '\U000000E6',
+ "agrave": '\U000000E0',
+ "amp": '\U00000026',
+ "aring": '\U000000E5',
+ "atilde": '\U000000E3',
+ "auml": '\U000000E4',
+ "brvbar": '\U000000A6',
+ "ccedil": '\U000000E7',
+ "cedil": '\U000000B8',
+ "cent": '\U000000A2',
+ "copy": '\U000000A9',
+ "curren": '\U000000A4',
+ "deg": '\U000000B0',
+ "divide": '\U000000F7',
+ "eacute": '\U000000E9',
+ "ecirc": '\U000000EA',
+ "egrave": '\U000000E8',
+ "eth": '\U000000F0',
+ "euml": '\U000000EB',
+ "frac12": '\U000000BD',
+ "frac14": '\U000000BC',
+ "frac34": '\U000000BE',
+ "gt": '\U0000003E',
+ "iacute": '\U000000ED',
+ "icirc": '\U000000EE',
+ "iexcl": '\U000000A1',
+ "igrave": '\U000000EC',
+ "iquest": '\U000000BF',
+ "iuml": '\U000000EF',
+ "laquo": '\U000000AB',
+ "lt": '\U0000003C',
+ "macr": '\U000000AF',
+ "micro": '\U000000B5',
+ "middot": '\U000000B7',
+ "nbsp": '\U000000A0',
+ "not": '\U000000AC',
+ "ntilde": '\U000000F1',
+ "oacute": '\U000000F3',
+ "ocirc": '\U000000F4',
+ "ograve": '\U000000F2',
+ "ordf": '\U000000AA',
+ "ordm": '\U000000BA',
+ "oslash": '\U000000F8',
+ "otilde": '\U000000F5',
+ "ouml": '\U000000F6',
+ "para": '\U000000B6',
+ "plusmn": '\U000000B1',
+ "pound": '\U000000A3',
+ "quot": '\U00000022',
+ "raquo": '\U000000BB',
+ "reg": '\U000000AE',
+ "sect": '\U000000A7',
+ "shy": '\U000000AD',
+ "sup1": '\U000000B9',
+ "sup2": '\U000000B2',
+ "sup3": '\U000000B3',
+ "szlig": '\U000000DF',
+ "thorn": '\U000000FE',
+ "times": '\U000000D7',
+ "uacute": '\U000000FA',
+ "ucirc": '\U000000FB',
+ "ugrave": '\U000000F9',
+ "uml": '\U000000A8',
+ "uuml": '\U000000FC',
+ "yacute": '\U000000FD',
+ "yen": '\U000000A5',
+ "yuml": '\U000000FF',
+}
+
+// HTML entities that are two unicode codepoints.
+var entity2 = map[string][2]rune{
+ // TODO(nigeltao): Handle replacements that are wider than their names.
+ // "nLt;": {'\u226A', '\u20D2'},
+ // "nGt;": {'\u226B', '\u20D2'},
+ "NotEqualTilde;": {'\u2242', '\u0338'},
+ "NotGreaterFullEqual;": {'\u2267', '\u0338'},
+ "NotGreaterGreater;": {'\u226B', '\u0338'},
+ "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'},
+ "NotHumpDownHump;": {'\u224E', '\u0338'},
+ "NotHumpEqual;": {'\u224F', '\u0338'},
+ "NotLeftTriangleBar;": {'\u29CF', '\u0338'},
+ "NotLessLess;": {'\u226A', '\u0338'},
+ "NotLessSlantEqual;": {'\u2A7D', '\u0338'},
+ "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'},
+ "NotNestedLessLess;": {'\u2AA1', '\u0338'},
+ "NotPrecedesEqual;": {'\u2AAF', '\u0338'},
+ "NotRightTriangleBar;": {'\u29D0', '\u0338'},
+ "NotSquareSubset;": {'\u228F', '\u0338'},
+ "NotSquareSuperset;": {'\u2290', '\u0338'},
+ "NotSubset;": {'\u2282', '\u20D2'},
+ "NotSucceedsEqual;": {'\u2AB0', '\u0338'},
+ "NotSucceedsTilde;": {'\u227F', '\u0338'},
+ "NotSuperset;": {'\u2283', '\u20D2'},
+ "ThickSpace;": {'\u205F', '\u200A'},
+ "acE;": {'\u223E', '\u0333'},
+ "bne;": {'\u003D', '\u20E5'},
+ "bnequiv;": {'\u2261', '\u20E5'},
+ "caps;": {'\u2229', '\uFE00'},
+ "cups;": {'\u222A', '\uFE00'},
+ "fjlig;": {'\u0066', '\u006A'},
+ "gesl;": {'\u22DB', '\uFE00'},
+ "gvertneqq;": {'\u2269', '\uFE00'},
+ "gvnE;": {'\u2269', '\uFE00'},
+ "lates;": {'\u2AAD', '\uFE00'},
+ "lesg;": {'\u22DA', '\uFE00'},
+ "lvertneqq;": {'\u2268', '\uFE00'},
+ "lvnE;": {'\u2268', '\uFE00'},
+ "nGg;": {'\u22D9', '\u0338'},
+ "nGtv;": {'\u226B', '\u0338'},
+ "nLl;": {'\u22D8', '\u0338'},
+ "nLtv;": {'\u226A', '\u0338'},
+ "nang;": {'\u2220', '\u20D2'},
+ "napE;": {'\u2A70', '\u0338'},
+ "napid;": {'\u224B', '\u0338'},
+ "nbump;": {'\u224E', '\u0338'},
+ "nbumpe;": {'\u224F', '\u0338'},
+ "ncongdot;": {'\u2A6D', '\u0338'},
+ "nedot;": {'\u2250', '\u0338'},
+ "nesim;": {'\u2242', '\u0338'},
+ "ngE;": {'\u2267', '\u0338'},
+ "ngeqq;": {'\u2267', '\u0338'},
+ "ngeqslant;": {'\u2A7E', '\u0338'},
+ "nges;": {'\u2A7E', '\u0338'},
+ "nlE;": {'\u2266', '\u0338'},
+ "nleqq;": {'\u2266', '\u0338'},
+ "nleqslant;": {'\u2A7D', '\u0338'},
+ "nles;": {'\u2A7D', '\u0338'},
+ "notinE;": {'\u22F9', '\u0338'},
+ "notindot;": {'\u22F5', '\u0338'},
+ "nparsl;": {'\u2AFD', '\u20E5'},
+ "npart;": {'\u2202', '\u0338'},
+ "npre;": {'\u2AAF', '\u0338'},
+ "npreceq;": {'\u2AAF', '\u0338'},
+ "nrarrc;": {'\u2933', '\u0338'},
+ "nrarrw;": {'\u219D', '\u0338'},
+ "nsce;": {'\u2AB0', '\u0338'},
+ "nsubE;": {'\u2AC5', '\u0338'},
+ "nsubset;": {'\u2282', '\u20D2'},
+ "nsubseteqq;": {'\u2AC5', '\u0338'},
+ "nsucceq;": {'\u2AB0', '\u0338'},
+ "nsupE;": {'\u2AC6', '\u0338'},
+ "nsupset;": {'\u2283', '\u20D2'},
+ "nsupseteqq;": {'\u2AC6', '\u0338'},
+ "nvap;": {'\u224D', '\u20D2'},
+ "nvge;": {'\u2265', '\u20D2'},
+ "nvgt;": {'\u003E', '\u20D2'},
+ "nvle;": {'\u2264', '\u20D2'},
+ "nvlt;": {'\u003C', '\u20D2'},
+ "nvltrie;": {'\u22B4', '\u20D2'},
+ "nvrtrie;": {'\u22B5', '\u20D2'},
+ "nvsim;": {'\u223C', '\u20D2'},
+ "race;": {'\u223D', '\u0331'},
+ "smtes;": {'\u2AAC', '\uFE00'},
+ "sqcaps;": {'\u2293', '\uFE00'},
+ "sqcups;": {'\u2294', '\uFE00'},
+ "varsubsetneq;": {'\u228A', '\uFE00'},
+ "varsubsetneqq;": {'\u2ACB', '\uFE00'},
+ "varsupsetneq;": {'\u228B', '\uFE00'},
+ "varsupsetneqq;": {'\u2ACC', '\uFE00'},
+ "vnsub;": {'\u2282', '\u20D2'},
+ "vnsup;": {'\u2283', '\u20D2'},
+ "vsubnE;": {'\u2ACB', '\uFE00'},
+ "vsubne;": {'\u228A', '\uFE00'},
+ "vsupnE;": {'\u2ACC', '\uFE00'},
+ "vsupne;": {'\u228B', '\uFE00'},
+}
diff --git a/html/entity_test.go b/html/entity_test.go
new file mode 100644
index 0000000..b53f866
--- /dev/null
+++ b/html/entity_test.go
@@ -0,0 +1,29 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "testing"
+ "unicode/utf8"
+)
+
+func TestEntityLength(t *testing.T) {
+ // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key).
+ // The +1 comes from the leading "&". This property implies that the length of
+ // unescaped text is <= the length of escaped text.
+ for k, v := range entity {
+ if 1+len(k) < utf8.RuneLen(v) {
+ t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v))
+ }
+ if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' {
+ t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon)
+ }
+ }
+ for k, v := range entity2 {
+ if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) {
+ t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1]))
+ }
+ }
+}
diff --git a/html/escape.go b/html/escape.go
new file mode 100644
index 0000000..d856139
--- /dev/null
+++ b/html/escape.go
@@ -0,0 +1,258 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "bytes"
+ "strings"
+ "unicode/utf8"
+)
+
+// These replacements permit compatibility with old numeric entities that
+// assumed Windows-1252 encoding.
+// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
+var replacementTable = [...]rune{
+ '\u20AC', // First entry is what 0x80 should be replaced with.
+ '\u0081',
+ '\u201A',
+ '\u0192',
+ '\u201E',
+ '\u2026',
+ '\u2020',
+ '\u2021',
+ '\u02C6',
+ '\u2030',
+ '\u0160',
+ '\u2039',
+ '\u0152',
+ '\u008D',
+ '\u017D',
+ '\u008F',
+ '\u0090',
+ '\u2018',
+ '\u2019',
+ '\u201C',
+ '\u201D',
+ '\u2022',
+ '\u2013',
+ '\u2014',
+ '\u02DC',
+ '\u2122',
+ '\u0161',
+ '\u203A',
+ '\u0153',
+ '\u009D',
+ '\u017E',
+ '\u0178', // Last entry is 0x9F.
+ // 0x00->'\uFFFD' is handled programmatically.
+ // 0x0D->'\u000D' is a no-op.
+}
+
+// unescapeEntity reads an entity like "<" from b[src:] and writes the
+// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
+// Precondition: b[src] == '&' && dst <= src.
+// attribute should be true if parsing an attribute value.
+func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
+ // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
+
+ // i starts at 1 because we already know that s[0] == '&'.
+ i, s := 1, b[src:]
+
+ if len(s) <= 1 {
+ b[dst] = b[src]
+ return dst + 1, src + 1
+ }
+
+ if s[i] == '#' {
+ if len(s) <= 3 { // We need to have at least "&#.".
+ b[dst] = b[src]
+ return dst + 1, src + 1
+ }
+ i++
+ c := s[i]
+ hex := false
+ if c == 'x' || c == 'X' {
+ hex = true
+ i++
+ }
+
+ x := '\x00'
+ for i < len(s) {
+ c = s[i]
+ i++
+ if hex {
+ if '0' <= c && c <= '9' {
+ x = 16*x + rune(c) - '0'
+ continue
+ } else if 'a' <= c && c <= 'f' {
+ x = 16*x + rune(c) - 'a' + 10
+ continue
+ } else if 'A' <= c && c <= 'F' {
+ x = 16*x + rune(c) - 'A' + 10
+ continue
+ }
+ } else if '0' <= c && c <= '9' {
+ x = 10*x + rune(c) - '0'
+ continue
+ }
+ if c != ';' {
+ i--
+ }
+ break
+ }
+
+ if i <= 3 { // No characters matched.
+ b[dst] = b[src]
+ return dst + 1, src + 1
+ }
+
+ if 0x80 <= x && x <= 0x9F {
+ // Replace characters from Windows-1252 with UTF-8 equivalents.
+ x = replacementTable[x-0x80]
+ } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
+ // Replace invalid characters with the replacement character.
+ x = '\uFFFD'
+ }
+
+ return dst + utf8.EncodeRune(b[dst:], x), src + i
+ }
+
+ // Consume the maximum number of characters possible, with the
+ // consumed characters matching one of the named references.
+
+ for i < len(s) {
+ c := s[i]
+ i++
+ // Lower-cased characters are more common in entities, so we check for them first.
+ if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
+ continue
+ }
+ if c != ';' {
+ i--
+ }
+ break
+ }
+
+ entityName := string(s[1:i])
+ if entityName == "" {
+ // No-op.
+ } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
+ // No-op.
+ } else if x := entity[entityName]; x != 0 {
+ return dst + utf8.EncodeRune(b[dst:], x), src + i
+ } else if x := entity2[entityName]; x[0] != 0 {
+ dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
+ return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
+ } else if !attribute {
+ maxLen := len(entityName) - 1
+ if maxLen > longestEntityWithoutSemicolon {
+ maxLen = longestEntityWithoutSemicolon
+ }
+ for j := maxLen; j > 1; j-- {
+ if x := entity[entityName[:j]]; x != 0 {
+ return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
+ }
+ }
+ }
+
+ dst1, src1 = dst+i, src+i
+ copy(b[dst:dst1], b[src:src1])
+ return dst1, src1
+}
+
+// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b".
+// attribute should be true if parsing an attribute value.
+func unescape(b []byte, attribute bool) []byte {
+ for i, c := range b {
+ if c == '&' {
+ dst, src := unescapeEntity(b, i, i, attribute)
+ for src < len(b) {
+ c := b[src]
+ if c == '&' {
+ dst, src = unescapeEntity(b, dst, src, attribute)
+ } else {
+ b[dst] = c
+ dst, src = dst+1, src+1
+ }
+ }
+ return b[0:dst]
+ }
+ }
+ return b
+}
+
+// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
+func lower(b []byte) []byte {
+ for i, c := range b {
+ if 'A' <= c && c <= 'Z' {
+ b[i] = c + 'a' - 'A'
+ }
+ }
+ return b
+}
+
+const escapedChars = "&'<>\"\r"
+
+func escape(w writer, s string) error {
+ i := strings.IndexAny(s, escapedChars)
+ for i != -1 {
+ if _, err := w.WriteString(s[:i]); err != nil {
+ return err
+ }
+ var esc string
+ switch s[i] {
+ case '&':
+ esc = "&"
+ case '\'':
+ // "'" is shorter than "'" and apos was not in HTML until HTML5.
+ esc = "'"
+ case '<':
+ esc = "<"
+ case '>':
+ esc = ">"
+ case '"':
+ // """ is shorter than """.
+ esc = """
+ case '\r':
+ esc = " "
+ default:
+ panic("unrecognized escape character")
+ }
+ s = s[i+1:]
+ if _, err := w.WriteString(esc); err != nil {
+ return err
+ }
+ i = strings.IndexAny(s, escapedChars)
+ }
+ _, err := w.WriteString(s)
+ return err
+}
+
+// EscapeString escapes special characters like "<" to become "<". It
+// escapes only five such characters: <, >, &, ' and ".
+// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
+// always true.
+func EscapeString(s string) string {
+ if strings.IndexAny(s, escapedChars) == -1 {
+ return s
+ }
+ var buf bytes.Buffer
+ escape(&buf, s)
+ return buf.String()
+}
+
+// UnescapeString unescapes entities like "<" to become "<". It unescapes a
+// larger range of entities than EscapeString escapes. For example, "á"
+// unescapes to "á", as does "á" and "&xE1;".
+// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
+// always true.
+func UnescapeString(s string) string {
+ for _, c := range s {
+ if c == '&' {
+ return string(unescape([]byte(s), false))
+ }
+ }
+ return s
+}
diff --git a/html/escape_test.go b/html/escape_test.go
new file mode 100644
index 0000000..b405d4b
--- /dev/null
+++ b/html/escape_test.go
@@ -0,0 +1,97 @@
+// Copyright 2013 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.
+
+package html
+
+import "testing"
+
+type unescapeTest struct {
+ // A short description of the test case.
+ desc string
+ // The HTML text.
+ html string
+ // The unescaped text.
+ unescaped string
+}
+
+var unescapeTests = []unescapeTest{
+ // Handle no entities.
+ {
+ "copy",
+ "A\ttext\nstring",
+ "A\ttext\nstring",
+ },
+ // Handle simple named entities.
+ {
+ "simple",
+ "& > <",
+ "& > <",
+ },
+ // Handle hitting the end of the string.
+ {
+ "stringEnd",
+ "& &",
+ "& &",
+ },
+ // Handle entities with two codepoints.
+ {
+ "multiCodepoint",
+ "text ⋛︀ blah",
+ "text \u22db\ufe00 blah",
+ },
+ // Handle decimal numeric entities.
+ {
+ "decimalEntity",
+ "Delta = Δ ",
+ "Delta = Δ ",
+ },
+ // Handle hexadecimal numeric entities.
+ {
+ "hexadecimalEntity",
+ "Lambda = λ = λ ",
+ "Lambda = λ = λ ",
+ },
+ // Handle numeric early termination.
+ {
+ "numericEnds",
+ "&# &#x €43 © = ©f = ©",
+ "&# &#x €43 © = ©f = ©",
+ },
+ // Handle numeric ISO-8859-1 entity replacements.
+ {
+ "numericReplacements",
+ "Footnote‡",
+ "Footnote‡",
+ },
+}
+
+func TestUnescape(t *testing.T) {
+ for _, tt := range unescapeTests {
+ unescaped := UnescapeString(tt.html)
+ if unescaped != tt.unescaped {
+ t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
+ }
+ }
+}
+
+func TestUnescapeEscape(t *testing.T) {
+ ss := []string{
+ ``,
+ `abc def`,
+ `a & b`,
+ `a&b`,
+ `a & b`,
+ `"`,
+ `"`,
+ `"<&>"`,
+ `"<&>"`,
+ `3&5==1 && 0<1, "0<1", a+acute=á`,
+ `The special characters are: <, >, &, ' and "`,
+ }
+ for _, s := range ss {
+ if got := UnescapeString(EscapeString(s)); got != s {
+ t.Errorf("got %q want %q", got, s)
+ }
+ }
+}
diff --git a/html/example_test.go b/html/example_test.go
new file mode 100644
index 0000000..0b06ed7
--- /dev/null
+++ b/html/example_test.go
@@ -0,0 +1,40 @@
+// Copyright 2012 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.
+
+// This example demonstrates parsing HTML data and walking the resulting tree.
+package html_test
+
+import (
+ "fmt"
+ "log"
+ "strings"
+
+ "golang.org/x/net/html"
+)
+
+func ExampleParse() {
+ s := `<p>Links:</p><ul><li><a href="foo">Foo</a><li><a href="/bar/baz">BarBaz</a></ul>`
+ doc, err := html.Parse(strings.NewReader(s))
+ if err != nil {
+ log.Fatal(err)
+ }
+ var f func(*html.Node)
+ f = func(n *html.Node) {
+ if n.Type == html.ElementNode && n.Data == "a" {
+ for _, a := range n.Attr {
+ if a.Key == "href" {
+ fmt.Println(a.Val)
+ break
+ }
+ }
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ f(c)
+ }
+ }
+ f(doc)
+ // Output:
+ // foo
+ // /bar/baz
+}
diff --git a/html/foreign.go b/html/foreign.go
new file mode 100644
index 0000000..d3b3844
--- /dev/null
+++ b/html/foreign.go
@@ -0,0 +1,226 @@
+// Copyright 2011 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.
+
+package html
+
+import (
+ "strings"
+)
+
+func adjustAttributeNames(aa []Attribute, nameMap map[string]string) {
+ for i := range aa {
+ if newName, ok := nameMap[aa[i].Key]; ok {
+ aa[i].Key = newName
+ }
+ }
+}
+
+func adjustForeignAttributes(aa []Attribute) {
+ for i, a := range aa {
+ if a.Key == "" || a.Key[0] != 'x' {
+ continue
+ }
+ switch a.Key {
+ case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show",
+ "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink":
+ j := strings.Index(a.Key, ":")
+ aa[i].Namespace = a.Key[:j]
+ aa[i].Key = a.Key[j+1:]
+ }
+ }
+}
+
+func htmlIntegrationPoint(n *Node) bool {
+ if n.Type != ElementNode {
+ return false
+ }
+ switch n.Namespace {
+ case "math":
+ if n.Data == "annotation-xml" {
+ for _, a := range n.Attr {
+ if a.Key == "encoding" {
+ val := strings.ToLower(a.Val)
+ if val == "text/html" || val == "application/xhtml+xml" {
+ return true
+ }
+ }
+ }
+ }
+ case "svg":
+ switch n.Data {
+ case "desc", "foreignObject", "title":
+ return true
+ }
+ }
+ return false
+}
+
+func mathMLTextIntegrationPoint(n *Node) bool {
+ if n.Namespace != "math" {
+ return false
+ }
+ switch n.Data {
+ case "mi", "mo", "mn", "ms", "mtext":
+ return true
+ }
+ return false
+}
+
+// Section 12.2.5.5.
+var breakout = map[string]bool{
+ "b": true,
+ "big": true,
+ "blockquote": true,
+ "body": true,
+ "br": true,
+ "center": true,
+ "code": true,
+ "dd": true,
+ "div": true,
+ "dl": true,
+ "dt": true,
+ "em": true,
+ "embed": true,
+ "h1": true,
+ "h2": true,
+ "h3": true,
+ "h4": true,
+ "h5": true,
+ "h6": true,
+ "head": true,
+ "hr": true,
+ "i": true,
+ "img": true,
+ "li": true,
+ "listing": true,
+ "menu": true,
+ "meta": true,
+ "nobr": true,
+ "ol": true,
+ "p": true,
+ "pre": true,
+ "ruby": true,
+ "s": true,
+ "small": true,
+ "span": true,
+ "strong": true,
+ "strike": true,
+ "sub": true,
+ "sup": true,
+ "table": true,
+ "tt": true,
+ "u": true,
+ "ul": true,
+ "var": true,
+}
+
+// Section 12.2.5.5.
+var svgTagNameAdjustments = map[string]string{
+ "altglyph": "altGlyph",
+ "altglyphdef": "altGlyphDef",
+ "altglyphitem": "altGlyphItem",
+ "animatecolor": "animateColor",
+ "animatemotion": "animateMotion",
+ "animatetransform": "animateTransform",
+ "clippath": "clipPath",
+ "feblend": "feBlend",
+ "fecolormatrix": "feColorMatrix",
+ "fecomponenttransfer": "feComponentTransfer",
+ "fecomposite": "feComposite",
+ "feconvolvematrix": "feConvolveMatrix",
+ "fediffuselighting": "feDiffuseLighting",
+ "fedisplacementmap": "feDisplacementMap",
+ "fedistantlight": "feDistantLight",
+ "feflood": "feFlood",
+ "fefunca": "feFuncA",
+ "fefuncb": "feFuncB",
+ "fefuncg": "feFuncG",
+ "fefuncr": "feFuncR",
+ "fegaussianblur": "feGaussianBlur",
+ "feimage": "feImage",
+ "femerge": "feMerge",
+ "femergenode": "feMergeNode",
+ "femorphology": "feMorphology",
+ "feoffset": "feOffset",
+ "fepointlight": "fePointLight",
+ "fespecularlighting": "feSpecularLighting",
+ "fespotlight": "feSpotLight",
+ "fetile": "feTile",
+ "feturbulence": "feTurbulence",
+ "foreignobject": "foreignObject",
+ "glyphref": "glyphRef",
+ "lineargradient": "linearGradient",
+ "radialgradient": "radialGradient",
+ "textpath": "textPath",
+}
+
+// Section 12.2.5.1
+var mathMLAttributeAdjustments = map[string]string{
+ "definitionurl": "definitionURL",
+}
+
+var svgAttributeAdjustments = map[string]string{
+ "attributename": "attributeName",
+ "attributetype": "attributeType",
+ "basefrequency": "baseFrequency",
+ "baseprofile": "baseProfile",
+ "calcmode": "calcMode",
+ "clippathunits": "clipPathUnits",
+ "contentscripttype": "contentScriptType",
+ "contentstyletype": "contentStyleType",
+ "diffuseconstant": "diffuseConstant",
+ "edgemode": "edgeMode",
+ "externalresourcesrequired": "externalResourcesRequired",
+ "filterres": "filterRes",
+ "filterunits": "filterUnits",
+ "glyphref": "glyphRef",
+ "gradienttransform": "gradientTransform",
+ "gradientunits": "gradientUnits",
+ "kernelmatrix": "kernelMatrix",
+ "kernelunitlength": "kernelUnitLength",
+ "keypoints": "keyPoints",
+ "keysplines": "keySplines",
+ "keytimes": "keyTimes",
+ "lengthadjust": "lengthAdjust",
+ "limitingconeangle": "limitingConeAngle",
+ "markerheight": "markerHeight",
+ "markerunits": "markerUnits",
+ "markerwidth": "markerWidth",
+ "maskcontentunits": "maskContentUnits",
+ "maskunits": "maskUnits",
+ "numoctaves": "numOctaves",
+ "pathlength": "pathLength",
+ "patterncontentunits": "patternContentUnits",
+ "patterntransform": "patternTransform",
+ "patternunits": "patternUnits",
+ "pointsatx": "pointsAtX",
+ "pointsaty": "pointsAtY",
+ "pointsatz": "pointsAtZ",
+ "preservealpha": "preserveAlpha",
+ "preserveaspectratio": "preserveAspectRatio",
+ "primitiveunits": "primitiveUnits",
+ "refx": "refX",
+ "refy": "refY",
+ "repeatcount": "repeatCount",
+ "repeatdur": "repeatDur",
+ "requiredextensions": "requiredExtensions",
+ "requiredfeatures": "requiredFeatures",
+ "specularconstant": "specularConstant",
+ "specularexponent": "specularExponent",
+ "spreadmethod": "spreadMethod",
+ "startoffset": "startOffset",
+ "stddeviation": "stdDeviation",
+ "stitchtiles": "stitchTiles",
+ "surfacescale": "surfaceScale",
+ "systemlanguage": "systemLanguage",
+ "tablevalues": "tableValues",
+ "targetx": "targetX",
+ "targety": "targetY",
+ "textlength": "textLength",
+ "viewbox": "viewBox",
+ "viewtarget": "viewTarget",
+ "xchannelselector": "xChannelSelector",
+ "ychannelselector": "yChannelSelector",
+ "zoomandpan": "zoomAndPan",
+}
diff --git a/html/node.go b/html/node.go
new file mode 100644
index 0000000..26b657a
--- /dev/null
+++ b/html/node.go
@@ -0,0 +1,193 @@
+// Copyright 2011 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.
+
+package html
+
+import (
+ "golang.org/x/net/html/atom"
+)
+
+// A NodeType is the type of a Node.
+type NodeType uint32
+
+const (
+ ErrorNode NodeType = iota
+ TextNode
+ DocumentNode
+ ElementNode
+ CommentNode
+ DoctypeNode
+ scopeMarkerNode
+)
+
+// Section 12.2.3.3 says "scope markers are inserted when entering applet
+// elements, buttons, object elements, marquees, table cells, and table
+// captions, and are used to prevent formatting from 'leaking'".
+var scopeMarker = Node{Type: scopeMarkerNode}
+
+// A Node consists of a NodeType and some Data (tag name for element nodes,
+// content for text) and are part of a tree of Nodes. Element nodes may also
+// have a Namespace and contain a slice of Attributes. Data is unescaped, so
+// that it looks like "a<b" rather than "a<b". For element nodes, DataAtom
+// is the atom for Data, or zero if Data is not a known tag name.
+//
+// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
+// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
+// "svg" is short for "http://www.w3.org/2000/svg".
+type Node struct {
+ Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node
+
+ Type NodeType
+ DataAtom atom.Atom
+ Data string
+ Namespace string
+ Attr []Attribute
+}
+
+// InsertBefore inserts newChild as a child of n, immediately before oldChild
+// in the sequence of n's children. oldChild may be nil, in which case newChild
+// is appended to the end of n's children.
+//
+// It will panic if newChild already has a parent or siblings.
+func (n *Node) InsertBefore(newChild, oldChild *Node) {
+ if newChild.Parent != nil || newChild.PrevSibling != nil || newChild.NextSibling != nil {
+ panic("html: InsertBefore called for an attached child Node")
+ }
+ var prev, next *Node
+ if oldChild != nil {
+ prev, next = oldChild.PrevSibling, oldChild
+ } else {
+ prev = n.LastChild
+ }
+ if prev != nil {
+ prev.NextSibling = newChild
+ } else {
+ n.FirstChild = newChild
+ }
+ if next != nil {
+ next.PrevSibling = newChild
+ } else {
+ n.LastChild = newChild
+ }
+ newChild.Parent = n
+ newChild.PrevSibling = prev
+ newChild.NextSibling = next
+}
+
+// AppendChild adds a node c as a child of n.
+//
+// It will panic if c already has a parent or siblings.
+func (n *Node) AppendChild(c *Node) {
+ if c.Parent != nil || c.PrevSibling != nil || c.NextSibling != nil {
+ panic("html: AppendChild called for an attached child Node")
+ }
+ last := n.LastChild
+ if last != nil {
+ last.NextSibling = c
+ } else {
+ n.FirstChild = c
+ }
+ n.LastChild = c
+ c.Parent = n
+ c.PrevSibling = last
+}
+
+// RemoveChild removes a node c that is a child of n. Afterwards, c will have
+// no parent and no siblings.
+//
+// It will panic if c's parent is not n.
+func (n *Node) RemoveChild(c *Node) {
+ if c.Parent != n {
+ panic("html: RemoveChild called for a non-child Node")
+ }
+ if n.FirstChild == c {
+ n.FirstChild = c.NextSibling
+ }
+ if c.NextSibling != nil {
+ c.NextSibling.PrevSibling = c.PrevSibling
+ }
+ if n.LastChild == c {
+ n.LastChild = c.PrevSibling
+ }
+ if c.PrevSibling != nil {
+ c.PrevSibling.NextSibling = c.NextSibling
+ }
+ c.Parent = nil
+ c.PrevSibling = nil
+ c.NextSibling = nil
+}
+
+// reparentChildren reparents all of src's child nodes to dst.
+func reparentChildren(dst, src *Node) {
+ for {
+ child := src.FirstChild
+ if child == nil {
+ break
+ }
+ src.RemoveChild(child)
+ dst.AppendChild(child)
+ }
+}
+
+// clone returns a new node with the same type, data and attributes.
+// The clone has no parent, no siblings and no children.
+func (n *Node) clone() *Node {
+ m := &Node{
+ Type: n.Type,
+ DataAtom: n.DataAtom,
+ Data: n.Data,
+ Attr: make([]Attribute, len(n.Attr)),
+ }
+ copy(m.Attr, n.Attr)
+ return m
+}
+
+// nodeStack is a stack of nodes.
+type nodeStack []*Node
+
+// pop pops the stack. It will panic if s is empty.
+func (s *nodeStack) pop() *Node {
+ i := len(*s)
+ n := (*s)[i-1]
+ *s = (*s)[:i-1]
+ return n
+}
+
+// top returns the most recently pushed node, or nil if s is empty.
+func (s *nodeStack) top() *Node {
+ if i := len(*s); i > 0 {
+ return (*s)[i-1]
+ }
+ return nil
+}
+
+// index returns the index of the top-most occurrence of n in the stack, or -1
+// if n is not present.
+func (s *nodeStack) index(n *Node) int {
+ for i := len(*s) - 1; i >= 0; i-- {
+ if (*s)[i] == n {
+ return i
+ }
+ }
+ return -1
+}
+
+// insert inserts a node at the given index.
+func (s *nodeStack) insert(i int, n *Node) {
+ (*s) = append(*s, nil)
+ copy((*s)[i+1:], (*s)[i:])
+ (*s)[i] = n
+}
+
+// remove removes a node from the stack. It is a no-op if n is not present.
+func (s *nodeStack) remove(n *Node) {
+ i := s.index(n)
+ if i == -1 {
+ return
+ }
+ copy((*s)[i:], (*s)[i+1:])
+ j := len(*s) - 1
+ (*s)[j] = nil
+ *s = (*s)[:j]
+}
diff --git a/html/node_test.go b/html/node_test.go
new file mode 100644
index 0000000..471102f
--- /dev/null
+++ b/html/node_test.go
@@ -0,0 +1,146 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "fmt"
+)
+
+// checkTreeConsistency checks that a node and its descendants are all
+// consistent in their parent/child/sibling relationships.
+func checkTreeConsistency(n *Node) error {
+ return checkTreeConsistency1(n, 0)
+}
+
+func checkTreeConsistency1(n *Node, depth int) error {
+ if depth == 1e4 {
+ return fmt.Errorf("html: tree looks like it contains a cycle")
+ }
+ if err := checkNodeConsistency(n); err != nil {
+ return err
+ }
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if err := checkTreeConsistency1(c, depth+1); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// checkNodeConsistency checks that a node's parent/child/sibling relationships
+// are consistent.
+func checkNodeConsistency(n *Node) error {
+ if n == nil {
+ return nil
+ }
+
+ nParent := 0
+ for p := n.Parent; p != nil; p = p.Parent {
+ nParent++
+ if nParent == 1e4 {
+ return fmt.Errorf("html: parent list looks like an infinite loop")
+ }
+ }
+
+ nForward := 0
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ nForward++
+ if nForward == 1e6 {
+ return fmt.Errorf("html: forward list of children looks like an infinite loop")
+ }
+ if c.Parent != n {
+ return fmt.Errorf("html: inconsistent child/parent relationship")
+ }
+ }
+
+ nBackward := 0
+ for c := n.LastChild; c != nil; c = c.PrevSibling {
+ nBackward++
+ if nBackward == 1e6 {
+ return fmt.Errorf("html: backward list of children looks like an infinite loop")
+ }
+ if c.Parent != n {
+ return fmt.Errorf("html: inconsistent child/parent relationship")
+ }
+ }
+
+ if n.Parent != nil {
+ if n.Parent == n {
+ return fmt.Errorf("html: inconsistent parent relationship")
+ }
+ if n.Parent == n.FirstChild {
+ return fmt.Errorf("html: inconsistent parent/first relationship")
+ }
+ if n.Parent == n.LastChild {
+ return fmt.Errorf("html: inconsistent parent/last relationship")
+ }
+ if n.Parent == n.PrevSibling {
+ return fmt.Errorf("html: inconsistent parent/prev relationship")
+ }
+ if n.Parent == n.NextSibling {
+ return fmt.Errorf("html: inconsistent parent/next relationship")
+ }
+
+ parentHasNAsAChild := false
+ for c := n.Parent.FirstChild; c != nil; c = c.NextSibling {
+ if c == n {
+ parentHasNAsAChild = true
+ break
+ }
+ }
+ if !parentHasNAsAChild {
+ return fmt.Errorf("html: inconsistent parent/child relationship")
+ }
+ }
+
+ if n.PrevSibling != nil && n.PrevSibling.NextSibling != n {
+ return fmt.Errorf("html: inconsistent prev/next relationship")
+ }
+ if n.NextSibling != nil && n.NextSibling.PrevSibling != n {
+ return fmt.Errorf("html: inconsistent next/prev relationship")
+ }
+
+ if (n.FirstChild == nil) != (n.LastChild == nil) {
+ return fmt.Errorf("html: inconsistent first/last relationship")
+ }
+ if n.FirstChild != nil && n.FirstChild == n.LastChild {
+ // We have a sole child.
+ if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil {
+ return fmt.Errorf("html: inconsistent sole child's sibling relationship")
+ }
+ }
+
+ seen := map[*Node]bool{}
+
+ var last *Node
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if seen[c] {
+ return fmt.Errorf("html: inconsistent repeated child")
+ }
+ seen[c] = true
+ last = c
+ }
+ if last != n.LastChild {
+ return fmt.Errorf("html: inconsistent last relationship")
+ }
+
+ var first *Node
+ for c := n.LastChild; c != nil; c = c.PrevSibling {
+ if !seen[c] {
+ return fmt.Errorf("html: inconsistent missing child")
+ }
+ delete(seen, c)
+ first = c
+ }
+ if first != n.FirstChild {
+ return fmt.Errorf("html: inconsistent first relationship")
+ }
+
+ if len(seen) != 0 {
+ return fmt.Errorf("html: inconsistent forwards/backwards child list")
+ }
+
+ return nil
+}
diff --git a/html/parse.go b/html/parse.go
new file mode 100644
index 0000000..be4b2bf
--- /dev/null
+++ b/html/parse.go
@@ -0,0 +1,2094 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+
+ a "golang.org/x/net/html/atom"
+)
+
+// A parser implements the HTML5 parsing algorithm:
+// https://html.spec.whatwg.org/multipage/syntax.html#tree-construction
+type parser struct {
+ // tokenizer provides the tokens for the parser.
+ tokenizer *Tokenizer
+ // tok is the most recently read token.
+ tok Token
+ // Self-closing tags like <hr/> are treated as start tags, except that
+ // hasSelfClosingToken is set while they are being processed.
+ hasSelfClosingToken bool
+ // doc is the document root element.
+ doc *Node
+ // The stack of open elements (section 12.2.3.2) and active formatting
+ // elements (section 12.2.3.3).
+ oe, afe nodeStack
+ // Element pointers (section 12.2.3.4).
+ head, form *Node
+ // Other parsing state flags (section 12.2.3.5).
+ scripting, framesetOK bool
+ // im is the current insertion mode.
+ im insertionMode
+ // originalIM is the insertion mode to go back to after completing a text
+ // or inTableText insertion mode.
+ originalIM insertionMode
+ // fosterParenting is whether new elements should be inserted according to
+ // the foster parenting rules (section 12.2.5.3).
+ fosterParenting bool
+ // quirks is whether the parser is operating in "quirks mode."
+ quirks bool
+ // fragment is whether the parser is parsing an HTML fragment.
+ fragment bool
+ // context is the context element when parsing an HTML fragment
+ // (section 12.4).
+ context *Node
+}
+
+func (p *parser) top() *Node {
+ if n := p.oe.top(); n != nil {
+ return n
+ }
+ return p.doc
+}
+
+// Stop tags for use in popUntil. These come from section 12.2.3.2.
+var (
+ defaultScopeStopTags = map[string][]a.Atom{
+ "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
+ "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext},
+ "svg": {a.Desc, a.ForeignObject, a.Title},
+ }
+)
+
+type scope int
+
+const (
+ defaultScope scope = iota
+ listItemScope
+ buttonScope
+ tableScope
+ tableRowScope
+ tableBodyScope
+ selectScope
+)
+
+// popUntil pops the stack of open elements at the highest element whose tag
+// is in matchTags, provided there is no higher element in the scope's stop
+// tags (as defined in section 12.2.3.2). It returns whether or not there was
+// such an element. If there was not, popUntil leaves the stack unchanged.
+//
+// For example, the set of stop tags for table scope is: "html", "table". If
+// the stack was:
+// ["html", "body", "font", "table", "b", "i", "u"]
+// then popUntil(tableScope, "font") would return false, but
+// popUntil(tableScope, "i") would return true and the stack would become:
+// ["html", "body", "font", "table", "b"]
+//
+// If an element's tag is in both the stop tags and matchTags, then the stack
+// will be popped and the function returns true (provided, of course, there was
+// no higher element in the stack that was also in the stop tags). For example,
+// popUntil(tableScope, "table") returns true and leaves:
+// ["html", "body", "font"]
+func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool {
+ if i := p.indexOfElementInScope(s, matchTags...); i != -1 {
+ p.oe = p.oe[:i]
+ return true
+ }
+ return false
+}
+
+// indexOfElementInScope returns the index in p.oe of the highest element whose
+// tag is in matchTags that is in scope. If no matching element is in scope, it
+// returns -1.
+func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ tagAtom := p.oe[i].DataAtom
+ if p.oe[i].Namespace == "" {
+ for _, t := range matchTags {
+ if t == tagAtom {
+ return i
+ }
+ }
+ switch s {
+ case defaultScope:
+ // No-op.
+ case listItemScope:
+ if tagAtom == a.Ol || tagAtom == a.Ul {
+ return -1
+ }
+ case buttonScope:
+ if tagAtom == a.Button {
+ return -1
+ }
+ case tableScope:
+ if tagAtom == a.Html || tagAtom == a.Table {
+ return -1
+ }
+ case selectScope:
+ if tagAtom != a.Optgroup && tagAtom != a.Option {
+ return -1
+ }
+ default:
+ panic("unreachable")
+ }
+ }
+ switch s {
+ case defaultScope, listItemScope, buttonScope:
+ for _, t := range defaultScopeStopTags[p.oe[i].Namespace] {
+ if t == tagAtom {
+ return -1
+ }
+ }
+ }
+ }
+ return -1
+}
+
+// elementInScope is like popUntil, except that it doesn't modify the stack of
+// open elements.
+func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool {
+ return p.indexOfElementInScope(s, matchTags...) != -1
+}
+
+// clearStackToContext pops elements off the stack of open elements until a
+// scope-defined element is found.
+func (p *parser) clearStackToContext(s scope) {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ tagAtom := p.oe[i].DataAtom
+ switch s {
+ case tableScope:
+ if tagAtom == a.Html || tagAtom == a.Table {
+ p.oe = p.oe[:i+1]
+ return
+ }
+ case tableRowScope:
+ if tagAtom == a.Html || tagAtom == a.Tr {
+ p.oe = p.oe[:i+1]
+ return
+ }
+ case tableBodyScope:
+ if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead {
+ p.oe = p.oe[:i+1]
+ return
+ }
+ default:
+ panic("unreachable")
+ }
+ }
+}
+
+// generateImpliedEndTags pops nodes off the stack of open elements as long as
+// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt.
+// If exceptions are specified, nodes with that name will not be popped off.
+func (p *parser) generateImpliedEndTags(exceptions ...string) {
+ var i int
+loop:
+ for i = len(p.oe) - 1; i >= 0; i-- {
+ n := p.oe[i]
+ if n.Type == ElementNode {
+ switch n.DataAtom {
+ case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt:
+ for _, except := range exceptions {
+ if n.Data == except {
+ break loop
+ }
+ }
+ continue
+ }
+ }
+ break
+ }
+
+ p.oe = p.oe[:i+1]
+}
+
+// addChild adds a child node n to the top element, and pushes n onto the stack
+// of open elements if it is an element node.
+func (p *parser) addChild(n *Node) {
+ if p.shouldFosterParent() {
+ p.fosterParent(n)
+ } else {
+ p.top().AppendChild(n)
+ }
+
+ if n.Type == ElementNode {
+ p.oe = append(p.oe, n)
+ }
+}
+
+// shouldFosterParent returns whether the next node to be added should be
+// foster parented.
+func (p *parser) shouldFosterParent() bool {
+ if p.fosterParenting {
+ switch p.top().DataAtom {
+ case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ return true
+ }
+ }
+ return false
+}
+
+// fosterParent adds a child node according to the foster parenting rules.
+// Section 12.2.5.3, "foster parenting".
+func (p *parser) fosterParent(n *Node) {
+ var table, parent, prev *Node
+ var i int
+ for i = len(p.oe) - 1; i >= 0; i-- {
+ if p.oe[i].DataAtom == a.Table {
+ table = p.oe[i]
+ break
+ }
+ }
+
+ if table == nil {
+ // The foster parent is the html element.
+ parent = p.oe[0]
+ } else {
+ parent = table.Parent
+ }
+ if parent == nil {
+ parent = p.oe[i-1]
+ }
+
+ if table != nil {
+ prev = table.PrevSibling
+ } else {
+ prev = parent.LastChild
+ }
+ if prev != nil && prev.Type == TextNode && n.Type == TextNode {
+ prev.Data += n.Data
+ return
+ }
+
+ parent.InsertBefore(n, table)
+}
+
+// addText adds text to the preceding node if it is a text node, or else it
+// calls addChild with a new text node.
+func (p *parser) addText(text string) {
+ if text == "" {
+ return
+ }
+
+ if p.shouldFosterParent() {
+ p.fosterParent(&Node{
+ Type: TextNode,
+ Data: text,
+ })
+ return
+ }
+
+ t := p.top()
+ if n := t.LastChild; n != nil && n.Type == TextNode {
+ n.Data += text
+ return
+ }
+ p.addChild(&Node{
+ Type: TextNode,
+ Data: text,
+ })
+}
+
+// addElement adds a child element based on the current token.
+func (p *parser) addElement() {
+ p.addChild(&Node{
+ Type: ElementNode,
+ DataAtom: p.tok.DataAtom,
+ Data: p.tok.Data,
+ Attr: p.tok.Attr,
+ })
+}
+
+// Section 12.2.3.3.
+func (p *parser) addFormattingElement() {
+ tagAtom, attr := p.tok.DataAtom, p.tok.Attr
+ p.addElement()
+
+ // Implement the Noah's Ark clause, but with three per family instead of two.
+ identicalElements := 0
+findIdenticalElements:
+ for i := len(p.afe) - 1; i >= 0; i-- {
+ n := p.afe[i]
+ if n.Type == scopeMarkerNode {
+ break
+ }
+ if n.Type != ElementNode {
+ continue
+ }
+ if n.Namespace != "" {
+ continue
+ }
+ if n.DataAtom != tagAtom {
+ continue
+ }
+ if len(n.Attr) != len(attr) {
+ continue
+ }
+ compareAttributes:
+ for _, t0 := range n.Attr {
+ for _, t1 := range attr {
+ if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val {
+ // Found a match for this attribute, continue with the next attribute.
+ continue compareAttributes
+ }
+ }
+ // If we get here, there is no attribute that matches a.
+ // Therefore the element is not identical to the new one.
+ continue findIdenticalElements
+ }
+
+ identicalElements++
+ if identicalElements >= 3 {
+ p.afe.remove(n)
+ }
+ }
+
+ p.afe = append(p.afe, p.top())
+}
+
+// Section 12.2.3.3.
+func (p *parser) clearActiveFormattingElements() {
+ for {
+ n := p.afe.pop()
+ if len(p.afe) == 0 || n.Type == scopeMarkerNode {
+ return
+ }
+ }
+}
+
+// Section 12.2.3.3.
+func (p *parser) reconstructActiveFormattingElements() {
+ n := p.afe.top()
+ if n == nil {
+ return
+ }
+ if n.Type == scopeMarkerNode || p.oe.index(n) != -1 {
+ return
+ }
+ i := len(p.afe) - 1
+ for n.Type != scopeMarkerNode && p.oe.index(n) == -1 {
+ if i == 0 {
+ i = -1
+ break
+ }
+ i--
+ n = p.afe[i]
+ }
+ for {
+ i++
+ clone := p.afe[i].clone()
+ p.addChild(clone)
+ p.afe[i] = clone
+ if i == len(p.afe)-1 {
+ break
+ }
+ }
+}
+
+// Section 12.2.4.
+func (p *parser) acknowledgeSelfClosingTag() {
+ p.hasSelfClosingToken = false
+}
+
+// An insertion mode (section 12.2.3.1) is the state transition function from
+// a particular state in the HTML5 parser's state machine. It updates the
+// parser's fields depending on parser.tok (where ErrorToken means EOF).
+// It returns whether the token was consumed.
+type insertionMode func(*parser) bool
+
+// setOriginalIM sets the insertion mode to return to after completing a text or
+// inTableText insertion mode.
+// Section 12.2.3.1, "using the rules for".
+func (p *parser) setOriginalIM() {
+ if p.originalIM != nil {
+ panic("html: bad parser state: originalIM was set twice")
+ }
+ p.originalIM = p.im
+}
+
+// Section 12.2.3.1, "reset the insertion mode".
+func (p *parser) resetInsertionMode() {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ n := p.oe[i]
+ if i == 0 && p.context != nil {
+ n = p.context
+ }
+
+ switch n.DataAtom {
+ case a.Select:
+ p.im = inSelectIM
+ case a.Td, a.Th:
+ p.im = inCellIM
+ case a.Tr:
+ p.im = inRowIM
+ case a.Tbody, a.Thead, a.Tfoot:
+ p.im = inTableBodyIM
+ case a.Caption:
+ p.im = inCaptionIM
+ case a.Colgroup:
+ p.im = inColumnGroupIM
+ case a.Table:
+ p.im = inTableIM
+ case a.Head:
+ p.im = inBodyIM
+ case a.Body:
+ p.im = inBodyIM
+ case a.Frameset:
+ p.im = inFramesetIM
+ case a.Html:
+ p.im = beforeHeadIM
+ default:
+ continue
+ }
+ return
+ }
+ p.im = inBodyIM
+}
+
+const whitespace = " \t\r\n\f"
+
+// Section 12.2.5.4.1.
+func initialIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+ if len(p.tok.Data) == 0 {
+ // It was all whitespace, so ignore it.
+ return true
+ }
+ case CommentToken:
+ p.doc.AppendChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ n, quirks := parseDoctype(p.tok.Data)
+ p.doc.AppendChild(n)
+ p.quirks = quirks
+ p.im = beforeHTMLIM
+ return true
+ }
+ p.quirks = true
+ p.im = beforeHTMLIM
+ return false
+}
+
+// Section 12.2.5.4.2.
+func beforeHTMLIM(p *parser) bool {
+ switch p.tok.Type {
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ case TextToken:
+ p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+ if len(p.tok.Data) == 0 {
+ // It was all whitespace, so ignore it.
+ return true
+ }
+ case StartTagToken:
+ if p.tok.DataAtom == a.Html {
+ p.addElement()
+ p.im = beforeHeadIM
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Head, a.Body, a.Html, a.Br:
+ p.parseImpliedToken(StartTagToken, a.Html, a.Html.String())
+ return false
+ default:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.doc.AppendChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ }
+ p.parseImpliedToken(StartTagToken, a.Html, a.Html.String())
+ return false
+}
+
+// Section 12.2.5.4.3.
+func beforeHeadIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+ if len(p.tok.Data) == 0 {
+ // It was all whitespace, so ignore it.
+ return true
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Head:
+ p.addElement()
+ p.head = p.top()
+ p.im = inHeadIM
+ return true
+ case a.Html:
+ return inBodyIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Head, a.Body, a.Html, a.Br:
+ p.parseImpliedToken(StartTagToken, a.Head, a.Head.String())
+ return false
+ default:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ }
+
+ p.parseImpliedToken(StartTagToken, a.Head, a.Head.String())
+ return false
+}
+
+// Section 12.2.5.4.4.
+func inHeadIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ s := strings.TrimLeft(p.tok.Data, whitespace)
+ if len(s) < len(p.tok.Data) {
+ // Add the initial whitespace to the current node.
+ p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])
+ if s == "" {
+ return true
+ }
+ p.tok.Data = s
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta:
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ return true
+ case a.Script, a.Title, a.Noscript, a.Noframes, a.Style:
+ p.addElement()
+ p.setOriginalIM()
+ p.im = textIM
+ return true
+ case a.Head:
+ // Ignore the token.
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Head:
+ n := p.oe.pop()
+ if n.DataAtom != a.Head {
+ panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
+ }
+ p.im = afterHeadIM
+ return true
+ case a.Body, a.Html, a.Br:
+ p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
+ return false
+ default:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ }
+
+ p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
+ return false
+}
+
+// Section 12.2.5.4.6.
+func afterHeadIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ s := strings.TrimLeft(p.tok.Data, whitespace)
+ if len(s) < len(p.tok.Data) {
+ // Add the initial whitespace to the current node.
+ p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])
+ if s == "" {
+ return true
+ }
+ p.tok.Data = s
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Body:
+ p.addElement()
+ p.framesetOK = false
+ p.im = inBodyIM
+ return true
+ case a.Frameset:
+ p.addElement()
+ p.im = inFramesetIM
+ return true
+ case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
+ p.oe = append(p.oe, p.head)
+ defer p.oe.remove(p.head)
+ return inHeadIM(p)
+ case a.Head:
+ // Ignore the token.
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Body, a.Html, a.Br:
+ // Drop down to creating an implied <body> tag.
+ default:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ }
+
+ p.parseImpliedToken(StartTagToken, a.Body, a.Body.String())
+ p.framesetOK = true
+ return false
+}
+
+// copyAttributes copies attributes of src not found on dst to dst.
+func copyAttributes(dst *Node, src Token) {
+ if len(src.Attr) == 0 {
+ return
+ }
+ attr := map[string]string{}
+ for _, t := range dst.Attr {
+ attr[t.Key] = t.Val
+ }
+ for _, t := range src.Attr {
+ if _, ok := attr[t.Key]; !ok {
+ dst.Attr = append(dst.Attr, t)
+ attr[t.Key] = t.Val
+ }
+ }
+}
+
+// Section 12.2.5.4.7.
+func inBodyIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ d := p.tok.Data
+ switch n := p.oe.top(); n.DataAtom {
+ case a.Pre, a.Listing:
+ if n.FirstChild == nil {
+ // Ignore a newline at the start of a <pre> block.
+ if d != "" && d[0] == '\r' {
+ d = d[1:]
+ }
+ if d != "" && d[0] == '\n' {
+ d = d[1:]
+ }
+ }
+ }
+ d = strings.Replace(d, "\x00", "", -1)
+ if d == "" {
+ return true
+ }
+ p.reconstructActiveFormattingElements()
+ p.addText(d)
+ if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
+ // There were non-whitespace characters inserted.
+ p.framesetOK = false
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ copyAttributes(p.oe[0], p.tok)
+ case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
+ return inHeadIM(p)
+ case a.Body:
+ if len(p.oe) >= 2 {
+ body := p.oe[1]
+ if body.Type == ElementNode && body.DataAtom == a.Body {
+ p.framesetOK = false
+ copyAttributes(body, p.tok)
+ }
+ }
+ case a.Frameset:
+ if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body {
+ // Ignore the token.
+ return true
+ }
+ body := p.oe[1]
+ if body.Parent != nil {
+ body.Parent.RemoveChild(body)
+ }
+ p.oe = p.oe[:1]
+ p.addElement()
+ p.im = inFramesetIM
+ return true
+ case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+ p.popUntil(buttonScope, a.P)
+ switch n := p.top(); n.DataAtom {
+ case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+ p.oe.pop()
+ }
+ p.addElement()
+ case a.Pre, a.Listing:
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ // The newline, if any, will be dealt with by the TextToken case.
+ p.framesetOK = false
+ case a.Form:
+ if p.form == nil {
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ p.form = p.top()
+ }
+ case a.Li:
+ p.framesetOK = false
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ node := p.oe[i]
+ switch node.DataAtom {
+ case a.Li:
+ p.oe = p.oe[:i]
+ case a.Address, a.Div, a.P:
+ continue
+ default:
+ if !isSpecialElement(node) {
+ continue
+ }
+ }
+ break
+ }
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ case a.Dd, a.Dt:
+ p.framesetOK = false
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ node := p.oe[i]
+ switch node.DataAtom {
+ case a.Dd, a.Dt:
+ p.oe = p.oe[:i]
+ case a.Address, a.Div, a.P:
+ continue
+ default:
+ if !isSpecialElement(node) {
+ continue
+ }
+ }
+ break
+ }
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ case a.Plaintext:
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ case a.Button:
+ p.popUntil(defaultScope, a.Button)
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.framesetOK = false
+ case a.A:
+ for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {
+ if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {
+ p.inBodyEndTagFormatting(a.A)
+ p.oe.remove(n)
+ p.afe.remove(n)
+ break
+ }
+ }
+ p.reconstructActiveFormattingElements()
+ p.addFormattingElement()
+ case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
+ p.reconstructActiveFormattingElements()
+ p.addFormattingElement()
+ case a.Nobr:
+ p.reconstructActiveFormattingElements()
+ if p.elementInScope(defaultScope, a.Nobr) {
+ p.inBodyEndTagFormatting(a.Nobr)
+ p.reconstructActiveFormattingElements()
+ }
+ p.addFormattingElement()
+ case a.Applet, a.Marquee, a.Object:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.afe = append(p.afe, &scopeMarker)
+ p.framesetOK = false
+ case a.Table:
+ if !p.quirks {
+ p.popUntil(buttonScope, a.P)
+ }
+ p.addElement()
+ p.framesetOK = false
+ p.im = inTableIM
+ return true
+ case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ if p.tok.DataAtom == a.Input {
+ for _, t := range p.tok.Attr {
+ if t.Key == "type" {
+ if strings.ToLower(t.Val) == "hidden" {
+ // Skip setting framesetOK = false
+ return true
+ }
+ }
+ }
+ }
+ p.framesetOK = false
+ case a.Param, a.Source, a.Track:
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ case a.Hr:
+ p.popUntil(buttonScope, a.P)
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ p.framesetOK = false
+ case a.Image:
+ p.tok.DataAtom = a.Img
+ p.tok.Data = a.Img.String()
+ return false
+ case a.Isindex:
+ if p.form != nil {
+ // Ignore the token.
+ return true
+ }
+ action := ""
+ prompt := "This is a searchable index. Enter search keywords: "
+ attr := []Attribute{{Key: "name", Val: "isindex"}}
+ for _, t := range p.tok.Attr {
+ switch t.Key {
+ case "action":
+ action = t.Val
+ case "name":
+ // Ignore the attribute.
+ case "prompt":
+ prompt = t.Val
+ default:
+ attr = append(attr, t)
+ }
+ }
+ p.acknowledgeSelfClosingTag()
+ p.popUntil(buttonScope, a.P)
+ p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
+ if action != "" {
+ p.form.Attr = []Attribute{{Key: "action", Val: action}}
+ }
+ p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
+ p.parseImpliedToken(StartTagToken, a.Label, a.Label.String())
+ p.addText(prompt)
+ p.addChild(&Node{
+ Type: ElementNode,
+ DataAtom: a.Input,
+ Data: a.Input.String(),
+ Attr: attr,
+ })
+ p.oe.pop()
+ p.parseImpliedToken(EndTagToken, a.Label, a.Label.String())
+ p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
+ p.parseImpliedToken(EndTagToken, a.Form, a.Form.String())
+ case a.Textarea:
+ p.addElement()
+ p.setOriginalIM()
+ p.framesetOK = false
+ p.im = textIM
+ case a.Xmp:
+ p.popUntil(buttonScope, a.P)
+ p.reconstructActiveFormattingElements()
+ p.framesetOK = false
+ p.addElement()
+ p.setOriginalIM()
+ p.im = textIM
+ case a.Iframe:
+ p.framesetOK = false
+ p.addElement()
+ p.setOriginalIM()
+ p.im = textIM
+ case a.Noembed, a.Noscript:
+ p.addElement()
+ p.setOriginalIM()
+ p.im = textIM
+ case a.Select:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.framesetOK = false
+ p.im = inSelectIM
+ return true
+ case a.Optgroup, a.Option:
+ if p.top().DataAtom == a.Option {
+ p.oe.pop()
+ }
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ case a.Rp, a.Rt:
+ if p.elementInScope(defaultScope, a.Ruby) {
+ p.generateImpliedEndTags()
+ }
+ p.addElement()
+ case a.Math, a.Svg:
+ p.reconstructActiveFormattingElements()
+ if p.tok.DataAtom == a.Math {
+ adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
+ } else {
+ adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
+ }
+ adjustForeignAttributes(p.tok.Attr)
+ p.addElement()
+ p.top().Namespace = p.tok.Data
+ if p.hasSelfClosingToken {
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ }
+ return true
+ case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+ // Ignore the token.
+ default:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Body:
+ if p.elementInScope(defaultScope, a.Body) {
+ p.im = afterBodyIM
+ }
+ case a.Html:
+ if p.elementInScope(defaultScope, a.Body) {
+ p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
+ return false
+ }
+ return true
+ case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
+ p.popUntil(defaultScope, p.tok.DataAtom)
+ case a.Form:
+ node := p.form
+ p.form = nil
+ i := p.indexOfElementInScope(defaultScope, a.Form)
+ if node == nil || i == -1 || p.oe[i] != node {
+ // Ignore the token.
+ return true
+ }
+ p.generateImpliedEndTags()
+ p.oe.remove(node)
+ case a.P:
+ if !p.elementInScope(buttonScope, a.P) {
+ p.parseImpliedToken(StartTagToken, a.P, a.P.String())
+ }
+ p.popUntil(buttonScope, a.P)
+ case a.Li:
+ p.popUntil(listItemScope, a.Li)
+ case a.Dd, a.Dt:
+ p.popUntil(defaultScope, p.tok.DataAtom)
+ case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+ p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
+ case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
+ p.inBodyEndTagFormatting(p.tok.DataAtom)
+ case a.Applet, a.Marquee, a.Object:
+ if p.popUntil(defaultScope, p.tok.DataAtom) {
+ p.clearActiveFormattingElements()
+ }
+ case a.Br:
+ p.tok.Type = StartTagToken
+ return false
+ default:
+ p.inBodyEndTagOther(p.tok.DataAtom)
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ }
+
+ return true
+}
+
+func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
+ // This is the "adoption agency" algorithm, described at
+ // https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency
+
+ // TODO: this is a fairly literal line-by-line translation of that algorithm.
+ // Once the code successfully parses the comprehensive test suite, we should
+ // refactor this code to be more idiomatic.
+
+ // Steps 1-4. The outer loop.
+ for i := 0; i < 8; i++ {
+ // Step 5. Find the formatting element.
+ var formattingElement *Node
+ for j := len(p.afe) - 1; j >= 0; j-- {
+ if p.afe[j].Type == scopeMarkerNode {
+ break
+ }
+ if p.afe[j].DataAtom == tagAtom {
+ formattingElement = p.afe[j]
+ break
+ }
+ }
+ if formattingElement == nil {
+ p.inBodyEndTagOther(tagAtom)
+ return
+ }
+ feIndex := p.oe.index(formattingElement)
+ if feIndex == -1 {
+ p.afe.remove(formattingElement)
+ return
+ }
+ if !p.elementInScope(defaultScope, tagAtom) {
+ // Ignore the tag.
+ return
+ }
+
+ // Steps 9-10. Find the furthest block.
+ var furthestBlock *Node
+ for _, e := range p.oe[feIndex:] {
+ if isSpecialElement(e) {
+ furthestBlock = e
+ break
+ }
+ }
+ if furthestBlock == nil {
+ e := p.oe.pop()
+ for e != formattingElement {
+ e = p.oe.pop()
+ }
+ p.afe.remove(e)
+ return
+ }
+
+ // Steps 11-12. Find the common ancestor and bookmark node.
+ commonAncestor := p.oe[feIndex-1]
+ bookmark := p.afe.index(formattingElement)
+
+ // Step 13. The inner loop. Find the lastNode to reparent.
+ lastNode := furthestBlock
+ node := furthestBlock
+ x := p.oe.index(node)
+ // Steps 13.1-13.2
+ for j := 0; j < 3; j++ {
+ // Step 13.3.
+ x--
+ node = p.oe[x]
+ // Step 13.4 - 13.5.
+ if p.afe.index(node) == -1 {
+ p.oe.remove(node)
+ continue
+ }
+ // Step 13.6.
+ if node == formattingElement {
+ break
+ }
+ // Step 13.7.
+ clone := node.clone()
+ p.afe[p.afe.index(node)] = clone
+ p.oe[p.oe.index(node)] = clone
+ node = clone
+ // Step 13.8.
+ if lastNode == furthestBlock {
+ bookmark = p.afe.index(node) + 1
+ }
+ // Step 13.9.
+ if lastNode.Parent != nil {
+ lastNode.Parent.RemoveChild(lastNode)
+ }
+ node.AppendChild(lastNode)
+ // Step 13.10.
+ lastNode = node
+ }
+
+ // Step 14. Reparent lastNode to the common ancestor,
+ // or for misnested table nodes, to the foster parent.
+ if lastNode.Parent != nil {
+ lastNode.Parent.RemoveChild(lastNode)
+ }
+ switch commonAncestor.DataAtom {
+ case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ p.fosterParent(lastNode)
+ default:
+ commonAncestor.AppendChild(lastNode)
+ }
+
+ // Steps 15-17. Reparent nodes from the furthest block's children
+ // to a clone of the formatting element.
+ clone := formattingElement.clone()
+ reparentChildren(clone, furthestBlock)
+ furthestBlock.AppendChild(clone)
+
+ // Step 18. Fix up the list of active formatting elements.
+ if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {
+ // Move the bookmark with the rest of the list.
+ bookmark--
+ }
+ p.afe.remove(formattingElement)
+ p.afe.insert(bookmark, clone)
+
+ // Step 19. Fix up the stack of open elements.
+ p.oe.remove(formattingElement)
+ p.oe.insert(p.oe.index(furthestBlock)+1, clone)
+ }
+}
+
+// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
+// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content
+// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
+func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ if p.oe[i].DataAtom == tagAtom {
+ p.oe = p.oe[:i]
+ break
+ }
+ if isSpecialElement(p.oe[i]) {
+ break
+ }
+ }
+}
+
+// Section 12.2.5.4.8.
+func textIM(p *parser) bool {
+ switch p.tok.Type {
+ case ErrorToken:
+ p.oe.pop()
+ case TextToken:
+ d := p.tok.Data
+ if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil {
+ // Ignore a newline at the start of a <textarea> block.
+ if d != "" && d[0] == '\r' {
+ d = d[1:]
+ }
+ if d != "" && d[0] == '\n' {
+ d = d[1:]
+ }
+ }
+ if d == "" {
+ return true
+ }
+ p.addText(d)
+ return true
+ case EndTagToken:
+ p.oe.pop()
+ }
+ p.im = p.originalIM
+ p.originalIM = nil
+ return p.tok.Type == EndTagToken
+}
+
+// Section 12.2.5.4.9.
+func inTableIM(p *parser) bool {
+ switch p.tok.Type {
+ case ErrorToken:
+ // Stop parsing.
+ return true
+ case TextToken:
+ p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1)
+ switch p.oe.top().DataAtom {
+ case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ if strings.Trim(p.tok.Data, whitespace) == "" {
+ p.addText(p.tok.Data)
+ return true
+ }
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Caption:
+ p.clearStackToContext(tableScope)
+ p.afe = append(p.afe, &scopeMarker)
+ p.addElement()
+ p.im = inCaptionIM
+ return true
+ case a.Colgroup:
+ p.clearStackToContext(tableScope)
+ p.addElement()
+ p.im = inColumnGroupIM
+ return true
+ case a.Col:
+ p.parseImpliedToken(StartTagToken, a.Colgroup, a.Colgroup.String())
+ return false
+ case a.Tbody, a.Tfoot, a.Thead:
+ p.clearStackToContext(tableScope)
+ p.addElement()
+ p.im = inTableBodyIM
+ return true
+ case a.Td, a.Th, a.Tr:
+ p.parseImpliedToken(StartTagToken, a.Tbody, a.Tbody.String())
+ return false
+ case a.Table:
+ if p.popUntil(tableScope, a.Table) {
+ p.resetInsertionMode()
+ return false
+ }
+ // Ignore the token.
+ return true
+ case a.Style, a.Script:
+ return inHeadIM(p)
+ case a.Input:
+ for _, t := range p.tok.Attr {
+ if t.Key == "type" && strings.ToLower(t.Val) == "hidden" {
+ p.addElement()
+ p.oe.pop()
+ return true
+ }
+ }
+ // Otherwise drop down to the default action.
+ case a.Form:
+ if p.form != nil {
+ // Ignore the token.
+ return true
+ }
+ p.addElement()
+ p.form = p.oe.pop()
+ case a.Select:
+ p.reconstructActiveFormattingElements()
+ switch p.top().DataAtom {
+ case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ p.fosterParenting = true
+ }
+ p.addElement()
+ p.fosterParenting = false
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Table:
+ if p.popUntil(tableScope, a.Table) {
+ p.resetInsertionMode()
+ return true
+ }
+ // Ignore the token.
+ return true
+ case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ }
+
+ p.fosterParenting = true
+ defer func() { p.fosterParenting = false }()
+
+ return inBodyIM(p)
+}
+
+// Section 12.2.5.4.11.
+func inCaptionIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr:
+ if p.popUntil(tableScope, a.Caption) {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ return false
+ } else {
+ // Ignore the token.
+ return true
+ }
+ case a.Select:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Caption:
+ if p.popUntil(tableScope, a.Caption) {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ }
+ return true
+ case a.Table:
+ if p.popUntil(tableScope, a.Caption) {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ return false
+ } else {
+ // Ignore the token.
+ return true
+ }
+ case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+ // Ignore the token.
+ return true
+ }
+ }
+ return inBodyIM(p)
+}
+
+// Section 12.2.5.4.12.
+func inColumnGroupIM(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ s := strings.TrimLeft(p.tok.Data, whitespace)
+ if len(s) < len(p.tok.Data) {
+ // Add the initial whitespace to the current node.
+ p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])
+ if s == "" {
+ return true
+ }
+ p.tok.Data = s
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Col:
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Colgroup:
+ if p.oe.top().DataAtom != a.Html {
+ p.oe.pop()
+ p.im = inTableIM
+ }
+ return true
+ case a.Col:
+ // Ignore the token.
+ return true
+ }
+ }
+ if p.oe.top().DataAtom != a.Html {
+ p.oe.pop()
+ p.im = inTableIM
+ return false
+ }
+ return true
+}
+
+// Section 12.2.5.4.13.
+func inTableBodyIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Tr:
+ p.clearStackToContext(tableBodyScope)
+ p.addElement()
+ p.im = inRowIM
+ return true
+ case a.Td, a.Th:
+ p.parseImpliedToken(StartTagToken, a.Tr, a.Tr.String())
+ return false
+ case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
+ if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {
+ p.im = inTableIM
+ return false
+ }
+ // Ignore the token.
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Tbody, a.Tfoot, a.Thead:
+ if p.elementInScope(tableScope, p.tok.DataAtom) {
+ p.clearStackToContext(tableBodyScope)
+ p.oe.pop()
+ p.im = inTableIM
+ }
+ return true
+ case a.Table:
+ if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) {
+ p.im = inTableIM
+ return false
+ }
+ // Ignore the token.
+ return true
+ case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th, a.Tr:
+ // Ignore the token.
+ return true
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ }
+
+ return inTableIM(p)
+}
+
+// Section 12.2.5.4.14.
+func inRowIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Td, a.Th:
+ p.clearStackToContext(tableRowScope)
+ p.addElement()
+ p.afe = append(p.afe, &scopeMarker)
+ p.im = inCellIM
+ return true
+ case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ if p.popUntil(tableScope, a.Tr) {
+ p.im = inTableBodyIM
+ return false
+ }
+ // Ignore the token.
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Tr:
+ if p.popUntil(tableScope, a.Tr) {
+ p.im = inTableBodyIM
+ return true
+ }
+ // Ignore the token.
+ return true
+ case a.Table:
+ if p.popUntil(tableScope, a.Tr) {
+ p.im = inTableBodyIM
+ return false
+ }
+ // Ignore the token.
+ return true
+ case a.Tbody, a.Tfoot, a.Thead:
+ if p.elementInScope(tableScope, p.tok.DataAtom) {
+ p.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String())
+ return false
+ }
+ // Ignore the token.
+ return true
+ case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th:
+ // Ignore the token.
+ return true
+ }
+ }
+
+ return inTableIM(p)
+}
+
+// Section 12.2.5.4.15.
+func inCellIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+ if p.popUntil(tableScope, a.Td, a.Th) {
+ // Close the cell and reprocess.
+ p.clearActiveFormattingElements()
+ p.im = inRowIM
+ return false
+ }
+ // Ignore the token.
+ return true
+ case a.Select:
+ p.reconstructActiveFormattingElements()
+ p.addElement()
+ p.framesetOK = false
+ p.im = inSelectInTableIM
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Td, a.Th:
+ if !p.popUntil(tableScope, p.tok.DataAtom) {
+ // Ignore the token.
+ return true
+ }
+ p.clearActiveFormattingElements()
+ p.im = inRowIM
+ return true
+ case a.Body, a.Caption, a.Col, a.Colgroup, a.Html:
+ // Ignore the token.
+ return true
+ case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+ if !p.elementInScope(tableScope, p.tok.DataAtom) {
+ // Ignore the token.
+ return true
+ }
+ // Close the cell and reprocess.
+ p.popUntil(tableScope, a.Td, a.Th)
+ p.clearActiveFormattingElements()
+ p.im = inRowIM
+ return false
+ }
+ }
+ return inBodyIM(p)
+}
+
+// Section 12.2.5.4.16.
+func inSelectIM(p *parser) bool {
+ switch p.tok.Type {
+ case ErrorToken:
+ // Stop parsing.
+ return true
+ case TextToken:
+ p.addText(strings.Replace(p.tok.Data, "\x00", "", -1))
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Option:
+ if p.top().DataAtom == a.Option {
+ p.oe.pop()
+ }
+ p.addElement()
+ case a.Optgroup:
+ if p.top().DataAtom == a.Option {
+ p.oe.pop()
+ }
+ if p.top().DataAtom == a.Optgroup {
+ p.oe.pop()
+ }
+ p.addElement()
+ case a.Select:
+ p.tok.Type = EndTagToken
+ return false
+ case a.Input, a.Keygen, a.Textarea:
+ if p.elementInScope(selectScope, a.Select) {
+ p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
+ return false
+ }
+ // In order to properly ignore <textarea>, we need to change the tokenizer mode.
+ p.tokenizer.NextIsNotRawText()
+ // Ignore the token.
+ return true
+ case a.Script:
+ return inHeadIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Option:
+ if p.top().DataAtom == a.Option {
+ p.oe.pop()
+ }
+ case a.Optgroup:
+ i := len(p.oe) - 1
+ if p.oe[i].DataAtom == a.Option {
+ i--
+ }
+ if p.oe[i].DataAtom == a.Optgroup {
+ p.oe = p.oe[:i]
+ }
+ case a.Select:
+ if p.popUntil(selectScope, a.Select) {
+ p.resetInsertionMode()
+ }
+ }
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ }
+
+ return true
+}
+
+// Section 12.2.5.4.17.
+func inSelectInTableIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken, EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th:
+ if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.DataAtom) {
+ p.parseImpliedToken(EndTagToken, a.Select, a.Select.String())
+ return false
+ } else {
+ // Ignore the token.
+ return true
+ }
+ }
+ }
+ return inSelectIM(p)
+}
+
+// Section 12.2.5.4.18.
+func afterBodyIM(p *parser) bool {
+ switch p.tok.Type {
+ case ErrorToken:
+ // Stop parsing.
+ return true
+ case TextToken:
+ s := strings.TrimLeft(p.tok.Data, whitespace)
+ if len(s) == 0 {
+ // It was all whitespace.
+ return inBodyIM(p)
+ }
+ case StartTagToken:
+ if p.tok.DataAtom == a.Html {
+ return inBodyIM(p)
+ }
+ case EndTagToken:
+ if p.tok.DataAtom == a.Html {
+ if !p.fragment {
+ p.im = afterAfterBodyIM
+ }
+ return true
+ }
+ case CommentToken:
+ // The comment is attached to the <html> element.
+ if len(p.oe) < 1 || p.oe[0].DataAtom != a.Html {
+ panic("html: bad parser state: <html> element not found, in the after-body insertion mode")
+ }
+ p.oe[0].AppendChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ }
+ p.im = inBodyIM
+ return false
+}
+
+// Section 12.2.5.4.19.
+func inFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case TextToken:
+ // Ignore all text but whitespace.
+ s := strings.Map(func(c rune) rune {
+ switch c {
+ case ' ', '\t', '\n', '\f', '\r':
+ return c
+ }
+ return -1
+ }, p.tok.Data)
+ if s != "" {
+ p.addText(s)
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Frameset:
+ p.addElement()
+ case a.Frame:
+ p.addElement()
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ case a.Noframes:
+ return inHeadIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Frameset:
+ if p.oe.top().DataAtom != a.Html {
+ p.oe.pop()
+ if p.oe.top().DataAtom != a.Frameset {
+ p.im = afterFramesetIM
+ return true
+ }
+ }
+ }
+ default:
+ // Ignore the token.
+ }
+ return true
+}
+
+// Section 12.2.5.4.20.
+func afterFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case TextToken:
+ // Ignore all text but whitespace.
+ s := strings.Map(func(c rune) rune {
+ switch c {
+ case ' ', '\t', '\n', '\f', '\r':
+ return c
+ }
+ return -1
+ }, p.tok.Data)
+ if s != "" {
+ p.addText(s)
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Noframes:
+ return inHeadIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ p.im = afterAfterFramesetIM
+ return true
+ }
+ default:
+ // Ignore the token.
+ }
+ return true
+}
+
+// Section 12.2.5.4.21.
+func afterAfterBodyIM(p *parser) bool {
+ switch p.tok.Type {
+ case ErrorToken:
+ // Stop parsing.
+ return true
+ case TextToken:
+ s := strings.TrimLeft(p.tok.Data, whitespace)
+ if len(s) == 0 {
+ // It was all whitespace.
+ return inBodyIM(p)
+ }
+ case StartTagToken:
+ if p.tok.DataAtom == a.Html {
+ return inBodyIM(p)
+ }
+ case CommentToken:
+ p.doc.AppendChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ return inBodyIM(p)
+ }
+ p.im = inBodyIM
+ return false
+}
+
+// Section 12.2.5.4.22.
+func afterAfterFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.doc.AppendChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case TextToken:
+ // Ignore all text but whitespace.
+ s := strings.Map(func(c rune) rune {
+ switch c {
+ case ' ', '\t', '\n', '\f', '\r':
+ return c
+ }
+ return -1
+ }, p.tok.Data)
+ if s != "" {
+ p.tok.Data = s
+ return inBodyIM(p)
+ }
+ case StartTagToken:
+ switch p.tok.DataAtom {
+ case a.Html:
+ return inBodyIM(p)
+ case a.Noframes:
+ return inHeadIM(p)
+ }
+ case DoctypeToken:
+ return inBodyIM(p)
+ default:
+ // Ignore the token.
+ }
+ return true
+}
+
+const whitespaceOrNUL = whitespace + "\x00"
+
+// Section 12.2.5.5.
+func parseForeignContent(p *parser) bool {
+ switch p.tok.Type {
+ case TextToken:
+ if p.framesetOK {
+ p.framesetOK = strings.TrimLeft(p.tok.Data, whitespaceOrNUL) == ""
+ }
+ p.tok.Data = strings.Replace(p.tok.Data, "\x00", "\ufffd", -1)
+ p.addText(p.tok.Data)
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case StartTagToken:
+ b := breakout[p.tok.Data]
+ if p.tok.DataAtom == a.Font {
+ loop:
+ for _, attr := range p.tok.Attr {
+ switch attr.Key {
+ case "color", "face", "size":
+ b = true
+ break loop
+ }
+ }
+ }
+ if b {
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ n := p.oe[i]
+ if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) {
+ p.oe = p.oe[:i+1]
+ break
+ }
+ }
+ return false
+ }
+ switch p.top().Namespace {
+ case "math":
+ adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
+ case "svg":
+ // Adjust SVG tag names. The tokenizer lower-cases tag names, but
+ // SVG wants e.g. "foreignObject" with a capital second "O".
+ if x := svgTagNameAdjustments[p.tok.Data]; x != "" {
+ p.tok.DataAtom = a.Lookup([]byte(x))
+ p.tok.Data = x
+ }
+ adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
+ default:
+ panic("html: bad parser state: unexpected namespace")
+ }
+ adjustForeignAttributes(p.tok.Attr)
+ namespace := p.top().Namespace
+ p.addElement()
+ p.top().Namespace = namespace
+ if namespace != "" {
+ // Don't let the tokenizer go into raw text mode in foreign content
+ // (e.g. in an SVG <title> tag).
+ p.tokenizer.NextIsNotRawText()
+ }
+ if p.hasSelfClosingToken {
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ }
+ case EndTagToken:
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ if p.oe[i].Namespace == "" {
+ return p.im(p)
+ }
+ if strings.EqualFold(p.oe[i].Data, p.tok.Data) {
+ p.oe = p.oe[:i]
+ break
+ }
+ }
+ return true
+ default:
+ // Ignore the token.
+ }
+ return true
+}
+
+// Section 12.2.5.
+func (p *parser) inForeignContent() bool {
+ if len(p.oe) == 0 {
+ return false
+ }
+ n := p.oe[len(p.oe)-1]
+ if n.Namespace == "" {
+ return false
+ }
+ if mathMLTextIntegrationPoint(n) {
+ if p.tok.Type == StartTagToken && p.tok.DataAtom != a.Mglyph && p.tok.DataAtom != a.Malignmark {
+ return false
+ }
+ if p.tok.Type == TextToken {
+ return false
+ }
+ }
+ if n.Namespace == "math" && n.DataAtom == a.AnnotationXml && p.tok.Type == StartTagToken && p.tok.DataAtom == a.Svg {
+ return false
+ }
+ if htmlIntegrationPoint(n) && (p.tok.Type == StartTagToken || p.tok.Type == TextToken) {
+ return false
+ }
+ if p.tok.Type == ErrorToken {
+ return false
+ }
+ return true
+}
+
+// parseImpliedToken parses a token as though it had appeared in the parser's
+// input.
+func (p *parser) parseImpliedToken(t TokenType, dataAtom a.Atom, data string) {
+ realToken, selfClosing := p.tok, p.hasSelfClosingToken
+ p.tok = Token{
+ Type: t,
+ DataAtom: dataAtom,
+ Data: data,
+ }
+ p.hasSelfClosingToken = false
+ p.parseCurrentToken()
+ p.tok, p.hasSelfClosingToken = realToken, selfClosing
+}
+
+// parseCurrentToken runs the current token through the parsing routines
+// until it is consumed.
+func (p *parser) parseCurrentToken() {
+ if p.tok.Type == SelfClosingTagToken {
+ p.hasSelfClosingToken = true
+ p.tok.Type = StartTagToken
+ }
+
+ consumed := false
+ for !consumed {
+ if p.inForeignContent() {
+ consumed = parseForeignContent(p)
+ } else {
+ consumed = p.im(p)
+ }
+ }
+
+ if p.hasSelfClosingToken {
+ // This is a parse error, but ignore it.
+ p.hasSelfClosingToken = false
+ }
+}
+
+func (p *parser) parse() error {
+ // Iterate until EOF. Any other error will cause an early return.
+ var err error
+ for err != io.EOF {
+ // CDATA sections are allowed only in foreign content.
+ n := p.oe.top()
+ p.tokenizer.AllowCDATA(n != nil && n.Namespace != "")
+ // Read and parse the next token.
+ p.tokenizer.Next()
+ p.tok = p.tokenizer.Token()
+ if p.tok.Type == ErrorToken {
+ err = p.tokenizer.Err()
+ if err != nil && err != io.EOF {
+ return err
+ }
+ }
+ p.parseCurrentToken()
+ }
+ return nil
+}
+
+// Parse returns the parse tree for the HTML from the given Reader.
+// The input is assumed to be UTF-8 encoded.
+func Parse(r io.Reader) (*Node, error) {
+ p := &parser{
+ tokenizer: NewTokenizer(r),
+ doc: &Node{
+ Type: DocumentNode,
+ },
+ scripting: true,
+ framesetOK: true,
+ im: initialIM,
+ }
+ err := p.parse()
+ if err != nil {
+ return nil, err
+ }
+ return p.doc, nil
+}
+
+// ParseFragment parses a fragment of HTML and returns the nodes that were
+// found. If the fragment is the InnerHTML for an existing element, pass that
+// element in context.
+func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
+ contextTag := ""
+ if context != nil {
+ if context.Type != ElementNode {
+ return nil, errors.New("html: ParseFragment of non-element Node")
+ }
+ // The next check isn't just context.DataAtom.String() == context.Data because
+ // it is valid to pass an element whose tag isn't a known atom. For example,
+ // DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent.
+ if context.DataAtom != a.Lookup([]byte(context.Data)) {
+ return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)
+ }
+ contextTag = context.DataAtom.String()
+ }
+ p := &parser{
+ tokenizer: NewTokenizerFragment(r, contextTag),
+ doc: &Node{
+ Type: DocumentNode,
+ },
+ scripting: true,
+ fragment: true,
+ context: context,
+ }
+
+ root := &Node{
+ Type: ElementNode,
+ DataAtom: a.Html,
+ Data: a.Html.String(),
+ }
+ p.doc.AppendChild(root)
+ p.oe = nodeStack{root}
+ p.resetInsertionMode()
+
+ for n := context; n != nil; n = n.Parent {
+ if n.Type == ElementNode && n.DataAtom == a.Form {
+ p.form = n
+ break
+ }
+ }
+
+ err := p.parse()
+ if err != nil {
+ return nil, err
+ }
+
+ parent := p.doc
+ if context != nil {
+ parent = root
+ }
+
+ var result []*Node
+ for c := parent.FirstChild; c != nil; {
+ next := c.NextSibling
+ parent.RemoveChild(c)
+ result = append(result, c)
+ c = next
+ }
+ return result, nil
+}
diff --git a/html/parse_test.go b/html/parse_test.go
new file mode 100644
index 0000000..7e47d11
--- /dev/null
+++ b/html/parse_test.go
@@ -0,0 +1,388 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+ "testing"
+
+ "golang.org/x/net/html/atom"
+)
+
+// readParseTest reads a single test case from r.
+func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ return "", "", "", err
+ }
+ var b []byte
+
+ // Read the HTML.
+ if string(line) != "#data\n" {
+ return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line)
+ }
+ for {
+ line, err = r.ReadSlice('\n')
+ if err != nil {
+ return "", "", "", err
+ }
+ if line[0] == '#' {
+ break
+ }
+ b = append(b, line...)
+ }
+ text = strings.TrimSuffix(string(b), "\n")
+ b = b[:0]
+
+ // Skip the error list.
+ if string(line) != "#errors\n" {
+ return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line)
+ }
+ for {
+ line, err = r.ReadSlice('\n')
+ if err != nil {
+ return "", "", "", err
+ }
+ if line[0] == '#' {
+ break
+ }
+ }
+
+ if string(line) == "#document-fragment\n" {
+ line, err = r.ReadSlice('\n')
+ if err != nil {
+ return "", "", "", err
+ }
+ context = strings.TrimSpace(string(line))
+ line, err = r.ReadSlice('\n')
+ if err != nil {
+ return "", "", "", err
+ }
+ }
+
+ // Read the dump of what the parse tree should be.
+ if string(line) != "#document\n" {
+ return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line)
+ }
+ inQuote := false
+ for {
+ line, err = r.ReadSlice('\n')
+ if err != nil && err != io.EOF {
+ return "", "", "", err
+ }
+ trimmed := bytes.Trim(line, "| \n")
+ if len(trimmed) > 0 {
+ if line[0] == '|' && trimmed[0] == '"' {
+ inQuote = true
+ }
+ if trimmed[len(trimmed)-1] == '"' && !(line[0] == '|' && len(trimmed) == 1) {
+ inQuote = false
+ }
+ }
+ if len(line) == 0 || len(line) == 1 && line[0] == '\n' && !inQuote {
+ break
+ }
+ b = append(b, line...)
+ }
+ return text, string(b), context, nil
+}
+
+func dumpIndent(w io.Writer, level int) {
+ io.WriteString(w, "| ")
+ for i := 0; i < level; i++ {
+ io.WriteString(w, " ")
+ }
+}
+
+type sortedAttributes []Attribute
+
+func (a sortedAttributes) Len() int {
+ return len(a)
+}
+
+func (a sortedAttributes) Less(i, j int) bool {
+ if a[i].Namespace != a[j].Namespace {
+ return a[i].Namespace < a[j].Namespace
+ }
+ return a[i].Key < a[j].Key
+}
+
+func (a sortedAttributes) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func dumpLevel(w io.Writer, n *Node, level int) error {
+ dumpIndent(w, level)
+ switch n.Type {
+ case ErrorNode:
+ return errors.New("unexpected ErrorNode")
+ case DocumentNode:
+ return errors.New("unexpected DocumentNode")
+ case ElementNode:
+ if n.Namespace != "" {
+ fmt.Fprintf(w, "<%s %s>", n.Namespace, n.Data)
+ } else {
+ fmt.Fprintf(w, "<%s>", n.Data)
+ }
+ attr := sortedAttributes(n.Attr)
+ sort.Sort(attr)
+ for _, a := range attr {
+ io.WriteString(w, "\n")
+ dumpIndent(w, level+1)
+ if a.Namespace != "" {
+ fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val)
+ } else {
+ fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
+ }
+ }
+ case TextNode:
+ fmt.Fprintf(w, `"%s"`, n.Data)
+ case CommentNode:
+ fmt.Fprintf(w, "<!-- %s -->", n.Data)
+ case DoctypeNode:
+ fmt.Fprintf(w, "<!DOCTYPE %s", n.Data)
+ if n.Attr != nil {
+ var p, s string
+ for _, a := range n.Attr {
+ switch a.Key {
+ case "public":
+ p = a.Val
+ case "system":
+ s = a.Val
+ }
+ }
+ if p != "" || s != "" {
+ fmt.Fprintf(w, ` "%s"`, p)
+ fmt.Fprintf(w, ` "%s"`, s)
+ }
+ }
+ io.WriteString(w, ">")
+ case scopeMarkerNode:
+ return errors.New("unexpected scopeMarkerNode")
+ default:
+ return errors.New("unknown node type")
+ }
+ io.WriteString(w, "\n")
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if err := dumpLevel(w, c, level+1); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func dump(n *Node) (string, error) {
+ if n == nil || n.FirstChild == nil {
+ return "", nil
+ }
+ var b bytes.Buffer
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if err := dumpLevel(&b, c, 0); err != nil {
+ return "", err
+ }
+ }
+ return b.String(), nil
+}
+
+const testDataDir = "testdata/webkit/"
+
+func TestParser(t *testing.T) {
+ testFiles, err := filepath.Glob(testDataDir + "*.dat")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, tf := range testFiles {
+ f, err := os.Open(tf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ r := bufio.NewReader(f)
+
+ for i := 0; ; i++ {
+ text, want, context, err := readParseTest(r)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = testParseCase(text, want, context)
+
+ if err != nil {
+ t.Errorf("%s test #%d %q, %s", tf, i, text, err)
+ }
+ }
+ }
+}
+
+// testParseCase tests one test case from the test files. If the test does not
+// pass, it returns an error that explains the failure.
+// text is the HTML to be parsed, want is a dump of the correct parse tree,
+// and context is the name of the context node, if any.
+func testParseCase(text, want, context string) (err error) {
+ defer func() {
+ if x := recover(); x != nil {
+ switch e := x.(type) {
+ case error:
+ err = e
+ default:
+ err = fmt.Errorf("%v", e)
+ }
+ }
+ }()
+
+ var doc *Node
+ if context == "" {
+ doc, err = Parse(strings.NewReader(text))
+ if err != nil {
+ return err
+ }
+ } else {
+ contextNode := &Node{
+ Type: ElementNode,
+ DataAtom: atom.Lookup([]byte(context)),
+ Data: context,
+ }
+ nodes, err := ParseFragment(strings.NewReader(text), contextNode)
+ if err != nil {
+ return err
+ }
+ doc = &Node{
+ Type: DocumentNode,
+ }
+ for _, n := range nodes {
+ doc.AppendChild(n)
+ }
+ }
+
+ if err := checkTreeConsistency(doc); err != nil {
+ return err
+ }
+
+ got, err := dump(doc)
+ if err != nil {
+ return err
+ }
+ // Compare the parsed tree to the #document section.
+ if got != want {
+ return fmt.Errorf("got vs want:\n----\n%s----\n%s----", got, want)
+ }
+
+ if renderTestBlacklist[text] || context != "" {
+ return nil
+ }
+
+ // Check that rendering and re-parsing results in an identical tree.
+ pr, pw := io.Pipe()
+ go func() {
+ pw.CloseWithError(Render(pw, doc))
+ }()
+ doc1, err := Parse(pr)
+ if err != nil {
+ return err
+ }
+ got1, err := dump(doc1)
+ if err != nil {
+ return err
+ }
+ if got != got1 {
+ return fmt.Errorf("got vs got1:\n----\n%s----\n%s----", got, got1)
+ }
+
+ return nil
+}
+
+// Some test input result in parse trees are not 'well-formed' despite
+// following the HTML5 recovery algorithms. Rendering and re-parsing such a
+// tree will not result in an exact clone of that tree. We blacklist such
+// inputs from the render test.
+var renderTestBlacklist = map[string]bool{
+ // The second <a> will be reparented to the first <table>'s parent. This
+ // results in an <a> whose parent is an <a>, which is not 'well-formed'.
+ `<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y`: true,
+ // The same thing with a <p>:
+ `<p><table></p>`: true,
+ // More cases of <a> being reparented:
+ `<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true,
+ `<a><table><a></table><p><a><div><a>`: true,
+ `<a><table><td><a><table></table><a></tr><a></table><a>`: true,
+ // A similar reparenting situation involving <nobr>:
+ `<!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3`: true,
+ // A <plaintext> element is reparented, putting it before a table.
+ // A <plaintext> element can't have anything after it in HTML.
+ `<table><plaintext><td>`: true,
+ `<!doctype html><table><plaintext></plaintext>`: true,
+ `<!doctype html><table><tbody><plaintext></plaintext>`: true,
+ `<!doctype html><table><tbody><tr><plaintext></plaintext>`: true,
+ // A form inside a table inside a form doesn't work either.
+ `<!doctype html><form><table></form><form></table></form>`: true,
+ // A script that ends at EOF may escape its own closing tag when rendered.
+ `<!doctype html><script><!--<script `: true,
+ `<!doctype html><script><!--<script <`: true,
+ `<!doctype html><script><!--<script <a`: true,
+ `<!doctype html><script><!--<script </`: true,
+ `<!doctype html><script><!--<script </s`: true,
+ `<!doctype html><script><!--<script </script`: true,
+ `<!doctype html><script><!--<script </scripta`: true,
+ `<!doctype html><script><!--<script -`: true,
+ `<!doctype html><script><!--<script -a`: true,
+ `<!doctype html><script><!--<script -<`: true,
+ `<!doctype html><script><!--<script --`: true,
+ `<!doctype html><script><!--<script --a`: true,
+ `<!doctype html><script><!--<script --<`: true,
+ `<script><!--<script `: true,
+ `<script><!--<script <a`: true,
+ `<script><!--<script </script`: true,
+ `<script><!--<script </scripta`: true,
+ `<script><!--<script -`: true,
+ `<script><!--<script -a`: true,
+ `<script><!--<script --`: true,
+ `<script><!--<script --a`: true,
+ `<script><!--<script <`: true,
+ `<script><!--<script </`: true,
+ `<script><!--<script </s`: true,
+ // Reconstructing the active formatting elements results in a <plaintext>
+ // element that contains an <a> element.
+ `<!doctype html><p><a><plaintext>b`: true,
+}
+
+func TestNodeConsistency(t *testing.T) {
+ // inconsistentNode is a Node whose DataAtom and Data do not agree.
+ inconsistentNode := &Node{
+ Type: ElementNode,
+ DataAtom: atom.Frameset,
+ Data: "table",
+ }
+ _, err := ParseFragment(strings.NewReader("<p>hello</p>"), inconsistentNode)
+ if err == nil {
+ t.Errorf("got nil error, want non-nil")
+ }
+}
+
+func BenchmarkParser(b *testing.B) {
+ buf, err := ioutil.ReadFile("testdata/go1.html")
+ if err != nil {
+ b.Fatalf("could not read testdata/go1.html: %v", err)
+ }
+ b.SetBytes(int64(len(buf)))
+ runtime.GC()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse(bytes.NewBuffer(buf))
+ }
+}
diff --git a/html/render.go b/html/render.go
new file mode 100644
index 0000000..d34564f
--- /dev/null
+++ b/html/render.go
@@ -0,0 +1,271 @@
+// Copyright 2011 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.
+
+package html
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+type writer interface {
+ io.Writer
+ io.ByteWriter
+ WriteString(string) (int, error)
+}
+
+// Render renders the parse tree n to the given writer.
+//
+// Rendering is done on a 'best effort' basis: calling Parse on the output of
+// Render will always result in something similar to the original tree, but it
+// is not necessarily an exact clone unless the original tree was 'well-formed'.
+// 'Well-formed' is not easily specified; the HTML5 specification is
+// complicated.
+//
+// Calling Parse on arbitrary input typically results in a 'well-formed' parse
+// tree. However, it is possible for Parse to yield a 'badly-formed' parse tree.
+// For example, in a 'well-formed' parse tree, no <a> element is a child of
+// another <a> element: parsing "<a><a>" results in two sibling elements.
+// Similarly, in a 'well-formed' parse tree, no <a> element is a child of a
+// <table> element: parsing "<p><table><a>" results in a <p> with two sibling
+// children; the <a> is reparented to the <table>'s parent. However, calling
+// Parse on "<a><table><a>" does not return an error, but the result has an <a>
+// element with an <a> child, and is therefore not 'well-formed'.
+//
+// Programmatically constructed trees are typically also 'well-formed', but it
+// is possible to construct a tree that looks innocuous but, when rendered and
+// re-parsed, results in a different tree. A simple example is that a solitary
+// text node would become a tree containing <html>, <head> and <body> elements.
+// Another example is that the programmatic equivalent of "a<head>b</head>c"
+// becomes "<html><head><head/><body>abc</body></html>".
+func Render(w io.Writer, n *Node) error {
+ if x, ok := w.(writer); ok {
+ return render(x, n)
+ }
+ buf := bufio.NewWriter(w)
+ if err := render(buf, n); err != nil {
+ return err
+ }
+ return buf.Flush()
+}
+
+// plaintextAbort is returned from render1 when a <plaintext> element
+// has been rendered. No more end tags should be rendered after that.
+var plaintextAbort = errors.New("html: internal error (plaintext abort)")
+
+func render(w writer, n *Node) error {
+ err := render1(w, n)
+ if err == plaintextAbort {
+ err = nil
+ }
+ return err
+}
+
+func render1(w writer, n *Node) error {
+ // Render non-element nodes; these are the easy cases.
+ switch n.Type {
+ case ErrorNode:
+ return errors.New("html: cannot render an ErrorNode node")
+ case TextNode:
+ return escape(w, n.Data)
+ case DocumentNode:
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if err := render1(w, c); err != nil {
+ return err
+ }
+ }
+ return nil
+ case ElementNode:
+ // No-op.
+ case CommentNode:
+ if _, err := w.WriteString("<!--"); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(n.Data); err != nil {
+ return err
+ }
+ if _, err := w.WriteString("-->"); err != nil {
+ return err
+ }
+ return nil
+ case DoctypeNode:
+ if _, err := w.WriteString("<!DOCTYPE "); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(n.Data); err != nil {
+ return err
+ }
+ if n.Attr != nil {
+ var p, s string
+ for _, a := range n.Attr {
+ switch a.Key {
+ case "public":
+ p = a.Val
+ case "system":
+ s = a.Val
+ }
+ }
+ if p != "" {
+ if _, err := w.WriteString(" PUBLIC "); err != nil {
+ return err
+ }
+ if err := writeQuoted(w, p); err != nil {
+ return err
+ }
+ if s != "" {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ if err := writeQuoted(w, s); err != nil {
+ return err
+ }
+ }
+ } else if s != "" {
+ if _, err := w.WriteString(" SYSTEM "); err != nil {
+ return err
+ }
+ if err := writeQuoted(w, s); err != nil {
+ return err
+ }
+ }
+ }
+ return w.WriteByte('>')
+ default:
+ return errors.New("html: unknown node type")
+ }
+
+ // Render the <xxx> opening tag.
+ if err := w.WriteByte('<'); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(n.Data); err != nil {
+ return err
+ }
+ for _, a := range n.Attr {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ if a.Namespace != "" {
+ if _, err := w.WriteString(a.Namespace); err != nil {
+ return err
+ }
+ if err := w.WriteByte(':'); err != nil {
+ return err
+ }
+ }
+ if _, err := w.WriteString(a.Key); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(`="`); err != nil {
+ return err
+ }
+ if err := escape(w, a.Val); err != nil {
+ return err
+ }
+ if err := w.WriteByte('"'); err != nil {
+ return err
+ }
+ }
+ if voidElements[n.Data] {
+ if n.FirstChild != nil {
+ return fmt.Errorf("html: void element <%s> has child nodes", n.Data)
+ }
+ _, err := w.WriteString("/>")
+ return err
+ }
+ if err := w.WriteByte('>'); err != nil {
+ return err
+ }
+
+ // Add initial newline where there is danger of a newline beging ignored.
+ if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") {
+ switch n.Data {
+ case "pre", "listing", "textarea":
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Render any child nodes.
+ switch n.Data {
+ case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if c.Type == TextNode {
+ if _, err := w.WriteString(c.Data); err != nil {
+ return err
+ }
+ } else {
+ if err := render1(w, c); err != nil {
+ return err
+ }
+ }
+ }
+ if n.Data == "plaintext" {
+ // Don't render anything else. <plaintext> must be the
+ // last element in the file, with no closing tag.
+ return plaintextAbort
+ }
+ default:
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ if err := render1(w, c); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Render the </xxx> closing tag.
+ if _, err := w.WriteString("</"); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(n.Data); err != nil {
+ return err
+ }
+ return w.WriteByte('>')
+}
+
+// writeQuoted writes s to w surrounded by quotes. Normally it will use double
+// quotes, but if s contains a double quote, it will use single quotes.
+// It is used for writing the identifiers in a doctype declaration.
+// In valid HTML, they can't contain both types of quotes.
+func writeQuoted(w writer, s string) error {
+ var q byte = '"'
+ if strings.Contains(s, `"`) {
+ q = '\''
+ }
+ if err := w.WriteByte(q); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(s); err != nil {
+ return err
+ }
+ if err := w.WriteByte(q); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Section 12.1.2, "Elements", gives this list of void elements. Void elements
+// are those that can't have any contents.
+var voidElements = map[string]bool{
+ "area": true,
+ "base": true,
+ "br": true,
+ "col": true,
+ "command": true,
+ "embed": true,
+ "hr": true,
+ "img": true,
+ "input": true,
+ "keygen": true,
+ "link": true,
+ "meta": true,
+ "param": true,
+ "source": true,
+ "track": true,
+ "wbr": true,
+}
diff --git a/html/render_test.go b/html/render_test.go
new file mode 100644
index 0000000..11da54b
--- /dev/null
+++ b/html/render_test.go
@@ -0,0 +1,156 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestRenderer(t *testing.T) {
+ nodes := [...]*Node{
+ 0: {
+ Type: ElementNode,
+ Data: "html",
+ },
+ 1: {
+ Type: ElementNode,
+ Data: "head",
+ },
+ 2: {
+ Type: ElementNode,
+ Data: "body",
+ },
+ 3: {
+ Type: TextNode,
+ Data: "0<1",
+ },
+ 4: {
+ Type: ElementNode,
+ Data: "p",
+ Attr: []Attribute{
+ {
+ Key: "id",
+ Val: "A",
+ },
+ {
+ Key: "foo",
+ Val: `abc"def`,
+ },
+ },
+ },
+ 5: {
+ Type: TextNode,
+ Data: "2",
+ },
+ 6: {
+ Type: ElementNode,
+ Data: "b",
+ Attr: []Attribute{
+ {
+ Key: "empty",
+ Val: "",
+ },
+ },
+ },
+ 7: {
+ Type: TextNode,
+ Data: "3",
+ },
+ 8: {
+ Type: ElementNode,
+ Data: "i",
+ Attr: []Attribute{
+ {
+ Key: "backslash",
+ Val: `\`,
+ },
+ },
+ },
+ 9: {
+ Type: TextNode,
+ Data: "&4",
+ },
+ 10: {
+ Type: TextNode,
+ Data: "5",
+ },
+ 11: {
+ Type: ElementNode,
+ Data: "blockquote",
+ },
+ 12: {
+ Type: ElementNode,
+ Data: "br",
+ },
+ 13: {
+ Type: TextNode,
+ Data: "6",
+ },
+ }
+
+ // Build a tree out of those nodes, based on a textual representation.
+ // Only the ".\t"s are significant. The trailing HTML-like text is
+ // just commentary. The "0:" prefixes are for easy cross-reference with
+ // the nodes array.
+ treeAsText := [...]string{
+ 0: `<html>`,
+ 1: `. <head>`,
+ 2: `. <body>`,
+ 3: `. . "0<1"`,
+ 4: `. . <p id="A" foo="abc"def">`,
+ 5: `. . . "2"`,
+ 6: `. . . <b empty="">`,
+ 7: `. . . . "3"`,
+ 8: `. . . <i backslash="\">`,
+ 9: `. . . . "&4"`,
+ 10: `. . "5"`,
+ 11: `. . <blockquote>`,
+ 12: `. . <br>`,
+ 13: `. . "6"`,
+ }
+ if len(nodes) != len(treeAsText) {
+ t.Fatal("len(nodes) != len(treeAsText)")
+ }
+ var stack [8]*Node
+ for i, line := range treeAsText {
+ level := 0
+ for line[0] == '.' {
+ // Strip a leading ".\t".
+ line = line[2:]
+ level++
+ }
+ n := nodes[i]
+ if level == 0 {
+ if stack[0] != nil {
+ t.Fatal("multiple root nodes")
+ }
+ stack[0] = n
+ } else {
+ stack[level-1].AppendChild(n)
+ stack[level] = n
+ for i := level + 1; i < len(stack); i++ {
+ stack[i] = nil
+ }
+ }
+ // At each stage of tree construction, we check all nodes for consistency.
+ for j, m := range nodes {
+ if err := checkNodeConsistency(m); err != nil {
+ t.Fatalf("i=%d, j=%d: %v", i, j, err)
+ }
+ }
+ }
+
+ want := `<html><head></head><body>0<1<p id="A" foo="abc"def">` +
+ `2<b empty="">3</b><i backslash="\">&4</i></p>` +
+ `5<blockquote></blockquote><br/>6</body></html>`
+ b := new(bytes.Buffer)
+ if err := Render(b, nodes[0]); err != nil {
+ t.Fatal(err)
+ }
+ if got := b.String(); got != want {
+ t.Errorf("got vs want:\n%s\n%s\n", got, want)
+ }
+}
diff --git a/html/testdata/go1.html b/html/testdata/go1.html
new file mode 100644
index 0000000..a782cc7
--- /dev/null
+++ b/html/testdata/go1.html
@@ -0,0 +1,2237 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+ <title>Go 1 Release Notes - The Go Programming Language</title>
+
+<link type="text/css" rel="stylesheet" href="/doc/style.css">
+<script type="text/javascript" src="/doc/godocs.js"></script>
+
+<link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" />
+
+<script type="text/javascript">
+var _gaq = _gaq || [];
+_gaq.push(["_setAccount", "UA-11222381-2"]);
+_gaq.push(["_trackPageview"]);
+</script>
+</head>
+<body>
+
+<div id="topbar"><div class="container wide">
+
+<form method="GET" action="/search">
+<div id="menu">
+<a href="/doc/">Documents</a>
+<a href="/ref/">References</a>
+<a href="/pkg/">Packages</a>
+<a href="/project/">The Project</a>
+<a href="/help/">Help</a>
+<input type="text" id="search" name="q" class="inactive" value="Search">
+</div>
+<div id="heading"><a href="/">The Go Programming Language</a></div>
+</form>
+
+</div></div>
+
+<div id="page" class="wide">
+
+
+ <div id="plusone"><g:plusone size="small" annotation="none"></g:plusone></div>
+ <h1>Go 1 Release Notes</h1>
+
+
+
+
+<div id="nav"></div>
+
+
+
+
+<h2 id="introduction">Introduction to Go 1</h2>
+
+<p>
+Go version 1, Go 1 for short, defines a language and a set of core libraries
+that provide a stable foundation for creating reliable products, projects, and
+publications.
+</p>
+
+<p>
+The driving motivation for Go 1 is stability for its users. People should be able to
+write Go programs and expect that they will continue to compile and run without
+change, on a time scale of years, including in production environments such as
+Google App Engine. Similarly, people should be able to write books about Go, be
+able to say which version of Go the book is describing, and have that version
+number still be meaningful much later.
+</p>
+
+<p>
+Code that compiles in Go 1 should, with few exceptions, continue to compile and
+run throughout the lifetime of that version, even as we issue updates and bug
+fixes such as Go version 1.1, 1.2, and so on. Other than critical fixes, changes
+made to the language and library for subsequent releases of Go 1 may
+add functionality but will not break existing Go 1 programs.
+<a href="go1compat.html">The Go 1 compatibility document</a>
+explains the compatibility guidelines in more detail.
+</p>
+
+<p>
+Go 1 is a representation of Go as it used today, not a wholesale rethinking of
+the language. We avoided designing new features and instead focused on cleaning
+up problems and inconsistencies and improving portability. There are a number
+changes to the Go language and packages that we had considered for some time and
+prototyped but not released primarily because they are significant and
+backwards-incompatible. Go 1 was an opportunity to get them out, which is
+helpful for the long term, but also means that Go 1 introduces incompatibilities
+for old programs. Fortunately, the <code>go</code> <code>fix</code> tool can
+automate much of the work needed to bring programs up to the Go 1 standard.
+</p>
+
+<p>
+This document outlines the major changes in Go 1 that will affect programmers
+updating existing code; its reference point is the prior release, r60 (tagged as
+r60.3). It also explains how to update code from r60 to run under Go 1.
+</p>
+
+<h2 id="language">Changes to the language</h2>
+
+<h3 id="append">Append</h3>
+
+<p>
+The <code>append</code> predeclared variadic function makes it easy to grow a slice
+by adding elements to the end.
+A common use is to add bytes to the end of a byte slice when generating output.
+However, <code>append</code> did not provide a way to append a string to a <code>[]byte</code>,
+which is another common case.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/greeting := ..byte/` `/append.*hello/`}}
+--> greeting := []byte{}
+ greeting = append(greeting, []byte("hello ")...)</pre>
+
+<p>
+By analogy with the similar property of <code>copy</code>, Go 1
+permits a string to be appended (byte-wise) directly to a byte
+slice, reducing the friction between strings and byte slices.
+The conversion is no longer necessary:
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/append.*world/`}}
+--> greeting = append(greeting, "world"...)</pre>
+
+<p>
+<em>Updating</em>:
+This is a new feature, so existing code needs no changes.
+</p>
+
+<h3 id="close">Close</h3>
+
+<p>
+The <code>close</code> predeclared function provides a mechanism
+for a sender to signal that no more values will be sent.
+It is important to the implementation of <code>for</code> <code>range</code>
+loops over channels and is helpful in other situations.
+Partly by design and partly because of race conditions that can occur otherwise,
+it is intended for use only by the goroutine sending on the channel,
+not by the goroutine receiving data.
+However, before Go 1 there was no compile-time checking that <code>close</code>
+was being used correctly.
+</p>
+
+<p>
+To close this gap, at least in part, Go 1 disallows <code>close</code> on receive-only channels.
+Attempting to close such a channel is a compile-time error.
+</p>
+
+<pre>
+ var c chan int
+ var csend chan<- int = c
+ var crecv <-chan int = c
+ close(c) // legal
+ close(csend) // legal
+ close(crecv) // illegal
+</pre>
+
+<p>
+<em>Updating</em>:
+Existing code that attempts to close a receive-only channel was
+erroneous even before Go 1 and should be fixed. The compiler will
+now reject such code.
+</p>
+
+<h3 id="literals">Composite literals</h3>
+
+<p>
+In Go 1, a composite literal of array, slice, or map type can elide the
+type specification for the elements' initializers if they are of pointer type.
+All four of the initializations in this example are legal; the last one was illegal before Go 1.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/type Date struct/` `/STOP/`}}
+--> type Date struct {
+ month string
+ day int
+ }
+ <span class="comment">// Struct values, fully qualified; always legal.</span>
+ holiday1 := []Date{
+ Date{"Feb", 14},
+ Date{"Nov", 11},
+ Date{"Dec", 25},
+ }
+ <span class="comment">// Struct values, type name elided; always legal.</span>
+ holiday2 := []Date{
+ {"Feb", 14},
+ {"Nov", 11},
+ {"Dec", 25},
+ }
+ <span class="comment">// Pointers, fully qualified, always legal.</span>
+ holiday3 := []*Date{
+ &Date{"Feb", 14},
+ &Date{"Nov", 11},
+ &Date{"Dec", 25},
+ }
+ <span class="comment">// Pointers, type name elided; legal in Go 1.</span>
+ holiday4 := []*Date{
+ {"Feb", 14},
+ {"Nov", 11},
+ {"Dec", 25},
+ }</pre>
+
+<p>
+<em>Updating</em>:
+This change has no effect on existing code, but the command
+<code>gofmt</code> <code>-s</code> applied to existing source
+will, among other things, elide explicit element types wherever permitted.
+</p>
+
+
+<h3 id="init">Goroutines during init</h3>
+
+<p>
+The old language defined that <code>go</code> statements executed during initialization created goroutines but that they did not begin to run until initialization of the entire program was complete.
+This introduced clumsiness in many places and, in effect, limited the utility
+of the <code>init</code> construct:
+if it was possible for another package to use the library during initialization, the library
+was forced to avoid goroutines.
+This design was done for reasons of simplicity and safety but,
+as our confidence in the language grew, it seemed unnecessary.
+Running goroutines during initialization is no more complex or unsafe than running them during normal execution.
+</p>
+
+<p>
+In Go 1, code that uses goroutines can be called from
+<code>init</code> routines and global initialization expressions
+without introducing a deadlock.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/PackageGlobal/` `/^}/`}}
+-->var PackageGlobal int
+
+func init() {
+ c := make(chan int)
+ go initializationFunction(c)
+ PackageGlobal = <-c
+}</pre>
+
+<p>
+<em>Updating</em>:
+This is a new feature, so existing code needs no changes,
+although it's possible that code that depends on goroutines not starting before <code>main</code> will break.
+There was no such code in the standard repository.
+</p>
+
+<h3 id="rune">The rune type</h3>
+
+<p>
+The language spec allows the <code>int</code> type to be 32 or 64 bits wide, but current implementations set <code>int</code> to 32 bits even on 64-bit platforms.
+It would be preferable to have <code>int</code> be 64 bits on 64-bit platforms.
+(There are important consequences for indexing large slices.)
+However, this change would waste space when processing Unicode characters with
+the old language because the <code>int</code> type was also used to hold Unicode code points: each code point would waste an extra 32 bits of storage if <code>int</code> grew from 32 bits to 64.
+</p>
+
+<p>
+To make changing to 64-bit <code>int</code> feasible,
+Go 1 introduces a new basic type, <code>rune</code>, to represent
+individual Unicode code points.
+It is an alias for <code>int32</code>, analogous to <code>byte</code>
+as an alias for <code>uint8</code>.
+</p>
+
+<p>
+Character literals such as <code>'a'</code>, <code>'語'</code>, and <code>'\u0345'</code>
+now have default type <code>rune</code>,
+analogous to <code>1.0</code> having default type <code>float64</code>.
+A variable initialized to a character constant will therefore
+have type <code>rune</code> unless otherwise specified.
+</p>
+
+<p>
+Libraries have been updated to use <code>rune</code> rather than <code>int</code>
+when appropriate. For instance, the functions <code>unicode.ToLower</code> and
+relatives now take and return a <code>rune</code>.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/STARTRUNE/` `/ENDRUNE/`}}
+--> delta := 'δ' <span class="comment">// delta has type rune.</span>
+ var DELTA rune
+ DELTA = unicode.ToUpper(delta)
+ epsilon := unicode.ToLower(DELTA + 1)
+ if epsilon != 'δ'+1 {
+ log.Fatal("inconsistent casing for Greek")
+ }</pre>
+
+<p>
+<em>Updating</em>:
+Most source code will be unaffected by this because the type inference from
+<code>:=</code> initializers introduces the new type silently, and it propagates
+from there.
+Some code may get type errors that a trivial conversion will resolve.
+</p>
+
+<h3 id="error">The error type</h3>
+
+<p>
+Go 1 introduces a new built-in type, <code>error</code>, which has the following definition:
+</p>
+
+<pre>
+ type error interface {
+ Error() string
+ }
+</pre>
+
+<p>
+Since the consequences of this type are all in the package library,
+it is discussed <a href="#errors">below</a>.
+</p>
+
+<h3 id="delete">Deleting from maps</h3>
+
+<p>
+In the old language, to delete the entry with key <code>k</code> from map <code>m</code>, one wrote the statement,
+</p>
+
+<pre>
+ m[k] = value, false
+</pre>
+
+<p>
+This syntax was a peculiar special case, the only two-to-one assignment.
+It required passing a value (usually ignored) that is evaluated but discarded,
+plus a boolean that was nearly always the constant <code>false</code>.
+It did the job but was odd and a point of contention.
+</p>
+
+<p>
+In Go 1, that syntax has gone; instead there is a new built-in
+function, <code>delete</code>. The call
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/delete\(m, k\)/`}}
+--> delete(m, k)</pre>
+
+<p>
+will delete the map entry retrieved by the expression <code>m[k]</code>.
+There is no return value. Deleting a non-existent entry is a no-op.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will convert expressions of the form <code>m[k] = value,
+false</code> into <code>delete(m, k)</code> when it is clear that
+the ignored value can be safely discarded from the program and
+<code>false</code> refers to the predefined boolean constant.
+The fix tool
+will flag other uses of the syntax for inspection by the programmer.
+</p>
+
+<h3 id="iteration">Iterating in maps</h3>
+
+<p>
+The old language specification did not define the order of iteration for maps,
+and in practice it differed across hardware platforms.
+This caused tests that iterated over maps to be fragile and non-portable, with the
+unpleasant property that a test might always pass on one machine but break on another.
+</p>
+
+<p>
+In Go 1, the order in which elements are visited when iterating
+over a map using a <code>for</code> <code>range</code> statement
+is defined to be unpredictable, even if the same loop is run multiple
+times with the same map.
+Code should not assume that the elements are visited in any particular order.
+</p>
+
+<p>
+This change means that code that depends on iteration order is very likely to break early and be fixed long before it becomes a problem.
+Just as important, it allows the map implementation to ensure better map balancing even when programs are using range loops to select an element from a map.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/Sunday/` `/^ }/`}}
+--> m := map[string]int{"Sunday": 0, "Monday": 1}
+ for name, value := range m {
+ <span class="comment">// This loop should not assume Sunday will be visited first.</span>
+ f(name, value)
+ }</pre>
+
+<p>
+<em>Updating</em>:
+This is one change where tools cannot help. Most existing code
+will be unaffected, but some programs may break or misbehave; we
+recommend manual checking of all range statements over maps to
+verify they do not depend on iteration order. There were a few such
+examples in the standard repository; they have been fixed.
+Note that it was already incorrect to depend on the iteration order, which
+was unspecified. This change codifies the unpredictability.
+</p>
+
+<h3 id="multiple_assignment">Multiple assignment</h3>
+
+<p>
+The language specification has long guaranteed that in assignments
+the right-hand-side expressions are all evaluated before any left-hand-side expressions are assigned.
+To guarantee predictable behavior,
+Go 1 refines the specification further.
+</p>
+
+<p>
+If the left-hand side of the assignment
+statement contains expressions that require evaluation, such as
+function calls or array indexing operations, these will all be done
+using the usual left-to-right rule before any variables are assigned
+their value. Once everything is evaluated, the actual assignments
+proceed in left-to-right order.
+</p>
+
+<p>
+These examples illustrate the behavior.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/sa :=/` `/then sc.0. = 2/`}}
+--> sa := []int{1, 2, 3}
+ i := 0
+ i, sa[i] = 1, 2 <span class="comment">// sets i = 1, sa[0] = 2</span>
+
+ sb := []int{1, 2, 3}
+ j := 0
+ sb[j], j = 2, 1 <span class="comment">// sets sb[0] = 2, j = 1</span>
+
+ sc := []int{1, 2, 3}
+ sc[0], sc[0] = 1, 2 <span class="comment">// sets sc[0] = 1, then sc[0] = 2 (so sc[0] = 2 at end)</span></pre>
+
+<p>
+<em>Updating</em>:
+This is one change where tools cannot help, but breakage is unlikely.
+No code in the standard repository was broken by this change, and code
+that depended on the previous unspecified behavior was already incorrect.
+</p>
+
+<h3 id="shadowing">Returns and shadowed variables</h3>
+
+<p>
+A common mistake is to use <code>return</code> (without arguments) after an assignment to a variable that has the same name as a result variable but is not the same variable.
+This situation is called <em>shadowing</em>: the result variable has been shadowed by another variable with the same name declared in an inner scope.
+</p>
+
+<p>
+In functions with named return values,
+the Go 1 compilers disallow return statements without arguments if any of the named return values is shadowed at the point of the return statement.
+(It isn't part of the specification, because this is one area we are still exploring;
+the situation is analogous to the compilers rejecting functions that do not end with an explicit return statement.)
+</p>
+
+<p>
+This function implicitly returns a shadowed return value and will be rejected by the compiler:
+</p>
+
+<pre>
+ func Bug() (i, j, k int) {
+ for i = 0; i < 5; i++ {
+ for j := 0; j < 5; j++ { // Redeclares j.
+ k += i*j
+ if k > 100 {
+ return // Rejected: j is shadowed here.
+ }
+ }
+ }
+ return // OK: j is not shadowed here.
+ }
+</pre>
+
+<p>
+<em>Updating</em>:
+Code that shadows return values in this way will be rejected by the compiler and will need to be fixed by hand.
+The few cases that arose in the standard repository were mostly bugs.
+</p>
+
+<h3 id="unexported">Copying structs with unexported fields</h3>
+
+<p>
+The old language did not allow a package to make a copy of a struct value containing unexported fields belonging to a different package.
+There was, however, a required exception for a method receiver;
+also, the implementations of <code>copy</code> and <code>append</code> have never honored the restriction.
+</p>
+
+<p>
+Go 1 will allow packages to copy struct values containing unexported fields from other packages.
+Besides resolving the inconsistency,
+this change admits a new kind of API: a package can return an opaque value without resorting to a pointer or interface.
+The new implementations of <code>time.Time</code> and
+<code>reflect.Value</code> are examples of types taking advantage of this new property.
+</p>
+
+<p>
+As an example, if package <code>p</code> includes the definitions,
+</p>
+
+<pre>
+ type Struct struct {
+ Public int
+ secret int
+ }
+ func NewStruct(a int) Struct { // Note: not a pointer.
+ return Struct{a, f(a)}
+ }
+ func (s Struct) String() string {
+ return fmt.Sprintf("{%d (secret %d)}", s.Public, s.secret)
+ }
+</pre>
+
+<p>
+a package that imports <code>p</code> can assign and copy values of type
+<code>p.Struct</code> at will.
+Behind the scenes the unexported fields will be assigned and copied just
+as if they were exported,
+but the client code will never be aware of them. The code
+</p>
+
+<pre>
+ import "p"
+
+ myStruct := p.NewStruct(23)
+ copyOfMyStruct := myStruct
+ fmt.Println(myStruct, copyOfMyStruct)
+</pre>
+
+<p>
+will show that the secret field of the struct has been copied to the new value.
+</p>
+
+<p>
+<em>Updating</em>:
+This is a new feature, so existing code needs no changes.
+</p>
+
+<h3 id="equality">Equality</h3>
+
+<p>
+Before Go 1, the language did not define equality on struct and array values.
+This meant,
+among other things, that structs and arrays could not be used as map keys.
+On the other hand, Go did define equality on function and map values.
+Function equality was problematic in the presence of closures
+(when are two closures equal?)
+while map equality compared pointers, not the maps' content, which was usually
+not what the user would want.
+</p>
+
+<p>
+Go 1 addressed these issues.
+First, structs and arrays can be compared for equality and inequality
+(<code>==</code> and <code>!=</code>),
+and therefore be used as map keys,
+provided they are composed from elements for which equality is also defined,
+using element-wise comparison.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/type Day struct/` `/Printf/`}}
+--> type Day struct {
+ long string
+ short string
+ }
+ Christmas := Day{"Christmas", "XMas"}
+ Thanksgiving := Day{"Thanksgiving", "Turkey"}
+ holiday := map[Day]bool{
+ Christmas: true,
+ Thanksgiving: true,
+ }
+ fmt.Printf("Christmas is a holiday: %t\n", holiday[Christmas])</pre>
+
+<p>
+Second, Go 1 removes the definition of equality for function values,
+except for comparison with <code>nil</code>.
+Finally, map equality is gone too, also except for comparison with <code>nil</code>.
+</p>
+
+<p>
+Note that equality is still undefined for slices, for which the
+calculation is in general infeasible. Also note that the ordered
+comparison operators (<code><</code> <code><=</code>
+<code>></code> <code>>=</code>) are still undefined for
+structs and arrays.
+
+<p>
+<em>Updating</em>:
+Struct and array equality is a new feature, so existing code needs no changes.
+Existing code that depends on function or map equality will be
+rejected by the compiler and will need to be fixed by hand.
+Few programs will be affected, but the fix may require some
+redesign.
+</p>
+
+<h2 id="packages">The package hierarchy</h2>
+
+<p>
+Go 1 addresses many deficiencies in the old standard library and
+cleans up a number of packages, making them more internally consistent
+and portable.
+</p>
+
+<p>
+This section describes how the packages have been rearranged in Go 1.
+Some have moved, some have been renamed, some have been deleted.
+New packages are described in later sections.
+</p>
+
+<h3 id="hierarchy">The package hierarchy</h3>
+
+<p>
+Go 1 has a rearranged package hierarchy that groups related items
+into subdirectories. For instance, <code>utf8</code> and
+<code>utf16</code> now occupy subdirectories of <code>unicode</code>.
+Also, <a href="#subrepo">some packages</a> have moved into
+subrepositories of
+<a href="http://code.google.com/p/go"><code>code.google.com/p/go</code></a>
+while <a href="#deleted">others</a> have been deleted outright.
+</p>
+
+<table class="codetable" frame="border" summary="Moved packages">
+<colgroup align="left" width="60%"></colgroup>
+<colgroup align="left" width="40%"></colgroup>
+<tr>
+<th align="left">Old path</th>
+<th align="left">New path</th>
+</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>asn1</td> <td>encoding/asn1</td></tr>
+<tr><td>csv</td> <td>encoding/csv</td></tr>
+<tr><td>gob</td> <td>encoding/gob</td></tr>
+<tr><td>json</td> <td>encoding/json</td></tr>
+<tr><td>xml</td> <td>encoding/xml</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>exp/template/html</td> <td>html/template</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>big</td> <td>math/big</td></tr>
+<tr><td>cmath</td> <td>math/cmplx</td></tr>
+<tr><td>rand</td> <td>math/rand</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>http</td> <td>net/http</td></tr>
+<tr><td>http/cgi</td> <td>net/http/cgi</td></tr>
+<tr><td>http/fcgi</td> <td>net/http/fcgi</td></tr>
+<tr><td>http/httptest</td> <td>net/http/httptest</td></tr>
+<tr><td>http/pprof</td> <td>net/http/pprof</td></tr>
+<tr><td>mail</td> <td>net/mail</td></tr>
+<tr><td>rpc</td> <td>net/rpc</td></tr>
+<tr><td>rpc/jsonrpc</td> <td>net/rpc/jsonrpc</td></tr>
+<tr><td>smtp</td> <td>net/smtp</td></tr>
+<tr><td>url</td> <td>net/url</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>exec</td> <td>os/exec</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>scanner</td> <td>text/scanner</td></tr>
+<tr><td>tabwriter</td> <td>text/tabwriter</td></tr>
+<tr><td>template</td> <td>text/template</td></tr>
+<tr><td>template/parse</td> <td>text/template/parse</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>utf8</td> <td>unicode/utf8</td></tr>
+<tr><td>utf16</td> <td>unicode/utf16</td></tr>
+</table>
+
+<p>
+Note that the package names for the old <code>cmath</code> and
+<code>exp/template/html</code> packages have changed to <code>cmplx</code>
+and <code>template</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update all imports and package renames for packages that
+remain inside the standard repository. Programs that import packages
+that are no longer in the standard repository will need to be edited
+by hand.
+</p>
+
+<h3 id="exp">The package tree exp</h3>
+
+<p>
+Because they are not standardized, the packages under the <code>exp</code> directory will not be available in the
+standard Go 1 release distributions, although they will be available in source code form
+in <a href="http://code.google.com/p/go/">the repository</a> for
+developers who wish to use them.
+</p>
+
+<p>
+Several packages have moved under <code>exp</code> at the time of Go 1's release:
+</p>
+
+<ul>
+<li><code>ebnf</code></li>
+<li><code>html</code><sup>†</sup></li>
+<li><code>go/types</code></li>
+</ul>
+
+<p>
+(<sup>†</sup>The <code>EscapeString</code> and <code>UnescapeString</code> types remain
+in package <code>html</code>.)
+</p>
+
+<p>
+All these packages are available under the same names, with the prefix <code>exp/</code>: <code>exp/ebnf</code> etc.
+</p>
+
+<p>
+Also, the <code>utf8.String</code> type has been moved to its own package, <code>exp/utf8string</code>.
+</p>
+
+<p>
+Finally, the <code>gotype</code> command now resides in <code>exp/gotype</code>, while
+<code>ebnflint</code> is now in <code>exp/ebnflint</code>.
+If they are installed, they now reside in <code>$GOROOT/bin/tool</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses packages in <code>exp</code> will need to be updated by hand,
+or else compiled from an installation that has <code>exp</code> available.
+The <code>go</code> <code>fix</code> tool or the compiler will complain about such uses.
+</p>
+
+<h3 id="old">The package tree old</h3>
+
+<p>
+Because they are deprecated, the packages under the <code>old</code> directory will not be available in the
+standard Go 1 release distributions, although they will be available in source code form for
+developers who wish to use them.
+</p>
+
+<p>
+The packages in their new locations are:
+</p>
+
+<ul>
+<li><code>old/netchan</code></li>
+<li><code>old/regexp</code></li>
+<li><code>old/template</code></li>
+</ul>
+
+<p>
+<em>Updating</em>:
+Code that uses packages now in <code>old</code> will need to be updated by hand,
+or else compiled from an installation that has <code>old</code> available.
+The <code>go</code> <code>fix</code> tool will warn about such uses.
+</p>
+
+<h3 id="deleted">Deleted packages</h3>
+
+<p>
+Go 1 deletes several packages outright:
+</p>
+
+<ul>
+<li><code>container/vector</code></li>
+<li><code>exp/datafmt</code></li>
+<li><code>go/typechecker</code></li>
+<li><code>try</code></li>
+</ul>
+
+<p>
+and also the command <code>gotry</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses <code>container/vector</code> should be updated to use
+slices directly. See
+<a href="http://code.google.com/p/go-wiki/wiki/SliceTricks">the Go
+Language Community Wiki</a> for some suggestions.
+Code that uses the other packages (there should be almost zero) will need to be rethought.
+</p>
+
+<h3 id="subrepo">Packages moving to subrepositories</h3>
+
+<p>
+Go 1 has moved a number of packages into other repositories, usually sub-repositories of
+<a href="http://code.google.com/p/go/">the main Go repository</a>.
+This table lists the old and new import paths:
+
+<table class="codetable" frame="border" summary="Sub-repositories">
+<colgroup align="left" width="40%"></colgroup>
+<colgroup align="left" width="60%"></colgroup>
+<tr>
+<th align="left">Old</th>
+<th align="left">New</th>
+</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>crypto/bcrypt</td> <td>code.google.com/p/go.crypto/bcrypt</tr>
+<tr><td>crypto/blowfish</td> <td>code.google.com/p/go.crypto/blowfish</tr>
+<tr><td>crypto/cast5</td> <td>code.google.com/p/go.crypto/cast5</tr>
+<tr><td>crypto/md4</td> <td>code.google.com/p/go.crypto/md4</tr>
+<tr><td>crypto/ocsp</td> <td>code.google.com/p/go.crypto/ocsp</tr>
+<tr><td>crypto/openpgp</td> <td>code.google.com/p/go.crypto/openpgp</tr>
+<tr><td>crypto/openpgp/armor</td> <td>code.google.com/p/go.crypto/openpgp/armor</tr>
+<tr><td>crypto/openpgp/elgamal</td> <td>code.google.com/p/go.crypto/openpgp/elgamal</tr>
+<tr><td>crypto/openpgp/errors</td> <td>code.google.com/p/go.crypto/openpgp/errors</tr>
+<tr><td>crypto/openpgp/packet</td> <td>code.google.com/p/go.crypto/openpgp/packet</tr>
+<tr><td>crypto/openpgp/s2k</td> <td>code.google.com/p/go.crypto/openpgp/s2k</tr>
+<tr><td>crypto/ripemd160</td> <td>code.google.com/p/go.crypto/ripemd160</tr>
+<tr><td>crypto/twofish</td> <td>code.google.com/p/go.crypto/twofish</tr>
+<tr><td>crypto/xtea</td> <td>code.google.com/p/go.crypto/xtea</tr>
+<tr><td>exp/ssh</td> <td>code.google.com/p/go.crypto/ssh</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>image/bmp</td> <td>code.google.com/p/go.image/bmp</tr>
+<tr><td>image/tiff</td> <td>code.google.com/p/go.image/tiff</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>net/dict</td> <td>code.google.com/p/go.net/dict</tr>
+<tr><td>net/websocket</td> <td>code.google.com/p/go.net/websocket</tr>
+<tr><td>exp/spdy</td> <td>code.google.com/p/go.net/spdy</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>encoding/git85</td> <td>code.google.com/p/go.codereview/git85</tr>
+<tr><td>patch</td> <td>code.google.com/p/go.codereview/patch</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>exp/wingui</td> <td>code.google.com/p/gowingui</tr>
+</table>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update imports of these packages to use the new import paths.
+Installations that depend on these packages will need to install them using
+a <code>go get</code> command.
+</p>
+
+<h2 id="major">Major changes to the library</h2>
+
+<p>
+This section describes significant changes to the core libraries, the ones that
+affect the most programs.
+</p>
+
+<h3 id="errors">The error type and errors package</h3>
+
+<p>
+The placement of <code>os.Error</code> in package <code>os</code> is mostly historical: errors first came up when implementing package <code>os</code>, and they seemed system-related at the time.
+Since then it has become clear that errors are more fundamental than the operating system. For example, it would be nice to use <code>Errors</code> in packages that <code>os</code> depends on, like <code>syscall</code>.
+Also, having <code>Error</code> in <code>os</code> introduces many dependencies on <code>os</code> that would otherwise not exist.
+</p>
+
+<p>
+Go 1 solves these problems by introducing a built-in <code>error</code> interface type and a separate <code>errors</code> package (analogous to <code>bytes</code> and <code>strings</code>) that contains utility functions.
+It replaces <code>os.NewError</code> with
+<a href="/pkg/errors/#New"><code>errors.New</code></a>,
+giving errors a more central place in the environment.
+</p>
+
+<p>
+So the widely-used <code>String</code> method does not cause accidental satisfaction
+of the <code>error</code> interface, the <code>error</code> interface uses instead
+the name <code>Error</code> for that method:
+</p>
+
+<pre>
+ type error interface {
+ Error() string
+ }
+</pre>
+
+<p>
+The <code>fmt</code> library automatically invokes <code>Error</code>, as it already
+does for <code>String</code>, for easy printing of error values.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/START ERROR EXAMPLE/` `/END ERROR EXAMPLE/`}}
+-->type SyntaxError struct {
+ File string
+ Line int
+ Message string
+}
+
+func (se *SyntaxError) Error() string {
+ return fmt.Sprintf("%s:%d: %s", se.File, se.Line, se.Message)
+}</pre>
+
+<p>
+All standard packages have been updated to use the new interface; the old <code>os.Error</code> is gone.
+</p>
+
+<p>
+A new package, <a href="/pkg/errors/"><code>errors</code></a>, contains the function
+</p>
+
+<pre>
+func New(text string) error
+</pre>
+
+<p>
+to turn a string into an error. It replaces the old <code>os.NewError</code>.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/ErrSyntax/`}}
+--> var ErrSyntax = errors.New("syntax error")</pre>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update almost all code affected by the change.
+Code that defines error types with a <code>String</code> method will need to be updated
+by hand to rename the methods to <code>Error</code>.
+</p>
+
+<h3 id="errno">System call errors</h3>
+
+<p>
+The old <code>syscall</code> package, which predated <code>os.Error</code>
+(and just about everything else),
+returned errors as <code>int</code> values.
+In turn, the <code>os</code> package forwarded many of these errors, such
+as <code>EINVAL</code>, but using a different set of errors on each platform.
+This behavior was unpleasant and unportable.
+</p>
+
+<p>
+In Go 1, the
+<a href="/pkg/syscall/"><code>syscall</code></a>
+package instead returns an <code>error</code> for system call errors.
+On Unix, the implementation is done by a
+<a href="/pkg/syscall/#Errno"><code>syscall.Errno</code></a> type
+that satisfies <code>error</code> and replaces the old <code>os.Errno</code>.
+</p>
+
+<p>
+The changes affecting <code>os.EINVAL</code> and relatives are
+described <a href="#os">elsewhere</a>.
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update almost all code affected by the change.
+Regardless, most code should use the <code>os</code> package
+rather than <code>syscall</code> and so will be unaffected.
+</p>
+
+<h3 id="time">Time</h3>
+
+<p>
+Time is always a challenge to support well in a programming language.
+The old Go <code>time</code> package had <code>int64</code> units, no
+real type safety,
+and no distinction between absolute times and durations.
+</p>
+
+<p>
+One of the most sweeping changes in the Go 1 library is therefore a
+complete redesign of the
+<a href="/pkg/time/"><code>time</code></a> package.
+Instead of an integer number of nanoseconds as an <code>int64</code>,
+and a separate <code>*time.Time</code> type to deal with human
+units such as hours and years,
+there are now two fundamental types:
+<a href="/pkg/time/#Time"><code>time.Time</code></a>
+(a value, so the <code>*</code> is gone), which represents a moment in time;
+and <a href="/pkg/time/#Duration"><code>time.Duration</code></a>,
+which represents an interval.
+Both have nanosecond resolution.
+A <code>Time</code> can represent any time into the ancient
+past and remote future, while a <code>Duration</code> can
+span plus or minus only about 290 years.
+There are methods on these types, plus a number of helpful
+predefined constant durations such as <code>time.Second</code>.
+</p>
+
+<p>
+Among the new methods are things like
+<a href="/pkg/time/#Time.Add"><code>Time.Add</code></a>,
+which adds a <code>Duration</code> to a <code>Time</code>, and
+<a href="/pkg/time/#Time.Sub"><code>Time.Sub</code></a>,
+which subtracts two <code>Times</code> to yield a <code>Duration</code>.
+</p>
+
+<p>
+The most important semantic change is that the Unix epoch (Jan 1, 1970) is now
+relevant only for those functions and methods that mention Unix:
+<a href="/pkg/time/#Unix"><code>time.Unix</code></a>
+and the <a href="/pkg/time/#Time.Unix"><code>Unix</code></a>
+and <a href="/pkg/time/#Time.UnixNano"><code>UnixNano</code></a> methods
+of the <code>Time</code> type.
+In particular,
+<a href="/pkg/time/#Now"><code>time.Now</code></a>
+returns a <code>time.Time</code> value rather than, in the old
+API, an integer nanosecond count since the Unix epoch.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/sleepUntil/` `/^}/`}}
+--><span class="comment">// sleepUntil sleeps until the specified time. It returns immediately if it's too late.</span>
+func sleepUntil(wakeup time.Time) {
+ now := time.Now() <span class="comment">// A Time.</span>
+ if !wakeup.After(now) {
+ return
+ }
+ delta := wakeup.Sub(now) <span class="comment">// A Duration.</span>
+ fmt.Printf("Sleeping for %.3fs\n", delta.Seconds())
+ time.Sleep(delta)
+}</pre>
+
+<p>
+The new types, methods, and constants have been propagated through
+all the standard packages that use time, such as <code>os</code> and
+its representation of file time stamps.
+</p>
+
+<p>
+<em>Updating</em>:
+The <code>go</code> <code>fix</code> tool will update many uses of the old <code>time</code> package to use the new
+types and methods, although it does not replace values such as <code>1e9</code>
+representing nanoseconds per second.
+Also, because of type changes in some of the values that arise,
+some of the expressions rewritten by the fix tool may require
+further hand editing; in such cases the rewrite will include
+the correct function or method for the old functionality, but
+may have the wrong type or require further analysis.
+</p>
+
+<h2 id="minor">Minor changes to the library</h2>
+
+<p>
+This section describes smaller changes, such as those to less commonly
+used packages or that affect
+few programs beyond the need to run <code>go</code> <code>fix</code>.
+This category includes packages that are new in Go 1.
+Collectively they improve portability, regularize behavior, and
+make the interfaces more modern and Go-like.
+</p>
+
+<h3 id="archive_zip">The archive/zip package</h3>
+
+<p>
+In Go 1, <a href="/pkg/archive/zip/#Writer"><code>*zip.Writer</code></a> no
+longer has a <code>Write</code> method. Its presence was a mistake.
+</p>
+
+<p>
+<em>Updating</em>:
+What little code is affected will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="bufio">The bufio package</h3>
+
+<p>
+In Go 1, <a href="/pkg/bufio/#NewReaderSize"><code>bufio.NewReaderSize</code></a>
+and
+<a href="/pkg/bufio/#NewWriterSize"><code>bufio.NewWriterSize</code></a>
+functions no longer return an error for invalid sizes.
+If the argument size is too small or invalid, it is adjusted.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update calls that assign the error to _.
+Calls that aren't fixed will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="compress">The compress/flate, compress/gzip and compress/zlib packages</h3>
+
+<p>
+In Go 1, the <code>NewWriterXxx</code> functions in
+<a href="/pkg/compress/flate"><code>compress/flate</code></a>,
+<a href="/pkg/compress/gzip"><code>compress/gzip</code></a> and
+<a href="/pkg/compress/zlib"><code>compress/zlib</code></a>
+all return <code>(*Writer, error)</code> if they take a compression level,
+and <code>*Writer</code> otherwise. Package <code>gzip</code>'s
+<code>Compressor</code> and <code>Decompressor</code> types have been renamed
+to <code>Writer</code> and <code>Reader</code>. Package <code>flate</code>'s
+<code>WrongValueError</code> type has been removed.
+</p>
+
+<p>
+<em>Updating</em>
+Running <code>go</code> <code>fix</code> will update old names and calls that assign the error to _.
+Calls that aren't fixed will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="crypto_aes_des">The crypto/aes and crypto/des packages</h3>
+
+<p>
+In Go 1, the <code>Reset</code> method has been removed. Go does not guarantee
+that memory is not copied and therefore this method was misleading.
+</p>
+
+<p>
+The cipher-specific types <code>*aes.Cipher</code>, <code>*des.Cipher</code>,
+and <code>*des.TripleDESCipher</code> have been removed in favor of
+<code>cipher.Block</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Remove the calls to Reset. Replace uses of the specific cipher types with
+cipher.Block.
+</p>
+
+<h3 id="crypto_elliptic">The crypto/elliptic package</h3>
+
+<p>
+In Go 1, <a href="/pkg/crypto/elliptic/#Curve"><code>elliptic.Curve</code></a>
+has been made an interface to permit alternative implementations. The curve
+parameters have been moved to the
+<a href="/pkg/crypto/elliptic/#CurveParams"><code>elliptic.CurveParams</code></a>
+structure.
+</p>
+
+<p>
+<em>Updating</em>:
+Existing users of <code>*elliptic.Curve</code> will need to change to
+simply <code>elliptic.Curve</code>. Calls to <code>Marshal</code>,
+<code>Unmarshal</code> and <code>GenerateKey</code> are now functions
+in <code>crypto/elliptic</code> that take an <code>elliptic.Curve</code>
+as their first argument.
+</p>
+
+<h3 id="crypto_hmac">The crypto/hmac package</h3>
+
+<p>
+In Go 1, the hash-specific functions, such as <code>hmac.NewMD5</code>, have
+been removed from <code>crypto/hmac</code>. Instead, <code>hmac.New</code> takes
+a function that returns a <code>hash.Hash</code>, such as <code>md5.New</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will perform the needed changes.
+</p>
+
+<h3 id="crypto_x509">The crypto/x509 package</h3>
+
+<p>
+In Go 1, the
+<a href="/pkg/crypto/x509/#CreateCertificate"><code>CreateCertificate</code></a>
+and
+<a href="/pkg/crypto/x509/#CreateCRL"><code>CreateCRL</code></a>
+functions in <code>crypto/x509</code> have been altered to take an
+<code>interface{}</code> where they previously took a <code>*rsa.PublicKey</code>
+or <code>*rsa.PrivateKey</code>. This will allow other public key algorithms
+to be implemented in the future.
+</p>
+
+<p>
+<em>Updating</em>:
+No changes will be needed.
+</p>
+
+<h3 id="encoding_binary">The encoding/binary package</h3>
+
+<p>
+In Go 1, the <code>binary.TotalSize</code> function has been replaced by
+<a href="/pkg/encoding/binary/#Size"><code>Size</code></a>,
+which takes an <code>interface{}</code> argument rather than
+a <code>reflect.Value</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+What little code is affected will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="encoding_xml">The encoding/xml package</h3>
+
+<p>
+In Go 1, the <a href="/pkg/encoding/xml/"><code>xml</code></a> package
+has been brought closer in design to the other marshaling packages such
+as <a href="/pkg/encoding/gob/"><code>encoding/gob</code></a>.
+</p>
+
+<p>
+The old <code>Parser</code> type is renamed
+<a href="/pkg/encoding/xml/#Decoder"><code>Decoder</code></a> and has a new
+<a href="/pkg/encoding/xml/#Decoder.Decode"><code>Decode</code></a> method. An
+<a href="/pkg/encoding/xml/#Encoder"><code>Encoder</code></a> type was also introduced.
+</p>
+
+<p>
+The functions <a href="/pkg/encoding/xml/#Marshal"><code>Marshal</code></a>
+and <a href="/pkg/encoding/xml/#Unmarshal"><code>Unmarshal</code></a>
+work with <code>[]byte</code> values now. To work with streams,
+use the new <a href="/pkg/encoding/xml/#Encoder"><code>Encoder</code></a>
+and <a href="/pkg/encoding/xml/#Decoder"><code>Decoder</code></a> types.
+</p>
+
+<p>
+When marshaling or unmarshaling values, the format of supported flags in
+field tags has changed to be closer to the
+<a href="/pkg/encoding/json"><code>json</code></a> package
+(<code>`xml:"name,flag"`</code>). The matching done between field tags, field
+names, and the XML attribute and element names is now case-sensitive.
+The <code>XMLName</code> field tag, if present, must also match the name
+of the XML element being marshaled.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update most uses of the package except for some calls to
+<code>Unmarshal</code>. Special care must be taken with field tags,
+since the fix tool will not update them and if not fixed by hand they will
+misbehave silently in some cases. For example, the old
+<code>"attr"</code> is now written <code>",attr"</code> while plain
+<code>"attr"</code> remains valid but with a different meaning.
+</p>
+
+<h3 id="expvar">The expvar package</h3>
+
+<p>
+In Go 1, the <code>RemoveAll</code> function has been removed.
+The <code>Iter</code> function and Iter method on <code>*Map</code> have
+been replaced by
+<a href="/pkg/expvar/#Do"><code>Do</code></a>
+and
+<a href="/pkg/expvar/#Map.Do"><code>(*Map).Do</code></a>.
+</p>
+
+<p>
+<em>Updating</em>:
+Most code using <code>expvar</code> will not need changing. The rare code that used
+<code>Iter</code> can be updated to pass a closure to <code>Do</code> to achieve the same effect.
+</p>
+
+<h3 id="flag">The flag package</h3>
+
+<p>
+In Go 1, the interface <a href="/pkg/flag/#Value"><code>flag.Value</code></a> has changed slightly.
+The <code>Set</code> method now returns an <code>error</code> instead of
+a <code>bool</code> to indicate success or failure.
+</p>
+
+<p>
+There is also a new kind of flag, <code>Duration</code>, to support argument
+values specifying time intervals.
+Values for such flags must be given units, just as <code>time.Duration</code>
+formats them: <code>10s</code>, <code>1h30m</code>, etc.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/timeout/`}}
+-->var timeout = flag.Duration("timeout", 30*time.Second, "how long to wait for completion")</pre>
+
+<p>
+<em>Updating</em>:
+Programs that implement their own flags will need minor manual fixes to update their
+<code>Set</code> methods.
+The <code>Duration</code> flag is new and affects no existing code.
+</p>
+
+
+<h3 id="go">The go/* packages</h3>
+
+<p>
+Several packages under <code>go</code> have slightly revised APIs.
+</p>
+
+<p>
+A concrete <code>Mode</code> type was introduced for configuration mode flags
+in the packages
+<a href="/pkg/go/scanner/"><code>go/scanner</code></a>,
+<a href="/pkg/go/parser/"><code>go/parser</code></a>,
+<a href="/pkg/go/printer/"><code>go/printer</code></a>, and
+<a href="/pkg/go/doc/"><code>go/doc</code></a>.
+</p>
+
+<p>
+The modes <code>AllowIllegalChars</code> and <code>InsertSemis</code> have been removed
+from the <a href="/pkg/go/scanner/"><code>go/scanner</code></a> package. They were mostly
+useful for scanning text other then Go source files. Instead, the
+<a href="/pkg/text/scanner/"><code>text/scanner</code></a> package should be used
+for that purpose.
+</p>
+
+<p>
+The <a href="/pkg/go/scanner/#ErrorHandler"><code>ErrorHandler</code></a> provided
+to the scanner's <a href="/pkg/go/scanner/#Scanner.Init"><code>Init</code></a> method is
+now simply a function rather than an interface. The <code>ErrorVector</code> type has
+been removed in favor of the (existing) <a href="/pkg/go/scanner/#ErrorList"><code>ErrorList</code></a>
+type, and the <code>ErrorVector</code> methods have been migrated. Instead of embedding
+an <code>ErrorVector</code> in a client of the scanner, now a client should maintain
+an <code>ErrorList</code>.
+</p>
+
+<p>
+The set of parse functions provided by the <a href="/pkg/go/parser/"><code>go/parser</code></a>
+package has been reduced to the primary parse function
+<a href="/pkg/go/parser/#ParseFile"><code>ParseFile</code></a>, and a couple of
+convenience functions <a href="/pkg/go/parser/#ParseDir"><code>ParseDir</code></a>
+and <a href="/pkg/go/parser/#ParseExpr"><code>ParseExpr</code></a>.
+</p>
+
+<p>
+The <a href="/pkg/go/printer/"><code>go/printer</code></a> package supports an additional
+configuration mode <a href="/pkg/go/printer/#Mode"><code>SourcePos</code></a>;
+if set, the printer will emit <code>//line</code> comments such that the generated
+output contains the original source code position information. The new type
+<a href="/pkg/go/printer/#CommentedNode"><code>CommentedNode</code></a> can be
+used to provide comments associated with an arbitrary
+<a href="/pkg/go/ast/#Node"><code>ast.Node</code></a> (until now only
+<a href="/pkg/go/ast/#File"><code>ast.File</code></a> carried comment information).
+</p>
+
+<p>
+The type names of the <a href="/pkg/go/doc/"><code>go/doc</code></a> package have been
+streamlined by removing the <code>Doc</code> suffix: <code>PackageDoc</code>
+is now <code>Package</code>, <code>ValueDoc</code> is <code>Value</code>, etc.
+Also, all types now consistently have a <code>Name</code> field (or <code>Names</code>,
+in the case of type <code>Value</code>) and <code>Type.Factories</code> has become
+<code>Type.Funcs</code>.
+Instead of calling <code>doc.NewPackageDoc(pkg, importpath)</code>,
+documentation for a package is created with:
+</p>
+
+<pre>
+ doc.New(pkg, importpath, mode)
+</pre>
+
+<p>
+where the new <code>mode</code> parameter specifies the operation mode:
+if set to <a href="/pkg/go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations
+(not just exported ones) are considered.
+The function <code>NewFileDoc</code> was removed, and the function
+<code>CommentText</code> has become the method
+<a href="/pkg/go/ast/#Text"><code>Text</code></a> of
+<a href="/pkg/go/ast/#CommentGroup"><code>ast.CommentGroup</code></a>.
+</p>
+
+<p>
+In package <a href="/pkg/go/token/"><code>go/token</code></a>, the
+<a href="/pkg/go/token/#FileSet"><code>token.FileSet</code></a> method <code>Files</code>
+(which originally returned a channel of <code>*token.File</code>s) has been replaced
+with the iterator <a href="/pkg/go/token/#FileSet.Iterate"><code>Iterate</code></a> that
+accepts a function argument instead.
+</p>
+
+<p>
+In package <a href="/pkg/go/build/"><code>go/build</code></a>, the API
+has been nearly completely replaced.
+The package still computes Go package information
+but it does not run the build: the <code>Cmd</code> and <code>Script</code>
+types are gone.
+(To build code, use the new
+<a href="/cmd/go/"><code>go</code></a> command instead.)
+The <code>DirInfo</code> type is now named
+<a href="/pkg/go/build/#Package"><code>Package</code></a>.
+<code>FindTree</code> and <code>ScanDir</code> are replaced by
+<a href="/pkg/go/build/#Import"><code>Import</code></a>
+and
+<a href="/pkg/go/build/#ImportDir"><code>ImportDir</code></a>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses packages in <code>go</code> will have to be updated by hand; the
+compiler will reject incorrect uses. Templates used in conjunction with any of the
+<code>go/doc</code> types may need manual fixes; the renamed fields will lead
+to run-time errors.
+</p>
+
+<h3 id="hash">The hash package</h3>
+
+<p>
+In Go 1, the definition of <a href="/pkg/hash/#Hash"><code>hash.Hash</code></a> includes
+a new method, <code>BlockSize</code>. This new method is used primarily in the
+cryptographic libraries.
+</p>
+
+<p>
+The <code>Sum</code> method of the
+<a href="/pkg/hash/#Hash"><code>hash.Hash</code></a> interface now takes a
+<code>[]byte</code> argument, to which the hash value will be appended.
+The previous behavior can be recreated by adding a <code>nil</code> argument to the call.
+</p>
+
+<p>
+<em>Updating</em>:
+Existing implementations of <code>hash.Hash</code> will need to add a
+<code>BlockSize</code> method. Hashes that process the input one byte at
+a time can implement <code>BlockSize</code> to return 1.
+Running <code>go</code> <code>fix</code> will update calls to the <code>Sum</code> methods of the various
+implementations of <code>hash.Hash</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Since the package's functionality is new, no updating is necessary.
+</p>
+
+<h3 id="http">The http package</h3>
+
+<p>
+In Go 1 the <a href="/pkg/net/http/"><code>http</code></a> package is refactored,
+putting some of the utilities into a
+<a href="/pkg/net/http/httputil/"><code>httputil</code></a> subdirectory.
+These pieces are only rarely needed by HTTP clients.
+The affected items are:
+</p>
+
+<ul>
+<li>ClientConn</li>
+<li>DumpRequest</li>
+<li>DumpRequestOut</li>
+<li>DumpResponse</li>
+<li>NewChunkedReader</li>
+<li>NewChunkedWriter</li>
+<li>NewClientConn</li>
+<li>NewProxyClientConn</li>
+<li>NewServerConn</li>
+<li>NewSingleHostReverseProxy</li>
+<li>ReverseProxy</li>
+<li>ServerConn</li>
+</ul>
+
+<p>
+The <code>Request.RawURL</code> field has been removed; it was a
+historical artifact.
+</p>
+
+<p>
+The <code>Handle</code> and <code>HandleFunc</code>
+functions, and the similarly-named methods of <code>ServeMux</code>,
+now panic if an attempt is made to register the same pattern twice.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update the few programs that are affected except for
+uses of <code>RawURL</code>, which must be fixed by hand.
+</p>
+
+<h3 id="image">The image package</h3>
+
+<p>
+The <a href="/pkg/image/"><code>image</code></a> package has had a number of
+minor changes, rearrangements and renamings.
+</p>
+
+<p>
+Most of the color handling code has been moved into its own package,
+<a href="/pkg/image/color/"><code>image/color</code></a>.
+For the elements that moved, a symmetry arises; for instance,
+each pixel of an
+<a href="/pkg/image/#RGBA"><code>image.RGBA</code></a>
+is a
+<a href="/pkg/image/color/#RGBA"><code>color.RGBA</code></a>.
+</p>
+
+<p>
+The old <code>image/ycbcr</code> package has been folded, with some
+renamings, into the
+<a href="/pkg/image/"><code>image</code></a>
+and
+<a href="/pkg/image/color/"><code>image/color</code></a>
+packages.
+</p>
+
+<p>
+The old <code>image.ColorImage</code> type is still in the <code>image</code>
+package but has been renamed
+<a href="/pkg/image/#Uniform"><code>image.Uniform</code></a>,
+while <code>image.Tiled</code> has been removed.
+</p>
+
+<p>
+This table lists the renamings.
+</p>
+
+<table class="codetable" frame="border" summary="image renames">
+<colgroup align="left" width="50%"></colgroup>
+<colgroup align="left" width="50%"></colgroup>
+<tr>
+<th align="left">Old</th>
+<th align="left">New</th>
+</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>image.Color</td> <td>color.Color</td></tr>
+<tr><td>image.ColorModel</td> <td>color.Model</td></tr>
+<tr><td>image.ColorModelFunc</td> <td>color.ModelFunc</td></tr>
+<tr><td>image.PalettedColorModel</td> <td>color.Palette</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>image.RGBAColor</td> <td>color.RGBA</td></tr>
+<tr><td>image.RGBA64Color</td> <td>color.RGBA64</td></tr>
+<tr><td>image.NRGBAColor</td> <td>color.NRGBA</td></tr>
+<tr><td>image.NRGBA64Color</td> <td>color.NRGBA64</td></tr>
+<tr><td>image.AlphaColor</td> <td>color.Alpha</td></tr>
+<tr><td>image.Alpha16Color</td> <td>color.Alpha16</td></tr>
+<tr><td>image.GrayColor</td> <td>color.Gray</td></tr>
+<tr><td>image.Gray16Color</td> <td>color.Gray16</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>image.RGBAColorModel</td> <td>color.RGBAModel</td></tr>
+<tr><td>image.RGBA64ColorModel</td> <td>color.RGBA64Model</td></tr>
+<tr><td>image.NRGBAColorModel</td> <td>color.NRGBAModel</td></tr>
+<tr><td>image.NRGBA64ColorModel</td> <td>color.NRGBA64Model</td></tr>
+<tr><td>image.AlphaColorModel</td> <td>color.AlphaModel</td></tr>
+<tr><td>image.Alpha16ColorModel</td> <td>color.Alpha16Model</td></tr>
+<tr><td>image.GrayColorModel</td> <td>color.GrayModel</td></tr>
+<tr><td>image.Gray16ColorModel</td> <td>color.Gray16Model</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>ycbcr.RGBToYCbCr</td> <td>color.RGBToYCbCr</td></tr>
+<tr><td>ycbcr.YCbCrToRGB</td> <td>color.YCbCrToRGB</td></tr>
+<tr><td>ycbcr.YCbCrColorModel</td> <td>color.YCbCrModel</td></tr>
+<tr><td>ycbcr.YCbCrColor</td> <td>color.YCbCr</td></tr>
+<tr><td>ycbcr.YCbCr</td> <td>image.YCbCr</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>ycbcr.SubsampleRatio444</td> <td>image.YCbCrSubsampleRatio444</td></tr>
+<tr><td>ycbcr.SubsampleRatio422</td> <td>image.YCbCrSubsampleRatio422</td></tr>
+<tr><td>ycbcr.SubsampleRatio420</td> <td>image.YCbCrSubsampleRatio420</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>image.ColorImage</td> <td>image.Uniform</td></tr>
+</table>
+
+<p>
+The image package's <code>New</code> functions
+(<a href="/pkg/image/#NewRGBA"><code>NewRGBA</code></a>,
+<a href="/pkg/image/#NewRGBA64"><code>NewRGBA64</code></a>, etc.)
+take an <a href="/pkg/image/#Rectangle"><code>image.Rectangle</code></a> as an argument
+instead of four integers.
+</p>
+
+<p>
+Finally, there are new predefined <code>color.Color</code> variables
+<a href="/pkg/image/color/#Black"><code>color.Black</code></a>,
+<a href="/pkg/image/color/#White"><code>color.White</code></a>,
+<a href="/pkg/image/color/#Opaque"><code>color.Opaque</code></a>
+and
+<a href="/pkg/image/color/#Transparent"><code>color.Transparent</code></a>.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update almost all code affected by the change.
+</p>
+
+<h3 id="log_syslog">The log/syslog package</h3>
+
+<p>
+In Go 1, the <a href="/pkg/log/syslog/#NewLogger"><code>syslog.NewLogger</code></a>
+function returns an error as well as a <code>log.Logger</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+What little code is affected will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="mime">The mime package</h3>
+
+<p>
+In Go 1, the <a href="/pkg/mime/#FormatMediaType"><code>FormatMediaType</code></a> function
+of the <code>mime</code> package has been simplified to make it
+consistent with
+<a href="/pkg/mime/#ParseMediaType"><code>ParseMediaType</code></a>.
+It now takes <code>"text/html"</code> rather than <code>"text"</code> and <code>"html"</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+What little code is affected will be caught by the compiler and must be updated by hand.
+</p>
+
+<h3 id="net">The net package</h3>
+
+<p>
+In Go 1, the various <code>SetTimeout</code>,
+<code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods
+have been replaced with
+<a href="/pkg/net/#IPConn.SetDeadline"><code>SetDeadline</code></a>,
+<a href="/pkg/net/#IPConn.SetReadDeadline"><code>SetReadDeadline</code></a>, and
+<a href="/pkg/net/#IPConn.SetWriteDeadline"><code>SetWriteDeadline</code></a>,
+respectively. Rather than taking a timeout value in nanoseconds that
+apply to any activity on the connection, the new methods set an
+absolute deadline (as a <code>time.Time</code> value) after which
+reads and writes will time out and no longer block.
+</p>
+
+<p>
+There are also new functions
+<a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a>
+to simplify timing out dialing a network address and
+<a href="/pkg/net/#ListenMulticastUDP"><code>net.ListenMulticastUDP</code></a>
+to allow multicast UDP to listen concurrently across multiple listeners.
+The <code>net.ListenMulticastUDP</code> function replaces the old
+<code>JoinGroup</code> and <code>LeaveGroup</code> methods.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses the old methods will fail to compile and must be updated by hand.
+The semantic change makes it difficult for the fix tool to update automatically.
+</p>
+
+<h3 id="os">The os package</h3>
+
+<p>
+The <code>Time</code> function has been removed; callers should use
+the <a href="/pkg/time/#Time"><code>Time</code></a> type from the
+<code>time</code> package.
+</p>
+
+<p>
+The <code>Exec</code> function has been removed; callers should use
+<code>Exec</code> from the <code>syscall</code> package, where available.
+</p>
+
+<p>
+The <code>ShellExpand</code> function has been renamed to <a
+href="/pkg/os/#ExpandEnv"><code>ExpandEnv</code></a>.
+</p>
+
+<p>
+The <a href="/pkg/os/#NewFile"><code>NewFile</code></a> function
+now takes a <code>uintptr</code> fd, instead of an <code>int</code>.
+The <a href="/pkg/os/#File.Fd"><code>Fd</code></a> method on files now
+also returns a <code>uintptr</code>.
+</p>
+
+<p>
+There are no longer error constants such as <code>EINVAL</code>
+in the <code>os</code> package, since the set of values varied with
+the underlying operating system. There are new portable functions like
+<a href="/pkg/os/#IsPermission"><code>IsPermission</code></a>
+to test common error properties, plus a few new error values
+with more Go-like names, such as
+<a href="/pkg/os/#ErrPermission"><code>ErrPermission</code></a>
+and
+<a href="/pkg/os/#ErrNoEnv"><code>ErrNoEnv</code></a>.
+</p>
+
+<p>
+The <code>Getenverror</code> function has been removed. To distinguish
+between a non-existent environment variable and an empty string,
+use <a href="/pkg/os/#Environ"><code>os.Environ</code></a> or
+<a href="/pkg/syscall/#Getenv"><code>syscall.Getenv</code></a>.
+</p>
+
+
+<p>
+The <a href="/pkg/os/#Process.Wait"><code>Process.Wait</code></a> method has
+dropped its option argument and the associated constants are gone
+from the package.
+Also, the function <code>Wait</code> is gone; only the method of
+the <code>Process</code> type persists.
+</p>
+
+<p>
+The <code>Waitmsg</code> type returned by
+<a href="/pkg/os/#Process.Wait"><code>Process.Wait</code></a>
+has been replaced with a more portable
+<a href="/pkg/os/#ProcessState"><code>ProcessState</code></a>
+type with accessor methods to recover information about the
+process.
+Because of changes to <code>Wait</code>, the <code>ProcessState</code>
+value always describes an exited process.
+Portability concerns simplified the interface in other ways, but the values returned by the
+<a href="/pkg/os/#ProcessState.Sys"><code>ProcessState.Sys</code></a> and
+<a href="/pkg/os/#ProcessState.SysUsage"><code>ProcessState.SysUsage</code></a>
+methods can be type-asserted to underlying system-specific data structures such as
+<a href="/pkg/syscall/#WaitStatus"><code>syscall.WaitStatus</code></a> and
+<a href="/pkg/syscall/#Rusage"><code>syscall.Rusage</code></a> on Unix.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will drop a zero argument to <code>Process.Wait</code>.
+All other changes will be caught by the compiler and must be updated by hand.
+</p>
+
+<h4 id="os_fileinfo">The os.FileInfo type</h4>
+
+<p>
+Go 1 redefines the <a href="/pkg/os/#FileInfo"><code>os.FileInfo</code></a> type,
+changing it from a struct to an interface:
+</p>
+
+<pre>
+ type FileInfo interface {
+ Name() string // base name of the file
+ Size() int64 // length in bytes
+ Mode() FileMode // file mode bits
+ ModTime() time.Time // modification time
+ IsDir() bool // abbreviation for Mode().IsDir()
+ Sys() interface{} // underlying data source (can return nil)
+ }
+</pre>
+
+<p>
+The file mode information has been moved into a subtype called
+<a href="/pkg/os/#FileMode"><code>os.FileMode</code></a>,
+a simple integer type with <code>IsDir</code>, <code>Perm</code>, and <code>String</code>
+methods.
+</p>
+
+<p>
+The system-specific details of file modes and properties such as (on Unix)
+i-number have been removed from <code>FileInfo</code> altogether.
+Instead, each operating system's <code>os</code> package provides an
+implementation of the <code>FileInfo</code> interface, which
+has a <code>Sys</code> method that returns the
+system-specific representation of file metadata.
+For instance, to discover the i-number of a file on a Unix system, unpack
+the <code>FileInfo</code> like this:
+</p>
+
+<pre>
+ fi, err := os.Stat("hello.go")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Check that it's a Unix file.
+ unixStat, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ log.Fatal("hello.go: not a Unix file")
+ }
+ fmt.Printf("file i-number: %d\n", unixStat.Ino)
+</pre>
+
+<p>
+Assuming (which is unwise) that <code>"hello.go"</code> is a Unix file,
+the i-number expression could be contracted to
+</p>
+
+<pre>
+ fi.Sys().(*syscall.Stat_t).Ino
+</pre>
+
+<p>
+The vast majority of uses of <code>FileInfo</code> need only the methods
+of the standard interface.
+</p>
+
+<p>
+The <code>os</code> package no longer contains wrappers for the POSIX errors
+such as <code>ENOENT</code>.
+For the few programs that need to verify particular error conditions, there are
+now the boolean functions
+<a href="/pkg/os/#IsExist"><code>IsExist</code></a>,
+<a href="/pkg/os/#IsNotExist"><code>IsNotExist</code></a>
+and
+<a href="/pkg/os/#IsPermission"><code>IsPermission</code></a>.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/os\.Open/` `/}/`}}
+--> f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
+ if os.IsExist(err) {
+ log.Printf("%s already exists", name)
+ }</pre>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update code that uses the old equivalent of the current <code>os.FileInfo</code>
+and <code>os.FileMode</code> API.
+Code that needs system-specific file details will need to be updated by hand.
+Code that uses the old POSIX error values from the <code>os</code> package
+will fail to compile and will also need to be updated by hand.
+</p>
+
+<h3 id="os_signal">The os/signal package</h3>
+
+<p>
+The <code>os/signal</code> package in Go 1 replaces the
+<code>Incoming</code> function, which returned a channel
+that received all incoming signals,
+with the selective <code>Notify</code> function, which asks
+for delivery of specific signals on an existing channel.
+</p>
+
+<p>
+<em>Updating</em>:
+Code must be updated by hand.
+A literal translation of
+</p>
+<pre>
+c := signal.Incoming()
+</pre>
+<p>
+is
+</p>
+<pre>
+c := make(chan os.Signal)
+signal.Notify(c) // ask for all signals
+</pre>
+<p>
+but most code should list the specific signals it wants to handle instead:
+</p>
+<pre>
+c := make(chan os.Signal)
+signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT)
+</pre>
+
+<h3 id="path_filepath">The path/filepath package</h3>
+
+<p>
+In Go 1, the <a href="/pkg/path/filepath/#Walk"><code>Walk</code></a> function of the
+<code>path/filepath</code> package
+has been changed to take a function value of type
+<a href="/pkg/path/filepath/#WalkFunc"><code>WalkFunc</code></a>
+instead of a <code>Visitor</code> interface value.
+<code>WalkFunc</code> unifies the handling of both files and directories.
+</p>
+
+<pre>
+ type WalkFunc func(path string, info os.FileInfo, err error) error
+</pre>
+
+<p>
+The <code>WalkFunc</code> function will be called even for files or directories that could not be opened;
+in such cases the error argument will describe the failure.
+If a directory's contents are to be skipped,
+the function should return the value <a href="/pkg/path/filepath/#variables"><code>filepath.SkipDir</code></a>
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/STARTWALK/` `/ENDWALK/`}}
+--> markFn := func(path string, info os.FileInfo, err error) error {
+ if path == "pictures" { <span class="comment">// Will skip walking of directory pictures and its contents.</span>
+ return filepath.SkipDir
+ }
+ if err != nil {
+ return err
+ }
+ log.Println(path)
+ return nil
+ }
+ err := filepath.Walk(".", markFn)
+ if err != nil {
+ log.Fatal(err)
+ }</pre>
+
+<p>
+<em>Updating</em>:
+The change simplifies most code but has subtle consequences, so affected programs
+will need to be updated by hand.
+The compiler will catch code using the old interface.
+</p>
+
+<h3 id="regexp">The regexp package</h3>
+
+<p>
+The <a href="/pkg/regexp/"><code>regexp</code></a> package has been rewritten.
+It has the same interface but the specification of the regular expressions
+it supports has changed from the old "egrep" form to that of
+<a href="http://code.google.com/p/re2/">RE2</a>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses the package should have its regular expressions checked by hand.
+</p>
+
+<h3 id="runtime">The runtime package</h3>
+
+<p>
+In Go 1, much of the API exported by package
+<code>runtime</code> has been removed in favor of
+functionality provided by other packages.
+Code using the <code>runtime.Type</code> interface
+or its specific concrete type implementations should
+now use package <a href="/pkg/reflect/"><code>reflect</code></a>.
+Code using <code>runtime.Semacquire</code> or <code>runtime.Semrelease</code>
+should use channels or the abstractions in package <a href="/pkg/sync/"><code>sync</code></a>.
+The <code>runtime.Alloc</code>, <code>runtime.Free</code>,
+and <code>runtime.Lookup</code> functions, an unsafe API created for
+debugging the memory allocator, have no replacement.
+</p>
+
+<p>
+Before, <code>runtime.MemStats</code> was a global variable holding
+statistics about memory allocation, and calls to <code>runtime.UpdateMemStats</code>
+ensured that it was up to date.
+In Go 1, <code>runtime.MemStats</code> is a struct type, and code should use
+<a href="/pkg/runtime/#ReadMemStats"><code>runtime.ReadMemStats</code></a>
+to obtain the current statistics.
+</p>
+
+<p>
+The package adds a new function,
+<a href="/pkg/runtime/#NumCPU"><code>runtime.NumCPU</code></a>, that returns the number of CPUs available
+for parallel execution, as reported by the operating system kernel.
+Its value can inform the setting of <code>GOMAXPROCS</code>.
+The <code>runtime.Cgocalls</code> and <code>runtime.Goroutines</code> functions
+have been renamed to <code>runtime.NumCgoCall</code> and <code>runtime.NumGoroutine</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update code for the function renamings.
+Other code will need to be updated by hand.
+</p>
+
+<h3 id="strconv">The strconv package</h3>
+
+<p>
+In Go 1, the
+<a href="/pkg/strconv/"><code>strconv</code></a>
+package has been significantly reworked to make it more Go-like and less C-like,
+although <code>Atoi</code> lives on (it's similar to
+<code>int(ParseInt(x, 10, 0))</code>, as does
+<code>Itoa(x)</code> (<code>FormatInt(int64(x), 10)</code>).
+There are also new variants of some of the functions that append to byte slices rather than
+return strings, to allow control over allocation.
+</p>
+
+<p>
+This table summarizes the renamings; see the
+<a href="/pkg/strconv/">package documentation</a>
+for full details.
+</p>
+
+<table class="codetable" frame="border" summary="strconv renames">
+<colgroup align="left" width="50%"></colgroup>
+<colgroup align="left" width="50%"></colgroup>
+<tr>
+<th align="left">Old call</th>
+<th align="left">New call</th>
+</tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Atob(x)</td> <td>ParseBool(x)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Atof32(x)</td> <td>ParseFloat(x, 32)§</td></tr>
+<tr><td>Atof64(x)</td> <td>ParseFloat(x, 64)</td></tr>
+<tr><td>AtofN(x, n)</td> <td>ParseFloat(x, n)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Atoi(x)</td> <td>Atoi(x)</td></tr>
+<tr><td>Atoi(x)</td> <td>ParseInt(x, 10, 0)§</td></tr>
+<tr><td>Atoi64(x)</td> <td>ParseInt(x, 10, 64)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Atoui(x)</td> <td>ParseUint(x, 10, 0)§</td></tr>
+<tr><td>Atoui64(x)</td> <td>ParseUint(x, 10, 64)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Btoi64(x, b)</td> <td>ParseInt(x, b, 64)</td></tr>
+<tr><td>Btoui64(x, b)</td> <td>ParseUint(x, b, 64)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Btoa(x)</td> <td>FormatBool(x)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Ftoa32(x, f, p)</td> <td>FormatFloat(float64(x), f, p, 32)</td></tr>
+<tr><td>Ftoa64(x, f, p)</td> <td>FormatFloat(x, f, p, 64)</td></tr>
+<tr><td>FtoaN(x, f, p, n)</td> <td>FormatFloat(x, f, p, n)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Itoa(x)</td> <td>Itoa(x)</td></tr>
+<tr><td>Itoa(x)</td> <td>FormatInt(int64(x), 10)</td></tr>
+<tr><td>Itoa64(x)</td> <td>FormatInt(x, 10)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Itob(x, b)</td> <td>FormatInt(int64(x), b)</td></tr>
+<tr><td>Itob64(x, b)</td> <td>FormatInt(x, b)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Uitoa(x)</td> <td>FormatUint(uint64(x), 10)</td></tr>
+<tr><td>Uitoa64(x)</td> <td>FormatUint(x, 10)</td></tr>
+<tr>
+<td colspan="2"><hr></td>
+</tr>
+<tr><td>Uitob(x, b)</td> <td>FormatUint(uint64(x), b)</td></tr>
+<tr><td>Uitob64(x, b)</td> <td>FormatUint(x, b)</td></tr>
+</table>
+
+<p>
+<em>Updating</em>:
+Running <code>go</code> <code>fix</code> will update almost all code affected by the change.
+<br>
+§ <code>Atoi</code> persists but <code>Atoui</code> and <code>Atof32</code> do not, so
+they may require
+a cast that must be added by hand; the <code>go</code> <code>fix</code> tool will warn about it.
+</p>
+
+
+<h3 id="templates">The template packages</h3>
+
+<p>
+The <code>template</code> and <code>exp/template/html</code> packages have moved to
+<a href="/pkg/text/template/"><code>text/template</code></a> and
+<a href="/pkg/html/template/"><code>html/template</code></a>.
+More significant, the interface to these packages has been simplified.
+The template language is the same, but the concept of "template set" is gone
+and the functions and methods of the packages have changed accordingly,
+often by elimination.
+</p>
+
+<p>
+Instead of sets, a <code>Template</code> object
+may contain multiple named template definitions,
+in effect constructing
+name spaces for template invocation.
+A template can invoke any other template associated with it, but only those
+templates associated with it.
+The simplest way to associate templates is to parse them together, something
+made easier with the new structure of the packages.
+</p>
+
+<p>
+<em>Updating</em>:
+The imports will be updated by fix tool.
+Single-template uses will be otherwise be largely unaffected.
+Code that uses multiple templates in concert will need to be updated by hand.
+The <a href="/pkg/text/template/#examples">examples</a> in
+the documentation for <code>text/template</code> can provide guidance.
+</p>
+
+<h3 id="testing">The testing package</h3>
+
+<p>
+The testing package has a type, <code>B</code>, passed as an argument to benchmark functions.
+In Go 1, <code>B</code> has new methods, analogous to those of <code>T</code>, enabling
+logging and failure reporting.
+</p>
+
+<pre><!--{{code "/doc/progs/go1.go" `/func.*Benchmark/` `/^}/`}}
+-->func BenchmarkSprintf(b *testing.B) {
+ <span class="comment">// Verify correctness before running benchmark.</span>
+ b.StopTimer()
+ got := fmt.Sprintf("%x", 23)
+ const expect = "17"
+ if expect != got {
+ b.Fatalf("expected %q; got %q", expect, got)
+ }
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Sprintf("%x", 23)
+ }
+}</pre>
+
+<p>
+<em>Updating</em>:
+Existing code is unaffected, although benchmarks that use <code>println</code>
+or <code>panic</code> should be updated to use the new methods.
+</p>
+
+<h3 id="testing_script">The testing/script package</h3>
+
+<p>
+The testing/script package has been deleted. It was a dreg.
+</p>
+
+<p>
+<em>Updating</em>:
+No code is likely to be affected.
+</p>
+
+<h3 id="unsafe">The unsafe package</h3>
+
+<p>
+In Go 1, the functions
+<code>unsafe.Typeof</code>, <code>unsafe.Reflect</code>,
+<code>unsafe.Unreflect</code>, <code>unsafe.New</code>, and
+<code>unsafe.NewArray</code> have been removed;
+they duplicated safer functionality provided by
+package <a href="/pkg/reflect/"><code>reflect</code></a>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code using these functions must be rewritten to use
+package <a href="/pkg/reflect/"><code>reflect</code></a>.
+The changes to <a href="http://code.google.com/p/go/source/detail?r=2646dc956207">encoding/gob</a> and the <a href="http://code.google.com/p/goprotobuf/source/detail?r=5340ad310031">protocol buffer library</a>
+may be helpful as examples.
+</p>
+
+<h3 id="url">The url package</h3>
+
+<p>
+In Go 1 several fields from the <a href="/pkg/net/url/#URL"><code>url.URL</code></a> type
+were removed or replaced.
+</p>
+
+<p>
+The <a href="/pkg/net/url/#URL.String"><code>String</code></a> method now
+predictably rebuilds an encoded URL string using all of <code>URL</code>'s
+fields as necessary. The resulting string will also no longer have
+passwords escaped.
+</p>
+
+<p>
+The <code>Raw</code> field has been removed. In most cases the <code>String</code>
+method may be used in its place.
+</p>
+
+<p>
+The old <code>RawUserinfo</code> field is replaced by the <code>User</code>
+field, of type <a href="/pkg/net/url/#Userinfo"><code>*net.Userinfo</code></a>.
+Values of this type may be created using the new <a href="/pkg/net/url/#User"><code>net.User</code></a>
+and <a href="/pkg/net/url/#UserPassword"><code>net.UserPassword</code></a>
+functions. The <code>EscapeUserinfo</code> and <code>UnescapeUserinfo</code>
+functions are also gone.
+</p>
+
+<p>
+The <code>RawAuthority</code> field has been removed. The same information is
+available in the <code>Host</code> and <code>User</code> fields.
+</p>
+
+<p>
+The <code>RawPath</code> field and the <code>EncodedPath</code> method have
+been removed. The path information in rooted URLs (with a slash following the
+schema) is now available only in decoded form in the <code>Path</code> field.
+Occasionally, the encoded data may be required to obtain information that
+was lost in the decoding process. These cases must be handled by accessing
+the data the URL was built from.
+</p>
+
+<p>
+URLs with non-rooted paths, such as <code>"mailto:dev@golang.org?subject=Hi"</code>,
+are also handled differently. The <code>OpaquePath</code> boolean field has been
+removed and a new <code>Opaque</code> string field introduced to hold the encoded
+path for such URLs. In Go 1, the cited URL parses as:
+</p>
+
+<pre>
+ URL{
+ Scheme: "mailto",
+ Opaque: "dev@golang.org",
+ RawQuery: "subject=Hi",
+ }
+</pre>
+
+<p>
+A new <a href="/pkg/net/url/#URL.RequestURI"><code>RequestURI</code></a> method was
+added to <code>URL</code>.
+</p>
+
+<p>
+The <code>ParseWithReference</code> function has been renamed to <code>ParseWithFragment</code>.
+</p>
+
+<p>
+<em>Updating</em>:
+Code that uses the old fields will fail to compile and must be updated by hand.
+The semantic changes make it difficult for the fix tool to update automatically.
+</p>
+
+<h2 id="cmd_go">The go command</h2>
+
+<p>
+Go 1 introduces the <a href="/cmd/go/">go command</a>, a tool for fetching,
+building, and installing Go packages and commands. The <code>go</code> command
+does away with makefiles, instead using Go source code to find dependencies and
+determine build conditions. Most existing Go programs will no longer require
+makefiles to be built.
+</p>
+
+<p>
+See <a href="/doc/code.html">How to Write Go Code</a> for a primer on the
+<code>go</code> command and the <a href="/cmd/go/">go command documentation</a>
+for the full details.
+</p>
+
+<p>
+<em>Updating</em>:
+Projects that depend on the Go project's old makefile-based build
+infrastructure (<code>Make.pkg</code>, <code>Make.cmd</code>, and so on) should
+switch to using the <code>go</code> command for building Go code and, if
+necessary, rewrite their makefiles to perform any auxiliary build tasks.
+</p>
+
+<h2 id="cmd_cgo">The cgo command</h2>
+
+<p>
+In Go 1, the <a href="/cmd/cgo">cgo command</a>
+uses a different <code>_cgo_export.h</code>
+file, which is generated for packages containing <code>//export</code> lines.
+The <code>_cgo_export.h</code> file now begins with the C preamble comment,
+so that exported function definitions can use types defined there.
+This has the effect of compiling the preamble multiple times, so a
+package using <code>//export</code> must not put function definitions
+or variable initializations in the C preamble.
+</p>
+
+<h2 id="releases">Packaged releases</h2>
+
+<p>
+One of the most significant changes associated with Go 1 is the availability
+of prepackaged, downloadable distributions.
+They are available for many combinations of architecture and operating system
+(including Windows) and the list will grow.
+Installation details are described on the
+<a href="/doc/install">Getting Started</a> page, while
+the distributions themselves are listed on the
+<a href="http://code.google.com/p/go/downloads/list">downloads page</a>.
+
+
+</div>
+
+<div id="footer">
+Build version go1.0.1.<br>
+Except as <a href="http://code.google.com/policies.html#restrictions">noted</a>,
+the content of this page is licensed under the
+Creative Commons Attribution 3.0 License,
+and code is licensed under a <a href="/LICENSE">BSD license</a>.<br>
+<a href="/doc/tos.html">Terms of Service</a> |
+<a href="http://www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>
+</div>
+
+<script type="text/javascript">
+(function() {
+ var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
+ ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
+ var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
+})();
+</script>
+</body>
+<script type="text/javascript">
+ (function() {
+ var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
+ po.src = 'https://apis.google.com/js/plusone.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
+ })();
+</script>
+</html>
+
diff --git a/html/testdata/webkit/README b/html/testdata/webkit/README
new file mode 100644
index 0000000..9b4c2d8
--- /dev/null
+++ b/html/testdata/webkit/README
@@ -0,0 +1,28 @@
+The *.dat files in this directory are copied from The WebKit Open Source
+Project, specifically $WEBKITROOT/LayoutTests/html5lib/resources.
+WebKit is licensed under a BSD style license.
+http://webkit.org/coding/bsd-license.html says:
+
+Copyright (C) 2009 Apple Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/html/testdata/webkit/adoption01.dat b/html/testdata/webkit/adoption01.dat
new file mode 100644
index 0000000..787e1b0
--- /dev/null
+++ b/html/testdata/webkit/adoption01.dat
@@ -0,0 +1,194 @@
+#data
+<a><p></a></p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <p>
+| <a>
+
+#data
+<a>1<p>2</a>3</p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <p>
+| <a>
+| "2"
+| "3"
+
+#data
+<a>1<button>2</a>3</button>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <button>
+| <a>
+| "2"
+| "3"
+
+#data
+<a>1<b>2</a>3</b>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <b>
+| "2"
+| <b>
+| "3"
+
+#data
+<a>1<div>2<div>3</a>4</div>5</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <div>
+| <a>
+| "2"
+| <div>
+| <a>
+| "3"
+| "4"
+| "5"
+
+#data
+<table><a>1<p>2</a>3</p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <p>
+| <a>
+| "2"
+| "3"
+| <table>
+
+#data
+<b><b><a><p></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <b>
+| <a>
+| <p>
+| <a>
+
+#data
+<b><a><b><p></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <a>
+| <b>
+| <b>
+| <p>
+| <a>
+
+#data
+<a><b><b><p></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <b>
+| <b>
+| <b>
+| <p>
+| <a>
+
+#data
+<p>1<s id="A">2<b id="B">3</p>4</s>5</b>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| "1"
+| <s>
+| id="A"
+| "2"
+| <b>
+| id="B"
+| "3"
+| <s>
+| id="A"
+| <b>
+| id="B"
+| "4"
+| <b>
+| id="B"
+| "5"
+
+#data
+<table><a>1<td>2</td>3</table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "1"
+| <a>
+| "3"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "2"
+
+#data
+<table>A<td>B</td>C</table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "AC"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "B"
+
+#data
+<a><svg><tr><input></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <svg svg>
+| <svg tr>
+| <svg input>
diff --git a/html/testdata/webkit/adoption02.dat b/html/testdata/webkit/adoption02.dat
new file mode 100644
index 0000000..d18151b
--- /dev/null
+++ b/html/testdata/webkit/adoption02.dat
@@ -0,0 +1,31 @@
+#data
+<b>1<i>2<p>3</b>4
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "1"
+| <i>
+| "2"
+| <i>
+| <p>
+| <b>
+| "3"
+| "4"
+
+#data
+<a><div><style></style><address><a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <div>
+| <a>
+| <style>
+| <address>
+| <a>
+| <a>
diff --git a/html/testdata/webkit/comments01.dat b/html/testdata/webkit/comments01.dat
new file mode 100644
index 0000000..44f1876
--- /dev/null
+++ b/html/testdata/webkit/comments01.dat
@@ -0,0 +1,135 @@
+#data
+FOO<!-- BAR -->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -->
+| "BAZ"
+
+#data
+FOO<!-- BAR --!>BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -->
+| "BAZ"
+
+#data
+FOO<!-- BAR -- >BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -- >BAZ -->
+
+#data
+FOO<!-- BAR -- <QUX> -- MUX -->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -- <QUX> -- MUX -->
+| "BAZ"
+
+#data
+FOO<!-- BAR -- <QUX> -- MUX --!>BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -- <QUX> -- MUX -->
+| "BAZ"
+
+#data
+FOO<!-- BAR -- <QUX> -- MUX -- >BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- BAR -- <QUX> -- MUX -- >BAZ -->
+
+#data
+FOO<!---->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- -->
+| "BAZ"
+
+#data
+FOO<!--->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- -->
+| "BAZ"
+
+#data
+FOO<!-->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- -->
+| "BAZ"
+
+#data
+<?xml version="1.0">Hi
+#errors
+#document
+| <!-- ?xml version="1.0" -->
+| <html>
+| <head>
+| <body>
+| "Hi"
+
+#data
+<?xml version="1.0">
+#errors
+#document
+| <!-- ?xml version="1.0" -->
+| <html>
+| <head>
+| <body>
+
+#data
+<?xml version
+#errors
+#document
+| <!-- ?xml version -->
+| <html>
+| <head>
+| <body>
+
+#data
+FOO<!----->BAZ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <!-- - -->
+| "BAZ"
diff --git a/html/testdata/webkit/doctype01.dat b/html/testdata/webkit/doctype01.dat
new file mode 100644
index 0000000..ae45732
--- /dev/null
+++ b/html/testdata/webkit/doctype01.dat
@@ -0,0 +1,370 @@
+#data
+<!DOCTYPE html>Hello
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!dOctYpE HtMl>Hello
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPEhtml>Hello
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE>Hello
+#errors
+#document
+| <!DOCTYPE >
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE >Hello
+#errors
+#document
+| <!DOCTYPE >
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato >Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato taco>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato taco "ddd>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato sYstEM>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato sYstEM >Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato sYstEM ggg>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato SYSTEM taco >Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato SYSTEM 'taco"'>Hello
+#errors
+#document
+| <!DOCTYPE potato "" "taco"">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato SYSTEM "taco">Hello
+#errors
+#document
+| <!DOCTYPE potato "" "taco">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato SYSTEM "tai'co">Hello
+#errors
+#document
+| <!DOCTYPE potato "" "tai'co">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato SYSTEMtaco "ddd">Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato grass SYSTEM taco>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato pUbLIc>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato pUbLIc >Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato pUbLIcgoof>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato PUBLIC goof>Hello
+#errors
+#document
+| <!DOCTYPE potato>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato PUBLIC "go'of">Hello
+#errors
+#document
+| <!DOCTYPE potato "go'of" "">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato PUBLIC 'go'of'>Hello
+#errors
+#document
+| <!DOCTYPE potato "go" "">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato PUBLIC 'go:hh of' >Hello
+#errors
+#document
+| <!DOCTYPE potato "go:hh of" "">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE potato PUBLIC "W3C-//dfdf" SYSTEM ggg>Hello
+#errors
+#document
+| <!DOCTYPE potato "W3C-//dfdf" "">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">Hello
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE ...>Hello
+#errors
+#document
+| <!DOCTYPE ...>
+| <html>
+| <head>
+| <body>
+| "Hello"
+
+#data
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [
+<!-- internal declarations -->
+]>
+#errors
+#document
+| <!DOCTYPE root-element>
+| <html>
+| <head>
+| <body>
+| "]>"
+
+#data
+<!DOCTYPE html PUBLIC
+ "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
+ "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
+#errors
+#document
+| <!DOCTYPE html "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE HTML SYSTEM "http://www.w3.org/DTD/HTML4-strict.dtd"><body><b>Mine!</b></body>
+#errors
+#document
+| <!DOCTYPE html "" "http://www.w3.org/DTD/HTML4-strict.dtd">
+| <html>
+| <head>
+| <body>
+| <b>
+| "Mine!"
+
+#data
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"'http://www.w3.org/TR/html4/strict.dtd'>
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 4.01//EN"'http://www.w3.org/TR/html4/strict.dtd'>
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE HTML PUBLIC'-//W3C//DTD HTML 4.01//EN''http://www.w3.org/TR/html4/strict.dtd'>
+#errors
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+| <html>
+| <head>
+| <body>
diff --git a/html/testdata/webkit/entities01.dat b/html/testdata/webkit/entities01.dat
new file mode 100644
index 0000000..c8073b7
--- /dev/null
+++ b/html/testdata/webkit/entities01.dat
@@ -0,0 +1,603 @@
+#data
+FOO>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO>BAR"
+
+#data
+FOO>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO>BAR"
+
+#data
+FOO> BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO> BAR"
+
+#data
+FOO>;;BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO>;;BAR"
+
+#data
+I'm ¬it; I tell you
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "I'm ¬it; I tell you"
+
+#data
+I'm ∉ I tell you
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "I'm ∉ I tell you"
+
+#data
+FOO& BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO& BAR"
+
+#data
+FOO&<BAR>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&"
+| <bar>
+
+#data
+FOO&&&>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&&&>BAR"
+
+#data
+FOO)BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO)BAR"
+
+#data
+FOOABAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOABAR"
+
+#data
+FOOABAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOABAR"
+
+#data
+FOO&#BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&#BAR"
+
+#data
+FOO&#ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&#ZOO"
+
+#data
+FOOºR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOºR"
+
+#data
+FOO&#xZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&#xZOO"
+
+#data
+FOO&#XZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO&#XZOO"
+
+#data
+FOO)BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO)BAR"
+
+#data
+FOO䆺R
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO䆺R"
+
+#data
+FOOAZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOAZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOOxZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOxZOO"
+
+#data
+FOOyZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOyZOO"
+
+#data
+FOO€ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO€ZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOO‚ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO‚ZOO"
+
+#data
+FOOƒZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOƒZOO"
+
+#data
+FOO„ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO„ZOO"
+
+#data
+FOO…ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO…ZOO"
+
+#data
+FOO†ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO†ZOO"
+
+#data
+FOO‡ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO‡ZOO"
+
+#data
+FOOˆZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOˆZOO"
+
+#data
+FOO‰ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO‰ZOO"
+
+#data
+FOOŠZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOŠZOO"
+
+#data
+FOO‹ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO‹ZOO"
+
+#data
+FOOŒZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOŒZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOOŽZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOŽZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOO‘ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO‘ZOO"
+
+#data
+FOO’ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO’ZOO"
+
+#data
+FOO“ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO“ZOO"
+
+#data
+FOO”ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO”ZOO"
+
+#data
+FOO•ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO•ZOO"
+
+#data
+FOO–ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO–ZOO"
+
+#data
+FOO—ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO—ZOO"
+
+#data
+FOO˜ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO˜ZOO"
+
+#data
+FOO™ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO™ZOO"
+
+#data
+FOOšZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOšZOO"
+
+#data
+FOO›ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO›ZOO"
+
+#data
+FOOœZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOœZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOOžZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOžZOO"
+
+#data
+FOOŸZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOŸZOO"
+
+#data
+FOO ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO ZOO"
+
+#data
+FOO퟿ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOO􈟔ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOOZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOOZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
+
+#data
+FOO�ZOO
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO�ZOO"
diff --git a/html/testdata/webkit/entities02.dat b/html/testdata/webkit/entities02.dat
new file mode 100644
index 0000000..e2fb42a
--- /dev/null
+++ b/html/testdata/webkit/entities02.dat
@@ -0,0 +1,249 @@
+#data
+<div bar="ZZ>YY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>YY"
+
+#data
+<div bar="ZZ&"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ&"
+
+#data
+<div bar='ZZ&'></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ&"
+
+#data
+<div bar=ZZ&></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ&"
+
+#data
+<div bar="ZZ>=YY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>=YY"
+
+#data
+<div bar="ZZ>0YY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>0YY"
+
+#data
+<div bar="ZZ>9YY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>9YY"
+
+#data
+<div bar="ZZ>aYY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>aYY"
+
+#data
+<div bar="ZZ>ZYY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>ZYY"
+
+#data
+<div bar="ZZ> YY"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ> YY"
+
+#data
+<div bar="ZZ>"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>"
+
+#data
+<div bar='ZZ>'></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>"
+
+#data
+<div bar=ZZ>></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ>"
+
+#data
+<div bar="ZZ£_id=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ£_id=23"
+
+#data
+<div bar="ZZ&prod_id=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ&prod_id=23"
+
+#data
+<div bar="ZZ£_id=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ£_id=23"
+
+#data
+<div bar="ZZ∏_id=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ∏_id=23"
+
+#data
+<div bar="ZZ£=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ£=23"
+
+#data
+<div bar="ZZ&prod=23"></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| bar="ZZ&prod=23"
+
+#data
+<div>ZZ£_id=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ£_id=23"
+
+#data
+<div>ZZ&prod_id=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ&prod_id=23"
+
+#data
+<div>ZZ£_id=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ£_id=23"
+
+#data
+<div>ZZ∏_id=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ∏_id=23"
+
+#data
+<div>ZZ£=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ£=23"
+
+#data
+<div>ZZ&prod=23</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "ZZ&prod=23"
diff --git a/html/testdata/webkit/html5test-com.dat b/html/testdata/webkit/html5test-com.dat
new file mode 100644
index 0000000..d7cb71d
--- /dev/null
+++ b/html/testdata/webkit/html5test-com.dat
@@ -0,0 +1,246 @@
+#data
+<div<div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div<div>
+
+#data
+<div foo<bar=''>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| foo<bar=""
+
+#data
+<div foo=`bar`>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| foo="`bar`"
+
+#data
+<div \"foo=''>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| \"foo=""
+
+#data
+<a href='\nbar'></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| href="\nbar"
+
+#data
+<!DOCTYPE html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+⟨⟩
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "⟨⟩"
+
+#data
+'
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "'"
+
+#data
+ⅈ
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "ⅈ"
+
+#data
+𝕂
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "𝕂"
+
+#data
+∉
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "∉"
+
+#data
+<?import namespace="foo" implementation="#bar">
+#errors
+#document
+| <!-- ?import namespace="foo" implementation="#bar" -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!--foo--bar-->
+#errors
+#document
+| <!-- foo--bar -->
+| <html>
+| <head>
+| <body>
+
+#data
+<![CDATA[x]]>
+#errors
+#document
+| <!-- [CDATA[x]] -->
+| <html>
+| <head>
+| <body>
+
+#data
+<textarea><!--</textarea>--></textarea>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<!--"
+| "-->"
+
+#data
+<textarea><!--</textarea>-->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<!--"
+| "-->"
+
+#data
+<style><!--</style>--></style>
+#errors
+#document
+| <html>
+| <head>
+| <style>
+| "<!--"
+| <body>
+| "-->"
+
+#data
+<style><!--</style>-->
+#errors
+#document
+| <html>
+| <head>
+| <style>
+| "<!--"
+| <body>
+| "-->"
+
+#data
+<ul><li>A </li> <li>B</li></ul>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <ul>
+| <li>
+| "A "
+| " "
+| <li>
+| "B"
+
+#data
+<table><form><input type=hidden><input></form><div></div></table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <input>
+| <div>
+| <table>
+| <form>
+| <input>
+| type="hidden"
+
+#data
+<i>A<b>B<p></i>C</b>D
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <i>
+| "A"
+| <b>
+| "B"
+| <b>
+| <p>
+| <b>
+| <i>
+| "C"
+| "D"
+
+#data
+<div></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+
+#data
+<svg></svg>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<math></math>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
diff --git a/html/testdata/webkit/inbody01.dat b/html/testdata/webkit/inbody01.dat
new file mode 100644
index 0000000..3f2bd37
--- /dev/null
+++ b/html/testdata/webkit/inbody01.dat
@@ -0,0 +1,43 @@
+#data
+<button>1</foo>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <button>
+| "1"
+
+#data
+<foo>1<p>2</foo>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| "1"
+| <p>
+| "2"
+
+#data
+<dd>1</foo>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <dd>
+| "1"
+
+#data
+<foo>1<dd>2</foo>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| "1"
+| <dd>
+| "2"
diff --git a/html/testdata/webkit/isindex.dat b/html/testdata/webkit/isindex.dat
new file mode 100644
index 0000000..88325ff
--- /dev/null
+++ b/html/testdata/webkit/isindex.dat
@@ -0,0 +1,40 @@
+#data
+<isindex>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <form>
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| <hr>
+
+#data
+<isindex name="A" action="B" prompt="C" foo="D">
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <form>
+| action="B"
+| <hr>
+| <label>
+| "C"
+| <input>
+| foo="D"
+| name="isindex"
+| <hr>
+
+#data
+<form><isindex>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <form>
diff --git a/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat b/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat
new file mode 100644
index 0000000..a5ebb1e
--- /dev/null
+++ b/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat
Binary files differ
diff --git a/html/testdata/webkit/pending-spec-changes.dat b/html/testdata/webkit/pending-spec-changes.dat
new file mode 100644
index 0000000..5a92084
--- /dev/null
+++ b/html/testdata/webkit/pending-spec-changes.dat
@@ -0,0 +1,52 @@
+#data
+<input type="hidden"><frameset>
+#errors
+21: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+31: “frameset” start tag seen.
+31: End of file seen and there were open elements.
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><table><caption><svg>foo</table>bar
+#errors
+47: End tag “table” did not match the name of the current open element (“svg”).
+47: “table” closed but “caption” was still open.
+47: End tag “table” seen, but there were open elements.
+36: Unclosed element “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <svg svg>
+| "foo"
+| "bar"
+
+#data
+<table><tr><td><svg><desc><td></desc><circle>
+#errors
+7: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+30: A table cell was implicitly closed, but there were open elements.
+26: Unclosed element “desc”.
+20: Unclosed element “svg”.
+37: Stray end tag “desc”.
+45: End of file seen and there were open elements.
+45: Unclosed element “circle”.
+7: Unclosed element “table”.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg desc>
+| <td>
+| <circle>
diff --git a/html/testdata/webkit/plain-text-unsafe.dat b/html/testdata/webkit/plain-text-unsafe.dat
new file mode 100644
index 0000000..04cc11f
--- /dev/null
+++ b/html/testdata/webkit/plain-text-unsafe.dat
Binary files differ
diff --git a/html/testdata/webkit/scriptdata01.dat b/html/testdata/webkit/scriptdata01.dat
new file mode 100644
index 0000000..76b67f4
--- /dev/null
+++ b/html/testdata/webkit/scriptdata01.dat
@@ -0,0 +1,308 @@
+#data
+FOO<script>'Hello'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'Hello'"
+| "BAR"
+
+#data
+FOO<script></script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "BAR"
+
+#data
+FOO<script></script >BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "BAR"
+
+#data
+FOO<script></script/>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "BAR"
+
+#data
+FOO<script></script/ >BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "BAR"
+
+#data
+FOO<script type="text/plain"></scriptx>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "</scriptx>BAR"
+
+#data
+FOO<script></script foo=">" dd>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "BAR"
+
+#data
+FOO<script>'<'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<'"
+| "BAR"
+
+#data
+FOO<script>'<!'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!'"
+| "BAR"
+
+#data
+FOO<script>'<!-'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-'"
+| "BAR"
+
+#data
+FOO<script>'<!--'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!--'"
+| "BAR"
+
+#data
+FOO<script>'<!---'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!---'"
+| "BAR"
+
+#data
+FOO<script>'<!-->'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-->'"
+| "BAR"
+
+#data
+FOO<script>'<!-->'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-->'"
+| "BAR"
+
+#data
+FOO<script>'<!-- potato'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-- potato'"
+| "BAR"
+
+#data
+FOO<script>'<!-- <sCrIpt'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-- <sCrIpt'"
+| "BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt>'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt>'</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt> -'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt> -'</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt> --'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt> --'</script>BAR"
+
+#data
+FOO<script>'<!-- <sCrIpt> -->'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| "'<!-- <sCrIpt> -->'"
+| "BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt> --!>'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt> --!>'</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt> -- >'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt> -- >'</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt '</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt '</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt/'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt/'</script>BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt\'</script>BAR
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt\'"
+| "BAR"
+
+#data
+FOO<script type="text/plain">'<!-- <sCrIpt/'</script>BAR</script>QUX
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "FOO"
+| <script>
+| type="text/plain"
+| "'<!-- <sCrIpt/'</script>BAR"
+| "QUX"
diff --git a/html/testdata/webkit/scripted/adoption01.dat b/html/testdata/webkit/scripted/adoption01.dat
new file mode 100644
index 0000000..4e08d0e
--- /dev/null
+++ b/html/testdata/webkit/scripted/adoption01.dat
@@ -0,0 +1,15 @@
+#data
+<p><b id="A"><script>document.getElementById("A").id = "B"</script></p>TEXT</b>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| id="B"
+| <script>
+| "document.getElementById("A").id = "B""
+| <b>
+| id="A"
+| "TEXT"
diff --git a/html/testdata/webkit/scripted/webkit01.dat b/html/testdata/webkit/scripted/webkit01.dat
new file mode 100644
index 0000000..ef4a41c
--- /dev/null
+++ b/html/testdata/webkit/scripted/webkit01.dat
@@ -0,0 +1,28 @@
+#data
+1<script>document.write("2")</script>3
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "1"
+| <script>
+| "document.write("2")"
+| "23"
+
+#data
+1<script>document.write("<script>document.write('2')</scr"+ "ipt><script>document.write('3')</scr" + "ipt>")</script>4
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "1"
+| <script>
+| "document.write("<script>document.write('2')</scr"+ "ipt><script>document.write('3')</scr" + "ipt>")"
+| <script>
+| "document.write('2')"
+| "2"
+| <script>
+| "document.write('3')"
+| "34"
diff --git a/html/testdata/webkit/tables01.dat b/html/testdata/webkit/tables01.dat
new file mode 100644
index 0000000..c4b47e4
--- /dev/null
+++ b/html/testdata/webkit/tables01.dat
@@ -0,0 +1,212 @@
+#data
+<table><th>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <th>
+
+#data
+<table><td>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><col foo='bar'>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <colgroup>
+| <col>
+| foo="bar"
+
+#data
+<table><colgroup></html>foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "foo"
+| <table>
+| <colgroup>
+
+#data
+<table></table><p>foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <p>
+| "foo"
+
+#data
+<table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr><td>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><select><option>3</select></table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| "3"
+| <table>
+
+#data
+<table><select><table></table></select></table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <table>
+| <table>
+
+#data
+<table><select></table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <table>
+
+#data
+<table><select><option>A<tr><td>B</td></tr></table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| "A"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "B"
+
+#data
+<table><td></body></caption></col></colgroup></html>foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "foo"
+
+#data
+<table><td>A</table>B
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "A"
+| "B"
+
+#data
+<table><tr><caption>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <caption>
+
+#data
+<table><tr></body></caption></col></colgroup></html></td></th><td>foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "foo"
+
+#data
+<table><td><tr>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <tr>
+
+#data
+<table><td><button><td>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <button>
+| <td>
+
+#data
+<table><tr><td><svg><desc><td>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg desc>
+| <td>
diff --git a/html/testdata/webkit/tests1.dat b/html/testdata/webkit/tests1.dat
new file mode 100644
index 0000000..cbf8bdd
--- /dev/null
+++ b/html/testdata/webkit/tests1.dat
@@ -0,0 +1,1952 @@
+#data
+Test
+#errors
+Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "Test"
+
+#data
+<p>One<p>Two
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| "One"
+| <p>
+| "Two"
+
+#data
+Line1<br>Line2<br>Line3<br>Line4
+#errors
+Line: 1 Col: 5 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "Line1"
+| <br>
+| "Line2"
+| <br>
+| "Line3"
+| <br>
+| "Line4"
+
+#data
+<html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<head>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<body>
+#errors
+Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head></head>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head></head><body>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head></head><body></body>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head><body></body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head></body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+Line: 1 Col: 19 Unexpected end tag (body).
+Line: 1 Col: 26 Unexpected end tag (html).
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><head><body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html><body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<head></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end tag (html). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+</head>
+#errors
+Line: 1 Col: 7 Unexpected end tag (head). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+</body>
+#errors
+Line: 1 Col: 7 Unexpected end tag (body). Expected DOCTYPE.
+Line: 1 Col: 7 Unexpected end tag (body) after the (implied) root element.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+</html>
+#errors
+Line: 1 Col: 7 Unexpected end tag (html). Expected DOCTYPE.
+Line: 1 Col: 7 Unexpected end tag (html) after the (implied) root element.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<b><table><td><i></table>
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 25 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 25 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <i>
+
+#data
+<b><table><td></b><i></table>X
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 18 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 29 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 30 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <i>
+| "X"
+
+#data
+<h1>Hello<h2>World
+#errors
+4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+13: Heading cannot be a child of another heading.
+18: End of file seen and there were open elements.
+#document
+| <html>
+| <head>
+| <body>
+| <h1>
+| "Hello"
+| <h2>
+| "World"
+
+#data
+<a><p>X<a>Y</a>Z</p></a>
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 10 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 10 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 24 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <p>
+| <a>
+| "X"
+| <a>
+| "Y"
+| "Z"
+
+#data
+<b><button>foo</b>bar
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 15 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <button>
+| <b>
+| "foo"
+| "bar"
+
+#data
+<!DOCTYPE html><span><button>foo</span>bar
+#errors
+39: End tag “span” seen but there were unclosed elements.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <span>
+| <button>
+| "foobar"
+
+#data
+<p><b><div><marquee></p></b></div>X
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end tag (p). Ignored.
+Line: 1 Col: 24 Unexpected end tag (p). Ignored.
+Line: 1 Col: 28 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 34 End tag (div) seen too early. Expected other end tag.
+Line: 1 Col: 35 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| <div>
+| <b>
+| <marquee>
+| <p>
+| "X"
+
+#data
+<script><div></script></div><title><p></title><p><p>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 28 Unexpected end tag (div). Ignored.
+#document
+| <html>
+| <head>
+| <script>
+| "<div>"
+| <title>
+| "<p>"
+| <body>
+| <p>
+| <p>
+
+#data
+<!--><div>--<!-->
+#errors
+Line: 1 Col: 5 Incorrect comment.
+Line: 1 Col: 10 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 17 Incorrect comment.
+Line: 1 Col: 17 Expected closing tag. Unexpected end of file.
+#document
+| <!-- -->
+| <html>
+| <head>
+| <body>
+| <div>
+| "--"
+| <!-- -->
+
+#data
+<p><hr></p>
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end tag (p). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <hr>
+| <p>
+
+#data
+<select><b><option><select><option></b></select>X
+#errors
+Line: 1 Col: 8 Unexpected start tag (select). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected start tag token (b) in the select phase. Ignored.
+Line: 1 Col: 27 Unexpected select start tag in the select phase treated as select end tag.
+Line: 1 Col: 39 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 48 Unexpected end tag (select). Ignored.
+Line: 1 Col: 49 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| <option>
+| "X"
+
+#data
+<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 40 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 43 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 43 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 43 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 51 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 63 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 64 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <a>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <a>
+| <table>
+| <a>
+| <a>
+| <b>
+| "X"
+| "C"
+| <a>
+| "Y"
+
+#data
+<a X>0<b>1<a Y>2
+#errors
+Line: 1 Col: 5 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 15 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 16 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| x=""
+| "0"
+| <b>
+| "1"
+| <b>
+| <a>
+| y=""
+| "2"
+
+#data
+<!-----><font><div>hello<table>excite!<b>me!<th><i>please!</tr><!--X-->
+#errors
+Line: 1 Col: 7 Unexpected '-' after '--' found in comment.
+Line: 1 Col: 14 Unexpected start tag (font). Expected DOCTYPE.
+Line: 1 Col: 38 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 41 Unexpected start tag (b) in table context caused voodoo mode.
+Line: 1 Col: 48 Unexpected implied end tag (b) in the table phase.
+Line: 1 Col: 48 Unexpected table cell start tag (th) in the table body phase.
+Line: 1 Col: 63 Got table cell end tag (th) while required end tags are missing.
+Line: 1 Col: 71 Unexpected end of file. Expected table content.
+#document
+| <!-- - -->
+| <html>
+| <head>
+| <body>
+| <font>
+| <div>
+| "helloexcite!"
+| <b>
+| "me!"
+| <table>
+| <tbody>
+| <tr>
+| <th>
+| <i>
+| "please!"
+| <!-- X -->
+
+#data
+<!DOCTYPE html><li>hello<li>world<ul>how<li>do</ul>you</body><!--do-->
+#errors
+Line: 1 Col: 61 Unexpected end tag (li). Missing end tag (body).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <li>
+| "hello"
+| <li>
+| "world"
+| <ul>
+| "how"
+| <li>
+| "do"
+| "you"
+| <!-- do -->
+
+#data
+<!DOCTYPE html>A<option>B<optgroup>C<select>D</option>E
+#errors
+Line: 1 Col: 54 Unexpected end tag (option) in the select phase. Ignored.
+Line: 1 Col: 55 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "A"
+| <option>
+| "B"
+| <optgroup>
+| "C"
+| <select>
+| "DE"
+
+#data
+<
+#errors
+Line: 1 Col: 1 Expected tag name. Got something else instead
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "<"
+
+#data
+<#
+#errors
+Line: 1 Col: 1 Expected tag name. Got something else instead
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "<#"
+
+#data
+</
+#errors
+Line: 1 Col: 2 Expected closing tag. Unexpected end of file.
+Line: 1 Col: 2 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "</"
+
+#data
+</#
+#errors
+Line: 1 Col: 2 Expected closing tag. Unexpected character '#' found.
+Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- # -->
+| <html>
+| <head>
+| <body>
+
+#data
+<?
+#errors
+Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.)
+Line: 1 Col: 2 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- ? -->
+| <html>
+| <head>
+| <body>
+
+#data
+<?#
+#errors
+Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.)
+Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- ?# -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!
+#errors
+Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found.
+Line: 1 Col: 2 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!#
+#errors
+Line: 1 Col: 3 Expected '--' or 'DOCTYPE'. Not found.
+Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- # -->
+| <html>
+| <head>
+| <body>
+
+#data
+<?COMMENT?>
+#errors
+Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.)
+Line: 1 Col: 11 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- ?COMMENT? -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!COMMENT>
+#errors
+Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found.
+Line: 1 Col: 10 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- COMMENT -->
+| <html>
+| <head>
+| <body>
+
+#data
+</ COMMENT >
+#errors
+Line: 1 Col: 2 Expected closing tag. Unexpected character ' ' found.
+Line: 1 Col: 12 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- COMMENT -->
+| <html>
+| <head>
+| <body>
+
+#data
+<?COM--MENT?>
+#errors
+Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.)
+Line: 1 Col: 13 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- ?COM--MENT? -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!COM--MENT>
+#errors
+Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found.
+Line: 1 Col: 12 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- COM--MENT -->
+| <html>
+| <head>
+| <body>
+
+#data
+</ COM--MENT >
+#errors
+Line: 1 Col: 2 Expected closing tag. Unexpected character ' ' found.
+Line: 1 Col: 14 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- COM--MENT -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><style> EOF
+#errors
+Line: 1 Col: 26 Unexpected end of file. Expected end tag (style).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| " EOF"
+| <body>
+
+#data
+<!DOCTYPE html><script> <!-- </script> --> </script> EOF
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| " <!-- "
+| " "
+| <body>
+| "--> EOF"
+
+#data
+<b><p></b>TEST
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 10 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <p>
+| <b>
+| "TEST"
+
+#data
+<p id=a><b><p id=b></b>TEST
+#errors
+Line: 1 Col: 8 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 19 Unexpected end tag (p). Ignored.
+Line: 1 Col: 23 End tag (b) violates step 1, paragraph 2 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| id="a"
+| <b>
+| <p>
+| id="b"
+| "TEST"
+
+#data
+<b id=a><p><b id=b></p></b>TEST
+#errors
+Line: 1 Col: 8 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end tag (p). Ignored.
+Line: 1 Col: 27 End tag (b) violates step 1, paragraph 2 of the adoption agency algorithm.
+Line: 1 Col: 31 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| id="a"
+| <p>
+| <b>
+| id="b"
+| "TEST"
+
+#data
+<!DOCTYPE html><title>U-test</title><body><div><p>Test<u></p></div></body>
+#errors
+Line: 1 Col: 61 Unexpected end tag (p). Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "U-test"
+| <body>
+| <div>
+| <p>
+| "Test"
+| <u>
+
+#data
+<!DOCTYPE html><font><table></font></table></font>
+#errors
+Line: 1 Col: 35 Unexpected end tag (font) in table context caused voodoo mode.
+Line: 1 Col: 35 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <font>
+| <table>
+
+#data
+<font><p>hello<b>cruel</font>world
+#errors
+Line: 1 Col: 6 Unexpected start tag (font). Expected DOCTYPE.
+Line: 1 Col: 29 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 29 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 34 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <font>
+| <p>
+| <font>
+| "hello"
+| <b>
+| "cruel"
+| <b>
+| "world"
+
+#data
+<b>Test</i>Test
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 11 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 15 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "TestTest"
+
+#data
+<b>A<cite>B<div>C
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 17 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "A"
+| <cite>
+| "B"
+| <div>
+| "C"
+
+#data
+<b>A<cite>B<div>C</cite>D
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 24 Unexpected end tag (cite). Ignored.
+Line: 1 Col: 25 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "A"
+| <cite>
+| "B"
+| <div>
+| "CD"
+
+#data
+<b>A<cite>B<div>C</b>D
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 21 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 22 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "A"
+| <cite>
+| "B"
+| <div>
+| <b>
+| "C"
+| "D"
+
+#data
+
+#errors
+Line: 1 Col: 0 Unexpected End of file. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<DIV>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 5 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+
+#data
+<DIV> abc
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 9 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc"
+
+#data
+<DIV> abc <B>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 13 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+
+#data
+<DIV> abc <B> def
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 17 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def"
+
+#data
+<DIV> abc <B> def <I>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 21 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+
+#data
+<DIV> abc <B> def <I> ghi
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 25 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi"
+
+#data
+<DIV> abc <B> def <I> ghi <P>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 29 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <p>
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 33 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <p>
+| " jkl"
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 38 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <b>
+| " jkl "
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B> mno
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 42 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <b>
+| " jkl "
+| " mno"
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 47 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <i>
+| <b>
+| " jkl "
+| " mno "
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 51 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <i>
+| <b>
+| " jkl "
+| " mno "
+| " pqr"
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P>
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 56 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <i>
+| <b>
+| " jkl "
+| " mno "
+| " pqr "
+
+#data
+<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P> stu
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 60 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| " abc "
+| <b>
+| " def "
+| <i>
+| " ghi "
+| <i>
+| <p>
+| <i>
+| <b>
+| " jkl "
+| " mno "
+| " pqr "
+| " stu"
+
+#data
+<test attribute---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
+#errors
+Line: 1 Col: 1040 Unexpected start tag (test). Expected DOCTYPE.
+Line: 1 Col: 1040 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <test>
+| attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------=""
+
+#data
+<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe
+#errors
+Line: 1 Col: 15 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 39 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 39 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 39 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 45 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 68 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 71 Expected closing tag. Unexpected end of file.
+
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| href="blah"
+| "aba"
+| <a>
+| href="foo"
+| "br"
+| <a>
+| href="foo"
+| "x"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <a>
+| href="foo"
+| "aoe"
+
+#data
+<a href="blah">aba<table><tr><td><a href="foo">br</td></tr>x</table>aoe
+#errors
+Line: 1 Col: 15 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 54 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 60 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 71 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| href="blah"
+| "abax"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <a>
+| href="foo"
+| "br"
+| "aoe"
+
+#data
+<table><a href="blah">aba<tr><td><a href="foo">br</td></tr>x</table>aoe
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 29 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 54 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 68 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 71 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| href="blah"
+| "aba"
+| <a>
+| href="blah"
+| "x"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <a>
+| href="foo"
+| "br"
+| <a>
+| href="blah"
+| "aoe"
+
+#data
+<a href=a>aa<marquee>aa<a href=b>bb</marquee>aa
+#errors
+Line: 1 Col: 10 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 45 End tag (marquee) seen too early. Expected other end tag.
+Line: 1 Col: 47 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| href="a"
+| "aa"
+| <marquee>
+| "aa"
+| <a>
+| href="b"
+| "bb"
+| "aa"
+
+#data
+<wbr><strike><code></strike><code><strike></code>
+#errors
+Line: 1 Col: 5 Unexpected start tag (wbr). Expected DOCTYPE.
+Line: 1 Col: 28 End tag (strike) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 49 Unexpected end tag (code). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <wbr>
+| <strike>
+| <code>
+| <code>
+| <code>
+| <strike>
+
+#data
+<!DOCTYPE html><spacer>foo
+#errors
+26: End of file seen and there were open elements.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <spacer>
+| "foo"
+
+#data
+<title><meta></title><link><title><meta></title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| "<meta>"
+| <link>
+| <title>
+| "<meta>"
+| <body>
+
+#data
+<style><!--</style><meta><script>--><link></script>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 51 Unexpected end of file. Expected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| "<!--"
+| <meta>
+| <script>
+| "--><link>"
+| <body>
+
+#data
+<head><meta></head><link>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 25 Unexpected start tag (link) that can be in head. Moved.
+#document
+| <html>
+| <head>
+| <meta>
+| <link>
+| <body>
+
+#data
+<table><tr><tr><td><td><span><th><span>X</table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 33 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 48 Got table cell end tag (th) while required end tags are missing.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <tr>
+| <td>
+| <td>
+| <span>
+| <th>
+| <span>
+| "X"
+
+#data
+<body><body><base><link><meta><title><p></title><body><p></body>
+#errors
+Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected start tag (body).
+Line: 1 Col: 54 Unexpected start tag (body).
+Line: 1 Col: 64 Unexpected end tag (p). Missing end tag (body).
+#document
+| <html>
+| <head>
+| <body>
+| <base>
+| <link>
+| <meta>
+| <title>
+| "<p>"
+| <p>
+
+#data
+<textarea><p></textarea>
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<p>"
+
+#data
+<p><image></p>
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 10 Unexpected start tag (image). Treated as img.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <img>
+
+#data
+<a><table><a></table><p><a><div><a>
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 13 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 13 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 21 Unexpected end tag (table). Expected end tag (a).
+Line: 1 Col: 27 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 27 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm.
+Line: 1 Col: 32 Unexpected end tag (p). Ignored.
+Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 35 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm.
+Line: 1 Col: 35 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <a>
+| <table>
+| <p>
+| <a>
+| <div>
+| <a>
+
+#data
+<head></p><meta><p>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 10 Unexpected end tag (p). Ignored.
+#document
+| <html>
+| <head>
+| <meta>
+| <body>
+| <p>
+
+#data
+<head></html><meta><p>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 19 Unexpected start tag (meta).
+#document
+| <html>
+| <head>
+| <body>
+| <meta>
+| <p>
+
+#data
+<b><table><td><i></table>
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 25 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 25 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <i>
+
+#data
+<b><table><td></b><i></table>
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 18 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 29 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 29 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <i>
+
+#data
+<h1><h2>
+#errors
+4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+8: Heading cannot be a child of another heading.
+8: End of file seen and there were open elements.
+#document
+| <html>
+| <head>
+| <body>
+| <h1>
+| <h2>
+
+#data
+<a><p><a></a></p></a>
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 9 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 9 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 21 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <p>
+| <a>
+| <a>
+
+#data
+<b><button></b></button></b>
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 15 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <button>
+| <b>
+
+#data
+<p><b><div><marquee></p></b></div>
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end tag (p). Ignored.
+Line: 1 Col: 24 Unexpected end tag (p). Ignored.
+Line: 1 Col: 28 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 34 End tag (div) seen too early. Expected other end tag.
+Line: 1 Col: 34 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| <div>
+| <b>
+| <marquee>
+| <p>
+
+#data
+<script></script></div><title></title><p><p>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end tag (div). Ignored.
+#document
+| <html>
+| <head>
+| <script>
+| <title>
+| <body>
+| <p>
+| <p>
+
+#data
+<p><hr></p>
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end tag (p). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <hr>
+| <p>
+
+#data
+<select><b><option><select><option></b></select>
+#errors
+Line: 1 Col: 8 Unexpected start tag (select). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected start tag token (b) in the select phase. Ignored.
+Line: 1 Col: 27 Unexpected select start tag in the select phase treated as select end tag.
+Line: 1 Col: 39 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 48 Unexpected end tag (select). Ignored.
+Line: 1 Col: 48 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| <option>
+
+#data
+<html><head><title></title><body></body></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| <body>
+
+#data
+<a><table><td><a><table></table><a></tr><a></table><a>
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 40 Got table cell end tag (td) while required end tags are missing.
+Line: 1 Col: 43 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 43 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 43 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 51 Unexpected implied end tag (a) in the table phase.
+Line: 1 Col: 54 Unexpected start tag (a) implies end tag (a).
+Line: 1 Col: 54 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm.
+Line: 1 Col: 54 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <a>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <a>
+| <table>
+| <a>
+| <a>
+
+#data
+<ul><li></li><div><li></div><li><li><div><li><address><li><b><em></b><li></ul>
+#errors
+Line: 1 Col: 4 Unexpected start tag (ul). Expected DOCTYPE.
+Line: 1 Col: 45 Missing end tag (div, li).
+Line: 1 Col: 58 Missing end tag (address, li).
+Line: 1 Col: 69 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+#document
+| <html>
+| <head>
+| <body>
+| <ul>
+| <li>
+| <div>
+| <li>
+| <li>
+| <li>
+| <div>
+| <li>
+| <address>
+| <li>
+| <b>
+| <em>
+| <li>
+
+#data
+<ul><li><ul></li><li>a</li></ul></li></ul>
+#errors
+XXX: fix me
+#document
+| <html>
+| <head>
+| <body>
+| <ul>
+| <li>
+| <ul>
+| <li>
+| "a"
+
+#data
+<frameset><frame><frameset><frame></frameset><noframes></noframes></frameset>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <frameset>
+| <frame>
+| <frameset>
+| <frame>
+| <noframes>
+
+#data
+<h1><table><td><h3></table><h3></h1>
+#errors
+4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+15: “td” start tag in table body.
+27: Unclosed elements.
+31: Heading cannot be a child of another heading.
+36: End tag “h1” seen but there were unclosed elements.
+#document
+| <html>
+| <head>
+| <body>
+| <h1>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <h3>
+| <h3>
+
+#data
+<table><colgroup><col><colgroup><col><col><col><colgroup><col><col><thead><tr><td></table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <colgroup>
+| <col>
+| <colgroup>
+| <col>
+| <col>
+| <col>
+| <colgroup>
+| <col>
+| <col>
+| <thead>
+| <tr>
+| <td>
+
+#data
+<table><col><tbody><col><tr><col><td><col></table><col>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 37 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 55 Unexpected start tag col. Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <colgroup>
+| <col>
+| <tbody>
+| <colgroup>
+| <col>
+| <tbody>
+| <tr>
+| <colgroup>
+| <col>
+| <tbody>
+| <tr>
+| <td>
+| <colgroup>
+| <col>
+
+#data
+<table><colgroup><tbody><colgroup><tr><colgroup><td><colgroup></table><colgroup>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 52 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 80 Unexpected start tag colgroup. Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <colgroup>
+| <tbody>
+| <colgroup>
+| <tbody>
+| <tr>
+| <colgroup>
+| <tbody>
+| <tr>
+| <td>
+| <colgroup>
+
+#data
+</strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea>
+#errors
+Line: 1 Col: 9 Unexpected end tag (strong). Expected DOCTYPE.
+Line: 1 Col: 9 Unexpected end tag (strong) after the (implied) root element.
+Line: 1 Col: 13 Unexpected end tag (b) after the (implied) root element.
+Line: 1 Col: 18 Unexpected end tag (em) after the (implied) root element.
+Line: 1 Col: 22 Unexpected end tag (i) after the (implied) root element.
+Line: 1 Col: 26 Unexpected end tag (u) after the (implied) root element.
+Line: 1 Col: 35 Unexpected end tag (strike) after the (implied) root element.
+Line: 1 Col: 39 Unexpected end tag (s) after the (implied) root element.
+Line: 1 Col: 47 Unexpected end tag (blink) after the (implied) root element.
+Line: 1 Col: 52 Unexpected end tag (tt) after the (implied) root element.
+Line: 1 Col: 58 Unexpected end tag (pre) after the (implied) root element.
+Line: 1 Col: 64 Unexpected end tag (big) after the (implied) root element.
+Line: 1 Col: 72 Unexpected end tag (small) after the (implied) root element.
+Line: 1 Col: 79 Unexpected end tag (font) after the (implied) root element.
+Line: 1 Col: 88 Unexpected end tag (select) after the (implied) root element.
+Line: 1 Col: 93 Unexpected end tag (h1) after the (implied) root element.
+Line: 1 Col: 98 Unexpected end tag (h2) after the (implied) root element.
+Line: 1 Col: 103 Unexpected end tag (h3) after the (implied) root element.
+Line: 1 Col: 108 Unexpected end tag (h4) after the (implied) root element.
+Line: 1 Col: 113 Unexpected end tag (h5) after the (implied) root element.
+Line: 1 Col: 118 Unexpected end tag (h6) after the (implied) root element.
+Line: 1 Col: 125 Unexpected end tag (body) after the (implied) root element.
+Line: 1 Col: 130 Unexpected end tag (br). Treated as br element.
+Line: 1 Col: 134 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 140 This element (img) has no end tag.
+Line: 1 Col: 148 Unexpected end tag (title). Ignored.
+Line: 1 Col: 155 Unexpected end tag (span). Ignored.
+Line: 1 Col: 163 Unexpected end tag (style). Ignored.
+Line: 1 Col: 172 Unexpected end tag (script). Ignored.
+Line: 1 Col: 180 Unexpected end tag (table). Ignored.
+Line: 1 Col: 185 Unexpected end tag (th). Ignored.
+Line: 1 Col: 190 Unexpected end tag (td). Ignored.
+Line: 1 Col: 195 Unexpected end tag (tr). Ignored.
+Line: 1 Col: 203 This element (frame) has no end tag.
+Line: 1 Col: 210 This element (area) has no end tag.
+Line: 1 Col: 217 Unexpected end tag (link). Ignored.
+Line: 1 Col: 225 This element (param) has no end tag.
+Line: 1 Col: 230 This element (hr) has no end tag.
+Line: 1 Col: 238 This element (input) has no end tag.
+Line: 1 Col: 244 Unexpected end tag (col). Ignored.
+Line: 1 Col: 251 Unexpected end tag (base). Ignored.
+Line: 1 Col: 258 Unexpected end tag (meta). Ignored.
+Line: 1 Col: 269 This element (basefont) has no end tag.
+Line: 1 Col: 279 This element (bgsound) has no end tag.
+Line: 1 Col: 287 This element (embed) has no end tag.
+Line: 1 Col: 296 This element (spacer) has no end tag.
+Line: 1 Col: 300 Unexpected end tag (p). Ignored.
+Line: 1 Col: 305 End tag (dd) seen too early. Expected other end tag.
+Line: 1 Col: 310 End tag (dt) seen too early. Expected other end tag.
+Line: 1 Col: 320 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 331 Unexpected end tag (colgroup). Ignored.
+Line: 1 Col: 339 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 347 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 355 Unexpected end tag (thead). Ignored.
+Line: 1 Col: 365 End tag (address) seen too early. Expected other end tag.
+Line: 1 Col: 378 End tag (blockquote) seen too early. Expected other end tag.
+Line: 1 Col: 387 End tag (center) seen too early. Expected other end tag.
+Line: 1 Col: 393 Unexpected end tag (dir). Ignored.
+Line: 1 Col: 399 End tag (div) seen too early. Expected other end tag.
+Line: 1 Col: 404 End tag (dl) seen too early. Expected other end tag.
+Line: 1 Col: 415 End tag (fieldset) seen too early. Expected other end tag.
+Line: 1 Col: 425 End tag (listing) seen too early. Expected other end tag.
+Line: 1 Col: 432 End tag (menu) seen too early. Expected other end tag.
+Line: 1 Col: 437 End tag (ol) seen too early. Expected other end tag.
+Line: 1 Col: 442 End tag (ul) seen too early. Expected other end tag.
+Line: 1 Col: 447 End tag (li) seen too early. Expected other end tag.
+Line: 1 Col: 454 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 460 This element (wbr) has no end tag.
+Line: 1 Col: 476 End tag (button) seen too early. Expected other end tag.
+Line: 1 Col: 486 End tag (marquee) seen too early. Expected other end tag.
+Line: 1 Col: 495 End tag (object) seen too early. Expected other end tag.
+Line: 1 Col: 513 Unexpected end tag (html). Ignored.
+Line: 1 Col: 513 Unexpected end tag (frameset). Ignored.
+Line: 1 Col: 520 Unexpected end tag (head). Ignored.
+Line: 1 Col: 529 Unexpected end tag (iframe). Ignored.
+Line: 1 Col: 537 This element (image) has no end tag.
+Line: 1 Col: 547 This element (isindex) has no end tag.
+Line: 1 Col: 557 Unexpected end tag (noembed). Ignored.
+Line: 1 Col: 568 Unexpected end tag (noframes). Ignored.
+Line: 1 Col: 579 Unexpected end tag (noscript). Ignored.
+Line: 1 Col: 590 Unexpected end tag (optgroup). Ignored.
+Line: 1 Col: 599 Unexpected end tag (option). Ignored.
+Line: 1 Col: 611 Unexpected end tag (plaintext). Ignored.
+Line: 1 Col: 622 Unexpected end tag (textarea). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <br>
+| <p>
+
+#data
+<table><tr></strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end tag (strong) in table context caused voodoo mode.
+Line: 1 Col: 20 End tag (strong) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 24 Unexpected end tag (b) in table context caused voodoo mode.
+Line: 1 Col: 24 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 29 Unexpected end tag (em) in table context caused voodoo mode.
+Line: 1 Col: 29 End tag (em) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 33 Unexpected end tag (i) in table context caused voodoo mode.
+Line: 1 Col: 33 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 37 Unexpected end tag (u) in table context caused voodoo mode.
+Line: 1 Col: 37 End tag (u) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 46 Unexpected end tag (strike) in table context caused voodoo mode.
+Line: 1 Col: 46 End tag (strike) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 50 Unexpected end tag (s) in table context caused voodoo mode.
+Line: 1 Col: 50 End tag (s) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 58 Unexpected end tag (blink) in table context caused voodoo mode.
+Line: 1 Col: 58 Unexpected end tag (blink). Ignored.
+Line: 1 Col: 63 Unexpected end tag (tt) in table context caused voodoo mode.
+Line: 1 Col: 63 End tag (tt) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 69 Unexpected end tag (pre) in table context caused voodoo mode.
+Line: 1 Col: 69 End tag (pre) seen too early. Expected other end tag.
+Line: 1 Col: 75 Unexpected end tag (big) in table context caused voodoo mode.
+Line: 1 Col: 75 End tag (big) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 83 Unexpected end tag (small) in table context caused voodoo mode.
+Line: 1 Col: 83 End tag (small) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 90 Unexpected end tag (font) in table context caused voodoo mode.
+Line: 1 Col: 90 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 99 Unexpected end tag (select) in table context caused voodoo mode.
+Line: 1 Col: 99 Unexpected end tag (select). Ignored.
+Line: 1 Col: 104 Unexpected end tag (h1) in table context caused voodoo mode.
+Line: 1 Col: 104 End tag (h1) seen too early. Expected other end tag.
+Line: 1 Col: 109 Unexpected end tag (h2) in table context caused voodoo mode.
+Line: 1 Col: 109 End tag (h2) seen too early. Expected other end tag.
+Line: 1 Col: 114 Unexpected end tag (h3) in table context caused voodoo mode.
+Line: 1 Col: 114 End tag (h3) seen too early. Expected other end tag.
+Line: 1 Col: 119 Unexpected end tag (h4) in table context caused voodoo mode.
+Line: 1 Col: 119 End tag (h4) seen too early. Expected other end tag.
+Line: 1 Col: 124 Unexpected end tag (h5) in table context caused voodoo mode.
+Line: 1 Col: 124 End tag (h5) seen too early. Expected other end tag.
+Line: 1 Col: 129 Unexpected end tag (h6) in table context caused voodoo mode.
+Line: 1 Col: 129 End tag (h6) seen too early. Expected other end tag.
+Line: 1 Col: 136 Unexpected end tag (body) in the table row phase. Ignored.
+Line: 1 Col: 141 Unexpected end tag (br) in table context caused voodoo mode.
+Line: 1 Col: 141 Unexpected end tag (br). Treated as br element.
+Line: 1 Col: 145 Unexpected end tag (a) in table context caused voodoo mode.
+Line: 1 Col: 145 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 151 Unexpected end tag (img) in table context caused voodoo mode.
+Line: 1 Col: 151 This element (img) has no end tag.
+Line: 1 Col: 159 Unexpected end tag (title) in table context caused voodoo mode.
+Line: 1 Col: 159 Unexpected end tag (title). Ignored.
+Line: 1 Col: 166 Unexpected end tag (span) in table context caused voodoo mode.
+Line: 1 Col: 166 Unexpected end tag (span). Ignored.
+Line: 1 Col: 174 Unexpected end tag (style) in table context caused voodoo mode.
+Line: 1 Col: 174 Unexpected end tag (style). Ignored.
+Line: 1 Col: 183 Unexpected end tag (script) in table context caused voodoo mode.
+Line: 1 Col: 183 Unexpected end tag (script). Ignored.
+Line: 1 Col: 196 Unexpected end tag (th). Ignored.
+Line: 1 Col: 201 Unexpected end tag (td). Ignored.
+Line: 1 Col: 206 Unexpected end tag (tr). Ignored.
+Line: 1 Col: 214 This element (frame) has no end tag.
+Line: 1 Col: 221 This element (area) has no end tag.
+Line: 1 Col: 228 Unexpected end tag (link). Ignored.
+Line: 1 Col: 236 This element (param) has no end tag.
+Line: 1 Col: 241 This element (hr) has no end tag.
+Line: 1 Col: 249 This element (input) has no end tag.
+Line: 1 Col: 255 Unexpected end tag (col). Ignored.
+Line: 1 Col: 262 Unexpected end tag (base). Ignored.
+Line: 1 Col: 269 Unexpected end tag (meta). Ignored.
+Line: 1 Col: 280 This element (basefont) has no end tag.
+Line: 1 Col: 290 This element (bgsound) has no end tag.
+Line: 1 Col: 298 This element (embed) has no end tag.
+Line: 1 Col: 307 This element (spacer) has no end tag.
+Line: 1 Col: 311 Unexpected end tag (p). Ignored.
+Line: 1 Col: 316 End tag (dd) seen too early. Expected other end tag.
+Line: 1 Col: 321 End tag (dt) seen too early. Expected other end tag.
+Line: 1 Col: 331 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 342 Unexpected end tag (colgroup). Ignored.
+Line: 1 Col: 350 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 358 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 366 Unexpected end tag (thead). Ignored.
+Line: 1 Col: 376 End tag (address) seen too early. Expected other end tag.
+Line: 1 Col: 389 End tag (blockquote) seen too early. Expected other end tag.
+Line: 1 Col: 398 End tag (center) seen too early. Expected other end tag.
+Line: 1 Col: 404 Unexpected end tag (dir). Ignored.
+Line: 1 Col: 410 End tag (div) seen too early. Expected other end tag.
+Line: 1 Col: 415 End tag (dl) seen too early. Expected other end tag.
+Line: 1 Col: 426 End tag (fieldset) seen too early. Expected other end tag.
+Line: 1 Col: 436 End tag (listing) seen too early. Expected other end tag.
+Line: 1 Col: 443 End tag (menu) seen too early. Expected other end tag.
+Line: 1 Col: 448 End tag (ol) seen too early. Expected other end tag.
+Line: 1 Col: 453 End tag (ul) seen too early. Expected other end tag.
+Line: 1 Col: 458 End tag (li) seen too early. Expected other end tag.
+Line: 1 Col: 465 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm.
+Line: 1 Col: 471 This element (wbr) has no end tag.
+Line: 1 Col: 487 End tag (button) seen too early. Expected other end tag.
+Line: 1 Col: 497 End tag (marquee) seen too early. Expected other end tag.
+Line: 1 Col: 506 End tag (object) seen too early. Expected other end tag.
+Line: 1 Col: 524 Unexpected end tag (html). Ignored.
+Line: 1 Col: 524 Unexpected end tag (frameset). Ignored.
+Line: 1 Col: 531 Unexpected end tag (head). Ignored.
+Line: 1 Col: 540 Unexpected end tag (iframe). Ignored.
+Line: 1 Col: 548 This element (image) has no end tag.
+Line: 1 Col: 558 This element (isindex) has no end tag.
+Line: 1 Col: 568 Unexpected end tag (noembed). Ignored.
+Line: 1 Col: 579 Unexpected end tag (noframes). Ignored.
+Line: 1 Col: 590 Unexpected end tag (noscript). Ignored.
+Line: 1 Col: 601 Unexpected end tag (optgroup). Ignored.
+Line: 1 Col: 610 Unexpected end tag (option). Ignored.
+Line: 1 Col: 622 Unexpected end tag (plaintext). Ignored.
+Line: 1 Col: 633 Unexpected end tag (textarea). Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| <br>
+| <table>
+| <tbody>
+| <tr>
+| <p>
+
+#data
+<frameset>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 1 Col: 10 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <frameset>
diff --git a/html/testdata/webkit/tests10.dat b/html/testdata/webkit/tests10.dat
new file mode 100644
index 0000000..4f8df86
--- /dev/null
+++ b/html/testdata/webkit/tests10.dat
@@ -0,0 +1,799 @@
+#data
+<!DOCTYPE html><svg></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<!DOCTYPE html><svg></svg><![CDATA[a]]>
+#errors
+29: Bogus comment
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <!-- [CDATA[a]] -->
+
+#data
+<!DOCTYPE html><body><svg></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<!DOCTYPE html><body><select><svg></svg></select>
+#errors
+35: Stray “svg” start tag.
+42: Stray end tag “svg”
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!DOCTYPE html><body><select><option><svg></svg></option></select>
+#errors
+43: Stray “svg” start tag.
+50: Stray end tag “svg”
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+
+#data
+<!DOCTYPE html><body><table><svg></svg></table>
+#errors
+34: Start tag “svg” seen in “table”.
+41: Stray end tag “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <table>
+
+#data
+<!DOCTYPE html><body><table><svg><g>foo</g></svg></table>
+#errors
+34: Start tag “svg” seen in “table”.
+46: Stray end tag “g”.
+53: Stray end tag “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <table>
+
+#data
+<!DOCTYPE html><body><table><svg><g>foo</g><g>bar</g></svg></table>
+#errors
+34: Start tag “svg” seen in “table”.
+46: Stray end tag “g”.
+58: Stray end tag “g”.
+65: Stray end tag “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <table>
+
+#data
+<!DOCTYPE html><body><table><tbody><svg><g>foo</g><g>bar</g></svg></tbody></table>
+#errors
+41: Start tag “svg” seen in “table”.
+53: Stray end tag “g”.
+65: Stray end tag “g”.
+72: Stray end tag “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <table>
+| <tbody>
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><svg><g>foo</g><g>bar</g></svg></tr></tbody></table>
+#errors
+45: Start tag “svg” seen in “table”.
+57: Stray end tag “g”.
+69: Stray end tag “g”.
+76: Stray end tag “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg></td></tr></tbody></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg><p>baz</td></tr></tbody></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</caption></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g><p>baz</table><p>quux
+#errors
+70: HTML start tag “p” in a foreign namespace context.
+81: “table” closed but “caption” was still open.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g>baz</table><p>quux
+#errors
+78: “table” closed but “caption” was still open.
+78: Unclosed elements on stack.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| "baz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><colgroup><svg><g>foo</g><g>bar</g><p>baz</table><p>quux
+#errors
+44: Start tag “svg” seen in “table”.
+56: Stray end tag “g”.
+68: Stray end tag “g”.
+71: HTML start tag “p” in a foreign namespace context.
+71: Start tag “p” seen in “table”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+| <table>
+| <colgroup>
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><tr><td><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux
+#errors
+50: Stray “svg” start tag.
+54: Stray “g” start tag.
+62: Stray end tag “g”
+66: Stray “g” start tag.
+74: Stray end tag “g”
+77: Stray “p” start tag.
+88: “table” end tag with “select” open.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <select>
+| "foobarbaz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux
+#errors
+36: Start tag “select” seen in “table”.
+42: Stray “svg” start tag.
+46: Stray “g” start tag.
+54: Stray end tag “g”
+58: Stray “g” start tag.
+66: Stray end tag “g”
+69: Stray “p” start tag.
+80: “table” end tag with “select” open.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| "foobarbaz"
+| <table>
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body></body></html><svg><g>foo</g><g>bar</g><p>baz
+#errors
+41: Stray “svg” start tag.
+68: HTML start tag “p” in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body></body><svg><g>foo</g><g>bar</g><p>baz
+#errors
+34: Stray “svg” start tag.
+61: HTML start tag “p” in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg g>
+| "foo"
+| <svg g>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><frameset><svg><g></g><g></g><p><span>
+#errors
+31: Stray “svg” start tag.
+35: Stray “g” start tag.
+40: Stray end tag “g”
+44: Stray “g” start tag.
+49: Stray end tag “g”
+52: Stray “p” start tag.
+58: Stray “span” start tag.
+58: End of file seen and there were open elements.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><frameset></frameset><svg><g></g><g></g><p><span>
+#errors
+42: Stray “svg” start tag.
+46: Stray “g” start tag.
+51: Stray end tag “g”
+55: Stray “g” start tag.
+60: Stray end tag “g”
+63: Stray “p” start tag.
+69: Stray “span” start tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><body xlink:href=foo><svg xlink:href=foo></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| <svg svg>
+| xlink href="foo"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo></g></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <svg svg>
+| <svg g>
+| xlink href="foo"
+| xml lang="en"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo /></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <svg svg>
+| <svg g>
+| xlink href="foo"
+| xml lang="en"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo />bar</svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <svg svg>
+| <svg g>
+| xlink href="foo"
+| xml lang="en"
+| "bar"
+
+#data
+<svg></path>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<div><svg></div>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| "a"
+
+#data
+<div><svg><path></div>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| <svg path>
+| "a"
+
+#data
+<div><svg><path></svg><path>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| <svg path>
+| <path>
+
+#data
+<div><svg><path><foreignObject><math></div>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| <svg path>
+| <svg foreignObject>
+| <math math>
+| "a"
+
+#data
+<div><svg><path><foreignObject><p></div>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| <svg path>
+| <svg foreignObject>
+| <p>
+| "a"
+
+#data
+<!DOCTYPE html><svg><desc><div><svg><ul>a
+#errors
+40: HTML start tag “ul” in a foreign namespace context.
+41: End of file in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg desc>
+| <div>
+| <svg svg>
+| <ul>
+| "a"
+
+#data
+<!DOCTYPE html><svg><desc><svg><ul>a
+#errors
+35: HTML start tag “ul” in a foreign namespace context.
+36: End of file in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg desc>
+| <svg svg>
+| <ul>
+| "a"
+
+#data
+<!DOCTYPE html><p><svg><desc><p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <svg svg>
+| <svg desc>
+| <p>
+
+#data
+<!DOCTYPE html><p><svg><title><p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <svg svg>
+| <svg title>
+| <p>
+
+#data
+<div><svg><path><foreignObject><p></foreignObject><p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <svg svg>
+| <svg path>
+| <svg foreignObject>
+| <p>
+| <p>
+
+#data
+<math><mi><div><object><div><span></span></div></object></div></mi><mi>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| <div>
+| <object>
+| <div>
+| <span>
+| <math mi>
+
+#data
+<math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| <svg svg>
+| <svg foreignObject>
+| <div>
+| <div>
+| <math mi>
+
+#data
+<svg><script></script><path>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg script>
+| <svg path>
+
+#data
+<table><svg></svg><tr>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<math><mi><mglyph>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| <math mglyph>
+
+#data
+<math><mi><malignmark>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| <math malignmark>
+
+#data
+<math><mo><mglyph>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mo>
+| <math mglyph>
+
+#data
+<math><mo><malignmark>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mo>
+| <math malignmark>
+
+#data
+<math><mn><mglyph>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mn>
+| <math mglyph>
+
+#data
+<math><mn><malignmark>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mn>
+| <math malignmark>
+
+#data
+<math><ms><mglyph>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math ms>
+| <math mglyph>
+
+#data
+<math><ms><malignmark>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math ms>
+| <math malignmark>
+
+#data
+<math><mtext><mglyph>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mtext>
+| <math mglyph>
+
+#data
+<math><mtext><malignmark>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mtext>
+| <math malignmark>
+
+#data
+<math><annotation-xml><svg></svg></annotation-xml><mi>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| <svg svg>
+| <math mi>
+
+#data
+<math><annotation-xml><svg><foreignObject><div><math><mi></mi></math><span></span></div></foreignObject><path></path></svg></annotation-xml><mi>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| <svg svg>
+| <svg foreignObject>
+| <div>
+| <math math>
+| <math mi>
+| <span>
+| <svg path>
+| <math mi>
+
+#data
+<math><annotation-xml><svg><foreignObject><math><mi><svg></svg></mi><mo></mo></math><span></span></foreignObject><path></path></svg></annotation-xml><mi>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| <svg svg>
+| <svg foreignObject>
+| <math math>
+| <math mi>
+| <svg svg>
+| <math mo>
+| <span>
+| <svg path>
+| <math mi>
diff --git a/html/testdata/webkit/tests11.dat b/html/testdata/webkit/tests11.dat
new file mode 100644
index 0000000..638cde4
--- /dev/null
+++ b/html/testdata/webkit/tests11.dat
@@ -0,0 +1,482 @@
+#data
+<!DOCTYPE html><body><svg attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' contentScriptType='' contentStyleType='' diffuseConstant='' edgeMode='' externalResourcesRequired='' filterRes='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| attributeName=""
+| attributeType=""
+| baseFrequency=""
+| baseProfile=""
+| calcMode=""
+| clipPathUnits=""
+| contentScriptType=""
+| contentStyleType=""
+| diffuseConstant=""
+| edgeMode=""
+| externalResourcesRequired=""
+| filterRes=""
+| filterUnits=""
+| glyphRef=""
+| gradientTransform=""
+| gradientUnits=""
+| kernelMatrix=""
+| kernelUnitLength=""
+| keyPoints=""
+| keySplines=""
+| keyTimes=""
+| lengthAdjust=""
+| limitingConeAngle=""
+| markerHeight=""
+| markerUnits=""
+| markerWidth=""
+| maskContentUnits=""
+| maskUnits=""
+| numOctaves=""
+| pathLength=""
+| patternContentUnits=""
+| patternTransform=""
+| patternUnits=""
+| pointsAtX=""
+| pointsAtY=""
+| pointsAtZ=""
+| preserveAlpha=""
+| preserveAspectRatio=""
+| primitiveUnits=""
+| refX=""
+| refY=""
+| repeatCount=""
+| repeatDur=""
+| requiredExtensions=""
+| requiredFeatures=""
+| specularConstant=""
+| specularExponent=""
+| spreadMethod=""
+| startOffset=""
+| stdDeviation=""
+| stitchTiles=""
+| surfaceScale=""
+| systemLanguage=""
+| tableValues=""
+| targetX=""
+| targetY=""
+| textLength=""
+| viewBox=""
+| viewTarget=""
+| xChannelSelector=""
+| yChannelSelector=""
+| zoomAndPan=""
+
+#data
+<!DOCTYPE html><BODY><SVG ATTRIBUTENAME='' ATTRIBUTETYPE='' BASEFREQUENCY='' BASEPROFILE='' CALCMODE='' CLIPPATHUNITS='' CONTENTSCRIPTTYPE='' CONTENTSTYLETYPE='' DIFFUSECONSTANT='' EDGEMODE='' EXTERNALRESOURCESREQUIRED='' FILTERRES='' FILTERUNITS='' GLYPHREF='' GRADIENTTRANSFORM='' GRADIENTUNITS='' KERNELMATRIX='' KERNELUNITLENGTH='' KEYPOINTS='' KEYSPLINES='' KEYTIMES='' LENGTHADJUST='' LIMITINGCONEANGLE='' MARKERHEIGHT='' MARKERUNITS='' MARKERWIDTH='' MASKCONTENTUNITS='' MASKUNITS='' NUMOCTAVES='' PATHLENGTH='' PATTERNCONTENTUNITS='' PATTERNTRANSFORM='' PATTERNUNITS='' POINTSATX='' POINTSATY='' POINTSATZ='' PRESERVEALPHA='' PRESERVEASPECTRATIO='' PRIMITIVEUNITS='' REFX='' REFY='' REPEATCOUNT='' REPEATDUR='' REQUIREDEXTENSIONS='' REQUIREDFEATURES='' SPECULARCONSTANT='' SPECULAREXPONENT='' SPREADMETHOD='' STARTOFFSET='' STDDEVIATION='' STITCHTILES='' SURFACESCALE='' SYSTEMLANGUAGE='' TABLEVALUES='' TARGETX='' TARGETY='' TEXTLENGTH='' VIEWBOX='' VIEWTARGET='' XCHANNELSELECTOR='' YCHANNELSELECTOR='' ZOOMANDPAN=''></SVG>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| attributeName=""
+| attributeType=""
+| baseFrequency=""
+| baseProfile=""
+| calcMode=""
+| clipPathUnits=""
+| contentScriptType=""
+| contentStyleType=""
+| diffuseConstant=""
+| edgeMode=""
+| externalResourcesRequired=""
+| filterRes=""
+| filterUnits=""
+| glyphRef=""
+| gradientTransform=""
+| gradientUnits=""
+| kernelMatrix=""
+| kernelUnitLength=""
+| keyPoints=""
+| keySplines=""
+| keyTimes=""
+| lengthAdjust=""
+| limitingConeAngle=""
+| markerHeight=""
+| markerUnits=""
+| markerWidth=""
+| maskContentUnits=""
+| maskUnits=""
+| numOctaves=""
+| pathLength=""
+| patternContentUnits=""
+| patternTransform=""
+| patternUnits=""
+| pointsAtX=""
+| pointsAtY=""
+| pointsAtZ=""
+| preserveAlpha=""
+| preserveAspectRatio=""
+| primitiveUnits=""
+| refX=""
+| refY=""
+| repeatCount=""
+| repeatDur=""
+| requiredExtensions=""
+| requiredFeatures=""
+| specularConstant=""
+| specularExponent=""
+| spreadMethod=""
+| startOffset=""
+| stdDeviation=""
+| stitchTiles=""
+| surfaceScale=""
+| systemLanguage=""
+| tableValues=""
+| targetX=""
+| targetY=""
+| textLength=""
+| viewBox=""
+| viewTarget=""
+| xChannelSelector=""
+| yChannelSelector=""
+| zoomAndPan=""
+
+#data
+<!DOCTYPE html><body><svg attributename='' attributetype='' basefrequency='' baseprofile='' calcmode='' clippathunits='' contentscripttype='' contentstyletype='' diffuseconstant='' edgemode='' externalresourcesrequired='' filterres='' filterunits='' glyphref='' gradienttransform='' gradientunits='' kernelmatrix='' kernelunitlength='' keypoints='' keysplines='' keytimes='' lengthadjust='' limitingconeangle='' markerheight='' markerunits='' markerwidth='' maskcontentunits='' maskunits='' numoctaves='' pathlength='' patterncontentunits='' patterntransform='' patternunits='' pointsatx='' pointsaty='' pointsatz='' preservealpha='' preserveaspectratio='' primitiveunits='' refx='' refy='' repeatcount='' repeatdur='' requiredextensions='' requiredfeatures='' specularconstant='' specularexponent='' spreadmethod='' startoffset='' stddeviation='' stitchtiles='' surfacescale='' systemlanguage='' tablevalues='' targetx='' targety='' textlength='' viewbox='' viewtarget='' xchannelselector='' ychannelselector='' zoomandpan=''></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| attributeName=""
+| attributeType=""
+| baseFrequency=""
+| baseProfile=""
+| calcMode=""
+| clipPathUnits=""
+| contentScriptType=""
+| contentStyleType=""
+| diffuseConstant=""
+| edgeMode=""
+| externalResourcesRequired=""
+| filterRes=""
+| filterUnits=""
+| glyphRef=""
+| gradientTransform=""
+| gradientUnits=""
+| kernelMatrix=""
+| kernelUnitLength=""
+| keyPoints=""
+| keySplines=""
+| keyTimes=""
+| lengthAdjust=""
+| limitingConeAngle=""
+| markerHeight=""
+| markerUnits=""
+| markerWidth=""
+| maskContentUnits=""
+| maskUnits=""
+| numOctaves=""
+| pathLength=""
+| patternContentUnits=""
+| patternTransform=""
+| patternUnits=""
+| pointsAtX=""
+| pointsAtY=""
+| pointsAtZ=""
+| preserveAlpha=""
+| preserveAspectRatio=""
+| primitiveUnits=""
+| refX=""
+| refY=""
+| repeatCount=""
+| repeatDur=""
+| requiredExtensions=""
+| requiredFeatures=""
+| specularConstant=""
+| specularExponent=""
+| spreadMethod=""
+| startOffset=""
+| stdDeviation=""
+| stitchTiles=""
+| surfaceScale=""
+| systemLanguage=""
+| tableValues=""
+| targetX=""
+| targetY=""
+| textLength=""
+| viewBox=""
+| viewTarget=""
+| xChannelSelector=""
+| yChannelSelector=""
+| zoomAndPan=""
+
+#data
+<!DOCTYPE html><body><math attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' contentScriptType='' contentStyleType='' diffuseConstant='' edgeMode='' externalResourcesRequired='' filterRes='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| attributename=""
+| attributetype=""
+| basefrequency=""
+| baseprofile=""
+| calcmode=""
+| clippathunits=""
+| contentscripttype=""
+| contentstyletype=""
+| diffuseconstant=""
+| edgemode=""
+| externalresourcesrequired=""
+| filterres=""
+| filterunits=""
+| glyphref=""
+| gradienttransform=""
+| gradientunits=""
+| kernelmatrix=""
+| kernelunitlength=""
+| keypoints=""
+| keysplines=""
+| keytimes=""
+| lengthadjust=""
+| limitingconeangle=""
+| markerheight=""
+| markerunits=""
+| markerwidth=""
+| maskcontentunits=""
+| maskunits=""
+| numoctaves=""
+| pathlength=""
+| patterncontentunits=""
+| patterntransform=""
+| patternunits=""
+| pointsatx=""
+| pointsaty=""
+| pointsatz=""
+| preservealpha=""
+| preserveaspectratio=""
+| primitiveunits=""
+| refx=""
+| refy=""
+| repeatcount=""
+| repeatdur=""
+| requiredextensions=""
+| requiredfeatures=""
+| specularconstant=""
+| specularexponent=""
+| spreadmethod=""
+| startoffset=""
+| stddeviation=""
+| stitchtiles=""
+| surfacescale=""
+| systemlanguage=""
+| tablevalues=""
+| targetx=""
+| targety=""
+| textlength=""
+| viewbox=""
+| viewtarget=""
+| xchannelselector=""
+| ychannelselector=""
+| zoomandpan=""
+
+#data
+<!DOCTYPE html><body><svg><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg altGlyph>
+| <svg altGlyphDef>
+| <svg altGlyphItem>
+| <svg animateColor>
+| <svg animateMotion>
+| <svg animateTransform>
+| <svg clipPath>
+| <svg feBlend>
+| <svg feColorMatrix>
+| <svg feComponentTransfer>
+| <svg feComposite>
+| <svg feConvolveMatrix>
+| <svg feDiffuseLighting>
+| <svg feDisplacementMap>
+| <svg feDistantLight>
+| <svg feFlood>
+| <svg feFuncA>
+| <svg feFuncB>
+| <svg feFuncG>
+| <svg feFuncR>
+| <svg feGaussianBlur>
+| <svg feImage>
+| <svg feMerge>
+| <svg feMergeNode>
+| <svg feMorphology>
+| <svg feOffset>
+| <svg fePointLight>
+| <svg feSpecularLighting>
+| <svg feSpotLight>
+| <svg feTile>
+| <svg feTurbulence>
+| <svg foreignObject>
+| <svg glyphRef>
+| <svg linearGradient>
+| <svg radialGradient>
+| <svg textPath>
+
+#data
+<!DOCTYPE html><body><svg><altglyph /><altglyphdef /><altglyphitem /><animatecolor /><animatemotion /><animatetransform /><clippath /><feblend /><fecolormatrix /><fecomponenttransfer /><fecomposite /><feconvolvematrix /><fediffuselighting /><fedisplacementmap /><fedistantlight /><feflood /><fefunca /><fefuncb /><fefuncg /><fefuncr /><fegaussianblur /><feimage /><femerge /><femergenode /><femorphology /><feoffset /><fepointlight /><fespecularlighting /><fespotlight /><fetile /><feturbulence /><foreignobject /><glyphref /><lineargradient /><radialgradient /><textpath /></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg altGlyph>
+| <svg altGlyphDef>
+| <svg altGlyphItem>
+| <svg animateColor>
+| <svg animateMotion>
+| <svg animateTransform>
+| <svg clipPath>
+| <svg feBlend>
+| <svg feColorMatrix>
+| <svg feComponentTransfer>
+| <svg feComposite>
+| <svg feConvolveMatrix>
+| <svg feDiffuseLighting>
+| <svg feDisplacementMap>
+| <svg feDistantLight>
+| <svg feFlood>
+| <svg feFuncA>
+| <svg feFuncB>
+| <svg feFuncG>
+| <svg feFuncR>
+| <svg feGaussianBlur>
+| <svg feImage>
+| <svg feMerge>
+| <svg feMergeNode>
+| <svg feMorphology>
+| <svg feOffset>
+| <svg fePointLight>
+| <svg feSpecularLighting>
+| <svg feSpotLight>
+| <svg feTile>
+| <svg feTurbulence>
+| <svg foreignObject>
+| <svg glyphRef>
+| <svg linearGradient>
+| <svg radialGradient>
+| <svg textPath>
+
+#data
+<!DOCTYPE html><BODY><SVG><ALTGLYPH /><ALTGLYPHDEF /><ALTGLYPHITEM /><ANIMATECOLOR /><ANIMATEMOTION /><ANIMATETRANSFORM /><CLIPPATH /><FEBLEND /><FECOLORMATRIX /><FECOMPONENTTRANSFER /><FECOMPOSITE /><FECONVOLVEMATRIX /><FEDIFFUSELIGHTING /><FEDISPLACEMENTMAP /><FEDISTANTLIGHT /><FEFLOOD /><FEFUNCA /><FEFUNCB /><FEFUNCG /><FEFUNCR /><FEGAUSSIANBLUR /><FEIMAGE /><FEMERGE /><FEMERGENODE /><FEMORPHOLOGY /><FEOFFSET /><FEPOINTLIGHT /><FESPECULARLIGHTING /><FESPOTLIGHT /><FETILE /><FETURBULENCE /><FOREIGNOBJECT /><GLYPHREF /><LINEARGRADIENT /><RADIALGRADIENT /><TEXTPATH /></SVG>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg altGlyph>
+| <svg altGlyphDef>
+| <svg altGlyphItem>
+| <svg animateColor>
+| <svg animateMotion>
+| <svg animateTransform>
+| <svg clipPath>
+| <svg feBlend>
+| <svg feColorMatrix>
+| <svg feComponentTransfer>
+| <svg feComposite>
+| <svg feConvolveMatrix>
+| <svg feDiffuseLighting>
+| <svg feDisplacementMap>
+| <svg feDistantLight>
+| <svg feFlood>
+| <svg feFuncA>
+| <svg feFuncB>
+| <svg feFuncG>
+| <svg feFuncR>
+| <svg feGaussianBlur>
+| <svg feImage>
+| <svg feMerge>
+| <svg feMergeNode>
+| <svg feMorphology>
+| <svg feOffset>
+| <svg fePointLight>
+| <svg feSpecularLighting>
+| <svg feSpotLight>
+| <svg feTile>
+| <svg feTurbulence>
+| <svg foreignObject>
+| <svg glyphRef>
+| <svg linearGradient>
+| <svg radialGradient>
+| <svg textPath>
+
+#data
+<!DOCTYPE html><body><math><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math altglyph>
+| <math altglyphdef>
+| <math altglyphitem>
+| <math animatecolor>
+| <math animatemotion>
+| <math animatetransform>
+| <math clippath>
+| <math feblend>
+| <math fecolormatrix>
+| <math fecomponenttransfer>
+| <math fecomposite>
+| <math feconvolvematrix>
+| <math fediffuselighting>
+| <math fedisplacementmap>
+| <math fedistantlight>
+| <math feflood>
+| <math fefunca>
+| <math fefuncb>
+| <math fefuncg>
+| <math fefuncr>
+| <math fegaussianblur>
+| <math feimage>
+| <math femerge>
+| <math femergenode>
+| <math femorphology>
+| <math feoffset>
+| <math fepointlight>
+| <math fespecularlighting>
+| <math fespotlight>
+| <math fetile>
+| <math feturbulence>
+| <math foreignobject>
+| <math glyphref>
+| <math lineargradient>
+| <math radialgradient>
+| <math textpath>
+
+#data
+<!DOCTYPE html><body><svg><solidColor /></svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg solidcolor>
diff --git a/html/testdata/webkit/tests12.dat b/html/testdata/webkit/tests12.dat
new file mode 100644
index 0000000..63107d2
--- /dev/null
+++ b/html/testdata/webkit/tests12.dat
@@ -0,0 +1,62 @@
+#data
+<!DOCTYPE html><body><p>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| "foo"
+| <math math>
+| <math mtext>
+| <i>
+| "baz"
+| <math annotation-xml>
+| <svg svg>
+| <svg desc>
+| <b>
+| "eggs"
+| <svg g>
+| <svg foreignObject>
+| <p>
+| "spam"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <img>
+| <svg g>
+| "quux"
+| "bar"
+
+#data
+<!DOCTYPE html><body>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "foo"
+| <math math>
+| <math mtext>
+| <i>
+| "baz"
+| <math annotation-xml>
+| <svg svg>
+| <svg desc>
+| <b>
+| "eggs"
+| <svg g>
+| <svg foreignObject>
+| <p>
+| "spam"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <img>
+| <svg g>
+| "quux"
+| "bar"
diff --git a/html/testdata/webkit/tests14.dat b/html/testdata/webkit/tests14.dat
new file mode 100644
index 0000000..b8713f8
--- /dev/null
+++ b/html/testdata/webkit/tests14.dat
@@ -0,0 +1,74 @@
+#data
+<!DOCTYPE html><html><body><xyz:abc></xyz:abc>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <xyz:abc>
+
+#data
+<!DOCTYPE html><html><body><xyz:abc></xyz:abc><span></span>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <xyz:abc>
+| <span>
+
+#data
+<!DOCTYPE html><html><html abc:def=gh><xyz:abc></xyz:abc>
+#errors
+15: Unexpected start tag html
+#document
+| <!DOCTYPE html>
+| <html>
+| abc:def="gh"
+| <head>
+| <body>
+| <xyz:abc>
+
+#data
+<!DOCTYPE html><html xml:lang=bar><html xml:lang=foo>
+#errors
+15: Unexpected start tag html
+#document
+| <!DOCTYPE html>
+| <html>
+| xml:lang="bar"
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><html 123=456>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| 123="456"
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><html 123=456><html 789=012>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| 123="456"
+| 789="012"
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><html><body 789=012>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| 789="012"
diff --git a/html/testdata/webkit/tests15.dat b/html/testdata/webkit/tests15.dat
new file mode 100644
index 0000000..6ce1c0d
--- /dev/null
+++ b/html/testdata/webkit/tests15.dat
@@ -0,0 +1,208 @@
+#data
+<!DOCTYPE html><p><b><i><u></p> <p>X
+#errors
+Line: 1 Col: 31 Unexpected end tag (p). Ignored.
+Line: 1 Col: 36 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| <i>
+| <u>
+| <b>
+| <i>
+| <u>
+| " "
+| <p>
+| "X"
+
+#data
+<p><b><i><u></p>
+<p>X
+#errors
+Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected end tag (p). Ignored.
+Line: 2 Col: 4 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| <i>
+| <u>
+| <b>
+| <i>
+| <u>
+| "
+"
+| <p>
+| "X"
+
+#data
+<!doctype html></html> <head>
+#errors
+Line: 1 Col: 22 Unexpected end tag (html) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " "
+
+#data
+<!doctype html></body><meta>
+#errors
+Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <meta>
+
+#data
+<html></html><!-- foo -->
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end tag (html) after the (implied) root element.
+#document
+| <html>
+| <head>
+| <body>
+| <!-- foo -->
+
+#data
+<!doctype html></body><title>X</title>
+#errors
+Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <title>
+| "X"
+
+#data
+<!doctype html><table> X<meta></table>
+#errors
+Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 30 Unexpected start tag (meta) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " X"
+| <meta>
+| <table>
+
+#data
+<!doctype html><table> x</table>
+#errors
+Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " x"
+| <table>
+
+#data
+<!doctype html><table> x </table>
+#errors
+Line: 1 Col: 25 Unexpected non-space characters in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " x "
+| <table>
+
+#data
+<!doctype html><table><tr> x</table>
+#errors
+Line: 1 Col: 28 Unexpected non-space characters in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " x"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><table>X<style> <tr>x </style> </table>
+#errors
+Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+| <table>
+| <style>
+| " <tr>x "
+| " "
+
+#data
+<!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div>
+#errors
+Line: 1 Col: 30 Unexpected start tag (a) in table context caused voodoo mode.
+Line: 1 Col: 37 Unexpected end tag (a) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+| <a>
+| "foo"
+| <table>
+| " "
+| <tbody>
+| <tr>
+| <td>
+| "bar"
+| " "
+
+#data
+<frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes>
+#errors
+6: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+13: Stray start tag “frame”.
+21: Stray end tag “frame”.
+29: Stray end tag “frame”.
+39: “frameset” start tag after “body” already open.
+105: End of file seen inside an [R]CDATA element.
+105: End of file seen and there were open elements.
+XXX: These errors are wrong, please fix me!
+#document
+| <html>
+| <head>
+| <frameset>
+| <frame>
+| <frameset>
+| <frame>
+| <noframes>
+| "</frameset><noframes>"
+
+#data
+<!DOCTYPE html><object></html>
+#errors
+1: Expected closing tag. Unexpected end of file
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <object>
diff --git a/html/testdata/webkit/tests16.dat b/html/testdata/webkit/tests16.dat
new file mode 100644
index 0000000..c8ef66f
--- /dev/null
+++ b/html/testdata/webkit/tests16.dat
@@ -0,0 +1,2299 @@
+#data
+<!doctype html><script>
+#errors
+Line: 1 Col: 23 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<!doctype html><script>a
+#errors
+Line: 1 Col: 24 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "a"
+| <body>
+
+#data
+<!doctype html><script><
+#errors
+Line: 1 Col: 24 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<"
+| <body>
+
+#data
+<!doctype html><script></
+#errors
+Line: 1 Col: 25 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</"
+| <body>
+
+#data
+<!doctype html><script></S
+#errors
+Line: 1 Col: 26 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</S"
+| <body>
+
+#data
+<!doctype html><script></SC
+#errors
+Line: 1 Col: 27 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</SC"
+| <body>
+
+#data
+<!doctype html><script></SCR
+#errors
+Line: 1 Col: 28 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</SCR"
+| <body>
+
+#data
+<!doctype html><script></SCRI
+#errors
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</SCRI"
+| <body>
+
+#data
+<!doctype html><script></SCRIP
+#errors
+Line: 1 Col: 30 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</SCRIP"
+| <body>
+
+#data
+<!doctype html><script></SCRIPT
+#errors
+Line: 1 Col: 31 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</SCRIPT"
+| <body>
+
+#data
+<!doctype html><script></SCRIPT
+#errors
+Line: 1 Col: 32 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<!doctype html><script></s
+#errors
+Line: 1 Col: 26 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</s"
+| <body>
+
+#data
+<!doctype html><script></sc
+#errors
+Line: 1 Col: 27 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</sc"
+| <body>
+
+#data
+<!doctype html><script></scr
+#errors
+Line: 1 Col: 28 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</scr"
+| <body>
+
+#data
+<!doctype html><script></scri
+#errors
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</scri"
+| <body>
+
+#data
+<!doctype html><script></scrip
+#errors
+Line: 1 Col: 30 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</scrip"
+| <body>
+
+#data
+<!doctype html><script></script
+#errors
+Line: 1 Col: 31 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "</script"
+| <body>
+
+#data
+<!doctype html><script></script
+#errors
+Line: 1 Col: 32 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<!doctype html><script><!
+#errors
+Line: 1 Col: 25 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!"
+| <body>
+
+#data
+<!doctype html><script><!a
+#errors
+Line: 1 Col: 26 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!a"
+| <body>
+
+#data
+<!doctype html><script><!-
+#errors
+Line: 1 Col: 26 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!-"
+| <body>
+
+#data
+<!doctype html><script><!-a
+#errors
+Line: 1 Col: 27 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!-a"
+| <body>
+
+#data
+<!doctype html><script><!--
+#errors
+Line: 1 Col: 27 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--"
+| <body>
+
+#data
+<!doctype html><script><!--a
+#errors
+Line: 1 Col: 28 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--a"
+| <body>
+
+#data
+<!doctype html><script><!--<
+#errors
+Line: 1 Col: 28 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<"
+| <body>
+
+#data
+<!doctype html><script><!--<a
+#errors
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<a"
+| <body>
+
+#data
+<!doctype html><script><!--</
+#errors
+Line: 1 Col: 27 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--</"
+| <body>
+
+#data
+<!doctype html><script><!--</script
+#errors
+Line: 1 Col: 35 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--</script"
+| <body>
+
+#data
+<!doctype html><script><!--</script
+#errors
+Line: 1 Col: 36 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--"
+| <body>
+
+#data
+<!doctype html><script><!--<s
+#errors
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<s"
+| <body>
+
+#data
+<!doctype html><script><!--<script
+#errors
+Line: 1 Col: 34 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script"
+| <body>
+
+#data
+<!doctype html><script><!--<script
+#errors
+Line: 1 Col: 35 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script "
+| <body>
+
+#data
+<!doctype html><script><!--<script <
+#errors
+Line: 1 Col: 36 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script <"
+| <body>
+
+#data
+<!doctype html><script><!--<script <a
+#errors
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script <a"
+| <body>
+
+#data
+<!doctype html><script><!--<script </
+#errors
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </"
+| <body>
+
+#data
+<!doctype html><script><!--<script </s
+#errors
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </s"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script
+#errors
+Line: 1 Col: 43 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script"
+| <body>
+
+#data
+<!doctype html><script><!--<script </scripta
+#errors
+Line: 1 Col: 44 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </scripta"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script
+#errors
+Line: 1 Col: 44 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<!doctype html><script><!--<script </script>
+#errors
+Line: 1 Col: 44 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script>"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script/
+#errors
+Line: 1 Col: 44 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script/"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script <
+#errors
+Line: 1 Col: 45 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script <"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script <a
+#errors
+Line: 1 Col: 46 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script <a"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script </
+#errors
+Line: 1 Col: 46 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script </"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script </script
+#errors
+Line: 1 Col: 52 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script </script"
+| <body>
+
+#data
+<!doctype html><script><!--<script </script </script
+#errors
+Line: 1 Col: 53 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<!doctype html><script><!--<script </script </script/
+#errors
+Line: 1 Col: 53 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<!doctype html><script><!--<script </script </script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<!doctype html><script><!--<script -
+#errors
+Line: 1 Col: 36 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -"
+| <body>
+
+#data
+<!doctype html><script><!--<script -a
+#errors
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -a"
+| <body>
+
+#data
+<!doctype html><script><!--<script -<
+#errors
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -<"
+| <body>
+
+#data
+<!doctype html><script><!--<script --
+#errors
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --"
+| <body>
+
+#data
+<!doctype html><script><!--<script --a
+#errors
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --a"
+| <body>
+
+#data
+<!doctype html><script><!--<script --<
+#errors
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --<"
+| <body>
+
+#data
+<!doctype html><script><!--<script -->
+#errors
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<!doctype html><script><!--<script --><
+#errors
+Line: 1 Col: 39 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --><"
+| <body>
+
+#data
+<!doctype html><script><!--<script --></
+#errors
+Line: 1 Col: 40 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --></"
+| <body>
+
+#data
+<!doctype html><script><!--<script --></script
+#errors
+Line: 1 Col: 46 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script --></script"
+| <body>
+
+#data
+<!doctype html><script><!--<script --></script
+#errors
+Line: 1 Col: 47 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<!doctype html><script><!--<script --></script/
+#errors
+Line: 1 Col: 47 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<!doctype html><script><!--<script --></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<!doctype html><script><!--<script><\/script>--></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script><\/script>-->"
+| <body>
+
+#data
+<!doctype html><script><!--<script></scr'+'ipt>--></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></scr'+'ipt>-->"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script>--><!--</script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>--><!--"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script>-- ></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>-- >"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script>- -></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>- ->"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script>- - ></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>- - >"
+| <body>
+
+#data
+<!doctype html><script><!--<script></script><script></script>-></script>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>->"
+| <body>
+
+#data
+<!doctype html><script><!--<script>--!></script>X
+#errors
+Line: 1 Col: 49 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script>--!></script>X"
+| <body>
+
+#data
+<!doctype html><script><!--<scr'+'ipt></script>--></script>
+#errors
+Line: 1 Col: 59 Unexpected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<scr'+'ipt>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><script><!--<script></scr'+'ipt></script>X
+#errors
+Line: 1 Col: 57 Unexpected end of file. Expected end tag (script).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "<!--<script></scr'+'ipt></script>X"
+| <body>
+
+#data
+<!doctype html><style><!--<style></style>--></style>
+#errors
+Line: 1 Col: 52 Unexpected end tag (style).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--<style>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><style><!--</style>X
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--"
+| <body>
+| "X"
+
+#data
+<!doctype html><style><!--...</style>...--></style>
+#errors
+Line: 1 Col: 51 Unexpected end tag (style).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--..."
+| <body>
+| "...-->"
+
+#data
+<!doctype html><style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>"
+| <body>
+| "X"
+
+#data
+<!doctype html><style><!--...<style><!--...--!></style>--></style>
+#errors
+Line: 1 Col: 66 Unexpected end tag (style).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--...<style><!--...--!>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><style><!--...</style><!-- --><style>@import ...</style>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "<!--..."
+| <!-- -->
+| <style>
+| "@import ..."
+| <body>
+
+#data
+<!doctype html><style>...<style><!--...</style><!-- --></style>
+#errors
+Line: 1 Col: 63 Unexpected end tag (style).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "...<style><!--..."
+| <!-- -->
+| <body>
+
+#data
+<!doctype html><style>...<!--[if IE]><style>...</style>X
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <style>
+| "...<!--[if IE]><style>..."
+| <body>
+| "X"
+
+#data
+<!doctype html><title><!--<title></title>--></title>
+#errors
+Line: 1 Col: 52 Unexpected end tag (title).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "<!--<title>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><title></title></title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "</title>"
+| <body>
+
+#data
+<!doctype html><title>foo/title><link></head><body>X
+#errors
+Line: 1 Col: 52 Unexpected end of file. Expected end tag (title).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "foo/title><link></head><body>X"
+| <body>
+
+#data
+<!doctype html><noscript><!--<noscript></noscript>--></noscript>
+#errors
+Line: 1 Col: 64 Unexpected end tag (noscript).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <noscript>
+| "<!--<noscript>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><noscript><!--</noscript>X<noscript>--></noscript>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <noscript>
+| "<!--"
+| <body>
+| "X"
+| <noscript>
+| "-->"
+
+#data
+<!doctype html><noscript><iframe></noscript>X
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <noscript>
+| "<iframe>"
+| <body>
+| "X"
+
+#data
+<!doctype html><noframes><!--<noframes></noframes>--></noframes>
+#errors
+Line: 1 Col: 64 Unexpected end tag (noframes).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <noframes>
+| "<!--<noframes>"
+| <body>
+| "-->"
+
+#data
+<!doctype html><noframes><body><script><!--...</script></body></noframes></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <noframes>
+| "<body><script><!--...</script></body>"
+| <body>
+
+#data
+<!doctype html><textarea><!--<textarea></textarea>--></textarea>
+#errors
+Line: 1 Col: 64 Unexpected end tag (textarea).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<!--<textarea>"
+| "-->"
+
+#data
+<!doctype html><textarea></textarea></textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "</textarea>"
+
+#data
+<!doctype html><textarea><</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<"
+
+#data
+<!doctype html><textarea>a<b</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "a<b"
+
+#data
+<!doctype html><iframe><!--<iframe></iframe>--></iframe>
+#errors
+Line: 1 Col: 56 Unexpected end tag (iframe).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <iframe>
+| "<!--<iframe>"
+| "-->"
+
+#data
+<!doctype html><iframe>...<!--X->...<!--/X->...</iframe>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <iframe>
+| "...<!--X->...<!--/X->..."
+
+#data
+<!doctype html><xmp><!--<xmp></xmp>--></xmp>
+#errors
+Line: 1 Col: 44 Unexpected end tag (xmp).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <xmp>
+| "<!--<xmp>"
+| "-->"
+
+#data
+<!doctype html><noembed><!--<noembed></noembed>--></noembed>
+#errors
+Line: 1 Col: 60 Unexpected end tag (noembed).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <noembed>
+| "<!--<noembed>"
+| "-->"
+
+#data
+<script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 8 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<script>a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 9 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "a"
+| <body>
+
+#data
+<script><
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 9 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<"
+| <body>
+
+#data
+<script></
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 10 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</"
+| <body>
+
+#data
+<script></S
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</S"
+| <body>
+
+#data
+<script></SC
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</SC"
+| <body>
+
+#data
+<script></SCR
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</SCR"
+| <body>
+
+#data
+<script></SCRI
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</SCRI"
+| <body>
+
+#data
+<script></SCRIP
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 15 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</SCRIP"
+| <body>
+
+#data
+<script></SCRIPT
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</SCRIPT"
+| <body>
+
+#data
+<script></SCRIPT
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 17 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<script></s
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</s"
+| <body>
+
+#data
+<script></sc
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</sc"
+| <body>
+
+#data
+<script></scr
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</scr"
+| <body>
+
+#data
+<script></scri
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</scri"
+| <body>
+
+#data
+<script></scrip
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 15 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</scrip"
+| <body>
+
+#data
+<script></script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</script"
+| <body>
+
+#data
+<script></script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 17 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<script><!
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 10 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!"
+| <body>
+
+#data
+<script><!a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!a"
+| <body>
+
+#data
+<script><!-
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!-"
+| <body>
+
+#data
+<script><!-a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!-a"
+| <body>
+
+#data
+<script><!--
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--"
+| <body>
+
+#data
+<script><!--a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--a"
+| <body>
+
+#data
+<script><!--<
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<"
+| <body>
+
+#data
+<script><!--<a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<a"
+| <body>
+
+#data
+<script><!--</
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--</"
+| <body>
+
+#data
+<script><!--</script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--</script"
+| <body>
+
+#data
+<script><!--</script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 21 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--"
+| <body>
+
+#data
+<script><!--<s
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<s"
+| <body>
+
+#data
+<script><!--<script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 19 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script"
+| <body>
+
+#data
+<script><!--<script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script "
+| <body>
+
+#data
+<script><!--<script <
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 21 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script <"
+| <body>
+
+#data
+<script><!--<script <a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script <a"
+| <body>
+
+#data
+<script><!--<script </
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </"
+| <body>
+
+#data
+<script><!--<script </s
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </s"
+| <body>
+
+#data
+<script><!--<script </script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 28 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script"
+| <body>
+
+#data
+<script><!--<script </scripta
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </scripta"
+| <body>
+
+#data
+<script><!--<script </script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<script><!--<script </script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script>"
+| <body>
+
+#data
+<script><!--<script </script/
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 29 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script/"
+| <body>
+
+#data
+<script><!--<script </script <
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 30 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script <"
+| <body>
+
+#data
+<script><!--<script </script <a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 31 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script <a"
+| <body>
+
+#data
+<script><!--<script </script </
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 31 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script </"
+| <body>
+
+#data
+<script><!--<script </script </script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script </script"
+| <body>
+
+#data
+<script><!--<script </script </script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<script><!--<script </script </script/
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 38 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<script><!--<script </script </script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script </script "
+| <body>
+
+#data
+<script><!--<script -
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 21 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -"
+| <body>
+
+#data
+<script><!--<script -a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -a"
+| <body>
+
+#data
+<script><!--<script --
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script --"
+| <body>
+
+#data
+<script><!--<script --a
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script --a"
+| <body>
+
+#data
+<script><!--<script -->
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<script><!--<script --><
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 24 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script --><"
+| <body>
+
+#data
+<script><!--<script --></
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 25 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script --></"
+| <body>
+
+#data
+<script><!--<script --></script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 31 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script --></script"
+| <body>
+
+#data
+<script><!--<script --></script
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 32 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<script><!--<script --></script/
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 32 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<script><!--<script --></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script -->"
+| <body>
+
+#data
+<script><!--<script><\/script>--></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script><\/script>-->"
+| <body>
+
+#data
+<script><!--<script></scr'+'ipt>--></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></scr'+'ipt>-->"
+| <body>
+
+#data
+<script><!--<script></script><script></script></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>"
+| <body>
+
+#data
+<script><!--<script></script><script></script>--><!--</script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>--><!--"
+| <body>
+
+#data
+<script><!--<script></script><script></script>-- ></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>-- >"
+| <body>
+
+#data
+<script><!--<script></script><script></script>- -></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>- ->"
+| <body>
+
+#data
+<script><!--<script></script><script></script>- - ></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>- - >"
+| <body>
+
+#data
+<script><!--<script></script><script></script>-></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></script><script></script>->"
+| <body>
+
+#data
+<script><!--<script>--!></script>X
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 34 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script>--!></script>X"
+| <body>
+
+#data
+<script><!--<scr'+'ipt></script>--></script>
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 44 Unexpected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<scr'+'ipt>"
+| <body>
+| "-->"
+
+#data
+<script><!--<script></scr'+'ipt></script>X
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 42 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "<!--<script></scr'+'ipt></script>X"
+| <body>
+
+#data
+<style><!--<style></style>--></style>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 37 Unexpected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| "<!--<style>"
+| <body>
+| "-->"
+
+#data
+<style><!--</style>X
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| "<!--"
+| <body>
+| "X"
+
+#data
+<style><!--...</style>...--></style>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 36 Unexpected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| "<!--..."
+| <body>
+| "...-->"
+
+#data
+<style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>"
+| <body>
+| "X"
+
+#data
+<style><!--...<style><!--...--!></style>--></style>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 51 Unexpected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| "<!--...<style><!--...--!>"
+| <body>
+| "-->"
+
+#data
+<style><!--...</style><!-- --><style>@import ...</style>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| "<!--..."
+| <!-- -->
+| <style>
+| "@import ..."
+| <body>
+
+#data
+<style>...<style><!--...</style><!-- --></style>
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 48 Unexpected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| "...<style><!--..."
+| <!-- -->
+| <body>
+
+#data
+<style>...<!--[if IE]><style>...</style>X
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| "...<!--[if IE]><style>..."
+| <body>
+| "X"
+
+#data
+<title><!--<title></title>--></title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+Line: 1 Col: 37 Unexpected end tag (title).
+#document
+| <html>
+| <head>
+| <title>
+| "<!--<title>"
+| <body>
+| "-->"
+
+#data
+<title></title></title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| "</title>"
+| <body>
+
+#data
+<title>foo/title><link></head><body>X
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+Line: 1 Col: 37 Unexpected end of file. Expected end tag (title).
+#document
+| <html>
+| <head>
+| <title>
+| "foo/title><link></head><body>X"
+| <body>
+
+#data
+<noscript><!--<noscript></noscript>--></noscript>
+#errors
+Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE.
+Line: 1 Col: 49 Unexpected end tag (noscript).
+#document
+| <html>
+| <head>
+| <noscript>
+| "<!--<noscript>"
+| <body>
+| "-->"
+
+#data
+<noscript><!--</noscript>X<noscript>--></noscript>
+#errors
+Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <noscript>
+| "<!--"
+| <body>
+| "X"
+| <noscript>
+| "-->"
+
+#data
+<noscript><iframe></noscript>X
+#errors
+Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <noscript>
+| "<iframe>"
+| <body>
+| "X"
+
+#data
+<noframes><!--<noframes></noframes>--></noframes>
+#errors
+Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE.
+Line: 1 Col: 49 Unexpected end tag (noframes).
+#document
+| <html>
+| <head>
+| <noframes>
+| "<!--<noframes>"
+| <body>
+| "-->"
+
+#data
+<noframes><body><script><!--...</script></body></noframes></html>
+#errors
+Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <noframes>
+| "<body><script><!--...</script></body>"
+| <body>
+
+#data
+<textarea><!--<textarea></textarea>--></textarea>
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+Line: 1 Col: 49 Unexpected end tag (textarea).
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "<!--<textarea>"
+| "-->"
+
+#data
+<textarea></textarea></textarea>
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "</textarea>"
+
+#data
+<iframe><!--<iframe></iframe>--></iframe>
+#errors
+Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE.
+Line: 1 Col: 41 Unexpected end tag (iframe).
+#document
+| <html>
+| <head>
+| <body>
+| <iframe>
+| "<!--<iframe>"
+| "-->"
+
+#data
+<iframe>...<!--X->...<!--/X->...</iframe>
+#errors
+Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <iframe>
+| "...<!--X->...<!--/X->..."
+
+#data
+<xmp><!--<xmp></xmp>--></xmp>
+#errors
+Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE.
+Line: 1 Col: 29 Unexpected end tag (xmp).
+#document
+| <html>
+| <head>
+| <body>
+| <xmp>
+| "<!--<xmp>"
+| "-->"
+
+#data
+<noembed><!--<noembed></noembed>--></noembed>
+#errors
+Line: 1 Col: 9 Unexpected start tag (noembed). Expected DOCTYPE.
+Line: 1 Col: 45 Unexpected end tag (noembed).
+#document
+| <html>
+| <head>
+| <body>
+| <noembed>
+| "<!--<noembed>"
+| "-->"
+
+#data
+<!doctype html><table>
+
+#errors
+Line 2 Col 0 Unexpected end of file. Expected table content.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| "
+"
+
+#data
+<!doctype html><table><td><span><font></span><span>
+#errors
+Line 1 Col 26 Unexpected table cell start tag (td) in the table body phase.
+Line 1 Col 45 Unexpected end tag (span).
+Line 1 Col 51 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <span>
+| <font>
+| <font>
+| <span>
+
+#data
+<!doctype html><form><table></form><form></table></form>
+#errors
+35: Stray end tag “form”.
+41: Start tag “form” seen in “table”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <table>
+| <form>
diff --git a/html/testdata/webkit/tests17.dat b/html/testdata/webkit/tests17.dat
new file mode 100644
index 0000000..7b555f8
--- /dev/null
+++ b/html/testdata/webkit/tests17.dat
@@ -0,0 +1,153 @@
+#data
+<!doctype html><table><tbody><select><tr>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><table><tr><select><td>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<!doctype html><table><tr><td><select><td>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <select>
+| <td>
+
+#data
+<!doctype html><table><tr><th><select><td>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <th>
+| <select>
+| <td>
+
+#data
+<!doctype html><table><caption><select><tr>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <select>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><select><tr>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><td>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><th>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><tbody>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><thead>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><tfoot>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><select><caption>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><table><tr></table>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| "a"
diff --git a/html/testdata/webkit/tests18.dat b/html/testdata/webkit/tests18.dat
new file mode 100644
index 0000000..680e1f0
--- /dev/null
+++ b/html/testdata/webkit/tests18.dat
@@ -0,0 +1,269 @@
+#data
+<!doctype html><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+
+#data
+<!doctype html><table><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+| <table>
+
+#data
+<!doctype html><table><tbody><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+| <table>
+| <tbody>
+
+#data
+<!doctype html><table><tbody><tr><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><table><tbody><tr><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><table><td><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <plaintext>
+| "</plaintext>"
+
+#data
+<!doctype html><table><caption><plaintext></plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <plaintext>
+| "</plaintext>"
+
+#data
+<!doctype html><table><tr><style></script></style>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "abc"
+| <table>
+| <tbody>
+| <tr>
+| <style>
+| "</script>"
+
+#data
+<!doctype html><table><tr><script></style></script>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "abc"
+| <table>
+| <tbody>
+| <tr>
+| <script>
+| "</style>"
+
+#data
+<!doctype html><table><caption><style></script></style>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <style>
+| "</script>"
+| "abc"
+
+#data
+<!doctype html><table><td><style></script></style>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <style>
+| "</script>"
+| "abc"
+
+#data
+<!doctype html><select><script></style></script>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <script>
+| "</style>"
+| "abc"
+
+#data
+<!doctype html><table><select><script></style></script>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <script>
+| "</style>"
+| "abc"
+| <table>
+
+#data
+<!doctype html><table><tr><select><script></style></script>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <script>
+| "</style>"
+| "abc"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><frameset></frameset><noframes>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <noframes>
+| "abc"
+
+#data
+<!doctype html><frameset></frameset><noframes>abc</noframes><!--abc-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <noframes>
+| "abc"
+| <!-- abc -->
+
+#data
+<!doctype html><frameset></frameset></html><noframes>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <noframes>
+| "abc"
+
+#data
+<!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <noframes>
+| "abc"
+| <!-- abc -->
+
+#data
+<!doctype html><table><tr></tbody><tfoot>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <tfoot>
+
+#data
+<!doctype html><table><td><svg></svg>abc<td>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| "abc"
+| <td>
diff --git a/html/testdata/webkit/tests19.dat b/html/testdata/webkit/tests19.dat
new file mode 100644
index 0000000..0d62f5a
--- /dev/null
+++ b/html/testdata/webkit/tests19.dat
@@ -0,0 +1,1237 @@
+#data
+<!doctype html><math><mn DefinitionUrl="foo">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mn>
+| definitionURL="foo"
+
+#data
+<!doctype html><html></p><!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <!-- foo -->
+| <head>
+| <body>
+
+#data
+<!doctype html><head></head></p><!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <!-- foo -->
+| <body>
+
+#data
+<!doctype html><body><p><pre>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <pre>
+
+#data
+<!doctype html><body><p><listing>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <listing>
+
+#data
+<!doctype html><p><plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <plaintext>
+
+#data
+<!doctype html><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <h1>
+
+#data
+<!doctype html><form><isindex>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+
+#data
+<!doctype html><isindex action="POST">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| action="POST"
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| <hr>
+
+#data
+<!doctype html><isindex prompt="this is isindex">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <hr>
+| <label>
+| "this is isindex"
+| <input>
+| name="isindex"
+| <hr>
+
+#data
+<!doctype html><isindex type="hidden">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| type="hidden"
+| <hr>
+
+#data
+<!doctype html><isindex name="foo">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| <hr>
+
+#data
+<!doctype html><ruby><p><rp>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <p>
+| <rp>
+
+#data
+<!doctype html><ruby><div><span><rp>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <span>
+| <rp>
+
+#data
+<!doctype html><ruby><div><p><rp>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <p>
+| <rp>
+
+#data
+<!doctype html><ruby><p><rt>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <p>
+| <rt>
+
+#data
+<!doctype html><ruby><div><span><rt>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <span>
+| <rt>
+
+#data
+<!doctype html><ruby><div><p><rt>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <p>
+| <rt>
+
+#data
+<!doctype html><math/><foo>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <foo>
+
+#data
+<!doctype html><svg/><foo>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <foo>
+
+#data
+<!doctype html><div></body><!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+| <!-- foo -->
+
+#data
+<!doctype html><h1><div><h3><span></h1>foo
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <h1>
+| <div>
+| <h3>
+| <span>
+| "foo"
+
+#data
+<!doctype html><p></h3>foo
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| "foo"
+
+#data
+<!doctype html><h3><li>abc</h2>foo
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <h3>
+| <li>
+| "abc"
+| "foo"
+
+#data
+<!doctype html><table>abc<!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "abc"
+| <table>
+| <!-- foo -->
+
+#data
+<!doctype html><table> <!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| " "
+| <!-- foo -->
+
+#data
+<!doctype html><table> b <!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " b "
+| <table>
+| <!-- foo -->
+
+#data
+<!doctype html><select><option><option>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| <option>
+
+#data
+<!doctype html><select><option></optgroup>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+
+#data
+<!doctype html><select><option></optgroup>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+
+#data
+<!doctype html><p><math><mi><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math mi>
+| <p>
+| <h1>
+
+#data
+<!doctype html><p><math><mo><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math mo>
+| <p>
+| <h1>
+
+#data
+<!doctype html><p><math><mn><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math mn>
+| <p>
+| <h1>
+
+#data
+<!doctype html><p><math><ms><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math ms>
+| <p>
+| <h1>
+
+#data
+<!doctype html><p><math><mtext><p><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math mtext>
+| <p>
+| <h1>
+
+#data
+<!doctype html><frameset></noframes>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><html c=d><body></html><html a=b>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| a="b"
+| c="d"
+| <head>
+| <body>
+
+#data
+<!doctype html><html c=d><frameset></frameset></html><html a=b>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| a="b"
+| c="d"
+| <head>
+| <frameset>
+
+#data
+<!doctype html><html><frameset></frameset></html><!--foo-->
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <!-- foo -->
+
+#data
+<!doctype html><html><frameset></frameset></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| " "
+
+#data
+<!doctype html><html><frameset></frameset></html>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><html><frameset></frameset></html><p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><html><frameset></frameset></html></p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<html><frameset></frameset></html><!doctype html>
+#errors
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><body><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<!doctype html><p><frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<!doctype html><p>a<frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| "a"
+
+#data
+<!doctype html><p> <frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<!doctype html><pre><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+
+#data
+<!doctype html><listing><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <listing>
+
+#data
+<!doctype html><li><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <li>
+
+#data
+<!doctype html><dd><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <dd>
+
+#data
+<!doctype html><dt><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <dt>
+
+#data
+<!doctype html><button><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <button>
+
+#data
+<!doctype html><applet><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <applet>
+
+#data
+<!doctype html><marquee><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <marquee>
+
+#data
+<!doctype html><object><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <object>
+
+#data
+<!doctype html><table><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+
+#data
+<!doctype html><area><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <area>
+
+#data
+<!doctype html><basefont><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <basefont>
+| <frameset>
+
+#data
+<!doctype html><bgsound><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <bgsound>
+| <frameset>
+
+#data
+<!doctype html><br><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <br>
+
+#data
+<!doctype html><embed><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <embed>
+
+#data
+<!doctype html><img><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <img>
+
+#data
+<!doctype html><input><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <input>
+
+#data
+<!doctype html><keygen><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <keygen>
+
+#data
+<!doctype html><wbr><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <wbr>
+
+#data
+<!doctype html><hr><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <hr>
+
+#data
+<!doctype html><textarea></textarea><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+
+#data
+<!doctype html><xmp></xmp><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <xmp>
+
+#data
+<!doctype html><iframe></iframe><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <iframe>
+
+#data
+<!doctype html><select></select><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!doctype html><svg></svg><frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<!doctype html><math></math><frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<!doctype html><svg><foreignObject><div> <frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<!doctype html><svg>a</svg><frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "a"
+
+#data
+<!doctype html><svg> </svg><frameset><frame>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+| <frame>
+
+#data
+<html>aaa<frameset></frameset>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "aaa"
+
+#data
+<html> a <frameset></frameset>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "a "
+
+#data
+<!doctype html><div><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><div><body><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+
+#data
+<!doctype html><p><math></p>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| "a"
+
+#data
+<!doctype html><p><math><mn><span></p>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <math math>
+| <math mn>
+| <span>
+| <p>
+| "a"
+
+#data
+<!doctype html><math></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+
+#data
+<!doctype html><meta charset="ascii">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <meta>
+| charset="ascii"
+| <body>
+
+#data
+<!doctype html><meta http-equiv="content-type" content="text/html;charset=ascii">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <meta>
+| content="text/html;charset=ascii"
+| http-equiv="content-type"
+| <body>
+
+#data
+<!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset="utf8">
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <!-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -->
+| <meta>
+| charset="utf8"
+| <body>
+
+#data
+<!doctype html><html a=b><head></head><html c=d>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| a="b"
+| c="d"
+| <head>
+| <body>
+
+#data
+<!doctype html><image/>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <img>
+
+#data
+<!doctype html>a<i>b<table>c<b>d</i>e</b>f
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "a"
+| <i>
+| "bc"
+| <b>
+| "de"
+| "f"
+| <table>
+
+#data
+<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <i>
+| "a"
+| <b>
+| "b"
+| <b>
+| <div>
+| <b>
+| <i>
+| "c"
+| <a>
+| "d"
+| <a>
+| "e"
+| <a>
+| "f"
+| <table>
+
+#data
+<!doctype html><i>a<b>b<div>c<a>d</i>e</b>f
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <i>
+| "a"
+| <b>
+| "b"
+| <b>
+| <div>
+| <b>
+| <i>
+| "c"
+| <a>
+| "d"
+| <a>
+| "e"
+| <a>
+| "f"
+
+#data
+<!doctype html><table><i>a<b>b<div>c</i>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <i>
+| "a"
+| <b>
+| "b"
+| <b>
+| <div>
+| <i>
+| "c"
+| <table>
+
+#data
+<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <i>
+| "a"
+| <b>
+| "b"
+| <b>
+| <div>
+| <b>
+| <i>
+| "c"
+| <a>
+| "d"
+| <a>
+| "e"
+| <a>
+| "f"
+| <table>
+
+#data
+<!doctype html><table><i>a<div>b<tr>c<b>d</i>e
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <i>
+| "a"
+| <div>
+| "b"
+| <i>
+| "c"
+| <b>
+| "d"
+| <b>
+| "e"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><table><td><table><i>a<div>b<b>c</i>d
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <i>
+| "a"
+| <div>
+| <i>
+| "b"
+| <b>
+| "c"
+| <b>
+| "d"
+| <table>
+
+#data
+<!doctype html><body><bgsound>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <bgsound>
+
+#data
+<!doctype html><body><basefont>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <basefont>
+
+#data
+<!doctype html><a><b></a><basefont>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <basefont>
+
+#data
+<!doctype html><a><b></a><bgsound>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <bgsound>
+
+#data
+<!doctype html><figcaption><article></figcaption>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <figcaption>
+| <article>
+| "a"
+
+#data
+<!doctype html><summary><article></summary>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <summary>
+| <article>
+| "a"
+
+#data
+<!doctype html><p><a><plaintext>b
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <a>
+| <plaintext>
+| <a>
+| "b"
+
+#data
+<!DOCTYPE html><div>a<a></div>b<p>c</p>d
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+| "a"
+| <a>
+| <a>
+| "b"
+| <p>
+| "c"
+| "d"
diff --git a/html/testdata/webkit/tests2.dat b/html/testdata/webkit/tests2.dat
new file mode 100644
index 0000000..60d8592
--- /dev/null
+++ b/html/testdata/webkit/tests2.dat
@@ -0,0 +1,763 @@
+#data
+<!DOCTYPE html>Test
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "Test"
+
+#data
+<textarea>test</div>test
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+Line: 1 Col: 24 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "test</div>test"
+
+#data
+<table><td>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 11 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><td>test</tbody></table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "test"
+
+#data
+<frame>test
+#errors
+Line: 1 Col: 7 Unexpected start tag (frame). Expected DOCTYPE.
+Line: 1 Col: 7 Unexpected start tag frame. Ignored.
+#document
+| <html>
+| <head>
+| <body>
+| "test"
+
+#data
+<!DOCTYPE html><frameset>test
+#errors
+Line: 1 Col: 29 Unepxected characters in the frameset phase. Characters ignored.
+Line: 1 Col: 29 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><frameset><!DOCTYPE html>
+#errors
+Line: 1 Col: 40 Unexpected DOCTYPE. Ignored.
+Line: 1 Col: 40 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><font><p><b>test</font>
+#errors
+Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <font>
+| <p>
+| <font>
+| <b>
+| "test"
+
+#data
+<!DOCTYPE html><dt><div><dd>
+#errors
+Line: 1 Col: 28 Missing end tag (div, dt).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <dt>
+| <div>
+| <dd>
+
+#data
+<script></x
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+Line: 1 Col: 11 Unexpected end of file. Expected end tag (script).
+#document
+| <html>
+| <head>
+| <script>
+| "</x"
+| <body>
+
+#data
+<table><plaintext><td>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 18 Unexpected start tag (plaintext) in table context caused voodoo mode.
+Line: 1 Col: 22 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "<td>"
+| <table>
+
+#data
+<plaintext></plaintext>
+#errors
+Line: 1 Col: 11 Unexpected start tag (plaintext). Expected DOCTYPE.
+Line: 1 Col: 23 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <plaintext>
+| "</plaintext>"
+
+#data
+<!DOCTYPE html><table><tr>TEST
+#errors
+Line: 1 Col: 30 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 30 Unexpected end of file. Expected table content.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "TEST"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4>
+#errors
+Line: 1 Col: 37 Unexpected start tag (body).
+Line: 1 Col: 53 Unexpected start tag (body).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| t1="1"
+| t2="2"
+| t3="3"
+| t4="4"
+
+#data
+</b test
+#errors
+Line: 1 Col: 8 Unexpected end of file in attribute name.
+Line: 1 Col: 8 End tag contains unexpected attributes.
+Line: 1 Col: 8 Unexpected end tag (b). Expected DOCTYPE.
+Line: 1 Col: 8 Unexpected end tag (b) after the (implied) root element.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html></b test<b &=&>X
+#errors
+Line: 1 Col: 32 Named entity didn't end with ';'.
+Line: 1 Col: 33 End tag contains unexpected attributes.
+Line: 1 Col: 33 Unexpected end tag (b) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+
+#data
+<!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+Line: 1 Col: 54 Unexpected end of file in the tag name.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| type="text/x-foobar;baz"
+| "X</SCRipt"
+| <body>
+
+#data
+&
+#errors
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&"
+
+#data
+&#
+#errors
+Line: 1 Col: 1 Numeric entity expected. Got end of file instead.
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&#"
+
+#data
+&#X
+#errors
+Line: 1 Col: 3 Numeric entity expected but none found.
+Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&#X"
+
+#data
+&#x
+#errors
+Line: 1 Col: 3 Numeric entity expected but none found.
+Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&#x"
+
+#data
+-
+#errors
+Line: 1 Col: 4 Numeric entity didn't end with ';'.
+Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "-"
+
+#data
+&x-test
+#errors
+Line: 1 Col: 1 Named entity expected. Got none.
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&x-test"
+
+#data
+<!doctypehtml><p><li>
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <li>
+
+#data
+<!doctypehtml><p><dt>
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <dt>
+
+#data
+<!doctypehtml><p><dd>
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <dd>
+
+#data
+<!doctypehtml><p><form>
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+Line: 1 Col: 23 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <form>
+
+#data
+<!DOCTYPE html><p></P>X
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| "X"
+
+#data
+&
+#errors
+Line: 1 Col: 4 Named entity didn't end with ';'.
+Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&"
+
+#data
+&AMp;
+#errors
+Line: 1 Col: 1 Named entity expected. Got none.
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "&AMp;"
+
+#data
+<!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY>
+#errors
+Line: 1 Col: 110 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly>
+
+#data
+<!DOCTYPE html>X</body>X
+#errors
+Line: 1 Col: 24 Unexpected non-space characters in the after body phase.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "XX"
+
+#data
+<!DOCTYPE html><!-- X
+#errors
+Line: 1 Col: 21 Unexpected end of file in comment.
+#document
+| <!DOCTYPE html>
+| <!-- X -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><table><caption>test TEST</caption><td>test
+#errors
+Line: 1 Col: 54 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 58 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| "test TEST"
+| <tbody>
+| <tr>
+| <td>
+| "test"
+
+#data
+<!DOCTYPE html><select><option><optgroup>
+#errors
+Line: 1 Col: 41 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| <optgroup>
+
+#data
+<!DOCTYPE html><select><optgroup><option></optgroup><option><select><option>
+#errors
+Line: 1 Col: 68 Unexpected select start tag in the select phase treated as select end tag.
+Line: 1 Col: 76 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <optgroup>
+| <option>
+| <option>
+| <option>
+
+#data
+<!DOCTYPE html><select><optgroup><option><optgroup>
+#errors
+Line: 1 Col: 51 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <optgroup>
+| <option>
+| <optgroup>
+
+#data
+<!DOCTYPE html><datalist><option>foo</datalist>bar
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <datalist>
+| <option>
+| "foo"
+| "bar"
+
+#data
+<!DOCTYPE html><font><input><input></font>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <font>
+| <input>
+| <input>
+
+#data
+<!DOCTYPE html><!-- XXX - XXX -->
+#errors
+#document
+| <!DOCTYPE html>
+| <!-- XXX - XXX -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><!-- XXX - XXX
+#errors
+Line: 1 Col: 29 Unexpected end of file in comment (-)
+#document
+| <!DOCTYPE html>
+| <!-- XXX - XXX -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><!-- XXX - XXX - XXX -->
+#errors
+#document
+| <!DOCTYPE html>
+| <!-- XXX - XXX - XXX -->
+| <html>
+| <head>
+| <body>
+
+#data
+<isindex test=x name=x>
+#errors
+Line: 1 Col: 23 Unexpected start tag (isindex). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected start tag isindex. Don't use it!
+#document
+| <html>
+| <head>
+| <body>
+| <form>
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| test="x"
+| <hr>
+
+#data
+test
+test
+#errors
+Line: 2 Col: 4 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "test
+test"
+
+#data
+<!DOCTYPE html><body><title>test</body></title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <title>
+| "test</body>"
+
+#data
+<!DOCTYPE html><body><title>X</title><meta name=z><link rel=foo><style>
+x { content:"</style" } </style>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <title>
+| "X"
+| <meta>
+| name="z"
+| <link>
+| rel="foo"
+| <style>
+| "
+x { content:"</style" } "
+
+#data
+<!DOCTYPE html><select><optgroup></optgroup></select>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <optgroup>
+
+#data
+
+
+#errors
+Line: 2 Col: 1 Unexpected End of file. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html> <html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><script>
+</script> <title>x</title> </head>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <script>
+| "
+"
+| " "
+| <title>
+| "x"
+| " "
+| <body>
+
+#data
+<!DOCTYPE html><html><body><html id=x>
+#errors
+Line: 1 Col: 38 html needs to be the first start tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| id="x"
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html>X</body><html id="x">
+#errors
+Line: 1 Col: 36 Unexpected start tag token (html) in the after body phase.
+Line: 1 Col: 36 html needs to be the first start tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| id="x"
+| <head>
+| <body>
+| "X"
+
+#data
+<!DOCTYPE html><head><html id=x>
+#errors
+Line: 1 Col: 32 html needs to be the first start tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| id="x"
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html>X</html>X
+#errors
+Line: 1 Col: 24 Unexpected non-space characters in the after body phase.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "XX"
+
+#data
+<!DOCTYPE html>X</html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X "
+
+#data
+<!DOCTYPE html>X</html><p>X
+#errors
+Line: 1 Col: 26 Unexpected start tag (p).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+| <p>
+| "X"
+
+#data
+<!DOCTYPE html>X<p/x/y/z>
+#errors
+Line: 1 Col: 19 Expected a > after the /.
+Line: 1 Col: 21 Solidus (/) incorrectly placed in tag.
+Line: 1 Col: 23 Solidus (/) incorrectly placed in tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+| <p>
+| x=""
+| y=""
+| z=""
+
+#data
+<!DOCTYPE html><!--x--
+#errors
+Line: 1 Col: 22 Unexpected end of file in comment (--).
+#document
+| <!DOCTYPE html>
+| <!-- x -->
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE html><table><tr><td></p></table>
+#errors
+Line: 1 Col: 34 Unexpected end tag (p). Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <p>
+
+#data
+<!DOCTYPE <!DOCTYPE HTML>><!--<!--x-->-->
+#errors
+Line: 1 Col: 20 Expected space or '>'. Got ''
+Line: 1 Col: 25 Erroneous DOCTYPE.
+Line: 1 Col: 35 Unexpected character in comment found.
+#document
+| <!DOCTYPE <!doctype>
+| <html>
+| <head>
+| <body>
+| ">"
+| <!-- <!--x -->
+| "-->"
+
+#data
+<!doctype html><div><form></form><div></div></div>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+| <form>
+| <div>
diff --git a/html/testdata/webkit/tests20.dat b/html/testdata/webkit/tests20.dat
new file mode 100644
index 0000000..6bd8256
--- /dev/null
+++ b/html/testdata/webkit/tests20.dat
@@ -0,0 +1,455 @@
+#data
+<!doctype html><p><button><button>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <button>
+
+#data
+<!doctype html><p><button><address>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <address>
+
+#data
+<!doctype html><p><button><blockquote>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <blockquote>
+
+#data
+<!doctype html><p><button><menu>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <menu>
+
+#data
+<!doctype html><p><button><p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <p>
+
+#data
+<!doctype html><p><button><ul>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <ul>
+
+#data
+<!doctype html><p><button><h1>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <h1>
+
+#data
+<!doctype html><p><button><h6>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <h6>
+
+#data
+<!doctype html><p><button><listing>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <listing>
+
+#data
+<!doctype html><p><button><pre>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <pre>
+
+#data
+<!doctype html><p><button><form>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <form>
+
+#data
+<!doctype html><p><button><li>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <li>
+
+#data
+<!doctype html><p><button><dd>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <dd>
+
+#data
+<!doctype html><p><button><dt>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <dt>
+
+#data
+<!doctype html><p><button><plaintext>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <plaintext>
+
+#data
+<!doctype html><p><button><table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <table>
+
+#data
+<!doctype html><p><button><hr>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <hr>
+
+#data
+<!doctype html><p><button><xmp>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <xmp>
+
+#data
+<!doctype html><p><button></p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <button>
+| <p>
+
+#data
+<!doctype html><address><button></address>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <address>
+| <button>
+| "a"
+
+#data
+<!doctype html><address><button></address>a
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <address>
+| <button>
+| "a"
+
+#data
+<p><table></p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <p>
+| <table>
+
+#data
+<!doctype html><svg>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<!doctype html><p><figcaption>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <figcaption>
+
+#data
+<!doctype html><p><summary>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <summary>
+
+#data
+<!doctype html><form><table><form>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <table>
+
+#data
+<!doctype html><table><form><form>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <form>
+
+#data
+<!doctype html><table><form></table><form>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <form>
+
+#data
+<!doctype html><svg><foreignObject><p>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg foreignObject>
+| <p>
+
+#data
+<!doctype html><svg><title>abc
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg title>
+| "abc"
+
+#data
+<option><span><option>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <option>
+| <span>
+| <option>
+
+#data
+<option><option>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <option>
+| <option>
+
+#data
+<math><annotation-xml><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| <div>
+
+#data
+<math><annotation-xml encoding="application/svg+xml"><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding="application/svg+xml"
+| <div>
+
+#data
+<math><annotation-xml encoding="application/xhtml+xml"><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding="application/xhtml+xml"
+| <div>
+
+#data
+<math><annotation-xml encoding="aPPlication/xhtmL+xMl"><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding="aPPlication/xhtmL+xMl"
+| <div>
+
+#data
+<math><annotation-xml encoding="text/html"><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding="text/html"
+| <div>
+
+#data
+<math><annotation-xml encoding="Text/htmL"><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding="Text/htmL"
+| <div>
+
+#data
+<math><annotation-xml encoding=" text/html "><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| encoding=" text/html "
+| <div>
diff --git a/html/testdata/webkit/tests21.dat b/html/testdata/webkit/tests21.dat
new file mode 100644
index 0000000..1260ec0
--- /dev/null
+++ b/html/testdata/webkit/tests21.dat
@@ -0,0 +1,221 @@
+#data
+<svg><![CDATA[foo]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "foo"
+
+#data
+<math><![CDATA[foo]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| "foo"
+
+#data
+<div><![CDATA[foo]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <!-- [CDATA[foo]] -->
+
+#data
+<svg><![CDATA[foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "foo"
+
+#data
+<svg><![CDATA[foo
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "foo"
+
+#data
+<svg><![CDATA[
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<svg><![CDATA[]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+
+#data
+<svg><![CDATA[]] >]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "]] >"
+
+#data
+<svg><![CDATA[]] >]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "]] >"
+
+#data
+<svg><![CDATA[]]
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "]]"
+
+#data
+<svg><![CDATA[]
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "]"
+
+#data
+<svg><![CDATA[]>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "]>a"
+
+#data
+<svg><foreignObject><div><![CDATA[foo]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg foreignObject>
+| <div>
+| <!-- [CDATA[foo]] -->
+
+#data
+<svg><![CDATA[<svg>]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>"
+
+#data
+<svg><![CDATA[</svg>a]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "</svg>a"
+
+#data
+<svg><![CDATA[<svg>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>a"
+
+#data
+<svg><![CDATA[</svg>a
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "</svg>a"
+
+#data
+<svg><![CDATA[<svg>]]><path>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>"
+| <svg path>
+
+#data
+<svg><![CDATA[<svg>]]></path>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>"
+
+#data
+<svg><![CDATA[<svg>]]><!--path-->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>"
+| <!-- path -->
+
+#data
+<svg><![CDATA[<svg>]]>path
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<svg>path"
+
+#data
+<svg><![CDATA[<!--svg-->]]>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| "<!--svg-->"
diff --git a/html/testdata/webkit/tests22.dat b/html/testdata/webkit/tests22.dat
new file mode 100644
index 0000000..aab27b2
--- /dev/null
+++ b/html/testdata/webkit/tests22.dat
@@ -0,0 +1,157 @@
+#data
+<a><b><big><em><strong><div>X</a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <big>
+| <em>
+| <strong>
+| <big>
+| <em>
+| <strong>
+| <div>
+| <a>
+| "X"
+
+#data
+<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8>A</a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <b>
+| <div>
+| id="1"
+| <a>
+| <div>
+| id="2"
+| <a>
+| <div>
+| id="3"
+| <a>
+| <div>
+| id="4"
+| <a>
+| <div>
+| id="5"
+| <a>
+| <div>
+| id="6"
+| <a>
+| <div>
+| id="7"
+| <a>
+| <div>
+| id="8"
+| <a>
+| "A"
+
+#data
+<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9>A</a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <b>
+| <div>
+| id="1"
+| <a>
+| <div>
+| id="2"
+| <a>
+| <div>
+| id="3"
+| <a>
+| <div>
+| id="4"
+| <a>
+| <div>
+| id="5"
+| <a>
+| <div>
+| id="6"
+| <a>
+| <div>
+| id="7"
+| <a>
+| <div>
+| id="8"
+| <a>
+| <div>
+| id="9"
+| "A"
+
+#data
+<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9><div id=10>A</a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <b>
+| <b>
+| <div>
+| id="1"
+| <a>
+| <div>
+| id="2"
+| <a>
+| <div>
+| id="3"
+| <a>
+| <div>
+| id="4"
+| <a>
+| <div>
+| id="5"
+| <a>
+| <div>
+| id="6"
+| <a>
+| <div>
+| id="7"
+| <a>
+| <div>
+| id="8"
+| <a>
+| <div>
+| id="9"
+| <div>
+| id="10"
+| "A"
+
+#data
+<cite><b><cite><i><cite><i><cite><i><div>X</b>TEST
+#errors
+Line: 1 Col: 6 Unexpected start tag (cite). Expected DOCTYPE.
+Line: 1 Col: 46 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 50 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <cite>
+| <b>
+| <cite>
+| <i>
+| <cite>
+| <i>
+| <cite>
+| <i>
+| <i>
+| <i>
+| <div>
+| <b>
+| "X"
+| "TEST"
diff --git a/html/testdata/webkit/tests23.dat b/html/testdata/webkit/tests23.dat
new file mode 100644
index 0000000..34d2a73
--- /dev/null
+++ b/html/testdata/webkit/tests23.dat
@@ -0,0 +1,155 @@
+#data
+<p><font size=4><font color=red><font size=4><font size=4><font size=4><font size=4><font size=4><font color=red><p>X
+#errors
+3: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+116: Unclosed elements.
+117: End of file seen and there were open elements.
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <font>
+| size="4"
+| <font>
+| color="red"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| color="red"
+| <p>
+| <font>
+| color="red"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| color="red"
+| "X"
+
+#data
+<p><font size=4><font size=4><font size=4><font size=4><p>X
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <p>
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| "X"
+
+#data
+<p><font size=4><font size=4><font size=4><font size="5"><font size=4><p>X
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="5"
+| <font>
+| size="4"
+| <p>
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="5"
+| <font>
+| size="4"
+| "X"
+
+#data
+<p><font size=4 id=a><font size=4 id=b><font size=4><font size=4><p>X
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <font>
+| id="a"
+| size="4"
+| <font>
+| id="b"
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| <p>
+| <font>
+| id="a"
+| size="4"
+| <font>
+| id="b"
+| size="4"
+| <font>
+| size="4"
+| <font>
+| size="4"
+| "X"
+
+#data
+<p><b id=a><b id=a><b id=a><b><object><b id=a><b id=a>X</object><p>Y
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <b>
+| id="a"
+| <b>
+| id="a"
+| <b>
+| id="a"
+| <b>
+| <object>
+| <b>
+| id="a"
+| <b>
+| id="a"
+| "X"
+| <p>
+| <b>
+| id="a"
+| <b>
+| id="a"
+| <b>
+| id="a"
+| <b>
+| "Y"
diff --git a/html/testdata/webkit/tests24.dat b/html/testdata/webkit/tests24.dat
new file mode 100644
index 0000000..f6dc7eb
--- /dev/null
+++ b/html/testdata/webkit/tests24.dat
@@ -0,0 +1,79 @@
+#data
+<!DOCTYPE html>≂̸
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "≂̸"
+
+#data
+<!DOCTYPE html>≂̸A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "≂̸A"
+
+#data
+<!DOCTYPE html>  
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " "
+
+#data
+<!DOCTYPE html>  A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| " A"
+
+#data
+<!DOCTYPE html>⊂⃒
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "⊂⃒"
+
+#data
+<!DOCTYPE html>⊂⃒A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "⊂⃒A"
+
+#data
+<!DOCTYPE html>𝔾
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "𝔾"
+
+#data
+<!DOCTYPE html>𝔾A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "𝔾A"
diff --git a/html/testdata/webkit/tests25.dat b/html/testdata/webkit/tests25.dat
new file mode 100644
index 0000000..00de729
--- /dev/null
+++ b/html/testdata/webkit/tests25.dat
@@ -0,0 +1,219 @@
+#data
+<!DOCTYPE html><body><foo>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <foo>
+| "A"
+
+#data
+<!DOCTYPE html><body><area>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <area>
+| "A"
+
+#data
+<!DOCTYPE html><body><base>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <base>
+| "A"
+
+#data
+<!DOCTYPE html><body><basefont>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <basefont>
+| "A"
+
+#data
+<!DOCTYPE html><body><bgsound>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <bgsound>
+| "A"
+
+#data
+<!DOCTYPE html><body><br>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <br>
+| "A"
+
+#data
+<!DOCTYPE html><body><col>A
+#errors
+26: Stray start tag “col”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "A"
+
+#data
+<!DOCTYPE html><body><command>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <command>
+| "A"
+
+#data
+<!DOCTYPE html><body><embed>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <embed>
+| "A"
+
+#data
+<!DOCTYPE html><body><frame>A
+#errors
+26: Stray start tag “frame”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "A"
+
+#data
+<!DOCTYPE html><body><hr>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <hr>
+| "A"
+
+#data
+<!DOCTYPE html><body><img>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <img>
+| "A"
+
+#data
+<!DOCTYPE html><body><input>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <input>
+| "A"
+
+#data
+<!DOCTYPE html><body><keygen>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <keygen>
+| "A"
+
+#data
+<!DOCTYPE html><body><link>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <link>
+| "A"
+
+#data
+<!DOCTYPE html><body><meta>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <meta>
+| "A"
+
+#data
+<!DOCTYPE html><body><param>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <param>
+| "A"
+
+#data
+<!DOCTYPE html><body><source>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <source>
+| "A"
+
+#data
+<!DOCTYPE html><body><track>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <track>
+| "A"
+
+#data
+<!DOCTYPE html><body><wbr>A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <wbr>
+| "A"
diff --git a/html/testdata/webkit/tests26.dat b/html/testdata/webkit/tests26.dat
new file mode 100644
index 0000000..fae11ff
--- /dev/null
+++ b/html/testdata/webkit/tests26.dat
@@ -0,0 +1,313 @@
+#data
+<!DOCTYPE html><body><a href='#1'><nobr>1<nobr></a><br><a href='#2'><nobr>2<nobr></a><br><a href='#3'><nobr>3<nobr></a>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <a>
+| href="#1"
+| <nobr>
+| "1"
+| <nobr>
+| <nobr>
+| <br>
+| <a>
+| href="#2"
+| <a>
+| href="#2"
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| <br>
+| <a>
+| href="#3"
+| <a>
+| href="#3"
+| <nobr>
+| "3"
+| <nobr>
+
+#data
+<!DOCTYPE html><body><b><nobr>1<nobr></b><i><nobr>2<nobr></i>3
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <nobr>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| "3"
+
+#data
+<!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| "3"
+| <table>
+
+#data
+<!DOCTYPE html><body><b><nobr>1<table><tr><td><nobr></b><i><nobr>2<nobr></i>3
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| "3"
+
+#data
+<!DOCTYPE html><body><b><nobr>1<div><nobr></b><i><nobr>2<nobr></i>3
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <div>
+| <b>
+| <nobr>
+| <nobr>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| "3"
+
+#data
+<!DOCTYPE html><body><b><nobr>1<nobr></b><div><i><nobr>2<nobr></i>3
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <nobr>
+| <div>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+| <nobr>
+| <nobr>
+| "3"
+
+#data
+<!DOCTYPE html><body><b><nobr>1<nobr><ins></b><i><nobr>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <nobr>
+| <ins>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+
+#data
+<!DOCTYPE html><body><b><nobr>1<ins><nobr></b><i>2
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| <nobr>
+| "1"
+| <ins>
+| <nobr>
+| <nobr>
+| <i>
+| "2"
+
+#data
+<!DOCTYPE html><body><b>1<nobr></b><i><nobr>2</i>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <b>
+| "1"
+| <nobr>
+| <nobr>
+| <i>
+| <i>
+| <nobr>
+| "2"
+
+#data
+<p><code x</code></p>
+
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <code>
+| code=""
+| x<=""
+| <code>
+| code=""
+| x<=""
+| "
+"
+
+#data
+<!DOCTYPE html><svg><foreignObject><p><i></p>a
+#errors
+45: End tag “p” seen, but there were open elements.
+41: Unclosed element “i”.
+46: End of file seen and there were open elements.
+35: Unclosed element “foreignObject”.
+20: Unclosed element “svg”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg foreignObject>
+| <p>
+| <i>
+| <i>
+| "a"
+
+#data
+<!DOCTYPE html><table><tr><td><svg><foreignObject><p><i></p>a
+#errors
+56: End tag “p” seen, but there were open elements.
+52: Unclosed element “i”.
+57: End of file seen and there were open elements.
+46: Unclosed element “foreignObject”.
+31: Unclosed element “svg”.
+22: Unclosed element “table”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg foreignObject>
+| <p>
+| <i>
+| <i>
+| "a"
+
+#data
+<!DOCTYPE html><math><mtext><p><i></p>a
+#errors
+38: End tag “p” seen, but there were open elements.
+34: Unclosed element “i”.
+39: End of file in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mtext>
+| <p>
+| <i>
+| <i>
+| "a"
+
+#data
+<!DOCTYPE html><table><tr><td><math><mtext><p><i></p>a
+#errors
+53: End tag “p” seen, but there were open elements.
+49: Unclosed element “i”.
+54: End of file in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <math math>
+| <math mtext>
+| <p>
+| <i>
+| <i>
+| "a"
+
+#data
+<!DOCTYPE html><body><div><!/div>a
+#errors
+29: Bogus comment.
+34: End of file seen and there were open elements.
+26: Unclosed element “div”.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <div>
+| <!-- /div -->
+| "a"
diff --git a/html/testdata/webkit/tests3.dat b/html/testdata/webkit/tests3.dat
new file mode 100644
index 0000000..38dc501
--- /dev/null
+++ b/html/testdata/webkit/tests3.dat
@@ -0,0 +1,305 @@
+#data
+<head></head><style></style>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected start tag (style) that can be in head. Moved.
+#document
+| <html>
+| <head>
+| <style>
+| <body>
+
+#data
+<head></head><script></script>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 21 Unexpected start tag (script) that can be in head. Moved.
+#document
+| <html>
+| <head>
+| <script>
+| <body>
+
+#data
+<head></head><!-- --><style></style><!-- --><script></script>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+Line: 1 Col: 28 Unexpected start tag (style) that can be in head. Moved.
+#document
+| <html>
+| <head>
+| <style>
+| <script>
+| <!-- -->
+| <!-- -->
+| <body>
+
+#data
+<head></head><!-- -->x<style></style><!-- --><script></script>
+#errors
+Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <!-- -->
+| <body>
+| "x"
+| <style>
+| <!-- -->
+| <script>
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>
+</pre></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>
+foo</pre></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "foo"
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>
+
+foo</pre></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "
+foo"
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>
+foo
+</pre></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "foo
+"
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>x</pre><span>
+</span></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "x"
+| <span>
+| "
+"
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>x
+y</pre></body></html>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "x
+y"
+
+#data
+<!DOCTYPE html><html><head></head><body><pre>x<div>
+y</pre></body></html>
+#errors
+Line: 2 Col: 7 End tag (pre) seen too early. Expected other end tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "x"
+| <div>
+| "
+y"
+
+#data
+<!DOCTYPE html><pre>

A</pre>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <pre>
+| "
+A"
+
+#data
+<!DOCTYPE html><HTML><META><HEAD></HEAD></HTML>
+#errors
+Line: 1 Col: 33 Unexpected start tag head in existing head. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <meta>
+| <body>
+
+#data
+<!DOCTYPE html><HTML><HEAD><head></HEAD></HTML>
+#errors
+Line: 1 Col: 33 Unexpected start tag head in existing head. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<textarea>foo<span>bar</span><i>baz
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+Line: 1 Col: 35 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "foo<span>bar</span><i>baz"
+
+#data
+<title>foo<span>bar</em><i>baz
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+Line: 1 Col: 30 Unexpected end of file. Expected end tag (title).
+#document
+| <html>
+| <head>
+| <title>
+| "foo<span>bar</em><i>baz"
+| <body>
+
+#data
+<!DOCTYPE html><textarea>
+</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+
+#data
+<!DOCTYPE html><textarea>
+foo</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "foo"
+
+#data
+<!DOCTYPE html><textarea>
+
+foo</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <textarea>
+| "
+foo"
+
+#data
+<!DOCTYPE html><html><head></head><body><ul><li><div><p><li></ul></body></html>
+#errors
+Line: 1 Col: 60 Missing end tag (div, li).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <ul>
+| <li>
+| <div>
+| <p>
+| <li>
+
+#data
+<!doctype html><nobr><nobr><nobr>
+#errors
+Line: 1 Col: 27 Unexpected start tag (nobr) implies end tag (nobr).
+Line: 1 Col: 33 Unexpected start tag (nobr) implies end tag (nobr).
+Line: 1 Col: 33 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <nobr>
+| <nobr>
+| <nobr>
+
+#data
+<!doctype html><nobr><nobr></nobr><nobr>
+#errors
+Line: 1 Col: 27 Unexpected start tag (nobr) implies end tag (nobr).
+Line: 1 Col: 40 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <nobr>
+| <nobr>
+| <nobr>
+
+#data
+<!doctype html><html><body><p><table></table></body></html>
+#errors
+Not known
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <table>
+
+#data
+<p><table></table>
+#errors
+Not known
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <table>
diff --git a/html/testdata/webkit/tests4.dat b/html/testdata/webkit/tests4.dat
new file mode 100644
index 0000000..3c50632
--- /dev/null
+++ b/html/testdata/webkit/tests4.dat
@@ -0,0 +1,59 @@
+#data
+direct div content
+#errors
+#document-fragment
+div
+#document
+| "direct div content"
+
+#data
+direct textarea content
+#errors
+#document-fragment
+textarea
+#document
+| "direct textarea content"
+
+#data
+textarea content with <em>pseudo</em> <foo>markup
+#errors
+#document-fragment
+textarea
+#document
+| "textarea content with <em>pseudo</em> <foo>markup"
+
+#data
+this is CDATA inside a <style> element
+#errors
+#document-fragment
+style
+#document
+| "this is CDATA inside a <style> element"
+
+#data
+</plaintext>
+#errors
+#document-fragment
+plaintext
+#document
+| "</plaintext>"
+
+#data
+setting html's innerHTML
+#errors
+Line: 1 Col: 24 Unexpected EOF in inner html mode.
+#document-fragment
+html
+#document
+| <head>
+| <body>
+| "setting html's innerHTML"
+
+#data
+<title>setting head's innerHTML</title>
+#errors
+#document-fragment
+head
+#document
+| <title>
+| "setting head's innerHTML"
diff --git a/html/testdata/webkit/tests5.dat b/html/testdata/webkit/tests5.dat
new file mode 100644
index 0000000..d7b5128
--- /dev/null
+++ b/html/testdata/webkit/tests5.dat
@@ -0,0 +1,191 @@
+#data
+<style> <!-- </style>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end of file. Expected end tag (style).
+#document
+| <html>
+| <head>
+| <style>
+| " <!-- "
+| <body>
+| "x"
+
+#data
+<style> <!-- </style> --> </style>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| " <!-- "
+| " "
+| <body>
+| "--> x"
+
+#data
+<style> <!--> </style>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| " <!--> "
+| <body>
+| "x"
+
+#data
+<style> <!---> </style>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| " <!---> "
+| <body>
+| "x"
+
+#data
+<iframe> <!---> </iframe>x
+#errors
+Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <iframe>
+| " <!---> "
+| "x"
+
+#data
+<iframe> <!--- </iframe>->x</iframe> --> </iframe>x
+#errors
+Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <iframe>
+| " <!--- "
+| "->x --> x"
+
+#data
+<script> <!-- </script> --> </script>x
+#errors
+Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <script>
+| " <!-- "
+| " "
+| <body>
+| "--> x"
+
+#data
+<title> <!-- </title> --> </title>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| " <!-- "
+| " "
+| <body>
+| "--> x"
+
+#data
+<textarea> <!--- </textarea>->x</textarea> --> </textarea>x
+#errors
+Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <textarea>
+| " <!--- "
+| "->x --> x"
+
+#data
+<style> <!</-- </style>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <style>
+| " <!</-- "
+| <body>
+| "x"
+
+#data
+<p><xmp></xmp>
+#errors
+XXX: Unknown
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <xmp>
+
+#data
+<xmp> <!-- > --> </xmp>
+#errors
+Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <xmp>
+| " <!-- > --> "
+
+#data
+<title>&</title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| "&"
+| <body>
+
+#data
+<title><!--&--></title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <title>
+| "<!--&-->"
+| <body>
+
+#data
+<title><!--</title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+Line: 1 Col: 19 Unexpected end of file. Expected end tag (title).
+#document
+| <html>
+| <head>
+| <title>
+| "<!--"
+| <body>
+
+#data
+<noscript><!--</noscript>--></noscript>
+#errors
+Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <noscript>
+| "<!--"
+| <body>
+| "-->"
diff --git a/html/testdata/webkit/tests6.dat b/html/testdata/webkit/tests6.dat
new file mode 100644
index 0000000..f28ece4
--- /dev/null
+++ b/html/testdata/webkit/tests6.dat
@@ -0,0 +1,663 @@
+#data
+<!doctype html></head> <head>
+#errors
+Line: 1 Col: 29 Unexpected start tag head. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| " "
+| <body>
+
+#data
+<!doctype html><form><div></form><div>
+#errors
+33: End tag "form" seen but there were unclosed elements.
+38: End of file seen and there were open elements.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <form>
+| <div>
+| <div>
+
+#data
+<!doctype html><title>&</title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "&"
+| <body>
+
+#data
+<!doctype html><title><!--&--></title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "<!--&-->"
+| <body>
+
+#data
+<!doctype>
+#errors
+Line: 1 Col: 9 No space after literal string 'DOCTYPE'.
+Line: 1 Col: 10 Unexpected > character. Expected DOCTYPE name.
+Line: 1 Col: 10 Erroneous DOCTYPE.
+#document
+| <!DOCTYPE >
+| <html>
+| <head>
+| <body>
+
+#data
+<!---x
+#errors
+Line: 1 Col: 6 Unexpected end of file in comment.
+Line: 1 Col: 6 Unexpected End of file. Expected DOCTYPE.
+#document
+| <!-- -x -->
+| <html>
+| <head>
+| <body>
+
+#data
+<body>
+<div>
+#errors
+Line: 1 Col: 6 Unexpected start tag (body).
+Line: 2 Col: 5 Expected closing tag. Unexpected end of file.
+#document-fragment
+div
+#document
+| "
+"
+| <div>
+
+#data
+<frameset></frameset>
+foo
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 2 Col: 3 Unexpected non-space characters in the after frameset phase. Ignored.
+#document
+| <html>
+| <head>
+| <frameset>
+| "
+"
+
+#data
+<frameset></frameset>
+<noframes>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 2 Col: 10 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <frameset>
+| "
+"
+| <noframes>
+
+#data
+<frameset></frameset>
+<div>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 2 Col: 5 Unexpected start tag (div) in the after frameset phase. Ignored.
+#document
+| <html>
+| <head>
+| <frameset>
+| "
+"
+
+#data
+<frameset></frameset>
+</html>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <frameset>
+| "
+"
+
+#data
+<frameset></frameset>
+</div>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 2 Col: 6 Unexpected end tag (div) in the after frameset phase. Ignored.
+#document
+| <html>
+| <head>
+| <frameset>
+| "
+"
+
+#data
+<form><form>
+#errors
+Line: 1 Col: 6 Unexpected start tag (form). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected start tag (form).
+Line: 1 Col: 12 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <form>
+
+#data
+<button><button>
+#errors
+Line: 1 Col: 8 Unexpected start tag (button). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected start tag (button) implies end tag (button).
+Line: 1 Col: 16 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <button>
+| <button>
+
+#data
+<table><tr><td></th>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end tag (th). Ignored.
+Line: 1 Col: 20 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><caption><td>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end tag (td). Ignored.
+Line: 1 Col: 20 Unexpected table cell start tag (td) in the table body phase.
+Line: 1 Col: 20 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><caption><div>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 21 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <div>
+
+#data
+</caption><div>
+#errors
+Line: 1 Col: 10 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 15 Expected closing tag. Unexpected end of file.
+#document-fragment
+caption
+#document
+| <div>
+
+#data
+<table><caption><div></caption>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 31 Unexpected end tag (caption). Missing end tag (div).
+Line: 1 Col: 31 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <div>
+
+#data
+<table><caption></table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 24 Unexpected end table tag in caption. Generates implied end caption.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+
+#data
+</table><div>
+#errors
+Line: 1 Col: 8 Unexpected end table tag in caption. Generates implied end caption.
+Line: 1 Col: 8 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 13 Expected closing tag. Unexpected end of file.
+#document-fragment
+caption
+#document
+| <div>
+
+#data
+<table><caption></body></col></colgroup></html></tbody></td></tfoot></th></thead></tr>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 23 Unexpected end tag (body). Ignored.
+Line: 1 Col: 29 Unexpected end tag (col). Ignored.
+Line: 1 Col: 40 Unexpected end tag (colgroup). Ignored.
+Line: 1 Col: 47 Unexpected end tag (html). Ignored.
+Line: 1 Col: 55 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 60 Unexpected end tag (td). Ignored.
+Line: 1 Col: 68 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 73 Unexpected end tag (th). Ignored.
+Line: 1 Col: 81 Unexpected end tag (thead). Ignored.
+Line: 1 Col: 86 Unexpected end tag (tr). Ignored.
+Line: 1 Col: 86 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+
+#data
+<table><caption><div></div>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 27 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <div>
+
+#data
+<table><tr><td></body></caption></col></colgroup></html>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end tag (body). Ignored.
+Line: 1 Col: 32 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 38 Unexpected end tag (col). Ignored.
+Line: 1 Col: 49 Unexpected end tag (colgroup). Ignored.
+Line: 1 Col: 56 Unexpected end tag (html). Ignored.
+Line: 1 Col: 56 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+</table></tbody></tfoot></thead></tr><div>
+#errors
+Line: 1 Col: 8 Unexpected end tag (table). Ignored.
+Line: 1 Col: 16 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 24 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 32 Unexpected end tag (thead). Ignored.
+Line: 1 Col: 37 Unexpected end tag (tr). Ignored.
+Line: 1 Col: 42 Expected closing tag. Unexpected end of file.
+#document-fragment
+td
+#document
+| <div>
+
+#data
+<table><colgroup>foo
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 20 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| "foo"
+| <table>
+| <colgroup>
+
+#data
+foo<col>
+#errors
+Line: 1 Col: 3 Unexpected end tag (colgroup). Ignored.
+#document-fragment
+colgroup
+#document
+| <col>
+
+#data
+<table><colgroup></col>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 23 This element (col) has no end tag.
+Line: 1 Col: 23 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <colgroup>
+
+#data
+<frameset><div>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 1 Col: 15 Unexpected start tag token (div) in the frameset phase. Ignored.
+Line: 1 Col: 15 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+</frameset><frame>
+#errors
+Line: 1 Col: 11 Unexpected end tag token (frameset) in the frameset phase (innerHTML).
+#document-fragment
+frameset
+#document
+| <frame>
+
+#data
+<frameset></div>
+#errors
+Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected end tag token (div) in the frameset phase. Ignored.
+Line: 1 Col: 16 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+</body><div>
+#errors
+Line: 1 Col: 7 Unexpected end tag (body). Ignored.
+Line: 1 Col: 12 Expected closing tag. Unexpected end of file.
+#document-fragment
+body
+#document
+| <div>
+
+#data
+<table><tr><div>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected start tag (div) in table context caused voodoo mode.
+Line: 1 Col: 16 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <table>
+| <tbody>
+| <tr>
+
+#data
+</tr><td>
+#errors
+Line: 1 Col: 5 Unexpected end tag (tr). Ignored.
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+</tbody></tfoot></thead><td>
+#errors
+Line: 1 Col: 8 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 16 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 24 Unexpected end tag (thead). Ignored.
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<table><tr><div><td>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 16 Unexpected start tag (div) in table context caused voodoo mode.
+Line: 1 Col: 20 Unexpected implied end tag (div) in the table row phase.
+Line: 1 Col: 20 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<caption><col><colgroup><tbody><tfoot><thead><tr>
+#errors
+Line: 1 Col: 9 Unexpected start tag (caption).
+Line: 1 Col: 14 Unexpected start tag (col).
+Line: 1 Col: 24 Unexpected start tag (colgroup).
+Line: 1 Col: 31 Unexpected start tag (tbody).
+Line: 1 Col: 38 Unexpected start tag (tfoot).
+Line: 1 Col: 45 Unexpected start tag (thead).
+Line: 1 Col: 49 Unexpected end of file. Expected table content.
+#document-fragment
+tbody
+#document
+| <tr>
+
+#data
+<table><tbody></thead>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 22 Unexpected end tag (thead) in the table body phase. Ignored.
+Line: 1 Col: 22 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+
+#data
+</table><tr>
+#errors
+Line: 1 Col: 8 Unexpected end tag (table). Ignored.
+Line: 1 Col: 12 Unexpected end of file. Expected table content.
+#document-fragment
+tbody
+#document
+| <tr>
+
+#data
+<table><tbody></body></caption></col></colgroup></html></td></th></tr>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 21 Unexpected end tag (body) in the table body phase. Ignored.
+Line: 1 Col: 31 Unexpected end tag (caption) in the table body phase. Ignored.
+Line: 1 Col: 37 Unexpected end tag (col) in the table body phase. Ignored.
+Line: 1 Col: 48 Unexpected end tag (colgroup) in the table body phase. Ignored.
+Line: 1 Col: 55 Unexpected end tag (html) in the table body phase. Ignored.
+Line: 1 Col: 60 Unexpected end tag (td) in the table body phase. Ignored.
+Line: 1 Col: 65 Unexpected end tag (th) in the table body phase. Ignored.
+Line: 1 Col: 70 Unexpected end tag (tr) in the table body phase. Ignored.
+Line: 1 Col: 70 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+
+#data
+<table><tbody></div>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 20 Unexpected end tag (div) in table context caused voodoo mode.
+Line: 1 Col: 20 End tag (div) seen too early. Expected other end tag.
+Line: 1 Col: 20 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+
+#data
+<table><table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected start tag (table) implies end tag (table).
+Line: 1 Col: 14 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <table>
+
+#data
+<table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 14 Unexpected end tag (body). Ignored.
+Line: 1 Col: 24 Unexpected end tag (caption). Ignored.
+Line: 1 Col: 30 Unexpected end tag (col). Ignored.
+Line: 1 Col: 41 Unexpected end tag (colgroup). Ignored.
+Line: 1 Col: 48 Unexpected end tag (html). Ignored.
+Line: 1 Col: 56 Unexpected end tag (tbody). Ignored.
+Line: 1 Col: 61 Unexpected end tag (td). Ignored.
+Line: 1 Col: 69 Unexpected end tag (tfoot). Ignored.
+Line: 1 Col: 74 Unexpected end tag (th). Ignored.
+Line: 1 Col: 82 Unexpected end tag (thead). Ignored.
+Line: 1 Col: 87 Unexpected end tag (tr). Ignored.
+Line: 1 Col: 87 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+
+#data
+</table><tr>
+#errors
+Line: 1 Col: 8 Unexpected end tag (table). Ignored.
+Line: 1 Col: 12 Unexpected end of file. Expected table content.
+#document-fragment
+table
+#document
+| <tbody>
+| <tr>
+
+#data
+<body></body></html>
+#errors
+Line: 1 Col: 20 Unexpected html end tag in inner html mode.
+Line: 1 Col: 20 Unexpected EOF in inner html mode.
+#document-fragment
+html
+#document
+| <head>
+| <body>
+
+#data
+<html><frameset></frameset></html>
+#errors
+Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <frameset>
+| " "
+
+#data
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html></html>
+#errors
+Line: 1 Col: 50 Erroneous DOCTYPE.
+Line: 1 Col: 63 Unexpected end tag (html) after the (implied) root element.
+#document
+| <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "">
+| <html>
+| <head>
+| <body>
+
+#data
+<param><frameset></frameset>
+#errors
+Line: 1 Col: 7 Unexpected start tag (param). Expected DOCTYPE.
+Line: 1 Col: 17 Unexpected start tag (frameset).
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+<source><frameset></frameset>
+#errors
+Line: 1 Col: 7 Unexpected start tag (source). Expected DOCTYPE.
+Line: 1 Col: 17 Unexpected start tag (frameset).
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+<track><frameset></frameset>
+#errors
+Line: 1 Col: 7 Unexpected start tag (track). Expected DOCTYPE.
+Line: 1 Col: 17 Unexpected start tag (frameset).
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+</html><frameset></frameset>
+#errors
+7: End tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+17: Stray “frameset” start tag.
+17: “frameset” start tag seen.
+#document
+| <html>
+| <head>
+| <frameset>
+
+#data
+</body><frameset></frameset>
+#errors
+7: End tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
+17: Stray “frameset” start tag.
+17: “frameset” start tag seen.
+#document
+| <html>
+| <head>
+| <frameset>
diff --git a/html/testdata/webkit/tests7.dat b/html/testdata/webkit/tests7.dat
new file mode 100644
index 0000000..f5193c6
--- /dev/null
+++ b/html/testdata/webkit/tests7.dat
@@ -0,0 +1,390 @@
+#data
+<!doctype html><body><title>X</title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <title>
+| "X"
+
+#data
+<!doctype html><table><title>X</title></table>
+#errors
+Line: 1 Col: 29 Unexpected start tag (title) in table context caused voodoo mode.
+Line: 1 Col: 38 Unexpected end tag (title) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <title>
+| "X"
+| <table>
+
+#data
+<!doctype html><head></head><title>X</title>
+#errors
+Line: 1 Col: 35 Unexpected start tag (title) that can be in head. Moved.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "X"
+| <body>
+
+#data
+<!doctype html></head><title>X</title>
+#errors
+Line: 1 Col: 29 Unexpected start tag (title) that can be in head. Moved.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <title>
+| "X"
+| <body>
+
+#data
+<!doctype html><table><meta></table>
+#errors
+Line: 1 Col: 28 Unexpected start tag (meta) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <meta>
+| <table>
+
+#data
+<!doctype html><table>X<tr><td><table> <meta></table></table>
+#errors
+Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 45 Unexpected start tag (meta) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <meta>
+| <table>
+| " "
+
+#data
+<!doctype html><html> <head>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<!doctype html> <head>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<!doctype html><table><style> <tr>x </style> </table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <style>
+| " <tr>x "
+| " "
+
+#data
+<!doctype html><table><TBODY><script> <tr>x </script> </table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <script>
+| " <tr>x "
+| " "
+
+#data
+<!doctype html><p><applet><p>X</p></applet>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <p>
+| <applet>
+| <p>
+| "X"
+
+#data
+<!doctype html><listing>
+X</listing>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <listing>
+| "X"
+
+#data
+<!doctype html><select><input>X
+#errors
+Line: 1 Col: 30 Unexpected input start tag in the select phase.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <input>
+| "X"
+
+#data
+<!doctype html><select><select>X
+#errors
+Line: 1 Col: 31 Unexpected select start tag in the select phase treated as select end tag.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| "X"
+
+#data
+<!doctype html><table><input type=hidDEN></table>
+#errors
+Line: 1 Col: 41 Unexpected input with type hidden in table context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <input>
+| type="hidDEN"
+
+#data
+<!doctype html><table>X<input type=hidDEN></table>
+#errors
+Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| "X"
+| <table>
+| <input>
+| type="hidDEN"
+
+#data
+<!doctype html><table> <input type=hidDEN></table>
+#errors
+Line: 1 Col: 43 Unexpected input with type hidden in table context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| " "
+| <input>
+| type="hidDEN"
+
+#data
+<!doctype html><table> <input type='hidDEN'></table>
+#errors
+Line: 1 Col: 45 Unexpected input with type hidden in table context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| " "
+| <input>
+| type="hidDEN"
+
+#data
+<!doctype html><table><input type=" hidden"><input type=hidDEN></table>
+#errors
+Line: 1 Col: 44 Unexpected start tag (input) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <input>
+| type=" hidden"
+| <table>
+| <input>
+| type="hidDEN"
+
+#data
+<!doctype html><table><select>X<tr>
+#errors
+Line: 1 Col: 30 Unexpected start tag (select) in table context caused voodoo mode.
+Line: 1 Col: 35 Unexpected table element start tag (trs) in the select in table phase.
+Line: 1 Col: 35 Unexpected end of file. Expected table content.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| "X"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!doctype html><select>X</select>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| "X"
+
+#data
+<!DOCTYPE hTmL><html></html>
+#errors
+Line: 1 Col: 28 Unexpected end tag (html) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<!DOCTYPE HTML><html></html>
+#errors
+Line: 1 Col: 28 Unexpected end tag (html) after the (implied) root element.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+
+#data
+<body>X</body></body>
+#errors
+Line: 1 Col: 21 Unexpected end tag token (body) in the after body phase.
+Line: 1 Col: 21 Unexpected EOF in inner html mode.
+#document-fragment
+html
+#document
+| <head>
+| <body>
+| "X"
+
+#data
+<div><p>a</x> b
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 13 Unexpected end tag (x). Ignored.
+Line: 1 Col: 15 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <p>
+| "a b"
+
+#data
+<table><tr><td><code></code> </table>
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <code>
+| " "
+
+#data
+<table><b><tr><td>aaa</td></tr>bbb</table>ccc
+#errors
+XXX: Fix me
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <b>
+| "bbb"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "aaa"
+| <b>
+| "ccc"
+
+#data
+A<table><tr> B</tr> B</table>
+#errors
+XXX: Fix me
+#document
+| <html>
+| <head>
+| <body>
+| "A B B"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+A<table><tr> B</tr> </em>C</table>
+#errors
+XXX: Fix me
+#document
+| <html>
+| <head>
+| <body>
+| "A BC"
+| <table>
+| <tbody>
+| <tr>
+| " "
+
+#data
+<select><keygen>
+#errors
+Not known
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <keygen>
diff --git a/html/testdata/webkit/tests8.dat b/html/testdata/webkit/tests8.dat
new file mode 100644
index 0000000..90e6c91
--- /dev/null
+++ b/html/testdata/webkit/tests8.dat
@@ -0,0 +1,148 @@
+#data
+<div>
+<div></div>
+</span>x
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 3 Col: 7 Unexpected end tag (span). Ignored.
+Line: 3 Col: 8 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "
+"
+| <div>
+| "
+x"
+
+#data
+<div>x<div></div>
+</span>x
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 2 Col: 7 Unexpected end tag (span). Ignored.
+Line: 2 Col: 8 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "x"
+| <div>
+| "
+x"
+
+#data
+<div>x<div></div>x</span>x
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 25 Unexpected end tag (span). Ignored.
+Line: 1 Col: 26 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "x"
+| <div>
+| "xx"
+
+#data
+<div>x<div></div>y</span>z
+#errors
+Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE.
+Line: 1 Col: 25 Unexpected end tag (span). Ignored.
+Line: 1 Col: 26 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "x"
+| <div>
+| "yz"
+
+#data
+<table><div>x<div></div>x</span>x
+#errors
+Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE.
+Line: 1 Col: 12 Unexpected start tag (div) in table context caused voodoo mode.
+Line: 1 Col: 18 Unexpected start tag (div) in table context caused voodoo mode.
+Line: 1 Col: 24 Unexpected end tag (div) in table context caused voodoo mode.
+Line: 1 Col: 32 Unexpected end tag (span) in table context caused voodoo mode.
+Line: 1 Col: 32 Unexpected end tag (span). Ignored.
+Line: 1 Col: 33 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "x"
+| <div>
+| "xx"
+| <table>
+
+#data
+x<table>x
+#errors
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+Line: 1 Col: 9 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 9 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| "xx"
+| <table>
+
+#data
+x<table><table>x
+#errors
+Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE.
+Line: 1 Col: 15 Unexpected start tag (table) implies end tag (table).
+Line: 1 Col: 16 Unexpected non-space characters in table context caused voodoo mode.
+Line: 1 Col: 16 Unexpected end of file. Expected table content.
+#document
+| <html>
+| <head>
+| <body>
+| "x"
+| <table>
+| "x"
+| <table>
+
+#data
+<b>a<div></div><div></b>y
+#errors
+Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE.
+Line: 1 Col: 24 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 25 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| "a"
+| <div>
+| <div>
+| <b>
+| "y"
+
+#data
+<a><div><p></a>
+#errors
+Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE.
+Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm.
+Line: 1 Col: 15 Expected closing tag. Unexpected end of file.
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <div>
+| <a>
+| <p>
+| <a>
diff --git a/html/testdata/webkit/tests9.dat b/html/testdata/webkit/tests9.dat
new file mode 100644
index 0000000..554e27a
--- /dev/null
+++ b/html/testdata/webkit/tests9.dat
@@ -0,0 +1,457 @@
+#data
+<!DOCTYPE html><math></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+
+#data
+<!DOCTYPE html><body><math></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+
+#data
+<!DOCTYPE html><math><mi>
+#errors
+25: End of file in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+
+#data
+<!DOCTYPE html><math><annotation-xml><svg><u>
+#errors
+45: HTML start tag “u” in a foreign namespace context.
+45: End of file seen and there were open elements.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math annotation-xml>
+| <svg svg>
+| <u>
+
+#data
+<!DOCTYPE html><body><select><math></math></select>
+#errors
+Line: 1 Col: 35 Unexpected start tag token (math) in the select phase. Ignored.
+Line: 1 Col: 42 Unexpected end tag (math) in the select phase. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+
+#data
+<!DOCTYPE html><body><select><option><math></math></option></select>
+#errors
+Line: 1 Col: 43 Unexpected start tag token (math) in the select phase. Ignored.
+Line: 1 Col: 50 Unexpected end tag (math) in the select phase. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+
+#data
+<!DOCTYPE html><body><table><math></math></table>
+#errors
+Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 41 Unexpected end tag (math) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <table>
+
+#data
+<!DOCTYPE html><body><table><math><mi>foo</mi></math></table>
+#errors
+Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 46 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 53 Unexpected end tag (math) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <table>
+
+#data
+<!DOCTYPE html><body><table><math><mi>foo</mi><mi>bar</mi></math></table>
+#errors
+Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 46 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 58 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 65 Unexpected end tag (math) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <table>
+
+#data
+<!DOCTYPE html><body><table><tbody><math><mi>foo</mi><mi>bar</mi></math></tbody></table>
+#errors
+Line: 1 Col: 41 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 53 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 65 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 72 Unexpected end tag (math) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <table>
+| <tbody>
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><math><mi>foo</mi><mi>bar</mi></math></tr></tbody></table>
+#errors
+Line: 1 Col: 45 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 57 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 69 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 76 Unexpected end tag (math) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math></td></tr></tbody></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+
+#data
+<!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math><p>baz</td></tr></tbody></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</caption></table>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux
+#errors
+Line: 1 Col: 70 HTML start tag "p" in a foreign namespace context.
+Line: 1 Col: 81 Unexpected end table tag in caption. Generates implied end caption.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi>baz</table><p>quux
+#errors
+Line: 1 Col: 78 Unexpected end table tag in caption. Generates implied end caption.
+Line: 1 Col: 78 Unexpected end tag (caption). Missing end tag (math).
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <caption>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| "baz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><colgroup><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux
+#errors
+Line: 1 Col: 44 Unexpected start tag (math) in table context caused voodoo mode.
+Line: 1 Col: 56 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 68 Unexpected end tag (mi) in table context caused voodoo mode.
+Line: 1 Col: 71 HTML start tag "p" in a foreign namespace context.
+Line: 1 Col: 71 Unexpected start tag (p) in table context caused voodoo mode.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+| <table>
+| <colgroup>
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><tr><td><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux
+#errors
+Line: 1 Col: 50 Unexpected start tag token (math) in the select phase. Ignored.
+Line: 1 Col: 54 Unexpected start tag token (mi) in the select phase. Ignored.
+Line: 1 Col: 62 Unexpected end tag (mi) in the select phase. Ignored.
+Line: 1 Col: 66 Unexpected start tag token (mi) in the select phase. Ignored.
+Line: 1 Col: 74 Unexpected end tag (mi) in the select phase. Ignored.
+Line: 1 Col: 77 Unexpected start tag token (p) in the select phase. Ignored.
+Line: 1 Col: 88 Unexpected table element end tag (tables) in the select in table phase.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <select>
+| "foobarbaz"
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body><table><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux
+#errors
+Line: 1 Col: 36 Unexpected start tag (select) in table context caused voodoo mode.
+Line: 1 Col: 42 Unexpected start tag token (math) in the select phase. Ignored.
+Line: 1 Col: 46 Unexpected start tag token (mi) in the select phase. Ignored.
+Line: 1 Col: 54 Unexpected end tag (mi) in the select phase. Ignored.
+Line: 1 Col: 58 Unexpected start tag token (mi) in the select phase. Ignored.
+Line: 1 Col: 66 Unexpected end tag (mi) in the select phase. Ignored.
+Line: 1 Col: 69 Unexpected start tag token (p) in the select phase. Ignored.
+Line: 1 Col: 80 Unexpected table element end tag (tables) in the select in table phase.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <select>
+| "foobarbaz"
+| <table>
+| <p>
+| "quux"
+
+#data
+<!DOCTYPE html><body></body></html><math><mi>foo</mi><mi>bar</mi><p>baz
+#errors
+Line: 1 Col: 41 Unexpected start tag (math).
+Line: 1 Col: 68 HTML start tag "p" in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><body></body><math><mi>foo</mi><mi>bar</mi><p>baz
+#errors
+Line: 1 Col: 34 Unexpected start tag token (math) in the after body phase.
+Line: 1 Col: 61 HTML start tag "p" in a foreign namespace context.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mi>
+| "foo"
+| <math mi>
+| "bar"
+| <p>
+| "baz"
+
+#data
+<!DOCTYPE html><frameset><math><mi></mi><mi></mi><p><span>
+#errors
+Line: 1 Col: 31 Unexpected start tag token (math) in the frameset phase. Ignored.
+Line: 1 Col: 35 Unexpected start tag token (mi) in the frameset phase. Ignored.
+Line: 1 Col: 40 Unexpected end tag token (mi) in the frameset phase. Ignored.
+Line: 1 Col: 44 Unexpected start tag token (mi) in the frameset phase. Ignored.
+Line: 1 Col: 49 Unexpected end tag token (mi) in the frameset phase. Ignored.
+Line: 1 Col: 52 Unexpected start tag token (p) in the frameset phase. Ignored.
+Line: 1 Col: 58 Unexpected start tag token (span) in the frameset phase. Ignored.
+Line: 1 Col: 58 Expected closing tag. Unexpected end of file.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><frameset></frameset><math><mi></mi><mi></mi><p><span>
+#errors
+Line: 1 Col: 42 Unexpected start tag (math) in the after frameset phase. Ignored.
+Line: 1 Col: 46 Unexpected start tag (mi) in the after frameset phase. Ignored.
+Line: 1 Col: 51 Unexpected end tag (mi) in the after frameset phase. Ignored.
+Line: 1 Col: 55 Unexpected start tag (mi) in the after frameset phase. Ignored.
+Line: 1 Col: 60 Unexpected end tag (mi) in the after frameset phase. Ignored.
+Line: 1 Col: 63 Unexpected start tag (p) in the after frameset phase. Ignored.
+Line: 1 Col: 69 Unexpected start tag (span) in the after frameset phase. Ignored.
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!DOCTYPE html><body xlink:href=foo><math xlink:href=foo></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| <math math>
+| xlink href="foo"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo></mi></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <math math>
+| <math mi>
+| xlink href="foo"
+| xml lang="en"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo /></math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <math math>
+| <math mi>
+| xlink href="foo"
+| xml lang="en"
+
+#data
+<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo />bar</math>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| xlink:href="foo"
+| xml:lang="en"
+| <math math>
+| <math mi>
+| xlink href="foo"
+| xml lang="en"
+| "bar"
diff --git a/html/testdata/webkit/tests_innerHTML_1.dat b/html/testdata/webkit/tests_innerHTML_1.dat
new file mode 100644
index 0000000..6c78661
--- /dev/null
+++ b/html/testdata/webkit/tests_innerHTML_1.dat
@@ -0,0 +1,741 @@
+#data
+<body><span>
+#errors
+#document-fragment
+body
+#document
+| <span>
+
+#data
+<span><body>
+#errors
+#document-fragment
+body
+#document
+| <span>
+
+#data
+<span><body>
+#errors
+#document-fragment
+div
+#document
+| <span>
+
+#data
+<body><span>
+#errors
+#document-fragment
+html
+#document
+| <head>
+| <body>
+| <span>
+
+#data
+<frameset><span>
+#errors
+#document-fragment
+body
+#document
+| <span>
+
+#data
+<span><frameset>
+#errors
+#document-fragment
+body
+#document
+| <span>
+
+#data
+<span><frameset>
+#errors
+#document-fragment
+div
+#document
+| <span>
+
+#data
+<frameset><span>
+#errors
+#document-fragment
+html
+#document
+| <head>
+| <frameset>
+
+#data
+<table><tr>
+#errors
+#document-fragment
+table
+#document
+| <tbody>
+| <tr>
+
+#data
+</table><tr>
+#errors
+#document-fragment
+table
+#document
+| <tbody>
+| <tr>
+
+#data
+<a>
+#errors
+#document-fragment
+table
+#document
+| <a>
+
+#data
+<a>
+#errors
+#document-fragment
+table
+#document
+| <a>
+
+#data
+<a><caption>a
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <caption>
+| "a"
+
+#data
+<a><colgroup><col>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <colgroup>
+| <col>
+
+#data
+<a><tbody><tr>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <tbody>
+| <tr>
+
+#data
+<a><tfoot><tr>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <tfoot>
+| <tr>
+
+#data
+<a><thead><tr>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <thead>
+| <tr>
+
+#data
+<a><tr>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <tbody>
+| <tr>
+
+#data
+<a><th>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <tbody>
+| <tr>
+| <th>
+
+#data
+<a><td>
+#errors
+#document-fragment
+table
+#document
+| <a>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table></table><tbody>
+#errors
+#document-fragment
+caption
+#document
+| <table>
+
+#data
+</table><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+
+#data
+<span></table>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+
+#data
+</caption><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+
+#data
+<span></caption><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><caption><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><col><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><colgroup><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><html><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><tbody><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><td><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><tfoot><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><thead><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><th><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span><tr><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+<span></table><span>
+#errors
+#document-fragment
+caption
+#document
+| <span>
+| <span>
+
+#data
+</colgroup><col>
+#errors
+#document-fragment
+colgroup
+#document
+| <col>
+
+#data
+<a><col>
+#errors
+#document-fragment
+colgroup
+#document
+| <col>
+
+#data
+<caption><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<col><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<colgroup><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<tbody><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<tfoot><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<thead><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+</table><a>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+
+#data
+<a><tr>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+| <tr>
+
+#data
+<a><td>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+| <tr>
+| <td>
+
+#data
+<a><td>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+| <tr>
+| <td>
+
+#data
+<a><td>
+#errors
+#document-fragment
+tbody
+#document
+| <a>
+| <tr>
+| <td>
+
+#data
+<td><table><tbody><a><tr>
+#errors
+#document-fragment
+tbody
+#document
+| <tr>
+| <td>
+| <a>
+| <table>
+| <tbody>
+| <tr>
+
+#data
+</tr><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<td><table><a><tr></tr><tr>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+| <a>
+| <table>
+| <tbody>
+| <tr>
+| <tr>
+
+#data
+<caption><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<col><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<colgroup><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<tbody><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<tfoot><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<thead><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<tr><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+</table><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+
+#data
+<td><table></table><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+| <table>
+| <td>
+
+#data
+<td><table></table><td>
+#errors
+#document-fragment
+tr
+#document
+| <td>
+| <table>
+| <td>
+
+#data
+<caption><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<col><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<colgroup><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<tbody><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<tfoot><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<th><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<thead><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<tr><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</table><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</tbody><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</td><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</tfoot><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</thead><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</th><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+</tr><a>
+#errors
+#document-fragment
+td
+#document
+| <a>
+
+#data
+<table><td><td>
+#errors
+#document-fragment
+td
+#document
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <td>
+
+#data
+</select><option>
+#errors
+#document-fragment
+select
+#document
+| <option>
+
+#data
+<input><option>
+#errors
+#document-fragment
+select
+#document
+| <option>
+
+#data
+<keygen><option>
+#errors
+#document-fragment
+select
+#document
+| <option>
+
+#data
+<textarea><option>
+#errors
+#document-fragment
+select
+#document
+| <option>
+
+#data
+</html><!--abc-->
+#errors
+#document-fragment
+html
+#document
+| <head>
+| <body>
+| <!-- abc -->
+
+#data
+</frameset><frame>
+#errors
+#document-fragment
+frameset
+#document
+| <frame>
+
+#data
+#errors
+#document-fragment
+html
+#document
+| <head>
+| <body>
diff --git a/html/testdata/webkit/tricky01.dat b/html/testdata/webkit/tricky01.dat
new file mode 100644
index 0000000..0841992
--- /dev/null
+++ b/html/testdata/webkit/tricky01.dat
@@ -0,0 +1,261 @@
+#data
+<b><p>Bold </b> Not bold</p>
+Also not bold.
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <b>
+| <p>
+| <b>
+| "Bold "
+| " Not bold"
+| "
+Also not bold."
+
+#data
+<html>
+<font color=red><i>Italic and Red<p>Italic and Red </font> Just italic.</p> Italic only.</i> Plain
+<p>I should not be red. <font color=red>Red. <i>Italic and red.</p>
+<p>Italic and red. </i> Red.</font> I should not be red.</p>
+<b>Bold <i>Bold and italic</b> Only Italic </i> Plain
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <font>
+| color="red"
+| <i>
+| "Italic and Red"
+| <i>
+| <p>
+| <font>
+| color="red"
+| "Italic and Red "
+| " Just italic."
+| " Italic only."
+| " Plain
+"
+| <p>
+| "I should not be red. "
+| <font>
+| color="red"
+| "Red. "
+| <i>
+| "Italic and red."
+| <font>
+| color="red"
+| <i>
+| "
+"
+| <p>
+| <font>
+| color="red"
+| <i>
+| "Italic and red. "
+| " Red."
+| " I should not be red."
+| "
+"
+| <b>
+| "Bold "
+| <i>
+| "Bold and italic"
+| <i>
+| " Only Italic "
+| " Plain"
+
+#data
+<html><body>
+<p><font size="7">First paragraph.</p>
+<p>Second paragraph.</p></font>
+<b><p><i>Bold and Italic</b> Italic</p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "
+"
+| <p>
+| <font>
+| size="7"
+| "First paragraph."
+| <font>
+| size="7"
+| "
+"
+| <p>
+| "Second paragraph."
+| "
+"
+| <b>
+| <p>
+| <b>
+| <i>
+| "Bold and Italic"
+| <i>
+| " Italic"
+
+#data
+<html>
+<dl>
+<dt><b>Boo
+<dd>Goo?
+</dl>
+</html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <dl>
+| "
+"
+| <dt>
+| <b>
+| "Boo
+"
+| <dd>
+| <b>
+| "Goo?
+"
+| <b>
+| "
+"
+
+#data
+<html><body>
+<label><a><div>Hello<div>World</div></a></label>
+</body></html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "
+"
+| <label>
+| <a>
+| <div>
+| <a>
+| "Hello"
+| <div>
+| "World"
+| "
+"
+
+#data
+<table><center> <font>a</center> <img> <tr><td> </td> </tr> </table>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <center>
+| " "
+| <font>
+| "a"
+| <font>
+| <img>
+| " "
+| <table>
+| " "
+| <tbody>
+| <tr>
+| <td>
+| " "
+| " "
+| " "
+
+#data
+<table><tr><p><a><p>You should see this text.
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| <a>
+| <p>
+| <a>
+| "You should see this text."
+| <table>
+| <tbody>
+| <tr>
+
+#data
+<TABLE>
+<TR>
+<CENTER><CENTER><TD></TD></TR><TR>
+<FONT>
+<TABLE><tr></tr></TABLE>
+</P>
+<a></font><font></a>
+This page contains an insanely badly-nested tag sequence.
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <center>
+| <center>
+| <font>
+| "
+"
+| <table>
+| "
+"
+| <tbody>
+| <tr>
+| "
+"
+| <td>
+| <tr>
+| "
+"
+| <table>
+| <tbody>
+| <tr>
+| <font>
+| "
+"
+| <p>
+| "
+"
+| <a>
+| <a>
+| <font>
+| <font>
+| "
+This page contains an insanely badly-nested tag sequence."
+
+#data
+<html>
+<body>
+<b><nobr><div>This text is in a div inside a nobr</nobr>More text that should not be in the nobr, i.e., the
+nobr should have closed the div inside it implicitly. </b><pre>A pre tag outside everything else.</pre>
+</body>
+</html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "
+"
+| <b>
+| <nobr>
+| <div>
+| <b>
+| <nobr>
+| "This text is in a div inside a nobr"
+| "More text that should not be in the nobr, i.e., the
+nobr should have closed the div inside it implicitly. "
+| <pre>
+| "A pre tag outside everything else."
+| "
+
+"
diff --git a/html/testdata/webkit/webkit01.dat b/html/testdata/webkit/webkit01.dat
new file mode 100644
index 0000000..9d425e9
--- /dev/null
+++ b/html/testdata/webkit/webkit01.dat
@@ -0,0 +1,610 @@
+#data
+Test
+#errors
+Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE.
+#document
+| <html>
+| <head>
+| <body>
+| "Test"
+
+#data
+<div></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+
+#data
+<div>Test</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "Test"
+
+#data
+<di
+#errors
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<div>Hello</div>
+<script>
+console.log("PASS");
+</script>
+<div>Bye</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "Hello"
+| "
+"
+| <script>
+| "
+console.log("PASS");
+"
+| "
+"
+| <div>
+| "Bye"
+
+#data
+<div foo="bar">Hello</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| foo="bar"
+| "Hello"
+
+#data
+<div>Hello</div>
+<script>
+console.log("FOO<span>BAR</span>BAZ");
+</script>
+<div>Bye</div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "Hello"
+| "
+"
+| <script>
+| "
+console.log("FOO<span>BAR</span>BAZ");
+"
+| "
+"
+| <div>
+| "Bye"
+
+#data
+<foo bar="baz"></foo><potato quack="duck"></potato>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| bar="baz"
+| <potato>
+| quack="duck"
+
+#data
+<foo bar="baz"><potato quack="duck"></potato></foo>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| bar="baz"
+| <potato>
+| quack="duck"
+
+#data
+<foo></foo bar="baz"><potato></potato quack="duck">
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| <potato>
+
+#data
+</ tttt>
+#errors
+#document
+| <!-- tttt -->
+| <html>
+| <head>
+| <body>
+
+#data
+<div FOO ><img><img></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| foo=""
+| <img>
+| <img>
+
+#data
+<p>Test</p<p>Test2</p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| "TestTest2"
+
+#data
+<rdar://problem/6869687>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <rdar:>
+| 6869687=""
+| problem=""
+
+#data
+<A>test< /A>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| "test< /A>"
+
+#data
+<
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "<"
+
+#data
+<body foo='bar'><body foo='baz' yo='mama'>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| foo="bar"
+| yo="mama"
+
+#data
+<body></br foo="bar"></body>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <br>
+
+#data
+<bdy><br foo="bar"></body>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <bdy>
+| <br>
+| foo="bar"
+
+#data
+<body></body></br foo="bar">
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <br>
+
+#data
+<bdy></body><br foo="bar">
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <bdy>
+| <br>
+| foo="bar"
+
+#data
+<html><body></body></html><!-- Hi there -->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <!-- Hi there -->
+
+#data
+<html><body></body></html>x<!-- Hi there -->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "x"
+| <!-- Hi there -->
+
+#data
+<html><body></body></html>x<!-- Hi there --></html><!-- Again -->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "x"
+| <!-- Hi there -->
+| <!-- Again -->
+
+#data
+<html><body></body></html>x<!-- Hi there --></body></html><!-- Again -->
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "x"
+| <!-- Hi there -->
+| <!-- Again -->
+
+#data
+<html><body><ruby><div><rp>xx</rp></div></ruby></body></html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <rp>
+| "xx"
+
+#data
+<html><body><ruby><div><rt>xx</rt></div></ruby></body></html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <ruby>
+| <div>
+| <rt>
+| "xx"
+
+#data
+<html><frameset><!--1--><noframes>A</noframes><!--2--></frameset><!--3--><noframes>B</noframes><!--4--></html><!--5--><noframes>C</noframes><!--6-->
+#errors
+#document
+| <html>
+| <head>
+| <frameset>
+| <!-- 1 -->
+| <noframes>
+| "A"
+| <!-- 2 -->
+| <!-- 3 -->
+| <noframes>
+| "B"
+| <!-- 4 -->
+| <noframes>
+| "C"
+| <!-- 5 -->
+| <!-- 6 -->
+
+#data
+<select><option>A<select><option>B<select><option>C<select><option>D<select><option>E<select><option>F<select><option>G<select>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <select>
+| <option>
+| "A"
+| <option>
+| "B"
+| <select>
+| <option>
+| "C"
+| <option>
+| "D"
+| <select>
+| <option>
+| "E"
+| <option>
+| "F"
+| <select>
+| <option>
+| "G"
+
+#data
+<dd><dd><dt><dt><dd><li><li>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <dd>
+| <dd>
+| <dt>
+| <dt>
+| <dd>
+| <li>
+| <li>
+
+#data
+<div><b></div><div><nobr>a<nobr>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <b>
+| <div>
+| <b>
+| <nobr>
+| "a"
+| <nobr>
+
+#data
+<head></head>
+<body></body>
+#errors
+#document
+| <html>
+| <head>
+| "
+"
+| <body>
+
+#data
+<head></head> <style></style>ddd
+#errors
+#document
+| <html>
+| <head>
+| <style>
+| " "
+| <body>
+| "ddd"
+
+#data
+<kbd><table></kbd><col><select><tr>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <kbd>
+| <select>
+| <table>
+| <colgroup>
+| <col>
+| <tbody>
+| <tr>
+
+#data
+<kbd><table></kbd><col><select><tr></table><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <kbd>
+| <select>
+| <table>
+| <colgroup>
+| <col>
+| <tbody>
+| <tr>
+| <div>
+
+#data
+<a><li><style></style><title></title></a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <li>
+| <a>
+| <style>
+| <title>
+
+#data
+<font></p><p><meta><title></title></font>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <font>
+| <p>
+| <p>
+| <font>
+| <meta>
+| <title>
+
+#data
+<a><center><title></title><a>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <a>
+| <center>
+| <a>
+| <title>
+| <a>
+
+#data
+<svg><title><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg title>
+| <div>
+
+#data
+<svg><title><rect><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg title>
+| <rect>
+| <div>
+
+#data
+<svg><title><svg><div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg title>
+| <svg svg>
+| <div>
+
+#data
+<img <="" FAIL>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <img>
+| <=""
+| fail=""
+
+#data
+<ul><li><div id='foo'/>A</li><li>B<div>C</div></li></ul>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <ul>
+| <li>
+| <div>
+| id="foo"
+| "A"
+| <li>
+| "B"
+| <div>
+| "C"
+
+#data
+<svg><em><desc></em>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <em>
+| <desc>
+
+#data
+<table><tr><td><svg><desc><td></desc><circle>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| <svg svg>
+| <svg desc>
+| <td>
+| <circle>
+
+#data
+<svg><tfoot></mi><td>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <svg svg>
+| <svg tfoot>
+| <svg td>
+
+#data
+<math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <math math>
+| <math mrow>
+| <math mrow>
+| <math mn>
+| "1"
+| <math mi>
+| "a"
+
+#data
+<!doctype html><input type="hidden"><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <frameset>
+
+#data
+<!doctype html><input type="button"><frameset>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+| <head>
+| <body>
+| <input>
+| type="button"
diff --git a/html/testdata/webkit/webkit02.dat b/html/testdata/webkit/webkit02.dat
new file mode 100644
index 0000000..905783d
--- /dev/null
+++ b/html/testdata/webkit/webkit02.dat
@@ -0,0 +1,159 @@
+#data
+<foo bar=qux/>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <foo>
+| bar="qux/"
+
+#data
+<p id="status"><noscript><strong>A</strong></noscript><span>B</span></p>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <p>
+| id="status"
+| <noscript>
+| "<strong>A</strong>"
+| <span>
+| "B"
+
+#data
+<div><sarcasm><div></div></sarcasm></div>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| <sarcasm>
+| <div>
+
+#data
+<html><body><img src="" border="0" alt="><div>A</div></body></html>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<table><td></tbody>A
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| "A"
+| <table>
+| <tbody>
+| <tr>
+| <td>
+
+#data
+<table><td></thead>A
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "A"
+
+#data
+<table><td></tfoot>A
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <tbody>
+| <tr>
+| <td>
+| "A"
+
+#data
+<table><thead><td></tbody>A
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <table>
+| <thead>
+| <tr>
+| <td>
+| "A"
+
+#data
+<legend>test</legend>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <legend>
+| "test"
+
+#data
+<table><input>
+#errors
+#document
+| <html>
+| <head>
+| <body>
+| <input>
+| <table>
+
+#data
+<b><em><dcell><postfield><postfield><postfield><postfield><missing_glyph><missing_glyph><missing_glyph><missing_glyph><hkern><aside></b></em>
+#errors
+#document-fragment
+div
+#document
+| <b>
+| <em>
+| <dcell>
+| <postfield>
+| <postfield>
+| <postfield>
+| <postfield>
+| <missing_glyph>
+| <missing_glyph>
+| <missing_glyph>
+| <missing_glyph>
+| <hkern>
+| <aside>
+| <em>
+| <b>
+
+#data
+<isindex action="x">
+#errors
+#document-fragment
+table
+#document
+| <form>
+| action="x"
+| <hr>
+| <label>
+| "This is a searchable index. Enter search keywords: "
+| <input>
+| name="isindex"
+| <hr>
+
+#data
+<option><XH<optgroup></optgroup>
+#errors
+#document-fragment
+select
+#document
+| <option>
diff --git a/html/token.go b/html/token.go
new file mode 100644
index 0000000..893e272
--- /dev/null
+++ b/html/token.go
@@ -0,0 +1,1219 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "strconv"
+ "strings"
+
+ "golang.org/x/net/html/atom"
+)
+
+// A TokenType is the type of a Token.
+type TokenType uint32
+
+const (
+ // ErrorToken means that an error occurred during tokenization.
+ ErrorToken TokenType = iota
+ // TextToken means a text node.
+ TextToken
+ // A StartTagToken looks like <a>.
+ StartTagToken
+ // An EndTagToken looks like </a>.
+ EndTagToken
+ // A SelfClosingTagToken tag looks like <br/>.
+ SelfClosingTagToken
+ // A CommentToken looks like <!--x-->.
+ CommentToken
+ // A DoctypeToken looks like <!DOCTYPE x>
+ DoctypeToken
+)
+
+// ErrBufferExceeded means that the buffering limit was exceeded.
+var ErrBufferExceeded = errors.New("max buffer exceeded")
+
+// String returns a string representation of the TokenType.
+func (t TokenType) String() string {
+ switch t {
+ case ErrorToken:
+ return "Error"
+ case TextToken:
+ return "Text"
+ case StartTagToken:
+ return "StartTag"
+ case EndTagToken:
+ return "EndTag"
+ case SelfClosingTagToken:
+ return "SelfClosingTag"
+ case CommentToken:
+ return "Comment"
+ case DoctypeToken:
+ return "Doctype"
+ }
+ return "Invalid(" + strconv.Itoa(int(t)) + ")"
+}
+
+// An Attribute is an attribute namespace-key-value triple. Namespace is
+// non-empty for foreign attributes like xlink, Key is alphabetic (and hence
+// does not contain escapable characters like '&', '<' or '>'), and Val is
+// unescaped (it looks like "a<b" rather than "a<b").
+//
+// Namespace is only used by the parser, not the tokenizer.
+type Attribute struct {
+ Namespace, Key, Val string
+}
+
+// A Token consists of a TokenType and some Data (tag name for start and end
+// tags, content for text, comments and doctypes). A tag Token may also contain
+// a slice of Attributes. Data is unescaped for all Tokens (it looks like "a<b"
+// rather than "a<b"). For tag Tokens, DataAtom is the atom for Data, or
+// zero if Data is not a known tag name.
+type Token struct {
+ Type TokenType
+ DataAtom atom.Atom
+ Data string
+ Attr []Attribute
+}
+
+// tagString returns a string representation of a tag Token's Data and Attr.
+func (t Token) tagString() string {
+ if len(t.Attr) == 0 {
+ return t.Data
+ }
+ buf := bytes.NewBufferString(t.Data)
+ for _, a := range t.Attr {
+ buf.WriteByte(' ')
+ buf.WriteString(a.Key)
+ buf.WriteString(`="`)
+ escape(buf, a.Val)
+ buf.WriteByte('"')
+ }
+ return buf.String()
+}
+
+// String returns a string representation of the Token.
+func (t Token) String() string {
+ switch t.Type {
+ case ErrorToken:
+ return ""
+ case TextToken:
+ return EscapeString(t.Data)
+ case StartTagToken:
+ return "<" + t.tagString() + ">"
+ case EndTagToken:
+ return "</" + t.tagString() + ">"
+ case SelfClosingTagToken:
+ return "<" + t.tagString() + "/>"
+ case CommentToken:
+ return "<!--" + t.Data + "-->"
+ case DoctypeToken:
+ return "<!DOCTYPE " + t.Data + ">"
+ }
+ return "Invalid(" + strconv.Itoa(int(t.Type)) + ")"
+}
+
+// span is a range of bytes in a Tokenizer's buffer. The start is inclusive,
+// the end is exclusive.
+type span struct {
+ start, end int
+}
+
+// A Tokenizer returns a stream of HTML Tokens.
+type Tokenizer struct {
+ // r is the source of the HTML text.
+ r io.Reader
+ // tt is the TokenType of the current token.
+ tt TokenType
+ // err is the first error encountered during tokenization. It is possible
+ // for tt != Error && err != nil to hold: this means that Next returned a
+ // valid token but the subsequent Next call will return an error token.
+ // For example, if the HTML text input was just "plain", then the first
+ // Next call would set z.err to io.EOF but return a TextToken, and all
+ // subsequent Next calls would return an ErrorToken.
+ // err is never reset. Once it becomes non-nil, it stays non-nil.
+ err error
+ // readErr is the error returned by the io.Reader r. It is separate from
+ // err because it is valid for an io.Reader to return (n int, err1 error)
+ // such that n > 0 && err1 != nil, and callers should always process the
+ // n > 0 bytes before considering the error err1.
+ readErr error
+ // buf[raw.start:raw.end] holds the raw bytes of the current token.
+ // buf[raw.end:] is buffered input that will yield future tokens.
+ raw span
+ buf []byte
+ // maxBuf limits the data buffered in buf. A value of 0 means unlimited.
+ maxBuf int
+ // buf[data.start:data.end] holds the raw bytes of the current token's data:
+ // a text token's text, a tag token's tag name, etc.
+ data span
+ // pendingAttr is the attribute key and value currently being tokenized.
+ // When complete, pendingAttr is pushed onto attr. nAttrReturned is
+ // incremented on each call to TagAttr.
+ pendingAttr [2]span
+ attr [][2]span
+ nAttrReturned int
+ // rawTag is the "script" in "</script>" that closes the next token. If
+ // non-empty, the subsequent call to Next will return a raw or RCDATA text
+ // token: one that treats "<p>" as text instead of an element.
+ // rawTag's contents are lower-cased.
+ rawTag string
+ // textIsRaw is whether the current text token's data is not escaped.
+ textIsRaw bool
+ // convertNUL is whether NUL bytes in the current token's data should
+ // be converted into \ufffd replacement characters.
+ convertNUL bool
+ // allowCDATA is whether CDATA sections are allowed in the current context.
+ allowCDATA bool
+}
+
+// AllowCDATA sets whether or not the tokenizer recognizes <![CDATA[foo]]> as
+// the text "foo". The default value is false, which means to recognize it as
+// a bogus comment "<!-- [CDATA[foo]] -->" instead.
+//
+// Strictly speaking, an HTML5 compliant tokenizer should allow CDATA if and
+// only if tokenizing foreign content, such as MathML and SVG. However,
+// tracking foreign-contentness is difficult to do purely in the tokenizer,
+// as opposed to the parser, due to HTML integration points: an <svg> element
+// can contain a <foreignObject> that is foreign-to-SVG but not foreign-to-
+// HTML. For strict compliance with the HTML5 tokenization algorithm, it is the
+// responsibility of the user of a tokenizer to call AllowCDATA as appropriate.
+// In practice, if using the tokenizer without caring whether MathML or SVG
+// CDATA is text or comments, such as tokenizing HTML to find all the anchor
+// text, it is acceptable to ignore this responsibility.
+func (z *Tokenizer) AllowCDATA(allowCDATA bool) {
+ z.allowCDATA = allowCDATA
+}
+
+// NextIsNotRawText instructs the tokenizer that the next token should not be
+// considered as 'raw text'. Some elements, such as script and title elements,
+// normally require the next token after the opening tag to be 'raw text' that
+// has no child elements. For example, tokenizing "<title>a<b>c</b>d</title>"
+// yields a start tag token for "<title>", a text token for "a<b>c</b>d", and
+// an end tag token for "</title>". There are no distinct start tag or end tag
+// tokens for the "<b>" and "</b>".
+//
+// This tokenizer implementation will generally look for raw text at the right
+// times. Strictly speaking, an HTML5 compliant tokenizer should not look for
+// raw text if in foreign content: <title> generally needs raw text, but a
+// <title> inside an <svg> does not. Another example is that a <textarea>
+// generally needs raw text, but a <textarea> is not allowed as an immediate
+// child of a <select>; in normal parsing, a <textarea> implies </select>, but
+// one cannot close the implicit element when parsing a <select>'s InnerHTML.
+// Similarly to AllowCDATA, tracking the correct moment to override raw-text-
+// ness is difficult to do purely in the tokenizer, as opposed to the parser.
+// For strict compliance with the HTML5 tokenization algorithm, it is the
+// responsibility of the user of a tokenizer to call NextIsNotRawText as
+// appropriate. In practice, like AllowCDATA, it is acceptable to ignore this
+// responsibility for basic usage.
+//
+// Note that this 'raw text' concept is different from the one offered by the
+// Tokenizer.Raw method.
+func (z *Tokenizer) NextIsNotRawText() {
+ z.rawTag = ""
+}
+
+// Err returns the error associated with the most recent ErrorToken token.
+// This is typically io.EOF, meaning the end of tokenization.
+func (z *Tokenizer) Err() error {
+ if z.tt != ErrorToken {
+ return nil
+ }
+ return z.err
+}
+
+// readByte returns the next byte from the input stream, doing a buffered read
+// from z.r into z.buf if necessary. z.buf[z.raw.start:z.raw.end] remains a contiguous byte
+// slice that holds all the bytes read so far for the current token.
+// It sets z.err if the underlying reader returns an error.
+// Pre-condition: z.err == nil.
+func (z *Tokenizer) readByte() byte {
+ if z.raw.end >= len(z.buf) {
+ // Our buffer is exhausted and we have to read from z.r. Check if the
+ // previous read resulted in an error.
+ if z.readErr != nil {
+ z.err = z.readErr
+ return 0
+ }
+ // We copy z.buf[z.raw.start:z.raw.end] to the beginning of z.buf. If the length
+ // z.raw.end - z.raw.start is more than half the capacity of z.buf, then we
+ // allocate a new buffer before the copy.
+ c := cap(z.buf)
+ d := z.raw.end - z.raw.start
+ var buf1 []byte
+ if 2*d > c {
+ buf1 = make([]byte, d, 2*c)
+ } else {
+ buf1 = z.buf[:d]
+ }
+ copy(buf1, z.buf[z.raw.start:z.raw.end])
+ if x := z.raw.start; x != 0 {
+ // Adjust the data/attr spans to refer to the same contents after the copy.
+ z.data.start -= x
+ z.data.end -= x
+ z.pendingAttr[0].start -= x
+ z.pendingAttr[0].end -= x
+ z.pendingAttr[1].start -= x
+ z.pendingAttr[1].end -= x
+ for i := range z.attr {
+ z.attr[i][0].start -= x
+ z.attr[i][0].end -= x
+ z.attr[i][1].start -= x
+ z.attr[i][1].end -= x
+ }
+ }
+ z.raw.start, z.raw.end, z.buf = 0, d, buf1[:d]
+ // Now that we have copied the live bytes to the start of the buffer,
+ // we read from z.r into the remainder.
+ var n int
+ n, z.readErr = readAtLeastOneByte(z.r, buf1[d:cap(buf1)])
+ if n == 0 {
+ z.err = z.readErr
+ return 0
+ }
+ z.buf = buf1[:d+n]
+ }
+ x := z.buf[z.raw.end]
+ z.raw.end++
+ if z.maxBuf > 0 && z.raw.end-z.raw.start >= z.maxBuf {
+ z.err = ErrBufferExceeded
+ return 0
+ }
+ return x
+}
+
+// Buffered returns a slice containing data buffered but not yet tokenized.
+func (z *Tokenizer) Buffered() []byte {
+ return z.buf[z.raw.end:]
+}
+
+// readAtLeastOneByte wraps an io.Reader so that reading cannot return (0, nil).
+// It returns io.ErrNoProgress if the underlying r.Read method returns (0, nil)
+// too many times in succession.
+func readAtLeastOneByte(r io.Reader, b []byte) (int, error) {
+ for i := 0; i < 100; i++ {
+ n, err := r.Read(b)
+ if n != 0 || err != nil {
+ return n, err
+ }
+ }
+ return 0, io.ErrNoProgress
+}
+
+// skipWhiteSpace skips past any white space.
+func (z *Tokenizer) skipWhiteSpace() {
+ if z.err != nil {
+ return
+ }
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f':
+ // No-op.
+ default:
+ z.raw.end--
+ return
+ }
+ }
+}
+
+// readRawOrRCDATA reads until the next "</foo>", where "foo" is z.rawTag and
+// is typically something like "script" or "textarea".
+func (z *Tokenizer) readRawOrRCDATA() {
+ if z.rawTag == "script" {
+ z.readScript()
+ z.textIsRaw = true
+ z.rawTag = ""
+ return
+ }
+loop:
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ break loop
+ }
+ if c != '<' {
+ continue loop
+ }
+ c = z.readByte()
+ if z.err != nil {
+ break loop
+ }
+ if c != '/' {
+ continue loop
+ }
+ if z.readRawEndTag() || z.err != nil {
+ break loop
+ }
+ }
+ z.data.end = z.raw.end
+ // A textarea's or title's RCDATA can contain escaped entities.
+ z.textIsRaw = z.rawTag != "textarea" && z.rawTag != "title"
+ z.rawTag = ""
+}
+
+// readRawEndTag attempts to read a tag like "</foo>", where "foo" is z.rawTag.
+// If it succeeds, it backs up the input position to reconsume the tag and
+// returns true. Otherwise it returns false. The opening "</" has already been
+// consumed.
+func (z *Tokenizer) readRawEndTag() bool {
+ for i := 0; i < len(z.rawTag); i++ {
+ c := z.readByte()
+ if z.err != nil {
+ return false
+ }
+ if c != z.rawTag[i] && c != z.rawTag[i]-('a'-'A') {
+ z.raw.end--
+ return false
+ }
+ }
+ c := z.readByte()
+ if z.err != nil {
+ return false
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f', '/', '>':
+ // The 3 is 2 for the leading "</" plus 1 for the trailing character c.
+ z.raw.end -= 3 + len(z.rawTag)
+ return true
+ }
+ z.raw.end--
+ return false
+}
+
+// readScript reads until the next </script> tag, following the byzantine
+// rules for escaping/hiding the closing tag.
+func (z *Tokenizer) readScript() {
+ defer func() {
+ z.data.end = z.raw.end
+ }()
+ var c byte
+
+scriptData:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c == '<' {
+ goto scriptDataLessThanSign
+ }
+ goto scriptData
+
+scriptDataLessThanSign:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '/':
+ goto scriptDataEndTagOpen
+ case '!':
+ goto scriptDataEscapeStart
+ }
+ z.raw.end--
+ goto scriptData
+
+scriptDataEndTagOpen:
+ if z.readRawEndTag() || z.err != nil {
+ return
+ }
+ goto scriptData
+
+scriptDataEscapeStart:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c == '-' {
+ goto scriptDataEscapeStartDash
+ }
+ z.raw.end--
+ goto scriptData
+
+scriptDataEscapeStartDash:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c == '-' {
+ goto scriptDataEscapedDashDash
+ }
+ z.raw.end--
+ goto scriptData
+
+scriptDataEscaped:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataEscapedDash
+ case '<':
+ goto scriptDataEscapedLessThanSign
+ }
+ goto scriptDataEscaped
+
+scriptDataEscapedDash:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataEscapedDashDash
+ case '<':
+ goto scriptDataEscapedLessThanSign
+ }
+ goto scriptDataEscaped
+
+scriptDataEscapedDashDash:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataEscapedDashDash
+ case '<':
+ goto scriptDataEscapedLessThanSign
+ case '>':
+ goto scriptData
+ }
+ goto scriptDataEscaped
+
+scriptDataEscapedLessThanSign:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c == '/' {
+ goto scriptDataEscapedEndTagOpen
+ }
+ if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
+ goto scriptDataDoubleEscapeStart
+ }
+ z.raw.end--
+ goto scriptData
+
+scriptDataEscapedEndTagOpen:
+ if z.readRawEndTag() || z.err != nil {
+ return
+ }
+ goto scriptDataEscaped
+
+scriptDataDoubleEscapeStart:
+ z.raw.end--
+ for i := 0; i < len("script"); i++ {
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c != "script"[i] && c != "SCRIPT"[i] {
+ z.raw.end--
+ goto scriptDataEscaped
+ }
+ }
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f', '/', '>':
+ goto scriptDataDoubleEscaped
+ }
+ z.raw.end--
+ goto scriptDataEscaped
+
+scriptDataDoubleEscaped:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataDoubleEscapedDash
+ case '<':
+ goto scriptDataDoubleEscapedLessThanSign
+ }
+ goto scriptDataDoubleEscaped
+
+scriptDataDoubleEscapedDash:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataDoubleEscapedDashDash
+ case '<':
+ goto scriptDataDoubleEscapedLessThanSign
+ }
+ goto scriptDataDoubleEscaped
+
+scriptDataDoubleEscapedDashDash:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch c {
+ case '-':
+ goto scriptDataDoubleEscapedDashDash
+ case '<':
+ goto scriptDataDoubleEscapedLessThanSign
+ case '>':
+ goto scriptData
+ }
+ goto scriptDataDoubleEscaped
+
+scriptDataDoubleEscapedLessThanSign:
+ c = z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c == '/' {
+ goto scriptDataDoubleEscapeEnd
+ }
+ z.raw.end--
+ goto scriptDataDoubleEscaped
+
+scriptDataDoubleEscapeEnd:
+ if z.readRawEndTag() {
+ z.raw.end += len("</script>")
+ goto scriptDataEscaped
+ }
+ if z.err != nil {
+ return
+ }
+ goto scriptDataDoubleEscaped
+}
+
+// readComment reads the next comment token starting with "<!--". The opening
+// "<!--" has already been consumed.
+func (z *Tokenizer) readComment() {
+ z.data.start = z.raw.end
+ defer func() {
+ if z.data.end < z.data.start {
+ // It's a comment with no data, like <!-->.
+ z.data.end = z.data.start
+ }
+ }()
+ for dashCount := 2; ; {
+ c := z.readByte()
+ if z.err != nil {
+ // Ignore up to two dashes at EOF.
+ if dashCount > 2 {
+ dashCount = 2
+ }
+ z.data.end = z.raw.end - dashCount
+ return
+ }
+ switch c {
+ case '-':
+ dashCount++
+ continue
+ case '>':
+ if dashCount >= 2 {
+ z.data.end = z.raw.end - len("-->")
+ return
+ }
+ case '!':
+ if dashCount >= 2 {
+ c = z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return
+ }
+ if c == '>' {
+ z.data.end = z.raw.end - len("--!>")
+ return
+ }
+ }
+ }
+ dashCount = 0
+ }
+}
+
+// readUntilCloseAngle reads until the next ">".
+func (z *Tokenizer) readUntilCloseAngle() {
+ z.data.start = z.raw.end
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return
+ }
+ if c == '>' {
+ z.data.end = z.raw.end - len(">")
+ return
+ }
+ }
+}
+
+// readMarkupDeclaration reads the next token starting with "<!". It might be
+// a "<!--comment-->", a "<!DOCTYPE foo>", a "<![CDATA[section]]>" or
+// "<!a bogus comment". The opening "<!" has already been consumed.
+func (z *Tokenizer) readMarkupDeclaration() TokenType {
+ z.data.start = z.raw.end
+ var c [2]byte
+ for i := 0; i < 2; i++ {
+ c[i] = z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return CommentToken
+ }
+ }
+ if c[0] == '-' && c[1] == '-' {
+ z.readComment()
+ return CommentToken
+ }
+ z.raw.end -= 2
+ if z.readDoctype() {
+ return DoctypeToken
+ }
+ if z.allowCDATA && z.readCDATA() {
+ z.convertNUL = true
+ return TextToken
+ }
+ // It's a bogus comment.
+ z.readUntilCloseAngle()
+ return CommentToken
+}
+
+// readDoctype attempts to read a doctype declaration and returns true if
+// successful. The opening "<!" has already been consumed.
+func (z *Tokenizer) readDoctype() bool {
+ const s = "DOCTYPE"
+ for i := 0; i < len(s); i++ {
+ c := z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return false
+ }
+ if c != s[i] && c != s[i]+('a'-'A') {
+ // Back up to read the fragment of "DOCTYPE" again.
+ z.raw.end = z.data.start
+ return false
+ }
+ }
+ if z.skipWhiteSpace(); z.err != nil {
+ z.data.start = z.raw.end
+ z.data.end = z.raw.end
+ return true
+ }
+ z.readUntilCloseAngle()
+ return true
+}
+
+// readCDATA attempts to read a CDATA section and returns true if
+// successful. The opening "<!" has already been consumed.
+func (z *Tokenizer) readCDATA() bool {
+ const s = "[CDATA["
+ for i := 0; i < len(s); i++ {
+ c := z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return false
+ }
+ if c != s[i] {
+ // Back up to read the fragment of "[CDATA[" again.
+ z.raw.end = z.data.start
+ return false
+ }
+ }
+ z.data.start = z.raw.end
+ brackets := 0
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return true
+ }
+ switch c {
+ case ']':
+ brackets++
+ case '>':
+ if brackets >= 2 {
+ z.data.end = z.raw.end - len("]]>")
+ return true
+ }
+ brackets = 0
+ default:
+ brackets = 0
+ }
+ }
+}
+
+// startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end]
+// case-insensitively matches any element of ss.
+func (z *Tokenizer) startTagIn(ss ...string) bool {
+loop:
+ for _, s := range ss {
+ if z.data.end-z.data.start != len(s) {
+ continue loop
+ }
+ for i := 0; i < len(s); i++ {
+ c := z.buf[z.data.start+i]
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ if c != s[i] {
+ continue loop
+ }
+ }
+ return true
+ }
+ return false
+}
+
+// readStartTag reads the next start tag token. The opening "<a" has already
+// been consumed, where 'a' means anything in [A-Za-z].
+func (z *Tokenizer) readStartTag() TokenType {
+ z.readTag(true)
+ if z.err != nil {
+ return ErrorToken
+ }
+ // Several tags flag the tokenizer's next token as raw.
+ c, raw := z.buf[z.data.start], false
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ switch c {
+ case 'i':
+ raw = z.startTagIn("iframe")
+ case 'n':
+ raw = z.startTagIn("noembed", "noframes", "noscript")
+ case 'p':
+ raw = z.startTagIn("plaintext")
+ case 's':
+ raw = z.startTagIn("script", "style")
+ case 't':
+ raw = z.startTagIn("textarea", "title")
+ case 'x':
+ raw = z.startTagIn("xmp")
+ }
+ if raw {
+ z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end]))
+ }
+ // Look for a self-closing token like "<br/>".
+ if z.err == nil && z.buf[z.raw.end-2] == '/' {
+ return SelfClosingTagToken
+ }
+ return StartTagToken
+}
+
+// readTag reads the next tag token and its attributes. If saveAttr, those
+// attributes are saved in z.attr, otherwise z.attr is set to an empty slice.
+// The opening "<a" or "</a" has already been consumed, where 'a' means anything
+// in [A-Za-z].
+func (z *Tokenizer) readTag(saveAttr bool) {
+ z.attr = z.attr[:0]
+ z.nAttrReturned = 0
+ // Read the tag name and attribute key/value pairs.
+ z.readTagName()
+ if z.skipWhiteSpace(); z.err != nil {
+ return
+ }
+ for {
+ c := z.readByte()
+ if z.err != nil || c == '>' {
+ break
+ }
+ z.raw.end--
+ z.readTagAttrKey()
+ z.readTagAttrVal()
+ // Save pendingAttr if saveAttr and that attribute has a non-empty key.
+ if saveAttr && z.pendingAttr[0].start != z.pendingAttr[0].end {
+ z.attr = append(z.attr, z.pendingAttr)
+ }
+ if z.skipWhiteSpace(); z.err != nil {
+ break
+ }
+ }
+}
+
+// readTagName sets z.data to the "div" in "<div k=v>". The reader (z.raw.end)
+// is positioned such that the first byte of the tag name (the "d" in "<div")
+// has already been consumed.
+func (z *Tokenizer) readTagName() {
+ z.data.start = z.raw.end - 1
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.data.end = z.raw.end
+ return
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f':
+ z.data.end = z.raw.end - 1
+ return
+ case '/', '>':
+ z.raw.end--
+ z.data.end = z.raw.end
+ return
+ }
+ }
+}
+
+// readTagAttrKey sets z.pendingAttr[0] to the "k" in "<div k=v>".
+// Precondition: z.err == nil.
+func (z *Tokenizer) readTagAttrKey() {
+ z.pendingAttr[0].start = z.raw.end
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.pendingAttr[0].end = z.raw.end
+ return
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f', '/':
+ z.pendingAttr[0].end = z.raw.end - 1
+ return
+ case '=', '>':
+ z.raw.end--
+ z.pendingAttr[0].end = z.raw.end
+ return
+ }
+ }
+}
+
+// readTagAttrVal sets z.pendingAttr[1] to the "v" in "<div k=v>".
+func (z *Tokenizer) readTagAttrVal() {
+ z.pendingAttr[1].start = z.raw.end
+ z.pendingAttr[1].end = z.raw.end
+ if z.skipWhiteSpace(); z.err != nil {
+ return
+ }
+ c := z.readByte()
+ if z.err != nil {
+ return
+ }
+ if c != '=' {
+ z.raw.end--
+ return
+ }
+ if z.skipWhiteSpace(); z.err != nil {
+ return
+ }
+ quote := z.readByte()
+ if z.err != nil {
+ return
+ }
+ switch quote {
+ case '>':
+ z.raw.end--
+ return
+
+ case '\'', '"':
+ z.pendingAttr[1].start = z.raw.end
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.pendingAttr[1].end = z.raw.end
+ return
+ }
+ if c == quote {
+ z.pendingAttr[1].end = z.raw.end - 1
+ return
+ }
+ }
+
+ default:
+ z.pendingAttr[1].start = z.raw.end - 1
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ z.pendingAttr[1].end = z.raw.end
+ return
+ }
+ switch c {
+ case ' ', '\n', '\r', '\t', '\f':
+ z.pendingAttr[1].end = z.raw.end - 1
+ return
+ case '>':
+ z.raw.end--
+ z.pendingAttr[1].end = z.raw.end
+ return
+ }
+ }
+ }
+}
+
+// Next scans the next token and returns its type.
+func (z *Tokenizer) Next() TokenType {
+ z.raw.start = z.raw.end
+ z.data.start = z.raw.end
+ z.data.end = z.raw.end
+ if z.err != nil {
+ z.tt = ErrorToken
+ return z.tt
+ }
+ if z.rawTag != "" {
+ if z.rawTag == "plaintext" {
+ // Read everything up to EOF.
+ for z.err == nil {
+ z.readByte()
+ }
+ z.data.end = z.raw.end
+ z.textIsRaw = true
+ } else {
+ z.readRawOrRCDATA()
+ }
+ if z.data.end > z.data.start {
+ z.tt = TextToken
+ z.convertNUL = true
+ return z.tt
+ }
+ }
+ z.textIsRaw = false
+ z.convertNUL = false
+
+loop:
+ for {
+ c := z.readByte()
+ if z.err != nil {
+ break loop
+ }
+ if c != '<' {
+ continue loop
+ }
+
+ // Check if the '<' we have just read is part of a tag, comment
+ // or doctype. If not, it's part of the accumulated text token.
+ c = z.readByte()
+ if z.err != nil {
+ break loop
+ }
+ var tokenType TokenType
+ switch {
+ case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
+ tokenType = StartTagToken
+ case c == '/':
+ tokenType = EndTagToken
+ case c == '!' || c == '?':
+ // We use CommentToken to mean any of "<!--actual comments-->",
+ // "<!DOCTYPE declarations>" and "<?xml processing instructions?>".
+ tokenType = CommentToken
+ default:
+ // Reconsume the current character.
+ z.raw.end--
+ continue
+ }
+
+ // We have a non-text token, but we might have accumulated some text
+ // before that. If so, we return the text first, and return the non-
+ // text token on the subsequent call to Next.
+ if x := z.raw.end - len("<a"); z.raw.start < x {
+ z.raw.end = x
+ z.data.end = x
+ z.tt = TextToken
+ return z.tt
+ }
+ switch tokenType {
+ case StartTagToken:
+ z.tt = z.readStartTag()
+ return z.tt
+ case EndTagToken:
+ c = z.readByte()
+ if z.err != nil {
+ break loop
+ }
+ if c == '>' {
+ // "</>" does not generate a token at all. Generate an empty comment
+ // to allow passthrough clients to pick up the data using Raw.
+ // Reset the tokenizer state and start again.
+ z.tt = CommentToken
+ return z.tt
+ }
+ if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
+ z.readTag(false)
+ if z.err != nil {
+ z.tt = ErrorToken
+ } else {
+ z.tt = EndTagToken
+ }
+ return z.tt
+ }
+ z.raw.end--
+ z.readUntilCloseAngle()
+ z.tt = CommentToken
+ return z.tt
+ case CommentToken:
+ if c == '!' {
+ z.tt = z.readMarkupDeclaration()
+ return z.tt
+ }
+ z.raw.end--
+ z.readUntilCloseAngle()
+ z.tt = CommentToken
+ return z.tt
+ }
+ }
+ if z.raw.start < z.raw.end {
+ z.data.end = z.raw.end
+ z.tt = TextToken
+ return z.tt
+ }
+ z.tt = ErrorToken
+ return z.tt
+}
+
+// Raw returns the unmodified text of the current token. Calling Next, Token,
+// Text, TagName or TagAttr may change the contents of the returned slice.
+func (z *Tokenizer) Raw() []byte {
+ return z.buf[z.raw.start:z.raw.end]
+}
+
+// convertNewlines converts "\r" and "\r\n" in s to "\n".
+// The conversion happens in place, but the resulting slice may be shorter.
+func convertNewlines(s []byte) []byte {
+ for i, c := range s {
+ if c != '\r' {
+ continue
+ }
+
+ src := i + 1
+ if src >= len(s) || s[src] != '\n' {
+ s[i] = '\n'
+ continue
+ }
+
+ dst := i
+ for src < len(s) {
+ if s[src] == '\r' {
+ if src+1 < len(s) && s[src+1] == '\n' {
+ src++
+ }
+ s[dst] = '\n'
+ } else {
+ s[dst] = s[src]
+ }
+ src++
+ dst++
+ }
+ return s[:dst]
+ }
+ return s
+}
+
+var (
+ nul = []byte("\x00")
+ replacement = []byte("\ufffd")
+)
+
+// Text returns the unescaped text of a text, comment or doctype token. The
+// contents of the returned slice may change on the next call to Next.
+func (z *Tokenizer) Text() []byte {
+ switch z.tt {
+ case TextToken, CommentToken, DoctypeToken:
+ s := z.buf[z.data.start:z.data.end]
+ z.data.start = z.raw.end
+ z.data.end = z.raw.end
+ s = convertNewlines(s)
+ if (z.convertNUL || z.tt == CommentToken) && bytes.Contains(s, nul) {
+ s = bytes.Replace(s, nul, replacement, -1)
+ }
+ if !z.textIsRaw {
+ s = unescape(s, false)
+ }
+ return s
+ }
+ return nil
+}
+
+// TagName returns the lower-cased name of a tag token (the `img` out of
+// `<IMG SRC="foo">`) and whether the tag has attributes.
+// The contents of the returned slice may change on the next call to Next.
+func (z *Tokenizer) TagName() (name []byte, hasAttr bool) {
+ if z.data.start < z.data.end {
+ switch z.tt {
+ case StartTagToken, EndTagToken, SelfClosingTagToken:
+ s := z.buf[z.data.start:z.data.end]
+ z.data.start = z.raw.end
+ z.data.end = z.raw.end
+ return lower(s), z.nAttrReturned < len(z.attr)
+ }
+ }
+ return nil, false
+}
+
+// TagAttr returns the lower-cased key and unescaped value of the next unparsed
+// attribute for the current tag token and whether there are more attributes.
+// The contents of the returned slices may change on the next call to Next.
+func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) {
+ if z.nAttrReturned < len(z.attr) {
+ switch z.tt {
+ case StartTagToken, SelfClosingTagToken:
+ x := z.attr[z.nAttrReturned]
+ z.nAttrReturned++
+ key = z.buf[x[0].start:x[0].end]
+ val = z.buf[x[1].start:x[1].end]
+ return lower(key), unescape(convertNewlines(val), true), z.nAttrReturned < len(z.attr)
+ }
+ }
+ return nil, nil, false
+}
+
+// Token returns the next Token. The result's Data and Attr values remain valid
+// after subsequent Next calls.
+func (z *Tokenizer) Token() Token {
+ t := Token{Type: z.tt}
+ switch z.tt {
+ case TextToken, CommentToken, DoctypeToken:
+ t.Data = string(z.Text())
+ case StartTagToken, SelfClosingTagToken, EndTagToken:
+ name, moreAttr := z.TagName()
+ for moreAttr {
+ var key, val []byte
+ key, val, moreAttr = z.TagAttr()
+ t.Attr = append(t.Attr, Attribute{"", atom.String(key), string(val)})
+ }
+ if a := atom.Lookup(name); a != 0 {
+ t.DataAtom, t.Data = a, a.String()
+ } else {
+ t.DataAtom, t.Data = 0, string(name)
+ }
+ }
+ return t
+}
+
+// SetMaxBuf sets a limit on the amount of data buffered during tokenization.
+// A value of 0 means unlimited.
+func (z *Tokenizer) SetMaxBuf(n int) {
+ z.maxBuf = n
+}
+
+// NewTokenizer returns a new HTML Tokenizer for the given Reader.
+// The input is assumed to be UTF-8 encoded.
+func NewTokenizer(r io.Reader) *Tokenizer {
+ return NewTokenizerFragment(r, "")
+}
+
+// NewTokenizerFragment returns a new HTML Tokenizer for the given Reader, for
+// tokenizing an existing element's InnerHTML fragment. contextTag is that
+// element's tag, such as "div" or "iframe".
+//
+// For example, how the InnerHTML "a<b" is tokenized depends on whether it is
+// for a <p> tag or a <script> tag.
+//
+// The input is assumed to be UTF-8 encoded.
+func NewTokenizerFragment(r io.Reader, contextTag string) *Tokenizer {
+ z := &Tokenizer{
+ r: r,
+ buf: make([]byte, 0, 4096),
+ }
+ if contextTag != "" {
+ switch s := strings.ToLower(contextTag); s {
+ case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp":
+ z.rawTag = s
+ }
+ }
+ return z
+}
diff --git a/html/token_test.go b/html/token_test.go
new file mode 100644
index 0000000..20221c3
--- /dev/null
+++ b/html/token_test.go
@@ -0,0 +1,748 @@
+// Copyright 2010 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.
+
+package html
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+type tokenTest struct {
+ // A short description of the test case.
+ desc string
+ // The HTML to parse.
+ html string
+ // The string representations of the expected tokens, joined by '$'.
+ golden string
+}
+
+var tokenTests = []tokenTest{
+ {
+ "empty",
+ "",
+ "",
+ },
+ // A single text node. The tokenizer should not break text nodes on whitespace,
+ // nor should it normalize whitespace within a text node.
+ {
+ "text",
+ "foo bar",
+ "foo bar",
+ },
+ // An entity.
+ {
+ "entity",
+ "one < two",
+ "one < two",
+ },
+ // A start, self-closing and end tag. The tokenizer does not care if the start
+ // and end tokens don't match; that is the job of the parser.
+ {
+ "tags",
+ "<a>b<c/>d</e>",
+ "<a>$b$<c/>$d$</e>",
+ },
+ // Angle brackets that aren't a tag.
+ {
+ "not a tag #0",
+ "<",
+ "<",
+ },
+ {
+ "not a tag #1",
+ "</",
+ "</",
+ },
+ {
+ "not a tag #2",
+ "</>",
+ "<!---->",
+ },
+ {
+ "not a tag #3",
+ "a</>b",
+ "a$<!---->$b",
+ },
+ {
+ "not a tag #4",
+ "</ >",
+ "<!-- -->",
+ },
+ {
+ "not a tag #5",
+ "</.",
+ "<!--.-->",
+ },
+ {
+ "not a tag #6",
+ "</.>",
+ "<!--.-->",
+ },
+ {
+ "not a tag #7",
+ "a < b",
+ "a < b",
+ },
+ {
+ "not a tag #8",
+ "<.>",
+ "<.>",
+ },
+ {
+ "not a tag #9",
+ "a<<<b>>>c",
+ "a<<$<b>$>>c",
+ },
+ {
+ "not a tag #10",
+ "if x<0 and y < 0 then x*y>0",
+ "if x<0 and y < 0 then x*y>0",
+ },
+ {
+ "not a tag #11",
+ "<<p>",
+ "<$<p>",
+ },
+ // EOF in a tag name.
+ {
+ "tag name eof #0",
+ "<a",
+ "",
+ },
+ {
+ "tag name eof #1",
+ "<a ",
+ "",
+ },
+ {
+ "tag name eof #2",
+ "a<b",
+ "a",
+ },
+ {
+ "tag name eof #3",
+ "<a><b",
+ "<a>",
+ },
+ {
+ "tag name eof #4",
+ `<a x`,
+ ``,
+ },
+ // Some malformed tags that are missing a '>'.
+ {
+ "malformed tag #0",
+ `<p</p>`,
+ `<p< p="">`,
+ },
+ {
+ "malformed tag #1",
+ `<p </p>`,
+ `<p <="" p="">`,
+ },
+ {
+ "malformed tag #2",
+ `<p id`,
+ ``,
+ },
+ {
+ "malformed tag #3",
+ `<p id=`,
+ ``,
+ },
+ {
+ "malformed tag #4",
+ `<p id=>`,
+ `<p id="">`,
+ },
+ {
+ "malformed tag #5",
+ `<p id=0`,
+ ``,
+ },
+ {
+ "malformed tag #6",
+ `<p id=0</p>`,
+ `<p id="0</p">`,
+ },
+ {
+ "malformed tag #7",
+ `<p id="0</p>`,
+ ``,
+ },
+ {
+ "malformed tag #8",
+ `<p id="0"</p>`,
+ `<p id="0" <="" p="">`,
+ },
+ {
+ "malformed tag #9",
+ `<p></p id`,
+ `<p>`,
+ },
+ // Raw text and RCDATA.
+ {
+ "basic raw text",
+ "<script><a></b></script>",
+ "<script>$<a></b>$</script>",
+ },
+ {
+ "unfinished script end tag",
+ "<SCRIPT>a</SCR",
+ "<script>$a</SCR",
+ },
+ {
+ "broken script end tag",
+ "<SCRIPT>a</SCR ipt>",
+ "<script>$a</SCR ipt>",
+ },
+ {
+ "EOF in script end tag",
+ "<SCRIPT>a</SCRipt",
+ "<script>$a</SCRipt",
+ },
+ {
+ "scriptx end tag",
+ "<SCRIPT>a</SCRiptx",
+ "<script>$a</SCRiptx",
+ },
+ {
+ "' ' completes script end tag",
+ "<SCRIPT>a</SCRipt ",
+ "<script>$a",
+ },
+ {
+ "'>' completes script end tag",
+ "<SCRIPT>a</SCRipt>",
+ "<script>$a$</script>",
+ },
+ {
+ "self-closing script end tag",
+ "<SCRIPT>a</SCRipt/>",
+ "<script>$a$</script>",
+ },
+ {
+ "nested script tag",
+ "<SCRIPT>a</SCRipt<script>",
+ "<script>$a</SCRipt<script>",
+ },
+ {
+ "script end tag after unfinished",
+ "<SCRIPT>a</SCRipt</script>",
+ "<script>$a</SCRipt$</script>",
+ },
+ {
+ "script/style mismatched tags",
+ "<script>a</style>",
+ "<script>$a</style>",
+ },
+ {
+ "style element with entity",
+ "<style>'",
+ "<style>$&apos;",
+ },
+ {
+ "textarea with tag",
+ "<textarea><div></textarea>",
+ "<textarea>$<div>$</textarea>",
+ },
+ {
+ "title with tag and entity",
+ "<title><b>K&R C</b></title>",
+ "<title>$<b>K&R C</b>$</title>",
+ },
+ // DOCTYPE tests.
+ {
+ "Proper DOCTYPE",
+ "<!DOCTYPE html>",
+ "<!DOCTYPE html>",
+ },
+ {
+ "DOCTYPE with no space",
+ "<!doctypehtml>",
+ "<!DOCTYPE html>",
+ },
+ {
+ "DOCTYPE with two spaces",
+ "<!doctype html>",
+ "<!DOCTYPE html>",
+ },
+ {
+ "looks like DOCTYPE but isn't",
+ "<!DOCUMENT html>",
+ "<!--DOCUMENT html-->",
+ },
+ {
+ "DOCTYPE at EOF",
+ "<!DOCtype",
+ "<!DOCTYPE >",
+ },
+ // XML processing instructions.
+ {
+ "XML processing instruction",
+ "<?xml?>",
+ "<!--?xml?-->",
+ },
+ // Comments.
+ {
+ "comment0",
+ "abc<b><!-- skipme --></b>def",
+ "abc$<b>$<!-- skipme -->$</b>$def",
+ },
+ {
+ "comment1",
+ "a<!-->z",
+ "a$<!---->$z",
+ },
+ {
+ "comment2",
+ "a<!--->z",
+ "a$<!---->$z",
+ },
+ {
+ "comment3",
+ "a<!--x>-->z",
+ "a$<!--x>-->$z",
+ },
+ {
+ "comment4",
+ "a<!--x->-->z",
+ "a$<!--x->-->$z",
+ },
+ {
+ "comment5",
+ "a<!>z",
+ "a$<!---->$z",
+ },
+ {
+ "comment6",
+ "a<!->z",
+ "a$<!----->$z",
+ },
+ {
+ "comment7",
+ "a<!---<>z",
+ "a$<!---<>z-->",
+ },
+ {
+ "comment8",
+ "a<!--z",
+ "a$<!--z-->",
+ },
+ {
+ "comment9",
+ "a<!--z-",
+ "a$<!--z-->",
+ },
+ {
+ "comment10",
+ "a<!--z--",
+ "a$<!--z-->",
+ },
+ {
+ "comment11",
+ "a<!--z---",
+ "a$<!--z--->",
+ },
+ {
+ "comment12",
+ "a<!--z----",
+ "a$<!--z---->",
+ },
+ {
+ "comment13",
+ "a<!--x--!>z",
+ "a$<!--x-->$z",
+ },
+ // An attribute with a backslash.
+ {
+ "backslash",
+ `<p id="a\"b">`,
+ `<p id="a\" b"="">`,
+ },
+ // Entities, tag name and attribute key lower-casing, and whitespace
+ // normalization within a tag.
+ {
+ "tricky",
+ "<p \t\n iD=\"a"B\" foo=\"bar\"><EM>te<&;xt</em></p>",
+ `<p id="a"B" foo="bar">$<em>$te<&;xt$</em>$</p>`,
+ },
+ // A nonexistent entity. Tokenizing and converting back to a string should
+ // escape the "&" to become "&".
+ {
+ "noSuchEntity",
+ `<a b="c&noSuchEntity;d"><&alsoDoesntExist;&`,
+ `<a b="c&noSuchEntity;d">$<&alsoDoesntExist;&`,
+ },
+ {
+ "entity without semicolon",
+ `¬it;∉<a b="q=z&=5¬ice=hello¬=world">`,
+ `¬it;∉$<a b="q=z&amp=5&notice=hello¬=world">`,
+ },
+ {
+ "entity with digits",
+ "½",
+ "½",
+ },
+ // Attribute tests:
+ // http://dev.w3.org/html5/pf-summary/Overview.html#attributes
+ {
+ "Empty attribute",
+ `<input disabled FOO>`,
+ `<input disabled="" foo="">`,
+ },
+ {
+ "Empty attribute, whitespace",
+ `<input disabled FOO >`,
+ `<input disabled="" foo="">`,
+ },
+ {
+ "Unquoted attribute value",
+ `<input value=yes FOO=BAR>`,
+ `<input value="yes" foo="BAR">`,
+ },
+ {
+ "Unquoted attribute value, spaces",
+ `<input value = yes FOO = BAR>`,
+ `<input value="yes" foo="BAR">`,
+ },
+ {
+ "Unquoted attribute value, trailing space",
+ `<input value=yes FOO=BAR >`,
+ `<input value="yes" foo="BAR">`,
+ },
+ {
+ "Single-quoted attribute value",
+ `<input value='yes' FOO='BAR'>`,
+ `<input value="yes" foo="BAR">`,
+ },
+ {
+ "Single-quoted attribute value, trailing space",
+ `<input value='yes' FOO='BAR' >`,
+ `<input value="yes" foo="BAR">`,
+ },
+ {
+ "Double-quoted attribute value",
+ `<input value="I'm an attribute" FOO="BAR">`,
+ `<input value="I'm an attribute" foo="BAR">`,
+ },
+ {
+ "Attribute name characters",
+ `<meta http-equiv="content-type">`,
+ `<meta http-equiv="content-type">`,
+ },
+ {
+ "Mixed attributes",
+ `a<P V="0 1" w='2' X=3 y>z`,
+ `a$<p v="0 1" w="2" x="3" y="">$z`,
+ },
+ {
+ "Attributes with a solitary single quote",
+ `<p id=can't><p id=won't>`,
+ `<p id="can't">$<p id="won't">`,
+ },
+}
+
+func TestTokenizer(t *testing.T) {
+loop:
+ for _, tt := range tokenTests {
+ z := NewTokenizer(strings.NewReader(tt.html))
+ if tt.golden != "" {
+ for i, s := range strings.Split(tt.golden, "$") {
+ if z.Next() == ErrorToken {
+ t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Err())
+ continue loop
+ }
+ actual := z.Token().String()
+ if s != actual {
+ t.Errorf("%s token %d: want %q got %q", tt.desc, i, s, actual)
+ continue loop
+ }
+ }
+ }
+ z.Next()
+ if z.Err() != io.EOF {
+ t.Errorf("%s: want EOF got %q", tt.desc, z.Err())
+ }
+ }
+}
+
+func TestMaxBuffer(t *testing.T) {
+ // Exceeding the maximum buffer size generates ErrBufferExceeded.
+ z := NewTokenizer(strings.NewReader("<" + strings.Repeat("t", 10)))
+ z.SetMaxBuf(5)
+ tt := z.Next()
+ if got, want := tt, ErrorToken; got != want {
+ t.Fatalf("token type: got: %v want: %v", got, want)
+ }
+ if got, want := z.Err(), ErrBufferExceeded; got != want {
+ t.Errorf("error type: got: %v want: %v", got, want)
+ }
+ if got, want := string(z.Raw()), "<tttt"; got != want {
+ t.Fatalf("buffered before overflow: got: %q want: %q", got, want)
+ }
+}
+
+func TestMaxBufferReconstruction(t *testing.T) {
+ // Exceeding the maximum buffer size at any point while tokenizing permits
+ // reconstructing the original input.
+tests:
+ for _, test := range tokenTests {
+ for maxBuf := 1; ; maxBuf++ {
+ r := strings.NewReader(test.html)
+ z := NewTokenizer(r)
+ z.SetMaxBuf(maxBuf)
+ var tokenized bytes.Buffer
+ for {
+ tt := z.Next()
+ tokenized.Write(z.Raw())
+ if tt == ErrorToken {
+ if err := z.Err(); err != io.EOF && err != ErrBufferExceeded {
+ t.Errorf("%s: unexpected error: %v", test.desc, err)
+ }
+ break
+ }
+ }
+ // Anything tokenized along with untokenized input or data left in the reader.
+ assembled, err := ioutil.ReadAll(io.MultiReader(&tokenized, bytes.NewReader(z.Buffered()), r))
+ if err != nil {
+ t.Errorf("%s: ReadAll: %v", test.desc, err)
+ continue tests
+ }
+ if got, want := string(assembled), test.html; got != want {
+ t.Errorf("%s: reassembled html:\n got: %q\nwant: %q", test.desc, got, want)
+ continue tests
+ }
+ // EOF indicates that we completed tokenization and hence found the max
+ // maxBuf that generates ErrBufferExceeded, so continue to the next test.
+ if z.Err() == io.EOF {
+ break
+ }
+ } // buffer sizes
+ } // tests
+}
+
+func TestPassthrough(t *testing.T) {
+ // Accumulating the raw output for each parse event should reconstruct the
+ // original input.
+ for _, test := range tokenTests {
+ z := NewTokenizer(strings.NewReader(test.html))
+ var parsed bytes.Buffer
+ for {
+ tt := z.Next()
+ parsed.Write(z.Raw())
+ if tt == ErrorToken {
+ break
+ }
+ }
+ if got, want := parsed.String(), test.html; got != want {
+ t.Errorf("%s: parsed output:\n got: %q\nwant: %q", test.desc, got, want)
+ }
+ }
+}
+
+func TestBufAPI(t *testing.T) {
+ s := "0<a>1</a>2<b>3<a>4<a>5</a>6</b>7</a>8<a/>9"
+ z := NewTokenizer(bytes.NewBufferString(s))
+ var result bytes.Buffer
+ depth := 0
+loop:
+ for {
+ tt := z.Next()
+ switch tt {
+ case ErrorToken:
+ if z.Err() != io.EOF {
+ t.Error(z.Err())
+ }
+ break loop
+ case TextToken:
+ if depth > 0 {
+ result.Write(z.Text())
+ }
+ case StartTagToken, EndTagToken:
+ tn, _ := z.TagName()
+ if len(tn) == 1 && tn[0] == 'a' {
+ if tt == StartTagToken {
+ depth++
+ } else {
+ depth--
+ }
+ }
+ }
+ }
+ u := "14567"
+ v := string(result.Bytes())
+ if u != v {
+ t.Errorf("TestBufAPI: want %q got %q", u, v)
+ }
+}
+
+func TestConvertNewlines(t *testing.T) {
+ testCases := map[string]string{
+ "Mac\rDOS\r\nUnix\n": "Mac\nDOS\nUnix\n",
+ "Unix\nMac\rDOS\r\n": "Unix\nMac\nDOS\n",
+ "DOS\r\nDOS\r\nDOS\r\n": "DOS\nDOS\nDOS\n",
+ "": "",
+ "\n": "\n",
+ "\n\r": "\n\n",
+ "\r": "\n",
+ "\r\n": "\n",
+ "\r\n\n": "\n\n",
+ "\r\n\r": "\n\n",
+ "\r\n\r\n": "\n\n",
+ "\r\r": "\n\n",
+ "\r\r\n": "\n\n",
+ "\r\r\n\n": "\n\n\n",
+ "\r\r\r\n": "\n\n\n",
+ "\r \n": "\n \n",
+ "xyz": "xyz",
+ }
+ for in, want := range testCases {
+ if got := string(convertNewlines([]byte(in))); got != want {
+ t.Errorf("input %q: got %q, want %q", in, got, want)
+ }
+ }
+}
+
+func TestReaderEdgeCases(t *testing.T) {
+ const s = "<p>An io.Reader can return (0, nil) or (n, io.EOF).</p>"
+ testCases := []io.Reader{
+ &zeroOneByteReader{s: s},
+ &eofStringsReader{s: s},
+ &stuckReader{},
+ }
+ for i, tc := range testCases {
+ got := []TokenType{}
+ z := NewTokenizer(tc)
+ for {
+ tt := z.Next()
+ if tt == ErrorToken {
+ break
+ }
+ got = append(got, tt)
+ }
+ if err := z.Err(); err != nil && err != io.EOF {
+ if err != io.ErrNoProgress {
+ t.Errorf("i=%d: %v", i, err)
+ }
+ continue
+ }
+ want := []TokenType{
+ StartTagToken,
+ TextToken,
+ EndTagToken,
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("i=%d: got %v, want %v", i, got, want)
+ continue
+ }
+ }
+}
+
+// zeroOneByteReader is like a strings.Reader that alternates between
+// returning 0 bytes and 1 byte at a time.
+type zeroOneByteReader struct {
+ s string
+ n int
+}
+
+func (r *zeroOneByteReader) Read(p []byte) (int, error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ if len(r.s) == 0 {
+ return 0, io.EOF
+ }
+ r.n++
+ if r.n%2 != 0 {
+ return 0, nil
+ }
+ p[0], r.s = r.s[0], r.s[1:]
+ return 1, nil
+}
+
+// eofStringsReader is like a strings.Reader but can return an (n, err) where
+// n > 0 && err != nil.
+type eofStringsReader struct {
+ s string
+}
+
+func (r *eofStringsReader) Read(p []byte) (int, error) {
+ n := copy(p, r.s)
+ r.s = r.s[n:]
+ if r.s != "" {
+ return n, nil
+ }
+ return n, io.EOF
+}
+
+// stuckReader is an io.Reader that always returns no data and no error.
+type stuckReader struct{}
+
+func (*stuckReader) Read(p []byte) (int, error) {
+ return 0, nil
+}
+
+const (
+ rawLevel = iota
+ lowLevel
+ highLevel
+)
+
+func benchmarkTokenizer(b *testing.B, level int) {
+ buf, err := ioutil.ReadFile("testdata/go1.html")
+ if err != nil {
+ b.Fatalf("could not read testdata/go1.html: %v", err)
+ }
+ b.SetBytes(int64(len(buf)))
+ runtime.GC()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ z := NewTokenizer(bytes.NewBuffer(buf))
+ for {
+ tt := z.Next()
+ if tt == ErrorToken {
+ if err := z.Err(); err != nil && err != io.EOF {
+ b.Fatalf("tokenizer error: %v", err)
+ }
+ break
+ }
+ switch level {
+ case rawLevel:
+ // Calling z.Raw just returns the raw bytes of the token. It does
+ // not unescape < to <, or lower-case tag names and attribute keys.
+ z.Raw()
+ case lowLevel:
+ // Caling z.Text, z.TagName and z.TagAttr returns []byte values
+ // whose contents may change on the next call to z.Next.
+ switch tt {
+ case TextToken, CommentToken, DoctypeToken:
+ z.Text()
+ case StartTagToken, SelfClosingTagToken:
+ _, more := z.TagName()
+ for more {
+ _, _, more = z.TagAttr()
+ }
+ case EndTagToken:
+ z.TagName()
+ }
+ case highLevel:
+ // Calling z.Token converts []byte values to strings whose validity
+ // extend beyond the next call to z.Next.
+ z.Token()
+ }
+ }
+ }
+}
+
+func BenchmarkRawLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, rawLevel) }
+func BenchmarkLowLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, lowLevel) }
+func BenchmarkHighLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, highLevel) }
diff --git a/icmp/dstunreach.go b/icmp/dstunreach.go
new file mode 100644
index 0000000..01dc660
--- /dev/null
+++ b/icmp/dstunreach.go
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+package icmp
+
+// A DstUnreach represents an ICMP destination unreachable message
+// body.
+type DstUnreach struct {
+ Data []byte // data, known as original datagram field
+ Extensions []Extension // extensions
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *DstUnreach) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
+ return l
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *DstUnreach) Marshal(proto int) ([]byte, error) {
+ return marshalMultipartMessageBody(proto, p.Data, p.Extensions)
+}
+
+// parseDstUnreach parses b as an ICMP destination unreachable message
+// body.
+func parseDstUnreach(proto int, b []byte) (MessageBody, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ p := &DstUnreach{}
+ var err error
+ p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
diff --git a/icmp/echo.go b/icmp/echo.go
new file mode 100644
index 0000000..8943eab
--- /dev/null
+++ b/icmp/echo.go
@@ -0,0 +1,43 @@
+// Copyright 2012 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.
+
+package icmp
+
+// An Echo represents an ICMP echo request or reply message body.
+type Echo struct {
+ ID int // identifier
+ Seq int // sequence number
+ Data []byte // data
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *Echo) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ return 4 + len(p.Data)
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *Echo) Marshal(proto int) ([]byte, error) {
+ b := make([]byte, 4+len(p.Data))
+ b[0], b[1] = byte(p.ID>>8), byte(p.ID)
+ b[2], b[3] = byte(p.Seq>>8), byte(p.Seq)
+ copy(b[4:], p.Data)
+ return b, nil
+}
+
+// parseEcho parses b as an ICMP echo request or reply message body.
+func parseEcho(proto int, b []byte) (MessageBody, error) {
+ bodyLen := len(b)
+ if bodyLen < 4 {
+ return nil, errMessageTooShort
+ }
+ p := &Echo{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
+ if bodyLen > 4 {
+ p.Data = make([]byte, bodyLen-4)
+ copy(p.Data, b[4:])
+ }
+ return p, nil
+}
diff --git a/icmp/endpoint.go b/icmp/endpoint.go
new file mode 100644
index 0000000..3de49e6
--- /dev/null
+++ b/icmp/endpoint.go
@@ -0,0 +1,118 @@
+// Copyright 2014 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.
+
+package icmp
+
+import (
+ "net"
+ "runtime"
+ "syscall"
+ "time"
+
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+var _ net.PacketConn = &PacketConn{}
+
+type ipc interface{}
+
+// A PacketConn represents a packet network endpoint that uses either
+// ICMPv4 or ICMPv6.
+type PacketConn struct {
+ c net.PacketConn
+ ipc // either ipv4.PacketConn or ipv6.PacketConn
+}
+
+func (c *PacketConn) ok() bool { return c != nil && c.c != nil }
+
+// IPv4PacketConn returns the ipv4.PacketConn of c.
+// It returns nil when c is not created as the endpoint for ICMPv4.
+func (c *PacketConn) IPv4PacketConn() *ipv4.PacketConn {
+ if !c.ok() {
+ return nil
+ }
+ p, _ := c.ipc.(*ipv4.PacketConn)
+ return p
+}
+
+// IPv6PacketConn returns the ipv6.PacketConn of c.
+// It returns nil when c is not created as the endpoint for ICMPv6.
+func (c *PacketConn) IPv6PacketConn() *ipv6.PacketConn {
+ if !c.ok() {
+ return nil
+ }
+ p, _ := c.ipc.(*ipv6.PacketConn)
+ return p
+}
+
+// ReadFrom reads an ICMP message from the connection.
+func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
+ if !c.ok() {
+ return 0, nil, syscall.EINVAL
+ }
+ // Please be informed that ipv4.NewPacketConn enables
+ // IP_STRIPHDR option by default on Darwin.
+ // See golang.org/issue/9395 for futher information.
+ if runtime.GOOS == "darwin" {
+ if p, _ := c.ipc.(*ipv4.PacketConn); p != nil {
+ n, _, peer, err := p.ReadFrom(b)
+ return n, peer, err
+ }
+ }
+ return c.c.ReadFrom(b)
+}
+
+// WriteTo writes the ICMP message b to dst.
+// Dst must be net.UDPAddr when c is a non-privileged
+// datagram-oriented ICMP endpoint. Otherwise it must be net.IPAddr.
+func (c *PacketConn) WriteTo(b []byte, dst net.Addr) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ return c.c.WriteTo(b, dst)
+}
+
+// Close closes the endpoint.
+func (c *PacketConn) Close() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.c.Close()
+}
+
+// LocalAddr returns the local network address.
+func (c *PacketConn) LocalAddr() net.Addr {
+ if !c.ok() {
+ return nil
+ }
+ return c.c.LocalAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated with the
+// endpoint.
+func (c *PacketConn) SetDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.c.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline associated with the
+// endpoint.
+func (c *PacketConn) SetReadDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.c.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline associated with the
+// endpoint.
+func (c *PacketConn) SetWriteDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.c.SetWriteDeadline(t)
+}
diff --git a/icmp/example_test.go b/icmp/example_test.go
new file mode 100644
index 0000000..1df4cec
--- /dev/null
+++ b/icmp/example_test.go
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+package icmp_test
+
+import (
+ "log"
+ "net"
+ "os"
+ "runtime"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/ipv6"
+)
+
+func ExamplePacketConn_nonPrivilegedPing() {
+ switch runtime.GOOS {
+ case "darwin":
+ case "linux":
+ log.Println("you may need to adjust the net.ipv4.ping_group_range kernel state")
+ default:
+ log.Println("not supported on", runtime.GOOS)
+ return
+ }
+
+ c, err := icmp.ListenPacket("udp6", "fe80::1%en0")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+
+ wm := icmp.Message{
+ Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }
+ wb, err := wm.Marshal(nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if _, err := c.WriteTo(wb, &net.UDPAddr{IP: net.ParseIP("ff02::1"), Zone: "en0"}); err != nil {
+ log.Fatal(err)
+ }
+
+ rb := make([]byte, 1500)
+ n, peer, err := c.ReadFrom(rb)
+ if err != nil {
+ log.Fatal(err)
+ }
+ rm, err := icmp.ParseMessage(58, rb[:n])
+ if err != nil {
+ log.Fatal(err)
+ }
+ switch rm.Type {
+ case ipv6.ICMPTypeEchoReply:
+ log.Printf("got reflection from %v", peer)
+ default:
+ log.Printf("got %+v; want echo reply", rm)
+ }
+}
diff --git a/icmp/extension.go b/icmp/extension.go
new file mode 100644
index 0000000..720e167
--- /dev/null
+++ b/icmp/extension.go
@@ -0,0 +1,87 @@
+// Copyright 2015 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.
+
+package icmp
+
+// An Extension represents an ICMP extension.
+type Extension interface {
+ // Len returns the length of ICMP extension.
+ // Proto must be either the ICMPv4 or ICMPv6 protocol number.
+ Len(proto int) int
+
+ // Marshal returns the binary enconding of ICMP extension.
+ // Proto must be either the ICMPv4 or ICMPv6 protocol number.
+ Marshal(proto int) ([]byte, error)
+}
+
+const extensionVersion = 2
+
+func validExtensionHeader(b []byte) bool {
+ v := int(b[0]&0xf0) >> 4
+ s := uint16(b[2])<<8 | uint16(b[3])
+ if s != 0 {
+ s = checksum(b)
+ }
+ if v != extensionVersion || s != 0 {
+ return false
+ }
+ return true
+}
+
+// parseExtensions parses b as a list of ICMP extensions.
+// The length attribute l must be the length attribute field in
+// received icmp messages.
+//
+// It will return a list of ICMP extensions and an adjusted length
+// attribute that represents the length of the padded original
+// datagram field. Otherwise, it returns an error.
+func parseExtensions(b []byte, l int) ([]Extension, int, error) {
+ // Still a lot of non-RFC 4884 compliant implementations are
+ // out there. Set the length attribute l to 128 when it looks
+ // inappropriate for backwards compatibility.
+ //
+ // A minimal extension at least requires 8 octets; 4 octets
+ // for an extension header, and 4 octets for a single object
+ // header.
+ //
+ // See RFC 4884 for further information.
+ if 128 > l || l+8 > len(b) {
+ l = 128
+ }
+ if l+8 > len(b) {
+ return nil, -1, errNoExtension
+ }
+ if !validExtensionHeader(b[l:]) {
+ if l == 128 {
+ return nil, -1, errNoExtension
+ }
+ l = 128
+ if !validExtensionHeader(b[l:]) {
+ return nil, -1, errNoExtension
+ }
+ }
+ var exts []Extension
+ for b = b[l+4:]; len(b) >= 4; {
+ ol := int(b[0])<<8 | int(b[1])
+ if 4 > ol || ol > len(b) {
+ break
+ }
+ switch b[2] {
+ case classMPLSLabelStack:
+ ext, err := parseMPLSLabelStack(b[:ol])
+ if err != nil {
+ return nil, -1, err
+ }
+ exts = append(exts, ext)
+ case classInterfaceInfo:
+ ext, err := parseInterfaceInfo(b[:ol])
+ if err != nil {
+ return nil, -1, err
+ }
+ exts = append(exts, ext)
+ }
+ b = b[ol:]
+ }
+ return exts, l, nil
+}
diff --git a/icmp/extension_test.go b/icmp/extension_test.go
new file mode 100644
index 0000000..0b3f7b9
--- /dev/null
+++ b/icmp/extension_test.go
@@ -0,0 +1,259 @@
+// Copyright 2015 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.
+
+package icmp
+
+import (
+ "net"
+ "reflect"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+)
+
+var marshalAndParseExtensionTests = []struct {
+ proto int
+ hdr []byte
+ obj []byte
+ exts []Extension
+}{
+ // MPLS label stack with no label
+ {
+ proto: iana.ProtocolICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x04, 0x01, 0x01,
+ },
+ exts: []Extension{
+ &MPLSLabelStack{
+ Class: classMPLSLabelStack,
+ Type: typeIncomingMPLSLabelStack,
+ },
+ },
+ },
+ // MPLS label stack with a single label
+ {
+ proto: iana.ProtocolIPv6ICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x08, 0x01, 0x01,
+ 0x03, 0xe8, 0xe9, 0xff,
+ },
+ exts: []Extension{
+ &MPLSLabelStack{
+ Class: classMPLSLabelStack,
+ Type: typeIncomingMPLSLabelStack,
+ Labels: []MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ },
+ },
+ // MPLS label stack with multiple labels
+ {
+ proto: iana.ProtocolICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x0c, 0x01, 0x01,
+ 0x03, 0xe8, 0xde, 0xfe,
+ 0x03, 0xe8, 0xe1, 0xff,
+ },
+ exts: []Extension{
+ &MPLSLabelStack{
+ Class: classMPLSLabelStack,
+ Type: typeIncomingMPLSLabelStack,
+ Labels: []MPLSLabel{
+ {
+ Label: 16013,
+ TC: 0x7,
+ S: false,
+ TTL: 254,
+ },
+ {
+ Label: 16014,
+ TC: 0,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ },
+ },
+ // Interface information with no attribute
+ {
+ proto: iana.ProtocolICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x04, 0x02, 0x00,
+ },
+ exts: []Extension{
+ &InterfaceInfo{
+ Class: classInterfaceInfo,
+ },
+ },
+ },
+ // Interface information with ifIndex and name
+ {
+ proto: iana.ProtocolICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x10, 0x02, 0x0a,
+ 0x00, 0x00, 0x00, 0x10,
+ 0x08, byte('e'), byte('n'), byte('1'),
+ byte('0'), byte('1'), 0x00, 0x00,
+ },
+ exts: []Extension{
+ &InterfaceInfo{
+ Class: classInterfaceInfo,
+ Type: 0x0a,
+ Interface: &net.Interface{
+ Index: 16,
+ Name: "en101",
+ },
+ },
+ },
+ },
+ // Interface information with ifIndex, IPAddr, name and MTU
+ {
+ proto: iana.ProtocolIPv6ICMP,
+ hdr: []byte{
+ 0x20, 0x00, 0x00, 0x00,
+ },
+ obj: []byte{
+ 0x00, 0x28, 0x02, 0x0f,
+ 0x00, 0x00, 0x00, 0x0f,
+ 0x00, 0x02, 0x00, 0x00,
+ 0xfe, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x08, byte('e'), byte('n'), byte('1'),
+ byte('0'), byte('1'), 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x00,
+ },
+ exts: []Extension{
+ &InterfaceInfo{
+ Class: classInterfaceInfo,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.ParseIP("fe80::1"),
+ Zone: "en101",
+ },
+ },
+ },
+ },
+}
+
+func TestMarshalAndParseExtension(t *testing.T) {
+ for i, tt := range marshalAndParseExtensionTests {
+ for j, ext := range tt.exts {
+ var err error
+ var b []byte
+ switch ext := ext.(type) {
+ case *MPLSLabelStack:
+ b, err = ext.Marshal(tt.proto)
+ if err != nil {
+ t.Errorf("#%v/%v: %v", i, j, err)
+ continue
+ }
+ case *InterfaceInfo:
+ b, err = ext.Marshal(tt.proto)
+ if err != nil {
+ t.Errorf("#%v/%v: %v", i, j, err)
+ continue
+ }
+ }
+ if !reflect.DeepEqual(b, tt.obj) {
+ t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
+ continue
+ }
+ }
+
+ for j, wire := range []struct {
+ data []byte // original datagram
+ inlattr int // length of padded original datagram, a hint
+ outlattr int // length of padded original datagram, a want
+ err error
+ }{
+ {nil, 0, -1, errNoExtension},
+ {make([]byte, 127), 128, -1, errNoExtension},
+
+ {make([]byte, 128), 127, -1, errNoExtension},
+ {make([]byte, 128), 128, -1, errNoExtension},
+ {make([]byte, 128), 129, -1, errNoExtension},
+
+ {append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 127, 128, nil},
+ {append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 128, 128, nil},
+ {append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 129, 128, nil},
+
+ {append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 511, -1, errNoExtension},
+ {append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 512, 512, nil},
+ {append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 513, -1, errNoExtension},
+ } {
+ exts, l, err := parseExtensions(wire.data, wire.inlattr)
+ if err != wire.err {
+ t.Errorf("#%v/%v: got %v; want %v", i, j, err, wire.err)
+ continue
+ }
+ if wire.err != nil {
+ continue
+ }
+ if l != wire.outlattr {
+ t.Errorf("#%v/%v: got %v; want %v", i, j, l, wire.outlattr)
+ }
+ if !reflect.DeepEqual(exts, tt.exts) {
+ for j, ext := range exts {
+ switch ext := ext.(type) {
+ case *MPLSLabelStack:
+ want := tt.exts[j].(*MPLSLabelStack)
+ t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
+ case *InterfaceInfo:
+ want := tt.exts[j].(*InterfaceInfo)
+ t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
+ }
+ }
+ continue
+ }
+ }
+ }
+}
+
+var parseInterfaceNameTests = []struct {
+ b []byte
+ error
+}{
+ {[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
+ {[]byte{4, 'e', 'n', '0'}, nil},
+ {[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
+ {[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
+}
+
+func TestParseInterfaceName(t *testing.T) {
+ ifi := InterfaceInfo{Interface: &net.Interface{}}
+ for i, tt := range parseInterfaceNameTests {
+ if _, err := ifi.parseName(tt.b); err != tt.error {
+ t.Errorf("#%d: got %v; want %v", i, err, tt.error)
+ }
+ }
+}
diff --git a/icmp/helper_posix.go b/icmp/helper_posix.go
new file mode 100644
index 0000000..398fd38
--- /dev/null
+++ b/icmp/helper_posix.go
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+
+package icmp
+
+import (
+ "net"
+ "strconv"
+ "syscall"
+)
+
+func sockaddr(family int, address string) (syscall.Sockaddr, error) {
+ switch family {
+ case syscall.AF_INET:
+ a, err := net.ResolveIPAddr("ip4", address)
+ if err != nil {
+ return nil, err
+ }
+ if len(a.IP) == 0 {
+ a.IP = net.IPv4zero
+ }
+ if a.IP = a.IP.To4(); a.IP == nil {
+ return nil, net.InvalidAddrError("non-ipv4 address")
+ }
+ sa := &syscall.SockaddrInet4{}
+ copy(sa.Addr[:], a.IP)
+ return sa, nil
+ case syscall.AF_INET6:
+ a, err := net.ResolveIPAddr("ip6", address)
+ if err != nil {
+ return nil, err
+ }
+ if len(a.IP) == 0 {
+ a.IP = net.IPv6unspecified
+ }
+ if a.IP.Equal(net.IPv4zero) {
+ a.IP = net.IPv6unspecified
+ }
+ if a.IP = a.IP.To16(); a.IP == nil || a.IP.To4() != nil {
+ return nil, net.InvalidAddrError("non-ipv6 address")
+ }
+ sa := &syscall.SockaddrInet6{ZoneId: zoneToUint32(a.Zone)}
+ copy(sa.Addr[:], a.IP)
+ return sa, nil
+ default:
+ return nil, net.InvalidAddrError("unexpected family")
+ }
+}
+
+func zoneToUint32(zone string) uint32 {
+ if zone == "" {
+ return 0
+ }
+ if ifi, err := net.InterfaceByName(zone); err == nil {
+ return uint32(ifi.Index)
+ }
+ n, err := strconv.Atoi(zone)
+ if err != nil {
+ return 0
+ }
+ return uint32(n)
+}
+
+func last(s string, b byte) int {
+ i := len(s)
+ for i--; i >= 0; i-- {
+ if s[i] == b {
+ break
+ }
+ }
+ return i
+}
diff --git a/icmp/interface.go b/icmp/interface.go
new file mode 100644
index 0000000..c7bf8dd
--- /dev/null
+++ b/icmp/interface.go
@@ -0,0 +1,235 @@
+// Copyright 2015 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.
+
+package icmp
+
+import (
+ "net"
+ "strings"
+
+ "golang.org/x/net/internal/iana"
+)
+
+const (
+ classInterfaceInfo = 2
+
+ afiIPv4 = 1
+ afiIPv6 = 2
+)
+
+const (
+ attrMTU = 1 << iota
+ attrName
+ attrIPAddr
+ attrIfIndex
+)
+
+// An InterfaceInfo represents interface and next-hop identification.
+type InterfaceInfo struct {
+ Class int // extension object class number
+ Type int // extension object sub-type
+ Interface *net.Interface
+ Addr *net.IPAddr
+}
+
+func (ifi *InterfaceInfo) nameLen() int {
+ if len(ifi.Interface.Name) > 63 {
+ return 64
+ }
+ l := 1 + len(ifi.Interface.Name)
+ return (l + 3) &^ 3
+}
+
+func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) {
+ l = 4
+ if ifi.Interface != nil && ifi.Interface.Index > 0 {
+ attrs |= attrIfIndex
+ l += 4
+ if len(ifi.Interface.Name) > 0 {
+ attrs |= attrName
+ l += ifi.nameLen()
+ }
+ if ifi.Interface.MTU > 0 {
+ attrs |= attrMTU
+ l += 4
+ }
+ }
+ if ifi.Addr != nil {
+ switch proto {
+ case iana.ProtocolICMP:
+ if ifi.Addr.IP.To4() != nil {
+ attrs |= attrIPAddr
+ l += 4 + net.IPv4len
+ }
+ case iana.ProtocolIPv6ICMP:
+ if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
+ attrs |= attrIPAddr
+ l += 4 + net.IPv6len
+ }
+ }
+ }
+ return
+}
+
+// Len implements the Len method of Extension interface.
+func (ifi *InterfaceInfo) Len(proto int) int {
+ _, l := ifi.attrsAndLen(proto)
+ return l
+}
+
+// Marshal implements the Marshal method of Extension interface.
+func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) {
+ attrs, l := ifi.attrsAndLen(proto)
+ b := make([]byte, l)
+ if err := ifi.marshal(proto, b, attrs, l); err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error {
+ b[0], b[1] = byte(l>>8), byte(l)
+ b[2], b[3] = classInterfaceInfo, byte(ifi.Type)
+ for b = b[4:]; len(b) > 0 && attrs != 0; {
+ switch {
+ case attrs&attrIfIndex != 0:
+ b = ifi.marshalIfIndex(proto, b)
+ attrs &^= attrIfIndex
+ case attrs&attrIPAddr != 0:
+ b = ifi.marshalIPAddr(proto, b)
+ attrs &^= attrIPAddr
+ case attrs&attrName != 0:
+ b = ifi.marshalName(proto, b)
+ attrs &^= attrName
+ case attrs&attrMTU != 0:
+ b = ifi.marshalMTU(proto, b)
+ attrs &^= attrMTU
+ }
+ }
+ return nil
+}
+
+func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte {
+ b[0], b[1], b[2], b[3] = byte(ifi.Interface.Index>>24), byte(ifi.Interface.Index>>16), byte(ifi.Interface.Index>>8), byte(ifi.Interface.Index)
+ return b[4:]
+}
+
+func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ ifi.Interface.Index = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
+ return b[4:], nil
+}
+
+func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
+ switch proto {
+ case iana.ProtocolICMP:
+ b[0], b[1] = byte(afiIPv4>>8), byte(afiIPv4)
+ copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
+ b = b[4+net.IPv4len:]
+ case iana.ProtocolIPv6ICMP:
+ b[0], b[1] = byte(afiIPv6>>8), byte(afiIPv6)
+ copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
+ b = b[4+net.IPv6len:]
+ }
+ return b
+}
+
+func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ afi := int(b[0])<<8 | int(b[1])
+ b = b[4:]
+ switch afi {
+ case afiIPv4:
+ if len(b) < net.IPv4len {
+ return nil, errMessageTooShort
+ }
+ ifi.Addr.IP = make(net.IP, net.IPv4len)
+ copy(ifi.Addr.IP, b[:net.IPv4len])
+ b = b[net.IPv4len:]
+ case afiIPv6:
+ if len(b) < net.IPv6len {
+ return nil, errMessageTooShort
+ }
+ ifi.Addr.IP = make(net.IP, net.IPv6len)
+ copy(ifi.Addr.IP, b[:net.IPv6len])
+ b = b[net.IPv6len:]
+ }
+ return b, nil
+}
+
+func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte {
+ l := byte(ifi.nameLen())
+ b[0] = l
+ copy(b[1:], []byte(ifi.Interface.Name))
+ return b[l:]
+}
+
+func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) {
+ if 4 > len(b) || len(b) < int(b[0]) {
+ return nil, errMessageTooShort
+ }
+ l := int(b[0])
+ if l%4 != 0 || 4 > l || l > 64 {
+ return nil, errInvalidExtension
+ }
+ var name [63]byte
+ copy(name[:], b[1:l])
+ ifi.Interface.Name = strings.Trim(string(name[:]), "\000")
+ return b[l:], nil
+}
+
+func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte {
+ b[0], b[1], b[2], b[3] = byte(ifi.Interface.MTU>>24), byte(ifi.Interface.MTU>>16), byte(ifi.Interface.MTU>>8), byte(ifi.Interface.MTU)
+ return b[4:]
+}
+
+func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ ifi.Interface.MTU = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
+ return b[4:], nil
+}
+
+func parseInterfaceInfo(b []byte) (Extension, error) {
+ ifi := &InterfaceInfo{
+ Class: int(b[2]),
+ Type: int(b[3]),
+ }
+ if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 {
+ ifi.Interface = &net.Interface{}
+ }
+ if ifi.Type&attrIPAddr != 0 {
+ ifi.Addr = &net.IPAddr{}
+ }
+ attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU)
+ for b = b[4:]; len(b) > 0 && attrs != 0; {
+ var err error
+ switch {
+ case attrs&attrIfIndex != 0:
+ b, err = ifi.parseIfIndex(b)
+ attrs &^= attrIfIndex
+ case attrs&attrIPAddr != 0:
+ b, err = ifi.parseIPAddr(b)
+ attrs &^= attrIPAddr
+ case attrs&attrName != 0:
+ b, err = ifi.parseName(b)
+ attrs &^= attrName
+ case attrs&attrMTU != 0:
+ b, err = ifi.parseMTU(b)
+ attrs &^= attrMTU
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
+ ifi.Addr.Zone = ifi.Interface.Name
+ }
+ return ifi, nil
+}
diff --git a/icmp/ipv4.go b/icmp/ipv4.go
new file mode 100644
index 0000000..a252d73
--- /dev/null
+++ b/icmp/ipv4.go
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+package icmp
+
+import (
+ "net"
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/net/ipv4"
+)
+
+// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+var freebsdVersion uint32
+
+// ParseIPv4Header parses b as an IPv4 header of ICMP error message
+// invoking packet, which is contained in ICMP error message.
+func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
+ if len(b) < ipv4.HeaderLen {
+ return nil, errHeaderTooShort
+ }
+ hdrlen := int(b[0]&0x0f) << 2
+ if hdrlen > len(b) {
+ return nil, errBufferTooShort
+ }
+ h := &ipv4.Header{
+ Version: int(b[0] >> 4),
+ Len: hdrlen,
+ TOS: int(b[1]),
+ ID: int(b[4])<<8 | int(b[5]),
+ FragOff: int(b[6])<<8 | int(b[7]),
+ TTL: int(b[8]),
+ Protocol: int(b[9]),
+ Checksum: int(b[10])<<8 | int(b[11]),
+ Src: net.IPv4(b[12], b[13], b[14], b[15]),
+ Dst: net.IPv4(b[16], b[17], b[18], b[19]),
+ }
+ switch runtime.GOOS {
+ case "darwin":
+ // TODO(mikio): fix potential misaligned memory access
+ h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+ case "freebsd":
+ if freebsdVersion >= 1000000 {
+ h.TotalLen = int(b[2])<<8 | int(b[3])
+ } else {
+ // TODO(mikio): fix potential misaligned memory access
+ h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+ }
+ default:
+ h.TotalLen = int(b[2])<<8 | int(b[3])
+ }
+ h.Flags = ipv4.HeaderFlags(h.FragOff&0xe000) >> 13
+ h.FragOff = h.FragOff & 0x1fff
+ if hdrlen-ipv4.HeaderLen > 0 {
+ h.Options = make([]byte, hdrlen-ipv4.HeaderLen)
+ copy(h.Options, b[ipv4.HeaderLen:])
+ }
+ return h, nil
+}
diff --git a/icmp/ipv4_test.go b/icmp/ipv4_test.go
new file mode 100644
index 0000000..b05c697
--- /dev/null
+++ b/icmp/ipv4_test.go
@@ -0,0 +1,71 @@
+// Copyright 2014 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.
+
+package icmp
+
+import (
+ "net"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/ipv4"
+)
+
+var (
+ wireHeaderFromKernel = [ipv4.HeaderLen]byte{
+ 0x45, 0x01, 0xbe, 0xef,
+ 0xca, 0xfe, 0x45, 0xdc,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ wireHeaderFromTradBSDKernel = [ipv4.HeaderLen]byte{
+ 0x45, 0x01, 0xef, 0xbe,
+ 0xca, 0xfe, 0x45, 0xdc,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ // TODO(mikio): Add platform dependent wire header formats when
+ // we support new platforms.
+
+ testHeader = &ipv4.Header{
+ Version: ipv4.Version,
+ Len: ipv4.HeaderLen,
+ TOS: 1,
+ TotalLen: 0xbeef,
+ ID: 0xcafe,
+ Flags: ipv4.DontFragment,
+ FragOff: 1500,
+ TTL: 255,
+ Protocol: 1,
+ Checksum: 0xdead,
+ Src: net.IPv4(172, 16, 254, 254),
+ Dst: net.IPv4(192, 168, 0, 1),
+ }
+)
+
+func TestParseIPv4Header(t *testing.T) {
+ var wh []byte
+ switch runtime.GOOS {
+ case "darwin":
+ wh = wireHeaderFromTradBSDKernel[:]
+ case "freebsd":
+ if freebsdVersion >= 1000000 {
+ wh = wireHeaderFromKernel[:]
+ } else {
+ wh = wireHeaderFromTradBSDKernel[:]
+ }
+ default:
+ wh = wireHeaderFromKernel[:]
+ }
+ h, err := ParseIPv4Header(wh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(h, testHeader) {
+ t.Fatalf("got %#v; want %#v", h, testHeader)
+ }
+}
diff --git a/icmp/ipv6.go b/icmp/ipv6.go
new file mode 100644
index 0000000..fe4031a
--- /dev/null
+++ b/icmp/ipv6.go
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+package icmp
+
+import (
+ "net"
+
+ "golang.org/x/net/internal/iana"
+)
+
+const ipv6PseudoHeaderLen = 2*net.IPv6len + 8
+
+// IPv6PseudoHeader returns an IPv6 pseudo header for checkusm
+// calculation.
+func IPv6PseudoHeader(src, dst net.IP) []byte {
+ b := make([]byte, ipv6PseudoHeaderLen)
+ copy(b, src.To16())
+ copy(b[net.IPv6len:], dst.To16())
+ b[len(b)-1] = byte(iana.ProtocolIPv6ICMP)
+ return b
+}
diff --git a/icmp/listen_posix.go b/icmp/listen_posix.go
new file mode 100644
index 0000000..fa1653b
--- /dev/null
+++ b/icmp/listen_posix.go
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+
+package icmp
+
+import (
+ "net"
+ "os"
+ "runtime"
+ "syscall"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option
+
+// ListenPacket listens for incoming ICMP packets addressed to
+// address. See net.Dial for the syntax of address.
+//
+// For non-privileged datagram-oriented ICMP endpoints, network must
+// be "udp4" or "udp6". The endpoint allows to read, write a few
+// limited ICMP messages such as echo request and echo reply.
+// Currently only Darwin and Linux support this.
+//
+// Examples:
+// ListenPacket("udp4", "192.168.0.1")
+// ListenPacket("udp4", "0.0.0.0")
+// ListenPacket("udp6", "fe80::1%en0")
+// ListenPacket("udp6", "::")
+//
+// For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
+// followed by a colon and an ICMP protocol number or name.
+//
+// Examples:
+// ListenPacket("ip4:icmp", "192.168.0.1")
+// ListenPacket("ip4:1", "0.0.0.0")
+// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
+// ListenPacket("ip6:58", "::")
+func ListenPacket(network, address string) (*PacketConn, error) {
+ var family, proto int
+ switch network {
+ case "udp4":
+ family, proto = syscall.AF_INET, iana.ProtocolICMP
+ case "udp6":
+ family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP
+ default:
+ i := last(network, ':')
+ switch network[:i] {
+ case "ip4":
+ proto = iana.ProtocolICMP
+ case "ip6":
+ proto = iana.ProtocolIPv6ICMP
+ }
+ }
+ var cerr error
+ var c net.PacketConn
+ switch family {
+ case syscall.AF_INET, syscall.AF_INET6:
+ s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto)
+ if err != nil {
+ return nil, os.NewSyscallError("socket", err)
+ }
+ defer syscall.Close(s)
+ if runtime.GOOS == "darwin" && family == syscall.AF_INET {
+ if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil {
+ return nil, os.NewSyscallError("setsockopt", err)
+ }
+ }
+ sa, err := sockaddr(family, address)
+ if err != nil {
+ return nil, err
+ }
+ if err := syscall.Bind(s, sa); err != nil {
+ return nil, os.NewSyscallError("bind", err)
+ }
+ f := os.NewFile(uintptr(s), "datagram-oriented icmp")
+ defer f.Close()
+ c, cerr = net.FilePacketConn(f)
+ default:
+ c, cerr = net.ListenPacket(network, address)
+ }
+ if cerr != nil {
+ return nil, cerr
+ }
+ switch proto {
+ case iana.ProtocolICMP:
+ return &PacketConn{c: c, ipc: ipv4.NewPacketConn(c)}, nil
+ case iana.ProtocolIPv6ICMP:
+ return &PacketConn{c: c, ipc: ipv6.NewPacketConn(c)}, nil
+ default:
+ return &PacketConn{c: c}, nil
+ }
+}
diff --git a/icmp/listen_stub.go b/icmp/listen_stub.go
new file mode 100644
index 0000000..668728d
--- /dev/null
+++ b/icmp/listen_stub.go
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+
+// +build nacl plan9
+
+package icmp
+
+// ListenPacket listens for incoming ICMP packets addressed to
+// address. See net.Dial for the syntax of address.
+//
+// For non-privileged datagram-oriented ICMP endpoints, network must
+// be "udp4" or "udp6". The endpoint allows to read, write a few
+// limited ICMP messages such as echo request and echo reply.
+// Currently only Darwin and Linux support this.
+//
+// Examples:
+// ListenPacket("udp4", "192.168.0.1")
+// ListenPacket("udp4", "0.0.0.0")
+// ListenPacket("udp6", "fe80::1%en0")
+// ListenPacket("udp6", "::")
+//
+// For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
+// followed by a colon and an ICMP protocol number or name.
+//
+// Examples:
+// ListenPacket("ip4:icmp", "192.168.0.1")
+// ListenPacket("ip4:1", "0.0.0.0")
+// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
+// ListenPacket("ip6:58", "::")
+func ListenPacket(network, address string) (*PacketConn, error) {
+ return nil, errOpNoSupport
+}
diff --git a/icmp/message.go b/icmp/message.go
new file mode 100644
index 0000000..3f9ccb1
--- /dev/null
+++ b/icmp/message.go
@@ -0,0 +1,149 @@
+// Copyright 2012 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.
+
+// Package icmp provides basic functions for the manipulation of
+// messages used in the Internet Control Message Protocols,
+// ICMPv4 and ICMPv6.
+//
+// ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
+// Multi-part message support for ICMP is defined in RFC 4884.
+// ICMP extensions for MPLS are defined in RFC 4950.
+// ICMP extensions for interface and next-hop identification are
+// defined in RFC 5837.
+package icmp // import "golang.org/x/net/icmp"
+
+import (
+ "errors"
+ "net"
+ "syscall"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+var (
+ errMessageTooShort = errors.New("message too short")
+ errHeaderTooShort = errors.New("header too short")
+ errBufferTooShort = errors.New("buffer too short")
+ errOpNoSupport = errors.New("operation not supported")
+ errNoExtension = errors.New("no extension")
+ errInvalidExtension = errors.New("invalid extension")
+)
+
+func checksum(b []byte) uint16 {
+ csumcv := len(b) - 1 // checksum coverage
+ s := uint32(0)
+ for i := 0; i < csumcv; i += 2 {
+ s += uint32(b[i+1])<<8 | uint32(b[i])
+ }
+ if csumcv&1 == 0 {
+ s += uint32(b[csumcv])
+ }
+ s = s>>16 + s&0xffff
+ s = s + s>>16
+ return ^uint16(s)
+}
+
+// A Type represents an ICMP message type.
+type Type interface {
+ Protocol() int
+}
+
+// A Message represents an ICMP message.
+type Message struct {
+ Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
+ Code int // code
+ Checksum int // checksum
+ Body MessageBody // body
+}
+
+// Marshal returns the binary enconding of the ICMP message m.
+//
+// For an ICMPv4 message, the returned message always contains the
+// calculated checksum field.
+//
+// For an ICMPv6 message, the returned message contains the calculated
+// checksum field when psh is not nil, otherwise the kernel will
+// compute the checksum field during the message transmission.
+// When psh is not nil, it must be the pseudo header for IPv6.
+func (m *Message) Marshal(psh []byte) ([]byte, error) {
+ var mtype int
+ switch typ := m.Type.(type) {
+ case ipv4.ICMPType:
+ mtype = int(typ)
+ case ipv6.ICMPType:
+ mtype = int(typ)
+ default:
+ return nil, syscall.EINVAL
+ }
+ b := []byte{byte(mtype), byte(m.Code), 0, 0}
+ if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
+ b = append(psh, b...)
+ }
+ if m.Body != nil && m.Body.Len(m.Type.Protocol()) != 0 {
+ mb, err := m.Body.Marshal(m.Type.Protocol())
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, mb...)
+ }
+ if m.Type.Protocol() == iana.ProtocolIPv6ICMP {
+ if psh == nil { // cannot calculate checksum here
+ return b, nil
+ }
+ off, l := 2*net.IPv6len, len(b)-len(psh)
+ b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+ }
+ s := checksum(b)
+ // Place checksum back in header; using ^= avoids the
+ // assumption the checksum bytes are zero.
+ b[len(psh)+2] ^= byte(s)
+ b[len(psh)+3] ^= byte(s >> 8)
+ return b[len(psh):], nil
+}
+
+var parseFns = map[Type]func(int, []byte) (MessageBody, error){
+ ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
+ ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
+ ipv4.ICMPTypeParameterProblem: parseParamProb,
+
+ ipv4.ICMPTypeEcho: parseEcho,
+ ipv4.ICMPTypeEchoReply: parseEcho,
+
+ ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
+ ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
+ ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
+ ipv6.ICMPTypeParameterProblem: parseParamProb,
+
+ ipv6.ICMPTypeEchoRequest: parseEcho,
+ ipv6.ICMPTypeEchoReply: parseEcho,
+}
+
+// ParseMessage parses b as an ICMP message.
+// Proto must be either the ICMPv4 or ICMPv6 protocol number.
+func ParseMessage(proto int, b []byte) (*Message, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ var err error
+ m := &Message{Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
+ switch proto {
+ case iana.ProtocolICMP:
+ m.Type = ipv4.ICMPType(b[0])
+ case iana.ProtocolIPv6ICMP:
+ m.Type = ipv6.ICMPType(b[0])
+ default:
+ return nil, syscall.EINVAL
+ }
+ if fn, ok := parseFns[m.Type]; !ok {
+ m.Body, err = parseDefaultMessageBody(proto, b[4:])
+ } else {
+ m.Body, err = fn(proto, b[4:])
+ }
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
diff --git a/icmp/message_test.go b/icmp/message_test.go
new file mode 100644
index 0000000..5d2605f
--- /dev/null
+++ b/icmp/message_test.go
@@ -0,0 +1,134 @@
+// Copyright 2014 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.
+
+package icmp_test
+
+import (
+ "net"
+ "reflect"
+ "testing"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+var marshalAndParseMessageForIPv4Tests = []icmp.Message{
+ {
+ Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
+ Body: &icmp.DstUnreach{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
+ Body: &icmp.TimeExceeded{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv4.ICMPTypeParameterProblem, Code: 2,
+ Body: &icmp.ParamProb{
+ Pointer: 8,
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: 1, Seq: 2,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ },
+ {
+ Type: ipv4.ICMPTypePhoturis,
+ Body: &icmp.DefaultMessageBody{
+ Data: []byte{0x80, 0x40, 0x20, 0x10},
+ },
+ },
+}
+
+func TestMarshalAndParseMessageForIPv4(t *testing.T) {
+ for i, tt := range marshalAndParseMessageForIPv4Tests {
+ b, err := tt.Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m.Type != tt.Type || m.Code != tt.Code {
+ t.Errorf("#%v: got %v; want %v", i, m, &tt)
+ }
+ if !reflect.DeepEqual(m.Body, tt.Body) {
+ t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
+ }
+ }
+}
+
+var marshalAndParseMessageForIPv6Tests = []icmp.Message{
+ {
+ Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
+ Body: &icmp.DstUnreach{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv6.ICMPTypePacketTooBig, Code: 0,
+ Body: &icmp.PacketTooBig{
+ MTU: 1<<16 - 1,
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
+ Body: &icmp.TimeExceeded{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv6.ICMPTypeParameterProblem, Code: 2,
+ Body: &icmp.ParamProb{
+ Pointer: 8,
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ },
+ },
+ {
+ Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+ Body: &icmp.Echo{
+ ID: 1, Seq: 2,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ },
+ {
+ Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
+ Body: &icmp.DefaultMessageBody{
+ Data: []byte{0x80, 0x40, 0x20, 0x10},
+ },
+ },
+}
+
+func TestMarshalAndParseMessageForIPv6(t *testing.T) {
+ pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
+ for i, tt := range marshalAndParseMessageForIPv6Tests {
+ for _, psh := range [][]byte{pshicmp, nil} {
+ b, err := tt.Marshal(psh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m.Type != tt.Type || m.Code != tt.Code {
+ t.Errorf("#%v: got %v; want %v", i, m, &tt)
+ }
+ if !reflect.DeepEqual(m.Body, tt.Body) {
+ t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
+ }
+ }
+ }
+}
diff --git a/icmp/messagebody.go b/icmp/messagebody.go
new file mode 100644
index 0000000..d314480
--- /dev/null
+++ b/icmp/messagebody.go
@@ -0,0 +1,41 @@
+// Copyright 2012 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.
+
+package icmp
+
+// A MessageBody represents an ICMP message body.
+type MessageBody interface {
+ // Len returns the length of ICMP message body.
+ // Proto must be either the ICMPv4 or ICMPv6 protocol number.
+ Len(proto int) int
+
+ // Marshal returns the binary enconding of ICMP message body.
+ // Proto must be either the ICMPv4 or ICMPv6 protocol number.
+ Marshal(proto int) ([]byte, error)
+}
+
+// A DefaultMessageBody represents the default message body.
+type DefaultMessageBody struct {
+ Data []byte // data
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *DefaultMessageBody) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ return len(p.Data)
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *DefaultMessageBody) Marshal(proto int) ([]byte, error) {
+ return p.Data, nil
+}
+
+// parseDefaultMessageBody parses b as an ICMP message body.
+func parseDefaultMessageBody(proto int, b []byte) (MessageBody, error) {
+ p := &DefaultMessageBody{Data: make([]byte, len(b))}
+ copy(p.Data, b)
+ return p, nil
+}
diff --git a/icmp/mpls.go b/icmp/mpls.go
new file mode 100644
index 0000000..31bcfe8
--- /dev/null
+++ b/icmp/mpls.go
@@ -0,0 +1,75 @@
+// Copyright 2015 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.
+
+package icmp
+
+// A MPLSLabel represents a MPLS label stack entry.
+type MPLSLabel struct {
+ Label int // label value
+ TC int // traffic class; formerly experimental use
+ S bool // bottom of stack
+ TTL int // time to live
+}
+
+const (
+ classMPLSLabelStack = 1
+ typeIncomingMPLSLabelStack = 1
+)
+
+// A MPLSLabelStack represents a MPLS label stack.
+type MPLSLabelStack struct {
+ Class int // extension object class number
+ Type int // extension object sub-type
+ Labels []MPLSLabel
+}
+
+// Len implements the Len method of Extension interface.
+func (ls *MPLSLabelStack) Len(proto int) int {
+ return 4 + (4 * len(ls.Labels))
+}
+
+// Marshal implements the Marshal method of Extension interface.
+func (ls *MPLSLabelStack) Marshal(proto int) ([]byte, error) {
+ b := make([]byte, ls.Len(proto))
+ if err := ls.marshal(proto, b); err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+func (ls *MPLSLabelStack) marshal(proto int, b []byte) error {
+ l := ls.Len(proto)
+ b[0], b[1] = byte(l>>8), byte(l)
+ b[2], b[3] = classMPLSLabelStack, typeIncomingMPLSLabelStack
+ off := 4
+ for _, ll := range ls.Labels {
+ b[off], b[off+1], b[off+2] = byte(ll.Label>>12), byte(ll.Label>>4&0xff), byte(ll.Label<<4&0xf0)
+ b[off+2] |= byte(ll.TC << 1 & 0x0e)
+ if ll.S {
+ b[off+2] |= 0x1
+ }
+ b[off+3] = byte(ll.TTL)
+ off += 4
+ }
+ return nil
+}
+
+func parseMPLSLabelStack(b []byte) (Extension, error) {
+ ls := &MPLSLabelStack{
+ Class: int(b[2]),
+ Type: int(b[3]),
+ }
+ for b = b[4:]; len(b) >= 4; b = b[4:] {
+ ll := MPLSLabel{
+ Label: int(b[0])<<12 | int(b[1])<<4 | int(b[2])>>4,
+ TC: int(b[2]&0x0e) >> 1,
+ TTL: int(b[3]),
+ }
+ if b[2]&0x1 != 0 {
+ ll.S = true
+ }
+ ls.Labels = append(ls.Labels, ll)
+ }
+ return ls, nil
+}
diff --git a/icmp/multipart.go b/icmp/multipart.go
new file mode 100644
index 0000000..54ac8bc
--- /dev/null
+++ b/icmp/multipart.go
@@ -0,0 +1,109 @@
+// Copyright 2015 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.
+
+package icmp
+
+import "golang.org/x/net/internal/iana"
+
+// multipartMessageBodyDataLen takes b as an original datagram and
+// exts as extensions, and returns a required length for message body
+// and a required length for a padded original datagram in wire
+// format.
+func multipartMessageBodyDataLen(proto int, b []byte, exts []Extension) (bodyLen, dataLen int) {
+ for _, ext := range exts {
+ bodyLen += ext.Len(proto)
+ }
+ if bodyLen > 0 {
+ dataLen = multipartMessageOrigDatagramLen(proto, b)
+ bodyLen += 4 // length of extension header
+ } else {
+ dataLen = len(b)
+ }
+ bodyLen += dataLen
+ return bodyLen, dataLen
+}
+
+// multipartMessageOrigDatagramLen takes b as an original datagram,
+// and returns a required length for a padded orignal datagram in wire
+// format.
+func multipartMessageOrigDatagramLen(proto int, b []byte) int {
+ roundup := func(b []byte, align int) int {
+ // According to RFC 4884, the padded original datagram
+ // field must contain at least 128 octets.
+ if len(b) < 128 {
+ return 128
+ }
+ r := len(b)
+ return (r + align) &^ (align - 1)
+ }
+ switch proto {
+ case iana.ProtocolICMP:
+ return roundup(b, 4)
+ case iana.ProtocolIPv6ICMP:
+ return roundup(b, 8)
+ default:
+ return len(b)
+ }
+}
+
+// marshalMultipartMessageBody takes data as an original datagram and
+// exts as extesnsions, and returns a binary encoding of message body.
+// It can be used for non-multipart message bodies when exts is nil.
+func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]byte, error) {
+ bodyLen, dataLen := multipartMessageBodyDataLen(proto, data, exts)
+ b := make([]byte, 4+bodyLen)
+ copy(b[4:], data)
+ off := dataLen + 4
+ if len(exts) > 0 {
+ b[dataLen+4] = byte(extensionVersion << 4)
+ off += 4 // length of object header
+ for _, ext := range exts {
+ switch ext := ext.(type) {
+ case *MPLSLabelStack:
+ if err := ext.marshal(proto, b[off:]); err != nil {
+ return nil, err
+ }
+ off += ext.Len(proto)
+ case *InterfaceInfo:
+ attrs, l := ext.attrsAndLen(proto)
+ if err := ext.marshal(proto, b[off:], attrs, l); err != nil {
+ return nil, err
+ }
+ off += ext.Len(proto)
+ }
+ }
+ s := checksum(b[dataLen+4:])
+ b[dataLen+4+2] ^= byte(s)
+ b[dataLen+4+3] ^= byte(s >> 8)
+ switch proto {
+ case iana.ProtocolICMP:
+ b[1] = byte(dataLen / 4)
+ case iana.ProtocolIPv6ICMP:
+ b[0] = byte(dataLen / 8)
+ }
+ }
+ return b, nil
+}
+
+// parseMultipartMessageBody parses b as either a non-multipart
+// message body or a multipart message body.
+func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) {
+ var l int
+ switch proto {
+ case iana.ProtocolICMP:
+ l = 4 * int(b[1])
+ case iana.ProtocolIPv6ICMP:
+ l = 8 * int(b[0])
+ }
+ if len(b) == 4 {
+ return nil, nil, nil
+ }
+ exts, l, err := parseExtensions(b[4:], l)
+ if err != nil {
+ l = len(b) - 4
+ }
+ data := make([]byte, l)
+ copy(data, b[4:])
+ return data, exts, nil
+}
diff --git a/icmp/multipart_test.go b/icmp/multipart_test.go
new file mode 100644
index 0000000..9248e47
--- /dev/null
+++ b/icmp/multipart_test.go
@@ -0,0 +1,315 @@
+// Copyright 2015 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.
+
+package icmp_test
+
+import (
+ "fmt"
+ "net"
+ "reflect"
+ "testing"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
+ {
+ Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
+ Body: &icmp.DstUnreach{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ Extensions: []icmp.Extension{
+ &icmp.MPLSLabelStack{
+ Class: 1,
+ Type: 1,
+ Labels: []icmp.MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.IPv4(192, 168, 0, 1).To4(),
+ },
+ },
+ },
+ },
+ },
+ {
+ Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
+ Body: &icmp.TimeExceeded{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ Extensions: []icmp.Extension{
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.IPv4(192, 168, 0, 1).To4(),
+ },
+ },
+ &icmp.MPLSLabelStack{
+ Class: 1,
+ Type: 1,
+ Labels: []icmp.MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ Type: ipv4.ICMPTypeParameterProblem, Code: 2,
+ Body: &icmp.ParamProb{
+ Pointer: 8,
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ Extensions: []icmp.Extension{
+ &icmp.MPLSLabelStack{
+ Class: 1,
+ Type: 1,
+ Labels: []icmp.MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.IPv4(192, 168, 0, 1).To4(),
+ },
+ },
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x2f,
+ Interface: &net.Interface{
+ Index: 16,
+ Name: "en102",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.IPv4(192, 168, 0, 2).To4(),
+ },
+ },
+ },
+ },
+ },
+}
+
+func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
+ for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
+ b, err := tt.Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if b[5] != 32 {
+ t.Errorf("#%v: got %v; want 32", i, b[5])
+ }
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m.Type != tt.Type || m.Code != tt.Code {
+ t.Errorf("#%v: got %v; want %v", i, m, &tt)
+ }
+ switch m.Type {
+ case ipv4.ICMPTypeDestinationUnreachable:
+ got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
+ if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+ t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+ }
+ if len(got.Data) != 128 {
+ t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+ }
+ case ipv4.ICMPTypeTimeExceeded:
+ got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
+ if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+ t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+ }
+ if len(got.Data) != 128 {
+ t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+ }
+ case ipv4.ICMPTypeParameterProblem:
+ got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb)
+ if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+ t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+ }
+ if len(got.Data) != 128 {
+ t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+ }
+ }
+ }
+}
+
+var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
+ {
+ Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
+ Body: &icmp.DstUnreach{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ Extensions: []icmp.Extension{
+ &icmp.MPLSLabelStack{
+ Class: 1,
+ Type: 1,
+ Labels: []icmp.MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.ParseIP("fe80::1"),
+ Zone: "en101",
+ },
+ },
+ },
+ },
+ },
+ {
+ Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
+ Body: &icmp.TimeExceeded{
+ Data: []byte("ERROR-INVOKING-PACKET"),
+ Extensions: []icmp.Extension{
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x0f,
+ Interface: &net.Interface{
+ Index: 15,
+ Name: "en101",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.ParseIP("fe80::1"),
+ Zone: "en101",
+ },
+ },
+ &icmp.MPLSLabelStack{
+ Class: 1,
+ Type: 1,
+ Labels: []icmp.MPLSLabel{
+ {
+ Label: 16014,
+ TC: 0x4,
+ S: true,
+ TTL: 255,
+ },
+ },
+ },
+ &icmp.InterfaceInfo{
+ Class: 2,
+ Type: 0x2f,
+ Interface: &net.Interface{
+ Index: 16,
+ Name: "en102",
+ MTU: 8192,
+ },
+ Addr: &net.IPAddr{
+ IP: net.ParseIP("fe80::1"),
+ Zone: "en102",
+ },
+ },
+ },
+ },
+ },
+}
+
+func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) {
+ pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
+ for i, tt := range marshalAndParseMultipartMessageForIPv6Tests {
+ for _, psh := range [][]byte{pshicmp, nil} {
+ b, err := tt.Marshal(psh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if b[4] != 16 {
+ t.Errorf("#%v: got %v; want 16", i, b[4])
+ }
+ m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m.Type != tt.Type || m.Code != tt.Code {
+ t.Errorf("#%v: got %v; want %v", i, m, &tt)
+ }
+ switch m.Type {
+ case ipv6.ICMPTypeDestinationUnreachable:
+ got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
+ if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+ t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+ }
+ if len(got.Data) != 128 {
+ t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+ }
+ case ipv6.ICMPTypeTimeExceeded:
+ got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
+ if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+ t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+ }
+ if len(got.Data) != 128 {
+ t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+ }
+ }
+ }
+ }
+}
+
+func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string {
+ var s string
+ for j, got := range gotExts {
+ switch got := got.(type) {
+ case *icmp.MPLSLabelStack:
+ want := wantExts[j].(*icmp.MPLSLabelStack)
+ if !reflect.DeepEqual(got, want) {
+ s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want)
+ }
+ case *icmp.InterfaceInfo:
+ want := wantExts[j].(*icmp.InterfaceInfo)
+ if !reflect.DeepEqual(got, want) {
+ s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
+ }
+ }
+ }
+ return s[:len(s)-1]
+}
diff --git a/icmp/packettoobig.go b/icmp/packettoobig.go
new file mode 100644
index 0000000..91d289b
--- /dev/null
+++ b/icmp/packettoobig.go
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+package icmp
+
+// A PacketTooBig represents an ICMP packet too big message body.
+type PacketTooBig struct {
+ MTU int // maximum transmission unit of the nexthop link
+ Data []byte // data, known as original datagram field
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *PacketTooBig) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ return 4 + len(p.Data)
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *PacketTooBig) Marshal(proto int) ([]byte, error) {
+ b := make([]byte, 4+len(p.Data))
+ b[0], b[1], b[2], b[3] = byte(p.MTU>>24), byte(p.MTU>>16), byte(p.MTU>>8), byte(p.MTU)
+ copy(b[4:], p.Data)
+ return b, nil
+}
+
+// parsePacketTooBig parses b as an ICMP packet too big message body.
+func parsePacketTooBig(proto int, b []byte) (MessageBody, error) {
+ bodyLen := len(b)
+ if bodyLen < 4 {
+ return nil, errMessageTooShort
+ }
+ p := &PacketTooBig{MTU: int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])}
+ if bodyLen > 4 {
+ p.Data = make([]byte, bodyLen-4)
+ copy(p.Data, b[4:])
+ }
+ return p, nil
+}
diff --git a/icmp/paramprob.go b/icmp/paramprob.go
new file mode 100644
index 0000000..f200a7c
--- /dev/null
+++ b/icmp/paramprob.go
@@ -0,0 +1,60 @@
+// Copyright 2014 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.
+
+package icmp
+
+import "golang.org/x/net/internal/iana"
+
+// A ParamProb represents an ICMP parameter problem message body.
+type ParamProb struct {
+ Pointer uintptr // offset within the data where the error was detected
+ Data []byte // data, known as original datagram field
+ Extensions []Extension // extensions
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *ParamProb) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
+ return l
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *ParamProb) Marshal(proto int) ([]byte, error) {
+ if proto == iana.ProtocolIPv6ICMP {
+ b := make([]byte, 4+p.Len(proto))
+ b[0], b[1], b[2], b[3] = byte(p.Pointer>>24), byte(p.Pointer>>16), byte(p.Pointer>>8), byte(p.Pointer)
+ copy(b[4:], p.Data)
+ return b, nil
+ }
+ b, err := marshalMultipartMessageBody(proto, p.Data, p.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ b[0] = byte(p.Pointer)
+ return b, nil
+}
+
+// parseParamProb parses b as an ICMP parameter problem message body.
+func parseParamProb(proto int, b []byte) (MessageBody, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ p := &ParamProb{}
+ if proto == iana.ProtocolIPv6ICMP {
+ p.Pointer = uintptr(b[0])<<24 | uintptr(b[1])<<16 | uintptr(b[2])<<8 | uintptr(b[3])
+ p.Data = make([]byte, len(b)-4)
+ copy(p.Data, b[4:])
+ return p, nil
+ }
+ p.Pointer = uintptr(b[0])
+ var err error
+ p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
diff --git a/icmp/ping_test.go b/icmp/ping_test.go
new file mode 100644
index 0000000..4ec2692
--- /dev/null
+++ b/icmp/ping_test.go
@@ -0,0 +1,166 @@
+// Copyright 2014 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.
+
+package icmp_test
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
+ const host = "www.google.com"
+ ips, err := net.LookupIP(host)
+ if err != nil {
+ return nil, err
+ }
+ netaddr := func(ip net.IP) (net.Addr, error) {
+ switch c.LocalAddr().(type) {
+ case *net.UDPAddr:
+ return &net.UDPAddr{IP: ip}, nil
+ case *net.IPAddr:
+ return &net.IPAddr{IP: ip}, nil
+ default:
+ return nil, errors.New("neither UDPAddr nor IPAddr")
+ }
+ }
+ for _, ip := range ips {
+ switch protocol {
+ case iana.ProtocolICMP:
+ if ip.To4() != nil {
+ return netaddr(ip)
+ }
+ case iana.ProtocolIPv6ICMP:
+ if ip.To16() != nil && ip.To4() == nil {
+ return netaddr(ip)
+ }
+ }
+ }
+ return nil, errors.New("no A or AAAA record")
+}
+
+type pingTest struct {
+ network, address string
+ protocol int
+ mtype icmp.Type
+}
+
+var nonPrivilegedPingTests = []pingTest{
+ {"udp4", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
+
+ {"udp6", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
+}
+
+func TestNonPrivilegedPing(t *testing.T) {
+ if testing.Short() {
+ t.Skip("avoid external network")
+ }
+ switch runtime.GOOS {
+ case "darwin":
+ case "linux":
+ t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
+ default:
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ for i, tt := range nonPrivilegedPingTests {
+ if err := doPing(tt, i); err != nil {
+ t.Error(err)
+ }
+ }
+}
+
+var privilegedPingTests = []pingTest{
+ {"ip4:icmp", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
+
+ {"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
+}
+
+func TestPrivilegedPing(t *testing.T) {
+ if testing.Short() {
+ t.Skip("avoid external network")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ for i, tt := range privilegedPingTests {
+ if err := doPing(tt, i); err != nil {
+ t.Error(err)
+ }
+ }
+}
+
+func doPing(tt pingTest, seq int) error {
+ c, err := icmp.ListenPacket(tt.network, tt.address)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ dst, err := googleAddr(c, tt.protocol)
+ if err != nil {
+ return err
+ }
+
+ if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
+ var f ipv6.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv6.ICMPTypeDestinationUnreachable)
+ f.Accept(ipv6.ICMPTypePacketTooBig)
+ f.Accept(ipv6.ICMPTypeTimeExceeded)
+ f.Accept(ipv6.ICMPTypeParameterProblem)
+ f.Accept(ipv6.ICMPTypeEchoReply)
+ if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
+ return err
+ }
+ }
+
+ wm := icmp.Message{
+ Type: tt.mtype, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq),
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }
+ wb, err := wm.Marshal(nil)
+ if err != nil {
+ return err
+ }
+ if n, err := c.WriteTo(wb, dst); err != nil {
+ return err
+ } else if n != len(wb) {
+ return fmt.Errorf("got %v; want %v", n, len(wb))
+ }
+
+ rb := make([]byte, 1500)
+ if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
+ return err
+ }
+ n, peer, err := c.ReadFrom(rb)
+ if err != nil {
+ return err
+ }
+ rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
+ if err != nil {
+ return err
+ }
+ switch rm.Type {
+ case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
+ return nil
+ default:
+ return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
+ }
+}
diff --git a/icmp/sys_freebsd.go b/icmp/sys_freebsd.go
new file mode 100644
index 0000000..c75f3dd
--- /dev/null
+++ b/icmp/sys_freebsd.go
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+package icmp
+
+import "syscall"
+
+func init() {
+ freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate")
+}
diff --git a/icmp/timeexceeded.go b/icmp/timeexceeded.go
new file mode 100644
index 0000000..18628c8
--- /dev/null
+++ b/icmp/timeexceeded.go
@@ -0,0 +1,39 @@
+// Copyright 2014 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.
+
+package icmp
+
+// A TimeExceeded represents an ICMP time exceeded message body.
+type TimeExceeded struct {
+ Data []byte // data, known as original datagram field
+ Extensions []Extension // extensions
+}
+
+// Len implements the Len method of MessageBody interface.
+func (p *TimeExceeded) Len(proto int) int {
+ if p == nil {
+ return 0
+ }
+ l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
+ return l
+}
+
+// Marshal implements the Marshal method of MessageBody interface.
+func (p *TimeExceeded) Marshal(proto int) ([]byte, error) {
+ return marshalMultipartMessageBody(proto, p.Data, p.Extensions)
+}
+
+// parseTimeExceeded parses b as an ICMP time exceeded message body.
+func parseTimeExceeded(proto int, b []byte) (MessageBody, error) {
+ if len(b) < 4 {
+ return nil, errMessageTooShort
+ }
+ p := &TimeExceeded{}
+ var err error
+ p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
diff --git a/idna/idna.go b/idna/idna.go
new file mode 100644
index 0000000..3daa897
--- /dev/null
+++ b/idna/idna.go
@@ -0,0 +1,68 @@
+// Copyright 2012 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.
+
+// Package idna implements IDNA2008 (Internationalized Domain Names for
+// Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and
+// RFC 5894.
+package idna // import "golang.org/x/net/idna"
+
+import (
+ "strings"
+ "unicode/utf8"
+)
+
+// TODO(nigeltao): specify when errors occur. For example, is ToASCII(".") or
+// ToASCII("foo\x00") an error? See also http://www.unicode.org/faq/idn.html#11
+
+// acePrefix is the ASCII Compatible Encoding prefix.
+const acePrefix = "xn--"
+
+// ToASCII converts a domain or domain label to its ASCII form. For example,
+// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
+// ToASCII("golang") is "golang".
+func ToASCII(s string) (string, error) {
+ if ascii(s) {
+ return s, nil
+ }
+ labels := strings.Split(s, ".")
+ for i, label := range labels {
+ if !ascii(label) {
+ a, err := encode(acePrefix, label)
+ if err != nil {
+ return "", err
+ }
+ labels[i] = a
+ }
+ }
+ return strings.Join(labels, "."), nil
+}
+
+// ToUnicode converts a domain or domain label to its Unicode form. For example,
+// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
+// ToUnicode("golang") is "golang".
+func ToUnicode(s string) (string, error) {
+ if !strings.Contains(s, acePrefix) {
+ return s, nil
+ }
+ labels := strings.Split(s, ".")
+ for i, label := range labels {
+ if strings.HasPrefix(label, acePrefix) {
+ u, err := decode(label[len(acePrefix):])
+ if err != nil {
+ return "", err
+ }
+ labels[i] = u
+ }
+ }
+ return strings.Join(labels, "."), nil
+}
+
+func ascii(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if s[i] >= utf8.RuneSelf {
+ return false
+ }
+ }
+ return true
+}
diff --git a/idna/idna_test.go b/idna/idna_test.go
new file mode 100644
index 0000000..b1bc6fa
--- /dev/null
+++ b/idna/idna_test.go
@@ -0,0 +1,43 @@
+// Copyright 2012 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.
+
+package idna
+
+import (
+ "testing"
+)
+
+var idnaTestCases = [...]struct {
+ ascii, unicode string
+}{
+ // Labels.
+ {"books", "books"},
+ {"xn--bcher-kva", "bücher"},
+
+ // Domains.
+ {"foo--xn--bar.org", "foo--xn--bar.org"},
+ {"golang.org", "golang.org"},
+ {"example.xn--p1ai", "example.рф"},
+ {"xn--czrw28b.tw", "商業.tw"},
+ {"www.xn--mller-kva.de", "www.müller.de"},
+}
+
+func TestIDNA(t *testing.T) {
+ for _, tc := range idnaTestCases {
+ if a, err := ToASCII(tc.unicode); err != nil {
+ t.Errorf("ToASCII(%q): %v", tc.unicode, err)
+ } else if a != tc.ascii {
+ t.Errorf("ToASCII(%q): got %q, want %q", tc.unicode, a, tc.ascii)
+ }
+
+ if u, err := ToUnicode(tc.ascii); err != nil {
+ t.Errorf("ToUnicode(%q): %v", tc.ascii, err)
+ } else if u != tc.unicode {
+ t.Errorf("ToUnicode(%q): got %q, want %q", tc.ascii, u, tc.unicode)
+ }
+ }
+}
+
+// TODO(nigeltao): test errors, once we've specified when ToASCII and ToUnicode
+// return errors.
diff --git a/idna/punycode.go b/idna/punycode.go
new file mode 100644
index 0000000..92e733f
--- /dev/null
+++ b/idna/punycode.go
@@ -0,0 +1,200 @@
+// Copyright 2012 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.
+
+package idna
+
+// This file implements the Punycode algorithm from RFC 3492.
+
+import (
+ "fmt"
+ "math"
+ "strings"
+ "unicode/utf8"
+)
+
+// These parameter values are specified in section 5.
+//
+// All computation is done with int32s, so that overflow behavior is identical
+// regardless of whether int is 32-bit or 64-bit.
+const (
+ base int32 = 36
+ damp int32 = 700
+ initialBias int32 = 72
+ initialN int32 = 128
+ skew int32 = 38
+ tmax int32 = 26
+ tmin int32 = 1
+)
+
+// decode decodes a string as specified in section 6.2.
+func decode(encoded string) (string, error) {
+ if encoded == "" {
+ return "", nil
+ }
+ pos := 1 + strings.LastIndex(encoded, "-")
+ if pos == 1 {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ if pos == len(encoded) {
+ return encoded[:len(encoded)-1], nil
+ }
+ output := make([]rune, 0, len(encoded))
+ if pos != 0 {
+ for _, r := range encoded[:pos-1] {
+ output = append(output, r)
+ }
+ }
+ i, n, bias := int32(0), initialN, initialBias
+ for pos < len(encoded) {
+ oldI, w := i, int32(1)
+ for k := base; ; k += base {
+ if pos == len(encoded) {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ digit, ok := decodeDigit(encoded[pos])
+ if !ok {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ pos++
+ i += digit * w
+ if i < 0 {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ t := k - bias
+ if t < tmin {
+ t = tmin
+ } else if t > tmax {
+ t = tmax
+ }
+ if digit < t {
+ break
+ }
+ w *= base - t
+ if w >= math.MaxInt32/base {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ }
+ x := int32(len(output) + 1)
+ bias = adapt(i-oldI, x, oldI == 0)
+ n += i / x
+ i %= x
+ if n > utf8.MaxRune || len(output) >= 1024 {
+ return "", fmt.Errorf("idna: invalid label %q", encoded)
+ }
+ output = append(output, 0)
+ copy(output[i+1:], output[i:])
+ output[i] = n
+ i++
+ }
+ return string(output), nil
+}
+
+// encode encodes a string as specified in section 6.3 and prepends prefix to
+// the result.
+//
+// The "while h < length(input)" line in the specification becomes "for
+// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
+func encode(prefix, s string) (string, error) {
+ output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
+ copy(output, prefix)
+ delta, n, bias := int32(0), initialN, initialBias
+ b, remaining := int32(0), int32(0)
+ for _, r := range s {
+ if r < 0x80 {
+ b++
+ output = append(output, byte(r))
+ } else {
+ remaining++
+ }
+ }
+ h := b
+ if b > 0 {
+ output = append(output, '-')
+ }
+ for remaining != 0 {
+ m := int32(0x7fffffff)
+ for _, r := range s {
+ if m > r && r >= n {
+ m = r
+ }
+ }
+ delta += (m - n) * (h + 1)
+ if delta < 0 {
+ return "", fmt.Errorf("idna: invalid label %q", s)
+ }
+ n = m
+ for _, r := range s {
+ if r < n {
+ delta++
+ if delta < 0 {
+ return "", fmt.Errorf("idna: invalid label %q", s)
+ }
+ continue
+ }
+ if r > n {
+ continue
+ }
+ q := delta
+ for k := base; ; k += base {
+ t := k - bias
+ if t < tmin {
+ t = tmin
+ } else if t > tmax {
+ t = tmax
+ }
+ if q < t {
+ break
+ }
+ output = append(output, encodeDigit(t+(q-t)%(base-t)))
+ q = (q - t) / (base - t)
+ }
+ output = append(output, encodeDigit(q))
+ bias = adapt(delta, h+1, h == b)
+ delta = 0
+ h++
+ remaining--
+ }
+ delta++
+ n++
+ }
+ return string(output), nil
+}
+
+func decodeDigit(x byte) (digit int32, ok bool) {
+ switch {
+ case '0' <= x && x <= '9':
+ return int32(x - ('0' - 26)), true
+ case 'A' <= x && x <= 'Z':
+ return int32(x - 'A'), true
+ case 'a' <= x && x <= 'z':
+ return int32(x - 'a'), true
+ }
+ return 0, false
+}
+
+func encodeDigit(digit int32) byte {
+ switch {
+ case 0 <= digit && digit < 26:
+ return byte(digit + 'a')
+ case 26 <= digit && digit < 36:
+ return byte(digit + ('0' - 26))
+ }
+ panic("idna: internal error in punycode encoding")
+}
+
+// adapt is the bias adaptation function specified in section 6.1.
+func adapt(delta, numPoints int32, firstTime bool) int32 {
+ if firstTime {
+ delta /= damp
+ } else {
+ delta /= 2
+ }
+ delta += delta / numPoints
+ k := int32(0)
+ for delta > ((base-tmin)*tmax)/2 {
+ delta /= base - tmin
+ k += base
+ }
+ return k + (base-tmin+1)*delta/(delta+skew)
+}
diff --git a/idna/punycode_test.go b/idna/punycode_test.go
new file mode 100644
index 0000000..bfec81d
--- /dev/null
+++ b/idna/punycode_test.go
@@ -0,0 +1,198 @@
+// Copyright 2012 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.
+
+package idna
+
+import (
+ "strings"
+ "testing"
+)
+
+var punycodeTestCases = [...]struct {
+ s, encoded string
+}{
+ {"", ""},
+ {"-", "--"},
+ {"-a", "-a-"},
+ {"-a-", "-a--"},
+ {"a", "a-"},
+ {"a-", "a--"},
+ {"a-b", "a-b-"},
+ {"books", "books-"},
+ {"bücher", "bcher-kva"},
+ {"Hello世界", "Hello-ck1hg65u"},
+ {"ü", "tda"},
+ {"üý", "tdac"},
+
+ // The test cases below come from RFC 3492 section 7.1 with Errata 3026.
+ {
+ // (A) Arabic (Egyptian).
+ "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" +
+ "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
+ "egbpdaj6bu4bxfgehfvwxn",
+ },
+ {
+ // (B) Chinese (simplified).
+ "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587",
+ "ihqwcrb4cv8a8dqg056pqjye",
+ },
+ {
+ // (C) Chinese (traditional).
+ "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
+ "ihqwctvzc91f659drss3x8bo0yb",
+ },
+ {
+ // (D) Czech.
+ "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" +
+ "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" +
+ "\u0065\u0073\u006B\u0079",
+ "Proprostnemluvesky-uyb24dma41a",
+ },
+ {
+ // (E) Hebrew.
+ "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" +
+ "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" +
+ "\u05D1\u05E8\u05D9\u05EA",
+ "4dbcagdahymbxekheh6e0a7fei0b",
+ },
+ {
+ // (F) Hindi (Devanagari).
+ "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" +
+ "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" +
+ "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" +
+ "\u0939\u0948\u0902",
+ "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd",
+ },
+ {
+ // (G) Japanese (kanji and hiragana).
+ "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" +
+ "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
+ "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa",
+ },
+ {
+ // (H) Korean (Hangul syllables).
+ "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" +
+ "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" +
+ "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
+ "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" +
+ "psd879ccm6fea98c",
+ },
+ {
+ // (I) Russian (Cyrillic).
+ "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" +
+ "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" +
+ "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" +
+ "\u0438",
+ "b1abfaaepdrnnbgefbadotcwatmq2g4l",
+ },
+ {
+ // (J) Spanish.
+ "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" +
+ "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" +
+ "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" +
+ "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" +
+ "\u0061\u00F1\u006F\u006C",
+ "PorqunopuedensimplementehablarenEspaol-fmd56a",
+ },
+ {
+ // (K) Vietnamese.
+ "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" +
+ "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" +
+ "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" +
+ "\u0056\u0069\u1EC7\u0074",
+ "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g",
+ },
+ {
+ // (L) 3<nen>B<gumi><kinpachi><sensei>.
+ "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F",
+ "3B-ww4c5e180e575a65lsy2b",
+ },
+ {
+ // (M) <amuro><namie>-with-SUPER-MONKEYS.
+ "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" +
+ "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" +
+ "\u004F\u004E\u004B\u0045\u0059\u0053",
+ "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
+ },
+ {
+ // (N) Hello-Another-Way-<sorezore><no><basho>.
+ "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" +
+ "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" +
+ "\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
+ "Hello-Another-Way--fc4qua05auwb3674vfr0b",
+ },
+ {
+ // (O) <hitotsu><yane><no><shita>2.
+ "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032",
+ "2-u9tlzr9756bt3uc0v",
+ },
+ {
+ // (P) Maji<de>Koi<suru>5<byou><mae>
+ "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" +
+ "\u308B\u0035\u79D2\u524D",
+ "MajiKoi5-783gue6qz075azm5e",
+ },
+ {
+ // (Q) <pafii>de<runba>
+ "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0",
+ "de-jg4avhby1noc0d",
+ },
+ {
+ // (R) <sono><supiido><de>
+ "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
+ "d9juau41awczczp",
+ },
+ {
+ // (S) -> $1.00 <-
+ "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" +
+ "\u003C\u002D",
+ "-> $1.00 <--",
+ },
+}
+
+func TestPunycode(t *testing.T) {
+ for _, tc := range punycodeTestCases {
+ if got, err := decode(tc.encoded); err != nil {
+ t.Errorf("decode(%q): %v", tc.encoded, err)
+ } else if got != tc.s {
+ t.Errorf("decode(%q): got %q, want %q", tc.encoded, got, tc.s)
+ }
+
+ if got, err := encode("", tc.s); err != nil {
+ t.Errorf(`encode("", %q): %v`, tc.s, err)
+ } else if got != tc.encoded {
+ t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded)
+ }
+ }
+}
+
+var punycodeErrorTestCases = [...]string{
+ "decode -", // A sole '-' is invalid.
+ "decode foo\x00bar", // '\x00' is not in [0-9A-Za-z].
+ "decode foo#bar", // '#' is not in [0-9A-Za-z].
+ "decode foo\u00A3bar", // '\u00A3' is not in [0-9A-Za-z].
+ "decode 9", // "9a" decodes to codepoint \u00A3; "9" is truncated.
+ "decode 99999a", // "99999a" decodes to codepoint \U0048A3C1, which is > \U0010FFFF.
+ "decode 9999999999a", // "9999999999a" overflows the int32 calculation.
+
+ "encode " + strings.Repeat("x", 65536) + "\uff00", // int32 overflow.
+}
+
+func TestPunycodeErrors(t *testing.T) {
+ for _, tc := range punycodeErrorTestCases {
+ var err error
+ switch {
+ case strings.HasPrefix(tc, "decode "):
+ _, err = decode(tc[7:])
+ case strings.HasPrefix(tc, "encode "):
+ _, err = encode("", tc[7:])
+ }
+ if err == nil {
+ if len(tc) > 256 {
+ tc = tc[:100] + "..." + tc[len(tc)-100:]
+ }
+ t.Errorf("no error for %s", tc)
+ }
+ }
+}
diff --git a/internal/iana/const.go b/internal/iana/const.go
new file mode 100644
index 0000000..7fe8822
--- /dev/null
+++ b/internal/iana/const.go
@@ -0,0 +1,181 @@
+// go generate gen.go
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
+package iana // import "golang.org/x/net/internal/iana"
+
+// Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25
+const (
+ DiffServCS0 = 0x0 // CS0
+ DiffServCS1 = 0x20 // CS1
+ DiffServCS2 = 0x40 // CS2
+ DiffServCS3 = 0x60 // CS3
+ DiffServCS4 = 0x80 // CS4
+ DiffServCS5 = 0xa0 // CS5
+ DiffServCS6 = 0xc0 // CS6
+ DiffServCS7 = 0xe0 // CS7
+ DiffServAF11 = 0x28 // AF11
+ DiffServAF12 = 0x30 // AF12
+ DiffServAF13 = 0x38 // AF13
+ DiffServAF21 = 0x48 // AF21
+ DiffServAF22 = 0x50 // AF22
+ DiffServAF23 = 0x58 // AF23
+ DiffServAF31 = 0x68 // AF31
+ DiffServAF32 = 0x70 // AF32
+ DiffServAF33 = 0x78 // AF33
+ DiffServAF41 = 0x88 // AF41
+ DiffServAF42 = 0x90 // AF42
+ DiffServAF43 = 0x98 // AF43
+ DiffServEFPHB = 0xb8 // EF PHB
+ DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
+)
+
+// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
+const (
+ NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport)
+ ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1))
+ ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0))
+ CongestionExperienced = 0x3 // CE (Congestion Experienced)
+)
+
+// Protocol Numbers, Updated: 2015-06-23
+const (
+ ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
+ ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
+ ProtocolICMP = 1 // Internet Control Message
+ ProtocolIGMP = 2 // Internet Group Management
+ ProtocolGGP = 3 // Gateway-to-Gateway
+ ProtocolIPv4 = 4 // IPv4 encapsulation
+ ProtocolST = 5 // Stream
+ ProtocolTCP = 6 // Transmission Control
+ ProtocolCBT = 7 // CBT
+ ProtocolEGP = 8 // Exterior Gateway Protocol
+ ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
+ ProtocolBBNRCCMON = 10 // BBN RCC Monitoring
+ ProtocolNVPII = 11 // Network Voice Protocol
+ ProtocolPUP = 12 // PUP
+ ProtocolARGUS = 13 // ARGUS
+ ProtocolEMCON = 14 // EMCON
+ ProtocolXNET = 15 // Cross Net Debugger
+ ProtocolCHAOS = 16 // Chaos
+ ProtocolUDP = 17 // User Datagram
+ ProtocolMUX = 18 // Multiplexing
+ ProtocolDCNMEAS = 19 // DCN Measurement Subsystems
+ ProtocolHMP = 20 // Host Monitoring
+ ProtocolPRM = 21 // Packet Radio Measurement
+ ProtocolXNSIDP = 22 // XEROX NS IDP
+ ProtocolTRUNK1 = 23 // Trunk-1
+ ProtocolTRUNK2 = 24 // Trunk-2
+ ProtocolLEAF1 = 25 // Leaf-1
+ ProtocolLEAF2 = 26 // Leaf-2
+ ProtocolRDP = 27 // Reliable Data Protocol
+ ProtocolIRTP = 28 // Internet Reliable Transaction
+ ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
+ ProtocolNETBLT = 30 // Bulk Data Transfer Protocol
+ ProtocolMFENSP = 31 // MFE Network Services Protocol
+ ProtocolMERITINP = 32 // MERIT Internodal Protocol
+ ProtocolDCCP = 33 // Datagram Congestion Control Protocol
+ Protocol3PC = 34 // Third Party Connect Protocol
+ ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
+ ProtocolXTP = 36 // XTP
+ ProtocolDDP = 37 // Datagram Delivery Protocol
+ ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
+ ProtocolTPPP = 39 // TP++ Transport Protocol
+ ProtocolIL = 40 // IL Transport Protocol
+ ProtocolIPv6 = 41 // IPv6 encapsulation
+ ProtocolSDRP = 42 // Source Demand Routing Protocol
+ ProtocolIPv6Route = 43 // Routing Header for IPv6
+ ProtocolIPv6Frag = 44 // Fragment Header for IPv6
+ ProtocolIDRP = 45 // Inter-Domain Routing Protocol
+ ProtocolRSVP = 46 // Reservation Protocol
+ ProtocolGRE = 47 // Generic Routing Encapsulation
+ ProtocolDSR = 48 // Dynamic Source Routing Protocol
+ ProtocolBNA = 49 // BNA
+ ProtocolESP = 50 // Encap Security Payload
+ ProtocolAH = 51 // Authentication Header
+ ProtocolINLSP = 52 // Integrated Net Layer Security TUBA
+ ProtocolNARP = 54 // NBMA Address Resolution Protocol
+ ProtocolMOBILE = 55 // IP Mobility
+ ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
+ ProtocolSKIP = 57 // SKIP
+ ProtocolIPv6ICMP = 58 // ICMP for IPv6
+ ProtocolIPv6NoNxt = 59 // No Next Header for IPv6
+ ProtocolIPv6Opts = 60 // Destination Options for IPv6
+ ProtocolCFTP = 62 // CFTP
+ ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
+ ProtocolKRYPTOLAN = 65 // Kryptolan
+ ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
+ ProtocolIPPC = 67 // Internet Pluribus Packet Core
+ ProtocolSATMON = 69 // SATNET Monitoring
+ ProtocolVISA = 70 // VISA Protocol
+ ProtocolIPCV = 71 // Internet Packet Core Utility
+ ProtocolCPNX = 72 // Computer Protocol Network Executive
+ ProtocolCPHB = 73 // Computer Protocol Heart Beat
+ ProtocolWSN = 74 // Wang Span Network
+ ProtocolPVP = 75 // Packet Video Protocol
+ ProtocolBRSATMON = 76 // Backroom SATNET Monitoring
+ ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
+ ProtocolWBMON = 78 // WIDEBAND Monitoring
+ ProtocolWBEXPAK = 79 // WIDEBAND EXPAK
+ ProtocolISOIP = 80 // ISO Internet Protocol
+ ProtocolVMTP = 81 // VMTP
+ ProtocolSECUREVMTP = 82 // SECURE-VMTP
+ ProtocolVINES = 83 // VINES
+ ProtocolTTP = 84 // Transaction Transport Protocol
+ ProtocolIPTM = 84 // Internet Protocol Traffic Manager
+ ProtocolNSFNETIGP = 85 // NSFNET-IGP
+ ProtocolDGP = 86 // Dissimilar Gateway Protocol
+ ProtocolTCF = 87 // TCF
+ ProtocolEIGRP = 88 // EIGRP
+ ProtocolOSPFIGP = 89 // OSPFIGP
+ ProtocolSpriteRPC = 90 // Sprite RPC Protocol
+ ProtocolLARP = 91 // Locus Address Resolution Protocol
+ ProtocolMTP = 92 // Multicast Transport Protocol
+ ProtocolAX25 = 93 // AX.25 Frames
+ ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
+ ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
+ ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
+ ProtocolENCAP = 98 // Encapsulation Header
+ ProtocolGMTP = 100 // GMTP
+ ProtocolIFMP = 101 // Ipsilon Flow Management Protocol
+ ProtocolPNNI = 102 // PNNI over IP
+ ProtocolPIM = 103 // Protocol Independent Multicast
+ ProtocolARIS = 104 // ARIS
+ ProtocolSCPS = 105 // SCPS
+ ProtocolQNX = 106 // QNX
+ ProtocolAN = 107 // Active Networks
+ ProtocolIPComp = 108 // IP Payload Compression Protocol
+ ProtocolSNP = 109 // Sitara Networks Protocol
+ ProtocolCompaqPeer = 110 // Compaq Peer Protocol
+ ProtocolIPXinIP = 111 // IPX in IP
+ ProtocolVRRP = 112 // Virtual Router Redundancy Protocol
+ ProtocolPGM = 113 // PGM Reliable Transport Protocol
+ ProtocolL2TP = 115 // Layer Two Tunneling Protocol
+ ProtocolDDX = 116 // D-II Data Exchange (DDX)
+ ProtocolIATP = 117 // Interactive Agent Transfer Protocol
+ ProtocolSTP = 118 // Schedule Transfer Protocol
+ ProtocolSRP = 119 // SpectraLink Radio Protocol
+ ProtocolUTI = 120 // UTI
+ ProtocolSMP = 121 // Simple Message Protocol
+ ProtocolPTP = 123 // Performance Transparency Protocol
+ ProtocolISIS = 124 // ISIS over IPv4
+ ProtocolFIRE = 125 // FIRE
+ ProtocolCRTP = 126 // Combat Radio Transport Protocol
+ ProtocolCRUDP = 127 // Combat Radio User Datagram
+ ProtocolSSCOPMCE = 128 // SSCOPMCE
+ ProtocolIPLT = 129 // IPLT
+ ProtocolSPS = 130 // Secure Packet Shield
+ ProtocolPIPE = 131 // Private IP Encapsulation within IP
+ ProtocolSCTP = 132 // Stream Control Transmission Protocol
+ ProtocolFC = 133 // Fibre Channel
+ ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
+ ProtocolMobilityHeader = 135 // Mobility Header
+ ProtocolUDPLite = 136 // UDPLite
+ ProtocolMPLSinIP = 137 // MPLS-in-IP
+ ProtocolMANET = 138 // MANET Protocols
+ ProtocolHIP = 139 // Host Identity Protocol
+ ProtocolShim6 = 140 // Shim6 Protocol
+ ProtocolWESP = 141 // Wrapped Encapsulating Security Payload
+ ProtocolROHC = 142 // Robust Header Compression
+ ProtocolReserved = 255 // Reserved
+)
diff --git a/internal/iana/gen.go b/internal/iana/gen.go
new file mode 100644
index 0000000..2d8c07c
--- /dev/null
+++ b/internal/iana/gen.go
@@ -0,0 +1,293 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+//go:generate go run gen.go
+
+// This program generates internet protocol constants and tables by
+// reading IANA protocol registries.
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "strconv"
+ "strings"
+)
+
+var registries = []struct {
+ url string
+ parse func(io.Writer, io.Reader) error
+}{
+ {
+ "http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
+ parseDSCPRegistry,
+ },
+ {
+ "http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
+ parseTOSTCByte,
+ },
+ {
+ "http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
+ parseProtocolNumbers,
+ },
+}
+
+func main() {
+ var bb bytes.Buffer
+ fmt.Fprintf(&bb, "// go generate gen.go\n")
+ fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
+ fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
+ fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
+ for _, r := range registries {
+ resp, err := http.Get(r.url)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
+ os.Exit(1)
+ }
+ if err := r.parse(&bb, resp.Body); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ fmt.Fprintf(&bb, "\n")
+ }
+ b, err := format.Source(bb.Bytes())
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func parseDSCPRegistry(w io.Writer, r io.Reader) error {
+ dec := xml.NewDecoder(r)
+ var dr dscpRegistry
+ if err := dec.Decode(&dr); err != nil {
+ return err
+ }
+ drs := dr.escape()
+ fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
+ fmt.Fprintf(w, "const (\n")
+ for _, dr := range drs {
+ fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
+ fmt.Fprintf(w, "// %s\n", dr.OrigName)
+ }
+ fmt.Fprintf(w, ")\n")
+ return nil
+}
+
+type dscpRegistry struct {
+ XMLName xml.Name `xml:"registry"`
+ Title string `xml:"title"`
+ Updated string `xml:"updated"`
+ Note string `xml:"note"`
+ RegTitle string `xml:"registry>title"`
+ PoolRecords []struct {
+ Name string `xml:"name"`
+ Space string `xml:"space"`
+ } `xml:"registry>record"`
+ Records []struct {
+ Name string `xml:"name"`
+ Space string `xml:"space"`
+ } `xml:"registry>registry>record"`
+}
+
+type canonDSCPRecord struct {
+ OrigName string
+ Name string
+ Value int
+}
+
+func (drr *dscpRegistry) escape() []canonDSCPRecord {
+ drs := make([]canonDSCPRecord, len(drr.Records))
+ sr := strings.NewReplacer(
+ "+", "",
+ "-", "",
+ "/", "",
+ ".", "",
+ " ", "",
+ )
+ for i, dr := range drr.Records {
+ s := strings.TrimSpace(dr.Name)
+ drs[i].OrigName = s
+ drs[i].Name = sr.Replace(s)
+ n, err := strconv.ParseUint(dr.Space, 2, 8)
+ if err != nil {
+ continue
+ }
+ drs[i].Value = int(n) << 2
+ }
+ return drs
+}
+
+func parseTOSTCByte(w io.Writer, r io.Reader) error {
+ dec := xml.NewDecoder(r)
+ var ttb tosTCByte
+ if err := dec.Decode(&ttb); err != nil {
+ return err
+ }
+ trs := ttb.escape()
+ fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
+ fmt.Fprintf(w, "const (\n")
+ for _, tr := range trs {
+ fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
+ fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
+ }
+ fmt.Fprintf(w, ")\n")
+ return nil
+}
+
+type tosTCByte struct {
+ XMLName xml.Name `xml:"registry"`
+ Title string `xml:"title"`
+ Updated string `xml:"updated"`
+ Note string `xml:"note"`
+ RegTitle string `xml:"registry>title"`
+ Records []struct {
+ Binary string `xml:"binary"`
+ Keyword string `xml:"keyword"`
+ } `xml:"registry>record"`
+}
+
+type canonTOSTCByteRecord struct {
+ OrigKeyword string
+ Keyword string
+ Value int
+}
+
+func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
+ trs := make([]canonTOSTCByteRecord, len(ttb.Records))
+ sr := strings.NewReplacer(
+ "Capable", "",
+ "(", "",
+ ")", "",
+ "+", "",
+ "-", "",
+ "/", "",
+ ".", "",
+ " ", "",
+ )
+ for i, tr := range ttb.Records {
+ s := strings.TrimSpace(tr.Keyword)
+ trs[i].OrigKeyword = s
+ ss := strings.Split(s, " ")
+ if len(ss) > 1 {
+ trs[i].Keyword = strings.Join(ss[1:], " ")
+ } else {
+ trs[i].Keyword = ss[0]
+ }
+ trs[i].Keyword = sr.Replace(trs[i].Keyword)
+ n, err := strconv.ParseUint(tr.Binary, 2, 8)
+ if err != nil {
+ continue
+ }
+ trs[i].Value = int(n)
+ }
+ return trs
+}
+
+func parseProtocolNumbers(w io.Writer, r io.Reader) error {
+ dec := xml.NewDecoder(r)
+ var pn protocolNumbers
+ if err := dec.Decode(&pn); err != nil {
+ return err
+ }
+ prs := pn.escape()
+ prs = append([]canonProtocolRecord{{
+ Name: "IP",
+ Descr: "IPv4 encapsulation, pseudo protocol number",
+ Value: 0,
+ }}, prs...)
+ fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
+ fmt.Fprintf(w, "const (\n")
+ for _, pr := range prs {
+ if pr.Name == "" {
+ continue
+ }
+ fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
+ s := pr.Descr
+ if s == "" {
+ s = pr.OrigName
+ }
+ fmt.Fprintf(w, "// %s\n", s)
+ }
+ fmt.Fprintf(w, ")\n")
+ return nil
+}
+
+type protocolNumbers struct {
+ XMLName xml.Name `xml:"registry"`
+ Title string `xml:"title"`
+ Updated string `xml:"updated"`
+ RegTitle string `xml:"registry>title"`
+ Note string `xml:"registry>note"`
+ Records []struct {
+ Value string `xml:"value"`
+ Name string `xml:"name"`
+ Descr string `xml:"description"`
+ } `xml:"registry>record"`
+}
+
+type canonProtocolRecord struct {
+ OrigName string
+ Name string
+ Descr string
+ Value int
+}
+
+func (pn *protocolNumbers) escape() []canonProtocolRecord {
+ prs := make([]canonProtocolRecord, len(pn.Records))
+ sr := strings.NewReplacer(
+ "-in-", "in",
+ "-within-", "within",
+ "-over-", "over",
+ "+", "P",
+ "-", "",
+ "/", "",
+ ".", "",
+ " ", "",
+ )
+ for i, pr := range pn.Records {
+ if strings.Contains(pr.Name, "Deprecated") ||
+ strings.Contains(pr.Name, "deprecated") {
+ continue
+ }
+ prs[i].OrigName = pr.Name
+ s := strings.TrimSpace(pr.Name)
+ switch pr.Name {
+ case "ISIS over IPv4":
+ prs[i].Name = "ISIS"
+ case "manet":
+ prs[i].Name = "MANET"
+ default:
+ prs[i].Name = sr.Replace(s)
+ }
+ ss := strings.Split(pr.Descr, "\n")
+ for i := range ss {
+ ss[i] = strings.TrimSpace(ss[i])
+ }
+ if len(ss) > 1 {
+ prs[i].Descr = strings.Join(ss, " ")
+ } else {
+ prs[i].Descr = ss[0]
+ }
+ prs[i].Value, _ = strconv.Atoi(pr.Value)
+ }
+ return prs
+}
diff --git a/internal/nettest/error_posix.go b/internal/nettest/error_posix.go
new file mode 100644
index 0000000..963ed99
--- /dev/null
+++ b/internal/nettest/error_posix.go
@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+
+package nettest
+
+import (
+ "os"
+ "syscall"
+)
+
+func protocolNotSupported(err error) bool {
+ switch err := err.(type) {
+ case syscall.Errno:
+ switch err {
+ case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
+ return true
+ }
+ case *os.SyscallError:
+ switch err := err.Err.(type) {
+ case syscall.Errno:
+ switch err {
+ case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/internal/nettest/error_stub.go b/internal/nettest/error_stub.go
new file mode 100644
index 0000000..3c74d81
--- /dev/null
+++ b/internal/nettest/error_stub.go
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+// +build nacl plan9
+
+package nettest
+
+func protocolNotSupported(err error) bool {
+ return false
+}
diff --git a/internal/nettest/interface.go b/internal/nettest/interface.go
new file mode 100644
index 0000000..53ae13a
--- /dev/null
+++ b/internal/nettest/interface.go
@@ -0,0 +1,94 @@
+// Copyright 2012 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.
+
+package nettest
+
+import "net"
+
+// IsMulticastCapable reports whether ifi is an IP multicast-capable
+// network interface. Network must be "ip", "ip4" or "ip6".
+func IsMulticastCapable(network string, ifi *net.Interface) (net.IP, bool) {
+ switch network {
+ case "ip", "ip4", "ip6":
+ default:
+ return nil, false
+ }
+ if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
+ return nil, false
+ }
+ return hasRoutableIP(network, ifi)
+}
+
+// RoutedInterface returns a network interface that can route IP
+// traffic and satisfies flags. It returns nil when an appropriate
+// network interface is not found. Network must be "ip", "ip4" or
+// "ip6".
+func RoutedInterface(network string, flags net.Flags) *net.Interface {
+ switch network {
+ case "ip", "ip4", "ip6":
+ default:
+ return nil
+ }
+ ift, err := net.Interfaces()
+ if err != nil {
+ return nil
+ }
+ for _, ifi := range ift {
+ if ifi.Flags&flags != flags {
+ continue
+ }
+ if _, ok := hasRoutableIP(network, &ifi); !ok {
+ continue
+ }
+ return &ifi
+ }
+ return nil
+}
+
+func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return nil, false
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip := routableIP(network, ifa.IP); ip != nil {
+ return ip, true
+ }
+ case *net.IPNet:
+ if ip := routableIP(network, ifa.IP); ip != nil {
+ return ip, true
+ }
+ }
+ }
+ return nil, false
+}
+
+func routableIP(network string, ip net.IP) net.IP {
+ if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
+ return nil
+ }
+ switch network {
+ case "ip4":
+ if ip := ip.To4(); ip != nil {
+ return ip
+ }
+ case "ip6":
+ if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
+ return nil
+ }
+ if ip := ip.To16(); ip != nil && ip.To4() == nil {
+ return ip
+ }
+ default:
+ if ip := ip.To4(); ip != nil {
+ return ip
+ }
+ if ip := ip.To16(); ip != nil {
+ return ip
+ }
+ }
+ return nil
+}
diff --git a/internal/nettest/rlimit.go b/internal/nettest/rlimit.go
new file mode 100644
index 0000000..bb34aec
--- /dev/null
+++ b/internal/nettest/rlimit.go
@@ -0,0 +1,11 @@
+// Copyright 2015 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.
+
+package nettest
+
+const defaultMaxOpenFiles = 256
+
+// MaxOpenFiles returns the maximum number of open files for the
+// caller's process.
+func MaxOpenFiles() int { return maxOpenFiles() }
diff --git a/internal/nettest/rlimit_stub.go b/internal/nettest/rlimit_stub.go
new file mode 100644
index 0000000..102bef9
--- /dev/null
+++ b/internal/nettest/rlimit_stub.go
@@ -0,0 +1,9 @@
+// Copyright 2015 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.
+
+// +build nacl plan9
+
+package nettest
+
+func maxOpenFiles() int { return defaultMaxOpenFiles }
diff --git a/internal/nettest/rlimit_unix.go b/internal/nettest/rlimit_unix.go
new file mode 100644
index 0000000..eb4312c
--- /dev/null
+++ b/internal/nettest/rlimit_unix.go
@@ -0,0 +1,17 @@
+// Copyright 2015 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package nettest
+
+import "syscall"
+
+func maxOpenFiles() int {
+ var rlim syscall.Rlimit
+ if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil {
+ return defaultMaxOpenFiles
+ }
+ return int(rlim.Cur)
+}
diff --git a/internal/nettest/rlimit_windows.go b/internal/nettest/rlimit_windows.go
new file mode 100644
index 0000000..de927b5
--- /dev/null
+++ b/internal/nettest/rlimit_windows.go
@@ -0,0 +1,7 @@
+// Copyright 2015 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.
+
+package nettest
+
+func maxOpenFiles() int { return 4 * defaultMaxOpenFiles /* actually it's 16581375 */ }
diff --git a/internal/nettest/stack.go b/internal/nettest/stack.go
new file mode 100644
index 0000000..e07c015
--- /dev/null
+++ b/internal/nettest/stack.go
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+// Package nettest provides utilities for IP testing.
+package nettest // import "golang.org/x/net/internal/nettest"
+
+import "net"
+
+// SupportsIPv4 reports whether the platform supports IPv4 networking
+// functionality.
+func SupportsIPv4() bool {
+ ln, err := net.Listen("tcp4", "127.0.0.1:0")
+ if err != nil {
+ return false
+ }
+ ln.Close()
+ return true
+}
+
+// SupportsIPv6 reports whether the platform supports IPv6 networking
+// functionality.
+func SupportsIPv6() bool {
+ ln, err := net.Listen("tcp6", "[::1]:0")
+ if err != nil {
+ return false
+ }
+ ln.Close()
+ return true
+}
+
+// ProtocolNotSupported reports whether err is a protocol not
+// supported error.
+func ProtocolNotSupported(err error) bool {
+ return protocolNotSupported(err)
+}
diff --git a/internal/nettest/stack_stub.go b/internal/nettest/stack_stub.go
new file mode 100644
index 0000000..1b5fde1
--- /dev/null
+++ b/internal/nettest/stack_stub.go
@@ -0,0 +1,18 @@
+// Copyright 2015 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.
+
+// +build nacl plan9
+
+package nettest
+
+import (
+ "fmt"
+ "runtime"
+)
+
+// SupportsRawIPSocket reports whether the platform supports raw IP
+// sockets.
+func SupportsRawIPSocket() (string, bool) {
+ return fmt.Sprintf("not supported on %s", runtime.GOOS), false
+}
diff --git a/internal/nettest/stack_unix.go b/internal/nettest/stack_unix.go
new file mode 100644
index 0000000..af89229
--- /dev/null
+++ b/internal/nettest/stack_unix.go
@@ -0,0 +1,22 @@
+// Copyright 2015 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package nettest
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+)
+
+// SupportsRawIPSocket reports whether the platform supports raw IP
+// sockets.
+func SupportsRawIPSocket() (string, bool) {
+ if os.Getuid() != 0 {
+ return fmt.Sprintf("must be root on %s", runtime.GOOS), false
+ }
+ return "", true
+}
diff --git a/internal/nettest/stack_windows.go b/internal/nettest/stack_windows.go
new file mode 100644
index 0000000..a21f499
--- /dev/null
+++ b/internal/nettest/stack_windows.go
@@ -0,0 +1,32 @@
+// Copyright 2015 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.
+
+package nettest
+
+import (
+ "fmt"
+ "runtime"
+ "syscall"
+)
+
+// SupportsRawIPSocket reports whether the platform supports raw IP
+// sockets.
+func SupportsRawIPSocket() (string, bool) {
+ // From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx:
+ // Note: To use a socket of type SOCK_RAW requires administrative privileges.
+ // Users running Winsock applications that use raw sockets must be a member of
+ // the Administrators group on the local computer, otherwise raw socket calls
+ // will fail with an error code of WSAEACCES. On Windows Vista and later, access
+ // for raw sockets is enforced at socket creation. In earlier versions of Windows,
+ // access for raw sockets is enforced during other socket operations.
+ s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0)
+ if err == syscall.WSAEACCES {
+ return fmt.Sprintf("no access to raw socket allowed on %s", runtime.GOOS), false
+ }
+ if err != nil {
+ return err.Error(), false
+ }
+ syscall.Closesocket(s)
+ return "", true
+}
diff --git a/internal/timeseries/timeseries.go b/internal/timeseries/timeseries.go
new file mode 100644
index 0000000..1119f34
--- /dev/null
+++ b/internal/timeseries/timeseries.go
@@ -0,0 +1,525 @@
+// Copyright 2015 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.
+
+// Package timeseries implements a time series structure for stats collection.
+package timeseries // import "golang.org/x/net/internal/timeseries"
+
+import (
+ "fmt"
+ "log"
+ "time"
+)
+
+const (
+ timeSeriesNumBuckets = 64
+ minuteHourSeriesNumBuckets = 60
+)
+
+var timeSeriesResolutions = []time.Duration{
+ 1 * time.Second,
+ 10 * time.Second,
+ 1 * time.Minute,
+ 10 * time.Minute,
+ 1 * time.Hour,
+ 6 * time.Hour,
+ 24 * time.Hour, // 1 day
+ 7 * 24 * time.Hour, // 1 week
+ 4 * 7 * 24 * time.Hour, // 4 weeks
+ 16 * 7 * 24 * time.Hour, // 16 weeks
+}
+
+var minuteHourSeriesResolutions = []time.Duration{
+ 1 * time.Second,
+ 1 * time.Minute,
+}
+
+// An Observable is a kind of data that can be aggregated in a time series.
+type Observable interface {
+ Multiply(ratio float64) // Multiplies the data in self by a given ratio
+ Add(other Observable) // Adds the data from a different observation to self
+ Clear() // Clears the observation so it can be reused.
+ CopyFrom(other Observable) // Copies the contents of a given observation to self
+}
+
+// Float attaches the methods of Observable to a float64.
+type Float float64
+
+// NewFloat returns a Float.
+func NewFloat() Observable {
+ f := Float(0)
+ return &f
+}
+
+// String returns the float as a string.
+func (f *Float) String() string { return fmt.Sprintf("%g", f.Value()) }
+
+// Value returns the float's value.
+func (f *Float) Value() float64 { return float64(*f) }
+
+func (f *Float) Multiply(ratio float64) { *f *= Float(ratio) }
+
+func (f *Float) Add(other Observable) {
+ o := other.(*Float)
+ *f += *o
+}
+
+func (f *Float) Clear() { *f = 0 }
+
+func (f *Float) CopyFrom(other Observable) {
+ o := other.(*Float)
+ *f = *o
+}
+
+// A Clock tells the current time.
+type Clock interface {
+ Time() time.Time
+}
+
+type defaultClock int
+
+var defaultClockInstance defaultClock
+
+func (defaultClock) Time() time.Time { return time.Now() }
+
+// Information kept per level. Each level consists of a circular list of
+// observations. The start of the level may be derived from end and the
+// len(buckets) * sizeInMillis.
+type tsLevel struct {
+ oldest int // index to oldest bucketed Observable
+ newest int // index to newest bucketed Observable
+ end time.Time // end timestamp for this level
+ size time.Duration // duration of the bucketed Observable
+ buckets []Observable // collections of observations
+ provider func() Observable // used for creating new Observable
+}
+
+func (l *tsLevel) Clear() {
+ l.oldest = 0
+ l.newest = len(l.buckets) - 1
+ l.end = time.Time{}
+ for i := range l.buckets {
+ if l.buckets[i] != nil {
+ l.buckets[i].Clear()
+ l.buckets[i] = nil
+ }
+ }
+}
+
+func (l *tsLevel) InitLevel(size time.Duration, numBuckets int, f func() Observable) {
+ l.size = size
+ l.provider = f
+ l.buckets = make([]Observable, numBuckets)
+}
+
+// Keeps a sequence of levels. Each level is responsible for storing data at
+// a given resolution. For example, the first level stores data at a one
+// minute resolution while the second level stores data at a one hour
+// resolution.
+
+// Each level is represented by a sequence of buckets. Each bucket spans an
+// interval equal to the resolution of the level. New observations are added
+// to the last bucket.
+type timeSeries struct {
+ provider func() Observable // make more Observable
+ numBuckets int // number of buckets in each level
+ levels []*tsLevel // levels of bucketed Observable
+ lastAdd time.Time // time of last Observable tracked
+ total Observable // convenient aggregation of all Observable
+ clock Clock // Clock for getting current time
+ pending Observable // observations not yet bucketed
+ pendingTime time.Time // what time are we keeping in pending
+ dirty bool // if there are pending observations
+}
+
+// init initializes a level according to the supplied criteria.
+func (ts *timeSeries) init(resolutions []time.Duration, f func() Observable, numBuckets int, clock Clock) {
+ ts.provider = f
+ ts.numBuckets = numBuckets
+ ts.clock = clock
+ ts.levels = make([]*tsLevel, len(resolutions))
+
+ for i := range resolutions {
+ if i > 0 && resolutions[i-1] >= resolutions[i] {
+ log.Print("timeseries: resolutions must be monotonically increasing")
+ break
+ }
+ newLevel := new(tsLevel)
+ newLevel.InitLevel(resolutions[i], ts.numBuckets, ts.provider)
+ ts.levels[i] = newLevel
+ }
+
+ ts.Clear()
+}
+
+// Clear removes all observations from the time series.
+func (ts *timeSeries) Clear() {
+ ts.lastAdd = time.Time{}
+ ts.total = ts.resetObservation(ts.total)
+ ts.pending = ts.resetObservation(ts.pending)
+ ts.pendingTime = time.Time{}
+ ts.dirty = false
+
+ for i := range ts.levels {
+ ts.levels[i].Clear()
+ }
+}
+
+// Add records an observation at the current time.
+func (ts *timeSeries) Add(observation Observable) {
+ ts.AddWithTime(observation, ts.clock.Time())
+}
+
+// AddWithTime records an observation at the specified time.
+func (ts *timeSeries) AddWithTime(observation Observable, t time.Time) {
+
+ smallBucketDuration := ts.levels[0].size
+
+ if t.After(ts.lastAdd) {
+ ts.lastAdd = t
+ }
+
+ if t.After(ts.pendingTime) {
+ ts.advance(t)
+ ts.mergePendingUpdates()
+ ts.pendingTime = ts.levels[0].end
+ ts.pending.CopyFrom(observation)
+ ts.dirty = true
+ } else if t.After(ts.pendingTime.Add(-1 * smallBucketDuration)) {
+ // The observation is close enough to go into the pending bucket.
+ // This compensates for clock skewing and small scheduling delays
+ // by letting the update stay in the fast path.
+ ts.pending.Add(observation)
+ ts.dirty = true
+ } else {
+ ts.mergeValue(observation, t)
+ }
+}
+
+// mergeValue inserts the observation at the specified time in the past into all levels.
+func (ts *timeSeries) mergeValue(observation Observable, t time.Time) {
+ for _, level := range ts.levels {
+ index := (ts.numBuckets - 1) - int(level.end.Sub(t)/level.size)
+ if 0 <= index && index < ts.numBuckets {
+ bucketNumber := (level.oldest + index) % ts.numBuckets
+ if level.buckets[bucketNumber] == nil {
+ level.buckets[bucketNumber] = level.provider()
+ }
+ level.buckets[bucketNumber].Add(observation)
+ }
+ }
+ ts.total.Add(observation)
+}
+
+// mergePendingUpdates applies the pending updates into all levels.
+func (ts *timeSeries) mergePendingUpdates() {
+ if ts.dirty {
+ ts.mergeValue(ts.pending, ts.pendingTime)
+ ts.pending = ts.resetObservation(ts.pending)
+ ts.dirty = false
+ }
+}
+
+// advance cycles the buckets at each level until the latest bucket in
+// each level can hold the time specified.
+func (ts *timeSeries) advance(t time.Time) {
+ if !t.After(ts.levels[0].end) {
+ return
+ }
+ for i := 0; i < len(ts.levels); i++ {
+ level := ts.levels[i]
+ if !level.end.Before(t) {
+ break
+ }
+
+ // If the time is sufficiently far, just clear the level and advance
+ // directly.
+ if !t.Before(level.end.Add(level.size * time.Duration(ts.numBuckets))) {
+ for _, b := range level.buckets {
+ ts.resetObservation(b)
+ }
+ level.end = time.Unix(0, (t.UnixNano()/level.size.Nanoseconds())*level.size.Nanoseconds())
+ }
+
+ for t.After(level.end) {
+ level.end = level.end.Add(level.size)
+ level.newest = level.oldest
+ level.oldest = (level.oldest + 1) % ts.numBuckets
+ ts.resetObservation(level.buckets[level.newest])
+ }
+
+ t = level.end
+ }
+}
+
+// Latest returns the sum of the num latest buckets from the level.
+func (ts *timeSeries) Latest(level, num int) Observable {
+ now := ts.clock.Time()
+ if ts.levels[0].end.Before(now) {
+ ts.advance(now)
+ }
+
+ ts.mergePendingUpdates()
+
+ result := ts.provider()
+ l := ts.levels[level]
+ index := l.newest
+
+ for i := 0; i < num; i++ {
+ if l.buckets[index] != nil {
+ result.Add(l.buckets[index])
+ }
+ if index == 0 {
+ index = ts.numBuckets
+ }
+ index--
+ }
+
+ return result
+}
+
+// LatestBuckets returns a copy of the num latest buckets from level.
+func (ts *timeSeries) LatestBuckets(level, num int) []Observable {
+ if level < 0 || level > len(ts.levels) {
+ log.Print("timeseries: bad level argument: ", level)
+ return nil
+ }
+ if num < 0 || num >= ts.numBuckets {
+ log.Print("timeseries: bad num argument: ", num)
+ return nil
+ }
+
+ results := make([]Observable, num)
+ now := ts.clock.Time()
+ if ts.levels[0].end.Before(now) {
+ ts.advance(now)
+ }
+
+ ts.mergePendingUpdates()
+
+ l := ts.levels[level]
+ index := l.newest
+
+ for i := 0; i < num; i++ {
+ result := ts.provider()
+ results[i] = result
+ if l.buckets[index] != nil {
+ result.CopyFrom(l.buckets[index])
+ }
+
+ if index == 0 {
+ index = ts.numBuckets
+ }
+ index -= 1
+ }
+ return results
+}
+
+// ScaleBy updates observations by scaling by factor.
+func (ts *timeSeries) ScaleBy(factor float64) {
+ for _, l := range ts.levels {
+ for i := 0; i < ts.numBuckets; i++ {
+ l.buckets[i].Multiply(factor)
+ }
+ }
+
+ ts.total.Multiply(factor)
+ ts.pending.Multiply(factor)
+}
+
+// Range returns the sum of observations added over the specified time range.
+// If start or finish times don't fall on bucket boundaries of the same
+// level, then return values are approximate answers.
+func (ts *timeSeries) Range(start, finish time.Time) Observable {
+ return ts.ComputeRange(start, finish, 1)[0]
+}
+
+// Recent returns the sum of observations from the last delta.
+func (ts *timeSeries) Recent(delta time.Duration) Observable {
+ now := ts.clock.Time()
+ return ts.Range(now.Add(-delta), now)
+}
+
+// Total returns the total of all observations.
+func (ts *timeSeries) Total() Observable {
+ ts.mergePendingUpdates()
+ return ts.total
+}
+
+// ComputeRange computes a specified number of values into a slice using
+// the observations recorded over the specified time period. The return
+// values are approximate if the start or finish times don't fall on the
+// bucket boundaries at the same level or if the number of buckets spanning
+// the range is not an integral multiple of num.
+func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observable {
+ if start.After(finish) {
+ log.Printf("timeseries: start > finish, %v>%v", start, finish)
+ return nil
+ }
+
+ if num < 0 {
+ log.Printf("timeseries: num < 0, %v", num)
+ return nil
+ }
+
+ results := make([]Observable, num)
+
+ for _, l := range ts.levels {
+ if !start.Before(l.end.Add(-l.size * time.Duration(ts.numBuckets))) {
+ ts.extract(l, start, finish, num, results)
+ return results
+ }
+ }
+
+ // Failed to find a level that covers the desired range. So just
+ // extract from the last level, even if it doesn't cover the entire
+ // desired range.
+ ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
+
+ return results
+}
+
+// RecentList returns the specified number of values in slice over the most
+// recent time period of the specified range.
+func (ts *timeSeries) RecentList(delta time.Duration, num int) []Observable {
+ if delta < 0 {
+ return nil
+ }
+ now := ts.clock.Time()
+ return ts.ComputeRange(now.Add(-delta), now, num)
+}
+
+// extract returns a slice of specified number of observations from a given
+// level over a given range.
+func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, results []Observable) {
+ ts.mergePendingUpdates()
+
+ srcInterval := l.size
+ dstInterval := finish.Sub(start) / time.Duration(num)
+ dstStart := start
+ srcStart := l.end.Add(-srcInterval * time.Duration(ts.numBuckets))
+
+ srcIndex := 0
+
+ // Where should scanning start?
+ if dstStart.After(srcStart) {
+ advance := dstStart.Sub(srcStart) / srcInterval
+ srcIndex += int(advance)
+ srcStart = srcStart.Add(advance * srcInterval)
+ }
+
+ // The i'th value is computed as show below.
+ // interval = (finish/start)/num
+ // i'th value = sum of observation in range
+ // [ start + i * interval,
+ // start + (i + 1) * interval )
+ for i := 0; i < num; i++ {
+ results[i] = ts.resetObservation(results[i])
+ dstEnd := dstStart.Add(dstInterval)
+ for srcIndex < ts.numBuckets && srcStart.Before(dstEnd) {
+ srcEnd := srcStart.Add(srcInterval)
+ if srcEnd.After(ts.lastAdd) {
+ srcEnd = ts.lastAdd
+ }
+
+ if !srcEnd.Before(dstStart) {
+ srcValue := l.buckets[(srcIndex+l.oldest)%ts.numBuckets]
+ if !srcStart.Before(dstStart) && !srcEnd.After(dstEnd) {
+ // dst completely contains src.
+ if srcValue != nil {
+ results[i].Add(srcValue)
+ }
+ } else {
+ // dst partially overlaps src.
+ overlapStart := maxTime(srcStart, dstStart)
+ overlapEnd := minTime(srcEnd, dstEnd)
+ base := srcEnd.Sub(srcStart)
+ fraction := overlapEnd.Sub(overlapStart).Seconds() / base.Seconds()
+
+ used := ts.provider()
+ if srcValue != nil {
+ used.CopyFrom(srcValue)
+ }
+ used.Multiply(fraction)
+ results[i].Add(used)
+ }
+
+ if srcEnd.After(dstEnd) {
+ break
+ }
+ }
+ srcIndex++
+ srcStart = srcStart.Add(srcInterval)
+ }
+ dstStart = dstStart.Add(dstInterval)
+ }
+}
+
+// resetObservation clears the content so the struct may be reused.
+func (ts *timeSeries) resetObservation(observation Observable) Observable {
+ if observation == nil {
+ observation = ts.provider()
+ } else {
+ observation.Clear()
+ }
+ return observation
+}
+
+// TimeSeries tracks data at granularities from 1 second to 16 weeks.
+type TimeSeries struct {
+ timeSeries
+}
+
+// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable.
+func NewTimeSeries(f func() Observable) *TimeSeries {
+ return NewTimeSeriesWithClock(f, defaultClockInstance)
+}
+
+// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for
+// assigning timestamps.
+func NewTimeSeriesWithClock(f func() Observable, clock Clock) *TimeSeries {
+ ts := new(TimeSeries)
+ ts.timeSeries.init(timeSeriesResolutions, f, timeSeriesNumBuckets, clock)
+ return ts
+}
+
+// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour.
+type MinuteHourSeries struct {
+ timeSeries
+}
+
+// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable.
+func NewMinuteHourSeries(f func() Observable) *MinuteHourSeries {
+ return NewMinuteHourSeriesWithClock(f, defaultClockInstance)
+}
+
+// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for
+// assigning timestamps.
+func NewMinuteHourSeriesWithClock(f func() Observable, clock Clock) *MinuteHourSeries {
+ ts := new(MinuteHourSeries)
+ ts.timeSeries.init(minuteHourSeriesResolutions, f,
+ minuteHourSeriesNumBuckets, clock)
+ return ts
+}
+
+func (ts *MinuteHourSeries) Minute() Observable {
+ return ts.timeSeries.Latest(0, 60)
+}
+
+func (ts *MinuteHourSeries) Hour() Observable {
+ return ts.timeSeries.Latest(1, 60)
+}
+
+func minTime(a, b time.Time) time.Time {
+ if a.Before(b) {
+ return a
+ }
+ return b
+}
+
+func maxTime(a, b time.Time) time.Time {
+ if a.After(b) {
+ return a
+ }
+ return b
+}
diff --git a/internal/timeseries/timeseries_test.go b/internal/timeseries/timeseries_test.go
new file mode 100644
index 0000000..66325a9
--- /dev/null
+++ b/internal/timeseries/timeseries_test.go
@@ -0,0 +1,170 @@
+// Copyright 2015 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.
+
+package timeseries
+
+import (
+ "math"
+ "testing"
+ "time"
+)
+
+func isNear(x *Float, y float64, tolerance float64) bool {
+ return math.Abs(x.Value()-y) < tolerance
+}
+
+func isApproximate(x *Float, y float64) bool {
+ return isNear(x, y, 1e-2)
+}
+
+func checkApproximate(t *testing.T, o Observable, y float64) {
+ x := o.(*Float)
+ if !isApproximate(x, y) {
+ t.Errorf("Wanted %g, got %g", y, x.Value())
+ }
+}
+
+func checkNear(t *testing.T, o Observable, y, tolerance float64) {
+ x := o.(*Float)
+ if !isNear(x, y, tolerance) {
+ t.Errorf("Wanted %g +- %g, got %g", y, tolerance, x.Value())
+ }
+}
+
+var baseTime = time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)
+
+func tu(s int64) time.Time {
+ return baseTime.Add(time.Duration(s) * time.Second)
+}
+
+func tu2(s int64, ns int64) time.Time {
+ return baseTime.Add(time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond)
+}
+
+func TestBasicTimeSeries(t *testing.T) {
+ ts := NewTimeSeries(NewFloat)
+ fo := new(Float)
+ *fo = Float(10)
+ ts.AddWithTime(fo, tu(1))
+ ts.AddWithTime(fo, tu(1))
+ ts.AddWithTime(fo, tu(1))
+ ts.AddWithTime(fo, tu(1))
+ checkApproximate(t, ts.Range(tu(0), tu(1)), 40)
+ checkApproximate(t, ts.Total(), 40)
+ ts.AddWithTime(fo, tu(3))
+ ts.AddWithTime(fo, tu(3))
+ ts.AddWithTime(fo, tu(3))
+ checkApproximate(t, ts.Range(tu(0), tu(2)), 40)
+ checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
+ checkApproximate(t, ts.Total(), 70)
+ ts.AddWithTime(fo, tu(1))
+ ts.AddWithTime(fo, tu(1))
+ checkApproximate(t, ts.Range(tu(0), tu(2)), 60)
+ checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
+ checkApproximate(t, ts.Total(), 90)
+ *fo = Float(100)
+ ts.AddWithTime(fo, tu(100))
+ checkApproximate(t, ts.Range(tu(99), tu(100)), 100)
+ checkApproximate(t, ts.Range(tu(0), tu(4)), 36)
+ checkApproximate(t, ts.Total(), 190)
+ *fo = Float(10)
+ ts.AddWithTime(fo, tu(1))
+ ts.AddWithTime(fo, tu(1))
+ checkApproximate(t, ts.Range(tu(0), tu(4)), 44)
+ checkApproximate(t, ts.Range(tu(37), tu2(100, 100e6)), 100)
+ checkApproximate(t, ts.Range(tu(50), tu2(100, 100e6)), 100)
+ checkApproximate(t, ts.Range(tu(99), tu2(100, 100e6)), 100)
+ checkApproximate(t, ts.Total(), 210)
+
+ for i, l := range ts.ComputeRange(tu(36), tu(100), 64) {
+ if i == 63 {
+ checkApproximate(t, l, 100)
+ } else {
+ checkApproximate(t, l, 0)
+ }
+ }
+
+ checkApproximate(t, ts.Range(tu(0), tu(100)), 210)
+ checkApproximate(t, ts.Range(tu(10), tu(100)), 100)
+
+ for i, l := range ts.ComputeRange(tu(0), tu(100), 100) {
+ if i < 10 {
+ checkApproximate(t, l, 11)
+ } else if i >= 90 {
+ checkApproximate(t, l, 10)
+ } else {
+ checkApproximate(t, l, 0)
+ }
+ }
+}
+
+func TestFloat(t *testing.T) {
+ f := Float(1)
+ if g, w := f.String(), "1"; g != w {
+ t.Errorf("Float(1).String = %q; want %q", g, w)
+ }
+ f2 := Float(2)
+ var o Observable = &f2
+ f.Add(o)
+ if g, w := f.Value(), 3.0; g != w {
+ t.Errorf("Float post-add = %v; want %v", g, w)
+ }
+ f.Multiply(2)
+ if g, w := f.Value(), 6.0; g != w {
+ t.Errorf("Float post-multiply = %v; want %v", g, w)
+ }
+ f.Clear()
+ if g, w := f.Value(), 0.0; g != w {
+ t.Errorf("Float post-clear = %v; want %v", g, w)
+ }
+ f.CopyFrom(&f2)
+ if g, w := f.Value(), 2.0; g != w {
+ t.Errorf("Float post-CopyFrom = %v; want %v", g, w)
+ }
+}
+
+type mockClock struct {
+ time time.Time
+}
+
+func (m *mockClock) Time() time.Time { return m.time }
+func (m *mockClock) Set(t time.Time) { m.time = t }
+
+const buckets = 6
+
+var testResolutions = []time.Duration{
+ 10 * time.Second, // level holds one minute of observations
+ 100 * time.Second, // level holds ten minutes of observations
+ 10 * time.Minute, // level holds one hour of observations
+}
+
+// TestTimeSeries uses a small number of buckets to force a higher
+// error rate on approximations from the timeseries.
+type TestTimeSeries struct {
+ timeSeries
+}
+
+func TestExpectedErrorRate(t *testing.T) {
+ ts := new(TestTimeSeries)
+ fake := new(mockClock)
+ fake.Set(time.Now())
+ ts.timeSeries.init(testResolutions, NewFloat, buckets, fake)
+ for i := 1; i <= 61*61; i++ {
+ fake.Set(fake.Time().Add(1 * time.Second))
+ ob := Float(1)
+ ts.AddWithTime(&ob, fake.Time())
+
+ // The results should be accurate within one missing bucket (1/6) of the observations recorded.
+ checkNear(t, ts.Latest(0, buckets), min(float64(i), 60), 10)
+ checkNear(t, ts.Latest(1, buckets), min(float64(i), 600), 100)
+ checkNear(t, ts.Latest(2, buckets), min(float64(i), 3600), 600)
+ }
+}
+
+func min(a, b float64) float64 {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/ipv4/control.go b/ipv4/control.go
new file mode 100644
index 0000000..1f5c993
--- /dev/null
+++ b/ipv4/control.go
@@ -0,0 +1,70 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "fmt"
+ "net"
+ "sync"
+)
+
+type rawOpt struct {
+ sync.RWMutex
+ cflags ControlFlags
+}
+
+func (c *rawOpt) set(f ControlFlags) { c.cflags |= f }
+func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f }
+func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
+
+type ControlFlags uint
+
+const (
+ FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet
+ FlagSrc // pass the source address on the received packet
+ FlagDst // pass the destination address on the received packet
+ FlagInterface // pass the interface index on the received packet
+)
+
+// A ControlMessage represents per packet basis IP-level socket options.
+type ControlMessage struct {
+ // Receiving socket options: SetControlMessage allows to
+ // receive the options from the protocol stack using ReadFrom
+ // method of PacketConn or RawConn.
+ //
+ // Specifying socket options: ControlMessage for WriteTo
+ // method of PacketConn or RawConn allows to send the options
+ // to the protocol stack.
+ //
+ TTL int // time-to-live, receiving only
+ Src net.IP // source address, specifying only
+ Dst net.IP // destination address, receiving only
+ IfIndex int // interface index, must be 1 <= value when specifying
+}
+
+func (cm *ControlMessage) String() string {
+ if cm == nil {
+ return "<nil>"
+ }
+ return fmt.Sprintf("ttl: %v, src: %v, dst: %v, ifindex: %v", cm.TTL, cm.Src, cm.Dst, cm.IfIndex)
+}
+
+// Ancillary data socket options
+const (
+ ctlTTL = iota // header field
+ ctlSrc // header field
+ ctlDst // header field
+ ctlInterface // inbound or outbound interface
+ ctlPacketInfo // inbound or outbound packet path
+ ctlMax
+)
+
+// A ctlOpt represents a binding for ancillary data socket option.
+type ctlOpt struct {
+ name int // option name, must be equal or greater than 1
+ length int // option length
+ marshal func([]byte, *ControlMessage) []byte
+ parse func(*ControlMessage, []byte)
+}
diff --git a/ipv4/control_bsd.go b/ipv4/control_bsd.go
new file mode 100644
index 0000000..33d8bc8
--- /dev/null
+++ b/ipv4/control_bsd.go
@@ -0,0 +1,40 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func marshalDst(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIP
+ m.Type = sysIP_RECVDSTADDR
+ m.SetLen(syscall.CmsgLen(net.IPv4len))
+ return b[syscall.CmsgSpace(net.IPv4len):]
+}
+
+func parseDst(cm *ControlMessage, b []byte) {
+ cm.Dst = b[:net.IPv4len]
+}
+
+func marshalInterface(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIP
+ m.Type = sysIP_RECVIF
+ m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
+ return b[syscall.CmsgSpace(syscall.SizeofSockaddrDatalink):]
+}
+
+func parseInterface(cm *ControlMessage, b []byte) {
+ sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0]))
+ cm.IfIndex = int(sadl.Index)
+}
diff --git a/ipv4/control_pktinfo.go b/ipv4/control_pktinfo.go
new file mode 100644
index 0000000..444782f
--- /dev/null
+++ b/ipv4/control_pktinfo.go
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+// +build darwin linux
+
+package ipv4
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIP
+ m.Type = sysIP_PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
+ if cm != nil {
+ pi := (*sysInetPktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+ if ip := cm.Src.To4(); ip != nil {
+ copy(pi.Spec_dst[:], ip)
+ }
+ if cm.IfIndex > 0 {
+ pi.setIfindex(cm.IfIndex)
+ }
+ }
+ return b[syscall.CmsgSpace(sysSizeofInetPktinfo):]
+}
+
+func parsePacketInfo(cm *ControlMessage, b []byte) {
+ pi := (*sysInetPktinfo)(unsafe.Pointer(&b[0]))
+ cm.IfIndex = int(pi.Ifindex)
+ cm.Dst = pi.Addr[:]
+}
diff --git a/ipv4/control_stub.go b/ipv4/control_stub.go
new file mode 100644
index 0000000..4d85071
--- /dev/null
+++ b/ipv4/control_stub.go
@@ -0,0 +1,23 @@
+// Copyright 2012 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+ return errOpNoSupport
+}
+
+func newControlMessage(opt *rawOpt) []byte {
+ return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ return nil, errOpNoSupport
+}
+
+func marshalControlMessage(cm *ControlMessage) []byte {
+ return nil
+}
diff --git a/ipv4/control_unix.go b/ipv4/control_unix.go
new file mode 100644
index 0000000..3000c52
--- /dev/null
+++ b/ipv4/control_unix.go
@@ -0,0 +1,164 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv4
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+ opt.Lock()
+ defer opt.Unlock()
+ if cf&FlagTTL != 0 && sockOpts[ssoReceiveTTL].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagTTL)
+ } else {
+ opt.clear(FlagTTL)
+ }
+ }
+ if sockOpts[ssoPacketInfo].name > 0 {
+ if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
+ if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
+ } else {
+ opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
+ }
+ }
+ } else {
+ if cf&FlagDst != 0 && sockOpts[ssoReceiveDst].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceiveDst], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagDst)
+ } else {
+ opt.clear(FlagDst)
+ }
+ }
+ if cf&FlagInterface != 0 && sockOpts[ssoReceiveInterface].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceiveInterface], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagInterface)
+ } else {
+ opt.clear(FlagInterface)
+ }
+ }
+ }
+ return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+ opt.RLock()
+ var l int
+ if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlTTL].length)
+ }
+ if ctlOpts[ctlPacketInfo].name > 0 {
+ if opt.isset(FlagSrc | FlagDst | FlagInterface) {
+ l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+ }
+ } else {
+ if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlDst].length)
+ }
+ if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlInterface].length)
+ }
+ }
+ if l > 0 {
+ oob = make([]byte, l)
+ b := oob
+ if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 {
+ b = ctlOpts[ctlTTL].marshal(b, nil)
+ }
+ if ctlOpts[ctlPacketInfo].name > 0 {
+ if opt.isset(FlagSrc | FlagDst | FlagInterface) {
+ b = ctlOpts[ctlPacketInfo].marshal(b, nil)
+ }
+ } else {
+ if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 {
+ b = ctlOpts[ctlDst].marshal(b, nil)
+ }
+ if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 {
+ b = ctlOpts[ctlInterface].marshal(b, nil)
+ }
+ }
+ }
+ opt.RUnlock()
+ return
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ if len(b) == 0 {
+ return nil, nil
+ }
+ cmsgs, err := syscall.ParseSocketControlMessage(b)
+ if err != nil {
+ return nil, os.NewSyscallError("parse socket control message", err)
+ }
+ cm := &ControlMessage{}
+ for _, m := range cmsgs {
+ if m.Header.Level != iana.ProtocolIP {
+ continue
+ }
+ switch int(m.Header.Type) {
+ case ctlOpts[ctlTTL].name:
+ ctlOpts[ctlTTL].parse(cm, m.Data[:])
+ case ctlOpts[ctlDst].name:
+ ctlOpts[ctlDst].parse(cm, m.Data[:])
+ case ctlOpts[ctlInterface].name:
+ ctlOpts[ctlInterface].parse(cm, m.Data[:])
+ case ctlOpts[ctlPacketInfo].name:
+ ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
+ }
+ }
+ return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+ if cm == nil {
+ return nil
+ }
+ var l int
+ pktinfo := false
+ if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex > 0) {
+ pktinfo = true
+ l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+ }
+ if l > 0 {
+ oob = make([]byte, l)
+ b := oob
+ if pktinfo {
+ b = ctlOpts[ctlPacketInfo].marshal(b, cm)
+ }
+ }
+ return
+}
+
+func marshalTTL(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIP
+ m.Type = sysIP_RECVTTL
+ m.SetLen(syscall.CmsgLen(1))
+ return b[syscall.CmsgSpace(1):]
+}
+
+func parseTTL(cm *ControlMessage, b []byte) {
+ cm.TTL = int(*(*byte)(unsafe.Pointer(&b[:1][0])))
+}
diff --git a/ipv4/control_windows.go b/ipv4/control_windows.go
new file mode 100644
index 0000000..800f637
--- /dev/null
+++ b/ipv4/control_windows.go
@@ -0,0 +1,27 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import "syscall"
+
+func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
+ // TODO(mikio): implement this
+ return syscall.EWINDOWS
+}
+
+func newControlMessage(opt *rawOpt) []byte {
+ // TODO(mikio): implement this
+ return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ // TODO(mikio): implement this
+ return nil, syscall.EWINDOWS
+}
+
+func marshalControlMessage(cm *ControlMessage) []byte {
+ // TODO(mikio): implement this
+ return nil
+}
diff --git a/ipv4/defs_darwin.go b/ipv4/defs_darwin.go
new file mode 100644
index 0000000..731d56a
--- /dev/null
+++ b/ipv4/defs_darwin.go
@@ -0,0 +1,77 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_STRIPHDR = C.IP_STRIPHDR
+ sysIP_RECVTTL = C.IP_RECVTTL
+ sysIP_BOUND_IF = C.IP_BOUND_IF
+ sysIP_PKTINFO = C.IP_PKTINFO
+ sysIP_RECVPKTINFO = C.IP_RECVPKTINFO
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+ sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
+ sysIP_MULTICAST_IFINDEX = C.IP_MULTICAST_IFINDEX
+ sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
+ sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
+ sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
+ sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+
+ sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+ sysSizeofSockaddrInet = C.sizeof_struct_sockaddr_in
+ sysSizeofInetPktinfo = C.sizeof_struct_in_pktinfo
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+ sysSizeofIPMreqn = C.sizeof_struct_ip_mreqn
+ sysSizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+)
+
+type sysSockaddrStorage C.struct_sockaddr_storage
+
+type sysSockaddrInet C.struct_sockaddr_in
+
+type sysInetPktinfo C.struct_in_pktinfo
+
+type sysIPMreq C.struct_ip_mreq
+
+type sysIPMreqn C.struct_ip_mreqn
+
+type sysIPMreqSource C.struct_ip_mreq_source
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
diff --git a/ipv4/defs_dragonfly.go b/ipv4/defs_dragonfly.go
new file mode 100644
index 0000000..08e3b85
--- /dev/null
+++ b/ipv4/defs_dragonfly.go
@@ -0,0 +1,38 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_RECVTTL = C.IP_RECVTTL
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+)
+
+type sysIPMreq C.struct_ip_mreq
diff --git a/ipv4/defs_freebsd.go b/ipv4/defs_freebsd.go
new file mode 100644
index 0000000..f12ca32
--- /dev/null
+++ b/ipv4/defs_freebsd.go
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_SENDSRCADDR = C.IP_SENDSRCADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_ONESBCAST = C.IP_ONESBCAST
+ sysIP_BINDANY = C.IP_BINDANY
+ sysIP_RECVTTL = C.IP_RECVTTL
+ sysIP_MINTTL = C.IP_MINTTL
+ sysIP_DONTFRAG = C.IP_DONTFRAG
+ sysIP_RECVTOS = C.IP_RECVTOS
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+ sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF
+ sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
+ sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
+ sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
+ sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+
+ sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+ sysSizeofSockaddrInet = C.sizeof_struct_sockaddr_in
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+ sysSizeofIPMreqn = C.sizeof_struct_ip_mreqn
+ sysSizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+)
+
+type sysSockaddrStorage C.struct_sockaddr_storage
+
+type sysSockaddrInet C.struct_sockaddr_in
+
+type sysIPMreq C.struct_ip_mreq
+
+type sysIPMreqn C.struct_ip_mreqn
+
+type sysIPMreqSource C.struct_ip_mreq_source
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
diff --git a/ipv4/defs_linux.go b/ipv4/defs_linux.go
new file mode 100644
index 0000000..fdba148
--- /dev/null
+++ b/ipv4/defs_linux.go
@@ -0,0 +1,111 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <time.h>
+
+#include <linux/errqueue.h>
+#include <linux/icmp.h>
+#include <linux/in.h>
+*/
+import "C"
+
+const (
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_ROUTER_ALERT = C.IP_ROUTER_ALERT
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_PKTINFO = C.IP_PKTINFO
+ sysIP_PKTOPTIONS = C.IP_PKTOPTIONS
+ sysIP_MTU_DISCOVER = C.IP_MTU_DISCOVER
+ sysIP_RECVERR = C.IP_RECVERR
+ sysIP_RECVTTL = C.IP_RECVTTL
+ sysIP_RECVTOS = C.IP_RECVTOS
+ sysIP_MTU = C.IP_MTU
+ sysIP_FREEBIND = C.IP_FREEBIND
+ sysIP_TRANSPARENT = C.IP_TRANSPARENT
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_ORIGDSTADDR = C.IP_ORIGDSTADDR
+ sysIP_RECVORIGDSTADDR = C.IP_RECVORIGDSTADDR
+ sysIP_MINTTL = C.IP_MINTTL
+ sysIP_NODEFRAG = C.IP_NODEFRAG
+ sysIP_UNICAST_IF = C.IP_UNICAST_IF
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+ sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
+ sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
+ sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
+ sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
+ sysIP_MSFILTER = C.IP_MSFILTER
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+ sysMCAST_MSFILTER = C.MCAST_MSFILTER
+ sysIP_MULTICAST_ALL = C.IP_MULTICAST_ALL
+
+ //sysIP_PMTUDISC_DONT = C.IP_PMTUDISC_DONT
+ //sysIP_PMTUDISC_WANT = C.IP_PMTUDISC_WANT
+ //sysIP_PMTUDISC_DO = C.IP_PMTUDISC_DO
+ //sysIP_PMTUDISC_PROBE = C.IP_PMTUDISC_PROBE
+ //sysIP_PMTUDISC_INTERFACE = C.IP_PMTUDISC_INTERFACE
+ //sysIP_PMTUDISC_OMIT = C.IP_PMTUDISC_OMIT
+
+ sysICMP_FILTER = C.ICMP_FILTER
+
+ sysSO_EE_ORIGIN_NONE = C.SO_EE_ORIGIN_NONE
+ sysSO_EE_ORIGIN_LOCAL = C.SO_EE_ORIGIN_LOCAL
+ sysSO_EE_ORIGIN_ICMP = C.SO_EE_ORIGIN_ICMP
+ sysSO_EE_ORIGIN_ICMP6 = C.SO_EE_ORIGIN_ICMP6
+ sysSO_EE_ORIGIN_TXSTATUS = C.SO_EE_ORIGIN_TXSTATUS
+ sysSO_EE_ORIGIN_TIMESTAMPING = C.SO_EE_ORIGIN_TIMESTAMPING
+
+ sysSizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage
+ sysSizeofSockaddrInet = C.sizeof_struct_sockaddr_in
+ sysSizeofInetPktinfo = C.sizeof_struct_in_pktinfo
+ sysSizeofSockExtendedErr = C.sizeof_struct_sock_extended_err
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+ sysSizeofIPMreqn = C.sizeof_struct_ip_mreqn
+ sysSizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+
+ sysSizeofICMPFilter = C.sizeof_struct_icmp_filter
+)
+
+type sysKernelSockaddrStorage C.struct___kernel_sockaddr_storage
+
+type sysSockaddrInet C.struct_sockaddr_in
+
+type sysInetPktinfo C.struct_in_pktinfo
+
+type sysSockExtendedErr C.struct_sock_extended_err
+
+type sysIPMreq C.struct_ip_mreq
+
+type sysIPMreqn C.struct_ip_mreqn
+
+type sysIPMreqSource C.struct_ip_mreq_source
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
+
+type sysICMPFilter C.struct_icmp_filter
diff --git a/ipv4/defs_netbsd.go b/ipv4/defs_netbsd.go
new file mode 100644
index 0000000..8642354
--- /dev/null
+++ b/ipv4/defs_netbsd.go
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_RECVTTL = C.IP_RECVTTL
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+)
+
+type sysIPMreq C.struct_ip_mreq
diff --git a/ipv4/defs_openbsd.go b/ipv4/defs_openbsd.go
new file mode 100644
index 0000000..8642354
--- /dev/null
+++ b/ipv4/defs_openbsd.go
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_RECVTTL = C.IP_RECVTTL
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+)
+
+type sysIPMreq C.struct_ip_mreq
diff --git a/ipv4/defs_solaris.go b/ipv4/defs_solaris.go
new file mode 100644
index 0000000..bb74afa
--- /dev/null
+++ b/ipv4/defs_solaris.go
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+
+package ipv4
+
+/*
+#include <netinet/in.h>
+*/
+import "C"
+
+const (
+ sysIP_OPTIONS = C.IP_OPTIONS
+ sysIP_HDRINCL = C.IP_HDRINCL
+ sysIP_TOS = C.IP_TOS
+ sysIP_TTL = C.IP_TTL
+ sysIP_RECVOPTS = C.IP_RECVOPTS
+ sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+ sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+ sysIP_RETOPTS = C.IP_RETOPTS
+ sysIP_RECVIF = C.IP_RECVIF
+ sysIP_RECVSLLA = C.IP_RECVSLLA
+ sysIP_RECVTTL = C.IP_RECVTTL
+ sysIP_NEXTHOP = C.IP_NEXTHOP
+ sysIP_PKTINFO = C.IP_PKTINFO
+ sysIP_RECVPKTINFO = C.IP_RECVPKTINFO
+ sysIP_DONTFRAG = C.IP_DONTFRAG
+ sysIP_BOUND_IF = C.IP_BOUND_IF
+ sysIP_UNSPEC_SRC = C.IP_UNSPEC_SRC
+ sysIP_BROADCAST_TTL = C.IP_BROADCAST_TTL
+ sysIP_DHCPINIT_IF = C.IP_DHCPINIT_IF
+
+ sysIP_MULTICAST_IF = C.IP_MULTICAST_IF
+ sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL
+ sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP
+ sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP
+ sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP
+ sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE
+ sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE
+ sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP
+ sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
+
+ sysSizeofInetPktinfo = C.sizeof_struct_in_pktinfo
+
+ sysSizeofIPMreq = C.sizeof_struct_ip_mreq
+ sysSizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
+)
+
+type sysInetPktinfo C.struct_in_pktinfo
+
+type sysIPMreq C.struct_ip_mreq
+
+type sysIPMreqSource C.struct_ip_mreq_source
diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go
new file mode 100644
index 0000000..103c4f6
--- /dev/null
+++ b/ipv4/dgramopt_posix.go
@@ -0,0 +1,251 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+// MulticastTTL returns the time-to-live field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastTTL() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoMulticastTTL])
+}
+
+// SetMulticastTTL sets the time-to-live field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastTTL(ttl int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoMulticastTTL], ttl)
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+ if !c.ok() {
+ return nil, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return nil, err
+ }
+ return getInterface(fd, &sockOpts[ssoMulticastInterface])
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInterface(fd, &sockOpts[ssoMulticastInterface], ifi)
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+ if !c.ok() {
+ return false, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return false, err
+ }
+ on, err := getInt(fd, &sockOpts[ssoMulticastLoopback])
+ if err != nil {
+ return false, err
+ }
+ return on == 1, nil
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoMulticastLoopback], boolint(on))
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// By default all sources that can cast data to group are accepted.
+// It's possible to mute and unmute data transmission from a specific
+// source by using ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup.
+// JoinGroup uses the system assigned multicast interface when ifi is
+// nil, although this is not recommended because the assignment
+// depends on platforms and sometimes it might require routing
+// configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ return setGroup(fd, &sockOpts[ssoJoinGroup], ifi, grp)
+}
+
+// LeaveGroup leaves the group address group on the interface ifi
+// regardless of whether the group is any-source group or
+// source-specific group.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
+}
+
+// JoinSourceSpecificGroup joins the source-specific group comprising
+// group and source on the interface ifi.
+// JoinSourceSpecificGroup uses the system assigned multicast
+// interface when ifi is nil, although this is not recommended because
+// the assignment depends on platforms and sometimes it might require
+// routing configuration.
+func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP4(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoJoinSourceGroup], ifi, grp, src)
+}
+
+// LeaveSourceSpecificGroup leaves the source-specific group on the
+// interface ifi.
+func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP4(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoLeaveSourceGroup], ifi, grp, src)
+}
+
+// ExcludeSourceSpecificGroup excludes the source-specific group from
+// the already joined any-source groups by JoinGroup on the interface
+// ifi.
+func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP4(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoBlockSourceGroup], ifi, grp, src)
+}
+
+// IncludeSourceSpecificGroup includes the excluded source-specific
+// group by ExcludeSourceSpecificGroup again on the interface ifi.
+func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP4(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP4(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
+}
+
+// ICMPFilter returns an ICMP filter.
+// Currently only Linux supports this.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+ if !c.ok() {
+ return nil, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return nil, err
+ }
+ return getICMPFilter(fd, &sockOpts[ssoICMPFilter])
+}
+
+// SetICMPFilter deploys the ICMP filter.
+// Currently only Linux supports this.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setICMPFilter(fd, &sockOpts[ssoICMPFilter], f)
+}
diff --git a/ipv4/dgramopt_stub.go b/ipv4/dgramopt_stub.go
new file mode 100644
index 0000000..b74df69
--- /dev/null
+++ b/ipv4/dgramopt_stub.go
@@ -0,0 +1,106 @@
+// Copyright 2012 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+import "net"
+
+// MulticastTTL returns the time-to-live field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastTTL() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetMulticastTTL sets the time-to-live field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastTTL(ttl int) error {
+ return errOpNoSupport
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+ return errOpNoSupport
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+ return false, errOpNoSupport
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+ return errOpNoSupport
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// By default all sources that can cast data to group are accepted.
+// It's possible to mute and unmute data transmission from a specific
+// source by using ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup.
+// JoinGroup uses the system assigned multicast interface when ifi is
+// nil, although this is not recommended because the assignment
+// depends on platforms and sometimes it might require routing
+// configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+ return errOpNoSupport
+}
+
+// LeaveGroup leaves the group address group on the interface ifi
+// regardless of whether the group is any-source group or
+// source-specific group.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+ return errOpNoSupport
+}
+
+// JoinSourceSpecificGroup joins the source-specific group comprising
+// group and source on the interface ifi.
+// JoinSourceSpecificGroup uses the system assigned multicast
+// interface when ifi is nil, although this is not recommended because
+// the assignment depends on platforms and sometimes it might require
+// routing configuration.
+func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// LeaveSourceSpecificGroup leaves the source-specific group on the
+// interface ifi.
+func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// ExcludeSourceSpecificGroup excludes the source-specific group from
+// the already joined any-source groups by JoinGroup on the interface
+// ifi.
+func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// IncludeSourceSpecificGroup includes the excluded source-specific
+// group by ExcludeSourceSpecificGroup again on the interface ifi.
+func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// ICMPFilter returns an ICMP filter.
+// Currently only Linux supports this.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+ return nil, errOpNoSupport
+}
+
+// SetICMPFilter deploys the ICMP filter.
+// Currently only Linux supports this.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/doc.go b/ipv4/doc.go
new file mode 100644
index 0000000..75eb12d
--- /dev/null
+++ b/ipv4/doc.go
@@ -0,0 +1,242 @@
+// Copyright 2012 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.
+
+// Package ipv4 implements IP-level socket options for the Internet
+// Protocol version 4.
+//
+// The package provides IP-level socket options that allow
+// manipulation of IPv4 facilities.
+//
+// The IPv4 protocol and basic host requirements for IPv4 are defined
+// in RFC 791 and RFC 1122.
+// Host extensions for multicasting and socket interface extensions
+// for multicast source filters are defined in RFC 1112 and RFC 3678.
+// IGMPv1, IGMPv2 and IGMPv3 are defined in RFC 1112, RFC 2236 and RFC
+// 3376.
+// Source-specific multicast is defined in RFC 4607.
+//
+//
+// Unicasting
+//
+// The options for unicasting are available for net.TCPConn,
+// net.UDPConn and net.IPConn which are created as network connections
+// that use the IPv4 transport. When a single TCP connection carrying
+// a data flow of multiple packets needs to indicate the flow is
+// important, ipv4.Conn is used to set the type-of-service field on
+// the IPv4 header for each packet.
+//
+// ln, err := net.Listen("tcp4", "0.0.0.0:1024")
+// if err != nil {
+// // error handling
+// }
+// defer ln.Close()
+// for {
+// c, err := ln.Accept()
+// if err != nil {
+// // error handling
+// }
+// go func(c net.Conn) {
+// defer c.Close()
+//
+// The outgoing packets will be labeled DiffServ assured forwarding
+// class 1 low drop precedence, known as AF11 packets.
+//
+// if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil {
+// // error handling
+// }
+// if _, err := c.Write(data); err != nil {
+// // error handling
+// }
+// }(c)
+// }
+//
+//
+// Multicasting
+//
+// The options for multicasting are available for net.UDPConn and
+// net.IPconn which are created as network connections that use the
+// IPv4 transport. A few network facilities must be prepared before
+// you begin multicasting, at a minimum joining network interfaces and
+// multicast groups.
+//
+// en0, err := net.InterfaceByName("en0")
+// if err != nil {
+// // error handling
+// }
+// en1, err := net.InterfaceByIndex(911)
+// if err != nil {
+// // error handling
+// }
+// group := net.IPv4(224, 0, 0, 250)
+//
+// First, an application listens to an appropriate address with an
+// appropriate service port.
+//
+// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c.Close()
+//
+// Second, the application joins multicast groups, starts listening to
+// the groups on the specified network interfaces. Note that the
+// service port for transport layer protocol does not matter with this
+// operation as joining groups affects only network and link layer
+// protocols, such as IPv4 and Ethernet.
+//
+// p := ipv4.NewPacketConn(c)
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
+// // error handling
+// }
+//
+// The application might set per packet control message transmissions
+// between the protocol stack within the kernel. When the application
+// needs a destination address on an incoming packet,
+// SetControlMessage of ipv4.PacketConn is used to enable control
+// message transmissons.
+//
+// if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
+// // error handling
+// }
+//
+// The application could identify whether the received packets are
+// of interest by using the control message that contains the
+// destination address of the received packet.
+//
+// b := make([]byte, 1500)
+// for {
+// n, cm, src, err := p.ReadFrom(b)
+// if err != nil {
+// // error handling
+// }
+// if cm.Dst.IsMulticast() {
+// if cm.Dst.Equal(group) {
+// // joined group, do something
+// } else {
+// // unknown group, discard
+// continue
+// }
+// }
+//
+// The application can also send both unicast and multicast packets.
+//
+// p.SetTOS(DiffServCS0)
+// p.SetTTL(16)
+// if _, err := p.WriteTo(data, nil, src); err != nil {
+// // error handling
+// }
+// dst := &net.UDPAddr{IP: group, Port: 1024}
+// for _, ifi := range []*net.Interface{en0, en1} {
+// if err := p.SetMulticastInterface(ifi); err != nil {
+// // error handling
+// }
+// p.SetMulticastTTL(2)
+// if _, err := p.WriteTo(data, nil, dst); err != nil {
+// // error handling
+// }
+// }
+// }
+//
+//
+// More multicasting
+//
+// An application that uses PacketConn or RawConn may join multiple
+// multicast groups. For example, a UDP listener with port 1024 might
+// join two different groups across over two different network
+// interfaces by using:
+//
+// c, err := net.ListenPacket("udp4", "0.0.0.0:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c.Close()
+// p := ipv4.NewPacketConn(c)
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil {
+// // error handling
+// }
+//
+// It is possible for multiple UDP listeners that listen on the same
+// UDP port to join the same multicast group. The net package will
+// provide a socket that listens to a wildcard address with reusable
+// UDP port when an appropriate multicast address prefix is passed to
+// the net.ListenPacket or net.ListenUDP.
+//
+// c1, err := net.ListenPacket("udp4", "224.0.0.0:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c1.Close()
+// c2, err := net.ListenPacket("udp4", "224.0.0.0:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c2.Close()
+// p1 := ipv4.NewPacketConn(c1)
+// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
+// // error handling
+// }
+// p2 := ipv4.NewPacketConn(c2)
+// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
+// // error handling
+// }
+//
+// Also it is possible for the application to leave or rejoin a
+// multicast group on the network interface.
+//
+// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil {
+// // error handling
+// }
+//
+//
+// Source-specific multicasting
+//
+// An application that uses PacketConn or RawConn on IGMPv3 supported
+// platform is able to join source-specific multicast groups.
+// The application may use JoinSourceSpecificGroup and
+// LeaveSourceSpecificGroup for the operation known as "include" mode,
+//
+// ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)}
+// ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)})
+// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
+// // error handling
+// }
+// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
+// // error handling
+// }
+//
+// or JoinGroup, ExcludeSourceSpecificGroup,
+// IncludeSourceSpecificGroup and LeaveGroup for the operation known
+// as "exclude" mode.
+//
+// exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)}
+// if err := p.JoinGroup(en0, &ssmgroup); err != nil {
+// // error handling
+// }
+// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
+// // error handling
+// }
+// if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
+// // error handling
+// }
+//
+// Note that it depends on each platform implementation what happens
+// when an application which runs on IGMPv3 unsupported platform uses
+// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
+// In general the platform tries to fall back to conversations using
+// IGMPv1 or IGMPv2 and starts to listen to multicast traffic.
+// In the fallback case, ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup may return an error.
+package ipv4 // import "golang.org/x/net/ipv4"
diff --git a/ipv4/endpoint.go b/ipv4/endpoint.go
new file mode 100644
index 0000000..bc45bf0
--- /dev/null
+++ b/ipv4/endpoint.go
@@ -0,0 +1,187 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+ "time"
+)
+
+// A Conn represents a network endpoint that uses the IPv4 transport.
+// It is used to control basic IP-level socket options such as TOS and
+// TTL.
+type Conn struct {
+ genericOpt
+}
+
+type genericOpt struct {
+ net.Conn
+}
+
+func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
+
+// NewConn returns a new Conn.
+func NewConn(c net.Conn) *Conn {
+ return &Conn{
+ genericOpt: genericOpt{Conn: c},
+ }
+}
+
+// A PacketConn represents a packet network endpoint that uses the
+// IPv4 transport. It is used to control several IP-level socket
+// options including multicasting. It also provides datagram based
+// network I/O methods specific to the IPv4 and higher layer protocols
+// such as UDP.
+type PacketConn struct {
+ genericOpt
+ dgramOpt
+ payloadHandler
+}
+
+type dgramOpt struct {
+ net.PacketConn
+}
+
+func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
+
+// SetControlMessage sets the per packet IP-level socket options.
+func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.payloadHandler.sysfd()
+ if err != nil {
+ return err
+ }
+ return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on)
+}
+
+// SetDeadline sets the read and write deadlines associated with the
+// endpoint.
+func (c *PacketConn) SetDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.PacketConn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline associated with the
+// endpoint.
+func (c *PacketConn) SetReadDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.PacketConn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline associated with the
+// endpoint.
+func (c *PacketConn) SetWriteDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.PacketConn.SetWriteDeadline(t)
+}
+
+// Close closes the endpoint.
+func (c *PacketConn) Close() error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.PacketConn.Close()
+}
+
+// NewPacketConn returns a new PacketConn using c as its underlying
+// transport.
+func NewPacketConn(c net.PacketConn) *PacketConn {
+ p := &PacketConn{
+ genericOpt: genericOpt{Conn: c.(net.Conn)},
+ dgramOpt: dgramOpt{PacketConn: c},
+ payloadHandler: payloadHandler{PacketConn: c},
+ }
+ if _, ok := c.(*net.IPConn); ok && sockOpts[ssoStripHeader].name > 0 {
+ if fd, err := p.payloadHandler.sysfd(); err == nil {
+ setInt(fd, &sockOpts[ssoStripHeader], boolint(true))
+ }
+ }
+ return p
+}
+
+// A RawConn represents a packet network endpoint that uses the IPv4
+// transport. It is used to control several IP-level socket options
+// including IPv4 header manipulation. It also provides datagram
+// based network I/O methods specific to the IPv4 and higher layer
+// protocols that handle IPv4 datagram directly such as OSPF, GRE.
+type RawConn struct {
+ genericOpt
+ dgramOpt
+ packetHandler
+}
+
+// SetControlMessage sets the per packet IP-level socket options.
+func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error {
+ if !c.packetHandler.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.packetHandler.sysfd()
+ if err != nil {
+ return err
+ }
+ return setControlMessage(fd, &c.packetHandler.rawOpt, cf, on)
+}
+
+// SetDeadline sets the read and write deadlines associated with the
+// endpoint.
+func (c *RawConn) SetDeadline(t time.Time) error {
+ if !c.packetHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.packetHandler.c.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline associated with the
+// endpoint.
+func (c *RawConn) SetReadDeadline(t time.Time) error {
+ if !c.packetHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.packetHandler.c.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline associated with the
+// endpoint.
+func (c *RawConn) SetWriteDeadline(t time.Time) error {
+ if !c.packetHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.packetHandler.c.SetWriteDeadline(t)
+}
+
+// Close closes the endpoint.
+func (c *RawConn) Close() error {
+ if !c.packetHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.packetHandler.c.Close()
+}
+
+// NewRawConn returns a new RawConn using c as its underlying
+// transport.
+func NewRawConn(c net.PacketConn) (*RawConn, error) {
+ r := &RawConn{
+ genericOpt: genericOpt{Conn: c.(net.Conn)},
+ dgramOpt: dgramOpt{PacketConn: c},
+ packetHandler: packetHandler{c: c.(*net.IPConn)},
+ }
+ fd, err := r.packetHandler.sysfd()
+ if err != nil {
+ return nil, err
+ }
+ if err := setInt(fd, &sockOpts[ssoHeaderPrepend], boolint(true)); err != nil {
+ return nil, err
+ }
+ return r, nil
+}
diff --git a/ipv4/example_test.go b/ipv4/example_test.go
new file mode 100644
index 0000000..2fdc6c6
--- /dev/null
+++ b/ipv4/example_test.go
@@ -0,0 +1,222 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "runtime"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/ipv4"
+)
+
+func ExampleConn_markingTCP() {
+ ln, err := net.Listen("tcp4", "0.0.0.0:1024")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer ln.Close()
+
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ log.Fatal(err)
+ }
+ go func(c net.Conn) {
+ defer c.Close()
+ p := ipv4.NewConn(c)
+ if err := p.SetTOS(0x28); err != nil { // DSCP AF11
+ log.Fatal(err)
+ }
+ if err := p.SetTTL(128); err != nil {
+ log.Fatal(err)
+ }
+ if _, err := c.Write([]byte("HELLO-R-U-THERE-ACK")); err != nil {
+ log.Fatal(err)
+ }
+ }(c)
+ }
+}
+
+func ExamplePacketConn_servingOneShotMulticastDNS() {
+ c, err := net.ListenPacket("udp4", "0.0.0.0:5353") // mDNS over UDP
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv4.NewPacketConn(c)
+
+ en0, err := net.InterfaceByName("en0")
+ if err != nil {
+ log.Fatal(err)
+ }
+ mDNSLinkLocal := net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}
+ if err := p.JoinGroup(en0, &mDNSLinkLocal); err != nil {
+ log.Fatal(err)
+ }
+ defer p.LeaveGroup(en0, &mDNSLinkLocal)
+ if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil {
+ log.Fatal(err)
+ }
+
+ b := make([]byte, 1500)
+ for {
+ _, cm, peer, err := p.ReadFrom(b)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if !cm.Dst.IsMulticast() || !cm.Dst.Equal(mDNSLinkLocal.IP) {
+ continue
+ }
+ answers := []byte("FAKE-MDNS-ANSWERS") // fake mDNS answers, you need to implement this
+ if _, err := p.WriteTo(answers, nil, peer); err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func ExamplePacketConn_tracingIPPacketRoute() {
+ // Tracing an IP packet route to www.google.com.
+
+ const host = "www.google.com"
+ ips, err := net.LookupIP(host)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var dst net.IPAddr
+ for _, ip := range ips {
+ if ip.To4() != nil {
+ dst.IP = ip
+ fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host)
+ break
+ }
+ }
+ if dst.IP == nil {
+ log.Fatal("no A record found")
+ }
+
+ c, err := net.ListenPacket("ip4:1", "0.0.0.0") // ICMP for IPv4
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv4.NewPacketConn(c)
+
+ if err := p.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil {
+ log.Fatal(err)
+ }
+ wm := icmp.Message{
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }
+
+ rb := make([]byte, 1500)
+ for i := 1; i <= 64; i++ { // up to 64 hops
+ wm.Body.(*icmp.Echo).Seq = i
+ wb, err := wm.Marshal(nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err := p.SetTTL(i); err != nil {
+ log.Fatal(err)
+ }
+
+ // In the real world usually there are several
+ // multiple traffic-engineered paths for each hop.
+ // You may need to probe a few times to each hop.
+ begin := time.Now()
+ if _, err := p.WriteTo(wb, nil, &dst); err != nil {
+ log.Fatal(err)
+ }
+ if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
+ log.Fatal(err)
+ }
+ n, cm, peer, err := p.ReadFrom(rb)
+ if err != nil {
+ if err, ok := err.(net.Error); ok && err.Timeout() {
+ fmt.Printf("%v\t*\n", i)
+ continue
+ }
+ log.Fatal(err)
+ }
+ rm, err := icmp.ParseMessage(1, rb[:n])
+ if err != nil {
+ log.Fatal(err)
+ }
+ rtt := time.Since(begin)
+
+ // In the real world you need to determine whether the
+ // received message is yours using ControlMessage.Src,
+ // ControlMessage.Dst, icmp.Echo.ID and icmp.Echo.Seq.
+ switch rm.Type {
+ case ipv4.ICMPTypeTimeExceeded:
+ names, _ := net.LookupAddr(peer.String())
+ fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm)
+ case ipv4.ICMPTypeEchoReply:
+ names, _ := net.LookupAddr(peer.String())
+ fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm)
+ return
+ default:
+ log.Printf("unknown ICMP message: %+v\n", rm)
+ }
+ }
+}
+
+func ExampleRawConn_advertisingOSPFHello() {
+ c, err := net.ListenPacket("ip4:89", "0.0.0.0") // OSPF for IPv4
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ en0, err := net.InterfaceByName("en0")
+ if err != nil {
+ log.Fatal(err)
+ }
+ allSPFRouters := net.IPAddr{IP: net.IPv4(224, 0, 0, 5)}
+ if err := r.JoinGroup(en0, &allSPFRouters); err != nil {
+ log.Fatal(err)
+ }
+ defer r.LeaveGroup(en0, &allSPFRouters)
+
+ hello := make([]byte, 24) // fake hello data, you need to implement this
+ ospf := make([]byte, 24) // fake ospf header, you need to implement this
+ ospf[0] = 2 // version 2
+ ospf[1] = 1 // hello packet
+ ospf = append(ospf, hello...)
+ iph := &ipv4.Header{
+ Version: ipv4.Version,
+ Len: ipv4.HeaderLen,
+ TOS: 0xc0, // DSCP CS6
+ TotalLen: ipv4.HeaderLen + len(ospf),
+ TTL: 1,
+ Protocol: 89,
+ Dst: allSPFRouters.IP.To4(),
+ }
+
+ var cm *ipv4.ControlMessage
+ switch runtime.GOOS {
+ case "darwin", "linux":
+ cm = &ipv4.ControlMessage{IfIndex: en0.Index}
+ default:
+ if err := r.SetMulticastInterface(en0); err != nil {
+ log.Fatal(err)
+ }
+ }
+ if err := r.WriteTo(iph, ospf, cm); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/ipv4/gen.go b/ipv4/gen.go
new file mode 100644
index 0000000..8cef7b3
--- /dev/null
+++ b/ipv4/gen.go
@@ -0,0 +1,208 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+//go:generate go run gen.go
+
+// This program generates system adaptation constants and types,
+// internet protocol constants and tables by reading template files
+// and IANA protocol registries.
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ if err := genzsys(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ if err := geniana(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func genzsys() error {
+ defs := "defs_" + runtime.GOOS + ".go"
+ f, err := os.Open(defs)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ f.Close()
+ cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
+ b, err := cmd.Output()
+ if err != nil {
+ return err
+ }
+ // The ipv4 pacakge still supports go1.2, and so we need to
+ // take care of additional platforms in go1.3 and above for
+ // working with go1.2.
+ switch {
+ case runtime.GOOS == "dragonfly" || runtime.GOOS == "solaris":
+ b = bytes.Replace(b, []byte("package ipv4\n"), []byte("// +build "+runtime.GOOS+"\n\npackage ipv4\n"), 1)
+ case runtime.GOOS == "linux" && (runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"):
+ b = bytes.Replace(b, []byte("package ipv4\n"), []byte("// +build "+runtime.GOOS+","+runtime.GOARCH+"\n\npackage ipv4\n"), 1)
+ }
+ b, err = format.Source(b)
+ if err != nil {
+ return err
+ }
+ zsys := "zsys_" + runtime.GOOS + ".go"
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
+ }
+ if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
+ return err
+ }
+ return nil
+}
+
+var registries = []struct {
+ url string
+ parse func(io.Writer, io.Reader) error
+}{
+ {
+ "http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml",
+ parseICMPv4Parameters,
+ },
+}
+
+func geniana() error {
+ var bb bytes.Buffer
+ fmt.Fprintf(&bb, "// go generate gen.go\n")
+ fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
+ fmt.Fprintf(&bb, "package ipv4\n\n")
+ for _, r := range registries {
+ resp, err := http.Get(r.url)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
+ }
+ if err := r.parse(&bb, resp.Body); err != nil {
+ return err
+ }
+ fmt.Fprintf(&bb, "\n")
+ }
+ b, err := format.Source(bb.Bytes())
+ if err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
+ return err
+ }
+ return nil
+}
+
+func parseICMPv4Parameters(w io.Writer, r io.Reader) error {
+ dec := xml.NewDecoder(r)
+ var icp icmpv4Parameters
+ if err := dec.Decode(&icp); err != nil {
+ return err
+ }
+ prs := icp.escape()
+ fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+ fmt.Fprintf(w, "const (\n")
+ for _, pr := range prs {
+ if pr.Descr == "" {
+ continue
+ }
+ fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Descr, pr.Value)
+ fmt.Fprintf(w, "// %s\n", pr.OrigDescr)
+ }
+ fmt.Fprintf(w, ")\n\n")
+ fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+ fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
+ for _, pr := range prs {
+ if pr.Descr == "" {
+ continue
+ }
+ fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigDescr))
+ }
+ fmt.Fprintf(w, "}\n")
+ return nil
+}
+
+type icmpv4Parameters struct {
+ XMLName xml.Name `xml:"registry"`
+ Title string `xml:"title"`
+ Updated string `xml:"updated"`
+ Registries []struct {
+ Title string `xml:"title"`
+ Records []struct {
+ Value string `xml:"value"`
+ Descr string `xml:"description"`
+ } `xml:"record"`
+ } `xml:"registry"`
+}
+
+type canonICMPv4ParamRecord struct {
+ OrigDescr string
+ Descr string
+ Value int
+}
+
+func (icp *icmpv4Parameters) escape() []canonICMPv4ParamRecord {
+ id := -1
+ for i, r := range icp.Registries {
+ if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
+ id = i
+ break
+ }
+ }
+ if id < 0 {
+ return nil
+ }
+ prs := make([]canonICMPv4ParamRecord, len(icp.Registries[id].Records))
+ sr := strings.NewReplacer(
+ "Messages", "",
+ "Message", "",
+ "ICMP", "",
+ "+", "P",
+ "-", "",
+ "/", "",
+ ".", "",
+ " ", "",
+ )
+ for i, pr := range icp.Registries[id].Records {
+ if strings.Contains(pr.Descr, "Reserved") ||
+ strings.Contains(pr.Descr, "Unassigned") ||
+ strings.Contains(pr.Descr, "Deprecated") ||
+ strings.Contains(pr.Descr, "Experiment") ||
+ strings.Contains(pr.Descr, "experiment") {
+ continue
+ }
+ ss := strings.Split(pr.Descr, "\n")
+ if len(ss) > 1 {
+ prs[i].Descr = strings.Join(ss, " ")
+ } else {
+ prs[i].Descr = ss[0]
+ }
+ s := strings.TrimSpace(prs[i].Descr)
+ prs[i].OrigDescr = s
+ prs[i].Descr = sr.Replace(s)
+ prs[i].Value, _ = strconv.Atoi(pr.Value)
+ }
+ return prs
+}
diff --git a/ipv4/genericopt_posix.go b/ipv4/genericopt_posix.go
new file mode 100644
index 0000000..fefa0be
--- /dev/null
+++ b/ipv4/genericopt_posix.go
@@ -0,0 +1,59 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
+
+package ipv4
+
+import "syscall"
+
+// TOS returns the type-of-service field value for outgoing packets.
+func (c *genericOpt) TOS() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoTOS])
+}
+
+// SetTOS sets the type-of-service field value for future outgoing
+// packets.
+func (c *genericOpt) SetTOS(tos int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoTOS], tos)
+}
+
+// TTL returns the time-to-live field value for outgoing packets.
+func (c *genericOpt) TTL() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoTTL])
+}
+
+// SetTTL sets the time-to-live field value for future outgoing
+// packets.
+func (c *genericOpt) SetTTL(ttl int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoTTL], ttl)
+}
diff --git a/ipv4/genericopt_stub.go b/ipv4/genericopt_stub.go
new file mode 100644
index 0000000..1817bad
--- /dev/null
+++ b/ipv4/genericopt_stub.go
@@ -0,0 +1,29 @@
+// Copyright 2012 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+// TOS returns the type-of-service field value for outgoing packets.
+func (c *genericOpt) TOS() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetTOS sets the type-of-service field value for future outgoing
+// packets.
+func (c *genericOpt) SetTOS(tos int) error {
+ return errOpNoSupport
+}
+
+// TTL returns the time-to-live field value for outgoing packets.
+func (c *genericOpt) TTL() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetTTL sets the time-to-live field value for future outgoing
+// packets.
+func (c *genericOpt) SetTTL(ttl int) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/header.go b/ipv4/header.go
new file mode 100644
index 0000000..b22336c
--- /dev/null
+++ b/ipv4/header.go
@@ -0,0 +1,149 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "runtime"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ errMissingAddress = errors.New("missing address")
+ errMissingHeader = errors.New("missing header")
+ errHeaderTooShort = errors.New("header too short")
+ errBufferTooShort = errors.New("buffer too short")
+ errInvalidConnType = errors.New("invalid conn type")
+)
+
+const (
+ Version = 4 // protocol version
+ HeaderLen = 20 // header length without extension headers
+ maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
+)
+
+type HeaderFlags int
+
+const (
+ MoreFragments HeaderFlags = 1 << iota // more fragments flag
+ DontFragment // don't fragment flag
+)
+
+// A Header represents an IPv4 header.
+type Header struct {
+ Version int // protocol version
+ Len int // header length
+ TOS int // type-of-service
+ TotalLen int // packet total length
+ ID int // identification
+ Flags HeaderFlags // flags
+ FragOff int // fragment offset
+ TTL int // time-to-live
+ Protocol int // next protocol
+ Checksum int // checksum
+ Src net.IP // source address
+ Dst net.IP // destination address
+ Options []byte // options, extension headers
+}
+
+func (h *Header) String() string {
+ if h == nil {
+ return "<nil>"
+ }
+ return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, flags: %#x, fragoff: %#x, ttl: %v, proto: %v, cksum: %#x, src: %v, dst: %v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
+}
+
+// Marshal returns the binary encoding of the IPv4 header h.
+func (h *Header) Marshal() ([]byte, error) {
+ if h == nil {
+ return nil, syscall.EINVAL
+ }
+ if h.Len < HeaderLen {
+ return nil, errHeaderTooShort
+ }
+ hdrlen := HeaderLen + len(h.Options)
+ b := make([]byte, hdrlen)
+ b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
+ b[1] = byte(h.TOS)
+ flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "freebsd", "netbsd":
+ // TODO(mikio): fix potential misaligned memory access
+ *(*uint16)(unsafe.Pointer(&b[2:3][0])) = uint16(h.TotalLen)
+ *(*uint16)(unsafe.Pointer(&b[6:7][0])) = uint16(flagsAndFragOff)
+ default:
+ b[2], b[3] = byte(h.TotalLen>>8), byte(h.TotalLen)
+ b[6], b[7] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff)
+ }
+ b[4], b[5] = byte(h.ID>>8), byte(h.ID)
+ b[8] = byte(h.TTL)
+ b[9] = byte(h.Protocol)
+ b[10], b[11] = byte(h.Checksum>>8), byte(h.Checksum)
+ if ip := h.Src.To4(); ip != nil {
+ copy(b[12:16], ip[:net.IPv4len])
+ }
+ if ip := h.Dst.To4(); ip != nil {
+ copy(b[16:20], ip[:net.IPv4len])
+ } else {
+ return nil, errMissingAddress
+ }
+ if len(h.Options) > 0 {
+ copy(b[HeaderLen:], h.Options)
+ }
+ return b, nil
+}
+
+// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+var freebsdVersion uint32
+
+// ParseHeader parses b as an IPv4 header.
+func ParseHeader(b []byte) (*Header, error) {
+ if len(b) < HeaderLen {
+ return nil, errHeaderTooShort
+ }
+ hdrlen := int(b[0]&0x0f) << 2
+ if hdrlen > len(b) {
+ return nil, errBufferTooShort
+ }
+ h := &Header{
+ Version: int(b[0] >> 4),
+ Len: hdrlen,
+ TOS: int(b[1]),
+ ID: int(b[4])<<8 | int(b[5]),
+ TTL: int(b[8]),
+ Protocol: int(b[9]),
+ Checksum: int(b[10])<<8 | int(b[11]),
+ Src: net.IPv4(b[12], b[13], b[14], b[15]),
+ Dst: net.IPv4(b[16], b[17], b[18], b[19]),
+ }
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "netbsd":
+ // TODO(mikio): fix potential misaligned memory access
+ h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0]))) + hdrlen
+ // TODO(mikio): fix potential misaligned memory access
+ h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
+ case "freebsd":
+ // TODO(mikio): fix potential misaligned memory access
+ h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+ if freebsdVersion < 1000000 {
+ h.TotalLen += hdrlen
+ }
+ // TODO(mikio): fix potential misaligned memory access
+ h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
+ default:
+ h.TotalLen = int(b[2])<<8 | int(b[3])
+ h.FragOff = int(b[6])<<8 | int(b[7])
+ }
+ h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
+ h.FragOff = h.FragOff & 0x1fff
+ if hdrlen-HeaderLen > 0 {
+ h.Options = make([]byte, hdrlen-HeaderLen)
+ copy(h.Options, b[HeaderLen:])
+ }
+ return h, nil
+}
diff --git a/ipv4/header_test.go b/ipv4/header_test.go
new file mode 100644
index 0000000..416be6b
--- /dev/null
+++ b/ipv4/header_test.go
@@ -0,0 +1,114 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "bytes"
+ "net"
+ "reflect"
+ "runtime"
+ "testing"
+)
+
+var (
+ wireHeaderFromKernel = [HeaderLen]byte{
+ 0x45, 0x01, 0xbe, 0xef,
+ 0xca, 0xfe, 0x45, 0xdc,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ wireHeaderToKernel = [HeaderLen]byte{
+ 0x45, 0x01, 0xbe, 0xef,
+ 0xca, 0xfe, 0x45, 0xdc,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ wireHeaderFromTradBSDKernel = [HeaderLen]byte{
+ 0x45, 0x01, 0xdb, 0xbe,
+ 0xca, 0xfe, 0xdc, 0x45,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ wireHeaderFromFreeBSD10Kernel = [HeaderLen]byte{
+ 0x45, 0x01, 0xef, 0xbe,
+ 0xca, 0xfe, 0xdc, 0x45,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ wireHeaderToTradBSDKernel = [HeaderLen]byte{
+ 0x45, 0x01, 0xef, 0xbe,
+ 0xca, 0xfe, 0xdc, 0x45,
+ 0xff, 0x01, 0xde, 0xad,
+ 172, 16, 254, 254,
+ 192, 168, 0, 1,
+ }
+ // TODO(mikio): Add platform dependent wire header formats when
+ // we support new platforms.
+
+ testHeader = &Header{
+ Version: Version,
+ Len: HeaderLen,
+ TOS: 1,
+ TotalLen: 0xbeef,
+ ID: 0xcafe,
+ Flags: DontFragment,
+ FragOff: 1500,
+ TTL: 255,
+ Protocol: 1,
+ Checksum: 0xdead,
+ Src: net.IPv4(172, 16, 254, 254),
+ Dst: net.IPv4(192, 168, 0, 1),
+ }
+)
+
+func TestMarshalHeader(t *testing.T) {
+ b, err := testHeader.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var wh []byte
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "netbsd":
+ wh = wireHeaderToTradBSDKernel[:]
+ case "freebsd":
+ if freebsdVersion < 1000000 {
+ wh = wireHeaderToTradBSDKernel[:]
+ } else {
+ wh = wireHeaderFromFreeBSD10Kernel[:]
+ }
+ default:
+ wh = wireHeaderToKernel[:]
+ }
+ if !bytes.Equal(b, wh) {
+ t.Fatalf("got %#v; want %#v", b, wh)
+ }
+}
+
+func TestParseHeader(t *testing.T) {
+ var wh []byte
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "netbsd":
+ wh = wireHeaderFromTradBSDKernel[:]
+ case "freebsd":
+ if freebsdVersion < 1000000 {
+ wh = wireHeaderFromTradBSDKernel[:]
+ } else {
+ wh = wireHeaderFromFreeBSD10Kernel[:]
+ }
+ default:
+ wh = wireHeaderFromKernel[:]
+ }
+ h, err := ParseHeader(wh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(h, testHeader) {
+ t.Fatalf("got %#v; want %#v", h, testHeader)
+ }
+}
diff --git a/ipv4/helper.go b/ipv4/helper.go
new file mode 100644
index 0000000..20c2d24
--- /dev/null
+++ b/ipv4/helper.go
@@ -0,0 +1,37 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "errors"
+ "net"
+)
+
+var (
+ errOpNoSupport = errors.New("operation not supported")
+ errNoSuchInterface = errors.New("no such interface")
+ errNoSuchMulticastInterface = errors.New("no such multicast interface")
+)
+
+func boolint(b bool) int {
+ if b {
+ return 1
+ }
+ return 0
+}
+
+func netAddrToIP4(a net.Addr) net.IP {
+ switch v := a.(type) {
+ case *net.UDPAddr:
+ if ip := v.IP.To4(); ip != nil {
+ return ip
+ }
+ case *net.IPAddr:
+ if ip := v.IP.To4(); ip != nil {
+ return ip
+ }
+ }
+ return nil
+}
diff --git a/ipv4/helper_stub.go b/ipv4/helper_stub.go
new file mode 100644
index 0000000..dc2120c
--- /dev/null
+++ b/ipv4/helper_stub.go
@@ -0,0 +1,23 @@
+// Copyright 2012 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+func (c *genericOpt) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
+
+func (c *packetHandler) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
diff --git a/ipv4/helper_unix.go b/ipv4/helper_unix.go
new file mode 100644
index 0000000..345ca7d
--- /dev/null
+++ b/ipv4/helper_unix.go
@@ -0,0 +1,50 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv4
+
+import (
+ "net"
+ "reflect"
+)
+
+func (c *genericOpt) sysfd() (int, error) {
+ switch p := c.Conn.(type) {
+ case *net.TCPConn, *net.UDPConn, *net.IPConn:
+ return sysfd(p)
+ }
+ return 0, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+ switch p := c.PacketConn.(type) {
+ case *net.UDPConn, *net.IPConn:
+ return sysfd(p.(net.Conn))
+ }
+ return 0, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+ return sysfd(c.PacketConn.(net.Conn))
+}
+
+func (c *packetHandler) sysfd() (int, error) {
+ return sysfd(c.c)
+}
+
+func sysfd(c net.Conn) (int, error) {
+ cv := reflect.ValueOf(c)
+ switch ce := cv.Elem(); ce.Kind() {
+ case reflect.Struct:
+ netfd := ce.FieldByName("conn").FieldByName("fd")
+ switch fe := netfd.Elem(); fe.Kind() {
+ case reflect.Struct:
+ fd := fe.FieldByName("sysfd")
+ return int(fd.Int()), nil
+ }
+ }
+ return 0, errInvalidConnType
+}
diff --git a/ipv4/helper_windows.go b/ipv4/helper_windows.go
new file mode 100644
index 0000000..322b2a5
--- /dev/null
+++ b/ipv4/helper_windows.go
@@ -0,0 +1,49 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "net"
+ "reflect"
+ "syscall"
+)
+
+func (c *genericOpt) sysfd() (syscall.Handle, error) {
+ switch p := c.Conn.(type) {
+ case *net.TCPConn, *net.UDPConn, *net.IPConn:
+ return sysfd(p)
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (syscall.Handle, error) {
+ switch p := c.PacketConn.(type) {
+ case *net.UDPConn, *net.IPConn:
+ return sysfd(p.(net.Conn))
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (syscall.Handle, error) {
+ return sysfd(c.PacketConn.(net.Conn))
+}
+
+func (c *packetHandler) sysfd() (syscall.Handle, error) {
+ return sysfd(c.c)
+}
+
+func sysfd(c net.Conn) (syscall.Handle, error) {
+ cv := reflect.ValueOf(c)
+ switch ce := cv.Elem(); ce.Kind() {
+ case reflect.Struct:
+ netfd := ce.FieldByName("conn").FieldByName("fd")
+ switch fe := netfd.Elem(); fe.Kind() {
+ case reflect.Struct:
+ fd := fe.FieldByName("sysfd")
+ return syscall.Handle(fd.Uint()), nil
+ }
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
diff --git a/ipv4/iana.go b/ipv4/iana.go
new file mode 100644
index 0000000..be10c94
--- /dev/null
+++ b/ipv4/iana.go
@@ -0,0 +1,34 @@
+// go generate gen.go
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package ipv4
+
+// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19
+const (
+ ICMPTypeEchoReply ICMPType = 0 // Echo Reply
+ ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable
+ ICMPTypeRedirect ICMPType = 5 // Redirect
+ ICMPTypeEcho ICMPType = 8 // Echo
+ ICMPTypeRouterAdvertisement ICMPType = 9 // Router Advertisement
+ ICMPTypeRouterSolicitation ICMPType = 10 // Router Solicitation
+ ICMPTypeTimeExceeded ICMPType = 11 // Time Exceeded
+ ICMPTypeParameterProblem ICMPType = 12 // Parameter Problem
+ ICMPTypeTimestamp ICMPType = 13 // Timestamp
+ ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply
+ ICMPTypePhoturis ICMPType = 40 // Photuris
+)
+
+// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19
+var icmpTypes = map[ICMPType]string{
+ 0: "echo reply",
+ 3: "destination unreachable",
+ 5: "redirect",
+ 8: "echo",
+ 9: "router advertisement",
+ 10: "router solicitation",
+ 11: "time exceeded",
+ 12: "parameter problem",
+ 13: "timestamp",
+ 14: "timestamp reply",
+ 40: "photuris",
+}
diff --git a/ipv4/icmp.go b/ipv4/icmp.go
new file mode 100644
index 0000000..dbd05cf
--- /dev/null
+++ b/ipv4/icmp.go
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+package ipv4
+
+import "golang.org/x/net/internal/iana"
+
+// An ICMPType represents a type of ICMP message.
+type ICMPType int
+
+func (typ ICMPType) String() string {
+ s, ok := icmpTypes[typ]
+ if !ok {
+ return "<nil>"
+ }
+ return s
+}
+
+// Protocol returns the ICMPv4 protocol number.
+func (typ ICMPType) Protocol() int {
+ return iana.ProtocolICMP
+}
+
+// An ICMPFilter represents an ICMP message filter for incoming
+// packets. The filter belongs to a packet delivery path on a host and
+// it cannot interact with forwarding packets or tunnel-outer packets.
+//
+// Note: RFC 2460 defines a reasonable role model and it works not
+// only for IPv6 but IPv4. A node means a device that implements IP.
+// A router means a node that forwards IP packets not explicitly
+// addressed to itself, and a host means a node that is not a router.
+type ICMPFilter struct {
+ sysICMPFilter
+}
+
+// Accept accepts incoming ICMP packets including the type field value
+// typ.
+func (f *ICMPFilter) Accept(typ ICMPType) {
+ f.accept(typ)
+}
+
+// Block blocks incoming ICMP packets including the type field value
+// typ.
+func (f *ICMPFilter) Block(typ ICMPType) {
+ f.block(typ)
+}
+
+// SetAll sets the filter action to the filter.
+func (f *ICMPFilter) SetAll(block bool) {
+ f.setAll(block)
+}
+
+// WillBlock reports whether the ICMP type will be blocked.
+func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
+ return f.willBlock(typ)
+}
diff --git a/ipv4/icmp_linux.go b/ipv4/icmp_linux.go
new file mode 100644
index 0000000..c912253
--- /dev/null
+++ b/ipv4/icmp_linux.go
@@ -0,0 +1,25 @@
+// Copyright 2014 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.
+
+package ipv4
+
+func (f *sysICMPFilter) accept(typ ICMPType) {
+ f.Data &^= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPFilter) block(typ ICMPType) {
+ f.Data |= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPFilter) setAll(block bool) {
+ if block {
+ f.Data = 1<<32 - 1
+ } else {
+ f.Data = 0
+ }
+}
+
+func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
+ return f.Data&(1<<(uint32(typ)&31)) != 0
+}
diff --git a/ipv4/icmp_stub.go b/ipv4/icmp_stub.go
new file mode 100644
index 0000000..9ee9b6a
--- /dev/null
+++ b/ipv4/icmp_stub.go
@@ -0,0 +1,25 @@
+// Copyright 2014 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.
+
+// +build !linux
+
+package ipv4
+
+const sysSizeofICMPFilter = 0x0
+
+type sysICMPFilter struct {
+}
+
+func (f *sysICMPFilter) accept(typ ICMPType) {
+}
+
+func (f *sysICMPFilter) block(typ ICMPType) {
+}
+
+func (f *sysICMPFilter) setAll(block bool) {
+}
+
+func (f *sysICMPFilter) willBlock(typ ICMPType) bool {
+ return false
+}
diff --git a/ipv4/icmp_test.go b/ipv4/icmp_test.go
new file mode 100644
index 0000000..3324b54
--- /dev/null
+++ b/ipv4/icmp_test.go
@@ -0,0 +1,95 @@
+// Copyright 2014 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.
+
+package ipv4_test
+
+import (
+ "net"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+var icmpStringTests = []struct {
+ in ipv4.ICMPType
+ out string
+}{
+ {ipv4.ICMPTypeDestinationUnreachable, "destination unreachable"},
+
+ {256, "<nil>"},
+}
+
+func TestICMPString(t *testing.T) {
+ for _, tt := range icmpStringTests {
+ s := tt.in.String()
+ if s != tt.out {
+ t.Errorf("got %s; want %s", s, tt.out)
+ }
+ }
+}
+
+func TestICMPFilter(t *testing.T) {
+ switch runtime.GOOS {
+ case "linux":
+ default:
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ var f ipv4.ICMPFilter
+ for _, toggle := range []bool{false, true} {
+ f.SetAll(toggle)
+ for _, typ := range []ipv4.ICMPType{
+ ipv4.ICMPTypeDestinationUnreachable,
+ ipv4.ICMPTypeEchoReply,
+ ipv4.ICMPTypeTimeExceeded,
+ ipv4.ICMPTypeParameterProblem,
+ } {
+ f.Accept(typ)
+ if f.WillBlock(typ) {
+ t.Errorf("ipv4.ICMPFilter.Set(%v, false) failed", typ)
+ }
+ f.Block(typ)
+ if !f.WillBlock(typ) {
+ t.Errorf("ipv4.ICMPFilter.Set(%v, true) failed", typ)
+ }
+ }
+ }
+}
+
+func TestSetICMPFilter(t *testing.T) {
+ switch runtime.GOOS {
+ case "linux":
+ default:
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv4.NewPacketConn(c)
+
+ var f ipv4.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv4.ICMPTypeEcho)
+ f.Accept(ipv4.ICMPTypeEchoReply)
+ if err := p.SetICMPFilter(&f); err != nil {
+ t.Fatal(err)
+ }
+ kf, err := p.ICMPFilter()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(kf, &f) {
+ t.Fatalf("got %#v; want %#v", kf, f)
+ }
+}
diff --git a/ipv4/mocktransponder_test.go b/ipv4/mocktransponder_test.go
new file mode 100644
index 0000000..e55aaee
--- /dev/null
+++ b/ipv4/mocktransponder_test.go
@@ -0,0 +1,21 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "net"
+ "testing"
+)
+
+func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
+ defer func() { done <- true }()
+
+ c, err := ln.Accept()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ c.Close()
+}
diff --git a/ipv4/multicast_test.go b/ipv4/multicast_test.go
new file mode 100644
index 0000000..3f03048
--- /dev/null
+++ b/ipv4/multicast_test.go
@@ -0,0 +1,334 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+var packetConnReadWriteMulticastUDPTests = []struct {
+ addr string
+ grp, src *net.UDPAddr
+}{
+ {"224.0.0.0:0", &net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
+
+ {"232.0.1.0:0", &net.UDPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+}
+
+func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range packetConnReadWriteMulticastUDPTests {
+ c, err := net.ListenPacket("udp4", tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ grp := *tt.grp
+ grp.Port = c.LocalAddr().(*net.UDPAddr).Port
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+ if tt.src == nil {
+ if err := p.JoinGroup(ifi, &grp); err != nil {
+ t.Fatal(err)
+ }
+ defer p.LeaveGroup(ifi, &grp)
+ } else {
+ if err := p.JoinSourceSpecificGroup(ifi, &grp, tt.src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support IGMPv2/3 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ defer p.LeaveSourceSpecificGroup(ifi, &grp, tt.src)
+ }
+ if err := p.SetMulticastInterface(ifi); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastInterface(); err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetMulticastLoopback(true); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastLoopback(); err != nil {
+ t.Fatal(err)
+ }
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+ wb := []byte("HELLO-R-U-THERE")
+
+ for i, toggle := range []bool{true, false, true} {
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ p.SetMulticastTTL(i + 1)
+ if n, err := p.WriteTo(wb, nil, &grp); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Fatalf("got %v; want %v", rb[:n], wb)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+ }
+}
+
+var packetConnReadWriteMulticastICMPTests = []struct {
+ grp, src *net.IPAddr
+}{
+ {&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
+
+ {&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+}
+
+func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range packetConnReadWriteMulticastICMPTests {
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+ if tt.src == nil {
+ if err := p.JoinGroup(ifi, tt.grp); err != nil {
+ t.Fatal(err)
+ }
+ defer p.LeaveGroup(ifi, tt.grp)
+ } else {
+ if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support IGMPv2/3 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src)
+ }
+ if err := p.SetMulticastInterface(ifi); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastInterface(); err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetMulticastLoopback(true); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastLoopback(); err != nil {
+ t.Fatal(err)
+ }
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
+ for i, toggle := range []bool{true, false, true} {
+ wb, err := (&icmp.Message{
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ p.SetMulticastTTL(i + 1)
+ if n, err := p.WriteTo(wb, nil, tt.grp); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n])
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch {
+ case m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
+ case m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
+ default:
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+ }
+}
+
+var rawConnReadWriteMulticastICMPTests = []struct {
+ grp, src *net.IPAddr
+}{
+ {&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
+
+ {&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+}
+
+func TestRawConnReadWriteMulticastICMP(t *testing.T) {
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range rawConnReadWriteMulticastICMPTests {
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ if tt.src == nil {
+ if err := r.JoinGroup(ifi, tt.grp); err != nil {
+ t.Fatal(err)
+ }
+ defer r.LeaveGroup(ifi, tt.grp)
+ } else {
+ if err := r.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support IGMPv2/3 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ defer r.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src)
+ }
+ if err := r.SetMulticastInterface(ifi); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := r.MulticastInterface(); err != nil {
+ t.Fatal(err)
+ }
+ if err := r.SetMulticastLoopback(true); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := r.MulticastLoopback(); err != nil {
+ t.Fatal(err)
+ }
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
+ for i, toggle := range []bool{true, false, true} {
+ wb, err := (&icmp.Message{
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wh := &ipv4.Header{
+ Version: ipv4.Version,
+ Len: ipv4.HeaderLen,
+ TOS: i + 1,
+ TotalLen: ipv4.HeaderLen + len(wb),
+ Protocol: 1,
+ Dst: tt.grp.IP,
+ }
+ if err := r.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := r.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ r.SetMulticastTTL(i + 1)
+ if err := r.WriteTo(wh, wb, nil); err != nil {
+ t.Fatal(err)
+ }
+ rb := make([]byte, ipv4.HeaderLen+128)
+ if rh, b, cm, err := r.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch {
+ case (rh.Dst.IsLoopback() || rh.Dst.IsLinkLocalUnicast() || rh.Dst.IsGlobalUnicast()) && m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
+ case rh.Dst.IsMulticast() && m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
+ default:
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+ }
+}
diff --git a/ipv4/multicastlistener_test.go b/ipv4/multicastlistener_test.go
new file mode 100644
index 0000000..e342bf1
--- /dev/null
+++ b/ipv4/multicastlistener_test.go
@@ -0,0 +1,249 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+var udpMultipleGroupListenerTests = []net.Addr{
+ &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727
+ &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)},
+ &net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
+}
+
+func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+
+ for _, gaddr := range udpMultipleGroupListenerTests {
+ c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv4.NewPacketConn(c)
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
+ continue
+ }
+ if err := p.JoinGroup(&ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ if err := p.LeaveGroup(ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+}
+
+func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+
+ for _, gaddr := range udpMultipleGroupListenerTests {
+ c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c1.Close()
+
+ c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // wildcard address with reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c2.Close()
+
+ var ps [2]*ipv4.PacketConn
+ ps[0] = ipv4.NewPacketConn(c1)
+ ps[1] = ipv4.NewPacketConn(c2)
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
+ continue
+ }
+ for _, p := range ps {
+ if err := p.JoinGroup(&ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ for _, p := range ps {
+ if err := p.LeaveGroup(ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ }
+}
+
+func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+
+ gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
+ type ml struct {
+ c *ipv4.PacketConn
+ ifi *net.Interface
+ }
+ var mlt []*ml
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ ip, ok := nettest.IsMulticastCapable("ip4", &ifi)
+ if !ok {
+ continue
+ }
+ c, err := net.ListenPacket("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv4.NewPacketConn(c)
+ if err := p.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mlt = append(mlt, &ml{p, &ift[i]})
+ }
+ for _, m := range mlt {
+ if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok {
+ continue
+ }
+ if err := r.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ if err := r.LeaveGroup(ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if testing.Short() {
+ t.Skip("to avoid external network")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
+ type ml struct {
+ c *ipv4.RawConn
+ ifi *net.Interface
+ }
+ var mlt []*ml
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ ip, ok := nettest.IsMulticastCapable("ip4", &ifi)
+ if !ok {
+ continue
+ }
+ c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := r.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mlt = append(mlt, &ml{r, &ift[i]})
+ }
+ for _, m := range mlt {
+ if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/ipv4/multicastsockopt_test.go b/ipv4/multicastsockopt_test.go
new file mode 100644
index 0000000..c76dbe4
--- /dev/null
+++ b/ipv4/multicastsockopt_test.go
@@ -0,0 +1,195 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+var packetConnMulticastSocketOptionTests = []struct {
+ net, proto, addr string
+ grp, src net.Addr
+}{
+ {"udp4", "", "224.0.0.0:0", &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, nil}, // see RFC 4727
+ {"ip4", ":icmp", "0.0.0.0", &net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}, nil}, // see RFC 4727
+
+ {"udp4", "", "232.0.0.0:0", &net.UDPAddr{IP: net.IPv4(232, 0, 1, 249)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+ {"ip4", ":icmp", "0.0.0.0", &net.IPAddr{IP: net.IPv4(232, 0, 1, 250)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+}
+
+func TestPacketConnMulticastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ m, ok := nettest.SupportsRawIPSocket()
+ for _, tt := range packetConnMulticastSocketOptionTests {
+ if tt.net == "ip4" && !ok {
+ t.Log(m)
+ continue
+ }
+ c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+
+ if tt.src == nil {
+ testMulticastSocketOptions(t, p, ifi, tt.grp)
+ } else {
+ testSourceSpecificMulticastSocketOptions(t, p, ifi, tt.grp, tt.src)
+ }
+ }
+}
+
+var rawConnMulticastSocketOptionTests = []struct {
+ grp, src net.Addr
+}{
+ {&net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}, nil}, // see RFC 4727
+
+ {&net.IPAddr{IP: net.IPv4(232, 0, 1, 250)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
+}
+
+func TestRawConnMulticastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range rawConnMulticastSocketOptionTests {
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+
+ if tt.src == nil {
+ testMulticastSocketOptions(t, r, ifi, tt.grp)
+ } else {
+ testSourceSpecificMulticastSocketOptions(t, r, ifi, tt.grp, tt.src)
+ }
+ }
+}
+
+type testIPv4MulticastConn interface {
+ MulticastTTL() (int, error)
+ SetMulticastTTL(ttl int) error
+ MulticastLoopback() (bool, error)
+ SetMulticastLoopback(bool) error
+ JoinGroup(*net.Interface, net.Addr) error
+ LeaveGroup(*net.Interface, net.Addr) error
+ JoinSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ LeaveSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ ExcludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ IncludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+}
+
+func testMulticastSocketOptions(t *testing.T, c testIPv4MulticastConn, ifi *net.Interface, grp net.Addr) {
+ const ttl = 255
+ if err := c.SetMulticastTTL(ttl); err != nil {
+ t.Error(err)
+ return
+ }
+ if v, err := c.MulticastTTL(); err != nil {
+ t.Error(err)
+ return
+ } else if v != ttl {
+ t.Errorf("got %v; want %v", v, ttl)
+ return
+ }
+
+ for _, toggle := range []bool{true, false} {
+ if err := c.SetMulticastLoopback(toggle); err != nil {
+ t.Error(err)
+ return
+ }
+ if v, err := c.MulticastLoopback(); err != nil {
+ t.Error(err)
+ return
+ } else if v != toggle {
+ t.Errorf("got %v; want %v", v, toggle)
+ return
+ }
+ }
+
+ if err := c.JoinGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+func testSourceSpecificMulticastSocketOptions(t *testing.T, c testIPv4MulticastConn, ifi *net.Interface, grp, src net.Addr) {
+ // MCAST_JOIN_GROUP -> MCAST_BLOCK_SOURCE -> MCAST_UNBLOCK_SOURCE -> MCAST_LEAVE_GROUP
+ if err := c.JoinGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.ExcludeSourceSpecificGroup(ifi, grp, src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support IGMPv2/3 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ return
+ }
+ t.Error(err)
+ return
+ }
+ if err := c.IncludeSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_SOURCE_GROUP
+ if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_GROUP
+ if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+}
diff --git a/ipv4/packet.go b/ipv4/packet.go
new file mode 100644
index 0000000..0986431
--- /dev/null
+++ b/ipv4/packet.go
@@ -0,0 +1,97 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+// A packetHandler represents the IPv4 datagram handler.
+type packetHandler struct {
+ c *net.IPConn
+ rawOpt
+}
+
+func (c *packetHandler) ok() bool { return c != nil && c.c != nil }
+
+// ReadFrom reads an IPv4 datagram from the endpoint c, copying the
+// datagram into b. It returns the received datagram as the IPv4
+// header h, the payload p and the control message cm.
+func (c *packetHandler) ReadFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) {
+ if !c.ok() {
+ return nil, nil, nil, syscall.EINVAL
+ }
+ oob := newControlMessage(&c.rawOpt)
+ n, oobn, _, src, err := c.c.ReadMsgIP(b, oob)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ var hs []byte
+ if hs, p, err = slicePacket(b[:n]); err != nil {
+ return nil, nil, nil, err
+ }
+ if h, err = ParseHeader(hs); err != nil {
+ return nil, nil, nil, err
+ }
+ if cm, err = parseControlMessage(oob[:oobn]); err != nil {
+ return nil, nil, nil, err
+ }
+ if src != nil && cm != nil {
+ cm.Src = src.IP
+ }
+ return
+}
+
+func slicePacket(b []byte) (h, p []byte, err error) {
+ if len(b) < HeaderLen {
+ return nil, nil, errHeaderTooShort
+ }
+ hdrlen := int(b[0]&0x0f) << 2
+ return b[:hdrlen], b[hdrlen:], nil
+}
+
+// WriteTo writes an IPv4 datagram through the endpoint c, copying the
+// datagram from the IPv4 header h and the payload p. The control
+// message cm allows the datagram path and the outgoing interface to be
+// specified. Currently only Darwin and Linux support this. The cm
+// may be nil if control of the outgoing datagram is not required.
+//
+// The IPv4 header h must contain appropriate fields that include:
+//
+// Version = ipv4.Version
+// Len = <must be specified>
+// TOS = <must be specified>
+// TotalLen = <must be specified>
+// ID = platform sets an appropriate value if ID is zero
+// FragOff = <must be specified>
+// TTL = <must be specified>
+// Protocol = <must be specified>
+// Checksum = platform sets an appropriate value if Checksum is zero
+// Src = platform sets an appropriate value if Src is nil
+// Dst = <must be specified>
+// Options = optional
+func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ oob := marshalControlMessage(cm)
+ wh, err := h.Marshal()
+ if err != nil {
+ return err
+ }
+ dst := &net.IPAddr{}
+ if cm != nil {
+ if ip := cm.Dst.To4(); ip != nil {
+ dst.IP = ip
+ }
+ }
+ if dst.IP == nil {
+ dst.IP = h.Dst
+ }
+ wh = append(wh, p...)
+ _, _, err = c.c.WriteMsgIP(wh, oob, dst)
+ return err
+}
diff --git a/ipv4/payload.go b/ipv4/payload.go
new file mode 100644
index 0000000..d7698cb
--- /dev/null
+++ b/ipv4/payload.go
@@ -0,0 +1,15 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import "net"
+
+// A payloadHandler represents the IPv4 datagram payload handler.
+type payloadHandler struct {
+ net.PacketConn
+ rawOpt
+}
+
+func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }
diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go
new file mode 100644
index 0000000..d358fc3
--- /dev/null
+++ b/ipv4/payload_cmsg.go
@@ -0,0 +1,81 @@
+// Copyright 2012 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.
+
+// +build !plan9,!solaris,!windows
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+// ReadFrom reads a payload of the received IPv4 datagram, from the
+// endpoint c, copying the payload into b. It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+ if !c.ok() {
+ return 0, nil, nil, syscall.EINVAL
+ }
+ oob := newControlMessage(&c.rawOpt)
+ var oobn int
+ switch c := c.PacketConn.(type) {
+ case *net.UDPConn:
+ if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
+ return 0, nil, nil, err
+ }
+ case *net.IPConn:
+ if sockOpts[ssoStripHeader].name > 0 {
+ if n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
+ return 0, nil, nil, err
+ }
+ } else {
+ nb := make([]byte, maxHeaderLen+len(b))
+ if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil {
+ return 0, nil, nil, err
+ }
+ hdrlen := int(nb[0]&0x0f) << 2
+ copy(b, nb[hdrlen:])
+ n -= hdrlen
+ }
+ default:
+ return 0, nil, nil, errInvalidConnType
+ }
+ if cm, err = parseControlMessage(oob[:oobn]); err != nil {
+ return 0, nil, nil, err
+ }
+ if cm != nil {
+ cm.Src = netAddrToIP4(src)
+ }
+ return
+}
+
+// WriteTo writes a payload of the IPv4 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b. It
+// returns the number of bytes written. The control message cm allows
+// the datagram path and the outgoing interface to be specified.
+// Currently only Darwin and Linux support this. The cm may be nil if
+// control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ oob := marshalControlMessage(cm)
+ if dst == nil {
+ return 0, errMissingAddress
+ }
+ switch c := c.PacketConn.(type) {
+ case *net.UDPConn:
+ n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
+ case *net.IPConn:
+ n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
+ default:
+ return 0, errInvalidConnType
+ }
+ if err != nil {
+ return 0, err
+ }
+ return
+}
diff --git a/ipv4/payload_nocmsg.go b/ipv4/payload_nocmsg.go
new file mode 100644
index 0000000..d128c9c
--- /dev/null
+++ b/ipv4/payload_nocmsg.go
@@ -0,0 +1,42 @@
+// Copyright 2012 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.
+
+// +build plan9 solaris windows
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+// ReadFrom reads a payload of the received IPv4 datagram, from the
+// endpoint c, copying the payload into b. It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+ if !c.ok() {
+ return 0, nil, nil, syscall.EINVAL
+ }
+ if n, src, err = c.PacketConn.ReadFrom(b); err != nil {
+ return 0, nil, nil, err
+ }
+ return
+}
+
+// WriteTo writes a payload of the IPv4 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b. It
+// returns the number of bytes written. The control message cm allows
+// the datagram path and the outgoing interface to be specified.
+// Currently only Darwin and Linux support this. The cm may be nil if
+// control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ if dst == nil {
+ return 0, errMissingAddress
+ }
+ return c.PacketConn.WriteTo(b, dst)
+}
diff --git a/ipv4/readwrite_test.go b/ipv4/readwrite_test.go
new file mode 100644
index 0000000..5e6533e
--- /dev/null
+++ b/ipv4/readwrite_test.go
@@ -0,0 +1,170 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "bytes"
+ "net"
+ "runtime"
+ "sync"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
+ c, err := net.ListenPacket("udp4", "127.0.0.1:0")
+ if err != nil {
+ return nil, nil, err
+ }
+ dst, err := net.ResolveUDPAddr("udp4", c.LocalAddr().String())
+ if err != nil {
+ c.Close()
+ return nil, nil, err
+ }
+ return c, dst, nil
+}
+
+func BenchmarkReadWriteNetUDP(b *testing.B) {
+ c, dst, err := benchmarkUDPListener()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+
+ wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ benchmarkReadWriteNetUDP(b, c, wb, rb, dst)
+ }
+}
+
+func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) {
+ if _, err := c.WriteTo(wb, dst); err != nil {
+ b.Fatal(err)
+ }
+ if _, _, err := c.ReadFrom(rb); err != nil {
+ b.Fatal(err)
+ }
+}
+
+func BenchmarkReadWriteIPv4UDP(b *testing.B) {
+ c, dst, err := benchmarkUDPListener()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+ cf := ipv4.FlagTTL | ipv4.FlagInterface
+ if err := p.SetControlMessage(cf, true); err != nil {
+ b.Fatal(err)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+
+ wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ benchmarkReadWriteIPv4UDP(b, p, wb, rb, dst, ifi)
+ }
+}
+
+func benchmarkReadWriteIPv4UDP(b *testing.B, p *ipv4.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) {
+ cm := ipv4.ControlMessage{TTL: 1}
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ b.Fatal(err)
+ } else if n != len(wb) {
+ b.Fatalf("got %v; want %v", n, len(wb))
+ }
+ if _, _, _, err := p.ReadFrom(rb); err != nil {
+ b.Fatal(err)
+ }
+}
+
+func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ c, err := net.ListenPacket("udp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+
+ dst, err := net.ResolveUDPAddr("udp4", c.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
+ wb := []byte("HELLO-R-U-THERE")
+
+ if err := p.SetControlMessage(cf, true); err != nil { // probe before test
+ if nettest.ProtocolNotSupported(err) {
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ t.Fatal(err)
+ }
+
+ var wg sync.WaitGroup
+ reader := func() {
+ defer wg.Done()
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Error(err)
+ return
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Errorf("got %v; want %v", rb[:n], wb)
+ return
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+ writer := func(toggle bool) {
+ defer wg.Done()
+ cm := ipv4.ControlMessage{
+ Src: net.IPv4(127, 0, 0, 1),
+ }
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ t.Error(err)
+ return
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ t.Error(err)
+ return
+ } else if n != len(wb) {
+ t.Errorf("short write: %v", n)
+ return
+ }
+ }
+
+ const N = 10
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go reader()
+ }
+ wg.Add(2 * N)
+ for i := 0; i < 2*N; i++ {
+ go writer(i%2 != 0)
+ }
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go reader()
+ }
+ wg.Wait()
+}
diff --git a/ipv4/sockopt.go b/ipv4/sockopt.go
new file mode 100644
index 0000000..ace37d3
--- /dev/null
+++ b/ipv4/sockopt.go
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+package ipv4
+
+// Sticky socket options
+const (
+ ssoTOS = iota // header field for unicast packet
+ ssoTTL // header field for unicast packet
+ ssoMulticastTTL // header field for multicast packet
+ ssoMulticastInterface // outbound interface for multicast packet
+ ssoMulticastLoopback // loopback for multicast packet
+ ssoReceiveTTL // header field on received packet
+ ssoReceiveDst // header field on received packet
+ ssoReceiveInterface // inbound interface on received packet
+ ssoPacketInfo // incbound or outbound packet path
+ ssoHeaderPrepend // ipv4 header prepend
+ ssoStripHeader // strip ipv4 header
+ ssoICMPFilter // icmp filter
+ ssoJoinGroup // any-source multicast
+ ssoLeaveGroup // any-source multicast
+ ssoJoinSourceGroup // source-specific multicast
+ ssoLeaveSourceGroup // source-specific multicast
+ ssoBlockSourceGroup // any-source or source-specific multicast
+ ssoUnblockSourceGroup // any-source or source-specific multicast
+ ssoMax
+)
+
+// Sticky socket option value types
+const (
+ ssoTypeByte = iota + 1
+ ssoTypeInt
+ ssoTypeInterface
+ ssoTypeICMPFilter
+ ssoTypeIPMreq
+ ssoTypeIPMreqn
+ ssoTypeGroupReq
+ ssoTypeGroupSourceReq
+)
+
+// A sockOpt represents a binding for sticky socket option.
+type sockOpt struct {
+ name int // option name, must be equal or greater than 1
+ typ int // option value type, must be equal or greater than 1
+}
diff --git a/ipv4/sockopt_asmreq.go b/ipv4/sockopt_asmreq.go
new file mode 100644
index 0000000..4a6aa78
--- /dev/null
+++ b/ipv4/sockopt_asmreq.go
@@ -0,0 +1,83 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd windows
+
+package ipv4
+
+import "net"
+
+func setIPMreqInterface(mreq *sysIPMreq, ifi *net.Interface) error {
+ if ifi == nil {
+ return nil
+ }
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip := ifa.IP.To4(); ip != nil {
+ copy(mreq.Interface[:], ip)
+ return nil
+ }
+ case *net.IPNet:
+ if ip := ifa.IP.To4(); ip != nil {
+ copy(mreq.Interface[:], ip)
+ return nil
+ }
+ }
+ }
+ return errNoSuchInterface
+}
+
+func netIP4ToInterface(ip net.IP) (*net.Interface, error) {
+ ift, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifi := range ift {
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip.Equal(ifa.IP) {
+ return &ifi, nil
+ }
+ case *net.IPNet:
+ if ip.Equal(ifa.IP) {
+ return &ifi, nil
+ }
+ }
+ }
+ }
+ return nil, errNoSuchInterface
+}
+
+func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) {
+ if ifi == nil {
+ return net.IPv4zero.To4(), nil
+ }
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifa := range ifat {
+ switch ifa := ifa.(type) {
+ case *net.IPAddr:
+ if ip := ifa.IP.To4(); ip != nil {
+ return ip, nil
+ }
+ case *net.IPNet:
+ if ip := ifa.IP.To4(); ip != nil {
+ return ip, nil
+ }
+ }
+ }
+ return nil, errNoSuchInterface
+}
diff --git a/ipv4/sockopt_asmreq_stub.go b/ipv4/sockopt_asmreq_stub.go
new file mode 100644
index 0000000..4555152
--- /dev/null
+++ b/ipv4/sockopt_asmreq_stub.go
@@ -0,0 +1,21 @@
+// Copyright 2012 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.
+
+// +build !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!windows
+
+package ipv4
+
+import "net"
+
+func setsockoptIPMreq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
+
+func getsockoptInterface(fd, name int) (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+func setsockoptInterface(fd, name int, ifi *net.Interface) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_asmreq_unix.go b/ipv4/sockopt_asmreq_unix.go
new file mode 100644
index 0000000..fefa901
--- /dev/null
+++ b/ipv4/sockopt_asmreq_unix.go
@@ -0,0 +1,46 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func setsockoptIPMreq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ mreq := sysIPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+ if err := setIPMreqInterface(&mreq, ifi); err != nil {
+ return err
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, name, unsafe.Pointer(&mreq), sysSizeofIPMreq))
+}
+
+func getsockoptInterface(fd, name int) (*net.Interface, error) {
+ var b [4]byte
+ l := sysSockoptLen(4)
+ if err := getsockopt(fd, iana.ProtocolIP, name, unsafe.Pointer(&b[0]), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3]))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptInterface(fd, name int, ifi *net.Interface) error {
+ ip, err := netInterfaceToIP4(ifi)
+ if err != nil {
+ return err
+ }
+ var b [4]byte
+ copy(b[:], ip)
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, name, unsafe.Pointer(&b[0]), sysSockoptLen(4)))
+}
diff --git a/ipv4/sockopt_asmreq_windows.go b/ipv4/sockopt_asmreq_windows.go
new file mode 100644
index 0000000..431930d
--- /dev/null
+++ b/ipv4/sockopt_asmreq_windows.go
@@ -0,0 +1,45 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func setsockoptIPMreq(fd syscall.Handle, name int, ifi *net.Interface, grp net.IP) error {
+ mreq := sysIPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+ if err := setIPMreqInterface(&mreq, ifi); err != nil {
+ return err
+ }
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, iana.ProtocolIP, int32(name), (*byte)(unsafe.Pointer(&mreq)), int32(sysSizeofIPMreq)))
+}
+
+func getsockoptInterface(fd syscall.Handle, name int) (*net.Interface, error) {
+ var b [4]byte
+ l := int32(4)
+ if err := syscall.Getsockopt(fd, iana.ProtocolIP, int32(name), (*byte)(unsafe.Pointer(&b[0])), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3]))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptInterface(fd syscall.Handle, name int, ifi *net.Interface) error {
+ ip, err := netInterfaceToIP4(ifi)
+ if err != nil {
+ return err
+ }
+ var b [4]byte
+ copy(b[:], ip)
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, iana.ProtocolIP, int32(name), (*byte)(unsafe.Pointer(&b[0])), 4))
+}
diff --git a/ipv4/sockopt_asmreqn_stub.go b/ipv4/sockopt_asmreqn_stub.go
new file mode 100644
index 0000000..332f403
--- /dev/null
+++ b/ipv4/sockopt_asmreqn_stub.go
@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+// +build !darwin,!freebsd,!linux,!windows
+
+package ipv4
+
+import "net"
+
+func getsockoptIPMreqn(fd, name int) (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+func setsockoptIPMreqn(fd, name int, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_asmreqn_unix.go b/ipv4/sockopt_asmreqn_unix.go
new file mode 100644
index 0000000..92c8e34
--- /dev/null
+++ b/ipv4/sockopt_asmreqn_unix.go
@@ -0,0 +1,42 @@
+// Copyright 2014 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.
+
+// +build darwin freebsd linux
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func getsockoptIPMreqn(fd, name int) (*net.Interface, error) {
+ var mreqn sysIPMreqn
+ l := sysSockoptLen(sysSizeofIPMreqn)
+ if err := getsockopt(fd, iana.ProtocolIP, name, unsafe.Pointer(&mreqn), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ if mreqn.Ifindex == 0 {
+ return nil, nil
+ }
+ ifi, err := net.InterfaceByIndex(int(mreqn.Ifindex))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setsockoptIPMreqn(fd, name int, ifi *net.Interface, grp net.IP) error {
+ var mreqn sysIPMreqn
+ if ifi != nil {
+ mreqn.Ifindex = int32(ifi.Index)
+ }
+ if grp != nil {
+ mreqn.Multiaddr = [4]byte{grp[0], grp[1], grp[2], grp[3]}
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, name, unsafe.Pointer(&mreqn), sysSizeofIPMreqn))
+}
diff --git a/ipv4/sockopt_ssmreq_stub.go b/ipv4/sockopt_ssmreq_stub.go
new file mode 100644
index 0000000..8546524
--- /dev/null
+++ b/ipv4/sockopt_ssmreq_stub.go
@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+// +build !darwin,!freebsd,!linux
+
+package ipv4
+
+import "net"
+
+func setsockoptGroupReq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
+
+func setsockoptGroupSourceReq(fd, name int, ifi *net.Interface, grp, src net.IP) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_ssmreq_unix.go b/ipv4/sockopt_ssmreq_unix.go
new file mode 100644
index 0000000..6f647bc
--- /dev/null
+++ b/ipv4/sockopt_ssmreq_unix.go
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+// +build darwin freebsd linux
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+var freebsd32o64 bool
+
+func setsockoptGroupReq(fd, name int, ifi *net.Interface, grp net.IP) error {
+ var gr sysGroupReq
+ if ifi != nil {
+ gr.Interface = uint32(ifi.Index)
+ }
+ gr.setGroup(grp)
+ var p unsafe.Pointer
+ var l sysSockoptLen
+ if freebsd32o64 {
+ var d [sysSizeofGroupReq + 4]byte
+ s := (*[sysSizeofGroupReq]byte)(unsafe.Pointer(&gr))
+ copy(d[:4], s[:4])
+ copy(d[8:], s[4:])
+ p = unsafe.Pointer(&d[0])
+ l = sysSizeofGroupReq + 4
+ } else {
+ p = unsafe.Pointer(&gr)
+ l = sysSizeofGroupReq
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, name, p, l))
+}
+
+func setsockoptGroupSourceReq(fd, name int, ifi *net.Interface, grp, src net.IP) error {
+ var gsr sysGroupSourceReq
+ if ifi != nil {
+ gsr.Interface = uint32(ifi.Index)
+ }
+ gsr.setSourceGroup(grp, src)
+ var p unsafe.Pointer
+ var l sysSockoptLen
+ if freebsd32o64 {
+ var d [sysSizeofGroupSourceReq + 4]byte
+ s := (*[sysSizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))
+ copy(d[:4], s[:4])
+ copy(d[8:], s[4:])
+ p = unsafe.Pointer(&d[0])
+ l = sysSizeofGroupSourceReq + 4
+ } else {
+ p = unsafe.Pointer(&gsr)
+ l = sysSizeofGroupSourceReq
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, name, p, l))
+}
diff --git a/ipv4/sockopt_stub.go b/ipv4/sockopt_stub.go
new file mode 100644
index 0000000..9d19f5d
--- /dev/null
+++ b/ipv4/sockopt_stub.go
@@ -0,0 +1,11 @@
+// Copyright 2012 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+func setInt(fd int, opt *sockOpt, v int) error {
+ return errOpNoSupport
+}
diff --git a/ipv4/sockopt_unix.go b/ipv4/sockopt_unix.go
new file mode 100644
index 0000000..50cdbd8
--- /dev/null
+++ b/ipv4/sockopt_unix.go
@@ -0,0 +1,122 @@
+// Copyright 2012 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func getInt(fd int, opt *sockOpt) (int, error) {
+ if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
+ return 0, errOpNoSupport
+ }
+ var i int32
+ var b byte
+ p := unsafe.Pointer(&i)
+ l := sysSockoptLen(4)
+ if opt.typ == ssoTypeByte {
+ p = unsafe.Pointer(&b)
+ l = sysSockoptLen(1)
+ }
+ if err := getsockopt(fd, iana.ProtocolIP, opt.name, p, &l); err != nil {
+ return 0, os.NewSyscallError("getsockopt", err)
+ }
+ if opt.typ == ssoTypeByte {
+ return int(b), nil
+ }
+ return int(i), nil
+}
+
+func setInt(fd int, opt *sockOpt, v int) error {
+ if opt.name < 1 || (opt.typ != ssoTypeByte && opt.typ != ssoTypeInt) {
+ return errOpNoSupport
+ }
+ i := int32(v)
+ var b byte
+ p := unsafe.Pointer(&i)
+ l := sysSockoptLen(4)
+ if opt.typ == ssoTypeByte {
+ b = byte(v)
+ p = unsafe.Pointer(&b)
+ l = sysSockoptLen(1)
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolIP, opt.name, p, l))
+}
+
+func getInterface(fd int, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 {
+ return nil, errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeInterface:
+ return getsockoptInterface(fd, opt.name)
+ case ssoTypeIPMreqn:
+ return getsockoptIPMreqn(fd, opt.name)
+ default:
+ return nil, errOpNoSupport
+ }
+}
+
+func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 {
+ return errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeInterface:
+ return setsockoptInterface(fd, opt.name, ifi)
+ case ssoTypeIPMreqn:
+ return setsockoptIPMreqn(fd, opt.name, ifi, nil)
+ default:
+ return errOpNoSupport
+ }
+}
+
+func getICMPFilter(fd int, opt *sockOpt) (*ICMPFilter, error) {
+ if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
+ return nil, errOpNoSupport
+ }
+ var f ICMPFilter
+ l := sysSockoptLen(sysSizeofICMPFilter)
+ if err := getsockopt(fd, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.sysICMPFilter), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ return &f, nil
+}
+
+func setICMPFilter(fd int, opt *sockOpt, f *ICMPFilter) error {
+ if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
+ return errOpNoSupport
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, iana.ProtocolReserved, opt.name, unsafe.Pointer(&f.sysICMPFilter), sysSizeofICMPFilter))
+}
+
+func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 {
+ return errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeIPMreq:
+ return setsockoptIPMreq(fd, opt.name, ifi, grp)
+ case ssoTypeIPMreqn:
+ return setsockoptIPMreqn(fd, opt.name, ifi, grp)
+ case ssoTypeGroupReq:
+ return setsockoptGroupReq(fd, opt.name, ifi, grp)
+ default:
+ return errOpNoSupport
+ }
+}
+
+func setSourceGroup(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ if opt.name < 1 || opt.typ != ssoTypeGroupSourceReq {
+ return errOpNoSupport
+ }
+ return setsockoptGroupSourceReq(fd, opt.name, ifi, grp, src)
+}
diff --git a/ipv4/sockopt_windows.go b/ipv4/sockopt_windows.go
new file mode 100644
index 0000000..c4c2441
--- /dev/null
+++ b/ipv4/sockopt_windows.go
@@ -0,0 +1,68 @@
+// Copyright 2012 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.
+
+package ipv4
+
+import (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func getInt(fd syscall.Handle, opt *sockOpt) (int, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return 0, errOpNoSupport
+ }
+ var i int32
+ l := int32(4)
+ if err := syscall.Getsockopt(fd, iana.ProtocolIP, int32(opt.name), (*byte)(unsafe.Pointer(&i)), &l); err != nil {
+ return 0, os.NewSyscallError("getsockopt", err)
+ }
+ return int(i), nil
+}
+
+func setInt(fd syscall.Handle, opt *sockOpt, v int) error {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return errOpNoSupport
+ }
+ i := int32(v)
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, iana.ProtocolIP, int32(opt.name), (*byte)(unsafe.Pointer(&i)), 4))
+}
+
+func getInterface(fd syscall.Handle, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return nil, errOpNoSupport
+ }
+ return getsockoptInterface(fd, opt.name)
+}
+
+func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return errOpNoSupport
+ }
+ return setsockoptInterface(fd, opt.name, ifi)
+}
+
+func getICMPFilter(fd syscall.Handle, opt *sockOpt) (*ICMPFilter, error) {
+ return nil, errOpNoSupport
+}
+
+func setICMPFilter(fd syscall.Handle, opt *sockOpt, f *ICMPFilter) error {
+ return errOpNoSupport
+}
+
+func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 || opt.typ != ssoTypeIPMreq {
+ return errOpNoSupport
+ }
+ return setsockoptIPMreq(fd, opt.name, ifi, grp)
+}
+
+func setSourceGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ // TODO(mikio): implement this
+ return errOpNoSupport
+}
diff --git a/ipv4/sys_bsd.go b/ipv4/sys_bsd.go
new file mode 100644
index 0000000..a669a44
--- /dev/null
+++ b/ipv4/sys_bsd.go
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+// +build dragonfly netbsd
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
+ ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
+ ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
diff --git a/ipv4/sys_darwin.go b/ipv4/sys_darwin.go
new file mode 100644
index 0000000..3f34734
--- /dev/null
+++ b/ipv4/sys_darwin.go
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
+ ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
+ ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoStripHeader: {sysIP_STRIPHDR, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
+
+func init() {
+ // Seems like kern.osreldate is veiled on latest OS X. We use
+ // kern.osrelease instead.
+ osver, err := syscall.Sysctl("kern.osrelease")
+ if err != nil {
+ return
+ }
+ var i int
+ for i = range osver {
+ if osver[i] == '.' {
+ break
+ }
+ }
+ // The IP_PKTINFO and protocol-independent multicast API were
+ // introduced in OS X 10.7 (Darwin 11.0.0). But it looks like
+ // those features require OS X 10.8 (Darwin 12.0.0) and above.
+ // See http://support.apple.com/kb/HT1633.
+ if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '2' {
+ ctlOpts[ctlPacketInfo].name = sysIP_PKTINFO
+ ctlOpts[ctlPacketInfo].length = sysSizeofInetPktinfo
+ ctlOpts[ctlPacketInfo].marshal = marshalPacketInfo
+ ctlOpts[ctlPacketInfo].parse = parsePacketInfo
+ sockOpts[ssoPacketInfo].name = sysIP_RECVPKTINFO
+ sockOpts[ssoPacketInfo].typ = ssoTypeInt
+ sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn
+ sockOpts[ssoJoinGroup].name = sysMCAST_JOIN_GROUP
+ sockOpts[ssoJoinGroup].typ = ssoTypeGroupReq
+ sockOpts[ssoLeaveGroup].name = sysMCAST_LEAVE_GROUP
+ sockOpts[ssoLeaveGroup].typ = ssoTypeGroupReq
+ sockOpts[ssoJoinSourceGroup].name = sysMCAST_JOIN_SOURCE_GROUP
+ sockOpts[ssoJoinSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoLeaveSourceGroup].name = sysMCAST_LEAVE_SOURCE_GROUP
+ sockOpts[ssoLeaveSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoBlockSourceGroup].name = sysMCAST_BLOCK_SOURCE
+ sockOpts[ssoBlockSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoUnblockSourceGroup].name = sysMCAST_UNBLOCK_SOURCE
+ sockOpts[ssoUnblockSourceGroup].typ = ssoTypeGroupSourceReq
+ }
+}
+
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = uint32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Pad_cgo_0[0]))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Pad_cgo_0[0]))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Pad_cgo_1[0]))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv4/sys_freebsd.go b/ipv4/sys_freebsd.go
new file mode 100644
index 0000000..09ef491
--- /dev/null
+++ b/ipv4/sys_freebsd.go
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+package ipv4
+
+import (
+ "net"
+ "runtime"
+ "strings"
+ "syscall"
+ "unsafe"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
+ ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
+ ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+ ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+ ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoLeaveSourceGroup: {sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoBlockSourceGroup: {sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+ ssoUnblockSourceGroup: {sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+ }
+)
+
+func init() {
+ freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate")
+ if freebsdVersion >= 1000000 {
+ sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn
+ }
+ if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
+ archs, _ := syscall.Sysctl("kern.supported_archs")
+ for _, s := range strings.Fields(archs) {
+ if s == "amd64" {
+ freebsd32o64 = true
+ break
+ }
+ }
+ }
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Group))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Group))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Source))
+ sa.Len = sysSizeofSockaddrInet
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv4/sys_linux.go b/ipv4/sys_linux.go
new file mode 100644
index 0000000..b1f3878
--- /dev/null
+++ b/ipv4/sys_linux.go
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTTL: {sysIP_TTL, 1, marshalTTL, parseTTL},
+ ctlPacketInfo: {sysIP_PKTINFO, sysSizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeInt},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeIPMreqn},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoPacketInfo: {sysIP_PKTINFO, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoICMPFilter: {sysICMP_FILTER, ssoTypeICMPFilter},
+ ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+ ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+ ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoLeaveSourceGroup: {sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoBlockSourceGroup: {sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+ ssoUnblockSourceGroup: {sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+ }
+)
+
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = int32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Group))
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Group))
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Source))
+ sa.Family = syscall.AF_INET
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv4/sys_openbsd.go b/ipv4/sys_openbsd.go
new file mode 100644
index 0000000..550f208
--- /dev/null
+++ b/ipv4/sys_openbsd.go
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+package ipv4
+
+import (
+ "net"
+ "syscall"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
+ ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
+ ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeByte},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeByte},
+ ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt},
+ ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt},
+ ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt},
+ ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
diff --git a/ipv4/sys_stub.go b/ipv4/sys_stub.go
new file mode 100644
index 0000000..efbcc47
--- /dev/null
+++ b/ipv4/sys_stub.go
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+// +build nacl plan9 solaris
+
+package ipv4
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{}
+
+ sockOpts = [ssoMax]sockOpt{}
+)
diff --git a/ipv4/sys_windows.go b/ipv4/sys_windows.go
new file mode 100644
index 0000000..466489f
--- /dev/null
+++ b/ipv4/sys_windows.go
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+package ipv4
+
+const (
+ // See ws2tcpip.h.
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_DONTFRAGMENT = 0xe
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0xf
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x10
+ sysIP_PKTINFO = 0x13
+
+ sysSizeofInetPktinfo = 0x8
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqSource = 0xc
+)
+
+type sysInetPktinfo struct {
+ Addr [4]byte
+ Ifindex int32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte
+ Interface [4]byte
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte
+ Sourceaddr [4]byte
+ Interface [4]byte
+}
+
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
+var (
+ ctlOpts = [ctlMax]ctlOpt{}
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTOS: {sysIP_TOS, ssoTypeInt},
+ ssoTTL: {sysIP_TTL, ssoTypeInt},
+ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeInt},
+ ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt},
+ ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq},
+ ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq},
+ }
+)
+
+func (pi *sysInetPktinfo) setIfindex(i int) {
+ pi.Ifindex = int32(i)
+}
diff --git a/ipv4/syscall_linux_386.go b/ipv4/syscall_linux_386.go
new file mode 100644
index 0000000..ab4ad04
--- /dev/null
+++ b/ipv4/syscall_linux_386.go
@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+package ipv4
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sysGETSOCKOPT = 0xf
+ sysSETSOCKOPT = 0xe
+)
+
+func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, errno := socketcall(sysGETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, errno := socketcall(sysSETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv4/syscall_unix.go b/ipv4/syscall_unix.go
new file mode 100644
index 0000000..5fe8e83
--- /dev/null
+++ b/ipv4/syscall_unix.go
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+// +build darwin dragonfly freebsd linux,!386 netbsd openbsd
+
+package ipv4
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv4/thunk_linux_386.s b/ipv4/thunk_linux_386.s
new file mode 100644
index 0000000..daa78bc
--- /dev/null
+++ b/ipv4/thunk_linux_386.s
@@ -0,0 +1,8 @@
+// Copyright 2014 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.
+
+// +build go1.2
+
+TEXT ·socketcall(SB),4,$0-36
+ JMP syscall·socketcall(SB)
diff --git a/ipv4/unicast_test.go b/ipv4/unicast_test.go
new file mode 100644
index 0000000..255096a
--- /dev/null
+++ b/ipv4/unicast_test.go
@@ -0,0 +1,250 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ c, err := net.ListenPacket("udp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ dst, err := net.ResolveUDPAddr("udp4", c.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+ wb := []byte("HELLO-R-U-THERE")
+
+ for i, toggle := range []bool{true, false, true} {
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ p.SetTTL(i + 1)
+ if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, err := p.WriteTo(wb, nil, dst); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Fatalf("got %v; want %v", rb[:n], wb)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+}
+
+func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ p := ipv4.NewPacketConn(c)
+ defer p.Close()
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
+ for i, toggle := range []bool{true, false, true} {
+ wb, err := (&icmp.Message{
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ p.SetTTL(i + 1)
+ if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, err := p.WriteTo(wb, nil, dst); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ loop:
+ if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
+ // On Linux we must handle own sent packets.
+ goto loop
+ }
+ if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+}
+
+func TestRawConnReadWriteUnicastICMP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ dst, err := net.ResolveIPAddr("ip4", "127.0.0.1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
+ for i, toggle := range []bool{true, false, true} {
+ wb, err := (&icmp.Message{
+ Type: ipv4.ICMPTypeEcho, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wh := &ipv4.Header{
+ Version: ipv4.Version,
+ Len: ipv4.HeaderLen,
+ TOS: i + 1,
+ TotalLen: ipv4.HeaderLen + len(wb),
+ TTL: i + 1,
+ Protocol: 1,
+ Dst: dst.IP,
+ }
+ if err := r.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := r.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if err := r.WriteTo(wh, wb, nil); err != nil {
+ t.Fatal(err)
+ }
+ rb := make([]byte, ipv4.HeaderLen+128)
+ loop:
+ if err := r.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if _, b, cm, err := r.ReadFrom(rb); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
+ // On Linux we must handle own sent packets.
+ goto loop
+ }
+ if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+}
diff --git a/ipv4/unicastsockopt_test.go b/ipv4/unicastsockopt_test.go
new file mode 100644
index 0000000..25606f2
--- /dev/null
+++ b/ipv4/unicastsockopt_test.go
@@ -0,0 +1,139 @@
+// Copyright 2012 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.
+
+package ipv4_test
+
+import (
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv4"
+)
+
+func TestConnUnicastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ ln, err := net.Listen("tcp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool)
+ go acceptor(t, ln, done)
+
+ c, err := net.Dial("tcp4", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ testUnicastSocketOptions(t, ipv4.NewConn(c))
+
+ <-done
+}
+
+var packetConnUnicastSocketOptionTests = []struct {
+ net, proto, addr string
+}{
+ {"udp4", "", "127.0.0.1:0"},
+ {"ip4", ":icmp", "127.0.0.1"},
+}
+
+func TestPacketConnUnicastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ m, ok := nettest.SupportsRawIPSocket()
+ for _, tt := range packetConnUnicastSocketOptionTests {
+ if tt.net == "ip4" && !ok {
+ t.Log(m)
+ continue
+ }
+ c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ testUnicastSocketOptions(t, ipv4.NewPacketConn(c))
+ }
+}
+
+func TestRawConnUnicastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ r, err := ipv4.NewRawConn(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ testUnicastSocketOptions(t, r)
+}
+
+type testIPv4UnicastConn interface {
+ TOS() (int, error)
+ SetTOS(int) error
+ TTL() (int, error)
+ SetTTL(int) error
+}
+
+func testUnicastSocketOptions(t *testing.T, c testIPv4UnicastConn) {
+ tos := iana.DiffServCS0 | iana.NotECNTransport
+ switch runtime.GOOS {
+ case "windows":
+ // IP_TOS option is supported on Windows 8 and beyond.
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ if err := c.SetTOS(tos); err != nil {
+ t.Fatal(err)
+ }
+ if v, err := c.TOS(); err != nil {
+ t.Fatal(err)
+ } else if v != tos {
+ t.Fatalf("got %v; want %v", v, tos)
+ }
+ const ttl = 255
+ if err := c.SetTTL(ttl); err != nil {
+ t.Fatal(err)
+ }
+ if v, err := c.TTL(); err != nil {
+ t.Fatal(err)
+ } else if v != ttl {
+ t.Fatalf("got %v; want %v", v, ttl)
+ }
+}
diff --git a/ipv4/zsys_darwin.go b/ipv4/zsys_darwin.go
new file mode 100644
index 0000000..087c639
--- /dev/null
+++ b/ipv4/zsys_darwin.go
@@ -0,0 +1,99 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_darwin.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_STRIPHDR = 0x17
+ sysIP_RECVTTL = 0x18
+ sysIP_BOUND_IF = 0x19
+ sysIP_PKTINFO = 0x1a
+ sysIP_RECVPKTINFO = 0x1a
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_MULTICAST_VIF = 0xe
+ sysIP_MULTICAST_IFINDEX = 0x42
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
+ sysIP_BLOCK_SOURCE = 0x48
+ sysIP_UNBLOCK_SOURCE = 0x49
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type sysInetPktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte /* in_addr */
+ Sourceaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [128]byte
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [128]byte
+ Pad_cgo_1 [128]byte
+}
diff --git a/ipv4/zsys_dragonfly.go b/ipv4/zsys_dragonfly.go
new file mode 100644
index 0000000..f5c9cce
--- /dev/null
+++ b/ipv4/zsys_dragonfly.go
@@ -0,0 +1,33 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_dragonfly.go
+
+// +build dragonfly
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_RECVTTL = 0x41
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_MULTICAST_VIF = 0xe
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+
+ sysSizeofIPMreq = 0x8
+)
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
diff --git a/ipv4/zsys_freebsd_386.go b/ipv4/zsys_freebsd_386.go
new file mode 100644
index 0000000..6fd67e1
--- /dev/null
+++ b/ipv4/zsys_freebsd_386.go
@@ -0,0 +1,93 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_SENDSRCADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_ONESBCAST = 0x17
+ sysIP_BINDANY = 0x18
+ sysIP_RECVTTL = 0x41
+ sysIP_MINTTL = 0x42
+ sysIP_DONTFRAG = 0x43
+ sysIP_RECVTOS = 0x44
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_MULTICAST_VIF = 0xe
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
+ sysIP_BLOCK_SOURCE = 0x48
+ sysIP_UNBLOCK_SOURCE = 0x49
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte /* in_addr */
+ Sourceaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
diff --git a/ipv4/zsys_freebsd_amd64.go b/ipv4/zsys_freebsd_amd64.go
new file mode 100644
index 0000000..ebac6d7
--- /dev/null
+++ b/ipv4/zsys_freebsd_amd64.go
@@ -0,0 +1,95 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_SENDSRCADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_ONESBCAST = 0x17
+ sysIP_BINDANY = 0x18
+ sysIP_RECVTTL = 0x41
+ sysIP_MINTTL = 0x42
+ sysIP_DONTFRAG = 0x43
+ sysIP_RECVTOS = 0x44
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_MULTICAST_VIF = 0xe
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
+ sysIP_BLOCK_SOURCE = 0x48
+ sysIP_UNBLOCK_SOURCE = 0x49
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte /* in_addr */
+ Sourceaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
diff --git a/ipv4/zsys_freebsd_arm.go b/ipv4/zsys_freebsd_arm.go
new file mode 100644
index 0000000..ebac6d7
--- /dev/null
+++ b/ipv4/zsys_freebsd_arm.go
@@ -0,0 +1,95 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_SENDSRCADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_ONESBCAST = 0x17
+ sysIP_BINDANY = 0x18
+ sysIP_RECVTTL = 0x41
+ sysIP_MINTTL = 0x42
+ sysIP_DONTFRAG = 0x43
+ sysIP_RECVTOS = 0x44
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+ sysIP_MULTICAST_VIF = 0xe
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
+ sysIP_BLOCK_SOURCE = 0x48
+ sysIP_UNBLOCK_SOURCE = 0x49
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte /* in_addr */
+ Sourceaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
diff --git a/ipv4/zsys_linux_386.go b/ipv4/zsys_linux_386.go
new file mode 100644
index 0000000..fc7a9eb
--- /dev/null
+++ b/ipv4/zsys_linux_386.go
@@ -0,0 +1,130 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_linux_amd64.go b/ipv4/zsys_linux_amd64.go
new file mode 100644
index 0000000..e324b81
--- /dev/null
+++ b/ipv4/zsys_linux_amd64.go
@@ -0,0 +1,132 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_linux_arm.go b/ipv4/zsys_linux_arm.go
new file mode 100644
index 0000000..fc7a9eb
--- /dev/null
+++ b/ipv4/zsys_linux_arm.go
@@ -0,0 +1,130 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_linux_arm64.go b/ipv4/zsys_linux_arm64.go
new file mode 100644
index 0000000..ce4194a
--- /dev/null
+++ b/ipv4/zsys_linux_arm64.go
@@ -0,0 +1,134 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,arm64
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_linux_ppc64.go b/ipv4/zsys_linux_ppc64.go
new file mode 100644
index 0000000..9fe5ee2
--- /dev/null
+++ b/ipv4/zsys_linux_ppc64.go
@@ -0,0 +1,134 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,ppc64
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_linux_ppc64le.go b/ipv4/zsys_linux_ppc64le.go
new file mode 100644
index 0000000..3891f54
--- /dev/null
+++ b/ipv4/zsys_linux_ppc64le.go
@@ -0,0 +1,134 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,ppc64le
+
+package ipv4
+
+const (
+ sysIP_TOS = 0x1
+ sysIP_TTL = 0x2
+ sysIP_HDRINCL = 0x3
+ sysIP_OPTIONS = 0x4
+ sysIP_ROUTER_ALERT = 0x5
+ sysIP_RECVOPTS = 0x6
+ sysIP_RETOPTS = 0x7
+ sysIP_PKTINFO = 0x8
+ sysIP_PKTOPTIONS = 0x9
+ sysIP_MTU_DISCOVER = 0xa
+ sysIP_RECVERR = 0xb
+ sysIP_RECVTTL = 0xc
+ sysIP_RECVTOS = 0xd
+ sysIP_MTU = 0xe
+ sysIP_FREEBIND = 0xf
+ sysIP_TRANSPARENT = 0x13
+ sysIP_RECVRETOPTS = 0x7
+ sysIP_ORIGDSTADDR = 0x14
+ sysIP_RECVORIGDSTADDR = 0x14
+ sysIP_MINTTL = 0x15
+ sysIP_NODEFRAG = 0x16
+ sysIP_UNICAST_IF = 0x32
+
+ sysIP_MULTICAST_IF = 0x20
+ sysIP_MULTICAST_TTL = 0x21
+ sysIP_MULTICAST_LOOP = 0x22
+ sysIP_ADD_MEMBERSHIP = 0x23
+ sysIP_DROP_MEMBERSHIP = 0x24
+ sysIP_UNBLOCK_SOURCE = 0x25
+ sysIP_BLOCK_SOURCE = 0x26
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x27
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x28
+ sysIP_MSFILTER = 0x29
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIP_MULTICAST_ALL = 0x31
+
+ sysICMP_FILTER = 0x1
+
+ sysSO_EE_ORIGIN_NONE = 0x0
+ sysSO_EE_ORIGIN_LOCAL = 0x1
+ sysSO_EE_ORIGIN_ICMP = 0x2
+ sysSO_EE_ORIGIN_ICMP6 = 0x3
+ sysSO_EE_ORIGIN_TXSTATUS = 0x4
+ sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet = 0x10
+ sysSizeofInetPktinfo = 0xc
+ sysSizeofSockExtendedErr = 0x10
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqn = 0xc
+ sysSizeofIPMreqSource = 0xc
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPFilter = 0x4
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet struct {
+ Family uint16
+ Port uint16
+ Addr [4]byte /* in_addr */
+ X__pad [8]uint8
+}
+
+type sysInetPktinfo struct {
+ Ifindex int32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysSockExtendedErr struct {
+ Errno uint32
+ Origin uint8
+ Type uint8
+ Code uint8
+ Pad uint8
+ Info uint32
+ Data uint32
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type sysIPMreqSource struct {
+ Multiaddr uint32
+ Interface uint32
+ Sourceaddr uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPFilter struct {
+ Data uint32
+}
diff --git a/ipv4/zsys_netbsd.go b/ipv4/zsys_netbsd.go
new file mode 100644
index 0000000..8a440eb
--- /dev/null
+++ b/ipv4/zsys_netbsd.go
@@ -0,0 +1,30 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_netbsd.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x14
+ sysIP_RECVTTL = 0x17
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+
+ sysSizeofIPMreq = 0x8
+)
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
diff --git a/ipv4/zsys_openbsd.go b/ipv4/zsys_openbsd.go
new file mode 100644
index 0000000..fd522b5
--- /dev/null
+++ b/ipv4/zsys_openbsd.go
@@ -0,0 +1,30 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_openbsd.go
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x1e
+ sysIP_RECVTTL = 0x1f
+
+ sysIP_MULTICAST_IF = 0x9
+ sysIP_MULTICAST_TTL = 0xa
+ sysIP_MULTICAST_LOOP = 0xb
+ sysIP_ADD_MEMBERSHIP = 0xc
+ sysIP_DROP_MEMBERSHIP = 0xd
+
+ sysSizeofIPMreq = 0x8
+)
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
diff --git a/ipv4/zsys_solaris.go b/ipv4/zsys_solaris.go
new file mode 100644
index 0000000..d7c2334
--- /dev/null
+++ b/ipv4/zsys_solaris.go
@@ -0,0 +1,60 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_solaris.go
+
+// +build solaris
+
+package ipv4
+
+const (
+ sysIP_OPTIONS = 0x1
+ sysIP_HDRINCL = 0x2
+ sysIP_TOS = 0x3
+ sysIP_TTL = 0x4
+ sysIP_RECVOPTS = 0x5
+ sysIP_RECVRETOPTS = 0x6
+ sysIP_RECVDSTADDR = 0x7
+ sysIP_RETOPTS = 0x8
+ sysIP_RECVIF = 0x9
+ sysIP_RECVSLLA = 0xa
+ sysIP_RECVTTL = 0xb
+ sysIP_NEXTHOP = 0x19
+ sysIP_PKTINFO = 0x1a
+ sysIP_RECVPKTINFO = 0x1a
+ sysIP_DONTFRAG = 0x1b
+ sysIP_BOUND_IF = 0x41
+ sysIP_UNSPEC_SRC = 0x42
+ sysIP_BROADCAST_TTL = 0x43
+ sysIP_DHCPINIT_IF = 0x45
+
+ sysIP_MULTICAST_IF = 0x10
+ sysIP_MULTICAST_TTL = 0x11
+ sysIP_MULTICAST_LOOP = 0x12
+ sysIP_ADD_MEMBERSHIP = 0x13
+ sysIP_DROP_MEMBERSHIP = 0x14
+ sysIP_BLOCK_SOURCE = 0x15
+ sysIP_UNBLOCK_SOURCE = 0x16
+ sysIP_ADD_SOURCE_MEMBERSHIP = 0x17
+ sysIP_DROP_SOURCE_MEMBERSHIP = 0x18
+
+ sysSizeofInetPktinfo = 0xc
+
+ sysSizeofIPMreq = 0x8
+ sysSizeofIPMreqSource = 0xc
+)
+
+type sysInetPktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
+type sysIPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type sysIPMreqSource struct {
+ Multiaddr [4]byte /* in_addr */
+ Sourceaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
diff --git a/ipv6/control.go b/ipv6/control.go
new file mode 100644
index 0000000..27f1d2f
--- /dev/null
+++ b/ipv6/control.go
@@ -0,0 +1,92 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+)
+
+var (
+ errMissingAddress = errors.New("missing address")
+ errInvalidConnType = errors.New("invalid conn type")
+ errNoSuchInterface = errors.New("no such interface")
+)
+
+// Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the
+// former still support RFC 2292 only. Please be aware that almost
+// all protocol implementations prohibit using a combination of RFC
+// 2292 and RFC 3542 for some practical reasons.
+
+type rawOpt struct {
+ sync.RWMutex
+ cflags ControlFlags
+}
+
+func (c *rawOpt) set(f ControlFlags) { c.cflags |= f }
+func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f }
+func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
+
+// A ControlFlags represents per packet basis IP-level socket option
+// control flags.
+type ControlFlags uint
+
+const (
+ FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
+ FlagHopLimit // pass the hop limit on the received packet
+ FlagSrc // pass the source address on the received packet
+ FlagDst // pass the destination address on the received packet
+ FlagInterface // pass the interface index on the received packet
+ FlagPathMTU // pass the path MTU on the received packet path
+)
+
+const flagPacketInfo = FlagDst | FlagInterface
+
+// A ControlMessage represents per packet basis IP-level socket
+// options.
+type ControlMessage struct {
+ // Receiving socket options: SetControlMessage allows to
+ // receive the options from the protocol stack using ReadFrom
+ // method of PacketConn.
+ //
+ // Specifying socket options: ControlMessage for WriteTo
+ // method of PacketConn allows to send the options to the
+ // protocol stack.
+ //
+ TrafficClass int // traffic class, must be 1 <= value <= 255 when specifying
+ HopLimit int // hop limit, must be 1 <= value <= 255 when specifying
+ Src net.IP // source address, specifying only
+ Dst net.IP // destination address, receiving only
+ IfIndex int // interface index, must be 1 <= value when specifying
+ NextHop net.IP // next hop address, specifying only
+ MTU int // path MTU, receiving only
+}
+
+func (cm *ControlMessage) String() string {
+ if cm == nil {
+ return "<nil>"
+ }
+ return fmt.Sprintf("tclass: %#x, hoplim: %v, src: %v, dst: %v, ifindex: %v, nexthop: %v, mtu: %v", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
+}
+
+// Ancillary data socket options
+const (
+ ctlTrafficClass = iota // header field
+ ctlHopLimit // header field
+ ctlPacketInfo // inbound or outbound packet path
+ ctlNextHop // nexthop
+ ctlPathMTU // path mtu
+ ctlMax
+)
+
+// A ctlOpt represents a binding for ancillary data socket option.
+type ctlOpt struct {
+ name int // option name, must be equal or greater than 1
+ length int // option length
+ marshal func([]byte, *ControlMessage) []byte
+ parse func(*ControlMessage, []byte)
+}
diff --git a/ipv6/control_rfc2292_unix.go b/ipv6/control_rfc2292_unix.go
new file mode 100644
index 0000000..ce201ce
--- /dev/null
+++ b/ipv6/control_rfc2292_unix.go
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+// +build darwin
+
+package ipv6
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func marshal2292HopLimit(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_2292HOPLIMIT
+ m.SetLen(syscall.CmsgLen(4))
+ if cm != nil {
+ data := b[syscall.CmsgLen(0):]
+ // TODO(mikio): fix potential misaligned memory access
+ *(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
+ }
+ return b[syscall.CmsgSpace(4):]
+}
+
+func marshal2292PacketInfo(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_2292PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
+ if cm != nil {
+ pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+ if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+ copy(pi.Addr[:], ip)
+ }
+ if cm.IfIndex > 0 {
+ pi.setIfindex(cm.IfIndex)
+ }
+ }
+ return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
+}
+
+func marshal2292NextHop(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_2292NEXTHOP
+ m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
+ if cm != nil {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+ sa.setSockaddr(cm.NextHop, cm.IfIndex)
+ }
+ return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
+}
diff --git a/ipv6/control_rfc3542_unix.go b/ipv6/control_rfc3542_unix.go
new file mode 100644
index 0000000..e55c4aa
--- /dev/null
+++ b/ipv6/control_rfc3542_unix.go
@@ -0,0 +1,103 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func marshalTrafficClass(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_TCLASS
+ m.SetLen(syscall.CmsgLen(4))
+ if cm != nil {
+ data := b[syscall.CmsgLen(0):]
+ // TODO(mikio): fix potential misaligned memory access
+ *(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.TrafficClass)
+ }
+ return b[syscall.CmsgSpace(4):]
+}
+
+func parseTrafficClass(cm *ControlMessage, b []byte) {
+ // TODO(mikio): fix potential misaligned memory access
+ cm.TrafficClass = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
+}
+
+func marshalHopLimit(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_HOPLIMIT
+ m.SetLen(syscall.CmsgLen(4))
+ if cm != nil {
+ data := b[syscall.CmsgLen(0):]
+ // TODO(mikio): fix potential misaligned memory access
+ *(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
+ }
+ return b[syscall.CmsgSpace(4):]
+}
+
+func parseHopLimit(cm *ControlMessage, b []byte) {
+ // TODO(mikio): fix potential misaligned memory access
+ cm.HopLimit = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
+}
+
+func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_PKTINFO
+ m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
+ if cm != nil {
+ pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+ if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+ copy(pi.Addr[:], ip)
+ }
+ if cm.IfIndex > 0 {
+ pi.setIfindex(cm.IfIndex)
+ }
+ }
+ return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
+}
+
+func parsePacketInfo(cm *ControlMessage, b []byte) {
+ pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[0]))
+ cm.Dst = pi.Addr[:]
+ cm.IfIndex = int(pi.Ifindex)
+}
+
+func marshalNextHop(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_NEXTHOP
+ m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
+ if cm != nil {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+ sa.setSockaddr(cm.NextHop, cm.IfIndex)
+ }
+ return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
+}
+
+func parseNextHop(cm *ControlMessage, b []byte) {
+}
+
+func marshalPathMTU(b []byte, cm *ControlMessage) []byte {
+ m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+ m.Level = iana.ProtocolIPv6
+ m.Type = sysIPV6_PATHMTU
+ m.SetLen(syscall.CmsgLen(sysSizeofIPv6Mtuinfo))
+ return b[syscall.CmsgSpace(sysSizeofIPv6Mtuinfo):]
+}
+
+func parsePathMTU(cm *ControlMessage, b []byte) {
+ mi := (*sysIPv6Mtuinfo)(unsafe.Pointer(&b[0]))
+ cm.Dst = mi.Addr.Addr[:]
+ cm.IfIndex = int(mi.Addr.Scope_id)
+ cm.MTU = int(mi.Mtu)
+}
diff --git a/ipv6/control_stub.go b/ipv6/control_stub.go
new file mode 100644
index 0000000..2fecf7e
--- /dev/null
+++ b/ipv6/control_stub.go
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+ return errOpNoSupport
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+ return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ return nil, errOpNoSupport
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+ return nil
+}
diff --git a/ipv6/control_unix.go b/ipv6/control_unix.go
new file mode 100644
index 0000000..2af5beb
--- /dev/null
+++ b/ipv6/control_unix.go
@@ -0,0 +1,166 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+ "os"
+ "syscall"
+
+ "golang.org/x/net/internal/iana"
+)
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+ opt.Lock()
+ defer opt.Unlock()
+ if cf&FlagTrafficClass != 0 && sockOpts[ssoReceiveTrafficClass].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceiveTrafficClass], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagTrafficClass)
+ } else {
+ opt.clear(FlagTrafficClass)
+ }
+ }
+ if cf&FlagHopLimit != 0 && sockOpts[ssoReceiveHopLimit].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceiveHopLimit], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagHopLimit)
+ } else {
+ opt.clear(FlagHopLimit)
+ }
+ }
+ if cf&flagPacketInfo != 0 && sockOpts[ssoReceivePacketInfo].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceivePacketInfo], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(cf & flagPacketInfo)
+ } else {
+ opt.clear(cf & flagPacketInfo)
+ }
+ }
+ if cf&FlagPathMTU != 0 && sockOpts[ssoReceivePathMTU].name > 0 {
+ if err := setInt(fd, &sockOpts[ssoReceivePathMTU], boolint(on)); err != nil {
+ return err
+ }
+ if on {
+ opt.set(FlagPathMTU)
+ } else {
+ opt.clear(FlagPathMTU)
+ }
+ }
+ return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+ opt.RLock()
+ var l int
+ if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
+ }
+ if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
+ }
+ if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+ }
+ if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
+ l += syscall.CmsgSpace(ctlOpts[ctlPathMTU].length)
+ }
+ if l > 0 {
+ oob = make([]byte, l)
+ b := oob
+ if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
+ b = ctlOpts[ctlTrafficClass].marshal(b, nil)
+ }
+ if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
+ b = ctlOpts[ctlHopLimit].marshal(b, nil)
+ }
+ if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
+ b = ctlOpts[ctlPacketInfo].marshal(b, nil)
+ }
+ if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
+ b = ctlOpts[ctlPathMTU].marshal(b, nil)
+ }
+ }
+ opt.RUnlock()
+ return
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ if len(b) == 0 {
+ return nil, nil
+ }
+ cmsgs, err := syscall.ParseSocketControlMessage(b)
+ if err != nil {
+ return nil, os.NewSyscallError("parse socket control message", err)
+ }
+ cm := &ControlMessage{}
+ for _, m := range cmsgs {
+ if m.Header.Level != iana.ProtocolIPv6 {
+ continue
+ }
+ switch int(m.Header.Type) {
+ case ctlOpts[ctlTrafficClass].name:
+ ctlOpts[ctlTrafficClass].parse(cm, m.Data[:])
+ case ctlOpts[ctlHopLimit].name:
+ ctlOpts[ctlHopLimit].parse(cm, m.Data[:])
+ case ctlOpts[ctlPacketInfo].name:
+ ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
+ case ctlOpts[ctlPathMTU].name:
+ ctlOpts[ctlPathMTU].parse(cm, m.Data[:])
+ }
+ }
+ return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+ if cm == nil {
+ return
+ }
+ var l int
+ tclass := false
+ if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 {
+ tclass = true
+ l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
+ }
+ hoplimit := false
+ if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 {
+ hoplimit = true
+ l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
+ }
+ pktinfo := false
+ if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0) {
+ pktinfo = true
+ l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+ }
+ nexthop := false
+ if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil {
+ nexthop = true
+ l += syscall.CmsgSpace(ctlOpts[ctlNextHop].length)
+ }
+ if l > 0 {
+ oob = make([]byte, l)
+ b := oob
+ if tclass {
+ b = ctlOpts[ctlTrafficClass].marshal(b, cm)
+ }
+ if hoplimit {
+ b = ctlOpts[ctlHopLimit].marshal(b, cm)
+ }
+ if pktinfo {
+ b = ctlOpts[ctlPacketInfo].marshal(b, cm)
+ }
+ if nexthop {
+ b = ctlOpts[ctlNextHop].marshal(b, cm)
+ }
+ }
+ return
+}
diff --git a/ipv6/control_windows.go b/ipv6/control_windows.go
new file mode 100644
index 0000000..72fdc1b
--- /dev/null
+++ b/ipv6/control_windows.go
@@ -0,0 +1,27 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import "syscall"
+
+func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
+ // TODO(mikio): implement this
+ return syscall.EWINDOWS
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+ // TODO(mikio): implement this
+ return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+ // TODO(mikio): implement this
+ return nil, syscall.EWINDOWS
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+ // TODO(mikio): implement this
+ return nil
+}
diff --git a/ipv6/defs_darwin.go b/ipv6/defs_darwin.go
new file mode 100644
index 0000000..4c7f476
--- /dev/null
+++ b/ipv6/defs_darwin.go
@@ -0,0 +1,112 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#define __APPLE_USE_RFC_3542
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+
+ sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
+ sysICMP6_FILTER = C.ICMP6_FILTER
+ sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO
+ sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT
+ sysIPV6_2292NEXTHOP = C.IPV6_2292NEXTHOP
+ sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS
+ sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS
+ sysIPV6_2292RTHDR = C.IPV6_2292RTHDR
+
+ sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
+
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+
+ sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
+
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+
+ sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
+
+ sysIPV6_MSFILTER = C.IPV6_MSFILTER
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+
+ sysIPV6_BOUND_IF = C.IPV6_BOUND_IF
+
+ sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
+ sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
+ sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
+
+ sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrStorage C.struct_sockaddr_storage
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysICMPv6Filter C.struct_icmp6_filter
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
diff --git a/ipv6/defs_dragonfly.go b/ipv6/defs_dragonfly.go
new file mode 100644
index 0000000..c72487c
--- /dev/null
+++ b/ipv6/defs_dragonfly.go
@@ -0,0 +1,84 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+ sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
+ sysICMP6_FILTER = C.ICMP6_FILTER
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
+
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+
+ sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
+
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+
+ sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
+
+ sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
+ sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
+ sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
+
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/defs_freebsd.go b/ipv6/defs_freebsd.go
new file mode 100644
index 0000000..de199ec
--- /dev/null
+++ b/ipv6/defs_freebsd.go
@@ -0,0 +1,105 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+ sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
+ sysICMP6_FILTER = C.ICMP6_FILTER
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
+
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+
+ sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
+
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+
+ sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR
+
+ sysIPV6_BINDANY = C.IPV6_BINDANY
+
+ sysIPV6_MSFILTER = C.IPV6_MSFILTER
+
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+
+ sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
+ sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
+ sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
+
+ sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrStorage C.struct_sockaddr_storage
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/defs_linux.go b/ipv6/defs_linux.go
new file mode 100644
index 0000000..d83abce
--- /dev/null
+++ b/ipv6/defs_linux.go
@@ -0,0 +1,136 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_ADDRFORM = C.IPV6_ADDRFORM
+ sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO
+ sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS
+ sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS
+ sysIPV6_2292RTHDR = C.IPV6_2292RTHDR
+ sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_FLOWINFO = C.IPV6_FLOWINFO
+
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_ADD_MEMBERSHIP = C.IPV6_ADD_MEMBERSHIP
+ sysIPV6_DROP_MEMBERSHIP = C.IPV6_DROP_MEMBERSHIP
+ sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP
+ sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP
+ sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP
+ sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+ sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE
+ sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE
+ sysMCAST_MSFILTER = C.MCAST_MSFILTER
+ sysIPV6_ROUTER_ALERT = C.IPV6_ROUTER_ALERT
+ sysIPV6_MTU_DISCOVER = C.IPV6_MTU_DISCOVER
+ sysIPV6_MTU = C.IPV6_MTU
+ sysIPV6_RECVERR = C.IPV6_RECVERR
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+ sysIPV6_JOIN_ANYCAST = C.IPV6_JOIN_ANYCAST
+ sysIPV6_LEAVE_ANYCAST = C.IPV6_LEAVE_ANYCAST
+
+ //sysIPV6_PMTUDISC_DONT = C.IPV6_PMTUDISC_DONT
+ //sysIPV6_PMTUDISC_WANT = C.IPV6_PMTUDISC_WANT
+ //sysIPV6_PMTUDISC_DO = C.IPV6_PMTUDISC_DO
+ //sysIPV6_PMTUDISC_PROBE = C.IPV6_PMTUDISC_PROBE
+ //sysIPV6_PMTUDISC_INTERFACE = C.IPV6_PMTUDISC_INTERFACE
+ //sysIPV6_PMTUDISC_OMIT = C.IPV6_PMTUDISC_OMIT
+
+ sysIPV6_FLOWLABEL_MGR = C.IPV6_FLOWLABEL_MGR
+ sysIPV6_FLOWINFO_SEND = C.IPV6_FLOWINFO_SEND
+
+ sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
+ sysIPV6_XFRM_POLICY = C.IPV6_XFRM_POLICY
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+
+ sysIPV6_ADDR_PREFERENCES = C.IPV6_ADDR_PREFERENCES
+
+ sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP
+ sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = C.IPV6_PREFER_SRC_PUBTMP_DEFAULT
+ sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA
+ sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME
+ sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA
+ sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA
+
+ sysIPV6_MINHOPCOUNT = C.IPV6_MINHOPCOUNT
+
+ sysIPV6_ORIGDSTADDR = C.IPV6_ORIGDSTADDR
+ sysIPV6_RECVORIGDSTADDR = C.IPV6_RECVORIGDSTADDR
+ sysIPV6_TRANSPARENT = C.IPV6_TRANSPARENT
+ sysIPV6_UNICAST_IF = C.IPV6_UNICAST_IF
+
+ sysICMPV6_FILTER = C.ICMPV6_FILTER
+
+ sysICMPV6_FILTER_BLOCK = C.ICMPV6_FILTER_BLOCK
+ sysICMPV6_FILTER_PASS = C.ICMPV6_FILTER_PASS
+ sysICMPV6_FILTER_BLOCKOTHERS = C.ICMPV6_FILTER_BLOCKOTHERS
+ sysICMPV6_FILTER_PASSONLY = C.ICMPV6_FILTER_PASSONLY
+
+ sysSizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+ sysSizeofIPv6FlowlabelReq = C.sizeof_struct_in6_flowlabel_req
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+ sysSizeofGroupReq = C.sizeof_struct_group_req
+ sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysKernelSockaddrStorage C.struct___kernel_sockaddr_storage
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6FlowlabelReq C.struct_in6_flowlabel_req
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/defs_netbsd.go b/ipv6/defs_netbsd.go
new file mode 100644
index 0000000..7bd09e8
--- /dev/null
+++ b/ipv6/defs_netbsd.go
@@ -0,0 +1,80 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+ sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
+ sysICMP6_FILTER = C.ICMP6_FILTER
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY
+
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+
+ sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
+ sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
+ sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
+
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/defs_openbsd.go b/ipv6/defs_openbsd.go
new file mode 100644
index 0000000..6796d9b
--- /dev/null
+++ b/ipv6/defs_openbsd.go
@@ -0,0 +1,89 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+ sysIPV6_PORTRANGE = C.IPV6_PORTRANGE
+ sysICMP6_FILTER = C.ICMP6_FILTER
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+
+ sysIPV6_AUTH_LEVEL = C.IPV6_AUTH_LEVEL
+ sysIPV6_ESP_TRANS_LEVEL = C.IPV6_ESP_TRANS_LEVEL
+ sysIPV6_ESP_NETWORK_LEVEL = C.IPV6_ESP_NETWORK_LEVEL
+ sysIPSEC6_OUTSA = C.IPSEC6_OUTSA
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+
+ sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL
+ sysIPV6_IPCOMP_LEVEL = C.IPV6_IPCOMP_LEVEL
+
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+ sysIPV6_PIPEX = C.IPV6_PIPEX
+
+ sysIPV6_RTABLE = C.IPV6_RTABLE
+
+ sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT
+ sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH
+ sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW
+
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/defs_solaris.go b/ipv6/defs_solaris.go
new file mode 100644
index 0000000..972b171
--- /dev/null
+++ b/ipv6/defs_solaris.go
@@ -0,0 +1,96 @@
+// Copyright 2014 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.
+
+// +build ignore
+
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package ipv6
+
+/*
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+*/
+import "C"
+
+const (
+ sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS
+ sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF
+ sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS
+ sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP
+ sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP
+ sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP
+
+ sysIPV6_PKTINFO = C.IPV6_PKTINFO
+
+ sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT
+ sysIPV6_NEXTHOP = C.IPV6_NEXTHOP
+ sysIPV6_HOPOPTS = C.IPV6_HOPOPTS
+ sysIPV6_DSTOPTS = C.IPV6_DSTOPTS
+
+ sysIPV6_RTHDR = C.IPV6_RTHDR
+ sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS
+
+ sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO
+ sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT
+ sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS
+
+ sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR
+
+ sysIPV6_RECVRTHDRDSTOPTS = C.IPV6_RECVRTHDRDSTOPTS
+
+ sysIPV6_CHECKSUM = C.IPV6_CHECKSUM
+ sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS
+ sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU
+ sysIPV6_DONTFRAG = C.IPV6_DONTFRAG
+ sysIPV6_SEC_OPT = C.IPV6_SEC_OPT
+ sysIPV6_SRC_PREFERENCES = C.IPV6_SRC_PREFERENCES
+ sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU
+ sysIPV6_PATHMTU = C.IPV6_PATHMTU
+ sysIPV6_TCLASS = C.IPV6_TCLASS
+ sysIPV6_V6ONLY = C.IPV6_V6ONLY
+
+ sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS
+
+ sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME
+ sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA
+ sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC
+ sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP
+ sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA
+ sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA
+
+ sysIPV6_PREFER_SRC_MIPMASK = C.IPV6_PREFER_SRC_MIPMASK
+ sysIPV6_PREFER_SRC_MIPDEFAULT = C.IPV6_PREFER_SRC_MIPDEFAULT
+ sysIPV6_PREFER_SRC_TMPMASK = C.IPV6_PREFER_SRC_TMPMASK
+ sysIPV6_PREFER_SRC_TMPDEFAULT = C.IPV6_PREFER_SRC_TMPDEFAULT
+ sysIPV6_PREFER_SRC_CGAMASK = C.IPV6_PREFER_SRC_CGAMASK
+ sysIPV6_PREFER_SRC_CGADEFAULT = C.IPV6_PREFER_SRC_CGADEFAULT
+
+ sysIPV6_PREFER_SRC_MASK = C.IPV6_PREFER_SRC_MASK
+
+ sysIPV6_PREFER_SRC_DEFAULT = C.IPV6_PREFER_SRC_DEFAULT
+
+ sysIPV6_BOUND_IF = C.IPV6_BOUND_IF
+ sysIPV6_UNSPEC_SRC = C.IPV6_UNSPEC_SRC
+
+ sysICMP6_FILTER = C.ICMP6_FILTER
+
+ sysSizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
+ sysSizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
+ sysSizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo
+
+ sysSizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
+
+ sysSizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
+)
+
+type sysSockaddrInet6 C.struct_sockaddr_in6
+
+type sysInet6Pktinfo C.struct_in6_pktinfo
+
+type sysIPv6Mtuinfo C.struct_ip6_mtuinfo
+
+type sysIPv6Mreq C.struct_ipv6_mreq
+
+type sysICMPv6Filter C.struct_icmp6_filter
diff --git a/ipv6/dgramopt_posix.go b/ipv6/dgramopt_posix.go
new file mode 100644
index 0000000..93ff2f1
--- /dev/null
+++ b/ipv6/dgramopt_posix.go
@@ -0,0 +1,288 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+)
+
+// MulticastHopLimit returns the hop limit field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastHopLimit() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoMulticastHopLimit])
+}
+
+// SetMulticastHopLimit sets the hop limit field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoMulticastHopLimit], hoplim)
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+ if !c.ok() {
+ return nil, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return nil, err
+ }
+ return getInterface(fd, &sockOpts[ssoMulticastInterface])
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInterface(fd, &sockOpts[ssoMulticastInterface], ifi)
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+ if !c.ok() {
+ return false, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return false, err
+ }
+ on, err := getInt(fd, &sockOpts[ssoMulticastLoopback])
+ if err != nil {
+ return false, err
+ }
+ return on == 1, nil
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoMulticastLoopback], boolint(on))
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// By default all sources that can cast data to group are accepted.
+// It's possible to mute and unmute data transmission from a specific
+// source by using ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup.
+// JoinGroup uses the system assigned multicast interface when ifi is
+// nil, although this is not recommended because the assignment
+// depends on platforms and sometimes it might require routing
+// configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ return setGroup(fd, &sockOpts[ssoJoinGroup], ifi, grp)
+}
+
+// LeaveGroup leaves the group address group on the interface ifi
+// regardless of whether the group is any-source group or
+// source-specific group.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
+}
+
+// JoinSourceSpecificGroup joins the source-specific group comprising
+// group and source on the interface ifi.
+// JoinSourceSpecificGroup uses the system assigned multicast
+// interface when ifi is nil, although this is not recommended because
+// the assignment depends on platforms and sometimes it might require
+// routing configuration.
+func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP16(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoJoinSourceGroup], ifi, grp, src)
+}
+
+// LeaveSourceSpecificGroup leaves the source-specific group on the
+// interface ifi.
+func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP16(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoLeaveSourceGroup], ifi, grp, src)
+}
+
+// ExcludeSourceSpecificGroup excludes the source-specific group from
+// the already joined any-source groups by JoinGroup on the interface
+// ifi.
+func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP16(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoBlockSourceGroup], ifi, grp, src)
+}
+
+// IncludeSourceSpecificGroup includes the excluded source-specific
+// group by ExcludeSourceSpecificGroup again on the interface ifi.
+func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ grp := netAddrToIP16(group)
+ if grp == nil {
+ return errMissingAddress
+ }
+ src := netAddrToIP16(source)
+ if src == nil {
+ return errMissingAddress
+ }
+ return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
+}
+
+// Checksum reports whether the kernel will compute, store or verify a
+// checksum for both incoming and outgoing packets. If on is true, it
+// returns an offset in bytes into the data of where the checksum
+// field is located.
+func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
+ if !c.ok() {
+ return false, 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return false, 0, err
+ }
+ offset, err = getInt(fd, &sockOpts[ssoChecksum])
+ if err != nil {
+ return false, 0, err
+ }
+ if offset < 0 {
+ return false, 0, nil
+ }
+ return true, offset, nil
+}
+
+// SetChecksum enables the kernel checksum processing. If on is ture,
+// the offset should be an offset in bytes into the data of where the
+// checksum field is located.
+func (c *dgramOpt) SetChecksum(on bool, offset int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ if !on {
+ offset = -1
+ }
+ return setInt(fd, &sockOpts[ssoChecksum], offset)
+}
+
+// ICMPFilter returns an ICMP filter.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+ if !c.ok() {
+ return nil, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return nil, err
+ }
+ return getICMPFilter(fd, &sockOpts[ssoICMPFilter])
+}
+
+// SetICMPFilter deploys the ICMP filter.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setICMPFilter(fd, &sockOpts[ssoICMPFilter], f)
+}
diff --git a/ipv6/dgramopt_stub.go b/ipv6/dgramopt_stub.go
new file mode 100644
index 0000000..fb067fb
--- /dev/null
+++ b/ipv6/dgramopt_stub.go
@@ -0,0 +1,119 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+import "net"
+
+// MulticastHopLimit returns the hop limit field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastHopLimit() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetMulticastHopLimit sets the hop limit field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
+ return errOpNoSupport
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+ return nil, errOpNoSupport
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+ return errOpNoSupport
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+ return false, errOpNoSupport
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+ return errOpNoSupport
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// By default all sources that can cast data to group are accepted.
+// It's possible to mute and unmute data transmission from a specific
+// source by using ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup.
+// JoinGroup uses the system assigned multicast interface when ifi is
+// nil, although this is not recommended because the assignment
+// depends on platforms and sometimes it might require routing
+// configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+ return errOpNoSupport
+}
+
+// LeaveGroup leaves the group address group on the interface ifi
+// regardless of whether the group is any-source group or
+// source-specific group.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+ return errOpNoSupport
+}
+
+// JoinSourceSpecificGroup joins the source-specific group comprising
+// group and source on the interface ifi.
+// JoinSourceSpecificGroup uses the system assigned multicast
+// interface when ifi is nil, although this is not recommended because
+// the assignment depends on platforms and sometimes it might require
+// routing configuration.
+func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// LeaveSourceSpecificGroup leaves the source-specific group on the
+// interface ifi.
+func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// ExcludeSourceSpecificGroup excludes the source-specific group from
+// the already joined any-source groups by JoinGroup on the interface
+// ifi.
+func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// IncludeSourceSpecificGroup includes the excluded source-specific
+// group by ExcludeSourceSpecificGroup again on the interface ifi.
+func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+ return errOpNoSupport
+}
+
+// Checksum reports whether the kernel will compute, store or verify a
+// checksum for both incoming and outgoing packets. If on is true, it
+// returns an offset in bytes into the data of where the checksum
+// field is located.
+func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
+ return false, 0, errOpNoSupport
+}
+
+// SetChecksum enables the kernel checksum processing. If on is ture,
+// the offset should be an offset in bytes into the data of where the
+// checksum field is located.
+func (c *dgramOpt) SetChecksum(on bool, offset int) error {
+ return errOpNoSupport
+}
+
+// ICMPFilter returns an ICMP filter.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+ return nil, errOpNoSupport
+}
+
+// SetICMPFilter deploys the ICMP filter.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+ return errOpNoSupport
+}
diff --git a/ipv6/doc.go b/ipv6/doc.go
new file mode 100644
index 0000000..c0c833e
--- /dev/null
+++ b/ipv6/doc.go
@@ -0,0 +1,240 @@
+// Copyright 2013 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.
+
+// Package ipv6 implements IP-level socket options for the Internet
+// Protocol version 6.
+//
+// The package provides IP-level socket options that allow
+// manipulation of IPv6 facilities.
+//
+// The IPv6 protocol is defined in RFC 2460.
+// Basic and advanced socket interface extensions are defined in RFC
+// 3493 and RFC 3542.
+// Socket interface extensions for multicast source filters are
+// defined in RFC 3678.
+// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
+// Source-specific multicast is defined in RFC 4607.
+//
+//
+// Unicasting
+//
+// The options for unicasting are available for net.TCPConn,
+// net.UDPConn and net.IPConn which are created as network connections
+// that use the IPv6 transport. When a single TCP connection carrying
+// a data flow of multiple packets needs to indicate the flow is
+// important, ipv6.Conn is used to set the traffic class field on the
+// IPv6 header for each packet.
+//
+// ln, err := net.Listen("tcp6", "[::]:1024")
+// if err != nil {
+// // error handling
+// }
+// defer ln.Close()
+// for {
+// c, err := ln.Accept()
+// if err != nil {
+// // error handling
+// }
+// go func(c net.Conn) {
+// defer c.Close()
+//
+// The outgoing packets will be labeled DiffServ assured forwarding
+// class 1 low drop precedence, known as AF11 packets.
+//
+// if err := ipv6.NewConn(c).SetTrafficClass(DiffServAF11); err != nil {
+// // error handling
+// }
+// if _, err := c.Write(data); err != nil {
+// // error handling
+// }
+// }(c)
+// }
+//
+//
+// Multicasting
+//
+// The options for multicasting are available for net.UDPConn and
+// net.IPconn which are created as network connections that use the
+// IPv6 transport. A few network facilities must be prepared before
+// you begin multicasting, at a minimum joining network interfaces and
+// multicast groups.
+//
+// en0, err := net.InterfaceByName("en0")
+// if err != nil {
+// // error handling
+// }
+// en1, err := net.InterfaceByIndex(911)
+// if err != nil {
+// // error handling
+// }
+// group := net.ParseIP("ff02::114")
+//
+// First, an application listens to an appropriate address with an
+// appropriate service port.
+//
+// c, err := net.ListenPacket("udp6", "[::]:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c.Close()
+//
+// Second, the application joins multicast groups, starts listening to
+// the groups on the specified network interfaces. Note that the
+// service port for transport layer protocol does not matter with this
+// operation as joining groups affects only network and link layer
+// protocols, such as IPv6 and Ethernet.
+//
+// p := ipv6.NewPacketConn(c)
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
+// // error handling
+// }
+//
+// The application might set per packet control message transmissions
+// between the protocol stack within the kernel. When the application
+// needs a destination address on an incoming packet,
+// SetControlMessage of ipv6.PacketConn is used to enable control
+// message transmissons.
+//
+// if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
+// // error handling
+// }
+//
+// The application could identify whether the received packets are
+// of interest by using the control message that contains the
+// destination address of the received packet.
+//
+// b := make([]byte, 1500)
+// for {
+// n, rcm, src, err := p.ReadFrom(b)
+// if err != nil {
+// // error handling
+// }
+// if rcm.Dst.IsMulticast() {
+// if rcm.Dst.Equal(group) {
+// // joined group, do something
+// } else {
+// // unknown group, discard
+// continue
+// }
+// }
+//
+// The application can also send both unicast and multicast packets.
+//
+// p.SetTrafficClass(DiffServCS0)
+// p.SetHopLimit(16)
+// if _, err := p.WriteTo(data[:n], nil, src); err != nil {
+// // error handling
+// }
+// dst := &net.UDPAddr{IP: group, Port: 1024}
+// wcm := ipv6.ControlMessage{TrafficClass: DiffServCS7, HopLimit: 1}
+// for _, ifi := range []*net.Interface{en0, en1} {
+// wcm.IfIndex = ifi.Index
+// if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
+// // error handling
+// }
+// }
+// }
+//
+//
+// More multicasting
+//
+// An application that uses PacketConn may join multiple multicast
+// groups. For example, a UDP listener with port 1024 might join two
+// different groups across over two different network interfaces by
+// using:
+//
+// c, err := net.ListenPacket("udp6", "[::]:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c.Close()
+// p := ipv6.NewPacketConn(c)
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
+// // error handling
+// }
+//
+// It is possible for multiple UDP listeners that listen on the same
+// UDP port to join the same multicast group. The net package will
+// provide a socket that listens to a wildcard address with reusable
+// UDP port when an appropriate multicast address prefix is passed to
+// the net.ListenPacket or net.ListenUDP.
+//
+// c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c1.Close()
+// c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
+// if err != nil {
+// // error handling
+// }
+// defer c2.Close()
+// p1 := ipv6.NewPacketConn(c1)
+// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+// // error handling
+// }
+// p2 := ipv6.NewPacketConn(c2)
+// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+// // error handling
+// }
+//
+// Also it is possible for the application to leave or rejoin a
+// multicast group on the network interface.
+//
+// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+// // error handling
+// }
+// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
+// // error handling
+// }
+//
+//
+// Source-specific multicasting
+//
+// An application that uses PacketConn on MLDv2 supported platform is
+// able to join source-specific multicast groups.
+// The application may use JoinSourceSpecificGroup and
+// LeaveSourceSpecificGroup for the operation known as "include" mode,
+//
+// ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
+// ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
+// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
+// // error handling
+// }
+// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
+// // error handling
+// }
+//
+// or JoinGroup, ExcludeSourceSpecificGroup,
+// IncludeSourceSpecificGroup and LeaveGroup for the operation known
+// as "exclude" mode.
+//
+// exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
+// if err := p.JoinGroup(en0, &ssmgroup); err != nil {
+// // error handling
+// }
+// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
+// // error handling
+// }
+// if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
+// // error handling
+// }
+//
+// Note that it depends on each platform implementation what happens
+// when an application which runs on MLDv2 unsupported platform uses
+// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
+// In general the platform tries to fall back to conversations using
+// MLDv1 and starts to listen to multicast traffic.
+// In the fallback case, ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup may return an error.
+package ipv6 // import "golang.org/x/net/ipv6"
diff --git a/ipv6/endpoint.go b/ipv6/endpoint.go
new file mode 100644
index 0000000..966eaa8
--- /dev/null
+++ b/ipv6/endpoint.go
@@ -0,0 +1,123 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+ "time"
+)
+
+// A Conn represents a network endpoint that uses IPv6 transport.
+// It allows to set basic IP-level socket options such as traffic
+// class and hop limit.
+type Conn struct {
+ genericOpt
+}
+
+type genericOpt struct {
+ net.Conn
+}
+
+func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
+
+// PathMTU returns a path MTU value for the destination associated
+// with the endpoint.
+func (c *Conn) PathMTU() (int, error) {
+ if !c.genericOpt.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.genericOpt.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ _, mtu, err := getMTUInfo(fd, &sockOpts[ssoPathMTU])
+ if err != nil {
+ return 0, err
+ }
+ return mtu, nil
+}
+
+// NewConn returns a new Conn.
+func NewConn(c net.Conn) *Conn {
+ return &Conn{
+ genericOpt: genericOpt{Conn: c},
+ }
+}
+
+// A PacketConn represents a packet network endpoint that uses IPv6
+// transport. It is used to control several IP-level socket options
+// including IPv6 header manipulation. It also provides datagram
+// based network I/O methods specific to the IPv6 and higher layer
+// protocols such as OSPF, GRE, and UDP.
+type PacketConn struct {
+ genericOpt
+ dgramOpt
+ payloadHandler
+}
+
+type dgramOpt struct {
+ net.PacketConn
+}
+
+func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
+
+// SetControlMessage allows to receive the per packet basis IP-level
+// socket options.
+func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.payloadHandler.sysfd()
+ if err != nil {
+ return err
+ }
+ return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on)
+}
+
+// SetDeadline sets the read and write deadlines associated with the
+// endpoint.
+func (c *PacketConn) SetDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline associated with the
+// endpoint.
+func (c *PacketConn) SetReadDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline associated with the
+// endpoint.
+func (c *PacketConn) SetWriteDeadline(t time.Time) error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.SetWriteDeadline(t)
+}
+
+// Close closes the endpoint.
+func (c *PacketConn) Close() error {
+ if !c.payloadHandler.ok() {
+ return syscall.EINVAL
+ }
+ return c.payloadHandler.Close()
+}
+
+// NewPacketConn returns a new PacketConn using c as its underlying
+// transport.
+func NewPacketConn(c net.PacketConn) *PacketConn {
+ return &PacketConn{
+ genericOpt: genericOpt{Conn: c.(net.Conn)},
+ dgramOpt: dgramOpt{PacketConn: c},
+ payloadHandler: payloadHandler{PacketConn: c},
+ }
+}
diff --git a/ipv6/example_test.go b/ipv6/example_test.go
new file mode 100644
index 0000000..a2a3030
--- /dev/null
+++ b/ipv6/example_test.go
@@ -0,0 +1,214 @@
+// Copyright 2014 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.
+
+package ipv6_test
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/ipv6"
+)
+
+func ExampleConn_markingTCP() {
+ ln, err := net.Listen("tcp6", "[::]:1024")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer ln.Close()
+
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ log.Fatal(err)
+ }
+ go func(c net.Conn) {
+ defer c.Close()
+ p := ipv6.NewConn(c)
+ if err := p.SetTrafficClass(0x28); err != nil { // DSCP AF11
+ log.Fatal(err)
+ }
+ if err := p.SetHopLimit(128); err != nil {
+ log.Fatal(err)
+ }
+ if _, err := c.Write([]byte("HELLO-R-U-THERE-ACK")); err != nil {
+ log.Fatal(err)
+ }
+ }(c)
+ }
+}
+
+func ExamplePacketConn_servingOneShotMulticastDNS() {
+ c, err := net.ListenPacket("udp6", "[::]:5353") // mDNS over UDP
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+
+ en0, err := net.InterfaceByName("en0")
+ if err != nil {
+ log.Fatal(err)
+ }
+ mDNSLinkLocal := net.UDPAddr{IP: net.ParseIP("ff02::fb")}
+ if err := p.JoinGroup(en0, &mDNSLinkLocal); err != nil {
+ log.Fatal(err)
+ }
+ defer p.LeaveGroup(en0, &mDNSLinkLocal)
+ if err := p.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true); err != nil {
+ log.Fatal(err)
+ }
+
+ var wcm ipv6.ControlMessage
+ b := make([]byte, 1500)
+ for {
+ _, rcm, peer, err := p.ReadFrom(b)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(mDNSLinkLocal.IP) {
+ continue
+ }
+ wcm.IfIndex = rcm.IfIndex
+ answers := []byte("FAKE-MDNS-ANSWERS") // fake mDNS answers, you need to implement this
+ if _, err := p.WriteTo(answers, &wcm, peer); err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func ExamplePacketConn_tracingIPPacketRoute() {
+ // Tracing an IP packet route to www.google.com.
+
+ const host = "www.google.com"
+ ips, err := net.LookupIP(host)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var dst net.IPAddr
+ for _, ip := range ips {
+ if ip.To16() != nil && ip.To4() == nil {
+ dst.IP = ip
+ fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host)
+ break
+ }
+ }
+ if dst.IP == nil {
+ log.Fatal("no AAAA record found")
+ }
+
+ c, err := net.ListenPacket("ip6:58", "::") // ICMP for IPv6
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+
+ if err := p.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true); err != nil {
+ log.Fatal(err)
+ }
+ wm := icmp.Message{
+ Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }
+ var f ipv6.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv6.ICMPTypeTimeExceeded)
+ f.Accept(ipv6.ICMPTypeEchoReply)
+ if err := p.SetICMPFilter(&f); err != nil {
+ log.Fatal(err)
+ }
+
+ var wcm ipv6.ControlMessage
+ rb := make([]byte, 1500)
+ for i := 1; i <= 64; i++ { // up to 64 hops
+ wm.Body.(*icmp.Echo).Seq = i
+ wb, err := wm.Marshal(nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // In the real world usually there are several
+ // multiple traffic-engineered paths for each hop.
+ // You may need to probe a few times to each hop.
+ begin := time.Now()
+ wcm.HopLimit = i
+ if _, err := p.WriteTo(wb, &wcm, &dst); err != nil {
+ log.Fatal(err)
+ }
+ if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
+ log.Fatal(err)
+ }
+ n, rcm, peer, err := p.ReadFrom(rb)
+ if err != nil {
+ if err, ok := err.(net.Error); ok && err.Timeout() {
+ fmt.Printf("%v\t*\n", i)
+ continue
+ }
+ log.Fatal(err)
+ }
+ rm, err := icmp.ParseMessage(58, rb[:n])
+ if err != nil {
+ log.Fatal(err)
+ }
+ rtt := time.Since(begin)
+
+ // In the real world you need to determine whether the
+ // received message is yours using ControlMessage.Src,
+ // ControlMesage.Dst, icmp.Echo.ID and icmp.Echo.Seq.
+ switch rm.Type {
+ case ipv6.ICMPTypeTimeExceeded:
+ names, _ := net.LookupAddr(peer.String())
+ fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm)
+ case ipv6.ICMPTypeEchoReply:
+ names, _ := net.LookupAddr(peer.String())
+ fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm)
+ return
+ }
+ }
+}
+
+func ExamplePacketConn_advertisingOSPFHello() {
+ c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+
+ en0, err := net.InterfaceByName("en0")
+ if err != nil {
+ log.Fatal(err)
+ }
+ allSPFRouters := net.IPAddr{IP: net.ParseIP("ff02::5")}
+ if err := p.JoinGroup(en0, &allSPFRouters); err != nil {
+ log.Fatal(err)
+ }
+ defer p.LeaveGroup(en0, &allSPFRouters)
+
+ hello := make([]byte, 24) // fake hello data, you need to implement this
+ ospf := make([]byte, 16) // fake ospf header, you need to implement this
+ ospf[0] = 3 // version 3
+ ospf[1] = 1 // hello packet
+ ospf = append(ospf, hello...)
+ if err := p.SetChecksum(true, 12); err != nil {
+ log.Fatal(err)
+ }
+
+ cm := ipv6.ControlMessage{
+ TrafficClass: 0xc0, // DSCP CS6
+ HopLimit: 1,
+ IfIndex: en0.Index,
+ }
+ if _, err := p.WriteTo(ospf, &cm, &allSPFRouters); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/ipv6/gen.go b/ipv6/gen.go
new file mode 100644
index 0000000..d9186c5
--- /dev/null
+++ b/ipv6/gen.go
@@ -0,0 +1,208 @@
+// Copyright 2013 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.
+
+// +build ignore
+
+//go:generate go run gen.go
+
+// This program generates system adaptation constants and types,
+// internet protocol constants and tables by reading template files
+// and IANA protocol registries.
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ if err := genzsys(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ if err := geniana(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func genzsys() error {
+ defs := "defs_" + runtime.GOOS + ".go"
+ f, err := os.Open(defs)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ f.Close()
+ cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
+ b, err := cmd.Output()
+ if err != nil {
+ return err
+ }
+ // The ipv6 pacakge still supports go1.2, and so we need to
+ // take care of additional platforms in go1.3 and above for
+ // working with go1.2.
+ switch {
+ case runtime.GOOS == "dragonfly" || runtime.GOOS == "solaris":
+ b = bytes.Replace(b, []byte("package ipv6\n"), []byte("// +build "+runtime.GOOS+"\n\npackage ipv6\n"), 1)
+ case runtime.GOOS == "linux" && (runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"):
+ b = bytes.Replace(b, []byte("package ipv6\n"), []byte("// +build "+runtime.GOOS+","+runtime.GOARCH+"\n\npackage ipv6\n"), 1)
+ }
+ b, err = format.Source(b)
+ if err != nil {
+ return err
+ }
+ zsys := "zsys_" + runtime.GOOS + ".go"
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
+ }
+ if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
+ return err
+ }
+ return nil
+}
+
+var registries = []struct {
+ url string
+ parse func(io.Writer, io.Reader) error
+}{
+ {
+ "http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
+ parseICMPv6Parameters,
+ },
+}
+
+func geniana() error {
+ var bb bytes.Buffer
+ fmt.Fprintf(&bb, "// go generate gen.go\n")
+ fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
+ fmt.Fprintf(&bb, "package ipv6\n\n")
+ for _, r := range registries {
+ resp, err := http.Get(r.url)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
+ }
+ if err := r.parse(&bb, resp.Body); err != nil {
+ return err
+ }
+ fmt.Fprintf(&bb, "\n")
+ }
+ b, err := format.Source(bb.Bytes())
+ if err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
+ return err
+ }
+ return nil
+}
+
+func parseICMPv6Parameters(w io.Writer, r io.Reader) error {
+ dec := xml.NewDecoder(r)
+ var icp icmpv6Parameters
+ if err := dec.Decode(&icp); err != nil {
+ return err
+ }
+ prs := icp.escape()
+ fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+ fmt.Fprintf(w, "const (\n")
+ for _, pr := range prs {
+ if pr.Name == "" {
+ continue
+ }
+ fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value)
+ fmt.Fprintf(w, "// %s\n", pr.OrigName)
+ }
+ fmt.Fprintf(w, ")\n\n")
+ fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+ fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
+ for _, pr := range prs {
+ if pr.Name == "" {
+ continue
+ }
+ fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName))
+ }
+ fmt.Fprintf(w, "}\n")
+ return nil
+}
+
+type icmpv6Parameters struct {
+ XMLName xml.Name `xml:"registry"`
+ Title string `xml:"title"`
+ Updated string `xml:"updated"`
+ Registries []struct {
+ Title string `xml:"title"`
+ Records []struct {
+ Value string `xml:"value"`
+ Name string `xml:"name"`
+ } `xml:"record"`
+ } `xml:"registry"`
+}
+
+type canonICMPv6ParamRecord struct {
+ OrigName string
+ Name string
+ Value int
+}
+
+func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord {
+ id := -1
+ for i, r := range icp.Registries {
+ if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
+ id = i
+ break
+ }
+ }
+ if id < 0 {
+ return nil
+ }
+ prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records))
+ sr := strings.NewReplacer(
+ "Messages", "",
+ "Message", "",
+ "ICMP", "",
+ "+", "P",
+ "-", "",
+ "/", "",
+ ".", "",
+ " ", "",
+ )
+ for i, pr := range icp.Registries[id].Records {
+ if strings.Contains(pr.Name, "Reserved") ||
+ strings.Contains(pr.Name, "Unassigned") ||
+ strings.Contains(pr.Name, "Deprecated") ||
+ strings.Contains(pr.Name, "Experiment") ||
+ strings.Contains(pr.Name, "experiment") {
+ continue
+ }
+ ss := strings.Split(pr.Name, "\n")
+ if len(ss) > 1 {
+ prs[i].Name = strings.Join(ss, " ")
+ } else {
+ prs[i].Name = ss[0]
+ }
+ s := strings.TrimSpace(prs[i].Name)
+ prs[i].OrigName = s
+ prs[i].Name = sr.Replace(s)
+ prs[i].Value, _ = strconv.Atoi(pr.Value)
+ }
+ return prs
+}
diff --git a/ipv6/genericopt_posix.go b/ipv6/genericopt_posix.go
new file mode 100644
index 0000000..dd77a01
--- /dev/null
+++ b/ipv6/genericopt_posix.go
@@ -0,0 +1,60 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
+
+package ipv6
+
+import "syscall"
+
+// TrafficClass returns the traffic class field value for outgoing
+// packets.
+func (c *genericOpt) TrafficClass() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoTrafficClass])
+}
+
+// SetTrafficClass sets the traffic class field value for future
+// outgoing packets.
+func (c *genericOpt) SetTrafficClass(tclass int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoTrafficClass], tclass)
+}
+
+// HopLimit returns the hop limit field value for outgoing packets.
+func (c *genericOpt) HopLimit() (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return 0, err
+ }
+ return getInt(fd, &sockOpts[ssoHopLimit])
+}
+
+// SetHopLimit sets the hop limit field value for future outgoing
+// packets.
+func (c *genericOpt) SetHopLimit(hoplim int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ fd, err := c.sysfd()
+ if err != nil {
+ return err
+ }
+ return setInt(fd, &sockOpts[ssoHopLimit], hoplim)
+}
diff --git a/ipv6/genericopt_stub.go b/ipv6/genericopt_stub.go
new file mode 100644
index 0000000..f5c3722
--- /dev/null
+++ b/ipv6/genericopt_stub.go
@@ -0,0 +1,30 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+// TrafficClass returns the traffic class field value for outgoing
+// packets.
+func (c *genericOpt) TrafficClass() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetTrafficClass sets the traffic class field value for future
+// outgoing packets.
+func (c *genericOpt) SetTrafficClass(tclass int) error {
+ return errOpNoSupport
+}
+
+// HopLimit returns the hop limit field value for outgoing packets.
+func (c *genericOpt) HopLimit() (int, error) {
+ return 0, errOpNoSupport
+}
+
+// SetHopLimit sets the hop limit field value for future outgoing
+// packets.
+func (c *genericOpt) SetHopLimit(hoplim int) error {
+ return errOpNoSupport
+}
diff --git a/ipv6/header.go b/ipv6/header.go
new file mode 100644
index 0000000..3c38b99
--- /dev/null
+++ b/ipv6/header.go
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+package ipv6
+
+import (
+ "errors"
+ "fmt"
+ "net"
+)
+
+const (
+ Version = 6 // protocol version
+ HeaderLen = 40 // header length
+)
+
+// A Header represents an IPv6 base header.
+type Header struct {
+ Version int // protocol version
+ TrafficClass int // traffic class
+ FlowLabel int // flow label
+ PayloadLen int // payload length
+ NextHeader int // next header
+ HopLimit int // hop limit
+ Src net.IP // source address
+ Dst net.IP // destination address
+}
+
+func (h *Header) String() string {
+ if h == nil {
+ return "<nil>"
+ }
+ return fmt.Sprintf("ver: %v, tclass: %#x, flowlbl: %#x, payloadlen: %v, nxthdr: %v, hoplim: %v, src: %v, dst: %v", h.Version, h.TrafficClass, h.FlowLabel, h.PayloadLen, h.NextHeader, h.HopLimit, h.Src, h.Dst)
+}
+
+// ParseHeader parses b as an IPv6 base header.
+func ParseHeader(b []byte) (*Header, error) {
+ if len(b) < HeaderLen {
+ return nil, errors.New("header too short")
+ }
+ h := &Header{
+ Version: int(b[0]) >> 4,
+ TrafficClass: int(b[0]&0x0f)<<4 | int(b[1])>>4,
+ FlowLabel: int(b[1]&0x0f)<<16 | int(b[2])<<8 | int(b[3]),
+ PayloadLen: int(b[4])<<8 | int(b[5]),
+ NextHeader: int(b[6]),
+ HopLimit: int(b[7]),
+ }
+ h.Src = make(net.IP, net.IPv6len)
+ copy(h.Src, b[8:24])
+ h.Dst = make(net.IP, net.IPv6len)
+ copy(h.Dst, b[24:40])
+ return h, nil
+}
diff --git a/ipv6/header_test.go b/ipv6/header_test.go
new file mode 100644
index 0000000..18e0023
--- /dev/null
+++ b/ipv6/header_test.go
@@ -0,0 +1,50 @@
+// Copyright 2014 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.
+
+package ipv6_test
+
+import (
+ "net"
+ "reflect"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/ipv6"
+)
+
+var (
+ wireHeaderFromKernel = [ipv6.HeaderLen]byte{
+ 0x69, 0x8b, 0xee, 0xf1,
+ 0xca, 0xfe, 0x2c, 0x01,
+ 0x20, 0x01, 0x0d, 0xb8,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x20, 0x01, 0x0d, 0xb8,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ }
+
+ testHeader = &ipv6.Header{
+ Version: ipv6.Version,
+ TrafficClass: iana.DiffServAF43,
+ FlowLabel: 0xbeef1,
+ PayloadLen: 0xcafe,
+ NextHeader: iana.ProtocolIPv6Frag,
+ HopLimit: 1,
+ Src: net.ParseIP("2001:db8:1::1"),
+ Dst: net.ParseIP("2001:db8:2::1"),
+ }
+)
+
+func TestParseHeader(t *testing.T) {
+ h, err := ipv6.ParseHeader(wireHeaderFromKernel[:])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(h, testHeader) {
+ t.Fatalf("got %#v; want %#v", h, testHeader)
+ }
+}
diff --git a/ipv6/helper.go b/ipv6/helper.go
new file mode 100644
index 0000000..6493484
--- /dev/null
+++ b/ipv6/helper.go
@@ -0,0 +1,33 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "errors"
+ "net"
+)
+
+var errOpNoSupport = errors.New("operation not supported")
+
+func boolint(b bool) int {
+ if b {
+ return 1
+ }
+ return 0
+}
+
+func netAddrToIP16(a net.Addr) net.IP {
+ switch v := a.(type) {
+ case *net.UDPAddr:
+ if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
+ return ip
+ }
+ case *net.IPAddr:
+ if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
+ return ip
+ }
+ }
+ return nil
+}
diff --git a/ipv6/helper_stub.go b/ipv6/helper_stub.go
new file mode 100644
index 0000000..20354ab
--- /dev/null
+++ b/ipv6/helper_stub.go
@@ -0,0 +1,19 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+func (c *genericOpt) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+ return 0, errOpNoSupport
+}
diff --git a/ipv6/helper_unix.go b/ipv6/helper_unix.go
new file mode 100644
index 0000000..92868ed
--- /dev/null
+++ b/ipv6/helper_unix.go
@@ -0,0 +1,46 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+ "net"
+ "reflect"
+)
+
+func (c *genericOpt) sysfd() (int, error) {
+ switch p := c.Conn.(type) {
+ case *net.TCPConn, *net.UDPConn, *net.IPConn:
+ return sysfd(p)
+ }
+ return 0, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+ switch p := c.PacketConn.(type) {
+ case *net.UDPConn, *net.IPConn:
+ return sysfd(p.(net.Conn))
+ }
+ return 0, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+ return sysfd(c.PacketConn.(net.Conn))
+}
+
+func sysfd(c net.Conn) (int, error) {
+ cv := reflect.ValueOf(c)
+ switch ce := cv.Elem(); ce.Kind() {
+ case reflect.Struct:
+ nfd := ce.FieldByName("conn").FieldByName("fd")
+ switch fe := nfd.Elem(); fe.Kind() {
+ case reflect.Struct:
+ fd := fe.FieldByName("sysfd")
+ return int(fd.Int()), nil
+ }
+ }
+ return 0, errInvalidConnType
+}
diff --git a/ipv6/helper_windows.go b/ipv6/helper_windows.go
new file mode 100644
index 0000000..28c401b
--- /dev/null
+++ b/ipv6/helper_windows.go
@@ -0,0 +1,45 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "reflect"
+ "syscall"
+)
+
+func (c *genericOpt) sysfd() (syscall.Handle, error) {
+ switch p := c.Conn.(type) {
+ case *net.TCPConn, *net.UDPConn, *net.IPConn:
+ return sysfd(p)
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (syscall.Handle, error) {
+ switch p := c.PacketConn.(type) {
+ case *net.UDPConn, *net.IPConn:
+ return sysfd(p.(net.Conn))
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (syscall.Handle, error) {
+ return sysfd(c.PacketConn.(net.Conn))
+}
+
+func sysfd(c net.Conn) (syscall.Handle, error) {
+ cv := reflect.ValueOf(c)
+ switch ce := cv.Elem(); ce.Kind() {
+ case reflect.Struct:
+ netfd := ce.FieldByName("conn").FieldByName("fd")
+ switch fe := netfd.Elem(); fe.Kind() {
+ case reflect.Struct:
+ fd := fe.FieldByName("sysfd")
+ return syscall.Handle(fd.Uint()), nil
+ }
+ }
+ return syscall.InvalidHandle, errInvalidConnType
+}
diff --git a/ipv6/iana.go b/ipv6/iana.go
new file mode 100644
index 0000000..3c6214f
--- /dev/null
+++ b/ipv6/iana.go
@@ -0,0 +1,82 @@
+// go generate gen.go
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package ipv6
+
+// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07
+const (
+ ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable
+ ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big
+ ICMPTypeTimeExceeded ICMPType = 3 // Time Exceeded
+ ICMPTypeParameterProblem ICMPType = 4 // Parameter Problem
+ ICMPTypeEchoRequest ICMPType = 128 // Echo Request
+ ICMPTypeEchoReply ICMPType = 129 // Echo Reply
+ ICMPTypeMulticastListenerQuery ICMPType = 130 // Multicast Listener Query
+ ICMPTypeMulticastListenerReport ICMPType = 131 // Multicast Listener Report
+ ICMPTypeMulticastListenerDone ICMPType = 132 // Multicast Listener Done
+ ICMPTypeRouterSolicitation ICMPType = 133 // Router Solicitation
+ ICMPTypeRouterAdvertisement ICMPType = 134 // Router Advertisement
+ ICMPTypeNeighborSolicitation ICMPType = 135 // Neighbor Solicitation
+ ICMPTypeNeighborAdvertisement ICMPType = 136 // Neighbor Advertisement
+ ICMPTypeRedirect ICMPType = 137 // Redirect Message
+ ICMPTypeRouterRenumbering ICMPType = 138 // Router Renumbering
+ ICMPTypeNodeInformationQuery ICMPType = 139 // ICMP Node Information Query
+ ICMPTypeNodeInformationResponse ICMPType = 140 // ICMP Node Information Response
+ ICMPTypeInverseNeighborDiscoverySolicitation ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message
+ ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message
+ ICMPTypeVersion2MulticastListenerReport ICMPType = 143 // Version 2 Multicast Listener Report
+ ICMPTypeHomeAgentAddressDiscoveryRequest ICMPType = 144 // Home Agent Address Discovery Request Message
+ ICMPTypeHomeAgentAddressDiscoveryReply ICMPType = 145 // Home Agent Address Discovery Reply Message
+ ICMPTypeMobilePrefixSolicitation ICMPType = 146 // Mobile Prefix Solicitation
+ ICMPTypeMobilePrefixAdvertisement ICMPType = 147 // Mobile Prefix Advertisement
+ ICMPTypeCertificationPathSolicitation ICMPType = 148 // Certification Path Solicitation Message
+ ICMPTypeCertificationPathAdvertisement ICMPType = 149 // Certification Path Advertisement Message
+ ICMPTypeMulticastRouterAdvertisement ICMPType = 151 // Multicast Router Advertisement
+ ICMPTypeMulticastRouterSolicitation ICMPType = 152 // Multicast Router Solicitation
+ ICMPTypeMulticastRouterTermination ICMPType = 153 // Multicast Router Termination
+ ICMPTypeFMIPv6 ICMPType = 154 // FMIPv6 Messages
+ ICMPTypeRPLControl ICMPType = 155 // RPL Control Message
+ ICMPTypeILNPv6LocatorUpdate ICMPType = 156 // ILNPv6 Locator Update Message
+ ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request
+ ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation
+ ICMPTypeMPLControl ICMPType = 159 // MPL Control Message
+)
+
+// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07
+var icmpTypes = map[ICMPType]string{
+ 1: "destination unreachable",
+ 2: "packet too big",
+ 3: "time exceeded",
+ 4: "parameter problem",
+ 128: "echo request",
+ 129: "echo reply",
+ 130: "multicast listener query",
+ 131: "multicast listener report",
+ 132: "multicast listener done",
+ 133: "router solicitation",
+ 134: "router advertisement",
+ 135: "neighbor solicitation",
+ 136: "neighbor advertisement",
+ 137: "redirect message",
+ 138: "router renumbering",
+ 139: "icmp node information query",
+ 140: "icmp node information response",
+ 141: "inverse neighbor discovery solicitation message",
+ 142: "inverse neighbor discovery advertisement message",
+ 143: "version 2 multicast listener report",
+ 144: "home agent address discovery request message",
+ 145: "home agent address discovery reply message",
+ 146: "mobile prefix solicitation",
+ 147: "mobile prefix advertisement",
+ 148: "certification path solicitation message",
+ 149: "certification path advertisement message",
+ 151: "multicast router advertisement",
+ 152: "multicast router solicitation",
+ 153: "multicast router termination",
+ 154: "fmipv6 messages",
+ 155: "rpl control message",
+ 156: "ilnpv6 locator update message",
+ 157: "duplicate address request",
+ 158: "duplicate address confirmation",
+ 159: "mpl control message",
+}
diff --git a/ipv6/icmp.go b/ipv6/icmp.go
new file mode 100644
index 0000000..a2de65a
--- /dev/null
+++ b/ipv6/icmp.go
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import "golang.org/x/net/internal/iana"
+
+// An ICMPType represents a type of ICMP message.
+type ICMPType int
+
+func (typ ICMPType) String() string {
+ s, ok := icmpTypes[typ]
+ if !ok {
+ return "<nil>"
+ }
+ return s
+}
+
+// Protocol returns the ICMPv6 protocol number.
+func (typ ICMPType) Protocol() int {
+ return iana.ProtocolIPv6ICMP
+}
+
+// An ICMPFilter represents an ICMP message filter for incoming
+// packets. The filter belongs to a packet delivery path on a host and
+// it cannot interact with forwarding packets or tunnel-outer packets.
+//
+// Note: RFC 2460 defines a reasonable role model. A node means a
+// device that implements IP. A router means a node that forwards IP
+// packets not explicitly addressed to itself, and a host means a node
+// that is not a router.
+type ICMPFilter struct {
+ sysICMPv6Filter
+}
+
+// Accept accepts incoming ICMP packets including the type field value
+// typ.
+func (f *ICMPFilter) Accept(typ ICMPType) {
+ f.accept(typ)
+}
+
+// Block blocks incoming ICMP packets including the type field value
+// typ.
+func (f *ICMPFilter) Block(typ ICMPType) {
+ f.block(typ)
+}
+
+// SetAll sets the filter action to the filter.
+func (f *ICMPFilter) SetAll(block bool) {
+ f.setAll(block)
+}
+
+// WillBlock reports whether the ICMP type will be blocked.
+func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
+ return f.willBlock(typ)
+}
diff --git a/ipv6/icmp_bsd.go b/ipv6/icmp_bsd.go
new file mode 100644
index 0000000..30e3ce4
--- /dev/null
+++ b/ipv6/icmp_bsd.go
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package ipv6
+
+func (f *sysICMPv6Filter) accept(typ ICMPType) {
+ f.Filt[typ>>5] |= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPv6Filter) block(typ ICMPType) {
+ f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPv6Filter) setAll(block bool) {
+ for i := range f.Filt {
+ if block {
+ f.Filt[i] = 0
+ } else {
+ f.Filt[i] = 1<<32 - 1
+ }
+ }
+}
+
+func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
+ return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
+}
diff --git a/ipv6/icmp_linux.go b/ipv6/icmp_linux.go
new file mode 100644
index 0000000..a67ecf6
--- /dev/null
+++ b/ipv6/icmp_linux.go
@@ -0,0 +1,27 @@
+// Copyright 2013 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.
+
+package ipv6
+
+func (f *sysICMPv6Filter) accept(typ ICMPType) {
+ f.Data[typ>>5] &^= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPv6Filter) block(typ ICMPType) {
+ f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
+}
+
+func (f *sysICMPv6Filter) setAll(block bool) {
+ for i := range f.Data {
+ if block {
+ f.Data[i] = 1<<32 - 1
+ } else {
+ f.Data[i] = 0
+ }
+ }
+}
+
+func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
+ return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
+}
diff --git a/ipv6/icmp_solaris.go b/ipv6/icmp_solaris.go
new file mode 100644
index 0000000..a942f35
--- /dev/null
+++ b/ipv6/icmp_solaris.go
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+// +build solaris
+
+package ipv6
+
+func (f *sysICMPv6Filter) accept(typ ICMPType) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) block(typ ICMPType) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) setAll(block bool) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
+ // TODO(mikio): implement this
+ return false
+}
diff --git a/ipv6/icmp_stub.go b/ipv6/icmp_stub.go
new file mode 100644
index 0000000..c1263ec
--- /dev/null
+++ b/ipv6/icmp_stub.go
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+// +build nacl plan9
+
+package ipv6
+
+type sysICMPv6Filter struct {
+}
+
+func (f *sysICMPv6Filter) accept(typ ICMPType) {
+}
+
+func (f *sysICMPv6Filter) block(typ ICMPType) {
+}
+
+func (f *sysICMPv6Filter) setAll(block bool) {
+}
+
+func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
+ return false
+}
diff --git a/ipv6/icmp_test.go b/ipv6/icmp_test.go
new file mode 100644
index 0000000..e192d6d
--- /dev/null
+++ b/ipv6/icmp_test.go
@@ -0,0 +1,96 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "net"
+ "reflect"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+var icmpStringTests = []struct {
+ in ipv6.ICMPType
+ out string
+}{
+ {ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"},
+
+ {256, "<nil>"},
+}
+
+func TestICMPString(t *testing.T) {
+ for _, tt := range icmpStringTests {
+ s := tt.in.String()
+ if s != tt.out {
+ t.Errorf("got %s; want %s", s, tt.out)
+ }
+ }
+}
+
+func TestICMPFilter(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ var f ipv6.ICMPFilter
+ for _, toggle := range []bool{false, true} {
+ f.SetAll(toggle)
+ for _, typ := range []ipv6.ICMPType{
+ ipv6.ICMPTypeDestinationUnreachable,
+ ipv6.ICMPTypeEchoReply,
+ ipv6.ICMPTypeNeighborSolicitation,
+ ipv6.ICMPTypeDuplicateAddressConfirmation,
+ } {
+ f.Accept(typ)
+ if f.WillBlock(typ) {
+ t.Errorf("ipv6.ICMPFilter.Set(%v, false) failed", typ)
+ }
+ f.Block(typ)
+ if !f.WillBlock(typ) {
+ t.Errorf("ipv6.ICMPFilter.Set(%v, true) failed", typ)
+ }
+ }
+ }
+}
+
+func TestSetICMPFilter(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv6.NewPacketConn(c)
+
+ var f ipv6.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv6.ICMPTypeEchoRequest)
+ f.Accept(ipv6.ICMPTypeEchoReply)
+ if err := p.SetICMPFilter(&f); err != nil {
+ t.Fatal(err)
+ }
+ kf, err := p.ICMPFilter()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(kf, &f) {
+ t.Fatalf("got %#v; want %#v", kf, f)
+ }
+}
diff --git a/ipv6/icmp_windows.go b/ipv6/icmp_windows.go
new file mode 100644
index 0000000..9dcfb81
--- /dev/null
+++ b/ipv6/icmp_windows.go
@@ -0,0 +1,26 @@
+// Copyright 2013 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.
+
+package ipv6
+
+type sysICMPv6Filter struct {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) accept(typ ICMPType) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) block(typ ICMPType) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) setAll(block bool) {
+ // TODO(mikio): implement this
+}
+
+func (f *sysICMPv6Filter) willBlock(typ ICMPType) bool {
+ // TODO(mikio): implement this
+ return false
+}
diff --git a/ipv6/mocktransponder_test.go b/ipv6/mocktransponder_test.go
new file mode 100644
index 0000000..d587922
--- /dev/null
+++ b/ipv6/mocktransponder_test.go
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "net"
+ "testing"
+)
+
+func connector(t *testing.T, network, addr string, done chan<- bool) {
+ defer func() { done <- true }()
+
+ c, err := net.Dial(network, addr)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ c.Close()
+}
+
+func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
+ defer func() { done <- true }()
+
+ c, err := ln.Accept()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ c.Close()
+}
diff --git a/ipv6/multicast_test.go b/ipv6/multicast_test.go
new file mode 100644
index 0000000..fc10ce1
--- /dev/null
+++ b/ipv6/multicast_test.go
@@ -0,0 +1,263 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+var packetConnReadWriteMulticastUDPTests = []struct {
+ addr string
+ grp, src *net.UDPAddr
+}{
+ {"[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727
+
+ {"[ff30::8000:0]:0", &net.UDPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.UDPAddr{IP: net.IPv6loopback}}, // see RFC 5771
+}
+
+func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "freebsd": // due to a bug on loopback marking
+ // See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
+ t.Skipf("not supported on %s", runtime.GOOS)
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range packetConnReadWriteMulticastUDPTests {
+ c, err := net.ListenPacket("udp6", tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ grp := *tt.grp
+ grp.Port = c.LocalAddr().(*net.UDPAddr).Port
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+ if tt.src == nil {
+ if err := p.JoinGroup(ifi, &grp); err != nil {
+ t.Fatal(err)
+ }
+ defer p.LeaveGroup(ifi, &grp)
+ } else {
+ if err := p.JoinSourceSpecificGroup(ifi, &grp, tt.src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support MLDv2 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ defer p.LeaveSourceSpecificGroup(ifi, &grp, tt.src)
+ }
+ if err := p.SetMulticastInterface(ifi); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastInterface(); err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetMulticastLoopback(true); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastLoopback(); err != nil {
+ t.Fatal(err)
+ }
+
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ Src: net.IPv6loopback,
+ IfIndex: ifi.Index,
+ }
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+ wb := []byte("HELLO-R-U-THERE")
+
+ for i, toggle := range []bool{true, false, true} {
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ cm.HopLimit = i + 1
+ if n, err := p.WriteTo(wb, &cm, &grp); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatal(err)
+ }
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Fatalf("got %v; want %v", rb[:n], wb)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+ }
+}
+
+var packetConnReadWriteMulticastICMPTests = []struct {
+ grp, src *net.IPAddr
+}{
+ {&net.IPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727
+
+ {&net.IPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.IPAddr{IP: net.IPv6loopback}}, // see RFC 5771
+}
+
+func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
+ switch runtime.GOOS {
+ case "freebsd": // due to a bug on loopback marking
+ // See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
+ t.Skipf("not supported on %s", runtime.GOOS)
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ for _, tt := range packetConnReadWriteMulticastICMPTests {
+ c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, tt.grp.IP)
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+ if tt.src == nil {
+ if err := p.JoinGroup(ifi, tt.grp); err != nil {
+ t.Fatal(err)
+ }
+ defer p.LeaveGroup(ifi, tt.grp)
+ } else {
+ if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support MLDv2 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src)
+ }
+ if err := p.SetMulticastInterface(ifi); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastInterface(); err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetMulticastLoopback(true); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := p.MulticastLoopback(); err != nil {
+ t.Fatal(err)
+ }
+
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ Src: net.IPv6loopback,
+ IfIndex: ifi.Index,
+ }
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+
+ var f ipv6.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv6.ICMPTypeEchoReply)
+ if err := p.SetICMPFilter(&f); err != nil {
+ t.Fatal(err)
+ }
+
+ var psh []byte
+ for i, toggle := range []bool{true, false, true} {
+ if toggle {
+ psh = nil
+ if err := p.SetChecksum(true, 2); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ psh = pshicmp
+ // Some platforms never allow to
+ // disable the kernel checksum
+ // processing.
+ p.SetChecksum(false, -1)
+ }
+ wb, err := (&icmp.Message{
+ Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(psh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ }
+ if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ cm.HopLimit = i + 1
+ if n, err := p.WriteTo(wb, &cm, tt.grp); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil {
+ t.Fatal(err)
+ } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+ }
+}
diff --git a/ipv6/multicastlistener_test.go b/ipv6/multicastlistener_test.go
new file mode 100644
index 0000000..9711f75
--- /dev/null
+++ b/ipv6/multicastlistener_test.go
@@ -0,0 +1,246 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "fmt"
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+var udpMultipleGroupListenerTests = []net.Addr{
+ &net.UDPAddr{IP: net.ParseIP("ff02::114")}, // see RFC 4727
+ &net.UDPAddr{IP: net.ParseIP("ff02::1:114")},
+ &net.UDPAddr{IP: net.ParseIP("ff02::2:114")},
+}
+
+func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ for _, gaddr := range udpMultipleGroupListenerTests {
+ c, err := net.ListenPacket("udp6", "[::]:0") // wildcard address with non-reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv6.NewPacketConn(c)
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok {
+ continue
+ }
+ if err := p.JoinGroup(&ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ if err := p.LeaveGroup(ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+}
+
+func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ for _, gaddr := range udpMultipleGroupListenerTests {
+ c1, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c1.Close()
+
+ c2, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c2.Close()
+
+ var ps [2]*ipv6.PacketConn
+ ps[0] = ipv6.NewPacketConn(c1)
+ ps[1] = ipv6.NewPacketConn(c2)
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok {
+ continue
+ }
+ for _, p := range ps {
+ if err := p.JoinGroup(&ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ for _, p := range ps {
+ if err := p.LeaveGroup(ifi, gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ }
+}
+
+func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
+ type ml struct {
+ c *ipv6.PacketConn
+ ifi *net.Interface
+ }
+ var mlt []*ml
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ ip, ok := nettest.IsMulticastCapable("ip6", &ifi)
+ if !ok {
+ continue
+ }
+ c, err := net.ListenPacket("udp6", fmt.Sprintf("[%s%%%s]:1024", ip.String(), ifi.Name)) // unicast address with non-reusable port
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ if err := p.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mlt = append(mlt, &ml{p, &ift[i]})
+ }
+ for _, m := range mlt {
+ if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv6.NewPacketConn(c)
+ gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
+ var mift []*net.Interface
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok {
+ continue
+ }
+ if err := p.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mift = append(mift, &ift[i])
+ }
+ for _, ifi := range mift {
+ if err := p.LeaveGroup(ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "openbsd": // platforms that return fe80::1%lo0: bind: can't assign requested address
+ t.Skipf("not supported on %s", runtime.GOOS)
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
+ type ml struct {
+ c *ipv6.PacketConn
+ ifi *net.Interface
+ }
+ var mlt []*ml
+
+ ift, err := net.Interfaces()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, ifi := range ift {
+ ip, ok := nettest.IsMulticastCapable("ip6", &ifi)
+ if !ok {
+ continue
+ }
+ c, err := net.ListenPacket("ip6:ipv6-icmp", fmt.Sprintf("%s%%%s", ip.String(), ifi.Name)) // unicast address
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ if err := p.JoinGroup(&ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ mlt = append(mlt, &ml{p, &ift[i]})
+ }
+ for _, m := range mlt {
+ if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/ipv6/multicastsockopt_test.go b/ipv6/multicastsockopt_test.go
new file mode 100644
index 0000000..fe0e6e1
--- /dev/null
+++ b/ipv6/multicastsockopt_test.go
@@ -0,0 +1,157 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+var packetConnMulticastSocketOptionTests = []struct {
+ net, proto, addr string
+ grp, src net.Addr
+}{
+ {"udp6", "", "[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727
+ {"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff02::115")}, nil}, // see RFC 4727
+
+ {"udp6", "", "[ff30::8000:0]:0", &net.UDPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.UDPAddr{IP: net.IPv6loopback}}, // see RFC 5771
+ {"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff30::8000:2")}, &net.IPAddr{IP: net.IPv6loopback}}, // see RFC 5771
+}
+
+func TestPacketConnMulticastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+ if ifi == nil {
+ t.Skipf("not available on %s", runtime.GOOS)
+ }
+
+ m, ok := nettest.SupportsRawIPSocket()
+ for _, tt := range packetConnMulticastSocketOptionTests {
+ if tt.net == "ip6" && !ok {
+ t.Log(m)
+ continue
+ }
+ c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+
+ if tt.src == nil {
+ testMulticastSocketOptions(t, p, ifi, tt.grp)
+ } else {
+ testSourceSpecificMulticastSocketOptions(t, p, ifi, tt.grp, tt.src)
+ }
+ }
+}
+
+type testIPv6MulticastConn interface {
+ MulticastHopLimit() (int, error)
+ SetMulticastHopLimit(ttl int) error
+ MulticastLoopback() (bool, error)
+ SetMulticastLoopback(bool) error
+ JoinGroup(*net.Interface, net.Addr) error
+ LeaveGroup(*net.Interface, net.Addr) error
+ JoinSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ LeaveSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ ExcludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+ IncludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
+}
+
+func testMulticastSocketOptions(t *testing.T, c testIPv6MulticastConn, ifi *net.Interface, grp net.Addr) {
+ const hoplim = 255
+ if err := c.SetMulticastHopLimit(hoplim); err != nil {
+ t.Error(err)
+ return
+ }
+ if v, err := c.MulticastHopLimit(); err != nil {
+ t.Error(err)
+ return
+ } else if v != hoplim {
+ t.Errorf("got %v; want %v", v, hoplim)
+ return
+ }
+
+ for _, toggle := range []bool{true, false} {
+ if err := c.SetMulticastLoopback(toggle); err != nil {
+ t.Error(err)
+ return
+ }
+ if v, err := c.MulticastLoopback(); err != nil {
+ t.Error(err)
+ return
+ } else if v != toggle {
+ t.Errorf("got %v; want %v", v, toggle)
+ return
+ }
+ }
+
+ if err := c.JoinGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+func testSourceSpecificMulticastSocketOptions(t *testing.T, c testIPv6MulticastConn, ifi *net.Interface, grp, src net.Addr) {
+ // MCAST_JOIN_GROUP -> MCAST_BLOCK_SOURCE -> MCAST_UNBLOCK_SOURCE -> MCAST_LEAVE_GROUP
+ if err := c.JoinGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.ExcludeSourceSpecificGroup(ifi, grp, src); err != nil {
+ switch runtime.GOOS {
+ case "freebsd", "linux":
+ default: // platforms that don't support MLDv2 fail here
+ t.Logf("not supported on %s", runtime.GOOS)
+ return
+ }
+ t.Error(err)
+ return
+ }
+ if err := c.IncludeSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_SOURCE_GROUP
+ if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_GROUP
+ if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil {
+ t.Error(err)
+ return
+ }
+ if err := c.LeaveGroup(ifi, grp); err != nil {
+ t.Error(err)
+ return
+ }
+}
diff --git a/ipv6/payload.go b/ipv6/payload.go
new file mode 100644
index 0000000..529b20b
--- /dev/null
+++ b/ipv6/payload.go
@@ -0,0 +1,15 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import "net"
+
+// A payloadHandler represents the IPv6 datagram payload handler.
+type payloadHandler struct {
+ net.PacketConn
+ rawOpt
+}
+
+func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }
diff --git a/ipv6/payload_cmsg.go b/ipv6/payload_cmsg.go
new file mode 100644
index 0000000..8e90d32
--- /dev/null
+++ b/ipv6/payload_cmsg.go
@@ -0,0 +1,70 @@
+// Copyright 2013 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.
+
+// +build !nacl,!plan9,!windows
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+)
+
+// ReadFrom reads a payload of the received IPv6 datagram, from the
+// endpoint c, copying the payload into b. It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+ if !c.ok() {
+ return 0, nil, nil, syscall.EINVAL
+ }
+ oob := newControlMessage(&c.rawOpt)
+ var oobn int
+ switch c := c.PacketConn.(type) {
+ case *net.UDPConn:
+ if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
+ return 0, nil, nil, err
+ }
+ case *net.IPConn:
+ if n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
+ return 0, nil, nil, err
+ }
+ default:
+ return 0, nil, nil, errInvalidConnType
+ }
+ if cm, err = parseControlMessage(oob[:oobn]); err != nil {
+ return 0, nil, nil, err
+ }
+ if cm != nil {
+ cm.Src = netAddrToIP16(src)
+ }
+ return
+}
+
+// WriteTo writes a payload of the IPv6 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b. It
+// returns the number of bytes written. The control message cm allows
+// the IPv6 header fields and the datagram path to be specified. The
+// cm may be nil if control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ oob := marshalControlMessage(cm)
+ if dst == nil {
+ return 0, errMissingAddress
+ }
+ switch c := c.PacketConn.(type) {
+ case *net.UDPConn:
+ n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
+ case *net.IPConn:
+ n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
+ default:
+ return 0, errInvalidConnType
+ }
+ if err != nil {
+ return 0, err
+ }
+ return
+}
diff --git a/ipv6/payload_nocmsg.go b/ipv6/payload_nocmsg.go
new file mode 100644
index 0000000..499204d
--- /dev/null
+++ b/ipv6/payload_nocmsg.go
@@ -0,0 +1,41 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 windows
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+)
+
+// ReadFrom reads a payload of the received IPv6 datagram, from the
+// endpoint c, copying the payload into b. It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+ if !c.ok() {
+ return 0, nil, nil, syscall.EINVAL
+ }
+ if n, src, err = c.PacketConn.ReadFrom(b); err != nil {
+ return 0, nil, nil, err
+ }
+ return
+}
+
+// WriteTo writes a payload of the IPv6 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b. It
+// returns the number of bytes written. The control message cm allows
+// the IPv6 header fields and the datagram path to be specified. The
+// cm may be nil if control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ if dst == nil {
+ return 0, errMissingAddress
+ }
+ return c.PacketConn.WriteTo(b, dst)
+}
diff --git a/ipv6/readwrite_test.go b/ipv6/readwrite_test.go
new file mode 100644
index 0000000..ff4ea2b
--- /dev/null
+++ b/ipv6/readwrite_test.go
@@ -0,0 +1,185 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "bytes"
+ "net"
+ "runtime"
+ "sync"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
+ c, err := net.ListenPacket("udp6", "[::1]:0")
+ if err != nil {
+ return nil, nil, err
+ }
+ dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
+ if err != nil {
+ c.Close()
+ return nil, nil, err
+ }
+ return c, dst, nil
+}
+
+func BenchmarkReadWriteNetUDP(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+
+ c, dst, err := benchmarkUDPListener()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+
+ wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ benchmarkReadWriteNetUDP(b, c, wb, rb, dst)
+ }
+}
+
+func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, wb, rb []byte, dst net.Addr) {
+ if _, err := c.WriteTo(wb, dst); err != nil {
+ b.Fatal(err)
+ }
+ if _, _, err := c.ReadFrom(rb); err != nil {
+ b.Fatal(err)
+ }
+}
+
+func BenchmarkReadWriteIPv6UDP(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+
+ c, dst, err := benchmarkUDPListener()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv6.NewPacketConn(c)
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+ if err := p.SetControlMessage(cf, true); err != nil {
+ b.Fatal(err)
+ }
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
+
+ wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ benchmarkReadWriteIPv6UDP(b, p, wb, rb, dst, ifi)
+ }
+}
+
+func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) {
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ HopLimit: 1,
+ }
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ b.Fatal(err)
+ } else if n != len(wb) {
+ b.Fatalf("got %v; want %v", n, len(wb))
+ }
+ if _, _, _, err := p.ReadFrom(rb); err != nil {
+ b.Fatal(err)
+ }
+}
+
+func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ c, err := net.ListenPacket("udp6", "[::1]:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+
+ dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+ wb := []byte("HELLO-R-U-THERE")
+
+ if err := p.SetControlMessage(cf, true); err != nil { // probe before test
+ if nettest.ProtocolNotSupported(err) {
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ t.Fatal(err)
+ }
+
+ var wg sync.WaitGroup
+ reader := func() {
+ defer wg.Done()
+ rb := make([]byte, 128)
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Error(err)
+ return
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Errorf("got %v; want %v", rb[:n], wb)
+ return
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+ writer := func(toggle bool) {
+ defer wg.Done()
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ Src: net.IPv6loopback,
+ }
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ t.Error(err)
+ return
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ t.Error(err)
+ return
+ } else if n != len(wb) {
+ t.Errorf("got %v; want %v", n, len(wb))
+ return
+ }
+ }
+
+ const N = 10
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go reader()
+ }
+ wg.Add(2 * N)
+ for i := 0; i < 2*N; i++ {
+ go writer(i%2 != 0)
+ }
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go reader()
+ }
+ wg.Wait()
+}
diff --git a/ipv6/sockopt.go b/ipv6/sockopt.go
new file mode 100644
index 0000000..f0cfc2f
--- /dev/null
+++ b/ipv6/sockopt.go
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+package ipv6
+
+// Sticky socket options
+const (
+ ssoTrafficClass = iota // header field for unicast packet, RFC 3542
+ ssoHopLimit // header field for unicast packet, RFC 3493
+ ssoMulticastInterface // outbound interface for multicast packet, RFC 3493
+ ssoMulticastHopLimit // header field for multicast packet, RFC 3493
+ ssoMulticastLoopback // loopback for multicast packet, RFC 3493
+ ssoReceiveTrafficClass // header field on received packet, RFC 3542
+ ssoReceiveHopLimit // header field on received packet, RFC 2292 or 3542
+ ssoReceivePacketInfo // incbound or outbound packet path, RFC 2292 or 3542
+ ssoReceivePathMTU // path mtu, RFC 3542
+ ssoPathMTU // path mtu, RFC 3542
+ ssoChecksum // packet checksum, RFC 2292 or 3542
+ ssoICMPFilter // icmp filter, RFC 2292 or 3542
+ ssoJoinGroup // any-source multicast, RFC 3493
+ ssoLeaveGroup // any-source multicast, RFC 3493
+ ssoJoinSourceGroup // source-specific multicast
+ ssoLeaveSourceGroup // source-specific multicast
+ ssoBlockSourceGroup // any-source or source-specific multicast
+ ssoUnblockSourceGroup // any-source or source-specific multicast
+ ssoMax
+)
+
+// Sticky socket option value types
+const (
+ ssoTypeInt = iota + 1
+ ssoTypeInterface
+ ssoTypeICMPFilter
+ ssoTypeMTUInfo
+ ssoTypeIPMreq
+ ssoTypeGroupReq
+ ssoTypeGroupSourceReq
+)
+
+// A sockOpt represents a binding for sticky socket option.
+type sockOpt struct {
+ level int // option level
+ name int // option name, must be equal or greater than 1
+ typ int // option value type, must be equal or greater than 1
+}
diff --git a/ipv6/sockopt_asmreq_unix.go b/ipv6/sockopt_asmreq_unix.go
new file mode 100644
index 0000000..b7fd4fe
--- /dev/null
+++ b/ipv6/sockopt_asmreq_unix.go
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+ "net"
+ "os"
+ "unsafe"
+)
+
+func setsockoptIPMreq(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ var mreq sysIPv6Mreq
+ copy(mreq.Multiaddr[:], grp)
+ if ifi != nil {
+ mreq.setIfindex(ifi.Index)
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&mreq), sysSizeofIPv6Mreq))
+}
diff --git a/ipv6/sockopt_asmreq_windows.go b/ipv6/sockopt_asmreq_windows.go
new file mode 100644
index 0000000..c03c731
--- /dev/null
+++ b/ipv6/sockopt_asmreq_windows.go
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func setsockoptIPMreq(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ var mreq sysIPv6Mreq
+ copy(mreq.Multiaddr[:], grp)
+ if ifi != nil {
+ mreq.setIfindex(ifi.Index)
+ }
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(opt.level), int32(opt.name), (*byte)(unsafe.Pointer(&mreq)), sysSizeofIPv6Mreq))
+}
diff --git a/ipv6/sockopt_ssmreq_stub.go b/ipv6/sockopt_ssmreq_stub.go
new file mode 100644
index 0000000..7732e49
--- /dev/null
+++ b/ipv6/sockopt_ssmreq_stub.go
@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+// +build !darwin,!freebsd,!linux
+
+package ipv6
+
+import "net"
+
+func setsockoptGroupReq(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ return errOpNoSupport
+}
+
+func setsockoptGroupSourceReq(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ return errOpNoSupport
+}
diff --git a/ipv6/sockopt_ssmreq_unix.go b/ipv6/sockopt_ssmreq_unix.go
new file mode 100644
index 0000000..c64d6d5
--- /dev/null
+++ b/ipv6/sockopt_ssmreq_unix.go
@@ -0,0 +1,59 @@
+// Copyright 2014 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.
+
+// +build darwin freebsd linux
+
+package ipv6
+
+import (
+ "net"
+ "os"
+ "unsafe"
+)
+
+var freebsd32o64 bool
+
+func setsockoptGroupReq(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ var gr sysGroupReq
+ if ifi != nil {
+ gr.Interface = uint32(ifi.Index)
+ }
+ gr.setGroup(grp)
+ var p unsafe.Pointer
+ var l sysSockoptLen
+ if freebsd32o64 {
+ var d [sysSizeofGroupReq + 4]byte
+ s := (*[sysSizeofGroupReq]byte)(unsafe.Pointer(&gr))
+ copy(d[:4], s[:4])
+ copy(d[8:], s[4:])
+ p = unsafe.Pointer(&d[0])
+ l = sysSizeofGroupReq + 4
+ } else {
+ p = unsafe.Pointer(&gr)
+ l = sysSizeofGroupReq
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, p, l))
+}
+
+func setsockoptGroupSourceReq(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ var gsr sysGroupSourceReq
+ if ifi != nil {
+ gsr.Interface = uint32(ifi.Index)
+ }
+ gsr.setSourceGroup(grp, src)
+ var p unsafe.Pointer
+ var l sysSockoptLen
+ if freebsd32o64 {
+ var d [sysSizeofGroupSourceReq + 4]byte
+ s := (*[sysSizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))
+ copy(d[:4], s[:4])
+ copy(d[8:], s[4:])
+ p = unsafe.Pointer(&d[0])
+ l = sysSizeofGroupSourceReq + 4
+ } else {
+ p = unsafe.Pointer(&gsr)
+ l = sysSizeofGroupSourceReq
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, p, l))
+}
diff --git a/ipv6/sockopt_stub.go b/ipv6/sockopt_stub.go
new file mode 100644
index 0000000..b8dacfd
--- /dev/null
+++ b/ipv6/sockopt_stub.go
@@ -0,0 +1,13 @@
+// Copyright 2013 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+import "net"
+
+func getMTUInfo(fd int, opt *sockOpt) (*net.Interface, int, error) {
+ return nil, 0, errOpNoSupport
+}
diff --git a/ipv6/sockopt_test.go b/ipv6/sockopt_test.go
new file mode 100644
index 0000000..9c21903
--- /dev/null
+++ b/ipv6/sockopt_test.go
@@ -0,0 +1,133 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "fmt"
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+var supportsIPv6 bool = nettest.SupportsIPv6()
+
+func TestConnInitiatorPathMTU(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ ln, err := net.Listen("tcp6", "[::1]:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool)
+ go acceptor(t, ln, done)
+
+ c, err := net.Dial("tcp6", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels don't support IPV6_PATHMTU option
+ t.Logf("not supported on %s", runtime.GOOS)
+ default:
+ t.Fatal(err)
+ }
+ } else {
+ t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
+ }
+
+ <-done
+}
+
+func TestConnResponderPathMTU(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ ln, err := net.Listen("tcp6", "[::1]:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool)
+ go connector(t, "tcp6", ln.Addr().String(), done)
+
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels don't support IPV6_PATHMTU option
+ t.Logf("not supported on %s", runtime.GOOS)
+ default:
+ t.Fatal(err)
+ }
+ } else {
+ t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
+ }
+
+ <-done
+}
+
+func TestPacketConnChecksum(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolOSPFIGP), "::") // OSPF for IPv6
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ p := ipv6.NewPacketConn(c)
+ offset := 12 // see RFC 5340
+
+ for _, toggle := range []bool{false, true} {
+ if err := p.SetChecksum(toggle, offset); err != nil {
+ if toggle {
+ t.Fatalf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
+ } else {
+ // Some platforms never allow to disable the kernel
+ // checksum processing.
+ t.Logf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
+ }
+ }
+ if on, offset, err := p.Checksum(); err != nil {
+ t.Fatal(err)
+ } else {
+ t.Logf("kernel checksum processing enabled=%v, offset=%v", on, offset)
+ }
+ }
+}
diff --git a/ipv6/sockopt_unix.go b/ipv6/sockopt_unix.go
new file mode 100644
index 0000000..25ea545
--- /dev/null
+++ b/ipv6/sockopt_unix.go
@@ -0,0 +1,122 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+ "net"
+ "os"
+ "unsafe"
+)
+
+func getInt(fd int, opt *sockOpt) (int, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return 0, errOpNoSupport
+ }
+ var i int32
+ l := sysSockoptLen(4)
+ if err := getsockopt(fd, opt.level, opt.name, unsafe.Pointer(&i), &l); err != nil {
+ return 0, os.NewSyscallError("getsockopt", err)
+ }
+ return int(i), nil
+}
+
+func setInt(fd int, opt *sockOpt, v int) error {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return errOpNoSupport
+ }
+ i := int32(v)
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&i), sysSockoptLen(4)))
+}
+
+func getInterface(fd int, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return nil, errOpNoSupport
+ }
+ var i int32
+ l := sysSockoptLen(4)
+ if err := getsockopt(fd, opt.level, opt.name, unsafe.Pointer(&i), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ if i == 0 {
+ return nil, nil
+ }
+ ifi, err := net.InterfaceByIndex(int(i))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setInterface(fd int, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return errOpNoSupport
+ }
+ var i int32
+ if ifi != nil {
+ i = int32(ifi.Index)
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&i), sysSockoptLen(4)))
+}
+
+func getICMPFilter(fd int, opt *sockOpt) (*ICMPFilter, error) {
+ if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
+ return nil, errOpNoSupport
+ }
+ var f ICMPFilter
+ l := sysSockoptLen(sysSizeofICMPv6Filter)
+ if err := getsockopt(fd, opt.level, opt.name, unsafe.Pointer(&f.sysICMPv6Filter), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ return &f, nil
+}
+
+func setICMPFilter(fd int, opt *sockOpt, f *ICMPFilter) error {
+ if opt.name < 1 || opt.typ != ssoTypeICMPFilter {
+ return errOpNoSupport
+ }
+ return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&f.sysICMPv6Filter), sysSizeofICMPv6Filter))
+}
+
+func getMTUInfo(fd int, opt *sockOpt) (*net.Interface, int, error) {
+ if opt.name < 1 || opt.typ != ssoTypeMTUInfo {
+ return nil, 0, errOpNoSupport
+ }
+ var mi sysIPv6Mtuinfo
+ l := sysSockoptLen(sysSizeofIPv6Mtuinfo)
+ if err := getsockopt(fd, opt.level, opt.name, unsafe.Pointer(&mi), &l); err != nil {
+ return nil, 0, os.NewSyscallError("getsockopt", err)
+ }
+ if mi.Addr.Scope_id == 0 {
+ return nil, int(mi.Mtu), nil
+ }
+ ifi, err := net.InterfaceByIndex(int(mi.Addr.Scope_id))
+ if err != nil {
+ return nil, 0, err
+ }
+ return ifi, int(mi.Mtu), nil
+}
+
+func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 {
+ return errOpNoSupport
+ }
+ switch opt.typ {
+ case ssoTypeIPMreq:
+ return setsockoptIPMreq(fd, opt, ifi, grp)
+ case ssoTypeGroupReq:
+ return setsockoptGroupReq(fd, opt, ifi, grp)
+ default:
+ return errOpNoSupport
+ }
+}
+
+func setSourceGroup(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ if opt.name < 1 || opt.typ != ssoTypeGroupSourceReq {
+ return errOpNoSupport
+ }
+ return setsockoptGroupSourceReq(fd, opt, ifi, grp, src)
+}
diff --git a/ipv6/sockopt_windows.go b/ipv6/sockopt_windows.go
new file mode 100644
index 0000000..32c73b7
--- /dev/null
+++ b/ipv6/sockopt_windows.go
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func getInt(fd syscall.Handle, opt *sockOpt) (int, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return 0, errOpNoSupport
+ }
+ var i int32
+ l := int32(4)
+ if err := syscall.Getsockopt(fd, int32(opt.level), int32(opt.name), (*byte)(unsafe.Pointer(&i)), &l); err != nil {
+ return 0, os.NewSyscallError("getsockopt", err)
+ }
+ return int(i), nil
+}
+
+func setInt(fd syscall.Handle, opt *sockOpt, v int) error {
+ if opt.name < 1 || opt.typ != ssoTypeInt {
+ return errOpNoSupport
+ }
+ i := int32(v)
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(opt.level), int32(opt.name), (*byte)(unsafe.Pointer(&i)), 4))
+}
+
+func getInterface(fd syscall.Handle, opt *sockOpt) (*net.Interface, error) {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return nil, errOpNoSupport
+ }
+ var i int32
+ l := int32(4)
+ if err := syscall.Getsockopt(fd, int32(opt.level), int32(opt.name), (*byte)(unsafe.Pointer(&i)), &l); err != nil {
+ return nil, os.NewSyscallError("getsockopt", err)
+ }
+ if i == 0 {
+ return nil, nil
+ }
+ ifi, err := net.InterfaceByIndex(int(i))
+ if err != nil {
+ return nil, err
+ }
+ return ifi, nil
+}
+
+func setInterface(fd syscall.Handle, opt *sockOpt, ifi *net.Interface) error {
+ if opt.name < 1 || opt.typ != ssoTypeInterface {
+ return errOpNoSupport
+ }
+ var i int32
+ if ifi != nil {
+ i = int32(ifi.Index)
+ }
+ return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(opt.level), int32(opt.name), (*byte)(unsafe.Pointer(&i)), 4))
+}
+
+func getICMPFilter(fd syscall.Handle, opt *sockOpt) (*ICMPFilter, error) {
+ return nil, errOpNoSupport
+}
+
+func setICMPFilter(fd syscall.Handle, opt *sockOpt, f *ICMPFilter) error {
+ return errOpNoSupport
+}
+
+func getMTUInfo(fd syscall.Handle, opt *sockOpt) (*net.Interface, int, error) {
+ return nil, 0, errOpNoSupport
+}
+
+func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+ if opt.name < 1 || opt.typ != ssoTypeIPMreq {
+ return errOpNoSupport
+ }
+ return setsockoptIPMreq(fd, opt, ifi, grp)
+}
+
+func setSourceGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+ // TODO(mikio): implement this
+ return errOpNoSupport
+}
diff --git a/ipv6/sys_bsd.go b/ipv6/sys_bsd.go
new file mode 100644
index 0000000..75a8863
--- /dev/null
+++ b/ipv6/sys_bsd.go
@@ -0,0 +1,58 @@
+// Copyright 2013 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.
+
+// +build dragonfly netbsd openbsd
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+
+ "golang.org/x/net/internal/iana"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass},
+ ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit},
+ ctlPacketInfo: {sysIPV6_PKTINFO, sysSizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo},
+ ctlNextHop: {sysIPV6_NEXTHOP, sysSizeofSockaddrInet6, marshalNextHop, parseNextHop},
+ ctlPathMTU: {sysIPV6_PATHMTU, sysSizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTrafficClass: {iana.ProtocolIPv6, sysIPV6_TCLASS, ssoTypeInt},
+ ssoHopLimit: {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+ ssoMulticastInterface: {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastHopLimit: {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+ ssoMulticastLoopback: {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTrafficClass: {iana.ProtocolIPv6, sysIPV6_RECVTCLASS, ssoTypeInt},
+ ssoReceiveHopLimit: {iana.ProtocolIPv6, sysIPV6_RECVHOPLIMIT, ssoTypeInt},
+ ssoReceivePacketInfo: {iana.ProtocolIPv6, sysIPV6_RECVPKTINFO, ssoTypeInt},
+ ssoReceivePathMTU: {iana.ProtocolIPv6, sysIPV6_RECVPATHMTU, ssoTypeInt},
+ ssoPathMTU: {iana.ProtocolIPv6, sysIPV6_PATHMTU, ssoTypeMTUInfo},
+ ssoChecksum: {iana.ProtocolIPv6, sysIPV6_CHECKSUM, ssoTypeInt},
+ ssoICMPFilter: {iana.ProtocolIPv6ICMP, sysICMP6_FILTER, ssoTypeICMPFilter},
+ ssoJoinGroup: {iana.ProtocolIPv6, sysIPV6_JOIN_GROUP, ssoTypeIPMreq},
+ ssoLeaveGroup: {iana.ProtocolIPv6, sysIPV6_LEAVE_GROUP, ssoTypeIPMreq},
+ }
+)
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], ip)
+ sa.Scope_id = uint32(i)
+}
+
+func (pi *sysInet6Pktinfo) setIfindex(i int) {
+ pi.Ifindex = uint32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+ mreq.Interface = uint32(i)
+}
diff --git a/ipv6/sys_darwin.go b/ipv6/sys_darwin.go
new file mode 100644
index 0000000..411fb49
--- /dev/null
+++ b/ipv6/sys_darwin.go
@@ -0,0 +1,135 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlHopLimit: {sysIPV6_2292HOPLIMIT, 4, marshal2292HopLimit, parseHopLimit},
+ ctlPacketInfo: {sysIPV6_2292PKTINFO, sysSizeofInet6Pktinfo, marshal2292PacketInfo, parsePacketInfo},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoHopLimit: {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+ ssoMulticastInterface: {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastHopLimit: {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+ ssoMulticastLoopback: {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveHopLimit: {iana.ProtocolIPv6, sysIPV6_2292HOPLIMIT, ssoTypeInt},
+ ssoReceivePacketInfo: {iana.ProtocolIPv6, sysIPV6_2292PKTINFO, ssoTypeInt},
+ ssoChecksum: {iana.ProtocolIPv6, sysIPV6_CHECKSUM, ssoTypeInt},
+ ssoICMPFilter: {iana.ProtocolIPv6ICMP, sysICMP6_FILTER, ssoTypeICMPFilter},
+ ssoJoinGroup: {iana.ProtocolIPv6, sysIPV6_JOIN_GROUP, ssoTypeIPMreq},
+ ssoLeaveGroup: {iana.ProtocolIPv6, sysIPV6_LEAVE_GROUP, ssoTypeIPMreq},
+ }
+)
+
+func init() {
+ // Seems like kern.osreldate is veiled on latest OS X. We use
+ // kern.osrelease instead.
+ osver, err := syscall.Sysctl("kern.osrelease")
+ if err != nil {
+ return
+ }
+ var i int
+ for i = range osver {
+ if osver[i] == '.' {
+ break
+ }
+ }
+ // The IP_PKTINFO and protocol-independent multicast API were
+ // introduced in OS X 10.7 (Darwin 11.0.0). But it looks like
+ // those features require OS X 10.8 (Darwin 12.0.0) and above.
+ // See http://support.apple.com/kb/HT1633.
+ if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '2' {
+ ctlOpts[ctlTrafficClass].name = sysIPV6_TCLASS
+ ctlOpts[ctlTrafficClass].length = 4
+ ctlOpts[ctlTrafficClass].marshal = marshalTrafficClass
+ ctlOpts[ctlTrafficClass].parse = parseTrafficClass
+ ctlOpts[ctlHopLimit].name = sysIPV6_HOPLIMIT
+ ctlOpts[ctlHopLimit].marshal = marshalHopLimit
+ ctlOpts[ctlPacketInfo].name = sysIPV6_PKTINFO
+ ctlOpts[ctlPacketInfo].marshal = marshalPacketInfo
+ ctlOpts[ctlNextHop].name = sysIPV6_NEXTHOP
+ ctlOpts[ctlNextHop].length = sysSizeofSockaddrInet6
+ ctlOpts[ctlNextHop].marshal = marshalNextHop
+ ctlOpts[ctlNextHop].parse = parseNextHop
+ ctlOpts[ctlPathMTU].name = sysIPV6_PATHMTU
+ ctlOpts[ctlPathMTU].length = sysSizeofIPv6Mtuinfo
+ ctlOpts[ctlPathMTU].marshal = marshalPathMTU
+ ctlOpts[ctlPathMTU].parse = parsePathMTU
+ sockOpts[ssoTrafficClass].level = iana.ProtocolIPv6
+ sockOpts[ssoTrafficClass].name = sysIPV6_TCLASS
+ sockOpts[ssoTrafficClass].typ = ssoTypeInt
+ sockOpts[ssoReceiveTrafficClass].level = iana.ProtocolIPv6
+ sockOpts[ssoReceiveTrafficClass].name = sysIPV6_RECVTCLASS
+ sockOpts[ssoReceiveTrafficClass].typ = ssoTypeInt
+ sockOpts[ssoReceiveHopLimit].name = sysIPV6_RECVHOPLIMIT
+ sockOpts[ssoReceivePacketInfo].name = sysIPV6_RECVPKTINFO
+ sockOpts[ssoReceivePathMTU].level = iana.ProtocolIPv6
+ sockOpts[ssoReceivePathMTU].name = sysIPV6_RECVPATHMTU
+ sockOpts[ssoReceivePathMTU].typ = ssoTypeInt
+ sockOpts[ssoPathMTU].level = iana.ProtocolIPv6
+ sockOpts[ssoPathMTU].name = sysIPV6_PATHMTU
+ sockOpts[ssoPathMTU].typ = ssoTypeMTUInfo
+ sockOpts[ssoJoinGroup].name = sysMCAST_JOIN_GROUP
+ sockOpts[ssoJoinGroup].typ = ssoTypeGroupReq
+ sockOpts[ssoLeaveGroup].name = sysMCAST_LEAVE_GROUP
+ sockOpts[ssoLeaveGroup].typ = ssoTypeGroupReq
+ sockOpts[ssoJoinSourceGroup].level = iana.ProtocolIPv6
+ sockOpts[ssoJoinSourceGroup].name = sysMCAST_JOIN_SOURCE_GROUP
+ sockOpts[ssoJoinSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoLeaveSourceGroup].level = iana.ProtocolIPv6
+ sockOpts[ssoLeaveSourceGroup].name = sysMCAST_LEAVE_SOURCE_GROUP
+ sockOpts[ssoLeaveSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoBlockSourceGroup].level = iana.ProtocolIPv6
+ sockOpts[ssoBlockSourceGroup].name = sysMCAST_BLOCK_SOURCE
+ sockOpts[ssoBlockSourceGroup].typ = ssoTypeGroupSourceReq
+ sockOpts[ssoUnblockSourceGroup].level = iana.ProtocolIPv6
+ sockOpts[ssoUnblockSourceGroup].name = sysMCAST_UNBLOCK_SOURCE
+ sockOpts[ssoUnblockSourceGroup].typ = ssoTypeGroupSourceReq
+ }
+}
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], ip)
+ sa.Scope_id = uint32(i)
+}
+
+func (pi *sysInet6Pktinfo) setIfindex(i int) {
+ pi.Ifindex = uint32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+ mreq.Interface = uint32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Pad_cgo_0[0]))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Pad_cgo_0[0]))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Pad_cgo_1[0]))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv6/sys_freebsd.go b/ipv6/sys_freebsd.go
new file mode 100644
index 0000000..b68725c
--- /dev/null
+++ b/ipv6/sys_freebsd.go
@@ -0,0 +1,93 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "runtime"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass},
+ ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit},
+ ctlPacketInfo: {sysIPV6_PKTINFO, sysSizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo},
+ ctlNextHop: {sysIPV6_NEXTHOP, sysSizeofSockaddrInet6, marshalNextHop, parseNextHop},
+ ctlPathMTU: {sysIPV6_PATHMTU, sysSizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTrafficClass: {iana.ProtocolIPv6, sysIPV6_TCLASS, ssoTypeInt},
+ ssoHopLimit: {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+ ssoMulticastInterface: {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastHopLimit: {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+ ssoMulticastLoopback: {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTrafficClass: {iana.ProtocolIPv6, sysIPV6_RECVTCLASS, ssoTypeInt},
+ ssoReceiveHopLimit: {iana.ProtocolIPv6, sysIPV6_RECVHOPLIMIT, ssoTypeInt},
+ ssoReceivePacketInfo: {iana.ProtocolIPv6, sysIPV6_RECVPKTINFO, ssoTypeInt},
+ ssoReceivePathMTU: {iana.ProtocolIPv6, sysIPV6_RECVPATHMTU, ssoTypeInt},
+ ssoPathMTU: {iana.ProtocolIPv6, sysIPV6_PATHMTU, ssoTypeMTUInfo},
+ ssoChecksum: {iana.ProtocolIPv6, sysIPV6_CHECKSUM, ssoTypeInt},
+ ssoICMPFilter: {iana.ProtocolIPv6ICMP, sysICMP6_FILTER, ssoTypeICMPFilter},
+ ssoJoinGroup: {iana.ProtocolIPv6, sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+ ssoLeaveGroup: {iana.ProtocolIPv6, sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+ ssoJoinSourceGroup: {iana.ProtocolIPv6, sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoLeaveSourceGroup: {iana.ProtocolIPv6, sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoBlockSourceGroup: {iana.ProtocolIPv6, sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+ ssoUnblockSourceGroup: {iana.ProtocolIPv6, sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+ }
+)
+
+func init() {
+ if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
+ archs, _ := syscall.Sysctl("kern.supported_archs")
+ for _, s := range strings.Fields(archs) {
+ if s == "amd64" {
+ freebsd32o64 = true
+ break
+ }
+ }
+ }
+}
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], ip)
+ sa.Scope_id = uint32(i)
+}
+
+func (pi *sysInet6Pktinfo) setIfindex(i int) {
+ pi.Ifindex = uint32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+ mreq.Interface = uint32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Group))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Group))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Source))
+ sa.Len = sysSizeofSockaddrInet6
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv6/sys_linux.go b/ipv6/sys_linux.go
new file mode 100644
index 0000000..2fa6088
--- /dev/null
+++ b/ipv6/sys_linux.go
@@ -0,0 +1,74 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/net/internal/iana"
+)
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{
+ ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass},
+ ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit},
+ ctlPacketInfo: {sysIPV6_PKTINFO, sysSizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo},
+ ctlPathMTU: {sysIPV6_PATHMTU, sysSizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU},
+ }
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoTrafficClass: {iana.ProtocolIPv6, sysIPV6_TCLASS, ssoTypeInt},
+ ssoHopLimit: {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+ ssoMulticastInterface: {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastHopLimit: {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+ ssoMulticastLoopback: {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+ ssoReceiveTrafficClass: {iana.ProtocolIPv6, sysIPV6_RECVTCLASS, ssoTypeInt},
+ ssoReceiveHopLimit: {iana.ProtocolIPv6, sysIPV6_RECVHOPLIMIT, ssoTypeInt},
+ ssoReceivePacketInfo: {iana.ProtocolIPv6, sysIPV6_RECVPKTINFO, ssoTypeInt},
+ ssoReceivePathMTU: {iana.ProtocolIPv6, sysIPV6_RECVPATHMTU, ssoTypeInt},
+ ssoPathMTU: {iana.ProtocolIPv6, sysIPV6_PATHMTU, ssoTypeMTUInfo},
+ ssoChecksum: {iana.ProtocolReserved, sysIPV6_CHECKSUM, ssoTypeInt},
+ ssoICMPFilter: {iana.ProtocolIPv6ICMP, sysICMPV6_FILTER, ssoTypeICMPFilter},
+ ssoJoinGroup: {iana.ProtocolIPv6, sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+ ssoLeaveGroup: {iana.ProtocolIPv6, sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+ ssoJoinSourceGroup: {iana.ProtocolIPv6, sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoLeaveSourceGroup: {iana.ProtocolIPv6, sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+ ssoBlockSourceGroup: {iana.ProtocolIPv6, sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+ ssoUnblockSourceGroup: {iana.ProtocolIPv6, sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+ }
+)
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], ip)
+ sa.Scope_id = uint32(i)
+}
+
+func (pi *sysInet6Pktinfo) setIfindex(i int) {
+ pi.Ifindex = int32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+ mreq.Ifindex = int32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Group))
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+ sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Group))
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], grp)
+ sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Source))
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], src)
+}
diff --git a/ipv6/sys_stub.go b/ipv6/sys_stub.go
new file mode 100644
index 0000000..6c9a143
--- /dev/null
+++ b/ipv6/sys_stub.go
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+// +build nacl plan9 solaris
+
+package ipv6
+
+type sysSockoptLen int32
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{}
+
+ sockOpts = [ssoMax]sockOpt{}
+)
diff --git a/ipv6/sys_windows.go b/ipv6/sys_windows.go
new file mode 100644
index 0000000..fda8757
--- /dev/null
+++ b/ipv6/sys_windows.go
@@ -0,0 +1,63 @@
+// Copyright 2013 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.
+
+package ipv6
+
+import (
+ "net"
+ "syscall"
+
+ "golang.org/x/net/internal/iana"
+)
+
+const (
+ // See ws2tcpip.h.
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PKTINFO = 0x13
+
+ sysSizeofSockaddrInet6 = 0x1c
+
+ sysSizeofIPv6Mreq = 0x14
+)
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+var (
+ ctlOpts = [ctlMax]ctlOpt{}
+
+ sockOpts = [ssoMax]sockOpt{
+ ssoHopLimit: {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+ ssoMulticastInterface: {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+ ssoMulticastHopLimit: {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+ ssoMulticastLoopback: {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+ ssoJoinGroup: {iana.ProtocolIPv6, sysIPV6_JOIN_GROUP, ssoTypeIPMreq},
+ ssoLeaveGroup: {iana.ProtocolIPv6, sysIPV6_LEAVE_GROUP, ssoTypeIPMreq},
+ }
+)
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+ sa.Family = syscall.AF_INET6
+ copy(sa.Addr[:], ip)
+ sa.Scope_id = uint32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+ mreq.Interface = uint32(i)
+}
diff --git a/ipv6/syscall_linux_386.go b/ipv6/syscall_linux_386.go
new file mode 100644
index 0000000..82633a5
--- /dev/null
+++ b/ipv6/syscall_linux_386.go
@@ -0,0 +1,31 @@
+// 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.
+
+package ipv6
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sysGETSOCKOPT = 0xf
+ sysSETSOCKOPT = 0xe
+)
+
+func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, errno := socketcall(sysGETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, errno := socketcall(sysSETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv6/syscall_unix.go b/ipv6/syscall_unix.go
new file mode 100644
index 0000000..a2bd836
--- /dev/null
+++ b/ipv6/syscall_unix.go
@@ -0,0 +1,26 @@
+// Copyright 2013 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.
+
+// +build darwin dragonfly freebsd linux,!386 netbsd openbsd
+
+package ipv6
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l *sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func setsockopt(fd, level, name int, v unsafe.Pointer, l sysSockoptLen) error {
+ if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
diff --git a/ipv6/thunk_linux_386.s b/ipv6/thunk_linux_386.s
new file mode 100644
index 0000000..daa78bc
--- /dev/null
+++ b/ipv6/thunk_linux_386.s
@@ -0,0 +1,8 @@
+// Copyright 2014 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.
+
+// +build go1.2
+
+TEXT ·socketcall(SB),4,$0-36
+ JMP syscall·socketcall(SB)
diff --git a/ipv6/unicast_test.go b/ipv6/unicast_test.go
new file mode 100644
index 0000000..6165698
--- /dev/null
+++ b/ipv6/unicast_test.go
@@ -0,0 +1,185 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ c, err := net.ListenPacket("udp6", "[::1]:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+
+ dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ Src: net.IPv6loopback,
+ }
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+ wb := []byte("HELLO-R-U-THERE")
+
+ for i, toggle := range []bool{true, false, true} {
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ t.Fatal(err)
+ }
+ cm.HopLimit = i + 1
+ if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ t.Fatal(err)
+ } else if !bytes.Equal(rb[:n], wb) {
+ t.Fatalf("got %v; want %v", rb[:n], wb)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ }
+ }
+}
+
+func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+ if m, ok := nettest.SupportsRawIPSocket(); !ok {
+ t.Skip(m)
+ }
+
+ c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ p := ipv6.NewPacketConn(c)
+ defer p.Close()
+
+ dst, err := net.ResolveIPAddr("ip6", "::1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP)
+ cm := ipv6.ControlMessage{
+ TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+ Src: net.IPv6loopback,
+ }
+ cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+ ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
+ if ifi != nil {
+ cm.IfIndex = ifi.Index
+ }
+
+ var f ipv6.ICMPFilter
+ f.SetAll(true)
+ f.Accept(ipv6.ICMPTypeEchoReply)
+ if err := p.SetICMPFilter(&f); err != nil {
+ t.Fatal(err)
+ }
+
+ var psh []byte
+ for i, toggle := range []bool{true, false, true} {
+ if toggle {
+ psh = nil
+ if err := p.SetChecksum(true, 2); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ psh = pshicmp
+ // Some platforms never allow to disable the
+ // kernel checksum processing.
+ p.SetChecksum(false, -1)
+ }
+ wb, err := (&icmp.Message{
+ Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+ Body: &icmp.Echo{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("HELLO-R-U-THERE"),
+ },
+ }).Marshal(psh)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := p.SetControlMessage(cf, toggle); err != nil {
+ if nettest.ProtocolNotSupported(err) {
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ t.Fatal(err)
+ }
+ cm.HopLimit = i + 1
+ if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, err := p.WriteTo(wb, &cm, dst); err != nil {
+ t.Fatal(err)
+ } else if n != len(wb) {
+ t.Fatalf("got %v; want %v", n, len(wb))
+ }
+ rb := make([]byte, 128)
+ if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+ t.Fatal(err)
+ }
+ if n, cm, _, err := p.ReadFrom(rb); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket
+ t.Logf("not supported on %s", runtime.GOOS)
+ continue
+ }
+ t.Fatal(err)
+ } else {
+ t.Logf("rcvd cmsg: %v", cm)
+ if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil {
+ t.Fatal(err)
+ } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
+ t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
+ }
+ }
+ }
+}
diff --git a/ipv6/unicastsockopt_test.go b/ipv6/unicastsockopt_test.go
new file mode 100644
index 0000000..7bb2e44
--- /dev/null
+++ b/ipv6/unicastsockopt_test.go
@@ -0,0 +1,111 @@
+// Copyright 2013 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.
+
+package ipv6_test
+
+import (
+ "net"
+ "runtime"
+ "testing"
+
+ "golang.org/x/net/internal/iana"
+ "golang.org/x/net/internal/nettest"
+ "golang.org/x/net/ipv6"
+)
+
+func TestConnUnicastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ ln, err := net.Listen("tcp6", "[::1]:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool)
+ go acceptor(t, ln, done)
+
+ c, err := net.Dial("tcp6", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ testUnicastSocketOptions(t, ipv6.NewConn(c))
+
+ <-done
+}
+
+var packetConnUnicastSocketOptionTests = []struct {
+ net, proto, addr string
+}{
+ {"udp6", "", "[::1]:0"},
+ {"ip6", ":ipv6-icmp", "::1"},
+}
+
+func TestPacketConnUnicastSocketOptions(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9", "solaris", "windows":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+ if !supportsIPv6 {
+ t.Skip("ipv6 is not supported")
+ }
+
+ m, ok := nettest.SupportsRawIPSocket()
+ for _, tt := range packetConnUnicastSocketOptionTests {
+ if tt.net == "ip6" && !ok {
+ t.Log(m)
+ continue
+ }
+ c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ testUnicastSocketOptions(t, ipv6.NewPacketConn(c))
+ }
+}
+
+type testIPv6UnicastConn interface {
+ TrafficClass() (int, error)
+ SetTrafficClass(int) error
+ HopLimit() (int, error)
+ SetHopLimit(int) error
+}
+
+func testUnicastSocketOptions(t *testing.T, c testIPv6UnicastConn) {
+ tclass := iana.DiffServCS0 | iana.NotECNTransport
+ if err := c.SetTrafficClass(tclass); err != nil {
+ switch runtime.GOOS {
+ case "darwin": // older darwin kernels don't support IPV6_TCLASS option
+ t.Logf("not supported on %s", runtime.GOOS)
+ goto next
+ }
+ t.Fatal(err)
+ }
+ if v, err := c.TrafficClass(); err != nil {
+ t.Fatal(err)
+ } else if v != tclass {
+ t.Fatalf("got %v; want %v", v, tclass)
+ }
+
+next:
+ hoplim := 255
+ if err := c.SetHopLimit(hoplim); err != nil {
+ t.Fatal(err)
+ }
+ if v, err := c.HopLimit(); err != nil {
+ t.Fatal(err)
+ } else if v != hoplim {
+ t.Fatalf("got %v; want %v", v, hoplim)
+ }
+}
diff --git a/ipv6/zsys_darwin.go b/ipv6/zsys_darwin.go
new file mode 100644
index 0000000..cb044b0
--- /dev/null
+++ b/ipv6/zsys_darwin.go
@@ -0,0 +1,131 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_darwin.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+ sysIPV6_2292PKTINFO = 0x13
+ sysIPV6_2292HOPLIMIT = 0x14
+ sysIPV6_2292NEXTHOP = 0x15
+ sysIPV6_2292HOPOPTS = 0x16
+ sysIPV6_2292DSTOPTS = 0x17
+ sysIPV6_2292RTHDR = 0x18
+
+ sysIPV6_2292PKTOPTIONS = 0x19
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RECVTCLASS = 0x23
+ sysIPV6_TCLASS = 0x24
+
+ sysIPV6_RTHDRDSTOPTS = 0x39
+
+ sysIPV6_RECVPKTINFO = 0x3d
+
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PREFER_TEMPADDR = 0x3f
+
+ sysIPV6_MSFILTER = 0x4a
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysIPV6_BOUND_IF = 0x7d
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [128]byte
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [128]byte
+ Pad_cgo_1 [128]byte
+}
diff --git a/ipv6/zsys_dragonfly.go b/ipv6/zsys_dragonfly.go
new file mode 100644
index 0000000..5a03ab7
--- /dev/null
+++ b/ipv6/zsys_dragonfly.go
@@ -0,0 +1,90 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_dragonfly.go
+
+// +build dragonfly
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PREFER_TEMPADDR = 0x3f
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_freebsd_386.go b/ipv6/zsys_freebsd_386.go
new file mode 100644
index 0000000..4ace96f
--- /dev/null
+++ b/ipv6/zsys_freebsd_386.go
@@ -0,0 +1,122 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PREFER_TEMPADDR = 0x3f
+
+ sysIPV6_BINDANY = 0x40
+
+ sysIPV6_MSFILTER = 0x4a
+
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_freebsd_amd64.go b/ipv6/zsys_freebsd_amd64.go
new file mode 100644
index 0000000..4a62c2d
--- /dev/null
+++ b/ipv6/zsys_freebsd_amd64.go
@@ -0,0 +1,124 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PREFER_TEMPADDR = 0x3f
+
+ sysIPV6_BINDANY = 0x40
+
+ sysIPV6_MSFILTER = 0x4a
+
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_freebsd_arm.go b/ipv6/zsys_freebsd_arm.go
new file mode 100644
index 0000000..4a62c2d
--- /dev/null
+++ b/ipv6/zsys_freebsd_arm.go
@@ -0,0 +1,124 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_freebsd.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PREFER_TEMPADDR = 0x3f
+
+ sysIPV6_BINDANY = 0x40
+
+ sysIPV6_MSFILTER = 0x4a
+
+ sysMCAST_JOIN_GROUP = 0x50
+ sysMCAST_LEAVE_GROUP = 0x51
+ sysMCAST_JOIN_SOURCE_GROUP = 0x52
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x53
+ sysMCAST_BLOCK_SOURCE = 0x54
+ sysMCAST_UNBLOCK_SOURCE = 0x55
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrStorage struct {
+ Len uint8
+ Family uint8
+ X__ss_pad1 [6]int8
+ X__ss_align int64
+ X__ss_pad2 [112]int8
+}
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysSockaddrStorage
+ Source sysSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_linux_386.go b/ipv6/zsys_linux_386.go
new file mode 100644
index 0000000..2727929
--- /dev/null
+++ b/ipv6/zsys_linux_386.go
@@ -0,0 +1,152 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_linux_amd64.go b/ipv6/zsys_linux_amd64.go
new file mode 100644
index 0000000..2f742e9
--- /dev/null
+++ b/ipv6/zsys_linux_amd64.go
@@ -0,0 +1,154 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_linux_arm.go b/ipv6/zsys_linux_arm.go
new file mode 100644
index 0000000..2727929
--- /dev/null
+++ b/ipv6/zsys_linux_arm.go
@@ -0,0 +1,152 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x84
+ sysSizeofGroupSourceReq = 0x104
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_linux_arm64.go b/ipv6/zsys_linux_arm64.go
new file mode 100644
index 0000000..ab10464
--- /dev/null
+++ b/ipv6/zsys_linux_arm64.go
@@ -0,0 +1,156 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,arm64
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_linux_ppc64.go b/ipv6/zsys_linux_ppc64.go
new file mode 100644
index 0000000..b99b8a5
--- /dev/null
+++ b/ipv6/zsys_linux_ppc64.go
@@ -0,0 +1,156 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,ppc64
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_linux_ppc64le.go b/ipv6/zsys_linux_ppc64le.go
new file mode 100644
index 0000000..992b56e
--- /dev/null
+++ b/ipv6/zsys_linux_ppc64le.go
@@ -0,0 +1,156 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_linux.go
+
+// +build linux,ppc64le
+
+package ipv6
+
+const (
+ sysIPV6_ADDRFORM = 0x1
+ sysIPV6_2292PKTINFO = 0x2
+ sysIPV6_2292HOPOPTS = 0x3
+ sysIPV6_2292DSTOPTS = 0x4
+ sysIPV6_2292RTHDR = 0x5
+ sysIPV6_2292PKTOPTIONS = 0x6
+ sysIPV6_CHECKSUM = 0x7
+ sysIPV6_2292HOPLIMIT = 0x8
+ sysIPV6_NEXTHOP = 0x9
+ sysIPV6_FLOWINFO = 0xb
+
+ sysIPV6_UNICAST_HOPS = 0x10
+ sysIPV6_MULTICAST_IF = 0x11
+ sysIPV6_MULTICAST_HOPS = 0x12
+ sysIPV6_MULTICAST_LOOP = 0x13
+ sysIPV6_ADD_MEMBERSHIP = 0x14
+ sysIPV6_DROP_MEMBERSHIP = 0x15
+ sysMCAST_JOIN_GROUP = 0x2a
+ sysMCAST_LEAVE_GROUP = 0x2d
+ sysMCAST_JOIN_SOURCE_GROUP = 0x2e
+ sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
+ sysMCAST_BLOCK_SOURCE = 0x2b
+ sysMCAST_UNBLOCK_SOURCE = 0x2c
+ sysMCAST_MSFILTER = 0x30
+ sysIPV6_ROUTER_ALERT = 0x16
+ sysIPV6_MTU_DISCOVER = 0x17
+ sysIPV6_MTU = 0x18
+ sysIPV6_RECVERR = 0x19
+ sysIPV6_V6ONLY = 0x1a
+ sysIPV6_JOIN_ANYCAST = 0x1b
+ sysIPV6_LEAVE_ANYCAST = 0x1c
+
+ sysIPV6_FLOWLABEL_MGR = 0x20
+ sysIPV6_FLOWINFO_SEND = 0x21
+
+ sysIPV6_IPSEC_POLICY = 0x22
+ sysIPV6_XFRM_POLICY = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x31
+ sysIPV6_PKTINFO = 0x32
+ sysIPV6_RECVHOPLIMIT = 0x33
+ sysIPV6_HOPLIMIT = 0x34
+ sysIPV6_RECVHOPOPTS = 0x35
+ sysIPV6_HOPOPTS = 0x36
+ sysIPV6_RTHDRDSTOPTS = 0x37
+ sysIPV6_RECVRTHDR = 0x38
+ sysIPV6_RTHDR = 0x39
+ sysIPV6_RECVDSTOPTS = 0x3a
+ sysIPV6_DSTOPTS = 0x3b
+ sysIPV6_RECVPATHMTU = 0x3c
+ sysIPV6_PATHMTU = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_RECVTCLASS = 0x42
+ sysIPV6_TCLASS = 0x43
+
+ sysIPV6_ADDR_PREFERENCES = 0x48
+
+ sysIPV6_PREFER_SRC_TMP = 0x1
+ sysIPV6_PREFER_SRC_PUBLIC = 0x2
+ sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
+ sysIPV6_PREFER_SRC_COA = 0x4
+ sysIPV6_PREFER_SRC_HOME = 0x400
+ sysIPV6_PREFER_SRC_CGA = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x800
+
+ sysIPV6_MINHOPCOUNT = 0x49
+
+ sysIPV6_ORIGDSTADDR = 0x4a
+ sysIPV6_RECVORIGDSTADDR = 0x4a
+ sysIPV6_TRANSPARENT = 0x4b
+ sysIPV6_UNICAST_IF = 0x4c
+
+ sysICMPV6_FILTER = 0x1
+
+ sysICMPV6_FILTER_BLOCK = 0x1
+ sysICMPV6_FILTER_PASS = 0x2
+ sysICMPV6_FILTER_BLOCKOTHERS = 0x3
+ sysICMPV6_FILTER_PASSONLY = 0x4
+
+ sysSizeofKernelSockaddrStorage = 0x80
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+ sysSizeofIPv6FlowlabelReq = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+ sysSizeofGroupReq = 0x88
+ sysSizeofGroupSourceReq = 0x108
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysKernelSockaddrStorage struct {
+ Family uint16
+ X__data [126]int8
+}
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6FlowlabelReq struct {
+ Dst [16]byte /* in6_addr */
+ Label uint32
+ Action uint8
+ Share uint8
+ Flags uint16
+ Expires uint16
+ Linger uint16
+ X__flr_pad uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Ifindex int32
+}
+
+type sysGroupReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+}
+
+type sysGroupSourceReq struct {
+ Interface uint32
+ Pad_cgo_0 [4]byte
+ Group sysKernelSockaddrStorage
+ Source sysKernelSockaddrStorage
+}
+
+type sysICMPv6Filter struct {
+ Data [8]uint32
+}
diff --git a/ipv6/zsys_netbsd.go b/ipv6/zsys_netbsd.go
new file mode 100644
index 0000000..d6ec88e
--- /dev/null
+++ b/ipv6/zsys_netbsd.go
@@ -0,0 +1,84 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_netbsd.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_IPSEC_POLICY = 0x1c
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_openbsd.go b/ipv6/zsys_openbsd.go
new file mode 100644
index 0000000..3e080b7
--- /dev/null
+++ b/ipv6/zsys_openbsd.go
@@ -0,0 +1,93 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_openbsd.go
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x4
+ sysIPV6_MULTICAST_IF = 0x9
+ sysIPV6_MULTICAST_HOPS = 0xa
+ sysIPV6_MULTICAST_LOOP = 0xb
+ sysIPV6_JOIN_GROUP = 0xc
+ sysIPV6_LEAVE_GROUP = 0xd
+ sysIPV6_PORTRANGE = 0xe
+ sysICMP6_FILTER = 0x12
+
+ sysIPV6_CHECKSUM = 0x1a
+ sysIPV6_V6ONLY = 0x1b
+
+ sysIPV6_RTHDRDSTOPTS = 0x23
+
+ sysIPV6_RECVPKTINFO = 0x24
+ sysIPV6_RECVHOPLIMIT = 0x25
+ sysIPV6_RECVRTHDR = 0x26
+ sysIPV6_RECVHOPOPTS = 0x27
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_USE_MIN_MTU = 0x2a
+ sysIPV6_RECVPATHMTU = 0x2b
+
+ sysIPV6_PATHMTU = 0x2c
+
+ sysIPV6_PKTINFO = 0x2e
+ sysIPV6_HOPLIMIT = 0x2f
+ sysIPV6_NEXTHOP = 0x30
+ sysIPV6_HOPOPTS = 0x31
+ sysIPV6_DSTOPTS = 0x32
+ sysIPV6_RTHDR = 0x33
+
+ sysIPV6_AUTH_LEVEL = 0x35
+ sysIPV6_ESP_TRANS_LEVEL = 0x36
+ sysIPV6_ESP_NETWORK_LEVEL = 0x37
+ sysIPSEC6_OUTSA = 0x38
+ sysIPV6_RECVTCLASS = 0x39
+
+ sysIPV6_AUTOFLOWLABEL = 0x3b
+ sysIPV6_IPCOMP_LEVEL = 0x3c
+
+ sysIPV6_TCLASS = 0x3d
+ sysIPV6_DONTFRAG = 0x3e
+ sysIPV6_PIPEX = 0x3f
+
+ sysIPV6_RTABLE = 0x1021
+
+ sysIPV6_PORTRANGE_DEFAULT = 0x0
+ sysIPV6_PORTRANGE_HIGH = 0x1
+ sysIPV6_PORTRANGE_LOW = 0x2
+
+ sysSizeofSockaddrInet6 = 0x1c
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x20
+
+ sysSizeofIPv6Mreq = 0x14
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysICMPv6Filter struct {
+ Filt [8]uint32
+}
diff --git a/ipv6/zsys_solaris.go b/ipv6/zsys_solaris.go
new file mode 100644
index 0000000..cdf00c2
--- /dev/null
+++ b/ipv6/zsys_solaris.go
@@ -0,0 +1,105 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_solaris.go
+
+// +build solaris
+
+package ipv6
+
+const (
+ sysIPV6_UNICAST_HOPS = 0x5
+ sysIPV6_MULTICAST_IF = 0x6
+ sysIPV6_MULTICAST_HOPS = 0x7
+ sysIPV6_MULTICAST_LOOP = 0x8
+ sysIPV6_JOIN_GROUP = 0x9
+ sysIPV6_LEAVE_GROUP = 0xa
+
+ sysIPV6_PKTINFO = 0xb
+
+ sysIPV6_HOPLIMIT = 0xc
+ sysIPV6_NEXTHOP = 0xd
+ sysIPV6_HOPOPTS = 0xe
+ sysIPV6_DSTOPTS = 0xf
+
+ sysIPV6_RTHDR = 0x10
+ sysIPV6_RTHDRDSTOPTS = 0x11
+
+ sysIPV6_RECVPKTINFO = 0x12
+ sysIPV6_RECVHOPLIMIT = 0x13
+ sysIPV6_RECVHOPOPTS = 0x14
+
+ sysIPV6_RECVRTHDR = 0x16
+
+ sysIPV6_RECVRTHDRDSTOPTS = 0x17
+
+ sysIPV6_CHECKSUM = 0x18
+ sysIPV6_RECVTCLASS = 0x19
+ sysIPV6_USE_MIN_MTU = 0x20
+ sysIPV6_DONTFRAG = 0x21
+ sysIPV6_SEC_OPT = 0x22
+ sysIPV6_SRC_PREFERENCES = 0x23
+ sysIPV6_RECVPATHMTU = 0x24
+ sysIPV6_PATHMTU = 0x25
+ sysIPV6_TCLASS = 0x26
+ sysIPV6_V6ONLY = 0x27
+
+ sysIPV6_RECVDSTOPTS = 0x28
+
+ sysIPV6_PREFER_SRC_HOME = 0x1
+ sysIPV6_PREFER_SRC_COA = 0x2
+ sysIPV6_PREFER_SRC_PUBLIC = 0x4
+ sysIPV6_PREFER_SRC_TMP = 0x8
+ sysIPV6_PREFER_SRC_NONCGA = 0x10
+ sysIPV6_PREFER_SRC_CGA = 0x20
+
+ sysIPV6_PREFER_SRC_MIPMASK = 0x3
+ sysIPV6_PREFER_SRC_MIPDEFAULT = 0x1
+ sysIPV6_PREFER_SRC_TMPMASK = 0xc
+ sysIPV6_PREFER_SRC_TMPDEFAULT = 0x4
+ sysIPV6_PREFER_SRC_CGAMASK = 0x30
+ sysIPV6_PREFER_SRC_CGADEFAULT = 0x10
+
+ sysIPV6_PREFER_SRC_MASK = 0x3f
+
+ sysIPV6_PREFER_SRC_DEFAULT = 0x15
+
+ sysIPV6_BOUND_IF = 0x41
+ sysIPV6_UNSPEC_SRC = 0x42
+
+ sysICMP6_FILTER = 0x1
+
+ sysSizeofSockaddrInet6 = 0x20
+ sysSizeofInet6Pktinfo = 0x14
+ sysSizeofIPv6Mtuinfo = 0x24
+
+ sysSizeofIPv6Mreq = 0x14
+
+ sysSizeofICMPv6Filter = 0x20
+)
+
+type sysSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+ X__sin6_src_id uint32
+}
+
+type sysInet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+type sysIPv6Mtuinfo struct {
+ Addr sysSockaddrInet6
+ Mtu uint32
+}
+
+type sysIPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type sysICMPv6Filter struct {
+ X__icmp6_filt [8]uint32
+}
diff --git a/netutil/listen.go b/netutil/listen.go
new file mode 100644
index 0000000..b317ba2
--- /dev/null
+++ b/netutil/listen.go
@@ -0,0 +1,48 @@
+// Copyright 2013 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.
+
+// Package netutil provides network utility functions, complementing the more
+// common ones in the net package.
+package netutil // import "golang.org/x/net/netutil"
+
+import (
+ "net"
+ "sync"
+)
+
+// LimitListener returns a Listener that accepts at most n simultaneous
+// connections from the provided Listener.
+func LimitListener(l net.Listener, n int) net.Listener {
+ return &limitListener{l, make(chan struct{}, n)}
+}
+
+type limitListener struct {
+ net.Listener
+ sem chan struct{}
+}
+
+func (l *limitListener) acquire() { l.sem <- struct{}{} }
+func (l *limitListener) release() { <-l.sem }
+
+func (l *limitListener) Accept() (net.Conn, error) {
+ l.acquire()
+ c, err := l.Listener.Accept()
+ if err != nil {
+ l.release()
+ return nil, err
+ }
+ return &limitListenerConn{Conn: c, release: l.release}, nil
+}
+
+type limitListenerConn struct {
+ net.Conn
+ releaseOnce sync.Once
+ release func()
+}
+
+func (l *limitListenerConn) Close() error {
+ err := l.Conn.Close()
+ l.releaseOnce.Do(l.release)
+ return err
+}
diff --git a/netutil/listen_test.go b/netutil/listen_test.go
new file mode 100644
index 0000000..c0d5bc2
--- /dev/null
+++ b/netutil/listen_test.go
@@ -0,0 +1,106 @@
+// Copyright 2013 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.
+
+// +build go1.3
+
+// (We only run this test on Go 1.3 because the HTTP client timeout behavior
+// was bad in previous releases, causing occasional deadlocks.)
+
+package netutil
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "golang.org/x/net/internal/nettest"
+)
+
+func TestLimitListener(t *testing.T) {
+ const max = 5
+ attempts := (nettest.MaxOpenFiles() - max) / 2
+ if attempts > 256 { // maximum length of accept queue is 128 by default
+ attempts = 256
+ }
+
+ l, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Close()
+ l = LimitListener(l, max)
+
+ var open int32
+ go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if n := atomic.AddInt32(&open, 1); n > max {
+ t.Errorf("%d open connections, want <= %d", n, max)
+ }
+ defer atomic.AddInt32(&open, -1)
+ time.Sleep(10 * time.Millisecond)
+ fmt.Fprint(w, "some body")
+ }))
+
+ var wg sync.WaitGroup
+ var failed int32
+ for i := 0; i < attempts; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ c := http.Client{Timeout: 3 * time.Second}
+ r, err := c.Get("http://" + l.Addr().String())
+ if err != nil {
+ t.Log(err)
+ atomic.AddInt32(&failed, 1)
+ return
+ }
+ defer r.Body.Close()
+ io.Copy(ioutil.Discard, r.Body)
+ }()
+ }
+ wg.Wait()
+
+ // We expect some Gets to fail as the kernel's accept queue is filled,
+ // but most should succeed.
+ if int(failed) >= attempts/2 {
+ t.Errorf("%d requests failed within %d attempts", failed, attempts)
+ }
+}
+
+type errorListener struct {
+ net.Listener
+}
+
+func (errorListener) Accept() (net.Conn, error) {
+ return nil, errFake
+}
+
+var errFake = errors.New("fake error from errorListener")
+
+// This used to hang.
+func TestLimitListenerError(t *testing.T) {
+ donec := make(chan bool, 1)
+ go func() {
+ const n = 2
+ ll := LimitListener(errorListener{}, n)
+ for i := 0; i < n+1; i++ {
+ _, err := ll.Accept()
+ if err != errFake {
+ t.Fatalf("Accept error = %v; want errFake", err)
+ }
+ }
+ donec <- true
+ }()
+ select {
+ case <-donec:
+ case <-time.After(5 * time.Second):
+ t.Fatal("timeout. deadlock?")
+ }
+}
diff --git a/proxy/direct.go b/proxy/direct.go
new file mode 100644
index 0000000..4c5ad88
--- /dev/null
+++ b/proxy/direct.go
@@ -0,0 +1,18 @@
+// Copyright 2011 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.
+
+package proxy
+
+import (
+ "net"
+)
+
+type direct struct{}
+
+// Direct is a direct proxy: one that makes network connections directly.
+var Direct = direct{}
+
+func (direct) Dial(network, addr string) (net.Conn, error) {
+ return net.Dial(network, addr)
+}
diff --git a/proxy/per_host.go b/proxy/per_host.go
new file mode 100644
index 0000000..f540b19
--- /dev/null
+++ b/proxy/per_host.go
@@ -0,0 +1,140 @@
+// Copyright 2011 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.
+
+package proxy
+
+import (
+ "net"
+ "strings"
+)
+
+// A PerHost directs connections to a default Dialer unless the hostname
+// requested matches one of a number of exceptions.
+type PerHost struct {
+ def, bypass Dialer
+
+ bypassNetworks []*net.IPNet
+ bypassIPs []net.IP
+ bypassZones []string
+ bypassHosts []string
+}
+
+// NewPerHost returns a PerHost Dialer that directs connections to either
+// defaultDialer or bypass, depending on whether the connection matches one of
+// the configured rules.
+func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
+ return &PerHost{
+ def: defaultDialer,
+ bypass: bypass,
+ }
+}
+
+// Dial connects to the address addr on the given network through either
+// defaultDialer or bypass.
+func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
+ host, _, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.dialerForRequest(host).Dial(network, addr)
+}
+
+func (p *PerHost) dialerForRequest(host string) Dialer {
+ if ip := net.ParseIP(host); ip != nil {
+ for _, net := range p.bypassNetworks {
+ if net.Contains(ip) {
+ return p.bypass
+ }
+ }
+ for _, bypassIP := range p.bypassIPs {
+ if bypassIP.Equal(ip) {
+ return p.bypass
+ }
+ }
+ return p.def
+ }
+
+ for _, zone := range p.bypassZones {
+ if strings.HasSuffix(host, zone) {
+ return p.bypass
+ }
+ if host == zone[1:] {
+ // For a zone "example.com", we match "example.com"
+ // too.
+ return p.bypass
+ }
+ }
+ for _, bypassHost := range p.bypassHosts {
+ if bypassHost == host {
+ return p.bypass
+ }
+ }
+ return p.def
+}
+
+// AddFromString parses a string that contains comma-separated values
+// specifying hosts that should use the bypass proxy. Each value is either an
+// IP address, a CIDR range, a zone (*.example.com) or a hostname
+// (localhost). A best effort is made to parse the string and errors are
+// ignored.
+func (p *PerHost) AddFromString(s string) {
+ hosts := strings.Split(s, ",")
+ for _, host := range hosts {
+ host = strings.TrimSpace(host)
+ if len(host) == 0 {
+ continue
+ }
+ if strings.Contains(host, "/") {
+ // We assume that it's a CIDR address like 127.0.0.0/8
+ if _, net, err := net.ParseCIDR(host); err == nil {
+ p.AddNetwork(net)
+ }
+ continue
+ }
+ if ip := net.ParseIP(host); ip != nil {
+ p.AddIP(ip)
+ continue
+ }
+ if strings.HasPrefix(host, "*.") {
+ p.AddZone(host[1:])
+ continue
+ }
+ p.AddHost(host)
+ }
+}
+
+// AddIP specifies an IP address that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match an IP.
+func (p *PerHost) AddIP(ip net.IP) {
+ p.bypassIPs = append(p.bypassIPs, ip)
+}
+
+// AddNetwork specifies an IP range that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match.
+func (p *PerHost) AddNetwork(net *net.IPNet) {
+ p.bypassNetworks = append(p.bypassNetworks, net)
+}
+
+// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
+// "example.com" matches "example.com" and all of its subdomains.
+func (p *PerHost) AddZone(zone string) {
+ if strings.HasSuffix(zone, ".") {
+ zone = zone[:len(zone)-1]
+ }
+ if !strings.HasPrefix(zone, ".") {
+ zone = "." + zone
+ }
+ p.bypassZones = append(p.bypassZones, zone)
+}
+
+// AddHost specifies a hostname that will use the bypass proxy.
+func (p *PerHost) AddHost(host string) {
+ if strings.HasSuffix(host, ".") {
+ host = host[:len(host)-1]
+ }
+ p.bypassHosts = append(p.bypassHosts, host)
+}
diff --git a/proxy/per_host_test.go b/proxy/per_host_test.go
new file mode 100644
index 0000000..a7d8095
--- /dev/null
+++ b/proxy/per_host_test.go
@@ -0,0 +1,55 @@
+// Copyright 2011 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.
+
+package proxy
+
+import (
+ "errors"
+ "net"
+ "reflect"
+ "testing"
+)
+
+type recordingProxy struct {
+ addrs []string
+}
+
+func (r *recordingProxy) Dial(network, addr string) (net.Conn, error) {
+ r.addrs = append(r.addrs, addr)
+ return nil, errors.New("recordingProxy")
+}
+
+func TestPerHost(t *testing.T) {
+ var def, bypass recordingProxy
+ perHost := NewPerHost(&def, &bypass)
+ perHost.AddFromString("localhost,*.zone,127.0.0.1,10.0.0.1/8,1000::/16")
+
+ expectedDef := []string{
+ "example.com:123",
+ "1.2.3.4:123",
+ "[1001::]:123",
+ }
+ expectedBypass := []string{
+ "localhost:123",
+ "zone:123",
+ "foo.zone:123",
+ "127.0.0.1:123",
+ "10.1.2.3:123",
+ "[1000::]:123",
+ }
+
+ for _, addr := range expectedDef {
+ perHost.Dial("tcp", addr)
+ }
+ for _, addr := range expectedBypass {
+ perHost.Dial("tcp", addr)
+ }
+
+ if !reflect.DeepEqual(expectedDef, def.addrs) {
+ t.Errorf("Hosts which went to the default proxy didn't match. Got %v, want %v", def.addrs, expectedDef)
+ }
+ if !reflect.DeepEqual(expectedBypass, bypass.addrs) {
+ t.Errorf("Hosts which went to the bypass proxy didn't match. Got %v, want %v", bypass.addrs, expectedBypass)
+ }
+}
diff --git a/proxy/proxy.go b/proxy/proxy.go
new file mode 100644
index 0000000..78a8b7b
--- /dev/null
+++ b/proxy/proxy.go
@@ -0,0 +1,94 @@
+// Copyright 2011 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.
+
+// Package proxy provides support for a variety of protocols to proxy network
+// data.
+package proxy // import "golang.org/x/net/proxy"
+
+import (
+ "errors"
+ "net"
+ "net/url"
+ "os"
+)
+
+// A Dialer is a means to establish a connection.
+type Dialer interface {
+ // Dial connects to the given address via the proxy.
+ Dial(network, addr string) (c net.Conn, err error)
+}
+
+// Auth contains authentication parameters that specific Dialers may require.
+type Auth struct {
+ User, Password string
+}
+
+// FromEnvironment returns the dialer specified by the proxy related variables in
+// the environment.
+func FromEnvironment() Dialer {
+ allProxy := os.Getenv("all_proxy")
+ if len(allProxy) == 0 {
+ return Direct
+ }
+
+ proxyURL, err := url.Parse(allProxy)
+ if err != nil {
+ return Direct
+ }
+ proxy, err := FromURL(proxyURL, Direct)
+ if err != nil {
+ return Direct
+ }
+
+ noProxy := os.Getenv("no_proxy")
+ if len(noProxy) == 0 {
+ return proxy
+ }
+
+ perHost := NewPerHost(proxy, Direct)
+ perHost.AddFromString(noProxy)
+ return perHost
+}
+
+// proxySchemes is a map from URL schemes to a function that creates a Dialer
+// from a URL with such a scheme.
+var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
+
+// RegisterDialerType takes a URL scheme and a function to generate Dialers from
+// a URL with that scheme and a forwarding Dialer. Registered schemes are used
+// by FromURL.
+func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
+ if proxySchemes == nil {
+ proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
+ }
+ proxySchemes[scheme] = f
+}
+
+// FromURL returns a Dialer given a URL specification and an underlying
+// Dialer for it to make network requests.
+func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
+ var auth *Auth
+ if u.User != nil {
+ auth = new(Auth)
+ auth.User = u.User.Username()
+ if p, ok := u.User.Password(); ok {
+ auth.Password = p
+ }
+ }
+
+ switch u.Scheme {
+ case "socks5":
+ return SOCKS5("tcp", u.Host, auth, forward)
+ }
+
+ // If the scheme doesn't match any of the built-in schemes, see if it
+ // was registered by another package.
+ if proxySchemes != nil {
+ if f, ok := proxySchemes[u.Scheme]; ok {
+ return f(u, forward)
+ }
+ }
+
+ return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
+}
diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go
new file mode 100644
index 0000000..c19a5c0
--- /dev/null
+++ b/proxy/proxy_test.go
@@ -0,0 +1,142 @@
+// Copyright 2011 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.
+
+package proxy
+
+import (
+ "io"
+ "net"
+ "net/url"
+ "strconv"
+ "sync"
+ "testing"
+)
+
+func TestFromURL(t *testing.T) {
+ endSystem, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen failed: %v", err)
+ }
+ defer endSystem.Close()
+ gateway, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen failed: %v", err)
+ }
+ defer gateway.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go socks5Gateway(t, gateway, endSystem, socks5Domain, &wg)
+
+ url, err := url.Parse("socks5://user:password@" + gateway.Addr().String())
+ if err != nil {
+ t.Fatalf("url.Parse failed: %v", err)
+ }
+ proxy, err := FromURL(url, Direct)
+ if err != nil {
+ t.Fatalf("FromURL failed: %v", err)
+ }
+ _, port, err := net.SplitHostPort(endSystem.Addr().String())
+ if err != nil {
+ t.Fatalf("net.SplitHostPort failed: %v", err)
+ }
+ if c, err := proxy.Dial("tcp", "localhost:"+port); err != nil {
+ t.Fatalf("FromURL.Dial failed: %v", err)
+ } else {
+ c.Close()
+ }
+
+ wg.Wait()
+}
+
+func TestSOCKS5(t *testing.T) {
+ endSystem, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen failed: %v", err)
+ }
+ defer endSystem.Close()
+ gateway, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen failed: %v", err)
+ }
+ defer gateway.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go socks5Gateway(t, gateway, endSystem, socks5IP4, &wg)
+
+ proxy, err := SOCKS5("tcp", gateway.Addr().String(), nil, Direct)
+ if err != nil {
+ t.Fatalf("SOCKS5 failed: %v", err)
+ }
+ if c, err := proxy.Dial("tcp", endSystem.Addr().String()); err != nil {
+ t.Fatalf("SOCKS5.Dial failed: %v", err)
+ } else {
+ c.Close()
+ }
+
+ wg.Wait()
+}
+
+func socks5Gateway(t *testing.T, gateway, endSystem net.Listener, typ byte, wg *sync.WaitGroup) {
+ defer wg.Done()
+
+ c, err := gateway.Accept()
+ if err != nil {
+ t.Errorf("net.Listener.Accept failed: %v", err)
+ return
+ }
+ defer c.Close()
+
+ b := make([]byte, 32)
+ var n int
+ if typ == socks5Domain {
+ n = 4
+ } else {
+ n = 3
+ }
+ if _, err := io.ReadFull(c, b[:n]); err != nil {
+ t.Errorf("io.ReadFull failed: %v", err)
+ return
+ }
+ if _, err := c.Write([]byte{socks5Version, socks5AuthNone}); err != nil {
+ t.Errorf("net.Conn.Write failed: %v", err)
+ return
+ }
+ if typ == socks5Domain {
+ n = 16
+ } else {
+ n = 10
+ }
+ if _, err := io.ReadFull(c, b[:n]); err != nil {
+ t.Errorf("io.ReadFull failed: %v", err)
+ return
+ }
+ if b[0] != socks5Version || b[1] != socks5Connect || b[2] != 0x00 || b[3] != typ {
+ t.Errorf("got an unexpected packet: %#02x %#02x %#02x %#02x", b[0], b[1], b[2], b[3])
+ return
+ }
+ if typ == socks5Domain {
+ copy(b[:5], []byte{socks5Version, 0x00, 0x00, socks5Domain, 9})
+ b = append(b, []byte("localhost")...)
+ } else {
+ copy(b[:4], []byte{socks5Version, 0x00, 0x00, socks5IP4})
+ }
+ host, port, err := net.SplitHostPort(endSystem.Addr().String())
+ if err != nil {
+ t.Errorf("net.SplitHostPort failed: %v", err)
+ return
+ }
+ b = append(b, []byte(net.ParseIP(host).To4())...)
+ p, err := strconv.Atoi(port)
+ if err != nil {
+ t.Errorf("strconv.Atoi failed: %v", err)
+ return
+ }
+ b = append(b, []byte{byte(p >> 8), byte(p)}...)
+ if _, err := c.Write(b); err != nil {
+ t.Errorf("net.Conn.Write failed: %v", err)
+ return
+ }
+}
diff --git a/proxy/socks5.go b/proxy/socks5.go
new file mode 100644
index 0000000..9b96282
--- /dev/null
+++ b/proxy/socks5.go
@@ -0,0 +1,210 @@
+// Copyright 2011 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.
+
+package proxy
+
+import (
+ "errors"
+ "io"
+ "net"
+ "strconv"
+)
+
+// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
+// with an optional username and password. See RFC 1928.
+func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
+ s := &socks5{
+ network: network,
+ addr: addr,
+ forward: forward,
+ }
+ if auth != nil {
+ s.user = auth.User
+ s.password = auth.Password
+ }
+
+ return s, nil
+}
+
+type socks5 struct {
+ user, password string
+ network, addr string
+ forward Dialer
+}
+
+const socks5Version = 5
+
+const (
+ socks5AuthNone = 0
+ socks5AuthPassword = 2
+)
+
+const socks5Connect = 1
+
+const (
+ socks5IP4 = 1
+ socks5Domain = 3
+ socks5IP6 = 4
+)
+
+var socks5Errors = []string{
+ "",
+ "general failure",
+ "connection forbidden",
+ "network unreachable",
+ "host unreachable",
+ "connection refused",
+ "TTL expired",
+ "command not supported",
+ "address type not supported",
+}
+
+// Dial connects to the address addr on the network net via the SOCKS5 proxy.
+func (s *socks5) Dial(network, addr string) (net.Conn, error) {
+ switch network {
+ case "tcp", "tcp6", "tcp4":
+ default:
+ return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
+ }
+
+ conn, err := s.forward.Dial(s.network, s.addr)
+ if err != nil {
+ return nil, err
+ }
+ closeConn := &conn
+ defer func() {
+ if closeConn != nil {
+ (*closeConn).Close()
+ }
+ }()
+
+ host, portStr, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+
+ port, err := strconv.Atoi(portStr)
+ if err != nil {
+ return nil, errors.New("proxy: failed to parse port number: " + portStr)
+ }
+ if port < 1 || port > 0xffff {
+ return nil, errors.New("proxy: port number out of range: " + portStr)
+ }
+
+ // the size here is just an estimate
+ buf := make([]byte, 0, 6+len(host))
+
+ buf = append(buf, socks5Version)
+ if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
+ buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
+ } else {
+ buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
+ }
+
+ if _, err := conn.Write(buf); err != nil {
+ return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ if buf[0] != 5 {
+ return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
+ }
+ if buf[1] == 0xff {
+ return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
+ }
+
+ if buf[1] == socks5AuthPassword {
+ buf = buf[:0]
+ buf = append(buf, 1 /* password protocol version */)
+ buf = append(buf, uint8(len(s.user)))
+ buf = append(buf, s.user...)
+ buf = append(buf, uint8(len(s.password)))
+ buf = append(buf, s.password...)
+
+ if _, err := conn.Write(buf); err != nil {
+ return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if buf[1] != 0 {
+ return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
+ }
+ }
+
+ buf = buf[:0]
+ buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
+
+ if ip := net.ParseIP(host); ip != nil {
+ if ip4 := ip.To4(); ip4 != nil {
+ buf = append(buf, socks5IP4)
+ ip = ip4
+ } else {
+ buf = append(buf, socks5IP6)
+ }
+ buf = append(buf, ip...)
+ } else {
+ if len(host) > 255 {
+ return nil, errors.New("proxy: destination hostname too long: " + host)
+ }
+ buf = append(buf, socks5Domain)
+ buf = append(buf, byte(len(host)))
+ buf = append(buf, host...)
+ }
+ buf = append(buf, byte(port>>8), byte(port))
+
+ if _, err := conn.Write(buf); err != nil {
+ return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:4]); err != nil {
+ return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ failure := "unknown error"
+ if int(buf[1]) < len(socks5Errors) {
+ failure = socks5Errors[buf[1]]
+ }
+
+ if len(failure) > 0 {
+ return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
+ }
+
+ bytesToDiscard := 0
+ switch buf[3] {
+ case socks5IP4:
+ bytesToDiscard = net.IPv4len
+ case socks5IP6:
+ bytesToDiscard = net.IPv6len
+ case socks5Domain:
+ _, err := io.ReadFull(conn, buf[:1])
+ if err != nil {
+ return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ bytesToDiscard = int(buf[0])
+ default:
+ return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
+ }
+
+ if cap(buf) < bytesToDiscard {
+ buf = make([]byte, bytesToDiscard)
+ } else {
+ buf = buf[:bytesToDiscard]
+ }
+ if _, err := io.ReadFull(conn, buf); err != nil {
+ return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ // Also need to discard the port number
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ closeConn = nil
+ return conn, nil
+}
diff --git a/publicsuffix/gen.go b/publicsuffix/gen.go
new file mode 100644
index 0000000..ee2598c
--- /dev/null
+++ b/publicsuffix/gen.go
@@ -0,0 +1,608 @@
+// Copyright 2012 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.
+
+// +build ignore
+
+package main
+
+// This program generates table.go and table_test.go.
+// Invoke as:
+//
+// go run gen.go -version "xxx" >table.go
+// go run gen.go -version "xxx" -test >table_test.go
+//
+// The version is derived from information found at
+// https://github.com/publicsuffix/list/commits/master/public_suffix_list.dat
+//
+// To fetch a particular git revision, such as 5c70ccd250, pass
+// -url "https://raw.githubusercontent.com/publicsuffix/list/5c70ccd250/public_suffix_list.dat"
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "fmt"
+ "go/format"
+ "io"
+ "net/http"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+
+ "golang.org/x/net/idna"
+)
+
+const (
+ nodesBitsChildren = 9
+ nodesBitsICANN = 1
+ nodesBitsTextOffset = 15
+ nodesBitsTextLength = 6
+
+ childrenBitsWildcard = 1
+ childrenBitsNodeType = 2
+ childrenBitsHi = 14
+ childrenBitsLo = 14
+)
+
+var (
+ maxChildren int
+ maxTextOffset int
+ maxTextLength int
+ maxHi uint32
+ maxLo uint32
+)
+
+func max(a, b int) int {
+ if a < b {
+ return b
+ }
+ return a
+}
+
+func u32max(a, b uint32) uint32 {
+ if a < b {
+ return b
+ }
+ return a
+}
+
+const (
+ nodeTypeNormal = 0
+ nodeTypeException = 1
+ nodeTypeParentOnly = 2
+ numNodeType = 3
+)
+
+func nodeTypeStr(n int) string {
+ switch n {
+ case nodeTypeNormal:
+ return "+"
+ case nodeTypeException:
+ return "!"
+ case nodeTypeParentOnly:
+ return "o"
+ }
+ panic("unreachable")
+}
+
+var (
+ labelEncoding = map[string]uint32{}
+ labelsList = []string{}
+ labelsMap = map[string]bool{}
+ rules = []string{}
+
+ // validSuffix is used to check that the entries in the public suffix list
+ // are in canonical form (after Punycode encoding). Specifically, capital
+ // letters are not allowed.
+ validSuffix = regexp.MustCompile(`^[a-z0-9_\!\*\-\.]+$`)
+
+ crush = flag.Bool("crush", true, "make the generated node text as small as possible")
+ subset = flag.Bool("subset", false, "generate only a subset of the full table, for debugging")
+ url = flag.String("url",
+ "https://publicsuffix.org/list/effective_tld_names.dat",
+ "URL of the publicsuffix.org list. If empty, stdin is read instead")
+ v = flag.Bool("v", false, "verbose output (to stderr)")
+ version = flag.String("version", "", "the effective_tld_names.dat version")
+ test = flag.Bool("test", false, "generate table_test.go")
+)
+
+func main() {
+ if err := main1(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+func main1() error {
+ flag.Parse()
+ if nodesBitsTextLength+nodesBitsTextOffset+nodesBitsICANN+nodesBitsChildren > 32 {
+ return fmt.Errorf("not enough bits to encode the nodes table")
+ }
+ if childrenBitsLo+childrenBitsHi+childrenBitsNodeType+childrenBitsWildcard > 32 {
+ return fmt.Errorf("not enough bits to encode the children table")
+ }
+ if *version == "" {
+ return fmt.Errorf("-version was not specified")
+ }
+ var r io.Reader = os.Stdin
+ if *url != "" {
+ res, err := http.Get(*url)
+ if err != nil {
+ return err
+ }
+ if res.StatusCode != http.StatusOK {
+ return fmt.Errorf("bad GET status for %s: %d", *url, res.Status)
+ }
+ r = res.Body
+ defer res.Body.Close()
+ }
+
+ var root node
+ icann := false
+ buf := new(bytes.Buffer)
+ br := bufio.NewReader(r)
+ for {
+ s, err := br.ReadString('\n')
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+ s = strings.TrimSpace(s)
+ if strings.Contains(s, "BEGIN ICANN DOMAINS") {
+ icann = true
+ continue
+ }
+ if strings.Contains(s, "END ICANN DOMAINS") {
+ icann = false
+ continue
+ }
+ if s == "" || strings.HasPrefix(s, "//") {
+ continue
+ }
+ s, err = idna.ToASCII(s)
+ if err != nil {
+ return err
+ }
+ if !validSuffix.MatchString(s) {
+ return fmt.Errorf("bad publicsuffix.org list data: %q", s)
+ }
+
+ if *subset {
+ switch {
+ case s == "ac.jp" || strings.HasSuffix(s, ".ac.jp"):
+ case s == "ak.us" || strings.HasSuffix(s, ".ak.us"):
+ case s == "ao" || strings.HasSuffix(s, ".ao"):
+ case s == "ar" || strings.HasSuffix(s, ".ar"):
+ case s == "arpa" || strings.HasSuffix(s, ".arpa"):
+ case s == "cy" || strings.HasSuffix(s, ".cy"):
+ case s == "dyndns.org" || strings.HasSuffix(s, ".dyndns.org"):
+ case s == "jp":
+ case s == "kobe.jp" || strings.HasSuffix(s, ".kobe.jp"):
+ case s == "kyoto.jp" || strings.HasSuffix(s, ".kyoto.jp"):
+ case s == "om" || strings.HasSuffix(s, ".om"):
+ case s == "uk" || strings.HasSuffix(s, ".uk"):
+ case s == "uk.com" || strings.HasSuffix(s, ".uk.com"):
+ case s == "tw" || strings.HasSuffix(s, ".tw"):
+ case s == "zw" || strings.HasSuffix(s, ".zw"):
+ case s == "xn--p1ai" || strings.HasSuffix(s, ".xn--p1ai"):
+ // xn--p1ai is Russian-Cyrillic "рф".
+ default:
+ continue
+ }
+ }
+
+ rules = append(rules, s)
+
+ nt, wildcard := nodeTypeNormal, false
+ switch {
+ case strings.HasPrefix(s, "*."):
+ s, nt = s[2:], nodeTypeParentOnly
+ wildcard = true
+ case strings.HasPrefix(s, "!"):
+ s, nt = s[1:], nodeTypeException
+ }
+ labels := strings.Split(s, ".")
+ for n, i := &root, len(labels)-1; i >= 0; i-- {
+ label := labels[i]
+ n = n.child(label)
+ if i == 0 {
+ if nt != nodeTypeParentOnly && n.nodeType == nodeTypeParentOnly {
+ n.nodeType = nt
+ }
+ n.icann = n.icann && icann
+ n.wildcard = n.wildcard || wildcard
+ }
+ labelsMap[label] = true
+ }
+ }
+ labelsList = make([]string, 0, len(labelsMap))
+ for label := range labelsMap {
+ labelsList = append(labelsList, label)
+ }
+ sort.Strings(labelsList)
+
+ p := printReal
+ if *test {
+ p = printTest
+ }
+ if err := p(buf, &root); err != nil {
+ return err
+ }
+
+ b, err := format.Source(buf.Bytes())
+ if err != nil {
+ return err
+ }
+ _, err = os.Stdout.Write(b)
+ return err
+}
+
+func printTest(w io.Writer, n *node) error {
+ fmt.Fprintf(w, "// generated by go run gen.go; DO NOT EDIT\n\n")
+ fmt.Fprintf(w, "package publicsuffix\n\nvar rules = [...]string{\n")
+ for _, rule := range rules {
+ fmt.Fprintf(w, "%q,\n", rule)
+ }
+ fmt.Fprintf(w, "}\n\nvar nodeLabels = [...]string{\n")
+ if err := n.walk(w, printNodeLabel); err != nil {
+ return err
+ }
+ fmt.Fprintf(w, "}\n")
+ return nil
+}
+
+func printReal(w io.Writer, n *node) error {
+ const header = `// generated by go run gen.go; DO NOT EDIT
+
+package publicsuffix
+
+const version = %q
+
+const (
+ nodesBitsChildren = %d
+ nodesBitsICANN = %d
+ nodesBitsTextOffset = %d
+ nodesBitsTextLength = %d
+
+ childrenBitsWildcard = %d
+ childrenBitsNodeType = %d
+ childrenBitsHi = %d
+ childrenBitsLo = %d
+)
+
+const (
+ nodeTypeNormal = %d
+ nodeTypeException = %d
+ nodeTypeParentOnly = %d
+)
+
+// numTLD is the number of top level domains.
+const numTLD = %d
+
+`
+ fmt.Fprintf(w, header, *version,
+ nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength,
+ childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo,
+ nodeTypeNormal, nodeTypeException, nodeTypeParentOnly, len(n.children))
+
+ text := makeText()
+ if text == "" {
+ return fmt.Errorf("internal error: makeText returned no text")
+ }
+ for _, label := range labelsList {
+ offset, length := strings.Index(text, label), len(label)
+ if offset < 0 {
+ return fmt.Errorf("internal error: could not find %q in text %q", label, text)
+ }
+ maxTextOffset, maxTextLength = max(maxTextOffset, offset), max(maxTextLength, length)
+ if offset >= 1<<nodesBitsTextOffset || length >= 1<<nodesBitsTextLength {
+ return fmt.Errorf("text offset/length is too large: %d/%d", offset, length)
+ }
+ labelEncoding[label] = uint32(offset)<<nodesBitsTextLength | uint32(length)
+ }
+ fmt.Fprintf(w, "// Text is the combined text of all labels.\nconst text = ")
+ for len(text) > 0 {
+ n, plus := len(text), ""
+ if n > 64 {
+ n, plus = 64, " +"
+ }
+ fmt.Fprintf(w, "%q%s\n", text[:n], plus)
+ text = text[n:]
+ }
+
+ n.walk(w, assignIndexes)
+
+ fmt.Fprintf(w, `
+
+// nodes is the list of nodes. Each node is represented as a uint32, which
+// encodes the node's children, wildcard bit and node type (as an index into
+// the children array), ICANN bit and text.
+//
+// In the //-comment after each node's data, the nodes indexes of the children
+// are formatted as (n0x1234-n0x1256), with * denoting the wildcard bit. The
+// nodeType is printed as + for normal, ! for exception, and o for parent-only
+// nodes that have children but don't match a domain label in their own right.
+// An I denotes an ICANN domain.
+//
+// The layout within the uint32, from MSB to LSB, is:
+// [%2d bits] unused
+// [%2d bits] children index
+// [%2d bits] ICANN bit
+// [%2d bits] text index
+// [%2d bits] text length
+var nodes = [...]uint32{
+`,
+ 32-nodesBitsChildren-nodesBitsICANN-nodesBitsTextOffset-nodesBitsTextLength,
+ nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength)
+ if err := n.walk(w, printNode); err != nil {
+ return err
+ }
+ fmt.Fprintf(w, `}
+
+// children is the list of nodes' children, the parent's wildcard bit and the
+// parent's node type. If a node has no children then their children index
+// will be in the range [0, 6), depending on the wildcard bit and node type.
+//
+// The layout within the uint32, from MSB to LSB, is:
+// [%2d bits] unused
+// [%2d bits] wildcard bit
+// [%2d bits] node type
+// [%2d bits] high nodes index (exclusive) of children
+// [%2d bits] low nodes index (inclusive) of children
+var children=[...]uint32{
+`,
+ 32-childrenBitsWildcard-childrenBitsNodeType-childrenBitsHi-childrenBitsLo,
+ childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo)
+ for i, c := range childrenEncoding {
+ s := "---------------"
+ lo := c & (1<<childrenBitsLo - 1)
+ hi := (c >> childrenBitsLo) & (1<<childrenBitsHi - 1)
+ if lo != hi {
+ s = fmt.Sprintf("n0x%04x-n0x%04x", lo, hi)
+ }
+ nodeType := int(c>>(childrenBitsLo+childrenBitsHi)) & (1<<childrenBitsNodeType - 1)
+ wildcard := c>>(childrenBitsLo+childrenBitsHi+childrenBitsNodeType) != 0
+ fmt.Fprintf(w, "0x%08x, // c0x%04x (%s)%s %s\n",
+ c, i, s, wildcardStr(wildcard), nodeTypeStr(nodeType))
+ }
+ fmt.Fprintf(w, "}\n\n")
+ fmt.Fprintf(w, "// max children %d (capacity %d)\n", maxChildren, 1<<nodesBitsChildren-1)
+ fmt.Fprintf(w, "// max text offset %d (capacity %d)\n", maxTextOffset, 1<<nodesBitsTextOffset-1)
+ fmt.Fprintf(w, "// max text length %d (capacity %d)\n", maxTextLength, 1<<nodesBitsTextLength-1)
+ fmt.Fprintf(w, "// max hi %d (capacity %d)\n", maxHi, 1<<childrenBitsHi-1)
+ fmt.Fprintf(w, "// max lo %d (capacity %d)\n", maxLo, 1<<childrenBitsLo-1)
+ return nil
+}
+
+type node struct {
+ label string
+ nodeType int
+ icann bool
+ wildcard bool
+ // nodesIndex and childrenIndex are the index of this node in the nodes
+ // and the index of its children offset/length in the children arrays.
+ nodesIndex, childrenIndex int
+ // firstChild is the index of this node's first child, or zero if this
+ // node has no children.
+ firstChild int
+ // children are the node's children, in strictly increasing node label order.
+ children []*node
+}
+
+func (n *node) walk(w io.Writer, f func(w1 io.Writer, n1 *node) error) error {
+ if err := f(w, n); err != nil {
+ return err
+ }
+ for _, c := range n.children {
+ if err := c.walk(w, f); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// child returns the child of n with the given label. The child is created if
+// it did not exist beforehand.
+func (n *node) child(label string) *node {
+ for _, c := range n.children {
+ if c.label == label {
+ return c
+ }
+ }
+ c := &node{
+ label: label,
+ nodeType: nodeTypeParentOnly,
+ icann: true,
+ }
+ n.children = append(n.children, c)
+ sort.Sort(byLabel(n.children))
+ return c
+}
+
+type byLabel []*node
+
+func (b byLabel) Len() int { return len(b) }
+func (b byLabel) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byLabel) Less(i, j int) bool { return b[i].label < b[j].label }
+
+var nextNodesIndex int
+
+// childrenEncoding are the encoded entries in the generated children array.
+// All these pre-defined entries have no children.
+var childrenEncoding = []uint32{
+ 0 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeNormal.
+ 1 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeException.
+ 2 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeParentOnly.
+ 4 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeNormal.
+ 5 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeException.
+ 6 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeParentOnly.
+}
+
+var firstCallToAssignIndexes = true
+
+func assignIndexes(w io.Writer, n *node) error {
+ if len(n.children) != 0 {
+ // Assign nodesIndex.
+ n.firstChild = nextNodesIndex
+ for _, c := range n.children {
+ c.nodesIndex = nextNodesIndex
+ nextNodesIndex++
+ }
+
+ // The root node's children is implicit.
+ if firstCallToAssignIndexes {
+ firstCallToAssignIndexes = false
+ return nil
+ }
+
+ // Assign childrenIndex.
+ maxChildren = max(maxChildren, len(childrenEncoding))
+ if len(childrenEncoding) >= 1<<nodesBitsChildren {
+ return fmt.Errorf("children table is too large")
+ }
+ n.childrenIndex = len(childrenEncoding)
+ lo := uint32(n.firstChild)
+ hi := lo + uint32(len(n.children))
+ maxLo, maxHi = u32max(maxLo, lo), u32max(maxHi, hi)
+ if lo >= 1<<childrenBitsLo || hi >= 1<<childrenBitsHi {
+ return fmt.Errorf("children lo/hi is too large: %d/%d", lo, hi)
+ }
+ enc := hi<<childrenBitsLo | lo
+ enc |= uint32(n.nodeType) << (childrenBitsLo + childrenBitsHi)
+ if n.wildcard {
+ enc |= 1 << (childrenBitsLo + childrenBitsHi + childrenBitsNodeType)
+ }
+ childrenEncoding = append(childrenEncoding, enc)
+ } else {
+ n.childrenIndex = n.nodeType
+ if n.wildcard {
+ n.childrenIndex += numNodeType
+ }
+ }
+ return nil
+}
+
+func printNode(w io.Writer, n *node) error {
+ for _, c := range n.children {
+ s := "---------------"
+ if len(c.children) != 0 {
+ s = fmt.Sprintf("n0x%04x-n0x%04x", c.firstChild, c.firstChild+len(c.children))
+ }
+ encoding := labelEncoding[c.label]
+ if c.icann {
+ encoding |= 1 << (nodesBitsTextLength + nodesBitsTextOffset)
+ }
+ encoding |= uint32(c.childrenIndex) << (nodesBitsTextLength + nodesBitsTextOffset + nodesBitsICANN)
+ fmt.Fprintf(w, "0x%08x, // n0x%04x c0x%04x (%s)%s %s %s %s\n",
+ encoding, c.nodesIndex, c.childrenIndex, s, wildcardStr(c.wildcard),
+ nodeTypeStr(c.nodeType), icannStr(c.icann), c.label,
+ )
+ }
+ return nil
+}
+
+func printNodeLabel(w io.Writer, n *node) error {
+ for _, c := range n.children {
+ fmt.Fprintf(w, "%q,\n", c.label)
+ }
+ return nil
+}
+
+func icannStr(icann bool) string {
+ if icann {
+ return "I"
+ }
+ return " "
+}
+
+func wildcardStr(wildcard bool) string {
+ if wildcard {
+ return "*"
+ }
+ return " "
+}
+
+// makeText combines all the strings in labelsList to form one giant string.
+// If the crush flag is true, then overlapping strings will be merged: "arpa"
+// and "parliament" could yield "arparliament".
+func makeText() string {
+ if !*crush {
+ return strings.Join(labelsList, "")
+ }
+
+ beforeLength := 0
+ for _, s := range labelsList {
+ beforeLength += len(s)
+ }
+
+ // Make a copy of labelsList.
+ ss := append(make([]string, 0, len(labelsList)), labelsList...)
+
+ // Remove strings that are substrings of other strings.
+ for changed := true; changed; {
+ changed = false
+ for i, s := range ss {
+ if s == "" {
+ continue
+ }
+ for j, t := range ss {
+ if i != j && t != "" && strings.Contains(s, t) {
+ changed = true
+ ss[j] = ""
+ }
+ }
+ }
+ }
+
+ // Remove the empty strings.
+ sort.Strings(ss)
+ for len(ss) > 0 && ss[0] == "" {
+ ss = ss[1:]
+ }
+
+ // Join strings where one suffix matches another prefix.
+ for {
+ // Find best i, j, k such that ss[i][len-k:] == ss[j][:k],
+ // maximizing overlap length k.
+ besti := -1
+ bestj := -1
+ bestk := 0
+ for i, s := range ss {
+ if s == "" {
+ continue
+ }
+ for j, t := range ss {
+ if i == j {
+ continue
+ }
+ for k := bestk + 1; k <= len(s) && k <= len(t); k++ {
+ if s[len(s)-k:] == t[:k] {
+ besti = i
+ bestj = j
+ bestk = k
+ }
+ }
+ }
+ }
+ if bestk > 0 {
+ if *v {
+ fmt.Fprintf(os.Stderr, "%d-length overlap at (%4d,%4d) out of (%4d,%4d): %q and %q\n",
+ bestk, besti, bestj, len(ss), len(ss), ss[besti], ss[bestj])
+ }
+ ss[besti] += ss[bestj][bestk:]
+ ss[bestj] = ""
+ continue
+ }
+ break
+ }
+
+ text := strings.Join(ss, "")
+ if *v {
+ fmt.Fprintf(os.Stderr, "crushed %d bytes to become %d bytes\n", beforeLength, len(text))
+ }
+ return text
+}
diff --git a/publicsuffix/list.go b/publicsuffix/list.go
new file mode 100644
index 0000000..9419ca9
--- /dev/null
+++ b/publicsuffix/list.go
@@ -0,0 +1,133 @@
+// Copyright 2012 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.
+
+// Package publicsuffix provides a public suffix list based on data from
+// http://publicsuffix.org/. A public suffix is one under which Internet users
+// can directly register names.
+package publicsuffix // import "golang.org/x/net/publicsuffix"
+
+// TODO: specify case sensitivity and leading/trailing dot behavior for
+// func PublicSuffix and func EffectiveTLDPlusOne.
+
+import (
+ "fmt"
+ "net/http/cookiejar"
+ "strings"
+)
+
+// List implements the cookiejar.PublicSuffixList interface by calling the
+// PublicSuffix function.
+var List cookiejar.PublicSuffixList = list{}
+
+type list struct{}
+
+func (list) PublicSuffix(domain string) string {
+ ps, _ := PublicSuffix(domain)
+ return ps
+}
+
+func (list) String() string {
+ return version
+}
+
+// PublicSuffix returns the public suffix of the domain using a copy of the
+// publicsuffix.org database compiled into the library.
+//
+// icann is whether the public suffix is managed by the Internet Corporation
+// for Assigned Names and Numbers. If not, the public suffix is privately
+// managed. For example, foo.org and foo.co.uk are ICANN domains,
+// foo.dyndns.org and foo.blogspot.co.uk are private domains.
+//
+// Use cases for distinguishing ICANN domains like foo.com from private
+// domains like foo.appspot.com can be found at
+// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
+func PublicSuffix(domain string) (publicSuffix string, icann bool) {
+ lo, hi := uint32(0), uint32(numTLD)
+ s, suffix, wildcard := domain, len(domain), false
+loop:
+ for {
+ dot := strings.LastIndex(s, ".")
+ if wildcard {
+ suffix = 1 + dot
+ }
+ if lo == hi {
+ break
+ }
+ f := find(s[1+dot:], lo, hi)
+ if f == notFound {
+ break
+ }
+
+ u := nodes[f] >> (nodesBitsTextOffset + nodesBitsTextLength)
+ icann = u&(1<<nodesBitsICANN-1) != 0
+ u >>= nodesBitsICANN
+ u = children[u&(1<<nodesBitsChildren-1)]
+ lo = u & (1<<childrenBitsLo - 1)
+ u >>= childrenBitsLo
+ hi = u & (1<<childrenBitsHi - 1)
+ u >>= childrenBitsHi
+ switch u & (1<<childrenBitsNodeType - 1) {
+ case nodeTypeNormal:
+ suffix = 1 + dot
+ case nodeTypeException:
+ suffix = 1 + len(s)
+ break loop
+ }
+ u >>= childrenBitsNodeType
+ wildcard = u&(1<<childrenBitsWildcard-1) != 0
+
+ if dot == -1 {
+ break
+ }
+ s = s[:dot]
+ }
+ if suffix == len(domain) {
+ // If no rules match, the prevailing rule is "*".
+ return domain[1+strings.LastIndex(domain, "."):], icann
+ }
+ return domain[suffix:], icann
+}
+
+const notFound uint32 = 1<<32 - 1
+
+// find returns the index of the node in the range [lo, hi) whose label equals
+// label, or notFound if there is no such node. The range is assumed to be in
+// strictly increasing node label order.
+func find(label string, lo, hi uint32) uint32 {
+ for lo < hi {
+ mid := lo + (hi-lo)/2
+ s := nodeLabel(mid)
+ if s < label {
+ lo = mid + 1
+ } else if s == label {
+ return mid
+ } else {
+ hi = mid
+ }
+ }
+ return notFound
+}
+
+// nodeLabel returns the label for the i'th node.
+func nodeLabel(i uint32) string {
+ x := nodes[i]
+ length := x & (1<<nodesBitsTextLength - 1)
+ x >>= nodesBitsTextLength
+ offset := x & (1<<nodesBitsTextOffset - 1)
+ return text[offset : offset+length]
+}
+
+// EffectiveTLDPlusOne returns the effective top level domain plus one more
+// label. For example, the eTLD+1 for "foo.bar.golang.org" is "golang.org".
+func EffectiveTLDPlusOne(domain string) (string, error) {
+ suffix, _ := PublicSuffix(domain)
+ if len(domain) <= len(suffix) {
+ return "", fmt.Errorf("publicsuffix: cannot derive eTLD+1 for domain %q", domain)
+ }
+ i := len(domain) - len(suffix) - 1
+ if domain[i] != '.' {
+ return "", fmt.Errorf("publicsuffix: invalid public suffix %q for domain %q", suffix, domain)
+ }
+ return domain[1+strings.LastIndex(domain[:i], "."):], nil
+}
diff --git a/publicsuffix/list_test.go b/publicsuffix/list_test.go
new file mode 100644
index 0000000..f231de4
--- /dev/null
+++ b/publicsuffix/list_test.go
@@ -0,0 +1,416 @@
+// Copyright 2012 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.
+
+package publicsuffix
+
+import (
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestNodeLabel(t *testing.T) {
+ for i, want := range nodeLabels {
+ got := nodeLabel(uint32(i))
+ if got != want {
+ t.Errorf("%d: got %q, want %q", i, got, want)
+ }
+ }
+}
+
+func TestFind(t *testing.T) {
+ testCases := []string{
+ "",
+ "a",
+ "a0",
+ "aaaa",
+ "ao",
+ "ap",
+ "ar",
+ "aro",
+ "arp",
+ "arpa",
+ "arpaa",
+ "arpb",
+ "az",
+ "b",
+ "b0",
+ "ba",
+ "z",
+ "zu",
+ "zv",
+ "zw",
+ "zx",
+ "zy",
+ "zz",
+ "zzzz",
+ }
+ for _, tc := range testCases {
+ got := find(tc, 0, numTLD)
+ want := notFound
+ for i := uint32(0); i < numTLD; i++ {
+ if tc == nodeLabel(i) {
+ want = i
+ break
+ }
+ }
+ if got != want {
+ t.Errorf("%q: got %d, want %d", tc, got, want)
+ }
+ }
+}
+
+func TestICANN(t *testing.T) {
+ testCases := map[string]bool{
+ "foo.org": true,
+ "foo.co.uk": true,
+ "foo.dyndns.org": false,
+ "foo.go.dyndns.org": false,
+ "foo.blogspot.co.uk": false,
+ "foo.intranet": false,
+ }
+ for domain, want := range testCases {
+ _, got := PublicSuffix(domain)
+ if got != want {
+ t.Errorf("%q: got %v, want %v", domain, got, want)
+ }
+ }
+}
+
+var publicSuffixTestCases = []struct {
+ domain, want string
+}{
+ // Empty string.
+ {"", ""},
+
+ // The .ao rules are:
+ // ao
+ // ed.ao
+ // gv.ao
+ // og.ao
+ // co.ao
+ // pb.ao
+ // it.ao
+ {"ao", "ao"},
+ {"www.ao", "ao"},
+ {"pb.ao", "pb.ao"},
+ {"www.pb.ao", "pb.ao"},
+ {"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
+
+ // The .ar rules are:
+ // ar
+ // com.ar
+ // edu.ar
+ // gob.ar
+ // gov.ar
+ // int.ar
+ // mil.ar
+ // net.ar
+ // org.ar
+ // tur.ar
+ // blogspot.com.ar
+ {"ar", "ar"},
+ {"www.ar", "ar"},
+ {"nic.ar", "ar"},
+ {"www.nic.ar", "ar"},
+ {"com.ar", "com.ar"},
+ {"www.com.ar", "com.ar"},
+ {"blogspot.com.ar", "blogspot.com.ar"},
+ {"www.blogspot.com.ar", "blogspot.com.ar"},
+ {"www.xxx.yyy.zzz.blogspot.com.ar", "blogspot.com.ar"},
+ {"logspot.com.ar", "com.ar"},
+ {"zlogspot.com.ar", "com.ar"},
+ {"zblogspot.com.ar", "com.ar"},
+
+ // The .arpa rules are:
+ // arpa
+ // e164.arpa
+ // in-addr.arpa
+ // ip6.arpa
+ // iris.arpa
+ // uri.arpa
+ // urn.arpa
+ {"arpa", "arpa"},
+ {"www.arpa", "arpa"},
+ {"urn.arpa", "urn.arpa"},
+ {"www.urn.arpa", "urn.arpa"},
+ {"www.xxx.yyy.zzz.urn.arpa", "urn.arpa"},
+
+ // The relevant {kobe,kyoto}.jp rules are:
+ // jp
+ // *.kobe.jp
+ // !city.kobe.jp
+ // kyoto.jp
+ // ide.kyoto.jp
+ {"jp", "jp"},
+ {"kobe.jp", "jp"},
+ {"c.kobe.jp", "c.kobe.jp"},
+ {"b.c.kobe.jp", "c.kobe.jp"},
+ {"a.b.c.kobe.jp", "c.kobe.jp"},
+ {"city.kobe.jp", "kobe.jp"},
+ {"www.city.kobe.jp", "kobe.jp"},
+ {"kyoto.jp", "kyoto.jp"},
+ {"test.kyoto.jp", "kyoto.jp"},
+ {"ide.kyoto.jp", "ide.kyoto.jp"},
+ {"b.ide.kyoto.jp", "ide.kyoto.jp"},
+ {"a.b.ide.kyoto.jp", "ide.kyoto.jp"},
+
+ // The .tw rules are:
+ // tw
+ // edu.tw
+ // gov.tw
+ // mil.tw
+ // com.tw
+ // net.tw
+ // org.tw
+ // idv.tw
+ // game.tw
+ // ebiz.tw
+ // club.tw
+ // 網路.tw (xn--zf0ao64a.tw)
+ // 組織.tw (xn--uc0atv.tw)
+ // 商業.tw (xn--czrw28b.tw)
+ // blogspot.tw
+ {"tw", "tw"},
+ {"aaa.tw", "tw"},
+ {"www.aaa.tw", "tw"},
+ {"xn--czrw28b.aaa.tw", "tw"},
+ {"edu.tw", "edu.tw"},
+ {"www.edu.tw", "edu.tw"},
+ {"xn--czrw28b.edu.tw", "edu.tw"},
+ {"xn--czrw28b.tw", "xn--czrw28b.tw"},
+ {"www.xn--czrw28b.tw", "xn--czrw28b.tw"},
+ {"xn--uc0atv.xn--czrw28b.tw", "xn--czrw28b.tw"},
+ {"xn--kpry57d.tw", "tw"},
+
+ // The .uk rules are:
+ // uk
+ // ac.uk
+ // co.uk
+ // gov.uk
+ // ltd.uk
+ // me.uk
+ // net.uk
+ // nhs.uk
+ // org.uk
+ // plc.uk
+ // police.uk
+ // *.sch.uk
+ // blogspot.co.uk
+ {"uk", "uk"},
+ {"aaa.uk", "uk"},
+ {"www.aaa.uk", "uk"},
+ {"mod.uk", "uk"},
+ {"www.mod.uk", "uk"},
+ {"sch.uk", "uk"},
+ {"mod.sch.uk", "mod.sch.uk"},
+ {"www.sch.uk", "www.sch.uk"},
+ {"blogspot.co.uk", "blogspot.co.uk"},
+ {"blogspot.nic.uk", "uk"},
+ {"blogspot.sch.uk", "blogspot.sch.uk"},
+
+ // The .рф rules are
+ // рф (xn--p1ai)
+ {"xn--p1ai", "xn--p1ai"},
+ {"aaa.xn--p1ai", "xn--p1ai"},
+ {"www.xxx.yyy.xn--p1ai", "xn--p1ai"},
+
+ // The .zw rules are:
+ // *.zw
+ {"zw", "zw"},
+ {"www.zw", "www.zw"},
+ {"zzz.zw", "zzz.zw"},
+ {"www.zzz.zw", "zzz.zw"},
+ {"www.xxx.yyy.zzz.zw", "zzz.zw"},
+
+ // There are no .nosuchtld rules.
+ {"nosuchtld", "nosuchtld"},
+ {"foo.nosuchtld", "nosuchtld"},
+ {"bar.foo.nosuchtld", "nosuchtld"},
+}
+
+func BenchmarkPublicSuffix(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, tc := range publicSuffixTestCases {
+ List.PublicSuffix(tc.domain)
+ }
+ }
+}
+
+func TestPublicSuffix(t *testing.T) {
+ for _, tc := range publicSuffixTestCases {
+ got := List.PublicSuffix(tc.domain)
+ if got != tc.want {
+ t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
+ }
+ }
+}
+
+func TestSlowPublicSuffix(t *testing.T) {
+ for _, tc := range publicSuffixTestCases {
+ got := slowPublicSuffix(tc.domain)
+ if got != tc.want {
+ t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
+ }
+ }
+}
+
+// slowPublicSuffix implements the canonical (but O(number of rules)) public
+// suffix algorithm described at http://publicsuffix.org/list/.
+//
+// 1. Match domain against all rules and take note of the matching ones.
+// 2. If no rules match, the prevailing rule is "*".
+// 3. If more than one rule matches, the prevailing rule is the one which is an exception rule.
+// 4. If there is no matching exception rule, the prevailing rule is the one with the most labels.
+// 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label.
+// 6. The public suffix is the set of labels from the domain which directly match the labels of the prevailing rule (joined by dots).
+// 7. The registered or registrable domain is the public suffix plus one additional label.
+//
+// This function returns the public suffix, not the registrable domain, and so
+// it stops after step 6.
+func slowPublicSuffix(domain string) string {
+ match := func(rulePart, domainPart string) bool {
+ switch rulePart[0] {
+ case '*':
+ return true
+ case '!':
+ return rulePart[1:] == domainPart
+ }
+ return rulePart == domainPart
+ }
+
+ domainParts := strings.Split(domain, ".")
+ var matchingRules [][]string
+
+loop:
+ for _, rule := range rules {
+ ruleParts := strings.Split(rule, ".")
+ if len(domainParts) < len(ruleParts) {
+ continue
+ }
+ for i := range ruleParts {
+ rulePart := ruleParts[len(ruleParts)-1-i]
+ domainPart := domainParts[len(domainParts)-1-i]
+ if !match(rulePart, domainPart) {
+ continue loop
+ }
+ }
+ matchingRules = append(matchingRules, ruleParts)
+ }
+ if len(matchingRules) == 0 {
+ matchingRules = append(matchingRules, []string{"*"})
+ } else {
+ sort.Sort(byPriority(matchingRules))
+ }
+ prevailing := matchingRules[0]
+ if prevailing[0][0] == '!' {
+ prevailing = prevailing[1:]
+ }
+ if prevailing[0][0] == '*' {
+ replaced := domainParts[len(domainParts)-len(prevailing)]
+ prevailing = append([]string{replaced}, prevailing[1:]...)
+ }
+ return strings.Join(prevailing, ".")
+}
+
+type byPriority [][]string
+
+func (b byPriority) Len() int { return len(b) }
+func (b byPriority) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byPriority) Less(i, j int) bool {
+ if b[i][0][0] == '!' {
+ return true
+ }
+ if b[j][0][0] == '!' {
+ return false
+ }
+ return len(b[i]) > len(b[j])
+}
+
+// eTLDPlusOneTestCases come from
+// https://github.com/publicsuffix/list/blob/master/tests/test_psl.txt
+var eTLDPlusOneTestCases = []struct {
+ domain, want string
+}{
+ // Empty input.
+ {"", ""},
+ // Unlisted TLD.
+ {"example", ""},
+ {"example.example", "example.example"},
+ {"b.example.example", "example.example"},
+ {"a.b.example.example", "example.example"},
+ // TLD with only 1 rule.
+ {"biz", ""},
+ {"domain.biz", "domain.biz"},
+ {"b.domain.biz", "domain.biz"},
+ {"a.b.domain.biz", "domain.biz"},
+ // TLD with some 2-level rules.
+ {"com", ""},
+ {"example.com", "example.com"},
+ {"b.example.com", "example.com"},
+ {"a.b.example.com", "example.com"},
+ {"uk.com", ""},
+ {"example.uk.com", "example.uk.com"},
+ {"b.example.uk.com", "example.uk.com"},
+ {"a.b.example.uk.com", "example.uk.com"},
+ {"test.ac", "test.ac"},
+ // TLD with only 1 (wildcard) rule.
+ {"il", ""},
+ {"c.il", ""},
+ {"b.c.il", "b.c.il"},
+ {"a.b.c.il", "b.c.il"},
+ // More complex TLD.
+ {"jp", ""},
+ {"test.jp", "test.jp"},
+ {"www.test.jp", "test.jp"},
+ {"ac.jp", ""},
+ {"test.ac.jp", "test.ac.jp"},
+ {"www.test.ac.jp", "test.ac.jp"},
+ {"kyoto.jp", ""},
+ {"test.kyoto.jp", "test.kyoto.jp"},
+ {"ide.kyoto.jp", ""},
+ {"b.ide.kyoto.jp", "b.ide.kyoto.jp"},
+ {"a.b.ide.kyoto.jp", "b.ide.kyoto.jp"},
+ {"c.kobe.jp", ""},
+ {"b.c.kobe.jp", "b.c.kobe.jp"},
+ {"a.b.c.kobe.jp", "b.c.kobe.jp"},
+ {"city.kobe.jp", "city.kobe.jp"},
+ {"www.city.kobe.jp", "city.kobe.jp"},
+ // TLD with a wildcard rule and exceptions.
+ {"ck", ""},
+ {"test.ck", ""},
+ {"b.test.ck", "b.test.ck"},
+ {"a.b.test.ck", "b.test.ck"},
+ {"www.ck", "www.ck"},
+ {"www.www.ck", "www.ck"},
+ // US K12.
+ {"us", ""},
+ {"test.us", "test.us"},
+ {"www.test.us", "test.us"},
+ {"ak.us", ""},
+ {"test.ak.us", "test.ak.us"},
+ {"www.test.ak.us", "test.ak.us"},
+ {"k12.ak.us", ""},
+ {"test.k12.ak.us", "test.k12.ak.us"},
+ {"www.test.k12.ak.us", "test.k12.ak.us"},
+ // Punycoded IDN labels
+ {"xn--85x722f.com.cn", "xn--85x722f.com.cn"},
+ {"xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
+ {"www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
+ {"shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn"},
+ {"xn--55qx5d.cn", ""},
+ {"xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
+ {"www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
+ {"shishi.xn--fiqs8s", "shishi.xn--fiqs8s"},
+ {"xn--fiqs8s", ""},
+}
+
+func TestEffectiveTLDPlusOne(t *testing.T) {
+ for _, tc := range eTLDPlusOneTestCases {
+ got, _ := EffectiveTLDPlusOne(tc.domain)
+ if got != tc.want {
+ t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
+ }
+ }
+}
diff --git a/publicsuffix/table.go b/publicsuffix/table.go
new file mode 100644
index 0000000..9b8c721
--- /dev/null
+++ b/publicsuffix/table.go
@@ -0,0 +1,8492 @@
+// generated by go run gen.go; DO NOT EDIT
+
+package publicsuffix
+
+const version = "publicsuffix.org's public_suffix_list.dat, git revision 0de6f8e (2015-07-15)"
+
+const (
+ nodesBitsChildren = 9
+ nodesBitsICANN = 1
+ nodesBitsTextOffset = 15
+ nodesBitsTextLength = 6
+
+ childrenBitsWildcard = 1
+ childrenBitsNodeType = 2
+ childrenBitsHi = 14
+ childrenBitsLo = 14
+)
+
+const (
+ nodeTypeNormal = 0
+ nodeTypeException = 1
+ nodeTypeParentOnly = 2
+)
+
+// numTLD is the number of top level domains.
+const numTLD = 1337
+
+// Text is the combined text of all labels.
+const text = "biomutashinainfoggiabirdartdecodynaliascoli-picenord-frontierbir" +
+ "kenesoddtangenovaravennagatorockartuzyunsakakinokiabirthplacevje" +
+ "-og-hornnesangostrodawarabjarkoyurihonjournalistjordalshalsenfsh" +
+ "ostre-totenkawabjerkreimmobilienhsanjotatsunostrolekaniepcexpose" +
+ "dogawarabikomaezakirunortonsbergminakamichigangwonikonantananger" +
+ "bjugninohelpaleostrowiecasertairabloombergbauernuorokunohealthca" +
+ "reerschmidtre-gauldalottokashikinuyamanouchikuhokuryugasakitaura" +
+ "yasudabluedatsunanjogaszkolahppiacenzachpomorskieninomiyakonojos" +
+ "oyrovnostrowwlkpmgmodalenirasakinvestmentsannanishiazais-a-candi" +
+ "datexasiabmsannohelsinkitakamiizumisanofieldyndns-freemasonryusu" +
+ "harabmweirbnpparibaselburgmxboxeroxjaworznobomloanswatch-and-clo" +
+ "ckerbondyndns-homednsanokasumigaurawa-mazowszexeterbonnishigotvs" +
+ "antabarbarabootsantacruzsantafedjelenia-goraboschlesischesanukis" +
+ "-a-catererbostikasuyakutiabostonakijinsekikogentingretajimakaneg" +
+ "asakitagawabotanicalgardenishiharabotanicgardenishiizunazukis-a-" +
+ "celticsfanishikatakayamatta-varjjatattoolsztynsettlersaotomelbou" +
+ "rnextraspace-to-rentalsapodhalebotanynysadoesntexistanbullensake" +
+ "rboutiquebecasinore-og-uvdalouvrepair-traffic-controlleyusuisser" +
+ "vegame-serverdalovenneslaskerrylogisticsapporobozentsujiiebrades" +
+ "corporationishikatsuragivingrimstadyndns-ip6brandywinevalleyuulm" +
+ "inamibosogndalowiczest-le-patrondheimperiabrasiljan-mayenishikaw" +
+ "azukaneyamaxunjargabresciabrindisiciliabristolgalsacebritishcolu" +
+ "mbialowiezagannakadomari-elasticbeanstalkaszubyuzawabroadcastleb" +
+ "timnetzgorabroadwayuzhno-sakhalinskatowicebroke-itaxihuanishimer" +
+ "abrokerrypropertiesaratovalleaostavropolicebronnoysundyndns-mail" +
+ "ubindalucaniabrothermesaverdealstahaugesundyndns-office-on-the-w" +
+ "ebcambridgestonewportlligatewaybrumunddaluccapetownishinomiyashi" +
+ "ronobrunelblagdenesnaaseralingenkainanaejrietiendaburyatiabrusse" +
+ "lsardegnamsosnowiecastresistancebruxellesardiniabryanskleppalerm" +
+ "omasvuotnakatsugawabryneuesarlucernebuyshousesarpsborgripebuzeni" +
+ "shinoomotegovtgorybuzzgorzeleccollegersundyndns-picsarufutsunomi" +
+ "yawakasaikaitakoelnishinoshimabwfarmequipmentjeldsundyndns-remot" +
+ "egildeskalmykiabzhitomirkutskodjeffersonishiokoppegardyndns-serv" +
+ "erbaniacntoyonezawacolognewjerseycolonialwilliamsburgrpanamacolo" +
+ "radoplateaudiocolumbusantiquest-a-la-masioncommunitydaluxembourg" +
+ "ruecomobaracompanycompute-1computerhistoryofscience-fictioncomse" +
+ "curitysfjordcondoshichikashukujitawaraconferenceconstructioncons" +
+ "uladollsatxn--0trq7p7nnconsultanthropologyconsultingvolluxurycon" +
+ "tactoyonocontemporaryartgallerybnikahokutogitsuldaluzerncontract" +
+ "orskenconventureshinodesashibetsuikindleikangercookingchannelver" +
+ "uminamiechizencoolbia-tempio-olbiatempioolbialystokkecoopocznori" +
+ "lskypescaravantaarparachutinguidellogliastradercopenhagencyclope" +
+ "dicdn77-sslattuminamidaitomangotsukisosakitagatakamoriokamikitay" +
+ "amatotakadacorsicamerakershus-east-1corvettelemarkazunocosenzako" +
+ "panerairguardcostumedio-campidano-mediocampidanomediocouncilviva" +
+ "no-frankivskchristiansburguitarsaudacouponsauheradcoursesavannah" +
+ "gacqhachinoheguris-a-democratoyookarasjohkamiokaminokawanishiaiz" +
+ "ubangecranbrookuwanalyticsaves-the-whalessandria-trani-barletta-" +
+ "andriatranibarlettaandriacreditcardcreditunioncremonashorokanaie" +
+ "crewiiheyaizuwakamatsubushikusakadogawacricketrzyncrimeacrotonew" +
+ "mexicoldwarmiamiastarnbergujolstercrowncrsavonamsskoganeis-a-des" +
+ "ignercruisesaxocuisinellancasterculturalcentertainmentoyosatomsk" +
+ "ddielddanuorrikuzentakatajirissafetysnesayokkaichirurgiens-denti" +
+ "stesbscholarshipschoolcuneocupcakecxn--11b4c3dcyouthdfcbankfhskh" +
+ "abarovskhakassiafinlandfinnoyfirebaseappspotenzamamicrolightingu" +
+ "nmaritimodellinguovdageaidnulsanfranciscotlandfirenzefirestonewy" +
+ "orkshireggio-calabriafirmdaleirfjordfishingoldpointelligencefitj" +
+ "arfitnessettlementoyotsukaidovre-eikerfjalerflesbergushikamifura" +
+ "noshiroomuraflickragerotikamchatkameokameyamashinatsukigatakahar" +
+ "ussiaflightschweizippodlasiellakasamatsudoosandnessjoenfloguchik" +
+ "uzenfloraflorencefloridafloristanohatakahashimamakirkenesciencec" +
+ "entersciencehistoryfloromskogxn--1ck2e1balestrandabergamoarekepn" +
+ "ordlandivtasvuodnakaiwamizawaugustowadaejeonbukarlsoyokote12flow" +
+ "erscientistor-elvdalflsmidthruhereggio-emilia-romagnakanotoddenf" +
+ "lynnhubalsanagochihayaakasakawagoebinagisoccertificationaturalsc" +
+ "iencesnaturelles3-sa-east-1fndfolldalfoodnetworkangerfor-better-" +
+ "thandafor-ourfor-somedizinhistorischescrapper-sitefor-theatreefo" +
+ "rexrothachiojiyahikobeatscrappingzjcbnlforgotdnservicesettsuppor" +
+ "toyouraforli-cesena-forlicesenaforliguriaforsaleirvikharkivalled" +
+ "-aostakinoueforsandasuolodingenfortmissoulan-udefenseljordfortwo" +
+ "rthachirogatakanabeauxartsandcraftsevastopoleforuminamiizukamito" +
+ "ndabayashiogamagoriziafosnesewildlifestylefotoystre-slidrettozaw" +
+ "afredrikstadtveronakamuratakahamaniwakuratelevisionfreiburgfreig" +
+ "htcmwilliamhillfribourgfriuli-v-giuliafriuli-ve-giuliafriuli-veg" +
+ "iuliafriuli-venezia-giuliafriuli-veneziagiuliafriuli-vgiuliafriu" +
+ "liv-giuliafriulive-giuliafriulivegiuliafriulivenezia-giuliafriul" +
+ "iveneziagiuliafriulivgiuliafrlfroganshakotankharkovalledaostakko" +
+ "fuelfrognfrolandfrom-akrehamnfrom-alfrom-arfrom-azlgfrom-capebre" +
+ "tonamiasakuchinotsuchiurakawassamukawataricohdavvesiidazaifudaig" +
+ "odoharuhrfrom-collectionfrom-ctozsdeltajimibungotakadavvenjargam" +
+ "vikhersonfrom-dcheltenham-radio-operaunitelefonicagliaridagawars" +
+ "zawashingtondclkatsushikabelaudiblebesbyglandyndns-weberlincolni" +
+ "shitosashimizunaminamiashigarafrom-degreefrom-flandersharis-a-ge" +
+ "ekhmelnitskiyamashikiyosatohoboleslawiechelyabinskydivingriwatar" +
+ "ailwayfrom-gausdalfrom-higashiagatsumagoizumizakirovogradoyfrom-" +
+ "iafrom-idfrom-ilfrom-in-the-bandaiwafunexusdecorativeartsharpara" +
+ "glidingfrom-kshawaiijimarylandfrom-kyknetnedalfrom-langevagrarbo" +
+ "retumbriafrom-mannosegawafrom-mdfrom-meereshellaspeziafrom-micro" +
+ "softbankhmelnytskyivallee-aosteroyfrom-mnfrom-mochizukiryuohkura" +
+ "from-msherbrookegawafrom-mtnfrom-nchernigovernmentjomeloyalistoc" +
+ "kholmestrandyndns-wikirafrom-ndfrom-nefrom-nhktranaritakurashiki" +
+ "s-a-greenfrom-njcparisor-fronfrom-nminamimakis-a-gurulvikhvallee" +
+ "aosteigenfrom-nvanylvenicefrom-nyfrom-ohtawaramotoineppugliafrom" +
+ "-oketogolfashionfrom-orfrom-paderbornfrom-praxis-a-anarchistoire" +
+ "ggiocalabriafrom-rittogurafrom-schokoladenfrom-sdnipropetrovskla" +
+ "businessebykleclerchernihivgucciprianiigataishinomakikugawatchan" +
+ "dclockatsuyamashikefrom-tnfrom-txn--1ctwolominamatamayukis-a-har" +
+ "d-workerfrom-utazuerichardlikes-piedmontblancashireggioemiliarom" +
+ "agnakasatsunairlinebraskaunbieidsvollfrom-vadsochildrensgardenfr" +
+ "om-vtranbyfrom-wafrom-wielunnerfrom-wvaofrom-wyfrosinonefrostalo" +
+ "wa-wolawafroyahabaghdadfstcgrouparliamentrani-andria-barletta-tr" +
+ "ani-andriafujiiderafujikawaguchikonefujiminohadanotaireshimojis-" +
+ "a-hunterfujinomiyadafujiokayamansionshimokawafujisatoshonairport" +
+ "land-4-salernogiessengerdalaskanittedallasalleaseeklogeshimokita" +
+ "yamafujisawafujishiroishidakabiratorideliveryggeelvinckmpspbalsf" +
+ "jordivttasvuotnakamagayachts3-us-gov-west-1fujiyoshidafukayabear" +
+ "dubaiduckdnsdojoetsurutaharaumakeupowiatmallorcafederationfukuch" +
+ "iyamadafukudominichernivtsicilyfukuis-a-knightraniandriabarletta" +
+ "traniandriafukumitsukefukuokazakisarazure-mobileitungsenfukurois" +
+ "higakishiwadafukusakisofukushimanxn--1lqs03nfukuyamagatakahataka" +
+ "ishimogosenfunabashiriuchinadafunagatakamatsukawafunahashikamiam" +
+ "akusatsumasendaisennangonohejis-a-landscaperugiafundaciofuoiskuj" +
+ "ukuriyamaoris-a-lawyerfuosskoczowinbaltimore-og-romsdalillesandi" +
+ "egotembaixadaukraanghkemerovodkagoshimagnitkakamigaharaholtaleni" +
+ "waizumiotsukumiyamazonawsaarlandgcampobassociates3-eu-west-1furn" +
+ "iturehabikinokawairtelecityeatshimonitayanagis-a-liberalfurubira" +
+ "quarelleasingleshimonosekikawafurudonostiafurukawaharafusodegaur" +
+ "afussagaeroclubmedecincinnativeamericanantiquesquarendalenugfuta" +
+ "bayamaguchinomigawafutboldlygoingnowhere-for-moregontrailroadfut" +
+ "tsurugashimarburgfvgfyis-a-libertarianfylkesbiblackfridayfyresda" +
+ "lhannanmokuizumodenakatombetsulikescandynathomebuiltranoyhannova" +
+ "reservebbshimotsukehanyuzenhapmirumarnardalhappounzenhareidsberg" +
+ "enharstadharvestcelebrationhasamarahasaminami-alpssells-for-ustk" +
+ "arasuyamazoehasudahasvikokonoehatogayahoooshikamaishimodatenris-" +
+ "a-nurseminehatoyamazakitahiroshimarriottransportrapaniimimatakas" +
+ "ugais-a-painteractivegaskimitsubatamicadaqueshimotsumahatsukaich" +
+ "iharahattfjelldalhayashimamotobunkyonanaoshimabariakehazuminobus" +
+ "ells-itravelchannelhembygdsforbundhemneshinichinanhemsedalheroku" +
+ "ssldheroyhgtvarggatravelersinsurancehigashichichibusheyhigashihi" +
+ "roshimanehigashiizumozakitakatakaokamikoaniikappulawyhigashikaga" +
+ "wahigashikagurasoedahigashikawakitaaikitakyushuaiahigashikurumee" +
+ "trdhigashimatsushimarugame-hostinghigashimatsuyamakitaakitadaito" +
+ "igawahigashimurayamalatvuopmidoris-a-patsfanhigashinarusellsyour" +
+ "homeftpaccesshinjournalismolanshinjukumanohigashinehigashiomihac" +
+ "himanchesterhigashiosakasayamamotorcycleshinkamigotoyohashimotok" +
+ "uyamahigashishirakawamatakarazukamiminershinshinotsurfastlyhigas" +
+ "hisumiyoshikawaminamiaikitamidsundhigashitsunotteroyhigashiuraus" +
+ "ukitamotosumidatlantichiryukyuragifuefukihabmerhigashiyamatokori" +
+ "yamanakakogawahigashiyodogawahigashiyoshinogaris-a-personaltrain" +
+ "erhiraizumisatohmarumorimachidahirakatashinagawahiranairtrafficb" +
+ "cghirarahiratsukagawahirayaitakasagooglecodespotrentino-a-adigeh" +
+ "istorichouseshinshirohitachiomiyaginowaniihamatamakawajimarcheap" +
+ "artmentshintokushimahitachiotagoparocherkasyzrankoshigayaltaikis" +
+ "-a-photographerokuapparshintomikasaharahitoyoshimifunehitradingh" +
+ "jartdalhjelmelandholeckobierzyceholidayhomeipartis-a-playerhomel" +
+ "inuxn--1lqs71dhomessinashikitanakagusukumodernhomeunixn--1qqw23a" +
+ "hondahonefosshinyoshitomiokanmakiwakunigamihamadahongorgehonjyoi" +
+ "chiropractichitachinakagawatchesasayamahornindalhorsembokukitash" +
+ "iobarahortendofinternetrentino-aadigehoteleshiojirishirifujiedah" +
+ "otmailhoyangerhoylandetroitskolobrzegyptianquanconagawakeisenbah" +
+ "nhurdalhurumajis-a-republicancerresearchaeologicaliforniahyogori" +
+ "s-a-rockstarachowicehyugawarajfkomakiyosemitejgorajlchloejlljmpa" +
+ "rtnershiraokanonjis-a-studentaljnjeonnamerikawauejoyoitakasakitc" +
+ "henjpmorganichocolatelekommunikationishiwakis-a-conservativegars" +
+ "heis-a-cpadoval-daostavalleyjpnchofunatorientexpressexyzgradyndn" +
+ "s-workshoppdalukowhalingroks-thisayamanashichinohedmarketsasebok" +
+ "nowsitallyngenissandvikcoromantovalle-aostatoiluroyjprshiratakah" +
+ "agis-a-teacherkassymantechnologyjurkredstonekristiansandefjordkr" +
+ "istiansundkrodsheradkrokstadelvaldaostathellewismillerkryminamio" +
+ "guniversitykumatorinokumejimateramoldelmenhorstalbanshisuifuette" +
+ "rtdasnetzwindmillkumenanyokaichibahccavuotnagareyamaintenancekun" +
+ "isakis-an-actresshirahamatonbetsurgeonshalloffamelhuslivinghisto" +
+ "rykunitachiaraisaijoshkar-olayangroupartykunitomigusukumamotoyam" +
+ "asudakunneppupasadenaklodzkodairakunstsammlungkunstunddesignkuok" +
+ "groupassagenshitaramakureitrentino-sudtirolkurgankurobellevuelos" +
+ "angelesjaguarchitecturebungoonomichinomiyakembuchikujobservercel" +
+ "liernemurorangemologicallfinanzkurogimilitarykuroisoftwaremarker" +
+ "ryhotelshizukuishimofusaikis-an-anarchistoricalsocietyumenkuroma" +
+ "tsunais-an-artistjohnkurotakikawasakis-an-engineeringerikekursko" +
+ "mmunalforbundkushirogawakustanais-an-entertainerkusunndalkutchan" +
+ "elkutnokuzbassnillfjordkuzumakis-bykvafjordkvalsundkvamlidlugole" +
+ "kagaminord-aurdalvdalipayufuchukotkafjordkvanangenkvinesdalkvinn" +
+ "heradkviteseidskogkvitsoykwpspjelkavikommunekyotobetsupersportre" +
+ "ntino-sued-tirolkyowariasahikawamissilexusgardenmisugitokigawami" +
+ "takeharamitourismolenskomorotsukamisunagawamitoyoakemiuramiyazur" +
+ "ewebsiteshikagamiishikarikaturindalmiyotamanomjondalenmlbarclays" +
+ "3-us-west-2monmouthaebaruminamiminowamonticellombardiamondshouji" +
+ "s-into-animegurovigorlicemontrealestatebankomvuxn--2m4a15emonza-" +
+ "brianzaporizhzhekinannestadmonza-e-della-brianzaporizhzhiamonzab" +
+ "rianzapposts-and-telecommunicationshowamonzaebrianzaramonzaedell" +
+ "abrianzamordoviajessheiminamisanrikubetsupplieshizuokanoyakagemo" +
+ "riyamatsusakahogithubusercontentrentino-suedtirolmoriyoshiokamit" +
+ "suemormoneymoroyamatsushigemortgagemoscowindowshriramsterdamberk" +
+ "eleymoseushistorymosjoenmoskenesienaplesigdalmossimbirskongsberg" +
+ "mosvikongsvingermoviemovistargardmtpccwioslombardyndns-at-homede" +
+ "potaruis-into-carshirakoenigmtrainingmuenstermugivestbytomakomai" +
+ "baramuikamogawamukochikushinonsenergymulhouseoullensvangmulticho" +
+ "icemunakatanemuncieszynmuosattemupassenger-associationmurmanskon" +
+ "injamisonymurotorcraftrentinoa-adigemusashimurayamatsuuramusashi" +
+ "noharamuseetrentinoaadigemuseumverenigingmutsuzawamutuellezajsko" +
+ "nskowolapyatigorskomatsushimashikokuchuomyphotoshibaikaliszczytn" +
+ "ordkappgmytis-a-bookkeeperminamitanephilatelyphilipsyphoenixn--3" +
+ "0rr7yphotographysiopiagetmyipaviancapitalpictetrentinoaltoadigep" +
+ "icturesirdalpiemontepilotslupskonsulatrobelgorodeopinkonyvelolku" +
+ "szpartshishikuis-a-techietis-a-socialistmeincheonpippupiszpittsb" +
+ "urghofauskedsmokorsetagayasells-for-lesschulepiwatepizzapkooris-" +
+ "a-therapistoiaplanetariuminamiuonumatsumotofukeplantationplantsn" +
+ "oasaintlouis-a-bruinsfansnzplatforminamiyamashirokawanabellunord" +
+ "re-landplaystationplazaplchonanbuildingrondarplomzansimagichosei" +
+ "kakegawawhoswhokksundyroyrvikingrongaplumbingoplusterpmnpodzonep" +
+ "ohlpokerpokrovskopervikomforbambleborkarpaczeladz-2polkowicepolt" +
+ "avalle-d-aostavangerpomorzeszowitdkoryolasitepordenonepornporsan" +
+ "gerporsangugeporsgrunnanpoznanprdpreservationpresidioprimeiwamat" +
+ "sumaebashikshacknetrentinos-tirolprincipeprivneprochowiceproduct" +
+ "ionsokanraprofermobilyprojectrentinostirolpromombetsupplypropert" +
+ "yprotectionpruszkowithgoogleapisa-hockeynutrentinosud-tirolprzew" +
+ "orskogptzpvtrentinosudtirolpzqldqponqslgbtrentinosued-tirolsrlsr" +
+ "varoystoragestordalstorenburgstorfjordstpetersburgstudiostudyndn" +
+ "s-blogdnsolarssonstuff-4-salestuttgartrentoshimasurgutsiracusait" +
+ "amatsukuris-into-cartoonshiranukannamiharusurnadalsurreysusakis-" +
+ "into-gameshiraois-a-soxfansusonosuzakanumazurysuzukanzakiwiensuz" +
+ "ukis-leetrentino-alto-adigesvalbardudinkamakurazakinkobayashikao" +
+ "irmincommbankomonosveiosvelvikosaigawasvizzeraswedenswidnicarbon" +
+ "ia-iglesias-carboniaiglesiascarboniaswiebodzindianapolis-a-blogg" +
+ "erswinoujscienceandhistoryswisshikis-lostfoldsxn--32vp30hagakhan" +
+ "amigawatuis-not-certifiedunetflixiltulaquilarvikoseis-an-account" +
+ "antshioyameldaltunesologneturystykarasjoksnesolundtuscanytushuma" +
+ "nitiesolutionsokndaltuvalle-daostavernversicherungvestfoldvestne" +
+ "somnarashinovestre-slidreamhostersoovestre-totennishiawakuravest" +
+ "vagoyvevelstadvibo-valentiavibovalentiavideovillaskvolloabathsbc" +
+ "ngvinnicargojomediavinnytsiavipsinaappharmacymruovatrentinoalto-" +
+ "adigevirginiavirtualvirtuelvistaprinternationalfirearmsopotrenti" +
+ "nosuedtirolviterboltrevisohuissier-justicevladikavkazanvladimirv" +
+ "ladivostokaizukaratevlogvoldavolgogradvolkenkunderseaportroandin" +
+ "osaurepbodyndns-at-workinggroupharmaciensimple-urlvolkswagentsor" +
+ "-odalvologdanskoshunantokamachippubetsubetsugaruvolyngdalvoronez" +
+ "hytomyrvossevangenvotevotingvotottoris-savedvrnwroclawloclawekos" +
+ "tromahabororoskolelwtchoyodobashibuyachiyodawtferrarawuozuwwwrit" +
+ "esthisblogspotrogstadwzmiuwajimaxn--3pxu8kosugexn--42c2d9axn--45" +
+ "brj9christmasakimobetsuwanouchikuseihichisobetsuitaitogakushimot" +
+ "oganewhampshirecreationissedalutskaufenisshinguernseyxn--45q11ch" +
+ "romedicaltanissettaiwanairforceoxn--4gbriminingxn--4it168dxn--4i" +
+ "t797kotohiradomainsureisenxn--4pvxsor-varangerxn--54b7fta0cchtra" +
+ "eumtgeradeatnurembergrossetouchijiwadell-ogliastrakhanawaxn--55q" +
+ "w42gxn--55qx5dxn--5js045dxn--5rtp49chungbukautokeinoxn--5rtq34ko" +
+ "touraxn--5tzm5gxn--6btw5axn--6frz82gxn--6orx2rxn--6qq986b3xlxn--" +
+ "7t0a264chungnamdalseidfjordxn--80adxhksorfoldxn--80ao21axn--80as" +
+ "ehdbargainstituteledatabaseballooningjerdrumemorialimitedownload" +
+ "rangedalimoliserniaustraliaisondre-landebudejjuedischesapeakebay" +
+ "erndigitalillehammerfest-mon-blogueurovisionasushiobaragrinetban" +
+ "kz-1kappleangaviikadenaamesjevuemielnoboribetsucks3-ap-northeast" +
+ "-1xn--80aswgxn--80audnedalnxn--8ltr62kouhokutamakizunokunimilano" +
+ "xn--8pvr4uxn--8y0a063axn--90a3academykolaivanovosibirskiervaapst" +
+ "eiermarkhangelskinderoyerimo-i-ranadexchangeiseiroumuenchendofth" +
+ "einternetcimdbarreauctionaturbruksgymnaturhistorischesakuraibiga" +
+ "waustrheimatunduhrennesoyokoze164xn--90aishobaraxn--90azhagebost" +
+ "adxn--9dbhblg6diethnologyxn--9dbq2axn--9et52uxn--9krt00axn--andy" +
+ "-iraxn--aroport-byanagawaxn--asky-iraxn--aurskog-hland-jnbarrel-" +
+ "of-knowledgeorgiauthordalandroidiscountysvardonnaharimamurogawag" +
+ "roks-theaternidds3-ap-southeast-1xn--avery-yuasakatakazakis-slic" +
+ "komaganexn--b-5gaxn--b4w605ferdxn--bck1b9a5dre4churchaseljejuego" +
+ "shikiminokamoenaircraftmpamperedchefarmsteadxn--bdddj-mrabdxn--b" +
+ "earalvhki-y4axn--berlevg-jxaxn--bhcavuotna-s4axn--bhccavuotna-k7" +
+ "axn--bidr-5nachikatsuuraxn--bievt-0qaxn--bjarky-fyanaizuxn--bjdd" +
+ "ar-ptambovdonskoshimizumakiyosumitakaginozawaonsenxn--blt-elabor" +
+ "xn--bmlo-graingerxn--bod-2naroyxn--brnny-wuaccident-investigatio" +
+ "njukudoyamaceratabuseat-band-campaniamallamadridvagsoygardenebak" +
+ "keshibechambagricaaarborteaches-yogasawaracingxn--brnnysund-m8ac" +
+ "cident-preventionlineat-urlxn--brum-voagatromsaitokorozawaxn--bt" +
+ "sfjord-9zaxn--c1avgxn--c2br7gxn--c3s14misakis-foundationxn--cck2" +
+ "b3barrell-of-knowledgets-itarumizusawautomotivelandiscoveryokami" +
+ "kawanehonbetsuruokaluganskarmoyomitanobihirosakikamijimakunecn-n" +
+ "orth-1xn--cg4bkis-uberleetrentino-altoadigexn--ciqpnxn--clchc0ea" +
+ "0b2g2a9gcdn77-securecipesaro-urbino-pesarourbinopesaromalvikouno" +
+ "sumypetshisognexn--comunicaes-v6a2oxn--correios-e-telecomunicaes" +
+ "-ghc29axn--czr694bashkiriautoscanadagestangeologyonabarudmurtiam" +
+ "usementargibestadevenes3-ap-southeast-2xn--czrs0tromsojavald-aos" +
+ "tarostwodzislawiwatsukiyonowtvbarefootballangenoamishirasatobish" +
+ "imaizurubtsovskjervoyageometre-experts-comptablesakuragawaustinn" +
+ "aspers3-external-1xn--czru2dxn--czrw28basilicataniaveroykenviron" +
+ "mentalconservationaturalhistorymuseumcentereportarnobrzegjemnes3" +
+ "-external-2xn--d1acj3batochigiftsakyotanabeneventochiokinoshimal" +
+ "opolskanlandrivefsncfagebizenakaniikawatanaguravocatanzarowebhop" +
+ "agespeedmobilizerobirasnesoddenmarketplace-burggfamilyokosukariy" +
+ "akumodumemerck-uralsk12xn--d1alferreroticanonoichikawamisatodayx" +
+ "n--d1atrusteexn--d5qv7z876chuvashiaxn--davvenjrga-y4axn--djrs72d" +
+ "6uyxn--djty4kouyamasoyxn--dnna-grajeworldxn--drbak-wuaxn--dyry-i" +
+ "raxn--eckvdtc9dxn--efvn9sorreisahayakawakamiichikaiseiyokoshibah" +
+ "ikariwanumataketomisatokyotangoxn--efvy88haibarakitahatakanezawa" +
+ "xn--ehqz56nxn--elqq16hakatanotogawaxn--estv75gxn--eveni-0qa01gax" +
+ "n--f6qx53axn--fct429kouzushimassa-carrara-massacarraramassabuske" +
+ "rudineustarhubarclaycards3-us-west-1xn--fhbeiarnxn--finny-yuaxn-" +
+ "-fiq228c5hsortlandxn--fiq64batsfjordrobaknoluoktainaibetsubameri" +
+ "canartanddesignieznogatagajobojibmdunloppacificarriereviewskrako" +
+ "weddingjerstadotsurugimetlifeinsurancehimejiinetatamotorsalangen" +
+ "atuurwetenschappenaumburgjesdalindaskoyabenorddalindesnesalatata" +
+ "rstanaustdalinkaruizawavoues3-fips-us-gov-west-1xn--fiqs8sorumin" +
+ "anoxn--fiqz9southcarolinazawaxn--fjord-lraxn--fjq720axn--fl-ziax" +
+ "n--flor-jraxn--flw351exn--fpcrj9c3dxn--frde-grandrapidsouthwestf" +
+ "alenxn--frna-woarais-very-badaddjamalborkdalxn--frya-hraxn--fzc2" +
+ "c9e2circlegnicahcesuolocalhistoryazannefrankfurtoyokawaxn--fzys8" +
+ "d69uvgmailxn--g2xx48circusculturedumbrellajollanbibaidarq-axn--g" +
+ "ckr3f0fetsundxn--gecrj9citicateringebuildersvpalmspringsakerxn--" +
+ "ggaviika-8ya47hakodatemasekmshimosuwalkis-a-linux-useranishiarit" +
+ "abashijonawatextilevangerxn--gildeskl-g0axn--givuotna-8yandexhib" +
+ "itionxn--gjvik-wuaxn--gls-elacaixaxn--gmq050is-very-evillagexn--" +
+ "gmqw5axn--h-2failxn--h1aeghakonexn--h2brj9civilaviationiyodogawa" +
+ "xn--hbmer-xqaxn--hcesuolo-7ya35bauhaustevollinzaiitatebayashiiba" +
+ "hcavuotnagarakkestadupontariobninskarumaifareastcoastaldefencemb" +
+ "roideryonagoyaxastronomyokohamamatsudaegubs3-eu-central-1xn--her" +
+ "y-iraxn--hgebostad-g3axn--hmmrfeasta-s4acoachampionshiphopenair-" +
+ "surveillancebetsukubabia-goracleaningatlantachikawakayamagadance" +
+ "chirealtorlandxn--hnefoss-q1axn--hobl-iraxn--holtlen-hxaxn--hpmi" +
+ "r-xqaxn--hxt814exn--hyanger-q1axn--hylandet-54axn--i1b6b1a6a2exn" +
+ "--imr513nxn--indery-fyaotsurgeryxn--io0a7is-very-goodyearthadsel" +
+ "fipirangaxn--j1aefgulenxn--j1amhakubanxn--j6w193gxn--jlq61u9w7bb" +
+ "cartierhcloudcontrolappalanakhodkanagawaxn--jlster-byaroslavlaan" +
+ "derenxn--jrpeland-54axn--jvr189misasaguris-gonexn--k7yn95exn--ka" +
+ "rmy-yuaxn--kbrq7oxn--kcrx77d1x4axn--kfjord-iuaxn--klbu-woaxn--kl" +
+ "t787dxn--kltp7dxn--kltx9axn--klty5xn--3bst00minnesotaketakatoris" +
+ "-certifieducatorahimeshimageandsoundandvisionxn--koluokta-7ya57h" +
+ "akuis-a-llamarylhursteinkjerusalembetsukuis-a-musicianxn--kprw13" +
+ "dxn--kpry57dxn--kpu716figuerestaurantoyotaris-a-doctorayxn--kput" +
+ "3is-very-nicexn--krager-gyasakaiminatoursowaxn--kranghke-b0axn--" +
+ "krdsherad-m8axn--krehamn-dxaxn--krjohka-hwab49jetztrentino-stiro" +
+ "lxn--ksnes-uuaxn--kvfjord-nxaxn--kvitsy-fyasugis-very-sweetrenti" +
+ "no-s-tirollagriculturennebudapest-a-la-maisondriodejaneirocheste" +
+ "rxn--kvnangen-k0axn--l-1fairwindspreadbettingxn--l1accentureklam" +
+ "borghinikkoebenhavnxn--laheadju-7yasuokaratsuginamikatagamihobby" +
+ "-sitevaksdalxn--langevg-jxaxn--lcvr32dxn--ldingen-q1axn--leagavi" +
+ "ika-52bbvacationsnasabaerobaticketsalondonetskasaokamisatohnosho" +
+ "oceanographicsaltdalipetskashibatakashimasfjordenaval-d-aosta-va" +
+ "lleyonagunikolaeventsalvadordalibabajddarchaeologyeongnamegawakk" +
+ "anaikawababydgoszczecinemailivornoceanographiquemergencyberlevag" +
+ "angaviikarugaulardalorenskogjovikashiharaxn--lesund-huaxn--lgbba" +
+ "t1ad8jevnakerxn--lgrd-poacivilisationrwifarsundxn--lhppi-xqaxn--" +
+ "linds-pratoyakokamishihoronobeokaminoyamatsuris-with-thebandoomd" +
+ "nsaliascolipicenord-odalxn--lns-qlavagiskexn--loabt-0qaxn--lrdal" +
+ "-sraxn--lrenskog-54axn--lt-liacivilizationxn--lten-granexn--lury" +
+ "-iraxn--mely-iraxn--merker-kuaxn--mgb2ddespydebergxn--mgb9awbfil" +
+ "ateliaxn--mgba3a3ejtrvchoshibukawaxn--mgba3a4f16axn--mgba3a4fran" +
+ "amizuholdingsmileksvikozagawaxn--mgba7c0bbn0axn--mgbaam7a8hakusa" +
+ "ndoyxn--mgbab2bdxn--mgbai9a5eva00beppubolognagasukesennumalselve" +
+ "ndrelloteneiiyamanobedzin-addrammenuernberglassassinationalherit" +
+ "agematsubarakawachinaganoharaogashimadachicagoboatsalzburgliwice" +
+ "mrxn--mgbai9azgqp6jewelryxn--mgbayh7gpaduaxn--mgbb9fbpobanazawax" +
+ "n--mgbbh1a71exn--mgbc0a9azcgxn--mgberp4a5d4a87gxn--mgberp4a5d4ar" +
+ "xn--mgbpl2fhvalerxn--mgbqly7c0a67fbcivilwarmanagementoyonakagyok" +
+ "utomobentleyxn--mgbqly7cvafranziskanerimarinextdirectoryxn--mgbt" +
+ "3dhdxn--mgbtf8flatangerxn--mgbtx2bernrtateshinanomachintaijindus" +
+ "triesteambulancepilepsykkylvenetogliattipschoenbrunnavigationavu" +
+ "otnakayamatsuzakinfinitiresamegawaxn--mgbx4cd0abogadocscbgxn--mi" +
+ "x082filminamifuranoxn--mix891finalxn--mjndalen-64axn--mk0axindia" +
+ "nmarketingxn--mk1bu44claimsaskatchewanggouvicenzaxn--mkru45isleo" +
+ "fmandalxn--mlatvuopmi-s4axn--mli-tlavangenxn--mlselv-iuaxn--more" +
+ "ke-juaxn--mori-qsakegawaxn--mosjen-eyatominamiawajikissmartertha" +
+ "nyoutubeeldengeluidxn--mot-tlazioxn--mre-og-romsdal-qqbeskidyn-o" +
+ "-saurlandesamnangerxn--msy-ula0haldenxn--mtta-vrjjat-k7aflakstad" +
+ "aokagakibichuoxn--muost-0qaxn--mxtq1misawaxn--ngbc5azdxn--ngbe9e" +
+ "0axn--nit225kozakis-an-actorxn--nmesjevuemie-tcbalatinabearalvah" +
+ "kikonaioirasebastopologyeonggiehtavuoatnagaivuotnagaokakyotambad" +
+ "ajozorahkkeravjudygarlandxn--nnx388axn--nodessakuhokkaidontexist" +
+ "eingeekpnxn--nqv7fs00emaxn--nry-yla5gxn--ntso0iqx3axn--ntsq17gxn" +
+ "--nttery-byaeserveftphiladelphiaareadmyblogsitexn--nvuotna-hwaxn" +
+ "--nyqy26axn--o1achattanooganorfolkebiblegallocuscountryestateofd" +
+ "elawarezzoologyxn--o3cw4halsagamiharaxn--od0algxn--od0aq3betaina" +
+ "boxfordealerdalottepsongdalenviknakanojohanamakinoharaxn--ogbpf8" +
+ "flekkefjordxn--oppegrd-ixaxn--ostery-fyatsukareliancexn--osyro-w" +
+ "uaxn--p1acfdxn--p1aiwchitosetoeiheijis-a-chefarmerseinewspaperxn" +
+ "--pbt977clickazimierz-dolnyxn--pgbs0dhammarfeastafricamagicherno" +
+ "vtsydneyxn--porsgu-sta26financexn--pssu33lxn--pssy2uxn--q9jyb4cl" +
+ "inicoffeedbackazoxn--qcka1pmclintonoshoesassaris-a-cubicle-slave" +
+ "llinowruzhgorodoyxn--qqqt11misconfusedxn--qxamurskjakdnepropetro" +
+ "vskiptveterinairealtychyllestadultrysilkosakaerodromegallupinbar" +
+ "celonagasakikuchikumagayagawalbrzycharternopilawalesundnpalacebi" +
+ "northwesternmutualimanowarudaurskog-holandroverhalla-speziaetnag" +
+ "ahamaroyekaterinburgdyniaeroportalaheadjudaicabbottarantomaritim" +
+ "ekeeping12000xn--rady-iraxn--rdal-poaxn--rde-ulaxn--rdy-0nabarix" +
+ "n--rennesy-v1axn--rhkkervju-01afineartschwarzgwangjuifminamiisel" +
+ "ectoyotomiyazakis-a-financialadvisor-aurdalxn--rholt-mragoworse-" +
+ "thandsoniizaxn--rhqv96gxn--rht27zxn--rht3dxn--rht61exn--risa-5na" +
+ "rusawaxn--risr-iraxn--rland-uuaxn--rlingen-mxaxn--rmskog-byatsus" +
+ "hiroxn--rny31hamurakamigoriginankokubunjis-a-nascarfanxn--rovu88" +
+ "bhartiffanycartoonarteducationalchikugokasejnyoriikashiwaraxn--r" +
+ "ros-granvindafjordxn--rskog-uuaxn--rst-0narutokonamegatakatsukix" +
+ "n--rsta-francaiseharaxn--ryken-vuaxn--ryrvik-byawaraxn--s-1faith" +
+ "eguardianxn--s9brj9clothingroundhandlingroznyxn--sandnessjen-ogb" +
+ "izhevskppspiegelxn--sandy-yuaxn--seral-lraxn--ses554gxn--sgne-gr" +
+ "atangenxn--skierv-utazasmatartcenterprisesakievenassisibenikihok" +
+ "umakogenglandxn--skjervy-v1axn--skjk-soaxn--sknit-yqaxn--sknland" +
+ "-fxaxn--slat-5narviikananporoxn--slt-elabourxn--smla-hraxn--smna" +
+ "-gratis-a-bulls-fanxn--snase-nraxn--sndre-land-0cbremangerxn--sn" +
+ "es-poaxn--snsa-roaxn--sr-aurdal-l8axn--sr-fron-q1axn--sr-odal-q1" +
+ "axn--sr-varanger-ggbielawallonieruchomoscienceandindustrynavyatk" +
+ "anazawaxn--srfold-byawatahamaxn--srreisa-q1axn--srum-grazxn--stf" +
+ "old-9xaxn--stjrdal-s1axn--stjrdalshalsen-sqbiellaakesvuemielecce" +
+ "verbankashiwazakiyokawaraxn--stre-toten-zcbieszczadygeyachimatai" +
+ "peigersundurbanpachigasakicks-assedicasadelamonedaxn--t60b56axn-" +
+ "-tckweatherchannelxn--tjme-hraxn--tn0agrigentomologyeongbukrasno" +
+ "darxn--tnsberg-q1axn--tor131oxn--trany-yuaxn--trgstad-r1axn--trn" +
+ "a-woaxn--troms-zuaxn--tysvr-vraxn--uc0atverranzanxn--uc0ay4axn--" +
+ "uist22hangglidingxn--uisz3gxn--unjrga-rtaobaomoriguchiharamcoalx" +
+ "n--unup4yxn--uuwu58axn--vads-jraxn--vard-jraxn--vegrshei-c0axn--" +
+ "vermgensberater-ctbievattorneyagawakuyabukijoburglobalashovhachi" +
+ "jorpelandurhamburglobodoes-itateyamaxn--vermgensberatung-pwbifuk" +
+ "agawalterxn--vestvgy-ixa6oxn--vg-yiabruzzoologicalabamagasakishi" +
+ "mabaragusartsaritsynxn--vgan-qoaxn--vgsy-qoa0jewishartrentino-su" +
+ "d-tirolxn--vgu402cloudcontrolledekakudamatsuenoharaxn--vhquversa" +
+ "illesomaxn--vler-qoaxn--vre-eiker-k8axn--vrggt-xqadxn--vry-yla5g" +
+ "xn--vuq861bihorologyukuhashimoichinosekigaharaxn--w4r85el8fhu5dn" +
+ "raxn--wcvs22dxn--wgbh1cloudfrontdoorxn--wgbl6axn--xhq521bikedati" +
+ "nglogowegroweibolzanordreisa-geekasukabeerxn--xkc2al3hye2axn--xk" +
+ "c2dl3a5ee0hangoutsystemscloudapparmaxn--y9a3aquariumishimatsunox" +
+ "n--yer-znarvikrasnoyarskomitamamuraxn--yfro4i67oxn--ygarden-p1ax" +
+ "n--ygbi2ammxn--3ds443gxn--ystre-slidre-ujbilbaogakidstvedestrand" +
+ "vrdnsamsungloppenzaokinawashirosatobamagazinedre-eikerxn--zbx025" +
+ "dxn--zf0ao64axn--zf0avxn--3e0b707exn--zfr164billustrationayorodd" +
+ "axperiaxxxn--3oq18vl8pn36axz"
+
+// nodes is the list of nodes. Each node is represented as a uint32, which
+// encodes the node's children, wildcard bit and node type (as an index into
+// the children array), ICANN bit and text.
+//
+// In the //-comment after each node's data, the nodes indexes of the children
+// are formatted as (n0x1234-n0x1256), with * denoting the wildcard bit. The
+// nodeType is printed as + for normal, ! for exception, and o for parent-only
+// nodes that have children but don't match a domain label in their own right.
+// An I denotes an ICANN domain.
+//
+// The layout within the uint32, from MSB to LSB, is:
+// [ 1 bits] unused
+// [ 9 bits] children index
+// [ 1 bits] ICANN bit
+// [15 bits] text index
+// [ 6 bits] text length
+var nodes = [...]uint32{
+ 0x00301443, // n0x0000 c0x0000 (---------------) + I aaa
+ 0x002293c4, // n0x0001 c0x0000 (---------------) + I aarp
+ 0x0036eb43, // n0x0002 c0x0000 (---------------) + I abb
+ 0x0036eb46, // n0x0003 c0x0000 (---------------) + I abbott
+ 0x0030cb04, // n0x0004 c0x0000 (---------------) + I able
+ 0x00355b87, // n0x0005 c0x0000 (---------------) + I abogado
+ 0x01a01e82, // n0x0006 c0x0006 (n0x0539-n0x053f) + I ac
+ 0x002f2787, // n0x0007 c0x0000 (---------------) + I academy
+ 0x0033dcc9, // n0x0008 c0x0000 (---------------) + I accenture
+ 0x002d7e4a, // n0x0009 c0x0000 (---------------) + I accountant
+ 0x002d7e4b, // n0x000a c0x0000 (---------------) + I accountants
+ 0x00221483, // n0x000b c0x0000 (---------------) + I aco
+ 0x0027d3c6, // n0x000c c0x0000 (---------------) + I active
+ 0x00226f45, // n0x000d c0x0000 (---------------) + I actor
+ 0x01e10a02, // n0x000e c0x0007 (n0x053f-n0x0540) + I ad
+ 0x00262ac3, // n0x000f c0x0000 (---------------) + I ads
+ 0x0036b745, // n0x0010 c0x0000 (---------------) + I adult
+ 0x02204342, // n0x0011 c0x0008 (n0x0540-n0x0548) + I ae
+ 0x003285c3, // n0x0012 c0x0000 (---------------) + I aeg
+ 0x026751c4, // n0x0013 c0x0009 (n0x0548-n0x05a1) + I aero
+ 0x0036de85, // n0x0014 c0x0000 (---------------) + I aetna
+ 0x02a0c702, // n0x0015 c0x000a (n0x05a1-n0x05a6) + I af
+ 0x0023a2c3, // n0x0016 c0x0000 (---------------) + I afl
+ 0x00367c06, // n0x0017 c0x0000 (---------------) + I africa
+ 0x00367c0b, // n0x0018 c0x0000 (---------------) + I africamagic
+ 0x02e015c2, // n0x0019 c0x000b (n0x05a6-n0x05ab) + I ag
+ 0x002d6e47, // n0x001a c0x0000 (---------------) + I agakhan
+ 0x00229d46, // n0x001b c0x0000 (---------------) + I agency
+ 0x032002c2, // n0x001c c0x000c (n0x05ab-n0x05af) + I ai
+ 0x0024ef43, // n0x001d c0x0000 (---------------) + I aig
+ 0x002e75c8, // n0x001e c0x0000 (---------------) + I airforce
+ 0x00273406, // n0x001f c0x0000 (---------------) + I airtel
+ 0x0036acc4, // n0x0020 c0x0000 (---------------) + I akdn
+ 0x03600882, // n0x0021 c0x000d (n0x05af-n0x05b6) + I al
+ 0x00342747, // n0x0022 c0x0000 (---------------) + I alibaba
+ 0x002ab4c6, // n0x0023 c0x0000 (---------------) + I alipay
+ 0x002a6589, // n0x0024 c0x0000 (---------------) + I allfinanz
+ 0x0029c184, // n0x0025 c0x0000 (---------------) + I ally
+ 0x00215bc6, // n0x0026 c0x0000 (---------------) + I alsace
+ 0x03a04942, // n0x0027 c0x000e (n0x05b6-n0x05b7) + I am
+ 0x0027d885, // n0x0028 c0x0000 (---------------) + I amica
+ 0x002b6cc9, // n0x0029 c0x0000 (---------------) + I amsterdam
+ 0x03e01242, // n0x002a c0x000f (n0x05b7-n0x05bb) + I an
+ 0x0022f449, // n0x002b c0x0000 (---------------) + I analytics
+ 0x002f8647, // n0x002c c0x0000 (---------------) + I android
+ 0x00295a46, // n0x002d c0x0000 (---------------) + I anquan
+ 0x0420fd82, // n0x002e c0x0010 (n0x05bb-n0x05c1) + I ao
+ 0x0028df8a, // n0x002f c0x0000 (---------------) + I apartments
+ 0x00212a03, // n0x0030 c0x0000 (---------------) + I app
+ 0x002f0145, // n0x0031 c0x0000 (---------------) + I apple
+ 0x00273fc2, // n0x0032 c0x0000 (---------------) + I aq
+ 0x00273fc9, // n0x0033 c0x0000 (---------------) + I aquarelle
+ 0x04600602, // n0x0034 c0x0011 (n0x05c1-n0x05ca) + I ar
+ 0x00387dc6, // n0x0035 c0x0000 (---------------) + I aramco
+ 0x0025dd45, // n0x0036 c0x0000 (---------------) + I archi
+ 0x00333fc4, // n0x0037 c0x0000 (---------------) + I army
+ 0x04e29404, // n0x0038 c0x0013 (n0x05cb-n0x05d1) + I arpa
+ 0x00359e04, // n0x0039 c0x0000 (---------------) + I arte
+ 0x05200182, // n0x003a c0x0014 (n0x05d1-n0x05d2) + I as
+ 0x00209144, // n0x003b c0x0000 (---------------) + I asia
+ 0x002729ca, // n0x003c c0x0000 (---------------) + I associates
+ 0x05601642, // n0x003d c0x0015 (n0x05d2-n0x05d9) + I at
+ 0x00389588, // n0x003e c0x0000 (---------------) + I attorney
+ 0x05e05ac2, // n0x003f c0x0017 (n0x05da-n0x05ec) + I au
+ 0x002f4487, // n0x0040 c0x0000 (---------------) + I auction
+ 0x00222244, // n0x0041 c0x0000 (---------------) + I audi
+ 0x00251707, // n0x0042 c0x0000 (---------------) + I audible
+ 0x00222245, // n0x0043 c0x0000 (---------------) + I audio
+ 0x002f8406, // n0x0044 c0x0000 (---------------) + I author
+ 0x002eaac4, // n0x0045 c0x0000 (---------------) + I auto
+ 0x00309645, // n0x0046 c0x0000 (---------------) + I autos
+ 0x002c0747, // n0x0047 c0x0000 (---------------) + I avianca
+ 0x06e02502, // n0x0048 c0x001b (n0x05fa-n0x05fb) + I aw
+ 0x00272583, // n0x0049 c0x0000 (---------------) + I aws
+ 0x00215282, // n0x004a c0x0000 (---------------) + I ax
+ 0x0032b343, // n0x004b c0x0000 (---------------) + I axa
+ 0x07208cc2, // n0x004c c0x001c (n0x05fb-n0x0607) + I az
+ 0x0026c905, // n0x004d c0x0000 (---------------) + I azure
+ 0x07605a82, // n0x004e c0x001d (n0x0607-n0x0612) + I ba
+ 0x00343204, // n0x004f c0x0000 (---------------) + I baby
+ 0x0026a085, // n0x0050 c0x0000 (---------------) + I baidu
+ 0x00255704, // n0x0051 c0x0000 (---------------) + I band
+ 0x00235dc4, // n0x0052 c0x0000 (---------------) + I bank
+ 0x0020c103, // n0x0053 c0x0000 (---------------) + I bar
+ 0x0036bf49, // n0x0054 c0x0000 (---------------) + I barcelona
+ 0x0031934b, // n0x0055 c0x0000 (---------------) + I barclaycard
+ 0x002afe08, // n0x0056 c0x0000 (---------------) + I barclays
+ 0x0030b788, // n0x0057 c0x0000 (---------------) + I barefoot
+ 0x002ed0c8, // n0x0058 c0x0000 (---------------) + I bargains
+ 0x003297c7, // n0x0059 c0x0000 (---------------) + I bauhaus
+ 0x002eef46, // n0x005a c0x0000 (---------------) + I bayern
+ 0x07a791c2, // n0x005b c0x001e (n0x0612-n0x061c) + I bb
+ 0x00331f83, // n0x005c c0x0000 (---------------) + I bbc
+ 0x00340184, // n0x005d c0x0000 (---------------) + I bbva
+ 0x0028bfc3, // n0x005e c0x0000 (---------------) + I bcg
+ 0x002dbf83, // n0x005f c0x0000 (---------------) + I bcn
+ 0x016fbc02, // n0x0060 c0x0005 (---------------)* o I bd
+ 0x07e04702, // n0x0061 c0x001f (n0x061c-n0x061e) + I be
+ 0x00243505, // n0x0062 c0x0000 (---------------) + I beats
+ 0x00391984, // n0x0063 c0x0000 (---------------) + I beer
+ 0x00352147, // n0x0064 c0x0000 (---------------) + I bentley
+ 0x00251d46, // n0x0065 c0x0000 (---------------) + I berlin
+ 0x0030a2c4, // n0x0066 c0x0000 (---------------) + I best
+ 0x00227703, // n0x0067 c0x0000 (---------------) + I bet
+ 0x08349f02, // n0x0068 c0x0020 (n0x061e-n0x061f) + I bf
+ 0x08755e02, // n0x0069 c0x0021 (n0x061f-n0x0644) + I bg
+ 0x08af6202, // n0x006a c0x0022 (n0x0644-n0x0649) + I bh
+ 0x00375006, // n0x006b c0x0000 (---------------) + I bharti
+ 0x08e00002, // n0x006c c0x0023 (n0x0649-n0x064e) + I bi
+ 0x003628c5, // n0x006d c0x0000 (---------------) + I bible
+ 0x002fd143, // n0x006e c0x0000 (---------------) + I bid
+ 0x00390e04, // n0x006f c0x0000 (---------------) + I bike
+ 0x002c7a44, // n0x0070 c0x0000 (---------------) + I bing
+ 0x002c7a45, // n0x0071 c0x0000 (---------------) + I bingo
+ 0x00200003, // n0x0072 c0x0000 (---------------) + I bio
+ 0x09310603, // n0x0073 c0x0024 (n0x064e-n0x0655) + I biz
+ 0x09602642, // n0x0074 c0x0025 (n0x0655-n0x0659) + I bj
+ 0x00277b85, // n0x0075 c0x0000 (---------------) + I black
+ 0x00277b8b, // n0x0076 c0x0000 (---------------) + I blackfriday
+ 0x002d0084, // n0x0077 c0x0000 (---------------) + I blog
+ 0x00205849, // n0x0078 c0x0000 (---------------) + I bloomberg
+ 0x00207184, // n0x0079 c0x0000 (---------------) + I blue
+ 0x09a09242, // n0x007a c0x0026 (n0x0659-n0x065e) + I bm
+ 0x00209243, // n0x007b c0x0000 (---------------) + I bms
+ 0x0020a103, // n0x007c c0x0000 (---------------) + I bmw
+ 0x0160a282, // n0x007d c0x0005 (---------------)* o I bn
+ 0x00243903, // n0x007e c0x0000 (---------------) + I bnl
+ 0x0020a28a, // n0x007f c0x0000 (---------------) + I bnpparibas
+ 0x09e0a702, // n0x0080 c0x0027 (n0x065e-n0x0667) + I bo
+ 0x0034eb85, // n0x0081 c0x0000 (---------------) + I boats
+ 0x0020aac3, // n0x0082 c0x0000 (---------------) + I bom
+ 0x0020b104, // n0x0083 c0x0000 (---------------) + I bond
+ 0x0020c2c3, // n0x0084 c0x0000 (---------------) + I boo
+ 0x0020c2c5, // n0x0085 c0x0000 (---------------) + I boots
+ 0x0020cb05, // n0x0086 c0x0000 (---------------) + I bosch
+ 0x0020d286, // n0x0087 c0x0000 (---------------) + I bostik
+ 0x0020e2c3, // n0x0088 c0x0000 (---------------) + I bot
+ 0x00211048, // n0x0089 c0x0000 (---------------) + I boutique
+ 0x0a212e82, // n0x008a c0x0028 (n0x0667-n0x06ad) + I br
+ 0x00212e88, // n0x008b c0x0000 (---------------) + I bradesco
+ 0x0021a14b, // n0x008c c0x0000 (---------------) + I bridgestone
+ 0x002172c8, // n0x008d c0x0000 (---------------) + I broadway
+ 0x00218046, // n0x008e c0x0000 (---------------) + I broker
+ 0x00219347, // n0x008f c0x0000 (---------------) + I brother
+ 0x0021be88, // n0x0090 c0x0000 (---------------) + I brussels
+ 0x0aa35102, // n0x0091 c0x002a (n0x06ae-n0x06b3) + I bs
+ 0x0ae16fc2, // n0x0092 c0x002b (n0x06b3-n0x06b8) + I bt
+ 0x0033c648, // n0x0093 c0x0000 (---------------) + I budapest
+ 0x002c67c5, // n0x0094 c0x0000 (---------------) + I build
+ 0x00324908, // n0x0095 c0x0000 (---------------) + I builders
+ 0x0025f008, // n0x0096 c0x0000 (---------------) + I business
+ 0x0021d903, // n0x0097 c0x0000 (---------------) + I buy
+ 0x0021e484, // n0x0098 c0x0000 (---------------) + I buzz
+ 0x003401c2, // n0x0099 c0x0000 (---------------) + I bv
+ 0x0b21f782, // n0x009a c0x002c (n0x06b8-n0x06ba) + I bw
+ 0x0b616b42, // n0x009b c0x002d (n0x06ba-n0x06be) + I by
+ 0x0be203c2, // n0x009c c0x002f (n0x06bf-n0x06c5) + I bz
+ 0x002203c3, // n0x009d c0x0000 (---------------) + I bzh
+ 0x0c2055c2, // n0x009e c0x0030 (n0x06c5-n0x06d6) + I ca
+ 0x0036eb03, // n0x009f c0x0000 (---------------) + I cab
+ 0x0026ab84, // n0x00a0 c0x0000 (---------------) + I cafe
+ 0x0020e443, // n0x00a1 c0x0000 (---------------) + I cal
+ 0x002a6544, // n0x00a2 c0x0000 (---------------) + I call
+ 0x0022b406, // n0x00a3 c0x0000 (---------------) + I camera
+ 0x0022c604, // n0x00a4 c0x0000 (---------------) + I camp
+ 0x0029668e, // n0x00a5 c0x0000 (---------------) + I cancerresearch
+ 0x00312a05, // n0x00a6 c0x0000 (---------------) + I canon
+ 0x0021aac8, // n0x00a7 c0x0000 (---------------) + I capetown
+ 0x002c0887, // n0x00a8 c0x0000 (---------------) + I capital
+ 0x00205f83, // n0x00a9 c0x0000 (---------------) + I car
+ 0x002291c7, // n0x00aa c0x0000 (---------------) + I caravan
+ 0x00319505, // n0x00ab c0x0000 (---------------) + I cards
+ 0x00205f84, // n0x00ac c0x0000 (---------------) + I care
+ 0x00205f86, // n0x00ad c0x0000 (---------------) + I career
+ 0x00205f87, // n0x00ae c0x0000 (---------------) + I careers
+ 0x002b9384, // n0x00af c0x0000 (---------------) + I cars
+ 0x00332007, // n0x00b0 c0x0000 (---------------) + I cartier
+ 0x00383904, // n0x00b1 c0x0000 (---------------) + I casa
+ 0x00261b04, // n0x00b2 c0x0000 (---------------) + I cash
+ 0x002112c6, // n0x00b3 c0x0000 (---------------) + I casino
+ 0x0020d0c3, // n0x00b4 c0x0000 (---------------) + I cat
+ 0x003246c8, // n0x00b5 c0x0000 (---------------) + I catering
+ 0x00235d83, // n0x00b6 c0x0000 (---------------) + I cba
+ 0x002438c3, // n0x00b7 c0x0000 (---------------) + I cbn
+ 0x0037dc04, // n0x00b8 c0x0000 (---------------) + I cbre
+ 0x0c61aa82, // n0x00b9 c0x0031 (n0x06d6-n0x06da) + I cc
+ 0x0ca2a082, // n0x00ba c0x0032 (n0x06da-n0x06db) + I cd
+ 0x00215cc3, // n0x00bb c0x0000 (---------------) + I ceb
+ 0x00233a06, // n0x00bc c0x0000 (---------------) + I center
+ 0x002e7743, // n0x00bd c0x0000 (---------------) + I ceo
+ 0x0021d7c4, // n0x00be c0x0000 (---------------) + I cern
+ 0x0cf104c2, // n0x00bf c0x0033 (n0x06db-n0x06dc) + I cf
+ 0x003104c3, // n0x00c0 c0x0000 (---------------) + I cfa
+ 0x00366243, // n0x00c1 c0x0000 (---------------) + I cfd
+ 0x0020ea02, // n0x00c2 c0x0000 (---------------) + I cg
+ 0x0d204a02, // n0x00c3 c0x0034 (n0x06dc-n0x06dd) + I ch
+ 0x002a9f06, // n0x00c4 c0x0000 (---------------) + I chanel
+ 0x00227d87, // n0x00c5 c0x0000 (---------------) + I channel
+ 0x002facc5, // n0x00c6 c0x0000 (---------------) + I chase
+ 0x0023a704, // n0x00c7 c0x0000 (---------------) + I chat
+ 0x0028dec5, // n0x00c8 c0x0000 (---------------) + I cheap
+ 0x00353cc7, // n0x00c9 c0x0000 (---------------) + I chintai
+ 0x00297d05, // n0x00ca c0x0000 (---------------) + I chloe
+ 0x002e5109, // n0x00cb c0x0000 (---------------) + I christmas
+ 0x002e6f86, // n0x00cc c0x0000 (---------------) + I chrome
+ 0x002fabc6, // n0x00cd c0x0000 (---------------) + I church
+ 0x0d6155c2, // n0x00ce c0x0035 (n0x06dd-n0x06ec) + I ci
+ 0x0025f788, // n0x00cf c0x0000 (---------------) + I cipriani
+ 0x00322106, // n0x00d0 c0x0000 (---------------) + I circle
+ 0x00237905, // n0x00d1 c0x0000 (---------------) + I cisco
+ 0x003245c5, // n0x00d2 c0x0000 (---------------) + I citic
+ 0x002735c4, // n0x00d3 c0x0000 (---------------) + I city
+ 0x002735c8, // n0x00d4 c0x0000 (---------------) + I cityeats
+ 0x0da01782, // n0x00d5 c0x0036 (n0x06ec-n0x06ed)* o I ck
+ 0x0de0af42, // n0x00d6 c0x0037 (n0x06ed-n0x06f2) + I cl
+ 0x00357546, // n0x00d7 c0x0000 (---------------) + I claims
+ 0x0032d748, // n0x00d8 c0x0000 (---------------) + I cleaning
+ 0x00367205, // n0x00d9 c0x0000 (---------------) + I click
+ 0x00368f86, // n0x00da c0x0000 (---------------) + I clinic
+ 0x003784c8, // n0x00db c0x0000 (---------------) + I clothing
+ 0x00332205, // n0x00dc c0x0000 (---------------) + I cloud
+ 0x002752c4, // n0x00dd c0x0000 (---------------) + I club
+ 0x002752c7, // n0x00de c0x0000 (---------------) + I clubmed
+ 0x0e249082, // n0x00df c0x0038 (n0x06f2-n0x06f6) + I cm
+ 0x0e6211c2, // n0x00e0 c0x0039 (n0x06f6-n0x0723) + I cn
+ 0x0fe00742, // n0x00e1 c0x003f (n0x0728-n0x0735) + I co
+ 0x0032ca05, // n0x00e2 c0x0000 (---------------) + I coach
+ 0x0028ca05, // n0x00e3 c0x0000 (---------------) + I codes
+ 0x003690c6, // n0x00e4 c0x0000 (---------------) + I coffee
+ 0x0021e787, // n0x00e5 c0x0000 (---------------) + I college
+ 0x002214c7, // n0x00e6 c0x0000 (---------------) + I cologne
+ 0x10622ac3, // n0x00e7 c0x0041 (n0x0736-n0x07ff) + I com
+ 0x002d4148, // n0x00e8 c0x0000 (---------------) + I commbank
+ 0x00222ac9, // n0x00e9 c0x0000 (---------------) + I community
+ 0x002232c7, // n0x00ea c0x0000 (---------------) + I company
+ 0x002236c8, // n0x00eb c0x0000 (---------------) + I computer
+ 0x00223ec6, // n0x00ec c0x0000 (---------------) + I comsec
+ 0x00224306, // n0x00ed c0x0000 (---------------) + I condos
+ 0x00224c0c, // n0x00ee c0x0000 (---------------) + I construction
+ 0x00225a8a, // n0x00ef c0x0000 (---------------) + I consulting
+ 0x00225f47, // n0x00f0 c0x0000 (---------------) + I contact
+ 0x00226e0b, // n0x00f1 c0x0000 (---------------) + I contractors
+ 0x00227bc7, // n0x00f2 c0x0000 (---------------) + I cooking
+ 0x00227bce, // n0x00f3 c0x0000 (---------------) + I cookingchannel
+ 0x00228384, // n0x00f4 c0x0000 (---------------) + I cool
+ 0x00228d44, // n0x00f5 c0x0000 (---------------) + I coop
+ 0x0022b2c7, // n0x00f6 c0x0000 (---------------) + I corsica
+ 0x00362c07, // n0x00f7 c0x0000 (---------------) + I country
+ 0x0022d906, // n0x00f8 c0x0000 (---------------) + I coupon
+ 0x0022d907, // n0x00f9 c0x0000 (---------------) + I coupons
+ 0x0022dc87, // n0x00fa c0x0000 (---------------) + I courses
+ 0x11a0c502, // n0x00fb c0x0046 (n0x081d-n0x0824) + I cr
+ 0x00230646, // n0x00fc c0x0000 (---------------) + I credit
+ 0x0023064a, // n0x00fd c0x0000 (---------------) + I creditcard
+ 0x002308cb, // n0x00fe c0x0000 (---------------) + I creditunion
+ 0x002319c7, // n0x00ff c0x0000 (---------------) + I cricket
+ 0x00232885, // n0x0100 c0x0000 (---------------) + I crown
+ 0x002329c3, // n0x0101 c0x0000 (---------------) + I crs
+ 0x00233147, // n0x0102 c0x0000 (---------------) + I cruises
+ 0x00355d43, // n0x0103 c0x0000 (---------------) + I csc
+ 0x11e24002, // n0x0104 c0x0047 (n0x0824-n0x082a) + I cu
+ 0x002333ca, // n0x0105 c0x0000 (---------------) + I cuisinella
+ 0x1233f802, // n0x0106 c0x0048 (n0x082a-n0x082b) + I cv
+ 0x126b8942, // n0x0107 c0x0049 (n0x082b-n0x082f) + I cw
+ 0x12a35882, // n0x0108 c0x004a (n0x082f-n0x0831) + I cx
+ 0x12e29e42, // n0x0109 c0x004b (n0x0831-n0x083e) o I cy
+ 0x002dcac5, // n0x010a c0x0000 (---------------) + I cymru
+ 0x00235b84, // n0x010b c0x0000 (---------------) + I cyou
+ 0x13614442, // n0x010c c0x004d (n0x083f-n0x0840) + I cz
+ 0x0021bc05, // n0x010d c0x0000 (---------------) + I dabur
+ 0x00264503, // n0x010e c0x0000 (---------------) + I dad
+ 0x0032dec5, // n0x010f c0x0000 (---------------) + I dance
+ 0x00209004, // n0x0110 c0x0000 (---------------) + I date
+ 0x00390f06, // n0x0111 c0x0000 (---------------) + I dating
+ 0x00207286, // n0x0112 c0x0000 (---------------) + I datsun
+ 0x00277d83, // n0x0113 c0x0000 (---------------) + I day
+ 0x00251304, // n0x0114 c0x0000 (---------------) + I dclk
+ 0x002f9383, // n0x0115 c0x0000 (---------------) + I dds
+ 0x13a006c2, // n0x0116 c0x004e (n0x0840-n0x0848) + I de
+ 0x002196c4, // n0x0117 c0x0000 (---------------) + I deal
+ 0x00364186, // n0x0118 c0x0000 (---------------) + I dealer
+ 0x002196c5, // n0x0119 c0x0000 (---------------) + I deals
+ 0x002528c6, // n0x011a c0x0000 (---------------) + I degree
+ 0x00268948, // n0x011b c0x0000 (---------------) + I delivery
+ 0x002297c4, // n0x011c c0x0000 (---------------) + I dell
+ 0x0024f885, // n0x011d c0x0000 (---------------) + I delta
+ 0x0022e548, // n0x011e c0x0000 (---------------) + I democrat
+ 0x00298646, // n0x011f c0x0000 (---------------) + I dental
+ 0x00234ec7, // n0x0120 c0x0000 (---------------) + I dentist
+ 0x00232f44, // n0x0121 c0x0000 (---------------) + I desi
+ 0x00232f46, // n0x0122 c0x0000 (---------------) + I design
+ 0x0030a403, // n0x0123 c0x0000 (---------------) + I dev
+ 0x002b0cc8, // n0x0124 c0x0000 (---------------) + I diamonds
+ 0x002f6384, // n0x0125 c0x0000 (---------------) + I diet
+ 0x002ef0c7, // n0x0126 c0x0000 (---------------) + I digital
+ 0x00352bc6, // n0x0127 c0x0000 (---------------) + I direct
+ 0x00352bc9, // n0x0128 c0x0000 (---------------) + I directory
+ 0x002f87c8, // n0x0129 c0x0000 (---------------) + I discount
+ 0x0020c7c2, // n0x012a c0x0000 (---------------) + I dj
+ 0x13e71742, // n0x012b c0x004f (n0x0848-n0x0849) + I dk
+ 0x142618c2, // n0x012c c0x0050 (n0x0849-n0x084e) + I dm
+ 0x0036cd83, // n0x012d c0x0000 (---------------) + I dnp
+ 0x14604002, // n0x012e c0x0051 (n0x084e-n0x0858) + I do
+ 0x00355cc4, // n0x012f c0x0000 (---------------) + I docs
+ 0x00204003, // n0x0130 c0x0000 (---------------) + I dog
+ 0x0024f044, // n0x0131 c0x0000 (---------------) + I doha
+ 0x002e8307, // n0x0132 c0x0000 (---------------) + I domains
+ 0x0023ba06, // n0x0133 c0x0000 (---------------) + I doosan
+ 0x0031c343, // n0x0134 c0x0000 (---------------) + I dot
+ 0x002ede08, // n0x0135 c0x0000 (---------------) + I download
+ 0x003102c5, // n0x0136 c0x0000 (---------------) + I drive
+ 0x00394cc4, // n0x0137 c0x0000 (---------------) + I dstv
+ 0x002482c3, // n0x0138 c0x0000 (---------------) + I dtv
+ 0x0026a005, // n0x0139 c0x0000 (---------------) + I dubai
+ 0x0031b886, // n0x013a c0x0000 (---------------) + I dunlop
+ 0x0032a4c6, // n0x013b c0x0000 (---------------) + I dupont
+ 0x00383246, // n0x013c c0x0000 (---------------) + I durban
+ 0x00300bc4, // n0x013d c0x0000 (---------------) + I dvag
+ 0x14aa3602, // n0x013e c0x0052 (n0x0858-n0x0860) + I dz
+ 0x00330d85, // n0x013f c0x0000 (---------------) + I earth
+ 0x00242e03, // n0x0140 c0x0000 (---------------) + I eat
+ 0x14e00702, // n0x0141 c0x0053 (n0x0860-n0x086c) + I ec
+ 0x0038d785, // n0x0142 c0x0000 (---------------) + I edeka
+ 0x002d75c3, // n0x0143 c0x0000 (---------------) + I edu
+ 0x00375549, // n0x0144 c0x0000 (---------------) + I education
+ 0x15206042, // n0x0145 c0x0054 (n0x086c-n0x0876) + I ee
+ 0x15a0df82, // n0x0146 c0x0056 (n0x0877-n0x0880) + I eg
+ 0x003435c5, // n0x0147 c0x0000 (---------------) + I email
+ 0x00312206, // n0x0148 c0x0000 (---------------) + I emerck
+ 0x002ba7c6, // n0x0149 c0x0000 (---------------) + I energy
+ 0x002a8a88, // n0x014a c0x0000 (---------------) + I engineer
+ 0x002a8a8b, // n0x014b c0x0000 (---------------) + I engineering
+ 0x0037a7cb, // n0x014c c0x0000 (---------------) + I enterprises
+ 0x00364485, // n0x014d c0x0000 (---------------) + I epson
+ 0x0021f909, // n0x014e c0x0000 (---------------) + I equipment
+ 0x01600ec2, // n0x014f c0x0005 (---------------)* o I er
+ 0x00259904, // n0x0150 c0x0000 (---------------) + I erni
+ 0x162010c2, // n0x0151 c0x0058 (n0x0881-n0x0886) + I es
+ 0x00275b43, // n0x0152 c0x0000 (---------------) + I esq
+ 0x002b1846, // n0x0153 c0x0000 (---------------) + I estate
+ 0x16a0bbc2, // n0x0154 c0x005a (n0x0887-n0x088f) + I et
+ 0x0021d5c2, // n0x0155 c0x0000 (---------------) + I eu
+ 0x002ef88a, // n0x0156 c0x0000 (---------------) + I eurovision
+ 0x002b71c3, // n0x0157 c0x0000 (---------------) + I eus
+ 0x003423c6, // n0x0158 c0x0000 (---------------) + I events
+ 0x00381fc8, // n0x0159 c0x0000 (---------------) + I everbank
+ 0x002f3908, // n0x015a c0x0000 (---------------) + I exchange
+ 0x0030c7c6, // n0x015b c0x0000 (---------------) + I expert
+ 0x00203e87, // n0x015c c0x0000 (---------------) + I exposed
+ 0x0029ab47, // n0x015d c0x0000 (---------------) + I express
+ 0x0021008a, // n0x015e c0x0000 (---------------) + I extraspace
+ 0x00310504, // n0x015f c0x0000 (---------------) + I fage
+ 0x00328344, // n0x0160 c0x0000 (---------------) + I fail
+ 0x0033d609, // n0x0161 c0x0000 (---------------) + I fairwinds
+ 0x00377ec5, // n0x0162 c0x0000 (---------------) + I faith
+ 0x00311c06, // n0x0163 c0x0000 (---------------) + I family
+ 0x0020f1c3, // n0x0164 c0x0000 (---------------) + I fan
+ 0x002c5544, // n0x0165 c0x0000 (---------------) + I fans
+ 0x0021f804, // n0x0166 c0x0000 (---------------) + I farm
+ 0x0025d247, // n0x0167 c0x0000 (---------------) + I fashion
+ 0x00287d44, // n0x0168 c0x0000 (---------------) + I fast
+ 0x00369188, // n0x0169 c0x0000 (---------------) + I feedback
+ 0x003127c7, // n0x016a c0x0000 (---------------) + I ferrero
+ 0x16e099c2, // n0x016b c0x005b (n0x088f-n0x0892) + I fi
+ 0x00356104, // n0x016c c0x0000 (---------------) + I film
+ 0x00356745, // n0x016d c0x0000 (---------------) + I final
+ 0x00368607, // n0x016e c0x0000 (---------------) + I finance
+ 0x00371509, // n0x016f c0x0000 (---------------) + I financial
+ 0x00236744, // n0x0170 c0x0000 (---------------) + I fire
+ 0x00237d49, // n0x0171 c0x0000 (---------------) + I firestone
+ 0x00238548, // n0x0172 c0x0000 (---------------) + I firmdale
+ 0x00238904, // n0x0173 c0x0000 (---------------) + I fish
+ 0x00238907, // n0x0174 c0x0000 (---------------) + I fishing
+ 0x00238f03, // n0x0175 c0x0000 (---------------) + I fit
+ 0x00239087, // n0x0176 c0x0000 (---------------) + I fitness
+ 0x016241c2, // n0x0177 c0x0005 (---------------)* o I fj
+ 0x01697782, // n0x0178 c0x0005 (---------------)* o I fk
+ 0x0023a306, // n0x0179 c0x0000 (---------------) + I flickr
+ 0x0023b147, // n0x017a c0x0000 (---------------) + I flights
+ 0x0023c607, // n0x017b c0x0000 (---------------) + I florist
+ 0x0023ef07, // n0x017c c0x0000 (---------------) + I flowers
+ 0x0023f508, // n0x017d c0x0000 (---------------) + I flsmidth
+ 0x0023ffc3, // n0x017e c0x0000 (---------------) + I fly
+ 0x00358002, // n0x017f c0x0000 (---------------) + I fm
+ 0x00200382, // n0x0180 c0x0000 (---------------) + I fo
+ 0x00241943, // n0x0181 c0x0000 (---------------) + I foo
+ 0x0024194b, // n0x0182 c0x0000 (---------------) + I foodnetwork
+ 0x0030b888, // n0x0183 c0x0000 (---------------) + I football
+ 0x003640c4, // n0x0184 c0x0000 (---------------) + I ford
+ 0x00242f85, // n0x0185 c0x0000 (---------------) + I forex
+ 0x00244a47, // n0x0186 c0x0000 (---------------) + I forsale
+ 0x00246b45, // n0x0187 c0x0000 (---------------) + I forum
+ 0x00303b8a, // n0x0188 c0x0000 (---------------) + I foundation
+ 0x17200d42, // n0x0189 c0x005c (n0x0892-n0x08aa) + I fr
+ 0x0024c6c3, // n0x018a c0x0000 (---------------) + I frl
+ 0x0024c787, // n0x018b c0x0000 (---------------) + I frogans
+ 0x003906c9, // n0x018c c0x0000 (---------------) + I frontdoor
+ 0x00200d48, // n0x018d c0x0000 (---------------) + I frontier
+ 0x0026fc04, // n0x018e c0x0000 (---------------) + I fund
+ 0x00272f09, // n0x018f c0x0000 (---------------) + I furniture
+ 0x002764c6, // n0x0190 c0x0000 (---------------) + I futbol
+ 0x00277503, // n0x0191 c0x0000 (---------------) + I fyi
+ 0x00201602, // n0x0192 c0x0000 (---------------) + I ga
+ 0x00215b83, // n0x0193 c0x0000 (---------------) + I gal
+ 0x00226607, // n0x0194 c0x0000 (---------------) + I gallery
+ 0x00362a05, // n0x0195 c0x0000 (---------------) + I gallo
+ 0x0036bd46, // n0x0196 c0x0000 (---------------) + I gallup
+ 0x00212084, // n0x0197 c0x0000 (---------------) + I game
+ 0x002d2145, // n0x0198 c0x0000 (---------------) + I games
+ 0x0020e506, // n0x0199 c0x0000 (---------------) + I garden
+ 0x00205a42, // n0x019a c0x0000 (---------------) + I gb
+ 0x00378f84, // n0x019b c0x0000 (---------------) + I gbiz
+ 0x0021b342, // n0x019c c0x0000 (---------------) + I gd
+ 0x002d0143, // n0x019d c0x0000 (---------------) + I gdn
+ 0x176012c2, // n0x019e c0x005d (n0x08aa-n0x08b1) + I ge
+ 0x00237543, // n0x019f c0x0000 (---------------) + I gea
+ 0x0020db04, // n0x01a0 c0x0000 (---------------) + I gent
+ 0x0020db07, // n0x01a1 c0x0000 (---------------) + I genting
+ 0x00248e82, // n0x01a2 c0x0000 (---------------) + I gf
+ 0x17a00402, // n0x01a3 c0x005e (n0x08b1-n0x08b4) + I gg
+ 0x00268b44, // n0x01a4 c0x0000 (---------------) + I ggee
+ 0x17e36e42, // n0x01a5 c0x005f (n0x08b4-n0x08b9) + I gh
+ 0x18200442, // n0x01a6 c0x0060 (n0x08b9-n0x08bf) + I gi
+ 0x0030f704, // n0x01a7 c0x0000 (---------------) + I gift
+ 0x0030f705, // n0x01a8 c0x0000 (---------------) + I gifts
+ 0x002b9bc5, // n0x01a9 c0x0000 (---------------) + I gives
+ 0x00213586, // n0x01aa c0x0000 (---------------) + I giving
+ 0x18629902, // n0x01ab c0x0061 (n0x08bf-n0x08c4) + I gl
+ 0x0034da85, // n0x01ac c0x0000 (---------------) + I glass
+ 0x00274303, // n0x01ad c0x0000 (---------------) + I gle
+ 0x00389c06, // n0x01ae c0x0000 (---------------) + I global
+ 0x0038a445, // n0x01af c0x0000 (---------------) + I globo
+ 0x002047c2, // n0x01b0 c0x0000 (---------------) + I gm
+ 0x00323145, // n0x01b1 c0x0000 (---------------) + I gmail
+ 0x00208443, // n0x01b2 c0x0000 (---------------) + I gmo
+ 0x0020a643, // n0x01b3 c0x0000 (---------------) + I gmx
+ 0x18a050c2, // n0x01b4 c0x0062 (n0x08c4-n0x08ca) + I gn
+ 0x00238a84, // n0x01b5 c0x0000 (---------------) + I gold
+ 0x00238a89, // n0x01b6 c0x0000 (---------------) + I goldpoint
+ 0x0025d184, // n0x01b7 c0x0000 (---------------) + I golf
+ 0x0028c883, // n0x01b8 c0x0000 (---------------) + I goo
+ 0x00330c48, // n0x01b9 c0x0000 (---------------) + I goodyear
+ 0x0028c884, // n0x01ba c0x0000 (---------------) + I goog
+ 0x0028c886, // n0x01bb c0x0000 (---------------) + I google
+ 0x0028e783, // n0x01bc c0x0000 (---------------) + I gop
+ 0x0020bec3, // n0x01bd c0x0000 (---------------) + I got
+ 0x0020bec4, // n0x01be c0x0000 (---------------) + I gotv
+ 0x0021e283, // n0x01bf c0x0000 (---------------) + I gov
+ 0x18ece142, // n0x01c0 c0x0063 (n0x08ca-n0x08d0) + I gp
+ 0x0034f382, // n0x01c1 c0x0000 (---------------) + I gq
+ 0x1920dc82, // n0x01c2 c0x0064 (n0x08d0-n0x08d6) + I gr
+ 0x002ff248, // n0x01c3 c0x0000 (---------------) + I grainger
+ 0x00341188, // n0x01c4 c0x0000 (---------------) + I graphics
+ 0x0037d046, // n0x01c5 c0x0000 (---------------) + I gratis
+ 0x0025b105, // n0x01c6 c0x0000 (---------------) + I green
+ 0x0021dd45, // n0x01c7 c0x0000 (---------------) + I gripe
+ 0x002646c5, // n0x01c8 c0x0000 (---------------) + I group
+ 0x0026cd02, // n0x01c9 c0x0000 (---------------) + I gs
+ 0x19651202, // n0x01ca c0x0065 (n0x08d6-n0x08dd) + I gt
+ 0x01629702, // n0x01cb c0x0005 (---------------)* o I gu
+ 0x0025f6c5, // n0x01cc c0x0000 (---------------) + I gucci
+ 0x002ca244, // n0x01cd c0x0000 (---------------) + I guge
+ 0x00229705, // n0x01ce c0x0000 (---------------) + I guide
+ 0x0022d647, // n0x01cf c0x0000 (---------------) + I guitars
+ 0x0025bc44, // n0x01d0 c0x0000 (---------------) + I guru
+ 0x00204b82, // n0x01d1 c0x0000 (---------------) + I gw
+ 0x19a25a02, // n0x01d2 c0x0066 (n0x08dd-n0x08e0) + I gy
+ 0x0038a2c7, // n0x01d3 c0x0000 (---------------) + I hamburg
+ 0x00392287, // n0x01d4 c0x0000 (---------------) + I hangout
+ 0x00329884, // n0x01d5 c0x0000 (---------------) + I haus
+ 0x00235cc8, // n0x01d6 c0x0000 (---------------) + I hdfcbank
+ 0x00205e06, // n0x01d7 c0x0000 (---------------) + I health
+ 0x00205e0a, // n0x01d8 c0x0000 (---------------) + I healthcare
+ 0x00205204, // n0x01d9 c0x0000 (---------------) + I help
+ 0x00209408, // n0x01da c0x0000 (---------------) + I helsinki
+ 0x0023f784, // n0x01db c0x0000 (---------------) + I here
+ 0x00219446, // n0x01dc c0x0000 (---------------) + I hermes
+ 0x00280244, // n0x01dd c0x0000 (---------------) + I hgtv
+ 0x0032cd06, // n0x01de c0x0000 (---------------) + I hiphop
+ 0x0028d547, // n0x01df c0x0000 (---------------) + I hitachi
+ 0x0025f603, // n0x01e0 c0x0000 (---------------) + I hiv
+ 0x19e2ea02, // n0x01e1 c0x0067 (n0x08e0-n0x08f8) + I hk
+ 0x0025ab03, // n0x01e2 c0x0000 (---------------) + I hkt
+ 0x00206182, // n0x01e3 c0x0000 (---------------) + I hm
+ 0x1a217542, // n0x01e4 c0x0068 (n0x08f8-n0x08fe) + I hn
+ 0x002cd886, // n0x01e5 c0x0000 (---------------) + I hockey
+ 0x0034b148, // n0x01e6 c0x0000 (---------------) + I holdings
+ 0x00290807, // n0x01e7 c0x0000 (---------------) + I holiday
+ 0x002b8e89, // n0x01e8 c0x0000 (---------------) + I homedepot
+ 0x00291385, // n0x01e9 c0x0000 (---------------) + I homes
+ 0x00292005, // n0x01ea c0x0000 (---------------) + I honda
+ 0x00293c05, // n0x01eb c0x0000 (---------------) + I horse
+ 0x00202fc4, // n0x01ec c0x0000 (---------------) + I host
+ 0x002836c7, // n0x01ed c0x0000 (---------------) + I hosting
+ 0x00294947, // n0x01ee c0x0000 (---------------) + I hoteles
+ 0x00294fc7, // n0x01ef c0x0000 (---------------) + I hotmail
+ 0x0021da05, // n0x01f0 c0x0000 (---------------) + I house
+ 0x00297383, // n0x01f1 c0x0000 (---------------) + I how
+ 0x1a625842, // n0x01f2 c0x0069 (n0x08fe-n0x0903) + I hr
+ 0x002dbf04, // n0x01f3 c0x0000 (---------------) + I hsbc
+ 0x1aa36e82, // n0x01f4 c0x006a (n0x0903-n0x0914) + I ht
+ 0x00249003, // n0x01f5 c0x0000 (---------------) + I htc
+ 0x1ae17d42, // n0x01f6 c0x006b (n0x0914-n0x0934) + I hu
+ 0x0031b7c3, // n0x01f7 c0x0000 (---------------) + I ibm
+ 0x0028bf44, // n0x01f8 c0x0000 (---------------) + I icbc
+ 0x00200b43, // n0x01f9 c0x0000 (---------------) + I ice
+ 0x0033c383, // n0x01fa c0x0000 (---------------) + I icu
+ 0x1b206202, // n0x01fb c0x006c (n0x0934-n0x093f) + I id
+ 0x1ba00e82, // n0x01fc c0x006e (n0x0940-n0x0942) + I ie
+ 0x00370d03, // n0x01fd c0x0000 (---------------) + I ifm
+ 0x0031cac5, // n0x01fe c0x0000 (---------------) + I iinet
+ 0x1be036c2, // n0x01ff c0x006f (n0x0942-n0x0943)* o I il
+ 0x1c603582, // n0x0200 c0x0071 (n0x0944-n0x094b) + I im
+ 0x002f4284, // n0x0201 c0x0000 (---------------) + I imdb
+ 0x00203584, // n0x0202 c0x0000 (---------------) + I immo
+ 0x0020358a, // n0x0203 c0x0000 (---------------) + I immobilien
+ 0x1ce00242, // n0x0204 c0x0073 (n0x094d-n0x095a) + I in
+ 0x00353eca, // n0x0205 c0x0000 (---------------) + I industries
+ 0x00355408, // n0x0206 c0x0000 (---------------) + I infiniti
+ 0x1d200304, // n0x0207 c0x0074 (n0x095a-n0x0964) + I info
+ 0x0020dc03, // n0x0208 c0x0000 (---------------) + I ing
+ 0x00209503, // n0x0209 c0x0000 (---------------) + I ink
+ 0x002ed209, // n0x020a c0x0000 (---------------) + I institute
+ 0x002806c9, // n0x020b c0x0000 (---------------) + I insurance
+ 0x002e8406, // n0x020c c0x0000 (---------------) + I insure
+ 0x1d638c03, // n0x020d c0x0075 (n0x0964-n0x0965) + I int
+ 0x002dd88d, // n0x020e c0x0000 (---------------) + I international
+ 0x002087cb, // n0x020f c0x0000 (---------------) + I investments
+ 0x1da00042, // n0x0210 c0x0076 (n0x0965-n0x0968) + I io
+ 0x00331048, // n0x0211 c0x0000 (---------------) + I ipiranga
+ 0x1de11142, // n0x0212 c0x0077 (n0x0968-n0x096e) + I iq
+ 0x1e200542, // n0x0213 c0x0078 (n0x096e-n0x0977) + I ir
+ 0x00294c05, // n0x0214 c0x0000 (---------------) + I irish
+ 0x1e602b42, // n0x0215 c0x0079 (n0x0977-n0x097f) + I is
+ 0x00370f07, // n0x0216 c0x0000 (---------------) + I iselect
+ 0x00202b43, // n0x0217 c0x0000 (---------------) + I ist
+ 0x00210c48, // n0x0218 c0x0000 (---------------) + I istanbul
+ 0x1ea06e82, // n0x0219 c0x007a (n0x097f-n0x0af0) + I it
+ 0x00206e84, // n0x021a c0x0000 (---------------) + I itau
+ 0x003664c3, // n0x021b c0x0000 (---------------) + I iwc
+ 0x002a51c6, // n0x021c c0x0000 (---------------) + I jaguar
+ 0x0030ad84, // n0x021d c0x0000 (---------------) + I java
+ 0x00243883, // n0x021e c0x0000 (---------------) + I jcb
+ 0x0025b3c3, // n0x021f c0x0000 (---------------) + I jcp
+ 0x1ee01f82, // n0x0220 c0x007b (n0x0af0-n0x0af3) + I je
+ 0x0033ab85, // n0x0221 c0x0000 (---------------) + I jetzt
+ 0x0034f487, // n0x0222 c0x0000 (---------------) + I jewelry
+ 0x00266583, // n0x0223 c0x0000 (---------------) + I jio
+ 0x00297c83, // n0x0224 c0x0000 (---------------) + I jlc
+ 0x00297e43, // n0x0225 c0x0000 (---------------) + I jll
+ 0x01697f02, // n0x0226 c0x0005 (---------------)* o I jm
+ 0x00297f03, // n0x0227 c0x0000 (---------------) + I jmp
+ 0x002987c3, // n0x0228 c0x0000 (---------------) + I jnj
+ 0x1f202982, // n0x0229 c0x007c (n0x0af3-n0x0afb) + I jo
+ 0x002a5d04, // n0x022a c0x0000 (---------------) + I jobs
+ 0x00389ac6, // n0x022b c0x0000 (---------------) + I joburg
+ 0x00203903, // n0x022c c0x0000 (---------------) + I jot
+ 0x00298c43, // n0x022d c0x0000 (---------------) + I joy
+ 0x1f6990c2, // n0x022e c0x007d (n0x0afb-n0x0b6a) + I jp
+ 0x002990c8, // n0x022f c0x0000 (---------------) + I jpmorgan
+ 0x0029ccc4, // n0x0230 c0x0000 (---------------) + I jprs
+ 0x002faec6, // n0x0231 c0x0000 (---------------) + I juegos
+ 0x002e6846, // n0x0232 c0x0000 (---------------) + I kaufen
+ 0x00233fc4, // n0x0233 c0x0000 (---------------) + I kddi
+ 0x2d201002, // n0x0234 c0x00b4 (n0x11fe-n0x11ff)* o I ke
+ 0x002a6f4b, // n0x0235 c0x0000 (---------------) + I kerryhotels
+ 0x0021268e, // n0x0236 c0x0000 (---------------) + I kerrylogistics
+ 0x0021810f, // n0x0237 c0x0000 (---------------) + I kerryproperties
+ 0x00235e83, // n0x0238 c0x0000 (---------------) + I kfh
+ 0x2daa3fc2, // n0x0239 c0x00b6 (n0x1200-n0x1206) + I kg
+ 0x016176c2, // n0x023a c0x0005 (---------------)* o I kh
+ 0x2de01b02, // n0x023b c0x00b7 (n0x1206-n0x120d) + I ki
+ 0x0027d603, // n0x023c c0x0000 (---------------) + I kim
+ 0x002f33c6, // n0x023d c0x0000 (---------------) + I kinder
+ 0x00227886, // n0x023e c0x0000 (---------------) + I kindle
+ 0x00298f07, // n0x023f c0x0000 (---------------) + I kitchen
+ 0x002d2dc4, // n0x0240 c0x0000 (---------------) + I kiwi
+ 0x2e268d82, // n0x0241 c0x00b8 (n0x120d-n0x121e) + I km
+ 0x2e656942, // n0x0242 c0x00b9 (n0x121e-n0x1222) + I kn
+ 0x0021f385, // n0x0243 c0x0000 (---------------) + I koeln
+ 0x002be447, // n0x0244 c0x0000 (---------------) + I komatsu
+ 0x2ea08382, // n0x0245 c0x00ba (n0x1222-n0x1228) + I kp
+ 0x00208384, // n0x0246 c0x0000 (---------------) + I kpmg
+ 0x00360183, // n0x0247 c0x0000 (---------------) + I kpn
+ 0x2ee034c2, // n0x0248 c0x00bb (n0x1228-n0x1246) + I kr
+ 0x0033a003, // n0x0249 c0x0000 (---------------) + I krd
+ 0x0029d8c4, // n0x024a c0x0000 (---------------) + I kred
+ 0x002a3f09, // n0x024b c0x0000 (---------------) + I kuokgroup
+ 0x016ac642, // n0x024c c0x0005 (---------------)* o I kw
+ 0x2f229082, // n0x024d c0x00bc (n0x1246-n0x124b) + I ky
+ 0x002568c6, // n0x024e c0x0000 (---------------) + I kyknet
+ 0x002acb05, // n0x024f c0x0000 (---------------) + I kyoto
+ 0x2f6f0002, // n0x0250 c0x00bd (n0x124b-n0x1251) + I kz
+ 0x2fa01e42, // n0x0251 c0x00be (n0x1251-n0x125a) + I la
+ 0x003276c7, // n0x0252 c0x0000 (---------------) + I lacaixa
+ 0x0033df4b, // n0x0253 c0x0000 (---------------) + I lamborghini
+ 0x002335c9, // n0x0254 c0x0000 (---------------) + I lancaster
+ 0x002364c4, // n0x0255 c0x0000 (---------------) + I land
+ 0x0036d989, // n0x0256 c0x0000 (---------------) + I landrover
+ 0x002679c7, // n0x0257 c0x0000 (---------------) + I lasalle
+ 0x00222143, // n0x0258 c0x0000 (---------------) + I lat
+ 0x002c18c7, // n0x0259 c0x0000 (---------------) + I latrobe
+ 0x00253883, // n0x025a c0x0000 (---------------) + I law
+ 0x00270406, // n0x025b c0x0000 (---------------) + I lawyer
+ 0x2fe0a542, // n0x025c c0x00bf (n0x125a-n0x125f) + I lb
+ 0x302339c2, // n0x025d c0x00c0 (n0x125f-n0x1265) + I lc
+ 0x0021fbc3, // n0x025e c0x0000 (---------------) + I lds
+ 0x00267b05, // n0x025f c0x0000 (---------------) + I lease
+ 0x0025f307, // n0x0260 c0x0000 (---------------) + I leclerc
+ 0x00362985, // n0x0261 c0x0000 (---------------) + I legal
+ 0x002ad945, // n0x0262 c0x0000 (---------------) + I lexus
+ 0x002ce984, // n0x0263 c0x0000 (---------------) + I lgbt
+ 0x306008c2, // n0x0264 c0x00c1 (n0x1265-n0x1266) + I li
+ 0x002ee607, // n0x0265 c0x0000 (---------------) + I liaison
+ 0x002aadc4, // n0x0266 c0x0000 (---------------) + I lidl
+ 0x00247844, // n0x0267 c0x0000 (---------------) + I life
+ 0x0031c64d, // n0x0268 c0x0000 (---------------) + I lifeinsurance
+ 0x00247849, // n0x0269 c0x0000 (---------------) + I lifestyle
+ 0x00236dc8, // n0x026a c0x0000 (---------------) + I lighting
+ 0x00261684, // n0x026b c0x0000 (---------------) + I like
+ 0x002edc87, // n0x026c c0x0000 (---------------) + I limited
+ 0x002ee1c4, // n0x026d c0x0000 (---------------) + I limo
+ 0x00251e07, // n0x026e c0x0000 (---------------) + I lincoln
+ 0x0031dc05, // n0x026f c0x0000 (---------------) + I linde
+ 0x0031e2c4, // n0x0270 c0x0000 (---------------) + I link
+ 0x002bfc05, // n0x0271 c0x0000 (---------------) + I lipsy
+ 0x0024b384, // n0x0272 c0x0000 (---------------) + I live
+ 0x002d7785, // n0x0273 c0x0000 (---------------) + I lixil
+ 0x30a08342, // n0x0274 c0x00c2 (n0x1266-n0x1275) + I lk
+ 0x0020ab84, // n0x0275 c0x0000 (---------------) + I loan
+ 0x0020ab85, // n0x0276 c0x0000 (---------------) + I loans
+ 0x0020af86, // n0x0277 c0x0000 (---------------) + I locker
+ 0x00362ac5, // n0x0278 c0x0000 (---------------) + I locus
+ 0x002c1ec3, // n0x0279 c0x0000 (---------------) + I lol
+ 0x00340906, // n0x027a c0x0000 (---------------) + I london
+ 0x00364385, // n0x027b c0x0000 (---------------) + I lotte
+ 0x00206505, // n0x027c c0x0000 (---------------) + I lotto
+ 0x002123c4, // n0x027d c0x0000 (---------------) + I love
+ 0x30e76e02, // n0x027e c0x00c3 (n0x1275-n0x127a) + I lr
+ 0x31202d42, // n0x027f c0x00c4 (n0x127a-n0x127c) + I ls
+ 0x31605ec2, // n0x0280 c0x00c5 (n0x127c-n0x127e) + I lt
+ 0x003413c3, // n0x0281 c0x0000 (---------------) + I ltd
+ 0x003413c4, // n0x0282 c0x0000 (---------------) + I ltda
+ 0x31a071c2, // n0x0283 c0x00c6 (n0x127e-n0x127f) + I lu
+ 0x0036be05, // n0x0284 c0x0000 (---------------) + I lupin
+ 0x00222d84, // n0x0285 c0x0000 (---------------) + I luxe
+ 0x00225dc6, // n0x0286 c0x0000 (---------------) + I luxury
+ 0x31e27f02, // n0x0287 c0x00c7 (n0x127f-n0x1288) + I lv
+ 0x32228b42, // n0x0288 c0x00c8 (n0x1288-n0x1291) + I ly
+ 0x32604302, // n0x0289 c0x00c9 (n0x1291-n0x1297) + I ma
+ 0x00300a86, // n0x028a c0x0000 (---------------) + I madrid
+ 0x0032a984, // n0x028b c0x0000 (---------------) + I maif
+ 0x0033c9c6, // n0x028c c0x0000 (---------------) + I maison
+ 0x0026a746, // n0x028d c0x0000 (---------------) + I makeup
+ 0x00206903, // n0x028e c0x0000 (---------------) + I man
+ 0x00351aca, // n0x028f c0x0000 (---------------) + I management
+ 0x0022a685, // n0x0290 c0x0000 (---------------) + I mango
+ 0x0029bcc6, // n0x0291 c0x0000 (---------------) + I market
+ 0x00357049, // n0x0292 c0x0000 (---------------) + I marketing
+ 0x0029bcc7, // n0x0293 c0x0000 (---------------) + I markets
+ 0x0027c808, // n0x0294 c0x0000 (---------------) + I marriott
+ 0x00271143, // n0x0295 c0x0000 (---------------) + I mba
+ 0x32a3a6c2, // n0x0296 c0x00ca (n0x1297-n0x1299) + I mc
+ 0x32e38602, // n0x0297 c0x00cb (n0x1299-n0x129a) + I md
+ 0x33208942, // n0x0298 c0x00cc (n0x129a-n0x12a2) + I me
+ 0x002dc385, // n0x0299 c0x0000 (---------------) + I media
+ 0x00282f44, // n0x029a c0x0000 (---------------) + I meet
+ 0x0020fe89, // n0x029b c0x0000 (---------------) + I melbourne
+ 0x003121c4, // n0x029c c0x0000 (---------------) + I meme
+ 0x002edac8, // n0x029d c0x0000 (---------------) + I memorial
+ 0x00208943, // n0x029e c0x0000 (---------------) + I men
+ 0x0034d804, // n0x029f c0x0000 (---------------) + I menu
+ 0x0023a883, // n0x02a0 c0x0000 (---------------) + I meo
+ 0x0031c587, // n0x02a1 c0x0000 (---------------) + I metlife
+ 0x33608402, // n0x02a2 c0x00cd (n0x12a2-n0x12aa) + I mg
+ 0x00249282, // n0x02a3 c0x0000 (---------------) + I mh
+ 0x002322c5, // n0x02a4 c0x0000 (---------------) + I miami
+ 0x00257ec9, // n0x02a5 c0x0000 (---------------) + I microsoft
+ 0x0023fa03, // n0x02a6 c0x0000 (---------------) + I mil
+ 0x0026b344, // n0x02a7 c0x0000 (---------------) + I mini
+ 0x00246f03, // n0x02a8 c0x0000 (---------------) + I mit
+ 0x33b56d82, // n0x02a9 c0x00ce (n0x12aa-n0x12b2) + I mk
+ 0x33e0ab42, // n0x02aa c0x00cf (n0x12b2-n0x12b9) + I ml
+ 0x002afd83, // n0x02ab c0x0000 (---------------) + I mlb
+ 0x00358b83, // n0x02ac c0x0000 (---------------) + I mls
+ 0x016035c2, // n0x02ad c0x0005 (---------------)* o I mm
+ 0x003679c3, // n0x02ae c0x0000 (---------------) + I mma
+ 0x34217082, // n0x02af c0x00d0 (n0x12b9-n0x12bd) + I mn
+ 0x00217084, // n0x02b0 c0x0000 (---------------) + I mnet
+ 0x34603602, // n0x02b1 c0x00d1 (n0x12bd-n0x12c2) + I mo
+ 0x00203604, // n0x02b2 c0x0000 (---------------) + I mobi
+ 0x002cc406, // n0x02b3 c0x0000 (---------------) + I mobily
+ 0x00208484, // n0x02b4 c0x0000 (---------------) + I moda
+ 0x002fb2c3, // n0x02b5 c0x0000 (---------------) + I moe
+ 0x0038f703, // n0x02b6 c0x0000 (---------------) + I moi
+ 0x0021cfc3, // n0x02b7 c0x0000 (---------------) + I mom
+ 0x00230c46, // n0x02b8 c0x0000 (---------------) + I monash
+ 0x002b6185, // n0x02b9 c0x0000 (---------------) + I money
+ 0x00261909, // n0x02ba c0x0000 (---------------) + I montblanc
+ 0x002b60c6, // n0x02bb c0x0000 (---------------) + I mormon
+ 0x002b66c8, // n0x02bc c0x0000 (---------------) + I mortgage
+ 0x002b68c6, // n0x02bd c0x0000 (---------------) + I moscow
+ 0x0025cb84, // n0x02be c0x0000 (---------------) + I moto
+ 0x0028678b, // n0x02bf c0x0000 (---------------) + I motorcycles
+ 0x002b8403, // n0x02c0 c0x0000 (---------------) + I mov
+ 0x002b8405, // n0x02c1 c0x0000 (---------------) + I movie
+ 0x002b8548, // n0x02c2 c0x0000 (---------------) + I movistar
+ 0x00214902, // n0x02c3 c0x0000 (---------------) + I mp
+ 0x003279c2, // n0x02c4 c0x0000 (---------------) + I mq
+ 0x34adcb42, // n0x02c5 c0x00d2 (n0x12c2-n0x12c4) + I mr
+ 0x34e09282, // n0x02c6 c0x00d3 (n0x12c4-n0x12c9) + I ms
+ 0x35259642, // n0x02c7 c0x00d4 (n0x12c9-n0x12cd) + I mt
+ 0x00259643, // n0x02c8 c0x0000 (---------------) + I mtn
+ 0x002b8844, // n0x02c9 c0x0000 (---------------) + I mtpc
+ 0x002b9703, // n0x02ca c0x0000 (---------------) + I mtr
+ 0x35a000c2, // n0x02cb c0x00d6 (n0x12ce-n0x12d5) + I mu
+ 0x002bae0b, // n0x02cc c0x0000 (---------------) + I multichoice
+ 0x35ebd646, // n0x02cd c0x00d7 (n0x12d5-n0x14f9) + I museum
+ 0x0036d306, // n0x02ce c0x0000 (---------------) + I mutual
+ 0x002bdc88, // n0x02cf c0x0000 (---------------) + I mutuelle
+ 0x3624ffc2, // n0x02d0 c0x00d8 (n0x14f9-n0x1507) + I mv
+ 0x3660a142, // n0x02d1 c0x00d9 (n0x1507-n0x1512) + I mw
+ 0x36a0a682, // n0x02d2 c0x00da (n0x1512-n0x1518) + I mx
+ 0x36e20282, // n0x02d3 c0x00db (n0x1518-n0x1520) + I my
+ 0x372c6c02, // n0x02d4 c0x00dc (n0x1520-n0x1521)* o I mz
+ 0x002c6c0b, // n0x02d5 c0x0000 (---------------) + I mzansimagic
+ 0x37600282, // n0x02d6 c0x00dd (n0x1521-n0x1532) + I na
+ 0x002f3845, // n0x02d7 c0x0000 (---------------) + I nadex
+ 0x0032b206, // n0x02d8 c0x0000 (---------------) + I nagoya
+ 0x37a98944, // n0x02d9 c0x00de (n0x1532-n0x1534) + I name
+ 0x0030cfc7, // n0x02da c0x0000 (---------------) + I naspers
+ 0x00240dc6, // n0x02db c0x0000 (---------------) + I natura
+ 0x0037fe44, // n0x02dc c0x0000 (---------------) + I navy
+ 0x3861c742, // n0x02dd c0x00e1 (n0x1536-n0x1537) + I nc
+ 0x00201082, // n0x02de c0x0000 (---------------) + I ne
+ 0x00305e83, // n0x02df c0x0000 (---------------) + I nec
+ 0x38a170c3, // n0x02e0 c0x00e2 (n0x1537-n0x1568) + I net
+ 0x002efe87, // n0x02e1 c0x0000 (---------------) + I netbank
+ 0x002d7687, // n0x02e2 c0x0000 (---------------) + I netflix
+ 0x00241a47, // n0x02e3 c0x0000 (---------------) + I network
+ 0x00319107, // n0x02e4 c0x0000 (---------------) + I neustar
+ 0x0021a383, // n0x02e5 c0x0000 (---------------) + I new
+ 0x00366d44, // n0x02e6 c0x0000 (---------------) + I news
+ 0x00210044, // n0x02e7 c0x0000 (---------------) + I next
+ 0x00352aca, // n0x02e8 c0x0000 (---------------) + I nextdirect
+ 0x00255985, // n0x02e9 c0x0000 (---------------) + I nexus
+ 0x39e00342, // n0x02ea c0x00e7 (n0x1570-n0x157a) + I nf
+ 0x3a201282, // n0x02eb c0x00e8 (n0x157a-n0x1583) + I ng
+ 0x00202303, // n0x02ec c0x0000 (---------------) + I ngo
+ 0x0025aac3, // n0x02ed c0x0000 (---------------) + I nhk
+ 0x01603d42, // n0x02ee c0x0005 (---------------)* o I ni
+ 0x00369044, // n0x02ef c0x0000 (---------------) + I nico
+ 0x00204c45, // n0x02f0 c0x0000 (---------------) + I nikon
+ 0x002bbfc5, // n0x02f1 c0x0000 (---------------) + I ninja
+ 0x0029c346, // n0x02f2 c0x0000 (---------------) + I nissan
+ 0x3aa36482, // n0x02f3 c0x00ea (n0x1584-n0x1587) + I nl
+ 0x3ae00c02, // n0x02f4 c0x00eb (n0x1587-n0x185d) + I no
+ 0x00201b85, // n0x02f5 c0x0000 (---------------) + I nokia
+ 0x0036d012, // n0x02f6 c0x0000 (---------------) + I northwesternmutual
+ 0x00204546, // n0x02f7 c0x0000 (---------------) + I norton
+ 0x0021c343, // n0x02f8 c0x0000 (---------------) + I now
+ 0x0036a0c6, // n0x02f9 c0x0000 (---------------) + I nowruz
+ 0x0030b645, // n0x02fa c0x0000 (---------------) + I nowtv
+ 0x0160a2c2, // n0x02fb c0x0005 (---------------)* o I np
+ 0x43209e82, // n0x02fc c0x010c (n0x1885-n0x188c) + I nr
+ 0x002cc1c3, // n0x02fd c0x0000 (---------------) + I nra
+ 0x00345903, // n0x02fe c0x0000 (---------------) + I nrw
+ 0x00361083, // n0x02ff c0x0000 (---------------) + I ntt
+ 0x43605bc2, // n0x0300 c0x010d (n0x188c-n0x188f) + I nu
+ 0x00223403, // n0x0301 c0x0000 (---------------) + I nyc
+ 0x43a078c2, // n0x0302 c0x010e (n0x188f-n0x189f) + I nz
+ 0x00203643, // n0x0303 c0x0000 (---------------) + I obi
+ 0x002a5d48, // n0x0304 c0x0000 (---------------) + I observer
+ 0x00219c46, // n0x0305 c0x0000 (---------------) + I office
+ 0x003954c7, // n0x0306 c0x0000 (---------------) + I okinawa
+ 0x002a2686, // n0x0307 c0x0000 (---------------) + I olayan
+ 0x002a268b, // n0x0308 c0x0000 (---------------) + I olayangroup
+ 0x002dbd04, // n0x0309 c0x0000 (---------------) + I ollo
+ 0x44200082, // n0x030a c0x0110 (n0x18a0-n0x18a9) + I om
+ 0x0036bc85, // n0x030b c0x0000 (---------------) + I omega
+ 0x0021a343, // n0x030c c0x0000 (---------------) + I one
+ 0x00292c83, // n0x030d c0x0000 (---------------) + I ong
+ 0x003023c3, // n0x030e c0x0000 (---------------) + I onl
+ 0x003023c6, // n0x030f c0x0000 (---------------) + I online
+ 0x0027b9c3, // n0x0310 c0x0000 (---------------) + I ooo
+ 0x0032d686, // n0x0311 c0x0000 (---------------) + I oracle
+ 0x002a6246, // n0x0312 c0x0000 (---------------) + I orange
+ 0x4461dcc3, // n0x0313 c0x0111 (n0x18a9-n0x18e3) + I org
+ 0x00299187, // n0x0314 c0x0000 (---------------) + I organic
+ 0x0029a9cd, // n0x0315 c0x0000 (---------------) + I orientexpress
+ 0x002864c5, // n0x0316 c0x0000 (---------------) + I osaka
+ 0x00239506, // n0x0317 c0x0000 (---------------) + I otsuka
+ 0x00206543, // n0x0318 c0x0000 (---------------) + I ott
+ 0x00389e43, // n0x0319 c0x0000 (---------------) + I ovh
+ 0x45e052c2, // n0x031a c0x0117 (n0x1920-n0x192b) + I pa
+ 0x00310fc4, // n0x031b c0x0000 (---------------) + I page
+ 0x002fb60c, // n0x031c c0x0000 (---------------) + I pamperedchef
+ 0x0022c007, // n0x031d c0x0000 (---------------) + I panerai
+ 0x0025b445, // n0x031e c0x0000 (---------------) + I paris
+ 0x0028f504, // n0x031f c0x0000 (---------------) + I pars
+ 0x00297f88, // n0x0320 c0x0000 (---------------) + I partners
+ 0x002c2085, // n0x0321 c0x0000 (---------------) + I parts
+ 0x002a2905, // n0x0322 c0x0000 (---------------) + I party
+ 0x002a4109, // n0x0323 c0x0000 (---------------) + I passagens
+ 0x002ab584, // n0x0324 c0x0000 (---------------) + I payu
+ 0x002b88c4, // n0x0325 c0x0000 (---------------) + I pccw
+ 0x46214942, // n0x0326 c0x0118 (n0x192b-n0x1933) + I pe
+ 0x0021ab43, // n0x0327 c0x0000 (---------------) + I pet
+ 0x46764f42, // n0x0328 c0x0119 (n0x1933-n0x1936) + I pf
+ 0x016bf182, // n0x0329 c0x0005 (---------------)* o I pg
+ 0x46a8f0c2, // n0x032a c0x011a (n0x1936-n0x193e) + I ph
+ 0x002dc948, // n0x032b c0x0000 (---------------) + I pharmacy
+ 0x002bfb47, // n0x032c c0x0000 (---------------) + I philips
+ 0x0028f0c5, // n0x032d c0x0000 (---------------) + I photo
+ 0x002c014b, // n0x032e c0x0000 (---------------) + I photography
+ 0x002bea86, // n0x032f c0x0000 (---------------) + I photos
+ 0x002c0346, // n0x0330 c0x0000 (---------------) + I physio
+ 0x002c04c6, // n0x0331 c0x0000 (---------------) + I piaget
+ 0x0021ec04, // n0x0332 c0x0000 (---------------) + I pics
+ 0x002c0a46, // n0x0333 c0x0000 (---------------) + I pictet
+ 0x002c0fc8, // n0x0334 c0x0000 (---------------) + I pictures
+ 0x0022c6c3, // n0x0335 c0x0000 (---------------) + I pid
+ 0x00243743, // n0x0336 c0x0000 (---------------) + I pin
+ 0x00243744, // n0x0337 c0x0000 (---------------) + I ping
+ 0x002c1c84, // n0x0338 c0x0000 (---------------) + I pink
+ 0x002c3cc5, // n0x0339 c0x0000 (---------------) + I pizza
+ 0x46ec3e02, // n0x033a c0x011b (n0x193e-n0x194c) + I pk
+ 0x47201e02, // n0x033b c0x011c (n0x194c-n0x19f1) + I pl
+ 0x00201e05, // n0x033c c0x0000 (---------------) + I place
+ 0x00290d44, // n0x033d c0x0000 (---------------) + I play
+ 0x002c61cb, // n0x033e c0x0000 (---------------) + I playstation
+ 0x002c7948, // n0x033f c0x0000 (---------------) + I plumbing
+ 0x002c7b84, // n0x0340 c0x0000 (---------------) + I plus
+ 0x002083c2, // n0x0341 c0x0000 (---------------) + I pm
+ 0x47a3df82, // n0x0342 c0x011e (n0x1a20-n0x1a25) + I pn
+ 0x0029a743, // n0x0343 c0x0000 (---------------) + I pnc
+ 0x002c7fc4, // n0x0344 c0x0000 (---------------) + I pohl
+ 0x002c80c5, // n0x0345 c0x0000 (---------------) + I poker
+ 0x002c9d84, // n0x0346 c0x0000 (---------------) + I porn
+ 0x002b31c4, // n0x0347 c0x0000 (---------------) + I post
+ 0x47e18242, // n0x0348 c0x011f (n0x1a25-n0x1a32) + I pr
+ 0x0025da85, // n0x0349 c0x0000 (---------------) + I praxi
+ 0x0029abc5, // n0x034a c0x0000 (---------------) + I press
+ 0x002cad45, // n0x034b c0x0000 (---------------) + I prime
+ 0x48218243, // n0x034c c0x0120 (n0x1a32-n0x1a39) + I pro
+ 0x002cbe44, // n0x034d c0x0000 (---------------) + I prod
+ 0x002cbe4b, // n0x034e c0x0000 (---------------) + I productions
+ 0x002cc284, // n0x034f c0x0000 (---------------) + I prof
+ 0x002cca85, // n0x0350 c0x0000 (---------------) + I promo
+ 0x0021824a, // n0x0351 c0x0000 (---------------) + I properties
+ 0x002cce48, // n0x0352 c0x0000 (---------------) + I property
+ 0x002cd04a, // n0x0353 c0x0000 (---------------) + I protection
+ 0x4861dc02, // n0x0354 c0x0121 (n0x1a39-n0x1a40) + I ps
+ 0x48a95982, // n0x0355 c0x0122 (n0x1a40-n0x1a49) + I pt
+ 0x00296543, // n0x0356 c0x0000 (---------------) + I pub
+ 0x48f8ae42, // n0x0357 c0x0123 (n0x1a49-n0x1a4f) + I pw
+ 0x492be202, // n0x0358 c0x0124 (n0x1a4f-n0x1a56) + I py
+ 0x496fd8c2, // n0x0359 c0x0125 (n0x1a56-n0x1a5f) + I qa
+ 0x002ce804, // n0x035a c0x0000 (---------------) + I qpon
+ 0x00211186, // n0x035b c0x0000 (---------------) + I quebec
+ 0x00222685, // n0x035c c0x0000 (---------------) + I quest
+ 0x00301a06, // n0x035d c0x0000 (---------------) + I racing
+ 0x49a030c2, // n0x035e c0x0126 (n0x1a5f-n0x1a63) + I re
+ 0x0033d884, // n0x035f c0x0000 (---------------) + I read
+ 0x0032e0c7, // n0x0360 c0x0000 (---------------) + I realtor
+ 0x0036b3c6, // n0x0361 c0x0000 (---------------) + I realty
+ 0x00307487, // n0x0362 c0x0000 (---------------) + I recipes
+ 0x00230683, // n0x0363 c0x0000 (---------------) + I red
+ 0x0029d908, // n0x0364 c0x0000 (---------------) + I redstone
+ 0x003237cb, // n0x0365 c0x0000 (---------------) + I redumbrella
+ 0x002730c5, // n0x0366 c0x0000 (---------------) + I rehab
+ 0x002e8505, // n0x0367 c0x0000 (---------------) + I reise
+ 0x002e8506, // n0x0368 c0x0000 (---------------) + I reisen
+ 0x002a45c4, // n0x0369 c0x0000 (---------------) + I reit
+ 0x00365b48, // n0x036a c0x0000 (---------------) + I reliance
+ 0x00210403, // n0x036b c0x0000 (---------------) + I ren
+ 0x00210404, // n0x036c c0x0000 (---------------) + I rent
+ 0x00210407, // n0x036d c0x0000 (---------------) + I rentals
+ 0x002117c6, // n0x036e c0x0000 (---------------) + I repair
+ 0x0030ea86, // n0x036f c0x0000 (---------------) + I report
+ 0x002964ca, // n0x0370 c0x0000 (---------------) + I republican
+ 0x00237dc4, // n0x0371 c0x0000 (---------------) + I rest
+ 0x003386ca, // n0x0372 c0x0000 (---------------) + I restaurant
+ 0x0031bd06, // n0x0373 c0x0000 (---------------) + I review
+ 0x0031bd07, // n0x0374 c0x0000 (---------------) + I reviews
+ 0x00243007, // n0x0375 c0x0000 (---------------) + I rexroth
+ 0x002614c4, // n0x0376 c0x0000 (---------------) + I rich
+ 0x002614c9, // n0x0377 c0x0000 (---------------) + I richardli
+ 0x0024ea05, // n0x0378 c0x0000 (---------------) + I ricoh
+ 0x00228f83, // n0x0379 c0x0000 (---------------) + I ril
+ 0x0022ad03, // n0x037a c0x0000 (---------------) + I rio
+ 0x0021dd83, // n0x037b c0x0000 (---------------) + I rip
+ 0x49e00d82, // n0x037c c0x0127 (n0x1a63-n0x1a6f) + I ro
+ 0x0028e886, // n0x037d c0x0000 (---------------) + I rocher
+ 0x00297105, // n0x037e c0x0000 (---------------) + I rocks
+ 0x002c1b45, // n0x037f c0x0000 (---------------) + I rodeo
+ 0x0023a144, // n0x0380 c0x0000 (---------------) + I room
+ 0x4a2060c2, // n0x0381 c0x0128 (n0x1a6f-n0x1a76) + I rs
+ 0x00324a84, // n0x0382 c0x0000 (---------------) + I rsvp
+ 0x4a6044c2, // n0x0383 c0x0129 (n0x1a76-n0x1afa) + I ru
+ 0x0024f144, // n0x0384 c0x0000 (---------------) + I ruhr
+ 0x002044c3, // n0x0385 c0x0000 (---------------) + I run
+ 0x4ab0d882, // n0x0386 c0x012a (n0x1afa-n0x1b03) + I rw
+ 0x0031d103, // n0x0387 c0x0000 (---------------) + I rwe
+ 0x00289606, // n0x0388 c0x0000 (---------------) + I ryukyu
+ 0x4ae01a02, // n0x0389 c0x012b (n0x1b03-n0x1b0b) + I sa
+ 0x00272608, // n0x038a c0x0000 (---------------) + I saarland
+ 0x00234784, // n0x038b c0x0000 (---------------) + I safe
+ 0x00234786, // n0x038c c0x0000 (---------------) + I safety
+ 0x002f4d46, // n0x038d c0x0000 (---------------) + I sakura
+ 0x00244b04, // n0x038e c0x0000 (---------------) + I sale
+ 0x00340885, // n0x038f c0x0000 (---------------) + I salon
+ 0x00395107, // n0x0390 c0x0000 (---------------) + I samsung
+ 0x0029c407, // n0x0391 c0x0000 (---------------) + I sandvik
+ 0x0029c40f, // n0x0392 c0x0000 (---------------) + I sandvikcoromant
+ 0x002098c6, // n0x0393 c0x0000 (---------------) + I sanofi
+ 0x00210583, // n0x0394 c0x0000 (---------------) + I sap
+ 0x00210584, // n0x0395 c0x0000 (---------------) + I sapo
+ 0x0021d684, // n0x0396 c0x0000 (---------------) + I sarl
+ 0x002275c3, // n0x0397 c0x0000 (---------------) + I sas
+ 0x00219584, // n0x0398 c0x0000 (---------------) + I save
+ 0x002332c4, // n0x0399 c0x0000 (---------------) + I saxo
+ 0x4b2046c2, // n0x039a c0x012c (n0x1b0b-n0x1b10) + I sb
+ 0x00277ac3, // n0x039b c0x0000 (---------------) + I sbi
+ 0x002350c3, // n0x039c c0x0000 (---------------) + I sbs
+ 0x4b600982, // n0x039d c0x012d (n0x1b10-n0x1b15) + I sc
+ 0x00229183, // n0x039e c0x0000 (---------------) + I sca
+ 0x00355d83, // n0x039f c0x0000 (---------------) + I scb
+ 0x00206107, // n0x03a0 c0x0000 (---------------) + I schmidt
+ 0x0023514c, // n0x03a1 c0x0000 (---------------) + I scholarships
+ 0x00235406, // n0x03a2 c0x0000 (---------------) + I school
+ 0x002c39c6, // n0x03a3 c0x0000 (---------------) + I schule
+ 0x00370987, // n0x03a4 c0x0000 (---------------) + I schwarz
+ 0x00223b07, // n0x03a5 c0x0000 (---------------) + I science
+ 0x00212fc4, // n0x03a6 c0x0000 (---------------) + I scor
+ 0x00237984, // n0x03a7 c0x0000 (---------------) + I scot
+ 0x4ba4f842, // n0x03a8 c0x012e (n0x1b15-n0x1b1d) + I sd
+ 0x4be02e82, // n0x03a9 c0x012f (n0x1b1d-n0x1b46) + I se
+ 0x003004c4, // n0x03aa c0x0000 (---------------) + I seat
+ 0x00223f88, // n0x03ab c0x0000 (---------------) + I security
+ 0x00267bc4, // n0x03ac c0x0000 (---------------) + I seek
+ 0x002ba785, // n0x03ad c0x0000 (---------------) + I sener
+ 0x00243bc8, // n0x03ae c0x0000 (---------------) + I services
+ 0x002476c3, // n0x03af c0x0000 (---------------) + I sew
+ 0x0029acc3, // n0x03b0 c0x0000 (---------------) + I sex
+ 0x0029acc4, // n0x03b1 c0x0000 (---------------) + I sexy
+ 0x4c262dc2, // n0x03b2 c0x0130 (n0x1b46-n0x1b4d) + I sg
+ 0x4c6001c2, // n0x03b3 c0x0131 (n0x1b4d-n0x1b53) + I sh
+ 0x00255e05, // n0x03b4 c0x0000 (---------------) + I sharp
+ 0x00256344, // n0x03b5 c0x0000 (---------------) + I shaw
+ 0x00208c04, // n0x03b6 c0x0000 (---------------) + I shia
+ 0x002cb1c7, // n0x03b7 c0x0000 (---------------) + I shiksha
+ 0x00369905, // n0x03b8 c0x0000 (---------------) + I shoes
+ 0x002b0e86, // n0x03b9 c0x0000 (---------------) + I shouji
+ 0x002b3884, // n0x03ba c0x0000 (---------------) + I show
+ 0x002b6b87, // n0x03bb c0x0000 (---------------) + I shriram
+ 0x4ca09182, // n0x03bc c0x0132 (n0x1b53-n0x1b54) + I si
+ 0x0036b904, // n0x03bd c0x0000 (---------------) + I silk
+ 0x002914c4, // n0x03be c0x0000 (---------------) + I sina
+ 0x00274247, // n0x03bf c0x0000 (---------------) + I singles
+ 0x00242b84, // n0x03c0 c0x0000 (---------------) + I site
+ 0x0022e942, // n0x03c1 c0x0000 (---------------) + I sj
+ 0x4ce07b42, // n0x03c2 c0x0133 (n0x1b54-n0x1b55) + I sk
+ 0x00207b43, // n0x03c3 c0x0000 (---------------) + I ski
+ 0x002f3384, // n0x03c4 c0x0000 (---------------) + I skin
+ 0x00229043, // n0x03c5 c0x0000 (---------------) + I sky
+ 0x00229045, // n0x03c6 c0x0000 (---------------) + I skype
+ 0x4d212582, // n0x03c7 c0x0134 (n0x1b55-n0x1b5a) + I sl
+ 0x0023f582, // n0x03c8 c0x0000 (---------------) + I sm
+ 0x0034b305, // n0x03c9 c0x0000 (---------------) + I smile
+ 0x4d610b02, // n0x03ca c0x0135 (n0x1b5a-n0x1b62) + I sn
+ 0x00310444, // n0x03cb c0x0000 (---------------) + I sncf
+ 0x4da01102, // n0x03cc c0x0136 (n0x1b62-n0x1b65) + I so
+ 0x00240a06, // n0x03cd c0x0000 (---------------) + I soccer
+ 0x002c27c6, // n0x03ce c0x0000 (---------------) + I social
+ 0x00258008, // n0x03cf c0x0000 (---------------) + I softbank
+ 0x002a6c88, // n0x03d0 c0x0000 (---------------) + I software
+ 0x002de604, // n0x03d1 c0x0000 (---------------) + I sohu
+ 0x002d0205, // n0x03d2 c0x0000 (---------------) + I solar
+ 0x002d9149, // n0x03d3 c0x0000 (---------------) + I solutions
+ 0x00364504, // n0x03d4 c0x0000 (---------------) + I song
+ 0x002bc184, // n0x03d5 c0x0000 (---------------) + I sony
+ 0x00207fc3, // n0x03d6 c0x0000 (---------------) + I soy
+ 0x002101c5, // n0x03d7 c0x0000 (---------------) + I space
+ 0x00379247, // n0x03d8 c0x0000 (---------------) + I spiegel
+ 0x00236a04, // n0x03d9 c0x0000 (---------------) + I spot
+ 0x0033d80d, // n0x03da c0x0000 (---------------) + I spreadbetting
+ 0x002ceec2, // n0x03db c0x0000 (---------------) + I sr
+ 0x002ceec3, // n0x03dc c0x0000 (---------------) + I srl
+ 0x4de023c2, // n0x03dd c0x0137 (n0x1b65-n0x1b71) + I st
+ 0x0035bf05, // n0x03de c0x0000 (---------------) + I stada
+ 0x00232444, // n0x03df c0x0000 (---------------) + I star
+ 0x003191c7, // n0x03e0 c0x0000 (---------------) + I starhub
+ 0x002b1889, // n0x03e1 c0x0000 (---------------) + I statebank
+ 0x0029ca07, // n0x03e2 c0x0000 (---------------) + I statoil
+ 0x00264603, // n0x03e3 c0x0000 (---------------) + I stc
+ 0x00264608, // n0x03e4 c0x0000 (---------------) + I stcgroup
+ 0x00259f09, // n0x03e5 c0x0000 (---------------) + I stockholm
+ 0x002cf147, // n0x03e6 c0x0000 (---------------) + I storage
+ 0x002cf4c5, // n0x03e7 c0x0000 (---------------) + I store
+ 0x002cfc86, // n0x03e8 c0x0000 (---------------) + I studio
+ 0x002cfe05, // n0x03e9 c0x0000 (---------------) + I study
+ 0x00247945, // n0x03ea c0x0000 (---------------) + I style
+ 0x4e203a42, // n0x03eb c0x0138 (n0x1b71-n0x1b91) + I su
+ 0x002f0b45, // n0x03ec c0x0000 (---------------) + I sucks
+ 0x002acd0a, // n0x03ed c0x0000 (---------------) + I supersport
+ 0x002b49c8, // n0x03ee c0x0000 (---------------) + I supplies
+ 0x002cccc6, // n0x03ef c0x0000 (---------------) + I supply
+ 0x00243e87, // n0x03f0 c0x0000 (---------------) + I support
+ 0x00287c84, // n0x03f1 c0x0000 (---------------) + I surf
+ 0x00330647, // n0x03f2 c0x0000 (---------------) + I surgery
+ 0x002d2f46, // n0x03f3 c0x0000 (---------------) + I suzuki
+ 0x4e61d0c2, // n0x03f4 c0x0139 (n0x1b91-n0x1b96) + I sv
+ 0x0020ac86, // n0x03f5 c0x0000 (---------------) + I swatch
+ 0x002d6685, // n0x03f6 c0x0000 (---------------) + I swiss
+ 0x4ead6b42, // n0x03f7 c0x013a (n0x1b96-n0x1b97) + I sx
+ 0x4ee84ec2, // n0x03f8 c0x013b (n0x1b97-n0x1b9d) + I sy
+ 0x00368086, // n0x03f9 c0x0000 (---------------) + I sydney
+ 0x0029d448, // n0x03fa c0x0000 (---------------) + I symantec
+ 0x00392447, // n0x03fb c0x0000 (---------------) + I systems
+ 0x4f207582, // n0x03fc c0x013c (n0x1b9d-n0x1ba0) + I sz
+ 0x0020c083, // n0x03fd c0x0000 (---------------) + I tab
+ 0x00382f46, // n0x03fe c0x0000 (---------------) + I taipei
+ 0x00216944, // n0x03ff c0x0000 (---------------) + I talk
+ 0x003879c6, // n0x0400 c0x0000 (---------------) + I taobao
+ 0x0031cbca, // n0x0401 c0x0000 (---------------) + I tatamotors
+ 0x0031df05, // n0x0402 c0x0000 (---------------) + I tatar
+ 0x0020f886, // n0x0403 c0x0000 (---------------) + I tattoo
+ 0x00217c43, // n0x0404 c0x0000 (---------------) + I tax
+ 0x00217c44, // n0x0405 c0x0000 (---------------) + I taxi
+ 0x0020ad42, // n0x0406 c0x0000 (---------------) + I tc
+ 0x002f4203, // n0x0407 c0x0000 (---------------) + I tci
+ 0x4f600682, // n0x0408 c0x013d (n0x1ba0-n0x1ba1) + I td
+ 0x002c9803, // n0x0409 c0x0000 (---------------) + I tdk
+ 0x00354144, // n0x040a c0x0000 (---------------) + I team
+ 0x0029d584, // n0x040b c0x0000 (---------------) + I tech
+ 0x0029d58a, // n0x040c c0x0000 (---------------) + I technology
+ 0x0022ba83, // n0x040d c0x0000 (---------------) + I tel
+ 0x002734c8, // n0x040e c0x0000 (---------------) + I telecity
+ 0x00250a0a, // n0x040f c0x0000 (---------------) + I telefonica
+ 0x00325507, // n0x0410 c0x0000 (---------------) + I temasek
+ 0x002dab46, // n0x0411 c0x0000 (---------------) + I tennis
+ 0x0033f0c4, // n0x0412 c0x0000 (---------------) + I teva
+ 0x0027e202, // n0x0413 c0x0000 (---------------) + I tf
+ 0x0021e342, // n0x0414 c0x0000 (---------------) + I tg
+ 0x4fa01d82, // n0x0415 c0x013e (n0x1ba1-n0x1ba8) + I th
+ 0x00235c83, // n0x0416 c0x0000 (---------------) + I thd
+ 0x002f9147, // n0x0417 c0x0000 (---------------) + I theater
+ 0x00242d87, // n0x0418 c0x0000 (---------------) + I theatre
+ 0x00377f8b, // n0x0419 c0x0000 (---------------) + I theguardian
+ 0x00340707, // n0x041a c0x0000 (---------------) + I tickets
+ 0x0021bb06, // n0x041b c0x0000 (---------------) + I tienda
+ 0x00375107, // n0x041c c0x0000 (---------------) + I tiffany
+ 0x00354984, // n0x041d c0x0000 (---------------) + I tips
+ 0x00355585, // n0x041e c0x0000 (---------------) + I tires
+ 0x002a4985, // n0x041f c0x0000 (---------------) + I tirol
+ 0x4fe02bc2, // n0x0420 c0x013f (n0x1ba8-n0x1bb7) + I tj
+ 0x0023a7c2, // n0x0421 c0x0000 (---------------) + I tk
+ 0x5020fc42, // n0x0422 c0x0140 (n0x1bb7-n0x1bb8) + I tl
+ 0x50608902, // n0x0423 c0x0141 (n0x1bb8-n0x1bc0) + I tm
+ 0x0026a9c5, // n0x0424 c0x0000 (---------------) + I tmall
+ 0x50a1d1c2, // n0x0425 c0x0142 (n0x1bc0-n0x1bd4) + I tn
+ 0x50e01682, // n0x0426 c0x0143 (n0x1bd4-n0x1bda) + I to
+ 0x00312e85, // n0x0427 c0x0000 (---------------) + I today
+ 0x00316545, // n0x0428 c0x0000 (---------------) + I tokyo
+ 0x0020f945, // n0x0429 c0x0000 (---------------) + I tools
+ 0x002469c3, // n0x042a c0x0000 (---------------) + I top
+ 0x00338cc5, // n0x042b c0x0000 (---------------) + I toray
+ 0x002beb47, // n0x042c c0x0000 (---------------) + I toshiba
+ 0x00339905, // n0x042d c0x0000 (---------------) + I tours
+ 0x0021abc4, // n0x042e c0x0000 (---------------) + I town
+ 0x00338906, // n0x042f c0x0000 (---------------) + I toyota
+ 0x00247b04, // n0x0430 c0x0000 (---------------) + I toys
+ 0x00285142, // n0x0431 c0x0000 (---------------) + I tp
+ 0x51202402, // n0x0432 c0x0144 (n0x1bda-n0x1bef) + I tr
+ 0x00229a45, // n0x0433 c0x0000 (---------------) + I trade
+ 0x0028fe07, // n0x0434 c0x0000 (---------------) + I trading
+ 0x002b9748, // n0x0435 c0x0000 (---------------) + I training
+ 0x0027f186, // n0x0436 c0x0000 (---------------) + I travel
+ 0x0027f18d, // n0x0437 c0x0000 (---------------) + I travelchannel
+ 0x00280489, // n0x0438 c0x0000 (---------------) + I travelers
+ 0x00280492, // n0x0439 c0x0000 (---------------) + I travelersinsurance
+ 0x00313185, // n0x043a c0x0000 (---------------) + I trust
+ 0x0034a4c3, // n0x043b c0x0000 (---------------) + I trv
+ 0x51e06582, // n0x043c c0x0147 (n0x1bf1-n0x1c02) + I tt
+ 0x0035a104, // n0x043d c0x0000 (---------------) + I tube
+ 0x002d71c3, // n0x043e c0x0000 (---------------) + I tui
+ 0x002d83c5, // n0x043f c0x0000 (---------------) + I tunes
+ 0x002d8e45, // n0x0440 c0x0000 (---------------) + I tushu
+ 0x5220bf42, // n0x0441 c0x0148 (n0x1c02-n0x1c06) + I tv
+ 0x0020bf43, // n0x0442 c0x0000 (---------------) + I tvs
+ 0x52641ac2, // n0x0443 c0x0149 (n0x1c06-n0x1c14) + I tw
+ 0x52a17142, // n0x0444 c0x014a (n0x1c14-n0x1c20) + I tz
+ 0x52e17d82, // n0x0445 c0x014b (n0x1c20-n0x1c6e) + I ua
+ 0x0032ba03, // n0x0446 c0x0000 (---------------) + I ubs
+ 0x53205082, // n0x0447 c0x014c (n0x1c6e-n0x1c77) + I ug
+ 0x5360cf02, // n0x0448 c0x014d (n0x1c77-n0x1c82) + I uk
+ 0x0029f04a, // n0x0449 c0x0000 (---------------) + I university
+ 0x00203a83, // n0x044a c0x0000 (---------------) + I uno
+ 0x00245543, // n0x044b c0x0000 (---------------) + I uol
+ 0x002c16c3, // n0x044c c0x0000 (---------------) + I ups
+ 0x54209f42, // n0x044d c0x0150 (n0x1c84-n0x1cc3) + I us
+ 0x62606842, // n0x044e c0x0189 (n0x1d66-n0x1d6c) + I uy
+ 0x62e018c2, // n0x044f c0x018b (n0x1d6d-n0x1d71) + I uz
+ 0x002013c2, // n0x0450 c0x0000 (---------------) + I va
+ 0x00340209, // n0x0451 c0x0000 (---------------) + I vacations
+ 0x002aba84, // n0x0452 c0x0000 (---------------) + I vana
+ 0x6334a542, // n0x0453 c0x018c (n0x1d71-n0x1d77) + I vc
+ 0x636014c2, // n0x0454 c0x018d (n0x1d77-n0x1d88) + I ve
+ 0x0027d4c5, // n0x0455 c0x0000 (---------------) + I vegas
+ 0x00227248, // n0x0456 c0x0000 (---------------) + I ventures
+ 0x002d998c, // n0x0457 c0x0000 (---------------) + I versicherung
+ 0x0022b9c3, // n0x0458 c0x0000 (---------------) + I vet
+ 0x0024ad42, // n0x0459 c0x0000 (---------------) + I vg
+ 0x63a13602, // n0x045a c0x018e (n0x1d88-n0x1d8d) + I vi
+ 0x002b4346, // n0x045b c0x0000 (---------------) + I viajes
+ 0x002db9c5, // n0x045c c0x0000 (---------------) + I video
+ 0x002b1403, // n0x045d c0x0000 (---------------) + I vig
+ 0x002c7686, // n0x045e c0x0000 (---------------) + I viking
+ 0x002dbb06, // n0x045f c0x0000 (---------------) + I villas
+ 0x00213603, // n0x0460 c0x0000 (---------------) + I vin
+ 0x002dc703, // n0x0461 c0x0000 (---------------) + I vip
+ 0x002dd146, // n0x0462 c0x0000 (---------------) + I virgin
+ 0x00248b46, // n0x0463 c0x0000 (---------------) + I vision
+ 0x002b85c5, // n0x0464 c0x0000 (---------------) + I vista
+ 0x002dd6ca, // n0x0465 c0x0000 (---------------) + I vistaprint
+ 0x0022cf04, // n0x0466 c0x0000 (---------------) + I viva
+ 0x00332eca, // n0x0467 c0x0000 (---------------) + I vlaanderen
+ 0x63e08102, // n0x0468 c0x018f (n0x1d8d-n0x1d9a) + I vn
+ 0x002716c5, // n0x0469 c0x0000 (---------------) + I vodka
+ 0x002e0c8a, // n0x046a c0x0000 (---------------) + I volkswagen
+ 0x002e2484, // n0x046b c0x0000 (---------------) + I vote
+ 0x002e2586, // n0x046c c0x0000 (---------------) + I voting
+ 0x002e2704, // n0x046d c0x0000 (---------------) + I voto
+ 0x0030c486, // n0x046e c0x0000 (---------------) + I voyage
+ 0x6421d102, // n0x046f c0x0190 (n0x1d9a-n0x1d9e) + I vu
+ 0x002a4e86, // n0x0470 c0x0000 (---------------) + I vuelos
+ 0x0036cbc5, // n0x0471 c0x0000 (---------------) + I wales
+ 0x0038b0c6, // n0x0472 c0x0000 (---------------) + I walter
+ 0x003578c4, // n0x0473 c0x0000 (---------------) + I wang
+ 0x003578c7, // n0x0474 c0x0000 (---------------) + I wanggou
+ 0x00351a06, // n0x0475 c0x0000 (---------------) + I warman
+ 0x0020acc5, // n0x0476 c0x0000 (---------------) + I watch
+ 0x00293647, // n0x0477 c0x0000 (---------------) + I watches
+ 0x00384107, // n0x0478 c0x0000 (---------------) + I weather
+ 0x0038410e, // n0x0479 c0x0000 (---------------) + I weatherchannel
+ 0x00219fc6, // n0x047a c0x0000 (---------------) + I webcam
+ 0x00251cc5, // n0x047b c0x0000 (---------------) + I weber
+ 0x002af047, // n0x047c c0x0000 (---------------) + I website
+ 0x002d4bc3, // n0x047d c0x0000 (---------------) + I wed
+ 0x0031c007, // n0x047e c0x0000 (---------------) + I wedding
+ 0x003912c5, // n0x047f c0x0000 (---------------) + I weibo
+ 0x0020a184, // n0x0480 c0x0000 (---------------) + I weir
+ 0x0021f7c2, // n0x0481 c0x0000 (---------------) + I wf
+ 0x002c7207, // n0x0482 c0x0000 (---------------) + I whoswho
+ 0x002d2e44, // n0x0483 c0x0000 (---------------) + I wien
+ 0x0025a484, // n0x0484 c0x0000 (---------------) + I wiki
+ 0x0024910b, // n0x0485 c0x0000 (---------------) + I williamhill
+ 0x00213c83, // n0x0486 c0x0000 (---------------) + I win
+ 0x002b6a07, // n0x0487 c0x0000 (---------------) + I windows
+ 0x00213c84, // n0x0488 c0x0000 (---------------) + I wine
+ 0x00231fc3, // n0x0489 c0x0000 (---------------) + I wme
+ 0x00241b04, // n0x048a c0x0000 (---------------) + I work
+ 0x0029b085, // n0x048b c0x0000 (---------------) + I works
+ 0x00314905, // n0x048c c0x0000 (---------------) + I world
+ 0x6460ba82, // n0x048d c0x0191 (n0x1d9e-n0x1da5) + I ws
+ 0x002e34c3, // n0x048e c0x0000 (---------------) + I wtc
+ 0x002e3b03, // n0x048f c0x0000 (---------------) + I wtf
+ 0x0020a6c4, // n0x0490 c0x0000 (---------------) + I xbox
+ 0x0020a785, // n0x0491 c0x0000 (---------------) + I xerox
+ 0x00217cc6, // n0x0492 c0x0000 (---------------) + I xihuan
+ 0x00356e83, // n0x0493 c0x0000 (---------------) + I xin
+ 0x002358cb, // n0x0494 c0x0000 (---------------) + I xn--11b4c3d
+ 0x0023d74b, // n0x0495 c0x0000 (---------------) + I xn--1ck2e1b
+ 0x00291d4b, // n0x0496 c0x0000 (---------------) + I xn--1qqw23a
+ 0x002bfeca, // n0x0497 c0x0000 (---------------) + I xn--30rr7y
+ 0x0033588b, // n0x0498 c0x0000 (---------------) + I xn--3bst00m
+ 0x003942cb, // n0x0499 c0x0000 (---------------) + I xn--3ds443g
+ 0x0039658c, // n0x049a c0x0000 (---------------) + I xn--3e0b707e
+ 0x00397251, // n0x049b c0x0000 (---------------) + I xn--3oq18vl8pn36a
+ 0x002e480a, // n0x049c c0x0000 (---------------) + I xn--3pxu8k
+ 0x002e4bcb, // n0x049d c0x0000 (---------------) + I xn--42c2d9a
+ 0x002e4e8b, // n0x049e c0x0000 (---------------) + I xn--45brj9c
+ 0x002e6d4a, // n0x049f c0x0000 (---------------) + I xn--45q11c
+ 0x002e780a, // n0x04a0 c0x0000 (---------------) + I xn--4gbrim
+ 0x002e8b8e, // n0x04a1 c0x0000 (---------------) + I xn--54b7fta0cc
+ 0x002e9e4b, // n0x04a2 c0x0000 (---------------) + I xn--55qw42g
+ 0x002ea10a, // n0x04a3 c0x0000 (---------------) + I xn--55qx5d
+ 0x002eb14a, // n0x04a4 c0x0000 (---------------) + I xn--5tzm5g
+ 0x002eb64b, // n0x04a5 c0x0000 (---------------) + I xn--6frz82g
+ 0x002ebb8e, // n0x04a6 c0x0000 (---------------) + I xn--6qq986b3xl
+ 0x002ec6cc, // n0x04a7 c0x0000 (---------------) + I xn--80adxhks
+ 0x002ecb4b, // n0x04a8 c0x0000 (---------------) + I xn--80ao21a
+ 0x002ece0c, // n0x04a9 c0x0000 (---------------) + I xn--80asehdb
+ 0x002f108a, // n0x04aa c0x0000 (---------------) + I xn--80aswg
+ 0x002f228c, // n0x04ab c0x0000 (---------------) + I xn--8y0a063a
+ 0x64af258a, // n0x04ac c0x0192 (n0x1da5-n0x1dab) + I xn--90a3ac
+ 0x002f5849, // n0x04ad c0x0000 (---------------) + I xn--90ais
+ 0x002f664a, // n0x04ae c0x0000 (---------------) + I xn--9dbq2a
+ 0x002f68ca, // n0x04af c0x0000 (---------------) + I xn--9et52u
+ 0x002f6b4b, // n0x04b0 c0x0000 (---------------) + I xn--9krt00a
+ 0x002fa44e, // n0x04b1 c0x0000 (---------------) + I xn--b4w605ferd
+ 0x002fa7d1, // n0x04b2 c0x0000 (---------------) + I xn--bck1b9a5dre4c
+ 0x00303289, // n0x04b3 c0x0000 (---------------) + I xn--c1avg
+ 0x003034ca, // n0x04b4 c0x0000 (---------------) + I xn--c2br7g
+ 0x00303e0b, // n0x04b5 c0x0000 (---------------) + I xn--cck2b3b
+ 0x0030618a, // n0x04b6 c0x0000 (---------------) + I xn--cg4bki
+ 0x00306d16, // n0x04b7 c0x0000 (---------------) + I xn--clchc0ea0b2g2a9gcd
+ 0x003091cb, // n0x04b8 c0x0000 (---------------) + I xn--czr694b
+ 0x0030a9ca, // n0x04b9 c0x0000 (---------------) + I xn--czrs0t
+ 0x0030d48a, // n0x04ba c0x0000 (---------------) + I xn--czru2d
+ 0x0030f2cb, // n0x04bb c0x0000 (---------------) + I xn--d1acj3b
+ 0x003125c9, // n0x04bc c0x0000 (---------------) + I xn--d1alf
+ 0x0031508d, // n0x04bd c0x0000 (---------------) + I xn--eckvdtc9d
+ 0x003167cb, // n0x04be c0x0000 (---------------) + I xn--efvy88h
+ 0x003178cb, // n0x04bf c0x0000 (---------------) + I xn--estv75g
+ 0x0031828b, // n0x04c0 c0x0000 (---------------) + I xn--fct429k
+ 0x00319909, // n0x04c1 c0x0000 (---------------) + I xn--fhbei
+ 0x00319f4e, // n0x04c2 c0x0000 (---------------) + I xn--fiq228c5hs
+ 0x0031a48a, // n0x04c3 c0x0000 (---------------) + I xn--fiq64b
+ 0x0031ec0a, // n0x04c4 c0x0000 (---------------) + I xn--fiqs8s
+ 0x0031f0ca, // n0x04c5 c0x0000 (---------------) + I xn--fiqz9s
+ 0x0031fa8b, // n0x04c6 c0x0000 (---------------) + I xn--fjq720a
+ 0x003202cb, // n0x04c7 c0x0000 (---------------) + I xn--flw351e
+ 0x0032058d, // n0x04c8 c0x0000 (---------------) + I xn--fpcrj9c3d
+ 0x00321e0d, // n0x04c9 c0x0000 (---------------) + I xn--fzc2c9e2c
+ 0x00322dd0, // n0x04ca c0x0000 (---------------) + I xn--fzys8d69uvgm
+ 0x0032328b, // n0x04cb c0x0000 (---------------) + I xn--g2xx48c
+ 0x00323ecc, // n0x04cc c0x0000 (---------------) + I xn--gckr3f0f
+ 0x0032434b, // n0x04cd c0x0000 (---------------) + I xn--gecrj9c
+ 0x0032880b, // n0x04ce c0x0000 (---------------) + I xn--h2brj9c
+ 0x0032f14b, // n0x04cf c0x0000 (---------------) + I xn--hxt814e
+ 0x0032fbcf, // n0x04d0 c0x0000 (---------------) + I xn--i1b6b1a6a2e
+ 0x0032ff8b, // n0x04d1 c0x0000 (---------------) + I xn--imr513n
+ 0x0033080a, // n0x04d2 c0x0000 (---------------) + I xn--io0a7i
+ 0x00331249, // n0x04d3 c0x0000 (---------------) + I xn--j1aef
+ 0x003315c9, // n0x04d4 c0x0000 (---------------) + I xn--j1amh
+ 0x0033198b, // n0x04d5 c0x0000 (---------------) + I xn--j6w193g
+ 0x00331c4e, // n0x04d6 c0x0000 (---------------) + I xn--jlq61u9w7b
+ 0x0033354b, // n0x04d7 c0x0000 (---------------) + I xn--jvr189m
+ 0x0033444f, // n0x04d8 c0x0000 (---------------) + I xn--kcrx77d1x4a
+ 0x00337d8b, // n0x04d9 c0x0000 (---------------) + I xn--kprw13d
+ 0x0033804b, // n0x04da c0x0000 (---------------) + I xn--kpry57d
+ 0x0033830b, // n0x04db c0x0000 (---------------) + I xn--kpu716f
+ 0x00338e0a, // n0x04dc c0x0000 (---------------) + I xn--kput3i
+ 0x0033db49, // n0x04dd c0x0000 (---------------) + I xn--l1acc
+ 0x00344dcf, // n0x04de c0x0000 (---------------) + I xn--lgbbat1ad8j
+ 0x0034978c, // n0x04df c0x0000 (---------------) + I xn--mgb2ddes
+ 0x00349c8c, // n0x04e0 c0x0000 (---------------) + I xn--mgb9awbf
+ 0x0034a18e, // n0x04e1 c0x0000 (---------------) + I xn--mgba3a3ejt
+ 0x0034a88f, // n0x04e2 c0x0000 (---------------) + I xn--mgba3a4f16a
+ 0x0034ac4e, // n0x04e3 c0x0000 (---------------) + I xn--mgba3a4fra
+ 0x0034b750, // n0x04e4 c0x0000 (---------------) + I xn--mgba7c0bbn0a
+ 0x0034bb4e, // n0x04e5 c0x0000 (---------------) + I xn--mgbaam7a8h
+ 0x0034c10c, // n0x04e6 c0x0000 (---------------) + I xn--mgbab2bd
+ 0x0034c412, // n0x04e7 c0x0000 (---------------) + I xn--mgbai9a5eva00b
+ 0x0034f091, // n0x04e8 c0x0000 (---------------) + I xn--mgbai9azgqp6j
+ 0x0034f64e, // n0x04e9 c0x0000 (---------------) + I xn--mgbayh7gpa
+ 0x0034fa8e, // n0x04ea c0x0000 (---------------) + I xn--mgbb9fbpob
+ 0x0034ffce, // n0x04eb c0x0000 (---------------) + I xn--mgbbh1a71e
+ 0x0035034f, // n0x04ec c0x0000 (---------------) + I xn--mgbc0a9azcg
+ 0x00350713, // n0x04ed c0x0000 (---------------) + I xn--mgberp4a5d4a87g
+ 0x00350bd1, // n0x04ee c0x0000 (---------------) + I xn--mgberp4a5d4ar
+ 0x0035100c, // n0x04ef c0x0000 (---------------) + I xn--mgbpl2fh
+ 0x00351453, // n0x04f0 c0x0000 (---------------) + I xn--mgbqly7c0a67fbc
+ 0x00352310, // n0x04f1 c0x0000 (---------------) + I xn--mgbqly7cvafr
+ 0x00352e0c, // n0x04f2 c0x0000 (---------------) + I xn--mgbt3dhd
+ 0x0035310c, // n0x04f3 c0x0000 (---------------) + I xn--mgbtf8fl
+ 0x003535cb, // n0x04f4 c0x0000 (---------------) + I xn--mgbtx2b
+ 0x0035588e, // n0x04f5 c0x0000 (---------------) + I xn--mgbx4cd0ab
+ 0x00355e8b, // n0x04f6 c0x0000 (---------------) + I xn--mix082f
+ 0x003564cb, // n0x04f7 c0x0000 (---------------) + I xn--mix891f
+ 0x0035728c, // n0x04f8 c0x0000 (---------------) + I xn--mk1bu44c
+ 0x0035c6ca, // n0x04f9 c0x0000 (---------------) + I xn--mxtq1m
+ 0x0035ca8c, // n0x04fa c0x0000 (---------------) + I xn--ngbc5azd
+ 0x0035cd8c, // n0x04fb c0x0000 (---------------) + I xn--ngbe9e0a
+ 0x0035f64b, // n0x04fc c0x0000 (---------------) + I xn--nnx388a
+ 0x0035f908, // n0x04fd c0x0000 (---------------) + I xn--node
+ 0x00360249, // n0x04fe c0x0000 (---------------) + I xn--nqv7f
+ 0x0036024f, // n0x04ff c0x0000 (---------------) + I xn--nqv7fs00ema
+ 0x00361f8b, // n0x0500 c0x0000 (---------------) + I xn--nyqy26a
+ 0x003633ca, // n0x0501 c0x0000 (---------------) + I xn--o3cw4h
+ 0x00364d8c, // n0x0502 c0x0000 (---------------) + I xn--ogbpf8fl
+ 0x00366089, // n0x0503 c0x0000 (---------------) + I xn--p1acf
+ 0x00366308, // n0x0504 c0x0000 (---------------) + I xn--p1ai
+ 0x00366f8b, // n0x0505 c0x0000 (---------------) + I xn--pbt977c
+ 0x003676cb, // n0x0506 c0x0000 (---------------) + I xn--pgbs0dh
+ 0x00368a8a, // n0x0507 c0x0000 (---------------) + I xn--pssy2u
+ 0x00368d0b, // n0x0508 c0x0000 (---------------) + I xn--q9jyb4c
+ 0x0036944c, // n0x0509 c0x0000 (---------------) + I xn--qcka1pmc
+ 0x0036a988, // n0x050a c0x0000 (---------------) + I xn--qxam
+ 0x0037230b, // n0x050b c0x0000 (---------------) + I xn--rhqv96g
+ 0x00374d8b, // n0x050c c0x0000 (---------------) + I xn--rovu88b
+ 0x0037824b, // n0x050d c0x0000 (---------------) + I xn--s9brj9c
+ 0x00379a8b, // n0x050e c0x0000 (---------------) + I xn--ses554g
+ 0x00383c8b, // n0x050f c0x0000 (---------------) + I xn--t60b56a
+ 0x00383f49, // n0x0510 c0x0000 (---------------) + I xn--tckwe
+ 0x00387fca, // n0x0511 c0x0000 (---------------) + I xn--unup4y
+ 0x00388f17, // n0x0512 c0x0000 (---------------) + I xn--vermgensberater-ctb
+ 0x0038a918, // n0x0513 c0x0000 (---------------) + I xn--vermgensberatung-pwb
+ 0x0038dcc9, // n0x0514 c0x0000 (---------------) + I xn--vhquv
+ 0x0038f00b, // n0x0515 c0x0000 (---------------) + I xn--vuq861b
+ 0x0038fb94, // n0x0516 c0x0000 (---------------) + I xn--w4r85el8fhu5dnra
+ 0x0039034a, // n0x0517 c0x0000 (---------------) + I xn--wgbh1c
+ 0x0039090a, // n0x0518 c0x0000 (---------------) + I xn--wgbl6a
+ 0x00390b8b, // n0x0519 c0x0000 (---------------) + I xn--xhq521b
+ 0x00391a90, // n0x051a c0x0000 (---------------) + I xn--xkc2al3hye2a
+ 0x00391e91, // n0x051b c0x0000 (---------------) + I xn--xkc2dl3a5ee0h
+ 0x0039290a, // n0x051c c0x0000 (---------------) + I xn--y9a3aq
+ 0x003938cd, // n0x051d c0x0000 (---------------) + I xn--yfro4i67o
+ 0x00393fcd, // n0x051e c0x0000 (---------------) + I xn--ygbi2ammx
+ 0x0039688b, // n0x051f c0x0000 (---------------) + I xn--zfr164b
+ 0x00397046, // n0x0520 c0x0000 (---------------) + I xperia
+ 0x003971c3, // n0x0521 c0x0000 (---------------) + I xxx
+ 0x0029ad43, // n0x0522 c0x0000 (---------------) + I xyz
+ 0x00269586, // n0x0523 c0x0000 (---------------) + I yachts
+ 0x0027b905, // n0x0524 c0x0000 (---------------) + I yahoo
+ 0x002151c7, // n0x0525 c0x0000 (---------------) + I yamaxun
+ 0x00326dc6, // n0x0526 c0x0000 (---------------) + I yandex
+ 0x01614d82, // n0x0527 c0x0005 (---------------)* o I ye
+ 0x002e3609, // n0x0528 c0x0000 (---------------) + I yodobashi
+ 0x00301804, // n0x0529 c0x0000 (---------------) + I yoga
+ 0x0032b5c8, // n0x052a c0x0000 (---------------) + I yokohama
+ 0x00235bc3, // n0x052b c0x0000 (---------------) + I you
+ 0x0035a047, // n0x052c c0x0000 (---------------) + I youtube
+ 0x0022f542, // n0x052d c0x0000 (---------------) + I yt
+ 0x00201943, // n0x052e c0x0000 (---------------) + I yun
+ 0x64e043c2, // n0x052f c0x0193 (n0x1dab-n0x1dbc) o I za
+ 0x002b3106, // n0x0530 c0x0000 (---------------) + I zappos
+ 0x002b3c84, // n0x0531 c0x0000 (---------------) + I zara
+ 0x00311384, // n0x0532 c0x0000 (---------------) + I zero
+ 0x0023b443, // n0x0533 c0x0000 (---------------) + I zip
+ 0x0023b445, // n0x0534 c0x0000 (---------------) + I zippo
+ 0x016e4582, // n0x0535 c0x0005 (---------------)* o I zm
+ 0x002c7ec4, // n0x0536 c0x0000 (---------------) + I zone
+ 0x00261407, // n0x0537 c0x0000 (---------------) + I zuerich
+ 0x016a0202, // n0x0538 c0x0005 (---------------)* o I zw
+ 0x00222ac3, // n0x0539 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x053a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x053b c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x053c c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x053d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x053e c0x0000 (---------------) + I org
+ 0x00207cc3, // n0x053f c0x0000 (---------------) + I nom
+ 0x00201e82, // n0x0540 c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x0541 c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x0542 c0x0000 (---------------) + I co
+ 0x0021e283, // n0x0543 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x0544 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0545 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0546 c0x0000 (---------------) + I org
+ 0x00206103, // n0x0547 c0x0000 (---------------) + I sch
+ 0x002ffad6, // n0x0548 c0x0000 (---------------) + I accident-investigation
+ 0x00301f93, // n0x0549 c0x0000 (---------------) + I accident-prevention
+ 0x00340589, // n0x054a c0x0000 (---------------) + I aerobatic
+ 0x002751c8, // n0x054b c0x0000 (---------------) + I aeroclub
+ 0x0036bb09, // n0x054c c0x0000 (---------------) + I aerodrome
+ 0x002e0e06, // n0x054d c0x0000 (---------------) + I agents
+ 0x0032cf10, // n0x054e c0x0000 (---------------) + I air-surveillance
+ 0x00211893, // n0x054f c0x0000 (---------------) + I air-traffic-control
+ 0x002fb3c8, // n0x0550 c0x0000 (---------------) + I aircraft
+ 0x00262307, // n0x0551 c0x0000 (---------------) + I airline
+ 0x00266e47, // n0x0552 c0x0000 (---------------) + I airport
+ 0x0028bd4a, // n0x0553 c0x0000 (---------------) + I airtraffic
+ 0x003541c9, // n0x0554 c0x0000 (---------------) + I ambulance
+ 0x00309f89, // n0x0555 c0x0000 (---------------) + I amusement
+ 0x002bbacb, // n0x0556 c0x0000 (---------------) + I association
+ 0x002f8406, // n0x0557 c0x0000 (---------------) + I author
+ 0x002ed6ca, // n0x0558 c0x0000 (---------------) + I ballooning
+ 0x00218046, // n0x0559 c0x0000 (---------------) + I broker
+ 0x00301403, // n0x055a c0x0000 (---------------) + I caa
+ 0x002dc1c5, // n0x055b c0x0000 (---------------) + I cargo
+ 0x003246c8, // n0x055c c0x0000 (---------------) + I catering
+ 0x00240acd, // n0x055d c0x0000 (---------------) + I certification
+ 0x0032cacc, // n0x055e c0x0000 (---------------) + I championship
+ 0x0036c887, // n0x055f c0x0000 (---------------) + I charter
+ 0x00328a8d, // n0x0560 c0x0000 (---------------) + I civilaviation
+ 0x002752c4, // n0x0561 c0x0000 (---------------) + I club
+ 0x0022498a, // n0x0562 c0x0000 (---------------) + I conference
+ 0x002255ca, // n0x0563 c0x0000 (---------------) + I consultant
+ 0x00225a8a, // n0x0564 c0x0000 (---------------) + I consulting
+ 0x00211b87, // n0x0565 c0x0000 (---------------) + I control
+ 0x0022cd47, // n0x0566 c0x0000 (---------------) + I council
+ 0x00231004, // n0x0567 c0x0000 (---------------) + I crew
+ 0x00232f46, // n0x0568 c0x0000 (---------------) + I design
+ 0x002727c4, // n0x0569 c0x0000 (---------------) + I dgca
+ 0x00336208, // n0x056a c0x0000 (---------------) + I educator
+ 0x00343bc9, // n0x056b c0x0000 (---------------) + I emergency
+ 0x002a8a86, // n0x056c c0x0000 (---------------) + I engine
+ 0x002a8a88, // n0x056d c0x0000 (---------------) + I engineer
+ 0x00233a4d, // n0x056e c0x0000 (---------------) + I entertainment
+ 0x0021f909, // n0x056f c0x0000 (---------------) + I equipment
+ 0x002f3908, // n0x0570 c0x0000 (---------------) + I exchange
+ 0x0029ab47, // n0x0571 c0x0000 (---------------) + I express
+ 0x0026ac0a, // n0x0572 c0x0000 (---------------) + I federation
+ 0x0023b146, // n0x0573 c0x0000 (---------------) + I flight
+ 0x00248ec7, // n0x0574 c0x0000 (---------------) + I freight
+ 0x0024d004, // n0x0575 c0x0000 (---------------) + I fuel
+ 0x00256007, // n0x0576 c0x0000 (---------------) + I gliding
+ 0x00259a0a, // n0x0577 c0x0000 (---------------) + I government
+ 0x0037868e, // n0x0578 c0x0000 (---------------) + I groundhandling
+ 0x002646c5, // n0x0579 c0x0000 (---------------) + I group
+ 0x0038718b, // n0x057a c0x0000 (---------------) + I hanggliding
+ 0x00278ac9, // n0x057b c0x0000 (---------------) + I homebuilt
+ 0x002806c9, // n0x057c c0x0000 (---------------) + I insurance
+ 0x00202987, // n0x057d c0x0000 (---------------) + I journal
+ 0x0020298a, // n0x057e c0x0000 (---------------) + I journalist
+ 0x00274187, // n0x057f c0x0000 (---------------) + I leasing
+ 0x002127c9, // n0x0580 c0x0000 (---------------) + I logistics
+ 0x00395948, // n0x0581 c0x0000 (---------------) + I magazine
+ 0x002a0c8b, // n0x0582 c0x0000 (---------------) + I maintenance
+ 0x003117cb, // n0x0583 c0x0000 (---------------) + I marketplace
+ 0x002dc385, // n0x0584 c0x0000 (---------------) + I media
+ 0x00236c8a, // n0x0585 c0x0000 (---------------) + I microlight
+ 0x002371c9, // n0x0586 c0x0000 (---------------) + I modelling
+ 0x00354cca, // n0x0587 c0x0000 (---------------) + I navigation
+ 0x0022948b, // n0x0588 c0x0000 (---------------) + I parachuting
+ 0x00255f0b, // n0x0589 c0x0000 (---------------) + I paragliding
+ 0x002bb855, // n0x058a c0x0000 (---------------) + I passenger-association
+ 0x002c1505, // n0x058b c0x0000 (---------------) + I pilot
+ 0x0029abc5, // n0x058c c0x0000 (---------------) + I press
+ 0x002cbe4a, // n0x058d c0x0000 (---------------) + I production
+ 0x002e634a, // n0x058e c0x0000 (---------------) + I recreation
+ 0x002e0107, // n0x058f c0x0000 (---------------) + I repbody
+ 0x00215503, // n0x0590 c0x0000 (---------------) + I res
+ 0x00296808, // n0x0591 c0x0000 (---------------) + I research
+ 0x002bc30a, // n0x0592 c0x0000 (---------------) + I rotorcraft
+ 0x00234786, // n0x0593 c0x0000 (---------------) + I safety
+ 0x0023f089, // n0x0594 c0x0000 (---------------) + I scientist
+ 0x00243bc8, // n0x0595 c0x0000 (---------------) + I services
+ 0x002b3884, // n0x0596 c0x0000 (---------------) + I show
+ 0x00253c09, // n0x0597 c0x0000 (---------------) + I skydiving
+ 0x002a6c88, // n0x0598 c0x0000 (---------------) + I software
+ 0x00298587, // n0x0599 c0x0000 (---------------) + I student
+ 0x00217c44, // n0x059a c0x0000 (---------------) + I taxi
+ 0x00229a46, // n0x059b c0x0000 (---------------) + I trader
+ 0x0028fe07, // n0x059c c0x0000 (---------------) + I trading
+ 0x0028aec7, // n0x059d c0x0000 (---------------) + I trainer
+ 0x00230a45, // n0x059e c0x0000 (---------------) + I union
+ 0x002e04cc, // n0x059f c0x0000 (---------------) + I workinggroup
+ 0x0029b085, // n0x05a0 c0x0000 (---------------) + I works
+ 0x00222ac3, // n0x05a1 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x05a2 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x05a3 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x05a4 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x05a5 c0x0000 (---------------) + I org
+ 0x00200742, // n0x05a6 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x05a7 c0x0000 (---------------) + I com
+ 0x002170c3, // n0x05a8 c0x0000 (---------------) + I net
+ 0x00207cc3, // n0x05a9 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x05aa c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x05ab c0x0000 (---------------) + I com
+ 0x002170c3, // n0x05ac c0x0000 (---------------) + I net
+ 0x00219c43, // n0x05ad c0x0000 (---------------) + I off
+ 0x0021dcc3, // n0x05ae c0x0000 (---------------) + I org
+ 0x000e4188, // n0x05af c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x05b0 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x05b1 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x05b2 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x05b3 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x05b4 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x05b5 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x05b6 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x05b7 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x05b8 c0x0000 (---------------) + I edu
+ 0x002170c3, // n0x05b9 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x05ba c0x0000 (---------------) + I org
+ 0x00200742, // n0x05bb c0x0000 (---------------) + I co
+ 0x00203fc2, // n0x05bc c0x0000 (---------------) + I ed
+ 0x00225cc2, // n0x05bd c0x0000 (---------------) + I gv
+ 0x00206e82, // n0x05be c0x0000 (---------------) + I it
+ 0x002003c2, // n0x05bf c0x0000 (---------------) + I og
+ 0x00268e82, // n0x05c0 c0x0000 (---------------) + I pb
+ 0x04a22ac3, // n0x05c1 c0x0012 (n0x05ca-n0x05cb) + I com
+ 0x002d75c3, // n0x05c2 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x05c3 c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x05c4 c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x05c5 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x05c6 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x05c7 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x05c8 c0x0000 (---------------) + I org
+ 0x00227303, // n0x05c9 c0x0000 (---------------) + I tur
+ 0x000e4188, // n0x05ca c0x0000 (---------------) + blogspot
+ 0x002f5744, // n0x05cb c0x0000 (---------------) + I e164
+ 0x0034d5c7, // n0x05cc c0x0000 (---------------) + I in-addr
+ 0x00213a43, // n0x05cd c0x0000 (---------------) + I ip6
+ 0x00234684, // n0x05ce c0x0000 (---------------) + I iris
+ 0x00202803, // n0x05cf c0x0000 (---------------) + I uri
+ 0x00202a03, // n0x05d0 c0x0000 (---------------) + I urn
+ 0x0021e283, // n0x05d1 c0x0000 (---------------) + I gov
+ 0x00201e82, // n0x05d2 c0x0000 (---------------) + I ac
+ 0x00110603, // n0x05d3 c0x0000 (---------------) + biz
+ 0x05a00742, // n0x05d4 c0x0016 (n0x05d9-n0x05da) + I co
+ 0x00225cc2, // n0x05d5 c0x0000 (---------------) + I gv
+ 0x00000304, // n0x05d6 c0x0000 (---------------) + info
+ 0x00200c42, // n0x05d7 c0x0000 (---------------) + I or
+ 0x000cba44, // n0x05d8 c0x0000 (---------------) + priv
+ 0x000e4188, // n0x05d9 c0x0000 (---------------) + blogspot
+ 0x00226043, // n0x05da c0x0000 (---------------) + I act
+ 0x002a00c3, // n0x05db c0x0000 (---------------) + I asn
+ 0x06222ac3, // n0x05dc c0x0018 (n0x05ec-n0x05ed) + I com
+ 0x00224984, // n0x05dd c0x0000 (---------------) + I conf
+ 0x066d75c3, // n0x05de c0x0019 (n0x05ed-n0x05f5) + I edu
+ 0x06a1e283, // n0x05df c0x001a (n0x05f5-n0x05fa) + I gov
+ 0x00206202, // n0x05e0 c0x0000 (---------------) + I id
+ 0x00200304, // n0x05e1 c0x0000 (---------------) + I info
+ 0x002170c3, // n0x05e2 c0x0000 (---------------) + I net
+ 0x0020ac43, // n0x05e3 c0x0000 (---------------) + I nsw
+ 0x00200e02, // n0x05e4 c0x0000 (---------------) + I nt
+ 0x0021dcc3, // n0x05e5 c0x0000 (---------------) + I org
+ 0x00212bc2, // n0x05e6 c0x0000 (---------------) + I oz
+ 0x002ce743, // n0x05e7 c0x0000 (---------------) + I qld
+ 0x00201a02, // n0x05e8 c0x0000 (---------------) + I sa
+ 0x00200143, // n0x05e9 c0x0000 (---------------) + I tas
+ 0x00243c83, // n0x05ea c0x0000 (---------------) + I vic
+ 0x00202542, // n0x05eb c0x0000 (---------------) + I wa
+ 0x000e4188, // n0x05ec c0x0000 (---------------) + blogspot
+ 0x00226043, // n0x05ed c0x0000 (---------------) + I act
+ 0x0020ac43, // n0x05ee c0x0000 (---------------) + I nsw
+ 0x00200e02, // n0x05ef c0x0000 (---------------) + I nt
+ 0x002ce743, // n0x05f0 c0x0000 (---------------) + I qld
+ 0x00201a02, // n0x05f1 c0x0000 (---------------) + I sa
+ 0x00200143, // n0x05f2 c0x0000 (---------------) + I tas
+ 0x00243c83, // n0x05f3 c0x0000 (---------------) + I vic
+ 0x00202542, // n0x05f4 c0x0000 (---------------) + I wa
+ 0x002ce743, // n0x05f5 c0x0000 (---------------) + I qld
+ 0x00201a02, // n0x05f6 c0x0000 (---------------) + I sa
+ 0x00200143, // n0x05f7 c0x0000 (---------------) + I tas
+ 0x00243c83, // n0x05f8 c0x0000 (---------------) + I vic
+ 0x00202542, // n0x05f9 c0x0000 (---------------) + I wa
+ 0x00222ac3, // n0x05fa c0x0000 (---------------) + I com
+ 0x00310603, // n0x05fb c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x05fc c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x05fd c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x05fe c0x0000 (---------------) + I gov
+ 0x00200304, // n0x05ff c0x0000 (---------------) + I info
+ 0x00238c03, // n0x0600 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x0601 c0x0000 (---------------) + I mil
+ 0x00298944, // n0x0602 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x0603 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0604 c0x0000 (---------------) + I org
+ 0x00207742, // n0x0605 c0x0000 (---------------) + I pp
+ 0x00218243, // n0x0606 c0x0000 (---------------) + I pro
+ 0x000e4188, // n0x0607 c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x0608 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x0609 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x060a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x060b c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x060c c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x060d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x060e c0x0000 (---------------) + I org
+ 0x002060c2, // n0x060f c0x0000 (---------------) + I rs
+ 0x00262644, // n0x0610 c0x0000 (---------------) + I unbi
+ 0x00201984, // n0x0611 c0x0000 (---------------) + I unsa
+ 0x00310603, // n0x0612 c0x0000 (---------------) + I biz
+ 0x00200742, // n0x0613 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x0614 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0615 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x0616 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x0617 c0x0000 (---------------) + I info
+ 0x002170c3, // n0x0618 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0619 c0x0000 (---------------) + I org
+ 0x002cf4c5, // n0x061a c0x0000 (---------------) + I store
+ 0x0020bf42, // n0x061b c0x0000 (---------------) + I tv
+ 0x00201e82, // n0x061c c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x061d c0x0000 (---------------) + blogspot
+ 0x0021e283, // n0x061e c0x0000 (---------------) + I gov
+ 0x00225381, // n0x061f c0x0000 (---------------) + I 0
+ 0x00223681, // n0x0620 c0x0000 (---------------) + I 1
+ 0x0023d901, // n0x0621 c0x0000 (---------------) + I 2
+ 0x00235b01, // n0x0622 c0x0000 (---------------) + I 3
+ 0x00235a81, // n0x0623 c0x0000 (---------------) + I 4
+ 0x002b1e01, // n0x0624 c0x0000 (---------------) + I 5
+ 0x00213ac1, // n0x0625 c0x0000 (---------------) + I 6
+ 0x00225481, // n0x0626 c0x0000 (---------------) + I 7
+ 0x002e4a01, // n0x0627 c0x0000 (---------------) + I 8
+ 0x002e4e01, // n0x0628 c0x0000 (---------------) + I 9
+ 0x00200181, // n0x0629 c0x0000 (---------------) + I a
+ 0x00200001, // n0x062a c0x0000 (---------------) + I b
+ 0x000e4188, // n0x062b c0x0000 (---------------) + blogspot
+ 0x00200741, // n0x062c c0x0000 (---------------) + I c
+ 0x002005c1, // n0x062d c0x0000 (---------------) + I d
+ 0x00200701, // n0x062e c0x0000 (---------------) + I e
+ 0x00200381, // n0x062f c0x0000 (---------------) + I f
+ 0x00200401, // n0x0630 c0x0000 (---------------) + I g
+ 0x00200201, // n0x0631 c0x0000 (---------------) + I h
+ 0x00200041, // n0x0632 c0x0000 (---------------) + I i
+ 0x00201f81, // n0x0633 c0x0000 (---------------) + I j
+ 0x00201001, // n0x0634 c0x0000 (---------------) + I k
+ 0x002008c1, // n0x0635 c0x0000 (---------------) + I l
+ 0x002000c1, // n0x0636 c0x0000 (---------------) + I m
+ 0x00200281, // n0x0637 c0x0000 (---------------) + I n
+ 0x00200081, // n0x0638 c0x0000 (---------------) + I o
+ 0x00200b01, // n0x0639 c0x0000 (---------------) + I p
+ 0x00211181, // n0x063a c0x0000 (---------------) + I q
+ 0x00200581, // n0x063b c0x0000 (---------------) + I r
+ 0x002001c1, // n0x063c c0x0000 (---------------) + I s
+ 0x00200141, // n0x063d c0x0000 (---------------) + I t
+ 0x00200101, // n0x063e c0x0000 (---------------) + I u
+ 0x002013c1, // n0x063f c0x0000 (---------------) + I v
+ 0x00202541, // n0x0640 c0x0000 (---------------) + I w
+ 0x00203ec1, // n0x0641 c0x0000 (---------------) + I x
+ 0x00200801, // n0x0642 c0x0000 (---------------) + I y
+ 0x00201901, // n0x0643 c0x0000 (---------------) + I z
+ 0x00222ac3, // n0x0644 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0645 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x0646 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x0647 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0648 c0x0000 (---------------) + I org
+ 0x00200742, // n0x0649 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x064a c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x064b c0x0000 (---------------) + I edu
+ 0x00200c42, // n0x064c c0x0000 (---------------) + I or
+ 0x0021dcc3, // n0x064d c0x0000 (---------------) + I org
+ 0x00009ac6, // n0x064e c0x0000 (---------------) + dyndns
+ 0x00041d4a, // n0x064f c0x0000 (---------------) + for-better
+ 0x00076a48, // n0x0650 c0x0000 (---------------) + for-more
+ 0x00042348, // n0x0651 c0x0000 (---------------) + for-some
+ 0x00042c87, // n0x0652 c0x0000 (---------------) + for-the
+ 0x00130f46, // n0x0653 c0x0000 (---------------) + selfip
+ 0x00110e86, // n0x0654 c0x0000 (---------------) + webhop
+ 0x002729c4, // n0x0655 c0x0000 (---------------) + I asso
+ 0x002f4347, // n0x0656 c0x0000 (---------------) + I barreau
+ 0x000e4188, // n0x0657 c0x0000 (---------------) + blogspot
+ 0x003579c4, // n0x0658 c0x0000 (---------------) + I gouv
+ 0x00222ac3, // n0x0659 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x065a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x065b c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x065c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x065d c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x065e c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x065f c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x0660 c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x0661 c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x0662 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x0663 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0664 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0665 c0x0000 (---------------) + I org
+ 0x0020bf42, // n0x0666 c0x0000 (---------------) + I tv
+ 0x002b2643, // n0x0667 c0x0000 (---------------) + I adm
+ 0x002db303, // n0x0668 c0x0000 (---------------) + I adv
+ 0x00256e43, // n0x0669 c0x0000 (---------------) + I agr
+ 0x00204942, // n0x066a c0x0000 (---------------) + I am
+ 0x00323d83, // n0x066b c0x0000 (---------------) + I arq
+ 0x00200603, // n0x066c c0x0000 (---------------) + I art
+ 0x00201643, // n0x066d c0x0000 (---------------) + I ato
+ 0x00200001, // n0x066e c0x0000 (---------------) + I b
+ 0x00200003, // n0x066f c0x0000 (---------------) + I bio
+ 0x002d0084, // n0x0670 c0x0000 (---------------) + I blog
+ 0x0031b803, // n0x0671 c0x0000 (---------------) + I bmd
+ 0x002f4243, // n0x0672 c0x0000 (---------------) + I cim
+ 0x002dbfc3, // n0x0673 c0x0000 (---------------) + I cng
+ 0x002211c3, // n0x0674 c0x0000 (---------------) + I cnt
+ 0x0a622ac3, // n0x0675 c0x0029 (n0x06ad-n0x06ae) + I com
+ 0x00228d44, // n0x0676 c0x0000 (---------------) + I coop
+ 0x00305ec3, // n0x0677 c0x0000 (---------------) + I ecn
+ 0x00200703, // n0x0678 c0x0000 (---------------) + I eco
+ 0x002d75c3, // n0x0679 c0x0000 (---------------) + I edu
+ 0x00226343, // n0x067a c0x0000 (---------------) + I emp
+ 0x002674c3, // n0x067b c0x0000 (---------------) + I eng
+ 0x0028cac3, // n0x067c c0x0000 (---------------) + I esp
+ 0x002f41c3, // n0x067d c0x0000 (---------------) + I etc
+ 0x0021bac3, // n0x067e c0x0000 (---------------) + I eti
+ 0x0021f803, // n0x067f c0x0000 (---------------) + I far
+ 0x0023bdc4, // n0x0680 c0x0000 (---------------) + I flog
+ 0x00358002, // n0x0681 c0x0000 (---------------) + I fm
+ 0x002416c3, // n0x0682 c0x0000 (---------------) + I fnd
+ 0x00247a83, // n0x0683 c0x0000 (---------------) + I fot
+ 0x002645c3, // n0x0684 c0x0000 (---------------) + I fst
+ 0x0036f1c3, // n0x0685 c0x0000 (---------------) + I g12
+ 0x00311b83, // n0x0686 c0x0000 (---------------) + I ggf
+ 0x0021e283, // n0x0687 c0x0000 (---------------) + I gov
+ 0x002b7c43, // n0x0688 c0x0000 (---------------) + I imb
+ 0x00215703, // n0x0689 c0x0000 (---------------) + I ind
+ 0x00200303, // n0x068a c0x0000 (---------------) + I inf
+ 0x00202c03, // n0x068b c0x0000 (---------------) + I jor
+ 0x002de8c3, // n0x068c c0x0000 (---------------) + I jus
+ 0x0021e843, // n0x068d c0x0000 (---------------) + I leg
+ 0x002e3403, // n0x068e c0x0000 (---------------) + I lel
+ 0x0020f583, // n0x068f c0x0000 (---------------) + I mat
+ 0x0020b403, // n0x0690 c0x0000 (---------------) + I med
+ 0x0023fa03, // n0x0691 c0x0000 (---------------) + I mil
+ 0x00214902, // n0x0692 c0x0000 (---------------) + I mp
+ 0x002bc903, // n0x0693 c0x0000 (---------------) + I mus
+ 0x002170c3, // n0x0694 c0x0000 (---------------) + I net
+ 0x01607cc3, // n0x0695 c0x0005 (---------------)* o I nom
+ 0x0023fdc3, // n0x0696 c0x0000 (---------------) + I not
+ 0x00211c03, // n0x0697 c0x0000 (---------------) + I ntr
+ 0x0024f003, // n0x0698 c0x0000 (---------------) + I odo
+ 0x0021dcc3, // n0x0699 c0x0000 (---------------) + I org
+ 0x002bf143, // n0x069a c0x0000 (---------------) + I ppg
+ 0x00218243, // n0x069b c0x0000 (---------------) + I pro
+ 0x002353c3, // n0x069c c0x0000 (---------------) + I psc
+ 0x002dc783, // n0x069d c0x0000 (---------------) + I psi
+ 0x002ce903, // n0x069e c0x0000 (---------------) + I qsl
+ 0x00250685, // n0x069f c0x0000 (---------------) + I radio
+ 0x002e6343, // n0x06a0 c0x0000 (---------------) + I rec
+ 0x002ce943, // n0x06a1 c0x0000 (---------------) + I slg
+ 0x002cef83, // n0x06a2 c0x0000 (---------------) + I srv
+ 0x00217c44, // n0x06a3 c0x0000 (---------------) + I taxi
+ 0x00362ec3, // n0x06a4 c0x0000 (---------------) + I teo
+ 0x002fb583, // n0x06a5 c0x0000 (---------------) + I tmp
+ 0x00283003, // n0x06a6 c0x0000 (---------------) + I trd
+ 0x00227303, // n0x06a7 c0x0000 (---------------) + I tur
+ 0x0020bf42, // n0x06a8 c0x0000 (---------------) + I tv
+ 0x0022b9c3, // n0x06a9 c0x0000 (---------------) + I vet
+ 0x002df504, // n0x06aa c0x0000 (---------------) + I vlog
+ 0x0025a484, // n0x06ab c0x0000 (---------------) + I wiki
+ 0x0024dc43, // n0x06ac c0x0000 (---------------) + I zlg
+ 0x000e4188, // n0x06ad c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x06ae c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x06af c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x06b0 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x06b1 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x06b2 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x06b3 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x06b4 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x06b5 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x06b6 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x06b7 c0x0000 (---------------) + I org
+ 0x00200742, // n0x06b8 c0x0000 (---------------) + I co
+ 0x0021dcc3, // n0x06b9 c0x0000 (---------------) + I org
+ 0x0ba22ac3, // n0x06ba c0x002e (n0x06be-n0x06bf) + I com
+ 0x0021e283, // n0x06bb c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x06bc c0x0000 (---------------) + I mil
+ 0x00209982, // n0x06bd c0x0000 (---------------) + I of
+ 0x000e4188, // n0x06be c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x06bf c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x06c0 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x06c1 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x06c2 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x06c3 c0x0000 (---------------) + I org
+ 0x000043c2, // n0x06c4 c0x0000 (---------------) + za
+ 0x002004c2, // n0x06c5 c0x0000 (---------------) + I ab
+ 0x0021a042, // n0x06c6 c0x0000 (---------------) + I bc
+ 0x000e4188, // n0x06c7 c0x0000 (---------------) + blogspot
+ 0x00000742, // n0x06c8 c0x0000 (---------------) + co
+ 0x00227d42, // n0x06c9 c0x0000 (---------------) + I gc
+ 0x00205942, // n0x06ca c0x0000 (---------------) + I mb
+ 0x00210d42, // n0x06cb c0x0000 (---------------) + I nb
+ 0x00200342, // n0x06cc c0x0000 (---------------) + I nf
+ 0x00236482, // n0x06cd c0x0000 (---------------) + I nl
+ 0x002019c2, // n0x06ce c0x0000 (---------------) + I ns
+ 0x00200e02, // n0x06cf c0x0000 (---------------) + I nt
+ 0x00205bc2, // n0x06d0 c0x0000 (---------------) + I nu
+ 0x00200dc2, // n0x06d1 c0x0000 (---------------) + I on
+ 0x00214942, // n0x06d2 c0x0000 (---------------) + I pe
+ 0x00369542, // n0x06d3 c0x0000 (---------------) + I qc
+ 0x00207b42, // n0x06d4 c0x0000 (---------------) + I sk
+ 0x002202c2, // n0x06d5 c0x0000 (---------------) + I yk
+ 0x00085109, // n0x06d6 c0x0000 (---------------) + ftpaccess
+ 0x0001208b, // n0x06d7 c0x0000 (---------------) + game-server
+ 0x000bea08, // n0x06d8 c0x0000 (---------------) + myphotos
+ 0x00043609, // n0x06d9 c0x0000 (---------------) + scrapping
+ 0x0021e283, // n0x06da c0x0000 (---------------) + I gov
+ 0x000e4188, // n0x06db c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x06dc c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x06dd c0x0000 (---------------) + I ac
+ 0x002729c4, // n0x06de c0x0000 (---------------) + I asso
+ 0x00200742, // n0x06df c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x06e0 c0x0000 (---------------) + I com
+ 0x00203fc2, // n0x06e1 c0x0000 (---------------) + I ed
+ 0x002d75c3, // n0x06e2 c0x0000 (---------------) + I edu
+ 0x00202342, // n0x06e3 c0x0000 (---------------) + I go
+ 0x003579c4, // n0x06e4 c0x0000 (---------------) + I gouv
+ 0x00238c03, // n0x06e5 c0x0000 (---------------) + I int
+ 0x00238602, // n0x06e6 c0x0000 (---------------) + I md
+ 0x002170c3, // n0x06e7 c0x0000 (---------------) + I net
+ 0x00200c42, // n0x06e8 c0x0000 (---------------) + I or
+ 0x0021dcc3, // n0x06e9 c0x0000 (---------------) + I org
+ 0x0029abc6, // n0x06ea c0x0000 (---------------) + I presse
+ 0x002f710f, // n0x06eb c0x0000 (---------------) + I xn--aroport-bya
+ 0x006e3e83, // n0x06ec c0x0001 (---------------) ! I www
+ 0x000e4188, // n0x06ed c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x06ee c0x0000 (---------------) + I co
+ 0x0034eb03, // n0x06ef c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x06f0 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x06f1 c0x0000 (---------------) + I mil
+ 0x00200742, // n0x06f2 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x06f3 c0x0000 (---------------) + I com
+ 0x0021e283, // n0x06f4 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x06f5 c0x0000 (---------------) + I net
+ 0x00201e82, // n0x06f6 c0x0000 (---------------) + I ac
+ 0x002076c2, // n0x06f7 c0x0000 (---------------) + I ah
+ 0x0ea72409, // n0x06f8 c0x003a (n0x0723-n0x0724) o I amazonaws
+ 0x00202642, // n0x06f9 c0x0000 (---------------) + I bj
+ 0x0f222ac3, // n0x06fa c0x003c (n0x0725-n0x0726) + I com
+ 0x0022e082, // n0x06fb c0x0000 (---------------) + I cq
+ 0x002d75c3, // n0x06fc c0x0000 (---------------) + I edu
+ 0x002241c2, // n0x06fd c0x0000 (---------------) + I fj
+ 0x0021b342, // n0x06fe c0x0000 (---------------) + I gd
+ 0x0021e283, // n0x06ff c0x0000 (---------------) + I gov
+ 0x0026cd02, // n0x0700 c0x0000 (---------------) + I gs
+ 0x0023d702, // n0x0701 c0x0000 (---------------) + I gx
+ 0x00243802, // n0x0702 c0x0000 (---------------) + I gz
+ 0x00202dc2, // n0x0703 c0x0000 (---------------) + I ha
+ 0x002f6242, // n0x0704 c0x0000 (---------------) + I hb
+ 0x00205202, // n0x0705 c0x0000 (---------------) + I he
+ 0x00200202, // n0x0706 c0x0000 (---------------) + I hi
+ 0x0022ea02, // n0x0707 c0x0000 (---------------) + I hk
+ 0x0020cc02, // n0x0708 c0x0000 (---------------) + I hl
+ 0x00217542, // n0x0709 c0x0000 (---------------) + I hn
+ 0x00297c82, // n0x070a c0x0000 (---------------) + I jl
+ 0x002bdf02, // n0x070b c0x0000 (---------------) + I js
+ 0x002fc642, // n0x070c c0x0000 (---------------) + I jx
+ 0x0021f442, // n0x070d c0x0000 (---------------) + I ln
+ 0x0023fa03, // n0x070e c0x0000 (---------------) + I mil
+ 0x00203602, // n0x070f c0x0000 (---------------) + I mo
+ 0x002170c3, // n0x0710 c0x0000 (---------------) + I net
+ 0x00233c42, // n0x0711 c0x0000 (---------------) + I nm
+ 0x0026d802, // n0x0712 c0x0000 (---------------) + I nx
+ 0x0021dcc3, // n0x0713 c0x0000 (---------------) + I org
+ 0x0022e0c2, // n0x0714 c0x0000 (---------------) + I qh
+ 0x00200982, // n0x0715 c0x0000 (---------------) + I sc
+ 0x0024f842, // n0x0716 c0x0000 (---------------) + I sd
+ 0x002001c2, // n0x0717 c0x0000 (---------------) + I sh
+ 0x00210b02, // n0x0718 c0x0000 (---------------) + I sn
+ 0x002d6b42, // n0x0719 c0x0000 (---------------) + I sx
+ 0x00202bc2, // n0x071a c0x0000 (---------------) + I tj
+ 0x00241ac2, // n0x071b c0x0000 (---------------) + I tw
+ 0x0020a882, // n0x071c c0x0000 (---------------) + I xj
+ 0x002ea10a, // n0x071d c0x0000 (---------------) + I xn--55qx5d
+ 0x0033080a, // n0x071e c0x0000 (---------------) + I xn--io0a7i
+ 0x0036394a, // n0x071f c0x0000 (---------------) + I xn--od0alg
+ 0x00397682, // n0x0720 c0x0000 (---------------) + I xz
+ 0x00200802, // n0x0721 c0x0000 (---------------) + I yn
+ 0x00243842, // n0x0722 c0x0000 (---------------) + I zj
+ 0x0ec23487, // n0x0723 c0x003b (n0x0724-n0x0725) + compute
+ 0x00105f0a, // n0x0724 c0x0000 (---------------) + cn-north-1
+ 0x0f672409, // n0x0725 c0x003d (n0x0726-n0x0727) o I amazonaws
+ 0x0fb05f0a, // n0x0726 c0x003e (n0x0727-n0x0728) o I cn-north-1
+ 0x000413c2, // n0x0727 c0x0000 (---------------) + s3
+ 0x00246584, // n0x0728 c0x0000 (---------------) + I arts
+ 0x10222ac3, // n0x0729 c0x0040 (n0x0735-n0x0736) + I com
+ 0x002d75c3, // n0x072a c0x0000 (---------------) + I edu
+ 0x00238544, // n0x072b c0x0000 (---------------) + I firm
+ 0x0021e283, // n0x072c c0x0000 (---------------) + I gov
+ 0x00200304, // n0x072d c0x0000 (---------------) + I info
+ 0x00238c03, // n0x072e c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x072f c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0730 c0x0000 (---------------) + I net
+ 0x00207cc3, // n0x0731 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x0732 c0x0000 (---------------) + I org
+ 0x002e6343, // n0x0733 c0x0000 (---------------) + I rec
+ 0x00219fc3, // n0x0734 c0x0000 (---------------) + I web
+ 0x000e4188, // n0x0735 c0x0000 (---------------) + blogspot
+ 0x000f00c5, // n0x0736 c0x0000 (---------------) + 1kapp
+ 0x000f2202, // n0x0737 c0x0000 (---------------) + 4u
+ 0x00167c06, // n0x0738 c0x0000 (---------------) + africa
+ 0x10a72409, // n0x0739 c0x0042 (n0x07ff-n0x0811) o I amazonaws
+ 0x00036947, // n0x073a c0x0000 (---------------) + appspot
+ 0x00000602, // n0x073b c0x0000 (---------------) + ar
+ 0x00163e4a, // n0x073c c0x0000 (---------------) + betainabox
+ 0x000d0087, // n0x073d c0x0000 (---------------) + blogdns
+ 0x000e4188, // n0x073e c0x0000 (---------------) + blogspot
+ 0x00012e82, // n0x073f c0x0000 (---------------) + br
+ 0x0012df87, // n0x0740 c0x0000 (---------------) + cechire
+ 0x0013220f, // n0x0741 c0x0000 (---------------) + cloudcontrolapp
+ 0x0018d44f, // n0x0742 c0x0000 (---------------) + cloudcontrolled
+ 0x000211c2, // n0x0743 c0x0000 (---------------) + cn
+ 0x00000742, // n0x0744 c0x0000 (---------------) + co
+ 0x0008ca08, // n0x0745 c0x0000 (---------------) + codespot
+ 0x000006c2, // n0x0746 c0x0000 (---------------) + de
+ 0x00146fc8, // n0x0747 c0x0000 (---------------) + dnsalias
+ 0x0006a247, // n0x0748 c0x0000 (---------------) + dnsdojo
+ 0x00010a4b, // n0x0749 c0x0000 (---------------) + doesntexist
+ 0x0015fdc9, // n0x074a c0x0000 (---------------) + dontexist
+ 0x00146ec7, // n0x074b c0x0000 (---------------) + doomdns
+ 0x000da58c, // n0x074c c0x0000 (---------------) + dreamhosters
+ 0x0015aeca, // n0x074d c0x0000 (---------------) + dyn-o-saur
+ 0x000007c8, // n0x074e c0x0000 (---------------) + dynalias
+ 0x000b8c0e, // n0x074f c0x0000 (---------------) + dyndns-at-home
+ 0x000e024e, // n0x0750 c0x0000 (---------------) + dyndns-at-work
+ 0x000cfecb, // n0x0751 c0x0000 (---------------) + dyndns-blog
+ 0x00009acb, // n0x0752 c0x0000 (---------------) + dyndns-free
+ 0x0000b1cb, // n0x0753 c0x0000 (---------------) + dyndns-home
+ 0x00013889, // n0x0754 c0x0000 (---------------) + dyndns-ip
+ 0x00018d4b, // n0x0755 c0x0000 (---------------) + dyndns-mail
+ 0x00019a8d, // n0x0756 c0x0000 (---------------) + dyndns-office
+ 0x0001ea4b, // n0x0757 c0x0000 (---------------) + dyndns-pics
+ 0x0001fd0d, // n0x0758 c0x0000 (---------------) + dyndns-remote
+ 0x00020d4d, // n0x0759 c0x0000 (---------------) + dyndns-server
+ 0x00051b0a, // n0x075a c0x0000 (---------------) + dyndns-web
+ 0x0005a2cb, // n0x075b c0x0000 (---------------) + dyndns-wiki
+ 0x0009aecb, // n0x075c c0x0000 (---------------) + dyndns-work
+ 0x00016650, // n0x075d c0x0000 (---------------) + elasticbeanstalk
+ 0x0013c78f, // n0x075e c0x0000 (---------------) + est-a-la-maison
+ 0x0002270f, // n0x075f c0x0000 (---------------) + est-a-la-masion
+ 0x000144cd, // n0x0760 c0x0000 (---------------) + est-le-patron
+ 0x000ef550, // n0x0761 c0x0000 (---------------) + est-mon-blogueur
+ 0x0001d5c2, // n0x0762 c0x0000 (---------------) + eu
+ 0x0003674b, // n0x0763 c0x0000 (---------------) + firebaseapp
+ 0x0003ffc8, // n0x0764 c0x0000 (---------------) + flynnhub
+ 0x0004d407, // n0x0765 c0x0000 (---------------) + from-ak
+ 0x0004d747, // n0x0766 c0x0000 (---------------) + from-al
+ 0x0004d907, // n0x0767 c0x0000 (---------------) + from-ar
+ 0x0004dd07, // n0x0768 c0x0000 (---------------) + from-ca
+ 0x0004f607, // n0x0769 c0x0000 (---------------) + from-ct
+ 0x00050247, // n0x076a c0x0000 (---------------) + from-dc
+ 0x00052787, // n0x076b c0x0000 (---------------) + from-de
+ 0x00052a47, // n0x076c c0x0000 (---------------) + from-fl
+ 0x00054187, // n0x076d c0x0000 (---------------) + from-ga
+ 0x00054487, // n0x076e c0x0000 (---------------) + from-hi
+ 0x00054ec7, // n0x076f c0x0000 (---------------) + from-ia
+ 0x00055087, // n0x0770 c0x0000 (---------------) + from-id
+ 0x00055247, // n0x0771 c0x0000 (---------------) + from-il
+ 0x00055407, // n0x0772 c0x0000 (---------------) + from-in
+ 0x000561c7, // n0x0773 c0x0000 (---------------) + from-ks
+ 0x00056787, // n0x0774 c0x0000 (---------------) + from-ky
+ 0x00057247, // n0x0775 c0x0000 (---------------) + from-ma
+ 0x00057647, // n0x0776 c0x0000 (---------------) + from-md
+ 0x00057d87, // n0x0777 c0x0000 (---------------) + from-mi
+ 0x00058887, // n0x0778 c0x0000 (---------------) + from-mn
+ 0x00058a47, // n0x0779 c0x0000 (---------------) + from-mo
+ 0x00059007, // n0x077a c0x0000 (---------------) + from-ms
+ 0x00059507, // n0x077b c0x0000 (---------------) + from-mt
+ 0x00059707, // n0x077c c0x0000 (---------------) + from-nc
+ 0x0005a607, // n0x077d c0x0000 (---------------) + from-nd
+ 0x0005a7c7, // n0x077e c0x0000 (---------------) + from-ne
+ 0x0005a987, // n0x077f c0x0000 (---------------) + from-nh
+ 0x0005b247, // n0x0780 c0x0000 (---------------) + from-nj
+ 0x0005b747, // n0x0781 c0x0000 (---------------) + from-nm
+ 0x0005c247, // n0x0782 c0x0000 (---------------) + from-nv
+ 0x0005c847, // n0x0783 c0x0000 (---------------) + from-oh
+ 0x0005cf07, // n0x0784 c0x0000 (---------------) + from-ok
+ 0x0005d407, // n0x0785 c0x0000 (---------------) + from-or
+ 0x0005d5c7, // n0x0786 c0x0000 (---------------) + from-pa
+ 0x0005d947, // n0x0787 c0x0000 (---------------) + from-pr
+ 0x0005e307, // n0x0788 c0x0000 (---------------) + from-ri
+ 0x0005e687, // n0x0789 c0x0000 (---------------) + from-sc
+ 0x0005ea87, // n0x078a c0x0000 (---------------) + from-sd
+ 0x000604c7, // n0x078b c0x0000 (---------------) + from-tn
+ 0x00060687, // n0x078c c0x0000 (---------------) + from-tx
+ 0x00061207, // n0x078d c0x0000 (---------------) + from-ut
+ 0x00062947, // n0x078e c0x0000 (---------------) + from-va
+ 0x00062f87, // n0x078f c0x0000 (---------------) + from-vt
+ 0x00063287, // n0x0790 c0x0000 (---------------) + from-wa
+ 0x00063447, // n0x0791 c0x0000 (---------------) + from-wi
+ 0x000637c7, // n0x0792 c0x0000 (---------------) + from-wv
+ 0x00063a07, // n0x0793 c0x0000 (---------------) + from-wy
+ 0x00005a42, // n0x0794 c0x0000 (---------------) + gb
+ 0x000c0587, // n0x0795 c0x0000 (---------------) + getmyip
+ 0x000b53d1, // n0x0796 c0x0000 (---------------) + githubusercontent
+ 0x000cd58a, // n0x0797 c0x0000 (---------------) + googleapis
+ 0x0008c88a, // n0x0798 c0x0000 (---------------) + googlecode
+ 0x00043a86, // n0x0799 c0x0000 (---------------) + gotdns
+ 0x0000dc82, // n0x079a c0x0000 (---------------) + gr
+ 0x0008f309, // n0x079b c0x0000 (---------------) + herokuapp
+ 0x0007fe89, // n0x079c c0x0000 (---------------) + herokussl
+ 0x0002ea02, // n0x079d c0x0000 (---------------) + hk
+ 0x0013eeca, // n0x079e c0x0000 (---------------) + hobby-site
+ 0x00090ec9, // n0x079f c0x0000 (---------------) + homelinux
+ 0x00091b88, // n0x07a0 c0x0000 (---------------) + homeunix
+ 0x00017d42, // n0x07a1 c0x0000 (---------------) + hu
+ 0x001008c9, // n0x07a2 c0x0000 (---------------) + iamallama
+ 0x0005db8e, // n0x07a3 c0x0000 (---------------) + is-a-anarchist
+ 0x000d5d8c, // n0x07a4 c0x0000 (---------------) + is-a-blogger
+ 0x000bf2cf, // n0x07a5 c0x0000 (---------------) + is-a-bookkeeper
+ 0x0017d14e, // n0x07a6 c0x0000 (---------------) + is-a-bulls-fan
+ 0x0000cf8c, // n0x07a7 c0x0000 (---------------) + is-a-caterer
+ 0x00166909, // n0x07a8 c0x0000 (---------------) + is-a-chef
+ 0x00099ad1, // n0x07a9 c0x0000 (---------------) + is-a-conservative
+ 0x0009a088, // n0x07aa c0x0000 (---------------) + is-a-cpa
+ 0x00169b92, // n0x07ab c0x0000 (---------------) + is-a-cubicle-slave
+ 0x0002e40d, // n0x07ac c0x0000 (---------------) + is-a-democrat
+ 0x00032e0d, // n0x07ad c0x0000 (---------------) + is-a-designer
+ 0x00138acb, // n0x07ae c0x0000 (---------------) + is-a-doctor
+ 0x001713d5, // n0x07af c0x0000 (---------------) + is-a-financialadvisor
+ 0x00052e49, // n0x07b0 c0x0000 (---------------) + is-a-geek
+ 0x0005afca, // n0x07b1 c0x0000 (---------------) + is-a-green
+ 0x0005bb09, // n0x07b2 c0x0000 (---------------) + is-a-guru
+ 0x00060e10, // n0x07b3 c0x0000 (---------------) + is-a-hard-worker
+ 0x00065f4b, // n0x07b4 c0x0000 (---------------) + is-a-hunter
+ 0x0006f74f, // n0x07b5 c0x0000 (---------------) + is-a-landscaper
+ 0x000702cb, // n0x07b6 c0x0000 (---------------) + is-a-lawyer
+ 0x00073b0c, // n0x07b7 c0x0000 (---------------) + is-a-liberal
+ 0x00077590, // n0x07b8 c0x0000 (---------------) + is-a-libertarian
+ 0x001370ca, // n0x07b9 c0x0000 (---------------) + is-a-llama
+ 0x00137a4d, // n0x07ba c0x0000 (---------------) + is-a-musician
+ 0x00174a0e, // n0x07bb c0x0000 (---------------) + is-a-nascarfan
+ 0x0007bf4a, // n0x07bc c0x0000 (---------------) + is-a-nurse
+ 0x0007d0cc, // n0x07bd c0x0000 (---------------) + is-a-painter
+ 0x0008ab94, // n0x07be c0x0000 (---------------) + is-a-personaltrainer
+ 0x0008ef91, // n0x07bf c0x0000 (---------------) + is-a-photographer
+ 0x00090c0b, // n0x07c0 c0x0000 (---------------) + is-a-player
+ 0x0009638f, // n0x07c1 c0x0000 (---------------) + is-a-republican
+ 0x00096fcd, // n0x07c2 c0x0000 (---------------) + is-a-rockstar
+ 0x000c268e, // n0x07c3 c0x0000 (---------------) + is-a-socialist
+ 0x0009844c, // n0x07c4 c0x0000 (---------------) + is-a-student
+ 0x0009d08c, // n0x07c5 c0x0000 (---------------) + is-a-teacher
+ 0x000c238b, // n0x07c6 c0x0000 (---------------) + is-a-techie
+ 0x000c3f4e, // n0x07c7 c0x0000 (---------------) + is-a-therapist
+ 0x000d7cd0, // n0x07c8 c0x0000 (---------------) + is-an-accountant
+ 0x0015d44b, // n0x07c9 c0x0000 (---------------) + is-an-actor
+ 0x000a110d, // n0x07ca c0x0000 (---------------) + is-an-actress
+ 0x000a768f, // n0x07cb c0x0000 (---------------) + is-an-anarchist
+ 0x000a814c, // n0x07cc c0x0000 (---------------) + is-an-artist
+ 0x000a890e, // n0x07cd c0x0000 (---------------) + is-an-engineer
+ 0x000a97d1, // n0x07ce c0x0000 (---------------) + is-an-entertainer
+ 0x00135f8c, // n0x07cf c0x0000 (---------------) + is-certified
+ 0x00133a07, // n0x07d0 c0x0000 (---------------) + is-gone
+ 0x000b0fcd, // n0x07d1 c0x0000 (---------------) + is-into-anime
+ 0x000b918c, // n0x07d2 c0x0000 (---------------) + is-into-cars
+ 0x000d1250, // n0x07d3 c0x0000 (---------------) + is-into-cartoons
+ 0x000d1f4d, // n0x07d4 c0x0000 (---------------) + is-into-games
+ 0x000d3087, // n0x07d5 c0x0000 (---------------) + is-leet
+ 0x000d7250, // n0x07d6 c0x0000 (---------------) + is-not-certified
+ 0x000f9e48, // n0x07d7 c0x0000 (---------------) + is-slick
+ 0x001063cb, // n0x07d8 c0x0000 (---------------) + is-uberleet
+ 0x00146b4f, // n0x07d9 c0x0000 (---------------) + is-with-theband
+ 0x00191648, // n0x07da c0x0000 (---------------) + isa-geek
+ 0x000cd78d, // n0x07db c0x0000 (---------------) + isa-hockeynut
+ 0x00159d10, // n0x07dc c0x0000 (---------------) + issmarterthanyou
+ 0x0009a703, // n0x07dd c0x0000 (---------------) + jpn
+ 0x000034c2, // n0x07de c0x0000 (---------------) + kr
+ 0x00061689, // n0x07df c0x0000 (---------------) + likes-pie
+ 0x0007878a, // n0x07e0 c0x0000 (---------------) + likescandy
+ 0x00032003, // n0x07e1 c0x0000 (---------------) + mex
+ 0x001024c8, // n0x07e2 c0x0000 (---------------) + neat-url
+ 0x00002f07, // n0x07e3 c0x0000 (---------------) + nfshost
+ 0x00000c02, // n0x07e4 c0x0000 (---------------) + no
+ 0x0005080a, // n0x07e5 c0x0000 (---------------) + operaunite
+ 0x0019238f, // n0x07e6 c0x0000 (---------------) + outsystemscloud
+ 0x00110fd2, // n0x07e7 c0x0000 (---------------) + pagespeedmobilizer
+ 0x00169542, // n0x07e8 c0x0000 (---------------) + qc
+ 0x00132187, // n0x07e9 c0x0000 (---------------) + rhcloud
+ 0x00000d82, // n0x07ea c0x0000 (---------------) + ro
+ 0x000044c2, // n0x07eb c0x0000 (---------------) + ru
+ 0x00001a02, // n0x07ec c0x0000 (---------------) + sa
+ 0x0002f650, // n0x07ed c0x0000 (---------------) + saves-the-whales
+ 0x00002e82, // n0x07ee c0x0000 (---------------) + se
+ 0x00130f46, // n0x07ef c0x0000 (---------------) + selfip
+ 0x000c368e, // n0x07f0 c0x0000 (---------------) + sells-for-less
+ 0x0007ac8b, // n0x07f1 c0x0000 (---------------) + sells-for-u
+ 0x00079088, // n0x07f2 c0x0000 (---------------) + servebbs
+ 0x000e0a0a, // n0x07f3 c0x0000 (---------------) + simple-url
+ 0x000dc7c7, // n0x07f4 c0x0000 (---------------) + sinaapp
+ 0x000101cd, // n0x07f5 c0x0000 (---------------) + space-to-rent
+ 0x0010160c, // n0x07f6 c0x0000 (---------------) + teaches-yoga
+ 0x0000cf02, // n0x07f7 c0x0000 (---------------) + uk
+ 0x00009f42, // n0x07f8 c0x0000 (---------------) + us
+ 0x00006842, // n0x07f9 c0x0000 (---------------) + uy
+ 0x000dc70a, // n0x07fa c0x0000 (---------------) + vipsinaapp
+ 0x000cd48a, // n0x07fb c0x0000 (---------------) + withgoogle
+ 0x000e3f0e, // n0x07fc c0x0000 (---------------) + writesthisblog
+ 0x000c9948, // n0x07fd c0x0000 (---------------) + yolasite
+ 0x000043c2, // n0x07fe c0x0000 (---------------) + za
+ 0x10c23487, // n0x07ff c0x0043 (n0x0811-n0x081a) + compute
+ 0x11023489, // n0x0800 c0x0044 (n0x081a-n0x081c) + compute-1
+ 0x0000a503, // n0x0801 c0x0000 (---------------) + elb
+ 0x1172bb4c, // n0x0802 c0x0045 (n0x081c-n0x081d) o I eu-central-1
+ 0x000413c2, // n0x0803 c0x0000 (---------------) + s3
+ 0x000f0c51, // n0x0804 c0x0000 (---------------) + s3-ap-northeast-1
+ 0x000f9411, // n0x0805 c0x0000 (---------------) + s3-ap-southeast-1
+ 0x0010a591, // n0x0806 c0x0000 (---------------) + s3-ap-southeast-2
+ 0x0012ba8f, // n0x0807 c0x0000 (---------------) + s3-eu-central-1
+ 0x00072c0c, // n0x0808 c0x0000 (---------------) + s3-eu-west-1
+ 0x0010d14d, // n0x0809 c0x0000 (---------------) + s3-external-1
+ 0x0010ef8d, // n0x080a c0x0000 (---------------) + s3-external-2
+ 0x0011e6d5, // n0x080b c0x0000 (---------------) + s3-fips-us-gov-west-1
+ 0x000413cc, // n0x080c c0x0000 (---------------) + s3-sa-east-1
+ 0x000696d0, // n0x080d c0x0000 (---------------) + s3-us-gov-west-1
+ 0x0011960c, // n0x080e c0x0000 (---------------) + s3-us-west-1
+ 0x000affcc, // n0x080f c0x0000 (---------------) + s3-us-west-2
+ 0x0002b6c9, // n0x0810 c0x0000 (---------------) + us-east-1
+ 0x000f0d0e, // n0x0811 c0x0000 (---------------) + ap-northeast-1
+ 0x000f94ce, // n0x0812 c0x0000 (---------------) + ap-southeast-1
+ 0x0010a64e, // n0x0813 c0x0000 (---------------) + ap-southeast-2
+ 0x0012bb4c, // n0x0814 c0x0000 (---------------) + eu-central-1
+ 0x00072cc9, // n0x0815 c0x0000 (---------------) + eu-west-1
+ 0x00041489, // n0x0816 c0x0000 (---------------) + sa-east-1
+ 0x0006978d, // n0x0817 c0x0000 (---------------) + us-gov-west-1
+ 0x001196c9, // n0x0818 c0x0000 (---------------) + us-west-1
+ 0x000b0089, // n0x0819 c0x0000 (---------------) + us-west-2
+ 0x000f0043, // n0x081a c0x0000 (---------------) + z-1
+ 0x000c8c03, // n0x081b c0x0000 (---------------) + z-2
+ 0x000413c2, // n0x081c c0x0000 (---------------) + s3
+ 0x00201e82, // n0x081d c0x0000 (---------------) + I ac
+ 0x00200742, // n0x081e c0x0000 (---------------) + I co
+ 0x00203fc2, // n0x081f c0x0000 (---------------) + I ed
+ 0x002099c2, // n0x0820 c0x0000 (---------------) + I fi
+ 0x00202342, // n0x0821 c0x0000 (---------------) + I go
+ 0x00200c42, // n0x0822 c0x0000 (---------------) + I or
+ 0x00201a02, // n0x0823 c0x0000 (---------------) + I sa
+ 0x00222ac3, // n0x0824 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0825 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x0826 c0x0000 (---------------) + I gov
+ 0x00200303, // n0x0827 c0x0000 (---------------) + I inf
+ 0x002170c3, // n0x0828 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0829 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x082a c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x082b c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x082c c0x0000 (---------------) + I edu
+ 0x002170c3, // n0x082d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x082e c0x0000 (---------------) + I org
+ 0x00078a43, // n0x082f c0x0000 (---------------) + ath
+ 0x0021e283, // n0x0830 c0x0000 (---------------) + I gov
+ 0x00201e82, // n0x0831 c0x0000 (---------------) + I ac
+ 0x00310603, // n0x0832 c0x0000 (---------------) + I biz
+ 0x13222ac3, // n0x0833 c0x004c (n0x083e-n0x083f) + I com
+ 0x00267c47, // n0x0834 c0x0000 (---------------) + I ekloges
+ 0x0021e283, // n0x0835 c0x0000 (---------------) + I gov
+ 0x003413c3, // n0x0836 c0x0000 (---------------) + I ltd
+ 0x00298944, // n0x0837 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x0838 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0839 c0x0000 (---------------) + I org
+ 0x002647ca, // n0x083a c0x0000 (---------------) + I parliament
+ 0x0029abc5, // n0x083b c0x0000 (---------------) + I press
+ 0x00218243, // n0x083c c0x0000 (---------------) + I pro
+ 0x00208902, // n0x083d c0x0000 (---------------) + I tm
+ 0x000e4188, // n0x083e c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x083f c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x0840 c0x0000 (---------------) + blogspot
+ 0x00022ac3, // n0x0841 c0x0000 (---------------) + com
+ 0x0009fe8f, // n0x0842 c0x0000 (---------------) + fuettertdasnetz
+ 0x0015ff4a, // n0x0843 c0x0000 (---------------) + isteingeek
+ 0x000c2947, // n0x0844 c0x0000 (---------------) + istmein
+ 0x00016f4a, // n0x0845 c0x0000 (---------------) + lebtimnetz
+ 0x0006cb8a, // n0x0846 c0x0000 (---------------) + leitungsen
+ 0x000e8f4d, // n0x0847 c0x0000 (---------------) + traeumtgerade
+ 0x000e4188, // n0x0848 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x0849 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x084a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x084b c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x084c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x084d c0x0000 (---------------) + I org
+ 0x00200603, // n0x084e c0x0000 (---------------) + I art
+ 0x00222ac3, // n0x084f c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0850 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x0851 c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x0852 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x0853 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0854 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0855 c0x0000 (---------------) + I org
+ 0x00280043, // n0x0856 c0x0000 (---------------) + I sld
+ 0x00219fc3, // n0x0857 c0x0000 (---------------) + I web
+ 0x00200603, // n0x0858 c0x0000 (---------------) + I art
+ 0x002729c4, // n0x0859 c0x0000 (---------------) + I asso
+ 0x00222ac3, // n0x085a c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x085b c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x085c c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x085d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x085e c0x0000 (---------------) + I org
+ 0x00218943, // n0x085f c0x0000 (---------------) + I pol
+ 0x00222ac3, // n0x0860 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0861 c0x0000 (---------------) + I edu
+ 0x00236403, // n0x0862 c0x0000 (---------------) + I fin
+ 0x0034eb03, // n0x0863 c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x0864 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x0865 c0x0000 (---------------) + I info
+ 0x00312503, // n0x0866 c0x0000 (---------------) + I k12
+ 0x0020b403, // n0x0867 c0x0000 (---------------) + I med
+ 0x0023fa03, // n0x0868 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0869 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x086a c0x0000 (---------------) + I org
+ 0x00218243, // n0x086b c0x0000 (---------------) + I pro
+ 0x00382f83, // n0x086c c0x0000 (---------------) + I aip
+ 0x15622ac3, // n0x086d c0x0055 (n0x0876-n0x0877) + I com
+ 0x002d75c3, // n0x086e c0x0000 (---------------) + I edu
+ 0x002099c3, // n0x086f c0x0000 (---------------) + I fie
+ 0x0021e283, // n0x0870 c0x0000 (---------------) + I gov
+ 0x00273c43, // n0x0871 c0x0000 (---------------) + I lib
+ 0x0020b403, // n0x0872 c0x0000 (---------------) + I med
+ 0x0021dcc3, // n0x0873 c0x0000 (---------------) + I org
+ 0x0025f803, // n0x0874 c0x0000 (---------------) + I pri
+ 0x00375bc4, // n0x0875 c0x0000 (---------------) + I riik
+ 0x000e4188, // n0x0876 c0x0000 (---------------) + blogspot
+ 0x15e22ac3, // n0x0877 c0x0057 (n0x0880-n0x0881) + I com
+ 0x002d75c3, // n0x0878 c0x0000 (---------------) + I edu
+ 0x00291c43, // n0x0879 c0x0000 (---------------) + I eun
+ 0x0021e283, // n0x087a c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x087b c0x0000 (---------------) + I mil
+ 0x00298944, // n0x087c c0x0000 (---------------) + I name
+ 0x002170c3, // n0x087d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x087e c0x0000 (---------------) + I org
+ 0x00215583, // n0x087f c0x0000 (---------------) + I sci
+ 0x000e4188, // n0x0880 c0x0000 (---------------) + blogspot
+ 0x16622ac3, // n0x0881 c0x0059 (n0x0886-n0x0887) + I com
+ 0x002d75c3, // n0x0882 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x0883 c0x0000 (---------------) + I gob
+ 0x00207cc3, // n0x0884 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x0885 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x0886 c0x0000 (---------------) + blogspot
+ 0x00310603, // n0x0887 c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x0888 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0889 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x088a c0x0000 (---------------) + I gov
+ 0x00200304, // n0x088b c0x0000 (---------------) + I info
+ 0x00298944, // n0x088c c0x0000 (---------------) + I name
+ 0x002170c3, // n0x088d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x088e c0x0000 (---------------) + I org
+ 0x002f85c5, // n0x088f c0x0000 (---------------) + I aland
+ 0x000e4188, // n0x0890 c0x0000 (---------------) + blogspot
+ 0x00006743, // n0x0891 c0x0000 (---------------) + iki
+ 0x0036e608, // n0x0892 c0x0000 (---------------) + I aeroport
+ 0x00383787, // n0x0893 c0x0000 (---------------) + I assedic
+ 0x002729c4, // n0x0894 c0x0000 (---------------) + I asso
+ 0x00310b86, // n0x0895 c0x0000 (---------------) + I avocat
+ 0x0031e586, // n0x0896 c0x0000 (---------------) + I avoues
+ 0x000e4188, // n0x0897 c0x0000 (---------------) + blogspot
+ 0x0025f743, // n0x0898 c0x0000 (---------------) + I cci
+ 0x003011c9, // n0x0899 c0x0000 (---------------) + I chambagri
+ 0x00234bd5, // n0x089a c0x0000 (---------------) + I chirurgiens-dentistes
+ 0x00222ac3, // n0x089b c0x0000 (---------------) + I com
+ 0x0030c7d2, // n0x089c c0x0000 (---------------) + I experts-comptables
+ 0x0030c58f, // n0x089d c0x0000 (---------------) + I geometre-expert
+ 0x003579c4, // n0x089e c0x0000 (---------------) + I gouv
+ 0x0020dc85, // n0x089f c0x0000 (---------------) + I greta
+ 0x002de690, // n0x08a0 c0x0000 (---------------) + I huissier-justice
+ 0x002753c7, // n0x08a1 c0x0000 (---------------) + I medecin
+ 0x00207cc3, // n0x08a2 c0x0000 (---------------) + I nom
+ 0x00265c08, // n0x08a3 c0x0000 (---------------) + I notaires
+ 0x002e078a, // n0x08a4 c0x0000 (---------------) + I pharmacien
+ 0x0021a444, // n0x08a5 c0x0000 (---------------) + I port
+ 0x002ca783, // n0x08a6 c0x0000 (---------------) + I prd
+ 0x0029abc6, // n0x08a7 c0x0000 (---------------) + I presse
+ 0x00208902, // n0x08a8 c0x0000 (---------------) + I tm
+ 0x0036b18b, // n0x08a9 c0x0000 (---------------) + I veterinaire
+ 0x00222ac3, // n0x08aa c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08ab c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08ac c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x08ad c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x08ae c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08af c0x0000 (---------------) + I org
+ 0x002ce243, // n0x08b0 c0x0000 (---------------) + I pvt
+ 0x00200742, // n0x08b1 c0x0000 (---------------) + I co
+ 0x002170c3, // n0x08b2 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08b3 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x08b4 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08b5 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08b6 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x08b7 c0x0000 (---------------) + I mil
+ 0x0021dcc3, // n0x08b8 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x08b9 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08ba c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08bb c0x0000 (---------------) + I gov
+ 0x003413c3, // n0x08bc c0x0000 (---------------) + I ltd
+ 0x00208483, // n0x08bd c0x0000 (---------------) + I mod
+ 0x0021dcc3, // n0x08be c0x0000 (---------------) + I org
+ 0x00200742, // n0x08bf c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x08c0 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08c1 c0x0000 (---------------) + I edu
+ 0x002170c3, // n0x08c2 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08c3 c0x0000 (---------------) + I org
+ 0x00201e82, // n0x08c4 c0x0000 (---------------) + I ac
+ 0x00222ac3, // n0x08c5 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08c6 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08c7 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x08c8 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08c9 c0x0000 (---------------) + I org
+ 0x002729c4, // n0x08ca c0x0000 (---------------) + I asso
+ 0x00222ac3, // n0x08cb c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08cc c0x0000 (---------------) + I edu
+ 0x00203604, // n0x08cd c0x0000 (---------------) + I mobi
+ 0x002170c3, // n0x08ce c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08cf c0x0000 (---------------) + I org
+ 0x000e4188, // n0x08d0 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x08d1 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08d2 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08d3 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x08d4 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08d5 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x08d6 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08d7 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x08d8 c0x0000 (---------------) + I gob
+ 0x00215703, // n0x08d9 c0x0000 (---------------) + I ind
+ 0x0023fa03, // n0x08da c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x08db c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08dc c0x0000 (---------------) + I org
+ 0x00200742, // n0x08dd c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x08de c0x0000 (---------------) + I com
+ 0x002170c3, // n0x08df c0x0000 (---------------) + I net
+ 0x000e4188, // n0x08e0 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x08e1 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08e2 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x08e3 c0x0000 (---------------) + I gov
+ 0x00300b83, // n0x08e4 c0x0000 (---------------) + I idv
+ 0x00051e43, // n0x08e5 c0x0000 (---------------) + inc
+ 0x001413c3, // n0x08e6 c0x0000 (---------------) + ltd
+ 0x002170c3, // n0x08e7 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08e8 c0x0000 (---------------) + I org
+ 0x002ea10a, // n0x08e9 c0x0000 (---------------) + I xn--55qx5d
+ 0x00306ac9, // n0x08ea c0x0000 (---------------) + I xn--ciqpn
+ 0x0032788b, // n0x08eb c0x0000 (---------------) + I xn--gmq050i
+ 0x00327f0a, // n0x08ec c0x0000 (---------------) + I xn--gmqw5a
+ 0x0033080a, // n0x08ed c0x0000 (---------------) + I xn--io0a7i
+ 0x0033f6cb, // n0x08ee c0x0000 (---------------) + I xn--lcvr32d
+ 0x00356c8a, // n0x08ef c0x0000 (---------------) + I xn--mk0axi
+ 0x0035c6ca, // n0x08f0 c0x0000 (---------------) + I xn--mxtq1m
+ 0x0036394a, // n0x08f1 c0x0000 (---------------) + I xn--od0alg
+ 0x00363bcb, // n0x08f2 c0x0000 (---------------) + I xn--od0aq3b
+ 0x00384789, // n0x08f3 c0x0000 (---------------) + I xn--tn0ag
+ 0x003867ca, // n0x08f4 c0x0000 (---------------) + I xn--uc0atv
+ 0x00386c4b, // n0x08f5 c0x0000 (---------------) + I xn--uc0ay4a
+ 0x0039008b, // n0x08f6 c0x0000 (---------------) + I xn--wcvs22d
+ 0x0039634a, // n0x08f7 c0x0000 (---------------) + I xn--zf0avx
+ 0x00222ac3, // n0x08f8 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x08f9 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x08fa c0x0000 (---------------) + I gob
+ 0x0023fa03, // n0x08fb c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x08fc c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x08fd c0x0000 (---------------) + I org
+ 0x000e4188, // n0x08fe c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x08ff c0x0000 (---------------) + I com
+ 0x0024d404, // n0x0900 c0x0000 (---------------) + I from
+ 0x00209782, // n0x0901 c0x0000 (---------------) + I iz
+ 0x00298944, // n0x0902 c0x0000 (---------------) + I name
+ 0x0036b745, // n0x0903 c0x0000 (---------------) + I adult
+ 0x00200603, // n0x0904 c0x0000 (---------------) + I art
+ 0x002729c4, // n0x0905 c0x0000 (---------------) + I asso
+ 0x00222ac3, // n0x0906 c0x0000 (---------------) + I com
+ 0x00228d44, // n0x0907 c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x0908 c0x0000 (---------------) + I edu
+ 0x00238544, // n0x0909 c0x0000 (---------------) + I firm
+ 0x003579c4, // n0x090a c0x0000 (---------------) + I gouv
+ 0x00200304, // n0x090b c0x0000 (---------------) + I info
+ 0x0020b403, // n0x090c c0x0000 (---------------) + I med
+ 0x002170c3, // n0x090d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x090e c0x0000 (---------------) + I org
+ 0x0028acc5, // n0x090f c0x0000 (---------------) + I perso
+ 0x00218943, // n0x0910 c0x0000 (---------------) + I pol
+ 0x00218243, // n0x0911 c0x0000 (---------------) + I pro
+ 0x00241283, // n0x0912 c0x0000 (---------------) + I rel
+ 0x0029b184, // n0x0913 c0x0000 (---------------) + I shop
+ 0x0036f244, // n0x0914 c0x0000 (---------------) + I 2000
+ 0x00256e45, // n0x0915 c0x0000 (---------------) + I agrar
+ 0x000e4188, // n0x0916 c0x0000 (---------------) + blogspot
+ 0x002de404, // n0x0917 c0x0000 (---------------) + I bolt
+ 0x002112c6, // n0x0918 c0x0000 (---------------) + I casino
+ 0x002735c4, // n0x0919 c0x0000 (---------------) + I city
+ 0x00200742, // n0x091a c0x0000 (---------------) + I co
+ 0x003128c7, // n0x091b c0x0000 (---------------) + I erotica
+ 0x0023a507, // n0x091c c0x0000 (---------------) + I erotika
+ 0x00356104, // n0x091d c0x0000 (---------------) + I film
+ 0x00246b45, // n0x091e c0x0000 (---------------) + I forum
+ 0x002d2145, // n0x091f c0x0000 (---------------) + I games
+ 0x00294945, // n0x0920 c0x0000 (---------------) + I hotel
+ 0x00200304, // n0x0921 c0x0000 (---------------) + I info
+ 0x0032d888, // n0x0922 c0x0000 (---------------) + I ingatlan
+ 0x00207486, // n0x0923 c0x0000 (---------------) + I jogasz
+ 0x002c1d48, // n0x0924 c0x0000 (---------------) + I konyvelo
+ 0x0023b745, // n0x0925 c0x0000 (---------------) + I lakas
+ 0x002dc385, // n0x0926 c0x0000 (---------------) + I media
+ 0x00366d44, // n0x0927 c0x0000 (---------------) + I news
+ 0x0021dcc3, // n0x0928 c0x0000 (---------------) + I org
+ 0x002cba44, // n0x0929 c0x0000 (---------------) + I priv
+ 0x0033de86, // n0x092a c0x0000 (---------------) + I reklam
+ 0x0029acc3, // n0x092b c0x0000 (---------------) + I sex
+ 0x0029b184, // n0x092c c0x0000 (---------------) + I shop
+ 0x0027cac5, // n0x092d c0x0000 (---------------) + I sport
+ 0x00278704, // n0x092e c0x0000 (---------------) + I suli
+ 0x0020bac4, // n0x092f c0x0000 (---------------) + I szex
+ 0x00208902, // n0x0930 c0x0000 (---------------) + I tm
+ 0x0024f786, // n0x0931 c0x0000 (---------------) + I tozsde
+ 0x0037a486, // n0x0932 c0x0000 (---------------) + I utazas
+ 0x002db9c5, // n0x0933 c0x0000 (---------------) + I video
+ 0x00201e82, // n0x0934 c0x0000 (---------------) + I ac
+ 0x00310603, // n0x0935 c0x0000 (---------------) + I biz
+ 0x1b600742, // n0x0936 c0x006d (n0x093f-n0x0940) + I co
+ 0x00227544, // n0x0937 c0x0000 (---------------) + I desa
+ 0x00202342, // n0x0938 c0x0000 (---------------) + I go
+ 0x0023fa03, // n0x0939 c0x0000 (---------------) + I mil
+ 0x00220282, // n0x093a c0x0000 (---------------) + I my
+ 0x002170c3, // n0x093b c0x0000 (---------------) + I net
+ 0x00200c42, // n0x093c c0x0000 (---------------) + I or
+ 0x00206103, // n0x093d c0x0000 (---------------) + I sch
+ 0x00219fc3, // n0x093e c0x0000 (---------------) + I web
+ 0x000e4188, // n0x093f c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x0940 c0x0000 (---------------) + blogspot
+ 0x0021e283, // n0x0941 c0x0000 (---------------) + I gov
+ 0x1c200742, // n0x0942 c0x0070 (n0x0943-n0x0944) o I co
+ 0x000e4188, // n0x0943 c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x0944 c0x0000 (---------------) + I ac
+ 0x1ca00742, // n0x0945 c0x0072 (n0x094b-n0x094d) + I co
+ 0x00222ac3, // n0x0946 c0x0000 (---------------) + I com
+ 0x002170c3, // n0x0947 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0948 c0x0000 (---------------) + I org
+ 0x00206582, // n0x0949 c0x0000 (---------------) + I tt
+ 0x0020bf42, // n0x094a c0x0000 (---------------) + I tv
+ 0x003413c3, // n0x094b c0x0000 (---------------) + I ltd
+ 0x002c65c3, // n0x094c c0x0000 (---------------) + I plc
+ 0x00201e82, // n0x094d c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x094e c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x094f c0x0000 (---------------) + I co
+ 0x002d75c3, // n0x0950 c0x0000 (---------------) + I edu
+ 0x00238544, // n0x0951 c0x0000 (---------------) + I firm
+ 0x002012c3, // n0x0952 c0x0000 (---------------) + I gen
+ 0x0021e283, // n0x0953 c0x0000 (---------------) + I gov
+ 0x00215703, // n0x0954 c0x0000 (---------------) + I ind
+ 0x0023fa03, // n0x0955 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x0956 c0x0000 (---------------) + I net
+ 0x0020e3c3, // n0x0957 c0x0000 (---------------) + I nic
+ 0x0021dcc3, // n0x0958 c0x0000 (---------------) + I org
+ 0x00215503, // n0x0959 c0x0000 (---------------) + I res
+ 0x000f7e53, // n0x095a c0x0000 (---------------) + barrel-of-knowledge
+ 0x00104094, // n0x095b c0x0000 (---------------) + barrell-of-knowledge
+ 0x00009ac6, // n0x095c c0x0000 (---------------) + dyndns
+ 0x00042187, // n0x095d c0x0000 (---------------) + for-our
+ 0x000f8fc9, // n0x095e c0x0000 (---------------) + groks-the
+ 0x0009b5ca, // n0x095f c0x0000 (---------------) + groks-this
+ 0x0007690d, // n0x0960 c0x0000 (---------------) + here-for-more
+ 0x0009bfca, // n0x0961 c0x0000 (---------------) + knowsitall
+ 0x00130f46, // n0x0962 c0x0000 (---------------) + selfip
+ 0x00110e86, // n0x0963 c0x0000 (---------------) + webhop
+ 0x0021d5c2, // n0x0964 c0x0000 (---------------) + I eu
+ 0x00222ac3, // n0x0965 c0x0000 (---------------) + I com
+ 0x000b53c6, // n0x0966 c0x0000 (---------------) + github
+ 0x000f9303, // n0x0967 c0x0000 (---------------) + nid
+ 0x00222ac3, // n0x0968 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0969 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x096a c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x096b c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x096c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x096d c0x0000 (---------------) + I org
+ 0x00201e82, // n0x096e c0x0000 (---------------) + I ac
+ 0x00200742, // n0x096f c0x0000 (---------------) + I co
+ 0x0021e283, // n0x0970 c0x0000 (---------------) + I gov
+ 0x00206202, // n0x0971 c0x0000 (---------------) + I id
+ 0x002170c3, // n0x0972 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0973 c0x0000 (---------------) + I org
+ 0x00206103, // n0x0974 c0x0000 (---------------) + I sch
+ 0x0034a88f, // n0x0975 c0x0000 (---------------) + I xn--mgba3a4f16a
+ 0x0034ac4e, // n0x0976 c0x0000 (---------------) + I xn--mgba3a4fra
+ 0x000e4188, // n0x0977 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x0978 c0x0000 (---------------) + I com
+ 0x000356c7, // n0x0979 c0x0000 (---------------) + cupcake
+ 0x002d75c3, // n0x097a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x097b c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x097c c0x0000 (---------------) + I int
+ 0x002170c3, // n0x097d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x097e c0x0000 (---------------) + I org
+ 0x00214a43, // n0x097f c0x0000 (---------------) + I abr
+ 0x0038b8c7, // n0x0980 c0x0000 (---------------) + I abruzzo
+ 0x002015c2, // n0x0981 c0x0000 (---------------) + I ag
+ 0x00384949, // n0x0982 c0x0000 (---------------) + I agrigento
+ 0x00200882, // n0x0983 c0x0000 (---------------) + I al
+ 0x0022f94b, // n0x0984 c0x0000 (---------------) + I alessandria
+ 0x002d344a, // n0x0985 c0x0000 (---------------) + I alto-adige
+ 0x002c0d89, // n0x0986 c0x0000 (---------------) + I altoadige
+ 0x00201242, // n0x0987 c0x0000 (---------------) + I an
+ 0x00295b46, // n0x0988 c0x0000 (---------------) + I ancona
+ 0x00264b95, // n0x0989 c0x0000 (---------------) + I andria-barletta-trani
+ 0x0022fa95, // n0x098a c0x0000 (---------------) + I andria-trani-barletta
+ 0x0026bc93, // n0x098b c0x0000 (---------------) + I andriabarlettatrani
+ 0x00230013, // n0x098c c0x0000 (---------------) + I andriatranibarletta
+ 0x0020fd82, // n0x098d c0x0000 (---------------) + I ao
+ 0x00218745, // n0x098e c0x0000 (---------------) + I aosta
+ 0x00341e0c, // n0x098f c0x0000 (---------------) + I aosta-valley
+ 0x0029a44b, // n0x0990 c0x0000 (---------------) + I aostavalley
+ 0x00258685, // n0x0991 c0x0000 (---------------) + I aoste
+ 0x002105c2, // n0x0992 c0x0000 (---------------) + I ap
+ 0x00273fc2, // n0x0993 c0x0000 (---------------) + I aq
+ 0x002d7986, // n0x0994 c0x0000 (---------------) + I aquila
+ 0x00200602, // n0x0995 c0x0000 (---------------) + I ar
+ 0x00363106, // n0x0996 c0x0000 (---------------) + I arezzo
+ 0x0020094d, // n0x0997 c0x0000 (---------------) + I ascoli-piceno
+ 0x0034714c, // n0x0998 c0x0000 (---------------) + I ascolipiceno
+ 0x002166c4, // n0x0999 c0x0000 (---------------) + I asti
+ 0x00201642, // n0x099a c0x0000 (---------------) + I at
+ 0x00201482, // n0x099b c0x0000 (---------------) + I av
+ 0x00369f48, // n0x099c c0x0000 (---------------) + I avellino
+ 0x00205a82, // n0x099d c0x0000 (---------------) + I ba
+ 0x00240186, // n0x099e c0x0000 (---------------) + I balsan
+ 0x0027eb84, // n0x099f c0x0000 (---------------) + I bari
+ 0x00264d55, // n0x09a0 c0x0000 (---------------) + I barletta-trani-andria
+ 0x0026be13, // n0x09a1 c0x0000 (---------------) + I barlettatraniandria
+ 0x0020a443, // n0x09a2 c0x0000 (---------------) + I bas
+ 0x0030d98a, // n0x09a3 c0x0000 (---------------) + I basilicata
+ 0x002c5dc7, // n0x09a4 c0x0000 (---------------) + I belluno
+ 0x0030fa49, // n0x09a5 c0x0000 (---------------) + I benevento
+ 0x0023dc87, // n0x09a6 c0x0000 (---------------) + I bergamo
+ 0x00355e02, // n0x09a7 c0x0000 (---------------) + I bg
+ 0x00200002, // n0x09a8 c0x0000 (---------------) + I bi
+ 0x00381ac6, // n0x09a9 c0x0000 (---------------) + I biella
+ 0x00205842, // n0x09aa c0x0000 (---------------) + I bl
+ 0x000e4188, // n0x09ab c0x0000 (---------------) + blogspot
+ 0x0020a282, // n0x09ac c0x0000 (---------------) + I bn
+ 0x0020a702, // n0x09ad c0x0000 (---------------) + I bo
+ 0x0034c987, // n0x09ae c0x0000 (---------------) + I bologna
+ 0x00391387, // n0x09af c0x0000 (---------------) + I bolzano
+ 0x00212b85, // n0x09b0 c0x0000 (---------------) + I bozen
+ 0x00212e82, // n0x09b1 c0x0000 (---------------) + I br
+ 0x002154c7, // n0x09b2 c0x0000 (---------------) + I brescia
+ 0x00215688, // n0x09b3 c0x0000 (---------------) + I brindisi
+ 0x00235102, // n0x09b4 c0x0000 (---------------) + I bs
+ 0x00216fc2, // n0x09b5 c0x0000 (---------------) + I bt
+ 0x002203c2, // n0x09b6 c0x0000 (---------------) + I bz
+ 0x002055c2, // n0x09b7 c0x0000 (---------------) + I ca
+ 0x00250c08, // n0x09b8 c0x0000 (---------------) + I cagliari
+ 0x0020e443, // n0x09b9 c0x0000 (---------------) + I cal
+ 0x00238348, // n0x09ba c0x0000 (---------------) + I calabria
+ 0x002e718d, // n0x09bb c0x0000 (---------------) + I caltanissetta
+ 0x0021a083, // n0x09bc c0x0000 (---------------) + I cam
+ 0x00300748, // n0x09bd c0x0000 (---------------) + I campania
+ 0x0022c60f, // n0x09be c0x0000 (---------------) + I campidano-medio
+ 0x0022c9ce, // n0x09bf c0x0000 (---------------) + I campidanomedio
+ 0x0027284a, // n0x09c0 c0x0000 (---------------) + I campobasso
+ 0x002d4e91, // n0x09c1 c0x0000 (---------------) + I carbonia-iglesias
+ 0x002d5310, // n0x09c2 c0x0000 (---------------) + I carboniaiglesias
+ 0x0031888d, // n0x09c3 c0x0000 (---------------) + I carrara-massa
+ 0x00318bcc, // n0x09c4 c0x0000 (---------------) + I carraramassa
+ 0x002055c7, // n0x09c5 c0x0000 (---------------) + I caserta
+ 0x0030db07, // n0x09c6 c0x0000 (---------------) + I catania
+ 0x00310c49, // n0x09c7 c0x0000 (---------------) + I catanzaro
+ 0x002167c2, // n0x09c8 c0x0000 (---------------) + I cb
+ 0x00200b82, // n0x09c9 c0x0000 (---------------) + I ce
+ 0x0024434c, // n0x09ca c0x0000 (---------------) + I cesena-forli
+ 0x0024464b, // n0x09cb c0x0000 (---------------) + I cesenaforli
+ 0x00204a02, // n0x09cc c0x0000 (---------------) + I ch
+ 0x002c2546, // n0x09cd c0x0000 (---------------) + I chieti
+ 0x002155c2, // n0x09ce c0x0000 (---------------) + I ci
+ 0x0020af42, // n0x09cf c0x0000 (---------------) + I cl
+ 0x002211c2, // n0x09d0 c0x0000 (---------------) + I cn
+ 0x00200742, // n0x09d1 c0x0000 (---------------) + I co
+ 0x002230c4, // n0x09d2 c0x0000 (---------------) + I como
+ 0x0022bdc7, // n0x09d3 c0x0000 (---------------) + I cosenza
+ 0x0020c502, // n0x09d4 c0x0000 (---------------) + I cr
+ 0x00230b87, // n0x09d5 c0x0000 (---------------) + I cremona
+ 0x00231e07, // n0x09d6 c0x0000 (---------------) + I crotone
+ 0x0020f142, // n0x09d7 c0x0000 (---------------) + I cs
+ 0x00223d82, // n0x09d8 c0x0000 (---------------) + I ct
+ 0x00235585, // n0x09d9 c0x0000 (---------------) + I cuneo
+ 0x00214442, // n0x09da c0x0000 (---------------) + I cz
+ 0x002e990e, // n0x09db c0x0000 (---------------) + I dell-ogliastra
+ 0x002297cd, // n0x09dc c0x0000 (---------------) + I dellogliastra
+ 0x002d75c3, // n0x09dd c0x0000 (---------------) + I edu
+ 0x0023f9ce, // n0x09de c0x0000 (---------------) + I emilia-romagna
+ 0x00261dcd, // n0x09df c0x0000 (---------------) + I emiliaromagna
+ 0x0034efc3, // n0x09e0 c0x0000 (---------------) + I emr
+ 0x00200bc2, // n0x09e1 c0x0000 (---------------) + I en
+ 0x00201504, // n0x09e2 c0x0000 (---------------) + I enna
+ 0x00235d42, // n0x09e3 c0x0000 (---------------) + I fc
+ 0x0020c742, // n0x09e4 c0x0000 (---------------) + I fe
+ 0x002cc345, // n0x09e5 c0x0000 (---------------) + I fermo
+ 0x002e3b87, // n0x09e6 c0x0000 (---------------) + I ferrara
+ 0x00331442, // n0x09e7 c0x0000 (---------------) + I fg
+ 0x002099c2, // n0x09e8 c0x0000 (---------------) + I fi
+ 0x00237b87, // n0x09e9 c0x0000 (---------------) + I firenze
+ 0x0023c248, // n0x09ea c0x0000 (---------------) + I florence
+ 0x00358002, // n0x09eb c0x0000 (---------------) + I fm
+ 0x00200386, // n0x09ec c0x0000 (---------------) + I foggia
+ 0x002441cc, // n0x09ed c0x0000 (---------------) + I forli-cesena
+ 0x0024450b, // n0x09ee c0x0000 (---------------) + I forlicesena
+ 0x00200d42, // n0x09ef c0x0000 (---------------) + I fr
+ 0x002495cf, // n0x09f0 c0x0000 (---------------) + I friuli-v-giulia
+ 0x00249990, // n0x09f1 c0x0000 (---------------) + I friuli-ve-giulia
+ 0x00249d8f, // n0x09f2 c0x0000 (---------------) + I friuli-vegiulia
+ 0x0024a155, // n0x09f3 c0x0000 (---------------) + I friuli-venezia-giulia
+ 0x0024a694, // n0x09f4 c0x0000 (---------------) + I friuli-veneziagiulia
+ 0x0024ab8e, // n0x09f5 c0x0000 (---------------) + I friuli-vgiulia
+ 0x0024af0e, // n0x09f6 c0x0000 (---------------) + I friuliv-giulia
+ 0x0024b28f, // n0x09f7 c0x0000 (---------------) + I friulive-giulia
+ 0x0024b64e, // n0x09f8 c0x0000 (---------------) + I friulivegiulia
+ 0x0024b9d4, // n0x09f9 c0x0000 (---------------) + I friulivenezia-giulia
+ 0x0024bed3, // n0x09fa c0x0000 (---------------) + I friuliveneziagiulia
+ 0x0024c38d, // n0x09fb c0x0000 (---------------) + I friulivgiulia
+ 0x00263bc9, // n0x09fc c0x0000 (---------------) + I frosinone
+ 0x00277443, // n0x09fd c0x0000 (---------------) + I fvg
+ 0x002012c2, // n0x09fe c0x0000 (---------------) + I ge
+ 0x0030bb05, // n0x09ff c0x0000 (---------------) + I genoa
+ 0x002012c6, // n0x0a00 c0x0000 (---------------) + I genova
+ 0x00202342, // n0x0a01 c0x0000 (---------------) + I go
+ 0x002473c7, // n0x0a02 c0x0000 (---------------) + I gorizia
+ 0x0021e283, // n0x0a03 c0x0000 (---------------) + I gov
+ 0x0020dc82, // n0x0a04 c0x0000 (---------------) + I gr
+ 0x002e9508, // n0x0a05 c0x0000 (---------------) + I grosseto
+ 0x002d50d1, // n0x0a06 c0x0000 (---------------) + I iglesias-carbonia
+ 0x002d5510, // n0x0a07 c0x0000 (---------------) + I iglesiascarbonia
+ 0x00203582, // n0x0a08 c0x0000 (---------------) + I im
+ 0x002148c7, // n0x0a09 c0x0000 (---------------) + I imperia
+ 0x00202b42, // n0x0a0a c0x0000 (---------------) + I is
+ 0x002ee307, // n0x0a0b c0x0000 (---------------) + I isernia
+ 0x002034c2, // n0x0a0c c0x0000 (---------------) + I kr
+ 0x0036dc89, // n0x0a0d c0x0000 (---------------) + I la-spezia
+ 0x002d7947, // n0x0a0e c0x0000 (---------------) + I laquila
+ 0x00257b88, // n0x0a0f c0x0000 (---------------) + I laspezia
+ 0x0035dc46, // n0x0a10 c0x0000 (---------------) + I latina
+ 0x002c64c3, // n0x0a11 c0x0000 (---------------) + I laz
+ 0x0035a705, // n0x0a12 c0x0000 (---------------) + I lazio
+ 0x002339c2, // n0x0a13 c0x0000 (---------------) + I lc
+ 0x00203c42, // n0x0a14 c0x0000 (---------------) + I le
+ 0x00381ec5, // n0x0a15 c0x0000 (---------------) + I lecce
+ 0x0021e6c5, // n0x0a16 c0x0000 (---------------) + I lecco
+ 0x002008c2, // n0x0a17 c0x0000 (---------------) + I li
+ 0x0021a583, // n0x0a18 c0x0000 (---------------) + I lig
+ 0x00244887, // n0x0a19 c0x0000 (---------------) + I liguria
+ 0x003436c7, // n0x0a1a c0x0000 (---------------) + I livorno
+ 0x00205882, // n0x0a1b c0x0000 (---------------) + I lo
+ 0x002455c4, // n0x0a1c c0x0000 (---------------) + I lodi
+ 0x00260a43, // n0x0a1d c0x0000 (---------------) + I lom
+ 0x002b0b49, // n0x0a1e c0x0000 (---------------) + I lombardia
+ 0x002b8a88, // n0x0a1f c0x0000 (---------------) + I lombardy
+ 0x00205ec2, // n0x0a20 c0x0000 (---------------) + I lt
+ 0x002071c2, // n0x0a21 c0x0000 (---------------) + I lu
+ 0x00219187, // n0x0a22 c0x0000 (---------------) + I lucania
+ 0x0021aa05, // n0x0a23 c0x0000 (---------------) + I lucca
+ 0x00300248, // n0x0a24 c0x0000 (---------------) + I macerata
+ 0x0029c6c7, // n0x0a25 c0x0000 (---------------) + I mantova
+ 0x00216503, // n0x0a26 c0x0000 (---------------) + I mar
+ 0x0028de06, // n0x0a27 c0x0000 (---------------) + I marche
+ 0x0031870d, // n0x0a28 c0x0000 (---------------) + I massa-carrara
+ 0x00318a8c, // n0x0a29 c0x0000 (---------------) + I massacarrara
+ 0x0029f6c6, // n0x0a2a c0x0000 (---------------) + I matera
+ 0x00205942, // n0x0a2b c0x0000 (---------------) + I mb
+ 0x0023a6c2, // n0x0a2c c0x0000 (---------------) + I mc
+ 0x00208942, // n0x0a2d c0x0000 (---------------) + I me
+ 0x0022c48f, // n0x0a2e c0x0000 (---------------) + I medio-campidano
+ 0x0022c88e, // n0x0a2f c0x0000 (---------------) + I mediocampidano
+ 0x00291407, // n0x0a30 c0x0000 (---------------) + I messina
+ 0x00204802, // n0x0a31 c0x0000 (---------------) + I mi
+ 0x002f1e85, // n0x0a32 c0x0000 (---------------) + I milan
+ 0x002f1e86, // n0x0a33 c0x0000 (---------------) + I milano
+ 0x00217082, // n0x0a34 c0x0000 (---------------) + I mn
+ 0x00203602, // n0x0a35 c0x0000 (---------------) + I mo
+ 0x00278386, // n0x0a36 c0x0000 (---------------) + I modena
+ 0x00285643, // n0x0a37 c0x0000 (---------------) + I mol
+ 0x002ee246, // n0x0a38 c0x0000 (---------------) + I molise
+ 0x002b1e85, // n0x0a39 c0x0000 (---------------) + I monza
+ 0x002b1e8d, // n0x0a3a c0x0000 (---------------) + I monza-brianza
+ 0x002b26d5, // n0x0a3b c0x0000 (---------------) + I monza-e-della-brianza
+ 0x002b2e8c, // n0x0a3c c0x0000 (---------------) + I monzabrianza
+ 0x002b39cd, // n0x0a3d c0x0000 (---------------) + I monzaebrianza
+ 0x002b3d92, // n0x0a3e c0x0000 (---------------) + I monzaedellabrianza
+ 0x00209282, // n0x0a3f c0x0000 (---------------) + I ms
+ 0x00259642, // n0x0a40 c0x0000 (---------------) + I mt
+ 0x00200282, // n0x0a41 c0x0000 (---------------) + I na
+ 0x002b7886, // n0x0a42 c0x0000 (---------------) + I naples
+ 0x002d5c46, // n0x0a43 c0x0000 (---------------) + I napoli
+ 0x00200c02, // n0x0a44 c0x0000 (---------------) + I no
+ 0x00201346, // n0x0a45 c0x0000 (---------------) + I novara
+ 0x00205bc2, // n0x0a46 c0x0000 (---------------) + I nu
+ 0x00205bc5, // n0x0a47 c0x0000 (---------------) + I nuoro
+ 0x002003c2, // n0x0a48 c0x0000 (---------------) + I og
+ 0x002298c9, // n0x0a49 c0x0000 (---------------) + I ogliastra
+ 0x0022840c, // n0x0a4a c0x0000 (---------------) + I olbia-tempio
+ 0x0022874b, // n0x0a4b c0x0000 (---------------) + I olbiatempio
+ 0x00200c42, // n0x0a4c c0x0000 (---------------) + I or
+ 0x0023c688, // n0x0a4d c0x0000 (---------------) + I oristano
+ 0x002031c2, // n0x0a4e c0x0000 (---------------) + I ot
+ 0x002052c2, // n0x0a4f c0x0000 (---------------) + I pa
+ 0x0029a206, // n0x0a50 c0x0000 (---------------) + I padova
+ 0x0034f945, // n0x0a51 c0x0000 (---------------) + I padua
+ 0x0021ce87, // n0x0a52 c0x0000 (---------------) + I palermo
+ 0x003927c5, // n0x0a53 c0x0000 (---------------) + I parma
+ 0x002c0705, // n0x0a54 c0x0000 (---------------) + I pavia
+ 0x00203e02, // n0x0a55 c0x0000 (---------------) + I pc
+ 0x0029b282, // n0x0a56 c0x0000 (---------------) + I pd
+ 0x00214942, // n0x0a57 c0x0000 (---------------) + I pe
+ 0x0026fa47, // n0x0a58 c0x0000 (---------------) + I perugia
+ 0x0030758d, // n0x0a59 c0x0000 (---------------) + I pesaro-urbino
+ 0x0030790c, // n0x0a5a c0x0000 (---------------) + I pesarourbino
+ 0x00229107, // n0x0a5b c0x0000 (---------------) + I pescara
+ 0x002bf182, // n0x0a5c c0x0000 (---------------) + I pg
+ 0x00200b02, // n0x0a5d c0x0000 (---------------) + I pi
+ 0x00207788, // n0x0a5e c0x0000 (---------------) + I piacenza
+ 0x00261808, // n0x0a5f c0x0000 (---------------) + I piedmont
+ 0x002c1308, // n0x0a60 c0x0000 (---------------) + I piemonte
+ 0x002cd744, // n0x0a61 c0x0000 (---------------) + I pisa
+ 0x002c41c7, // n0x0a62 c0x0000 (---------------) + I pistoia
+ 0x002c7d43, // n0x0a63 c0x0000 (---------------) + I pmn
+ 0x0023df82, // n0x0a64 c0x0000 (---------------) + I pn
+ 0x00203f02, // n0x0a65 c0x0000 (---------------) + I po
+ 0x002c9b49, // n0x0a66 c0x0000 (---------------) + I pordenone
+ 0x00236a47, // n0x0a67 c0x0000 (---------------) + I potenza
+ 0x00218242, // n0x0a68 c0x0000 (---------------) + I pr
+ 0x00346185, // n0x0a69 c0x0000 (---------------) + I prato
+ 0x00295982, // n0x0a6a c0x0000 (---------------) + I pt
+ 0x00223542, // n0x0a6b c0x0000 (---------------) + I pu
+ 0x0025cd83, // n0x0a6c c0x0000 (---------------) + I pug
+ 0x0025cd86, // n0x0a6d c0x0000 (---------------) + I puglia
+ 0x002ce242, // n0x0a6e c0x0000 (---------------) + I pv
+ 0x002ce6c2, // n0x0a6f c0x0000 (---------------) + I pz
+ 0x00201442, // n0x0a70 c0x0000 (---------------) + I ra
+ 0x0038c106, // n0x0a71 c0x0000 (---------------) + I ragusa
+ 0x00201447, // n0x0a72 c0x0000 (---------------) + I ravenna
+ 0x00227b82, // n0x0a73 c0x0000 (---------------) + I rc
+ 0x002030c2, // n0x0a74 c0x0000 (---------------) + I re
+ 0x0023818f, // n0x0a75 c0x0000 (---------------) + I reggio-calabria
+ 0x0023f80d, // n0x0a76 c0x0000 (---------------) + I reggio-emilia
+ 0x0025df8e, // n0x0a77 c0x0000 (---------------) + I reggiocalabria
+ 0x00261c4c, // n0x0a78 c0x0000 (---------------) + I reggioemilia
+ 0x00204782, // n0x0a79 c0x0000 (---------------) + I rg
+ 0x00202842, // n0x0a7a c0x0000 (---------------) + I ri
+ 0x0021ba45, // n0x0a7b c0x0000 (---------------) + I rieti
+ 0x002e79c6, // n0x0a7c c0x0000 (---------------) + I rimini
+ 0x002194c2, // n0x0a7d c0x0000 (---------------) + I rm
+ 0x00202182, // n0x0a7e c0x0000 (---------------) + I rn
+ 0x00200d82, // n0x0a7f c0x0000 (---------------) + I ro
+ 0x0023fb84, // n0x0a80 c0x0000 (---------------) + I roma
+ 0x002e7004, // n0x0a81 c0x0000 (---------------) + I rome
+ 0x002b1386, // n0x0a82 c0x0000 (---------------) + I rovigo
+ 0x00201a02, // n0x0a83 c0x0000 (---------------) + I sa
+ 0x002671c7, // n0x0a84 c0x0000 (---------------) + I salerno
+ 0x00218483, // n0x0a85 c0x0000 (---------------) + I sar
+ 0x0021c048, // n0x0a86 c0x0000 (---------------) + I sardegna
+ 0x0021ca08, // n0x0a87 c0x0000 (---------------) + I sardinia
+ 0x00369a07, // n0x0a88 c0x0000 (---------------) + I sassari
+ 0x00232a46, // n0x0a89 c0x0000 (---------------) + I savona
+ 0x00209182, // n0x0a8a c0x0000 (---------------) + I si
+ 0x00215803, // n0x0a8b c0x0000 (---------------) + I sic
+ 0x00215807, // n0x0a8c c0x0000 (---------------) + I sicilia
+ 0x0026b646, // n0x0a8d c0x0000 (---------------) + I sicily
+ 0x002b77c5, // n0x0a8e c0x0000 (---------------) + I siena
+ 0x002d0d88, // n0x0a8f c0x0000 (---------------) + I siracusa
+ 0x00201102, // n0x0a90 c0x0000 (---------------) + I so
+ 0x0033ca87, // n0x0a91 c0x0000 (---------------) + I sondrio
+ 0x002101c2, // n0x0a92 c0x0000 (---------------) + I sp
+ 0x002ceec2, // n0x0a93 c0x0000 (---------------) + I sr
+ 0x00211f02, // n0x0a94 c0x0000 (---------------) + I ss
+ 0x002b5a09, // n0x0a95 c0x0000 (---------------) + I suedtirol
+ 0x0021d0c2, // n0x0a96 c0x0000 (---------------) + I sv
+ 0x00200142, // n0x0a97 c0x0000 (---------------) + I ta
+ 0x00229383, // n0x0a98 c0x0000 (---------------) + I taa
+ 0x0036ec87, // n0x0a99 c0x0000 (---------------) + I taranto
+ 0x00203202, // n0x0a9a c0x0000 (---------------) + I te
+ 0x0022858c, // n0x0a9b c0x0000 (---------------) + I tempio-olbia
+ 0x0022888b, // n0x0a9c c0x0000 (---------------) + I tempioolbia
+ 0x0029f746, // n0x0a9d c0x0000 (---------------) + I teramo
+ 0x002f9245, // n0x0a9e c0x0000 (---------------) + I terni
+ 0x0021d1c2, // n0x0a9f c0x0000 (---------------) + I tn
+ 0x00201682, // n0x0aa0 c0x0000 (---------------) + I to
+ 0x0029f3c6, // n0x0aa1 c0x0000 (---------------) + I torino
+ 0x002520c3, // n0x0aa2 c0x0000 (---------------) + I tos
+ 0x003096c7, // n0x0aa3 c0x0000 (---------------) + I toscana
+ 0x00285142, // n0x0aa4 c0x0000 (---------------) + I tp
+ 0x00202402, // n0x0aa5 c0x0000 (---------------) + I tr
+ 0x00264a15, // n0x0aa6 c0x0000 (---------------) + I trani-andria-barletta
+ 0x0022fc55, // n0x0aa7 c0x0000 (---------------) + I trani-barletta-andria
+ 0x0026bb53, // n0x0aa8 c0x0000 (---------------) + I traniandriabarletta
+ 0x00230193, // n0x0aa9 c0x0000 (---------------) + I tranibarlettaandria
+ 0x0027cbc7, // n0x0aaa c0x0000 (---------------) + I trapani
+ 0x0028cbc8, // n0x0aab c0x0000 (---------------) + I trentino
+ 0x0028cbd0, // n0x0aac c0x0000 (---------------) + I trentino-a-adige
+ 0x0029458f, // n0x0aad c0x0000 (---------------) + I trentino-aadige
+ 0x002d3213, // n0x0aae c0x0000 (---------------) + I trentino-alto-adige
+ 0x00306652, // n0x0aaf c0x0000 (---------------) + I trentino-altoadige
+ 0x0033be90, // n0x0ab0 c0x0000 (---------------) + I trentino-s-tirol
+ 0x0033ac8f, // n0x0ab1 c0x0000 (---------------) + I trentino-stirol
+ 0x0038cd52, // n0x0ab2 c0x0000 (---------------) + I trentino-sud-tirol
+ 0x002a4691, // n0x0ab3 c0x0000 (---------------) + I trentino-sudtirol
+ 0x002acf53, // n0x0ab4 c0x0000 (---------------) + I trentino-sued-tirol
+ 0x002b57d2, // n0x0ab5 c0x0000 (---------------) + I trentino-suedtirol
+ 0x002bc54f, // n0x0ab6 c0x0000 (---------------) + I trentinoa-adige
+ 0x002bd2ce, // n0x0ab7 c0x0000 (---------------) + I trentinoaadige
+ 0x002dccd2, // n0x0ab8 c0x0000 (---------------) + I trentinoalto-adige
+ 0x002c0b91, // n0x0ab9 c0x0000 (---------------) + I trentinoaltoadige
+ 0x002cb48f, // n0x0aba c0x0000 (---------------) + I trentinos-tirol
+ 0x002cc70e, // n0x0abb c0x0000 (---------------) + I trentinostirol
+ 0x002cda91, // n0x0abc c0x0000 (---------------) + I trentinosud-tirol
+ 0x002ce2d0, // n0x0abd c0x0000 (---------------) + I trentinosudtirol
+ 0x002cea52, // n0x0abe c0x0000 (---------------) + I trentinosued-tirol
+ 0x002dde91, // n0x0abf c0x0000 (---------------) + I trentinosuedtirol
+ 0x002d0946, // n0x0ac0 c0x0000 (---------------) + I trento
+ 0x002de4c7, // n0x0ac1 c0x0000 (---------------) + I treviso
+ 0x00354007, // n0x0ac2 c0x0000 (---------------) + I trieste
+ 0x00203a02, // n0x0ac3 c0x0000 (---------------) + I ts
+ 0x002af6c5, // n0x0ac4 c0x0000 (---------------) + I turin
+ 0x002d8c87, // n0x0ac5 c0x0000 (---------------) + I tuscany
+ 0x0020bf42, // n0x0ac6 c0x0000 (---------------) + I tv
+ 0x002070c2, // n0x0ac7 c0x0000 (---------------) + I ud
+ 0x00319045, // n0x0ac8 c0x0000 (---------------) + I udine
+ 0x00215fc3, // n0x0ac9 c0x0000 (---------------) + I umb
+ 0x002570c6, // n0x0aca c0x0000 (---------------) + I umbria
+ 0x0030774d, // n0x0acb c0x0000 (---------------) + I urbino-pesaro
+ 0x00307a8c, // n0x0acc c0x0000 (---------------) + I urbinopesaro
+ 0x002013c2, // n0x0acd c0x0000 (---------------) + I va
+ 0x00341c8b, // n0x0ace c0x0000 (---------------) + I val-d-aosta
+ 0x0029a30a, // n0x0acf c0x0000 (---------------) + I val-daosta
+ 0x0030ae0a, // n0x0ad0 c0x0000 (---------------) + I vald-aosta
+ 0x0029e789, // n0x0ad1 c0x0000 (---------------) + I valdaosta
+ 0x0029c80b, // n0x0ad2 c0x0000 (---------------) + I valle-aosta
+ 0x002c904d, // n0x0ad3 c0x0000 (---------------) + I valle-d-aosta
+ 0x002d958c, // n0x0ad4 c0x0000 (---------------) + I valle-daosta
+ 0x0021860a, // n0x0ad5 c0x0000 (---------------) + I valleaosta
+ 0x00244e8c, // n0x0ad6 c0x0000 (---------------) + I valled-aosta
+ 0x0024cc8b, // n0x0ad7 c0x0000 (---------------) + I valledaosta
+ 0x002584cc, // n0x0ad8 c0x0000 (---------------) + I vallee-aoste
+ 0x0025be8b, // n0x0ad9 c0x0000 (---------------) + I valleeaoste
+ 0x00263943, // n0x0ada c0x0000 (---------------) + I vao
+ 0x00278f86, // n0x0adb c0x0000 (---------------) + I varese
+ 0x0030b742, // n0x0adc c0x0000 (---------------) + I vb
+ 0x0034a542, // n0x0add c0x0000 (---------------) + I vc
+ 0x00211603, // n0x0ade c0x0000 (---------------) + I vda
+ 0x002014c2, // n0x0adf c0x0000 (---------------) + I ve
+ 0x002014c3, // n0x0ae0 c0x0000 (---------------) + I ven
+ 0x003546c6, // n0x0ae1 c0x0000 (---------------) + I veneto
+ 0x0024a307, // n0x0ae2 c0x0000 (---------------) + I venezia
+ 0x0025c506, // n0x0ae3 c0x0000 (---------------) + I venice
+ 0x00220fc8, // n0x0ae4 c0x0000 (---------------) + I verbania
+ 0x002a5e88, // n0x0ae5 c0x0000 (---------------) + I vercelli
+ 0x00248346, // n0x0ae6 c0x0000 (---------------) + I verona
+ 0x00213602, // n0x0ae7 c0x0000 (---------------) + I vi
+ 0x002db38d, // n0x0ae8 c0x0000 (---------------) + I vibo-valentia
+ 0x002db6cc, // n0x0ae9 c0x0000 (---------------) + I vibovalentia
+ 0x00357a87, // n0x0aea c0x0000 (---------------) + I vicenza
+ 0x002de2c7, // n0x0aeb c0x0000 (---------------) + I viterbo
+ 0x00211782, // n0x0aec c0x0000 (---------------) + I vr
+ 0x0020bf82, // n0x0aed c0x0000 (---------------) + I vs
+ 0x0021e302, // n0x0aee c0x0000 (---------------) + I vt
+ 0x0024ebc2, // n0x0aef c0x0000 (---------------) + I vv
+ 0x00200742, // n0x0af0 c0x0000 (---------------) + I co
+ 0x002170c3, // n0x0af1 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0af2 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x0af3 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x0af4 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x0af5 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x0af6 c0x0000 (---------------) + I mil
+ 0x00298944, // n0x0af7 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x0af8 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x0af9 c0x0000 (---------------) + I org
+ 0x00206103, // n0x0afa c0x0000 (---------------) + I sch
+ 0x00201e82, // n0x0afb c0x0000 (---------------) + I ac
+ 0x00210a02, // n0x0afc c0x0000 (---------------) + I ad
+ 0x1fa34b45, // n0x0afd c0x007e (n0x0b6a-n0x0b9e) + I aichi
+ 0x1fe06e05, // n0x0afe c0x007f (n0x0b9e-n0x0bba) + I akita
+ 0x20387ac6, // n0x0aff c0x0080 (n0x0bba-n0x0bd0) + I aomori
+ 0x000e4188, // n0x0b00 c0x0000 (---------------) + blogspot
+ 0x206a0745, // n0x0b01 c0x0081 (n0x0bd0-n0x0c0a) + I chiba
+ 0x00200742, // n0x0b02 c0x0000 (---------------) + I co
+ 0x00203fc2, // n0x0b03 c0x0000 (---------------) + I ed
+ 0x20b1c945, // n0x0b04 c0x0082 (n0x0c0a-n0x0c20) + I ehime
+ 0x20e6b7c5, // n0x0b05 c0x0083 (n0x0c20-n0x0c2f) + I fukui
+ 0x2126c587, // n0x0b06 c0x0084 (n0x0c2f-n0x0c6e) + I fukuoka
+ 0x2166d5c9, // n0x0b07 c0x0085 (n0x0c6e-n0x0ca1) + I fukushima
+ 0x21a89804, // n0x0b08 c0x0086 (n0x0ca1-n0x0cc7) + I gifu
+ 0x00202342, // n0x0b09 c0x0000 (---------------) + I go
+ 0x0020dc82, // n0x0b0a c0x0000 (---------------) + I gr
+ 0x21e36f85, // n0x0b0b c0x0087 (n0x0cc7-n0x0ceb) + I gunma
+ 0x2227c649, // n0x0b0c c0x0088 (n0x0ceb-n0x0d04) + I hiroshima
+ 0x2275fc48, // n0x0b0d c0x0089 (n0x0d04-n0x0d92) + I hokkaido
+ 0x22a96e45, // n0x0b0e c0x008a (n0x0d92-n0x0dc0) + I hyogo
+ 0x22f16ac7, // n0x0b0f c0x008b (n0x0dc0-n0x0df3) + I ibaraki
+ 0x23214e48, // n0x0b10 c0x008c (n0x0df3-n0x0e06) + I ishikawa
+ 0x236c3b85, // n0x0b11 c0x008d (n0x0e06-n0x0e28) + I iwate
+ 0x23a81f06, // n0x0b12 c0x008e (n0x0e28-n0x0e37) + I kagawa
+ 0x23e71789, // n0x0b13 c0x008f (n0x0e37-n0x0e4b) + I kagoshima
+ 0x24332808, // n0x0b14 c0x0090 (n0x0e4b-n0x0e69) + I kanagawa
+ 0x246a8748, // n0x0b15 c0x0091 (n0x0e69-n0x0e6a)* o I kawasaki
+ 0x24a8294a, // n0x0b16 c0x0092 (n0x0e6a-n0x0e6b)* o I kitakyushu
+ 0x24e43484, // n0x0b17 c0x0093 (n0x0e6b-n0x0e6c)* o I kobe
+ 0x252ba445, // n0x0b18 c0x0094 (n0x0e6c-n0x0e8b) + I kochi
+ 0x256a2d48, // n0x0b19 c0x0095 (n0x0e8b-n0x0ea5) + I kumamoto
+ 0x25aacb05, // n0x0b1a c0x0096 (n0x0ea5-n0x0ec4) + I kyoto
+ 0x0020e4c2, // n0x0b1b c0x0000 (---------------) + I lg
+ 0x25e28143, // n0x0b1c c0x0097 (n0x0ec4-n0x0ee2) + I mie
+ 0x2628d746, // n0x0b1d c0x0098 (n0x0ee2-n0x0f03) + I miyagi
+ 0x26771208, // n0x0b1e c0x0099 (n0x0f03-n0x0f1e) + I miyazaki
+ 0x26b4e4c6, // n0x0b1f c0x009a (n0x0f1e-n0x0f69) + I nagano
+ 0x26f6c108, // n0x0b20 c0x009b (n0x0f69-n0x0f7f) + I nagasaki
+ 0x2732b206, // n0x0b21 c0x009c (n0x0f7f-n0x0f80)* o I nagoya
+ 0x276da0c4, // n0x0b22 c0x009d (n0x0f80-n0x0fa6) + I nara
+ 0x00201082, // n0x0b23 c0x0000 (---------------) + I ne
+ 0x27a5f907, // n0x0b24 c0x009e (n0x0fa6-n0x0fc8) + I niigata
+ 0x27e98d04, // n0x0b25 c0x009f (n0x0fc8-n0x0fdb) + I oita
+ 0x28266607, // n0x0b26 c0x00a0 (n0x0fdb-n0x0ff5) + I okayama
+ 0x287954c7, // n0x0b27 c0x00a1 (n0x0ff5-n0x101f) + I okinawa
+ 0x00200c42, // n0x0b28 c0x0000 (---------------) + I or
+ 0x28a864c5, // n0x0b29 c0x00a2 (n0x101f-n0x1051) + I osaka
+ 0x28e75104, // n0x0b2a c0x00a3 (n0x1051-n0x106b) + I saga
+ 0x292d0f07, // n0x0b2b c0x00a4 (n0x106b-n0x10b0) + I saitama
+ 0x296129c7, // n0x0b2c c0x00a5 (n0x10b0-n0x10b1)* o I sapporo
+ 0x29a6f286, // n0x0b2d c0x00a6 (n0x10b1-n0x10b2)* o I sendai
+ 0x29e525c5, // n0x0b2e c0x00a7 (n0x10b2-n0x10c9) + I shiga
+ 0x2a281087, // n0x0b2f c0x00a8 (n0x10c9-n0x10e0) + I shimane
+ 0x2a6b4b88, // n0x0b30 c0x00a9 (n0x10e0-n0x1104) + I shizuoka
+ 0x2ab0f5c7, // n0x0b31 c0x00aa (n0x1104-n0x1123) + I tochigi
+ 0x2ae8e2c9, // n0x0b32 c0x00ab (n0x1123-n0x1134) + I tokushima
+ 0x2b316545, // n0x0b33 c0x00ac (n0x1134-n0x116d) + I tokyo
+ 0x2b6e2787, // n0x0b34 c0x00ad (n0x116d-n0x117a) + I tottori
+ 0x2ba7c346, // n0x0b35 c0x00ae (n0x117a-n0x1192) + I toyama
+ 0x2bf2dc48, // n0x0b36 c0x00af (n0x1192-n0x11af) + I wakayama
+ 0x0022528d, // n0x0b37 c0x0000 (---------------) + I xn--0trq7p7nn
+ 0x00260809, // n0x0b38 c0x0000 (---------------) + I xn--1ctwo
+ 0x0026d84b, // n0x0b39 c0x0000 (---------------) + I xn--1lqs03n
+ 0x002910cb, // n0x0b3a c0x0000 (---------------) + I xn--1lqs71d
+ 0x002b1bcb, // n0x0b3b c0x0000 (---------------) + I xn--2m4a15e
+ 0x002d6b8b, // n0x0b3c c0x0000 (---------------) + I xn--32vp30h
+ 0x002e7bcb, // n0x0b3d c0x0000 (---------------) + I xn--4it168d
+ 0x002e7e8b, // n0x0b3e c0x0000 (---------------) + I xn--4it797k
+ 0x002e8689, // n0x0b3f c0x0000 (---------------) + I xn--4pvxs
+ 0x002ea38b, // n0x0b40 c0x0000 (---------------) + I xn--5js045d
+ 0x002ea64b, // n0x0b41 c0x0000 (---------------) + I xn--5rtp49c
+ 0x002ead0b, // n0x0b42 c0x0000 (---------------) + I xn--5rtq34k
+ 0x002eb3ca, // n0x0b43 c0x0000 (---------------) + I xn--6btw5a
+ 0x002eb90a, // n0x0b44 c0x0000 (---------------) + I xn--6orx2r
+ 0x002ebf0c, // n0x0b45 c0x0000 (---------------) + I xn--7t0a264c
+ 0x002f16cb, // n0x0b46 c0x0000 (---------------) + I xn--8ltr62k
+ 0x002f200a, // n0x0b47 c0x0000 (---------------) + I xn--8pvr4u
+ 0x0030374a, // n0x0b48 c0x0000 (---------------) + I xn--c3s14m
+ 0x0031334e, // n0x0b49 c0x0000 (---------------) + I xn--d5qv7z876c
+ 0x00313d4e, // n0x0b4a c0x0000 (---------------) + I xn--djrs72d6uy
+ 0x003140ca, // n0x0b4b c0x0000 (---------------) + I xn--djty4k
+ 0x003153ca, // n0x0b4c c0x0000 (---------------) + I xn--efvn9s
+ 0x0031700b, // n0x0b4d c0x0000 (---------------) + I xn--ehqz56n
+ 0x003172cb, // n0x0b4e c0x0000 (---------------) + I xn--elqq16h
+ 0x00317fcb, // n0x0b4f c0x0000 (---------------) + I xn--f6qx53a
+ 0x00333bcb, // n0x0b50 c0x0000 (---------------) + I xn--k7yn95e
+ 0x003341ca, // n0x0b51 c0x0000 (---------------) + I xn--kbrq7o
+ 0x00334e8b, // n0x0b52 c0x0000 (---------------) + I xn--klt787d
+ 0x0033514a, // n0x0b53 c0x0000 (---------------) + I xn--kltp7d
+ 0x003353ca, // n0x0b54 c0x0000 (---------------) + I xn--kltx9a
+ 0x0033564a, // n0x0b55 c0x0000 (---------------) + I xn--klty5x
+ 0x00357c4b, // n0x0b56 c0x0000 (---------------) + I xn--mkru45i
+ 0x0035d08b, // n0x0b57 c0x0000 (---------------) + I xn--nit225k
+ 0x0036094e, // n0x0b58 c0x0000 (---------------) + I xn--ntso0iqx3a
+ 0x00360ccb, // n0x0b59 c0x0000 (---------------) + I xn--ntsq17g
+ 0x003687cb, // n0x0b5a c0x0000 (---------------) + I xn--pssu33l
+ 0x0036a44b, // n0x0b5b c0x0000 (---------------) + I xn--qqqt11m
+ 0x003725ca, // n0x0b5c c0x0000 (---------------) + I xn--rht27z
+ 0x00372849, // n0x0b5d c0x0000 (---------------) + I xn--rht3d
+ 0x00372a8a, // n0x0b5e c0x0000 (---------------) + I xn--rht61e
+ 0x0037410a, // n0x0b5f c0x0000 (---------------) + I xn--rny31h
+ 0x0038548b, // n0x0b60 c0x0000 (---------------) + I xn--tor131o
+ 0x00386f0b, // n0x0b61 c0x0000 (---------------) + I xn--uist22h
+ 0x0038744a, // n0x0b62 c0x0000 (---------------) + I xn--uisz3g
+ 0x0038824b, // n0x0b63 c0x0000 (---------------) + I xn--uuwu58a
+ 0x0038d1cb, // n0x0b64 c0x0000 (---------------) + I xn--vgu402c
+ 0x00395d8b, // n0x0b65 c0x0000 (---------------) + I xn--zbx025d
+ 0x2c26dc08, // n0x0b66 c0x00b0 (n0x11af-n0x11d1) + I yamagata
+ 0x2c676089, // n0x0b67 c0x00b1 (n0x11d1-n0x11e1) + I yamaguchi
+ 0x2ca9b889, // n0x0b68 c0x00b2 (n0x11e1-n0x11fd) + I yamanashi
+ 0x2cf2b5c8, // n0x0b69 c0x00b3 (n0x11fd-n0x11fe)* o I yokohama
+ 0x002a2345, // n0x0b6a c0x0000 (---------------) + I aisai
+ 0x002068c3, // n0x0b6b c0x0000 (---------------) + I ama
+ 0x00203884, // n0x0b6c c0x0000 (---------------) + I anjo
+ 0x0034cb85, // n0x0b6d c0x0000 (---------------) + I asuke
+ 0x00289546, // n0x0b6e c0x0000 (---------------) + I chiryu
+ 0x002932c5, // n0x0b6f c0x0000 (---------------) + I chita
+ 0x00274d84, // n0x0b70 c0x0000 (---------------) + I fuso
+ 0x002472c8, // n0x0b71 c0x0000 (---------------) + I gamagori
+ 0x00242045, // n0x0b72 c0x0000 (---------------) + I handa
+ 0x0027ed44, // n0x0b73 c0x0000 (---------------) + I hazu
+ 0x002b2387, // n0x0b74 c0x0000 (---------------) + I hekinan
+ 0x00288d0a, // n0x0b75 c0x0000 (---------------) + I higashiura
+ 0x002a580a, // n0x0b76 c0x0000 (---------------) + I ichinomiya
+ 0x0031f587, // n0x0b77 c0x0000 (---------------) + I inazawa
+ 0x002067c7, // n0x0b78 c0x0000 (---------------) + I inuyama
+ 0x002d6707, // n0x0b79 c0x0000 (---------------) + I isshiki
+ 0x00248887, // n0x0b7a c0x0000 (---------------) + I iwakura
+ 0x00203cc5, // n0x0b7b c0x0000 (---------------) + I kanie
+ 0x00311ec6, // n0x0b7c c0x0000 (---------------) + I kariya
+ 0x0027cf47, // n0x0b7d c0x0000 (---------------) + I kasugai
+ 0x0025a504, // n0x0b7e c0x0000 (---------------) + I kira
+ 0x002fe646, // n0x0b7f c0x0000 (---------------) + I kiyosu
+ 0x002977c6, // n0x0b80 c0x0000 (---------------) + I komaki
+ 0x00204cc5, // n0x0b81 c0x0000 (---------------) + I konan
+ 0x0024c9c4, // n0x0b82 c0x0000 (---------------) + I kota
+ 0x00292a46, // n0x0b83 c0x0000 (---------------) + I mihama
+ 0x00288107, // n0x0b84 c0x0000 (---------------) + I miyoshi
+ 0x002209c6, // n0x0b85 c0x0000 (---------------) + I nishio
+ 0x002e6987, // n0x0b86 c0x0000 (---------------) + I nisshin
+ 0x0027e743, // n0x0b87 c0x0000 (---------------) + I obu
+ 0x0023be46, // n0x0b88 c0x0000 (---------------) + I oguchi
+ 0x0024f085, // n0x0b89 c0x0000 (---------------) + I oharu
+ 0x0026c687, // n0x0b8a c0x0000 (---------------) + I okazaki
+ 0x002ad48a, // n0x0b8b c0x0000 (---------------) + I owariasahi
+ 0x002e9604, // n0x0b8c c0x0000 (---------------) + I seto
+ 0x00213308, // n0x0b8d c0x0000 (---------------) + I shikatsu
+ 0x0028d309, // n0x0b8e c0x0000 (---------------) + I shinshiro
+ 0x002a4307, // n0x0b8f c0x0000 (---------------) + I shitara
+ 0x0026a586, // n0x0b90 c0x0000 (---------------) + I tahara
+ 0x00248648, // n0x0b91 c0x0000 (---------------) + I takahama
+ 0x0030be89, // n0x0b92 c0x0000 (---------------) + I tobishima
+ 0x00366704, // n0x0b93 c0x0000 (---------------) + I toei
+ 0x0025d104, // n0x0b94 c0x0000 (---------------) + I togo
+ 0x002df1c5, // n0x0b95 c0x0000 (---------------) + I tokai
+ 0x00376b08, // n0x0b96 c0x0000 (---------------) + I tokoname
+ 0x002aeb47, // n0x0b97 c0x0000 (---------------) + I toyoake
+ 0x00286c89, // n0x0b98 c0x0000 (---------------) + I toyohashi
+ 0x00322bc8, // n0x0b99 c0x0000 (---------------) + I toyokawa
+ 0x00221246, // n0x0b9a c0x0000 (---------------) + I toyone
+ 0x00338906, // n0x0b9b c0x0000 (---------------) + I toyota
+ 0x00283308, // n0x0b9c c0x0000 (---------------) + I tsushima
+ 0x00359906, // n0x0b9d c0x0000 (---------------) + I yatomi
+ 0x00206e05, // n0x0b9e c0x0000 (---------------) + I akita
+ 0x0026f346, // n0x0b9f c0x0000 (---------------) + I daisen
+ 0x00266b48, // n0x0ba0 c0x0000 (---------------) + I fujisato
+ 0x002dc286, // n0x0ba1 c0x0000 (---------------) + I gojome
+ 0x0024608b, // n0x0ba2 c0x0000 (---------------) + I hachirogata
+ 0x00279ac6, // n0x0ba3 c0x0000 (---------------) + I happou
+ 0x00284b0d, // n0x0ba4 c0x0000 (---------------) + I higashinaruse
+ 0x002028c5, // n0x0ba5 c0x0000 (---------------) + I honjo
+ 0x00292e46, // n0x0ba6 c0x0000 (---------------) + I honjyo
+ 0x00214f05, // n0x0ba7 c0x0000 (---------------) + I ikawa
+ 0x00281889, // n0x0ba8 c0x0000 (---------------) + I kamikoani
+ 0x0022ea47, // n0x0ba9 c0x0000 (---------------) + I kamioka
+ 0x0033ecc8, // n0x0baa c0x0000 (---------------) + I katagami
+ 0x0022bc46, // n0x0bab c0x0000 (---------------) + I kazuno
+ 0x00283c89, // n0x0bac c0x0000 (---------------) + I kitaakita
+ 0x0036b9c6, // n0x0bad c0x0000 (---------------) + I kosaka
+ 0x002ad405, // n0x0bae c0x0000 (---------------) + I kyowa
+ 0x0028b246, // n0x0baf c0x0000 (---------------) + I misato
+ 0x002bf786, // n0x0bb0 c0x0000 (---------------) + I mitane
+ 0x002b5c49, // n0x0bb1 c0x0000 (---------------) + I moriyoshi
+ 0x00226806, // n0x0bb2 c0x0000 (---------------) + I nikaho
+ 0x0023a007, // n0x0bb3 c0x0000 (---------------) + I noshiro
+ 0x0027bd85, // n0x0bb4 c0x0000 (---------------) + I odate
+ 0x00204043, // n0x0bb5 c0x0000 (---------------) + I oga
+ 0x00246205, // n0x0bb6 c0x0000 (---------------) + I ogata
+ 0x00293cc7, // n0x0bb7 c0x0000 (---------------) + I semboku
+ 0x0023ed06, // n0x0bb8 c0x0000 (---------------) + I yokote
+ 0x002027c9, // n0x0bb9 c0x0000 (---------------) + I yurihonjo
+ 0x00387ac6, // n0x0bba c0x0000 (---------------) + I aomori
+ 0x0026f586, // n0x0bbb c0x0000 (---------------) + I gonohe
+ 0x0022e109, // n0x0bbc c0x0000 (---------------) + I hachinohe
+ 0x0026ed49, // n0x0bbd c0x0000 (---------------) + I hashikami
+ 0x0028bc07, // n0x0bbe c0x0000 (---------------) + I hiranai
+ 0x00305a08, // n0x0bbf c0x0000 (---------------) + I hirosaki
+ 0x00273909, // n0x0bc0 c0x0000 (---------------) + I itayanagi
+ 0x0026ce88, // n0x0bc1 c0x0000 (---------------) + I kuroishi
+ 0x0035c906, // n0x0bc2 c0x0000 (---------------) + I misawa
+ 0x002bda45, // n0x0bc3 c0x0000 (---------------) + I mutsu
+ 0x0021638a, // n0x0bc4 c0x0000 (---------------) + I nakadomari
+ 0x0026f606, // n0x0bc5 c0x0000 (---------------) + I noheji
+ 0x0035e1c6, // n0x0bc6 c0x0000 (---------------) + I oirase
+ 0x0028d905, // n0x0bc7 c0x0000 (---------------) + I owani
+ 0x00205c88, // n0x0bc8 c0x0000 (---------------) + I rokunohe
+ 0x002092c7, // n0x0bc9 c0x0000 (---------------) + I sannohe
+ 0x0029ba0a, // n0x0bca c0x0000 (---------------) + I shichinohe
+ 0x00238986, // n0x0bcb c0x0000 (---------------) + I shingo
+ 0x0024cec5, // n0x0bcc c0x0000 (---------------) + I takko
+ 0x0023e846, // n0x0bcd c0x0000 (---------------) + I towada
+ 0x002e1a47, // n0x0bce c0x0000 (---------------) + I tsugaru
+ 0x0026a447, // n0x0bcf c0x0000 (---------------) + I tsuruta
+ 0x002041c5, // n0x0bd0 c0x0000 (---------------) + I abiko
+ 0x002ad5c5, // n0x0bd1 c0x0000 (---------------) + I asahi
+ 0x002c6646, // n0x0bd2 c0x0000 (---------------) + I chonan
+ 0x002c6e86, // n0x0bd3 c0x0000 (---------------) + I chosei
+ 0x0034a586, // n0x0bd4 c0x0000 (---------------) + I choshi
+ 0x002be904, // n0x0bd5 c0x0000 (---------------) + I chuo
+ 0x0026e2c9, // n0x0bd6 c0x0000 (---------------) + I funabashi
+ 0x00276f46, // n0x0bd7 c0x0000 (---------------) + I futtsu
+ 0x002d6f4a, // n0x0bd8 c0x0000 (---------------) + I hanamigawa
+ 0x0027df48, // n0x0bd9 c0x0000 (---------------) + I ichihara
+ 0x00312b88, // n0x0bda c0x0000 (---------------) + I ichikawa
+ 0x002a580a, // n0x0bdb c0x0000 (---------------) + I ichinomiya
+ 0x00329b05, // n0x0bdc c0x0000 (---------------) + I inzai
+ 0x00288045, // n0x0bdd c0x0000 (---------------) + I isumi
+ 0x00269408, // n0x0bde c0x0000 (---------------) + I kamagaya
+ 0x002ba1c8, // n0x0bdf c0x0000 (---------------) + I kamogawa
+ 0x00375c87, // n0x0be0 c0x0000 (---------------) + I kashiwa
+ 0x00335e46, // n0x0be1 c0x0000 (---------------) + I katori
+ 0x002fd408, // n0x0be2 c0x0000 (---------------) + I katsuura
+ 0x0027d607, // n0x0be3 c0x0000 (---------------) + I kimitsu
+ 0x0026c7c8, // n0x0be4 c0x0000 (---------------) + I kisarazu
+ 0x0035d306, // n0x0be5 c0x0000 (---------------) + I kozaki
+ 0x0026ff48, // n0x0be6 c0x0000 (---------------) + I kujukuri
+ 0x0027e846, // n0x0be7 c0x0000 (---------------) + I kyonan
+ 0x0023b8c7, // n0x0be8 c0x0000 (---------------) + I matsudo
+ 0x002846c6, // n0x0be9 c0x0000 (---------------) + I midori
+ 0x00292a46, // n0x0bea c0x0000 (---------------) + I mihama
+ 0x00213fca, // n0x0beb c0x0000 (---------------) + I minamiboso
+ 0x00223146, // n0x0bec c0x0000 (---------------) + I mobara
+ 0x002bda49, // n0x0bed c0x0000 (---------------) + I mutsuzawa
+ 0x0032a1c6, // n0x0bee c0x0000 (---------------) + I nagara
+ 0x002a0a8a, // n0x0bef c0x0000 (---------------) + I nagareyama
+ 0x002da0c9, // n0x0bf0 c0x0000 (---------------) + I narashino
+ 0x0025ac46, // n0x0bf1 c0x0000 (---------------) + I narita
+ 0x00384f84, // n0x0bf2 c0x0000 (---------------) + I noda
+ 0x0030bbcd, // n0x0bf3 c0x0000 (---------------) + I oamishirasato
+ 0x00276307, // n0x0bf4 c0x0000 (---------------) + I omigawa
+ 0x002fffc6, // n0x0bf5 c0x0000 (---------------) + I onjuku
+ 0x002a8605, // n0x0bf6 c0x0000 (---------------) + I otaki
+ 0x0036ba45, // n0x0bf7 c0x0000 (---------------) + I sakae
+ 0x002f4d46, // n0x0bf8 c0x0000 (---------------) + I sakura
+ 0x002a73c9, // n0x0bf9 c0x0000 (---------------) + I shimofusa
+ 0x002b9447, // n0x0bfa c0x0000 (---------------) + I shirako
+ 0x00268406, // n0x0bfb c0x0000 (---------------) + I shiroi
+ 0x0029fd06, // n0x0bfc c0x0000 (---------------) + I shisui
+ 0x00274e09, // n0x0bfd c0x0000 (---------------) + I sodegaura
+ 0x0022a904, // n0x0bfe c0x0000 (---------------) + I sosa
+ 0x0021f304, // n0x0bff c0x0000 (---------------) + I tako
+ 0x0038a708, // n0x0c00 c0x0000 (---------------) + I tateyama
+ 0x002e5fc6, // n0x0c01 c0x0000 (---------------) + I togane
+ 0x00340e08, // n0x0c02 c0x0000 (---------------) + I tohnosho
+ 0x003163c8, // n0x0c03 c0x0000 (---------------) + I tomisato
+ 0x00206f47, // n0x0c04 c0x0000 (---------------) + I urayasu
+ 0x00382d89, // n0x0c05 c0x0000 (---------------) + I yachimata
+ 0x002e38c7, // n0x0c06 c0x0000 (---------------) + I yachiyo
+ 0x002a060a, // n0x0c07 c0x0000 (---------------) + I yokaichiba
+ 0x00315d8f, // n0x0c08 c0x0000 (---------------) + I yokoshibahikari
+ 0x002394ca, // n0x0c09 c0x0000 (---------------) + I yotsukaido
+ 0x0021b845, // n0x0c0a c0x0000 (---------------) + I ainan
+ 0x00266d85, // n0x0c0b c0x0000 (---------------) + I honai
+ 0x0020f345, // n0x0c0c c0x0000 (---------------) + I ikata
+ 0x0027eac7, // n0x0c0d c0x0000 (---------------) + I imabari
+ 0x00253503, // n0x0c0e c0x0000 (---------------) + I iyo
+ 0x00305c08, // n0x0c0f c0x0000 (---------------) + I kamijima
+ 0x0037aec6, // n0x0c10 c0x0000 (---------------) + I kihoku
+ 0x0037afc9, // n0x0c11 c0x0000 (---------------) + I kumakogen
+ 0x002e5286, // n0x0c12 c0x0000 (---------------) + I masaki
+ 0x00392e07, // n0x0c13 c0x0000 (---------------) + I matsuno
+ 0x00283a49, // n0x0c14 c0x0000 (---------------) + I matsuyama
+ 0x0033ebc8, // n0x0c15 c0x0000 (---------------) + I namikata
+ 0x0028d9c7, // n0x0c16 c0x0000 (---------------) + I niihama
+ 0x002e3dc3, // n0x0c17 c0x0000 (---------------) + I ozu
+ 0x002a23c5, // n0x0c18 c0x0000 (---------------) + I saijo
+ 0x00315cc5, // n0x0c19 c0x0000 (---------------) + I seiyo
+ 0x002be74b, // n0x0c1a c0x0000 (---------------) + I shikokuchuo
+ 0x002acbc4, // n0x0c1b c0x0000 (---------------) + I tobe
+ 0x002d1504, // n0x0c1c c0x0000 (---------------) + I toon
+ 0x00265706, // n0x0c1d c0x0000 (---------------) + I uchiko
+ 0x002e4647, // n0x0c1e c0x0000 (---------------) + I uwajima
+ 0x003804ca, // n0x0c1f c0x0000 (---------------) + I yawatahama
+ 0x002281c7, // n0x0c20 c0x0000 (---------------) + I echizen
+ 0x00366787, // n0x0c21 c0x0000 (---------------) + I eiheiji
+ 0x0026b7c5, // n0x0c22 c0x0000 (---------------) + I fukui
+ 0x00390e45, // n0x0c23 c0x0000 (---------------) + I ikeda
+ 0x00260149, // n0x0c24 c0x0000 (---------------) + I katsuyama
+ 0x00292a46, // n0x0c25 c0x0000 (---------------) + I mihama
+ 0x0022804d, // n0x0c26 c0x0000 (---------------) + I minamiechizen
+ 0x00395885, // n0x0c27 c0x0000 (---------------) + I obama
+ 0x00285a43, // n0x0c28 c0x0000 (---------------) + I ohi
+ 0x00207e83, // n0x0c29 c0x0000 (---------------) + I ono
+ 0x003404c5, // n0x0c2a c0x0000 (---------------) + I sabae
+ 0x003396c5, // n0x0c2b c0x0000 (---------------) + I sakai
+ 0x00248648, // n0x0c2c c0x0000 (---------------) + I takahama
+ 0x00277007, // n0x0c2d c0x0000 (---------------) + I tsuruga
+ 0x0021f086, // n0x0c2e c0x0000 (---------------) + I wakasa
+ 0x00289c86, // n0x0c2f c0x0000 (---------------) + I ashiya
+ 0x0021de85, // n0x0c30 c0x0000 (---------------) + I buzen
+ 0x00375807, // n0x0c31 c0x0000 (---------------) + I chikugo
+ 0x00206a47, // n0x0c32 c0x0000 (---------------) + I chikuho
+ 0x002a5bc7, // n0x0c33 c0x0000 (---------------) + I chikujo
+ 0x002ba4ca, // n0x0c34 c0x0000 (---------------) + I chikushino
+ 0x0023bf08, // n0x0c35 c0x0000 (---------------) + I chikuzen
+ 0x002be904, // n0x0c36 c0x0000 (---------------) + I chuo
+ 0x0024ed47, // n0x0c37 c0x0000 (---------------) + I dazaifu
+ 0x0026ae87, // n0x0c38 c0x0000 (---------------) + I fukuchi
+ 0x00317546, // n0x0c39 c0x0000 (---------------) + I hakata
+ 0x002545c7, // n0x0c3a c0x0000 (---------------) + I higashi
+ 0x002c5b48, // n0x0c3b c0x0000 (---------------) + I hirokawa
+ 0x0029b788, // n0x0c3c c0x0000 (---------------) + I hisayama
+ 0x00246d86, // n0x0c3d c0x0000 (---------------) + I iizuka
+ 0x0023abc8, // n0x0c3e c0x0000 (---------------) + I inatsuki
+ 0x00226884, // n0x0c3f c0x0000 (---------------) + I kaho
+ 0x0027cf46, // n0x0c40 c0x0000 (---------------) + I kasuga
+ 0x0020d3c6, // n0x0c41 c0x0000 (---------------) + I kasuya
+ 0x003824c6, // n0x0c42 c0x0000 (---------------) + I kawara
+ 0x00295dc6, // n0x0c43 c0x0000 (---------------) + I keisen
+ 0x00232c84, // n0x0c44 c0x0000 (---------------) + I koga
+ 0x00248946, // n0x0c45 c0x0000 (---------------) + I kurate
+ 0x002a67c6, // n0x0c46 c0x0000 (---------------) + I kurogi
+ 0x00282e46, // n0x0c47 c0x0000 (---------------) + I kurume
+ 0x00213fc6, // n0x0c48 c0x0000 (---------------) + I minami
+ 0x00207d46, // n0x0c49 c0x0000 (---------------) + I miyako
+ 0x00272346, // n0x0c4a c0x0000 (---------------) + I miyama
+ 0x0021ef88, // n0x0c4b c0x0000 (---------------) + I miyawaka
+ 0x002fe4c8, // n0x0c4c c0x0000 (---------------) + I mizumaki
+ 0x002bb0c8, // n0x0c4d c0x0000 (---------------) + I munakata
+ 0x002934c8, // n0x0c4e c0x0000 (---------------) + I nakagawa
+ 0x00269386, // n0x0c4f c0x0000 (---------------) + I nakama
+ 0x00208b85, // n0x0c50 c0x0000 (---------------) + I nishi
+ 0x0031b486, // n0x0c51 c0x0000 (---------------) + I nogata
+ 0x00296ec5, // n0x0c52 c0x0000 (---------------) + I ogori
+ 0x0035c047, // n0x0c53 c0x0000 (---------------) + I okagaki
+ 0x0022ecc5, // n0x0c54 c0x0000 (---------------) + I okawa
+ 0x00201bc3, // n0x0c55 c0x0000 (---------------) + I oki
+ 0x00200085, // n0x0c56 c0x0000 (---------------) + I omuta
+ 0x002c7844, // n0x0c57 c0x0000 (---------------) + I onga
+ 0x00207e85, // n0x0c58 c0x0000 (---------------) + I onojo
+ 0x0020fdc3, // n0x0c59 c0x0000 (---------------) + I oto
+ 0x002d47c7, // n0x0c5a c0x0000 (---------------) + I saigawa
+ 0x00333848, // n0x0c5b c0x0000 (---------------) + I sasaguri
+ 0x002e6a46, // n0x0c5c c0x0000 (---------------) + I shingu
+ 0x0029230d, // n0x0c5d c0x0000 (---------------) + I shinyoshitomi
+ 0x00266d46, // n0x0c5e c0x0000 (---------------) + I shonai
+ 0x002823c5, // n0x0c5f c0x0000 (---------------) + I soeda
+ 0x002ad183, // n0x0c60 c0x0000 (---------------) + I sue
+ 0x002a2189, // n0x0c61 c0x0000 (---------------) + I tachiarai
+ 0x0020e146, // n0x0c62 c0x0000 (---------------) + I tagawa
+ 0x002344c6, // n0x0c63 c0x0000 (---------------) + I takata
+ 0x00253644, // n0x0c64 c0x0000 (---------------) + I toho
+ 0x00239447, // n0x0c65 c0x0000 (---------------) + I toyotsu
+ 0x00227786, // n0x0c66 c0x0000 (---------------) + I tsuiki
+ 0x00289985, // n0x0c67 c0x0000 (---------------) + I ukiha
+ 0x00209803, // n0x0c68 c0x0000 (---------------) + I umi
+ 0x00211e04, // n0x0c69 c0x0000 (---------------) + I usui
+ 0x0026b046, // n0x0c6a c0x0000 (---------------) + I yamada
+ 0x002d81c4, // n0x0c6b c0x0000 (---------------) + I yame
+ 0x002f7448, // n0x0c6c c0x0000 (---------------) + I yanagawa
+ 0x0038f4c9, // n0x0c6d c0x0000 (---------------) + I yukuhashi
+ 0x0022ef49, // n0x0c6e c0x0000 (---------------) + I aizubange
+ 0x0028b14a, // n0x0c6f c0x0000 (---------------) + I aizumisato
+ 0x0023124d, // n0x0c70 c0x0000 (---------------) + I aizuwakamatsu
+ 0x00240607, // n0x0c71 c0x0000 (---------------) + I asakawa
+ 0x00255706, // n0x0c72 c0x0000 (---------------) + I bandai
+ 0x00209004, // n0x0c73 c0x0000 (---------------) + I date
+ 0x0026d5c9, // n0x0c74 c0x0000 (---------------) + I fukushima
+ 0x00274788, // n0x0c75 c0x0000 (---------------) + I furudono
+ 0x00275f06, // n0x0c76 c0x0000 (---------------) + I futaba
+ 0x002e9cc6, // n0x0c77 c0x0000 (---------------) + I hanawa
+ 0x002545c7, // n0x0c78 c0x0000 (---------------) + I higashi
+ 0x0029cdc6, // n0x0c79 c0x0000 (---------------) + I hirata
+ 0x0021af86, // n0x0c7a c0x0000 (---------------) + I hirono
+ 0x00329c06, // n0x0c7b c0x0000 (---------------) + I iitate
+ 0x0039554a, // n0x0c7c c0x0000 (---------------) + I inawashiro
+ 0x00214e48, // n0x0c7d c0x0000 (---------------) + I ishikawa
+ 0x002999c5, // n0x0c7e c0x0000 (---------------) + I iwaki
+ 0x00254a09, // n0x0c7f c0x0000 (---------------) + I izumizaki
+ 0x002af2ca, // n0x0c80 c0x0000 (---------------) + I kagamiishi
+ 0x002150c8, // n0x0c81 c0x0000 (---------------) + I kaneyama
+ 0x00287448, // n0x0c82 c0x0000 (---------------) + I kawamata
+ 0x002815c8, // n0x0c83 c0x0000 (---------------) + I kitakata
+ 0x00293e8c, // n0x0c84 c0x0000 (---------------) + I kitashiobara
+ 0x002c3e45, // n0x0c85 c0x0000 (---------------) + I koori
+ 0x00289f08, // n0x0c86 c0x0000 (---------------) + I koriyama
+ 0x002f1d86, // n0x0c87 c0x0000 (---------------) + I kunimi
+ 0x002d1906, // n0x0c88 c0x0000 (---------------) + I miharu
+ 0x00392cc7, // n0x0c89 c0x0000 (---------------) + I mishima
+ 0x002280c5, // n0x0c8a c0x0000 (---------------) + I namie
+ 0x0026f4c5, // n0x0c8b c0x0000 (---------------) + I nango
+ 0x0022ee09, // n0x0c8c c0x0000 (---------------) + I nishiaizu
+ 0x0020bd87, // n0x0c8d c0x0000 (---------------) + I nishigo
+ 0x0037af85, // n0x0c8e c0x0000 (---------------) + I okuma
+ 0x0021e147, // n0x0c8f c0x0000 (---------------) + I omotego
+ 0x00207e83, // n0x0c90 c0x0000 (---------------) + I ono
+ 0x002af985, // n0x0c91 c0x0000 (---------------) + I otama
+ 0x00355688, // n0x0c92 c0x0000 (---------------) + I samegawa
+ 0x0026e047, // n0x0c93 c0x0000 (---------------) + I shimogo
+ 0x00287309, // n0x0c94 c0x0000 (---------------) + I shirakawa
+ 0x002b3885, // n0x0c95 c0x0000 (---------------) + I showa
+ 0x0038e104, // n0x0c96 c0x0000 (---------------) + I soma
+ 0x0028c348, // n0x0c97 c0x0000 (---------------) + I sukagawa
+ 0x0025fa47, // n0x0c98 c0x0000 (---------------) + I taishin
+ 0x0028db88, // n0x0c99 c0x0000 (---------------) + I tamakawa
+ 0x003109c8, // n0x0c9a c0x0000 (---------------) + I tanagura
+ 0x0034d1c5, // n0x0c9b c0x0000 (---------------) + I tenei
+ 0x00389946, // n0x0c9c c0x0000 (---------------) + I yabuki
+ 0x0022afc6, // n0x0c9d c0x0000 (---------------) + I yamato
+ 0x00346949, // n0x0c9e c0x0000 (---------------) + I yamatsuri
+ 0x002fdc47, // n0x0c9f c0x0000 (---------------) + I yanaizu
+ 0x00297546, // n0x0ca0 c0x0000 (---------------) + I yugawa
+ 0x00383347, // n0x0ca1 c0x0000 (---------------) + I anpachi
+ 0x00244403, // n0x0ca2 c0x0000 (---------------) + I ena
+ 0x00289804, // n0x0ca3 c0x0000 (---------------) + I gifu
+ 0x003746c5, // n0x0ca4 c0x0000 (---------------) + I ginan
+ 0x0024efc4, // n0x0ca5 c0x0000 (---------------) + I godo
+ 0x00232644, // n0x0ca6 c0x0000 (---------------) + I gujo
+ 0x0023ca07, // n0x0ca7 c0x0000 (---------------) + I hashima
+ 0x002e5907, // n0x0ca8 c0x0000 (---------------) + I hichiso
+ 0x002685c4, // n0x0ca9 c0x0000 (---------------) + I hida
+ 0x00287150, // n0x0caa c0x0000 (---------------) + I higashishirakawa
+ 0x002f4ec7, // n0x0cab c0x0000 (---------------) + I ibigawa
+ 0x00390e45, // n0x0cac c0x0000 (---------------) + I ikeda
+ 0x00271acc, // n0x0cad c0x0000 (---------------) + I kakamigahara
+ 0x00203cc4, // n0x0cae c0x0000 (---------------) + I kani
+ 0x0028f7c8, // n0x0caf c0x0000 (---------------) + I kasahara
+ 0x0023b7c9, // n0x0cb0 c0x0000 (---------------) + I kasamatsu
+ 0x00298ac6, // n0x0cb1 c0x0000 (---------------) + I kawaue
+ 0x0022aa08, // n0x0cb2 c0x0000 (---------------) + I kitagata
+ 0x0022ec04, // n0x0cb3 c0x0000 (---------------) + I mino
+ 0x002fb148, // n0x0cb4 c0x0000 (---------------) + I minokamo
+ 0x002adf86, // n0x0cb5 c0x0000 (---------------) + I mitake
+ 0x00252288, // n0x0cb6 c0x0000 (---------------) + I mizunami
+ 0x00289146, // n0x0cb7 c0x0000 (---------------) + I motosu
+ 0x0021d20b, // n0x0cb8 c0x0000 (---------------) + I nakatsugawa
+ 0x00394b85, // n0x0cb9 c0x0000 (---------------) + I ogaki
+ 0x002b5248, // n0x0cba c0x0000 (---------------) + I sakahogi
+ 0x0020d984, // n0x0cbb c0x0000 (---------------) + I seki
+ 0x0038f90a, // n0x0cbc c0x0000 (---------------) + I sekigahara
+ 0x00287309, // n0x0cbd c0x0000 (---------------) + I shirakawa
+ 0x0024f946, // n0x0cbe c0x0000 (---------------) + I tajimi
+ 0x0020f408, // n0x0cbf c0x0000 (---------------) + I takayama
+ 0x002b9085, // n0x0cc0 c0x0000 (---------------) + I tarui
+ 0x002add84, // n0x0cc1 c0x0000 (---------------) + I toki
+ 0x0028f6c6, // n0x0cc2 c0x0000 (---------------) + I tomika
+ 0x002e55c8, // n0x0cc3 c0x0000 (---------------) + I wanouchi
+ 0x0026dc08, // n0x0cc4 c0x0000 (---------------) + I yamagata
+ 0x00330546, // n0x0cc5 c0x0000 (---------------) + I yaotsu
+ 0x00396e84, // n0x0cc6 c0x0000 (---------------) + I yoro
+ 0x00216306, // n0x0cc7 c0x0000 (---------------) + I annaka
+ 0x002e3947, // n0x0cc8 c0x0000 (---------------) + I chiyoda
+ 0x00266507, // n0x0cc9 c0x0000 (---------------) + I fujioka
+ 0x002545cf, // n0x0cca c0x0000 (---------------) + I higashiagatsuma
+ 0x0037a987, // n0x0ccb c0x0000 (---------------) + I isesaki
+ 0x0025ad07, // n0x0ccc c0x0000 (---------------) + I itakura
+ 0x002d17c5, // n0x0ccd c0x0000 (---------------) + I kanna
+ 0x002cc145, // n0x0cce c0x0000 (---------------) + I kanra
+ 0x0028b8c9, // n0x0ccf c0x0000 (---------------) + I katashina
+ 0x00343106, // n0x0cd0 c0x0000 (---------------) + I kawaba
+ 0x00258d45, // n0x0cd1 c0x0000 (---------------) + I kiryu
+ 0x0026f047, // n0x0cd2 c0x0000 (---------------) + I kusatsu
+ 0x002cb088, // n0x0cd3 c0x0000 (---------------) + I maebashi
+ 0x002cae05, // n0x0cd4 c0x0000 (---------------) + I meiwa
+ 0x002846c6, // n0x0cd5 c0x0000 (---------------) + I midori
+ 0x00204808, // n0x0cd6 c0x0000 (---------------) + I minakami
+ 0x0034e4ca, // n0x0cd7 c0x0000 (---------------) + I naganohara
+ 0x00364808, // n0x0cd8 c0x0000 (---------------) + I nakanojo
+ 0x00278107, // n0x0cd9 c0x0000 (---------------) + I nanmoku
+ 0x003161c6, // n0x0cda c0x0000 (---------------) + I numata
+ 0x002549c6, // n0x0cdb c0x0000 (---------------) + I oizumi
+ 0x0020ca43, // n0x0cdc c0x0000 (---------------) + I ora
+ 0x00203943, // n0x0cdd c0x0000 (---------------) + I ota
+ 0x0034a649, // n0x0cde c0x0000 (---------------) + I shibukawa
+ 0x00273789, // n0x0cdf c0x0000 (---------------) + I shimonita
+ 0x0028e1c6, // n0x0ce0 c0x0000 (---------------) + I shinto
+ 0x002b3885, // n0x0ce1 c0x0000 (---------------) + I showa
+ 0x00298d88, // n0x0ce2 c0x0000 (---------------) + I takasaki
+ 0x0020f408, // n0x0ce3 c0x0000 (---------------) + I takayama
+ 0x003936c8, // n0x0ce4 c0x0000 (---------------) + I tamamura
+ 0x00329c8b, // n0x0ce5 c0x0000 (---------------) + I tatebayashi
+ 0x00292547, // n0x0ce6 c0x0000 (---------------) + I tomioka
+ 0x0030b489, // n0x0ce7 c0x0000 (---------------) + I tsukiyono
+ 0x00254848, // n0x0ce8 c0x0000 (---------------) + I tsumagoi
+ 0x0038dac4, // n0x0ce9 c0x0000 (---------------) + I ueno
+ 0x002b5d48, // n0x0cea c0x0000 (---------------) + I yoshioka
+ 0x0027a909, // n0x0ceb c0x0000 (---------------) + I asaminami
+ 0x002557c5, // n0x0cec c0x0000 (---------------) + I daiwa
+ 0x0020dd07, // n0x0ced c0x0000 (---------------) + I etajima
+ 0x002ab685, // n0x0cee c0x0000 (---------------) + I fuchu
+ 0x0026db08, // n0x0cef c0x0000 (---------------) + I fukuyama
+ 0x0027dd8b, // n0x0cf0 c0x0000 (---------------) + I hatsukaichi
+ 0x00280dd0, // n0x0cf1 c0x0000 (---------------) + I higashihiroshima
+ 0x00292c45, // n0x0cf2 c0x0000 (---------------) + I hongo
+ 0x0020d8cc, // n0x0cf3 c0x0000 (---------------) + I jinsekikogen
+ 0x0021f245, // n0x0cf4 c0x0000 (---------------) + I kaita
+ 0x0026b843, // n0x0cf5 c0x0000 (---------------) + I kui
+ 0x00285906, // n0x0cf6 c0x0000 (---------------) + I kumano
+ 0x002a4544, // n0x0cf7 c0x0000 (---------------) + I kure
+ 0x003637c6, // n0x0cf8 c0x0000 (---------------) + I mihara
+ 0x00288107, // n0x0cf9 c0x0000 (---------------) + I miyoshi
+ 0x00204884, // n0x0cfa c0x0000 (---------------) + I naka
+ 0x002a5708, // n0x0cfb c0x0000 (---------------) + I onomichi
+ 0x00305acd, // n0x0cfc c0x0000 (---------------) + I osakikamijima
+ 0x00335c85, // n0x0cfd c0x0000 (---------------) + I otake
+ 0x00201a04, // n0x0cfe c0x0000 (---------------) + I saka
+ 0x0021b584, // n0x0cff c0x0000 (---------------) + I sera
+ 0x00325cc9, // n0x0d00 c0x0000 (---------------) + I seranishi
+ 0x0027f9c8, // n0x0d01 c0x0000 (---------------) + I shinichi
+ 0x002f5a47, // n0x0d02 c0x0000 (---------------) + I shobara
+ 0x002ae008, // n0x0d03 c0x0000 (---------------) + I takehara
+ 0x0026e388, // n0x0d04 c0x0000 (---------------) + I abashiri
+ 0x00268705, // n0x0d05 c0x0000 (---------------) + I abira
+ 0x0031acc7, // n0x0d06 c0x0000 (---------------) + I aibetsu
+ 0x00268687, // n0x0d07 c0x0000 (---------------) + I akabira
+ 0x00300f87, // n0x0d08 c0x0000 (---------------) + I akkeshi
+ 0x002ad5c9, // n0x0d09 c0x0000 (---------------) + I asahikawa
+ 0x00227609, // n0x0d0a c0x0000 (---------------) + I ashibetsu
+ 0x00230d06, // n0x0d0b c0x0000 (---------------) + I ashoro
+ 0x00318dc6, // n0x0d0c c0x0000 (---------------) + I assabu
+ 0x00254806, // n0x0d0d c0x0000 (---------------) + I atsuma
+ 0x00323c05, // n0x0d0e c0x0000 (---------------) + I bibai
+ 0x002626c4, // n0x0d0f c0x0000 (---------------) + I biei
+ 0x0038aec6, // n0x0d10 c0x0000 (---------------) + I bifuka
+ 0x0038f286, // n0x0d11 c0x0000 (---------------) + I bihoro
+ 0x00268748, // n0x0d12 c0x0000 (---------------) + I biratori
+ 0x002e170b, // n0x0d13 c0x0000 (---------------) + I chippubetsu
+ 0x00366547, // n0x0d14 c0x0000 (---------------) + I chitose
+ 0x00209004, // n0x0d15 c0x0000 (---------------) + I date
+ 0x0032d2c6, // n0x0d16 c0x0000 (---------------) + I ebetsu
+ 0x00337807, // n0x0d17 c0x0000 (---------------) + I embetsu
+ 0x00271f45, // n0x0d18 c0x0000 (---------------) + I eniwa
+ 0x002f35c5, // n0x0d19 c0x0000 (---------------) + I erimo
+ 0x00202244, // n0x0d1a c0x0000 (---------------) + I esan
+ 0x00227586, // n0x0d1b c0x0000 (---------------) + I esashi
+ 0x0038af48, // n0x0d1c c0x0000 (---------------) + I fukagawa
+ 0x0026d5c9, // n0x0d1d c0x0000 (---------------) + I fukushima
+ 0x00239f06, // n0x0d1e c0x0000 (---------------) + I furano
+ 0x00273e08, // n0x0d1f c0x0000 (---------------) + I furubira
+ 0x002e3146, // n0x0d20 c0x0000 (---------------) + I haboro
+ 0x00325388, // n0x0d21 c0x0000 (---------------) + I hakodate
+ 0x002a154c, // n0x0d22 c0x0000 (---------------) + I hamatonbetsu
+ 0x002685c6, // n0x0d23 c0x0000 (---------------) + I hidaka
+ 0x0028208d, // n0x0d24 c0x0000 (---------------) + I higashikagura
+ 0x0028250b, // n0x0d25 c0x0000 (---------------) + I higashikawa
+ 0x0023a0c5, // n0x0d26 c0x0000 (---------------) + I hiroo
+ 0x00206b87, // n0x0d27 c0x0000 (---------------) + I hokuryu
+ 0x00226906, // n0x0d28 c0x0000 (---------------) + I hokuto
+ 0x00305188, // n0x0d29 c0x0000 (---------------) + I honbetsu
+ 0x00230d89, // n0x0d2a c0x0000 (---------------) + I horokanai
+ 0x00346588, // n0x0d2b c0x0000 (---------------) + I horonobe
+ 0x00390e45, // n0x0d2c c0x0000 (---------------) + I ikeda
+ 0x0020de07, // n0x0d2d c0x0000 (---------------) + I imakane
+ 0x002af448, // n0x0d2e c0x0000 (---------------) + I ishikari
+ 0x0023e509, // n0x0d2f c0x0000 (---------------) + I iwamizawa
+ 0x002e74c6, // n0x0d30 c0x0000 (---------------) + I iwanai
+ 0x00239e0a, // n0x0d31 c0x0000 (---------------) + I kamifurano
+ 0x00304f08, // n0x0d32 c0x0000 (---------------) + I kamikawa
+ 0x003463cb, // n0x0d33 c0x0000 (---------------) + I kamishihoro
+ 0x002ae7cc, // n0x0d34 c0x0000 (---------------) + I kamisunagawa
+ 0x002fb248, // n0x0d35 c0x0000 (---------------) + I kamoenai
+ 0x00269e06, // n0x0d36 c0x0000 (---------------) + I kayabe
+ 0x002a5a88, // n0x0d37 c0x0000 (---------------) + I kembuchi
+ 0x0035e007, // n0x0d38 c0x0000 (---------------) + I kikonai
+ 0x002e5389, // n0x0d39 c0x0000 (---------------) + I kimobetsu
+ 0x0027c54d, // n0x0d3a c0x0000 (---------------) + I kitahiroshima
+ 0x002885c6, // n0x0d3b c0x0000 (---------------) + I kitami
+ 0x002534c8, // n0x0d3c c0x0000 (---------------) + I kiyosato
+ 0x002fe389, // n0x0d3d c0x0000 (---------------) + I koshimizu
+ 0x002a3148, // n0x0d3e c0x0000 (---------------) + I kunneppu
+ 0x00270048, // n0x0d3f c0x0000 (---------------) + I kuriyama
+ 0x002a7e8c, // n0x0d40 c0x0000 (---------------) + I kuromatsunai
+ 0x002a9347, // n0x0d41 c0x0000 (---------------) + I kushiro
+ 0x002a9e47, // n0x0d42 c0x0000 (---------------) + I kutchan
+ 0x002ad405, // n0x0d43 c0x0000 (---------------) + I kyowa
+ 0x00260307, // n0x0d44 c0x0000 (---------------) + I mashike
+ 0x002caf48, // n0x0d45 c0x0000 (---------------) + I matsumae
+ 0x0028f746, // n0x0d46 c0x0000 (---------------) + I mikasa
+ 0x003561cc, // n0x0d47 c0x0000 (---------------) + I minamifurano
+ 0x002ccb48, // n0x0d48 c0x0000 (---------------) + I mombetsu
+ 0x002b7108, // n0x0d49 c0x0000 (---------------) + I moseushi
+ 0x0024e806, // n0x0d4a c0x0000 (---------------) + I mukawa
+ 0x002a6187, // n0x0d4b c0x0000 (---------------) + I muroran
+ 0x00230f04, // n0x0d4c c0x0000 (---------------) + I naie
+ 0x002934c8, // n0x0d4d c0x0000 (---------------) + I nakagawa
+ 0x0026208c, // n0x0d4e c0x0000 (---------------) + I nakasatsunai
+ 0x0027848c, // n0x0d4f c0x0000 (---------------) + I nakatombetsu
+ 0x0021b8c5, // n0x0d50 c0x0000 (---------------) + I nanae
+ 0x0037c587, // n0x0d51 c0x0000 (---------------) + I nanporo
+ 0x00396e06, // n0x0d52 c0x0000 (---------------) + I nayoro
+ 0x002a6106, // n0x0d53 c0x0000 (---------------) + I nemuro
+ 0x00281a48, // n0x0d54 c0x0000 (---------------) + I niikappu
+ 0x0037ae44, // n0x0d55 c0x0000 (---------------) + I niki
+ 0x002209cb, // n0x0d56 c0x0000 (---------------) + I nishiokoppe
+ 0x002f090b, // n0x0d57 c0x0000 (---------------) + I noboribetsu
+ 0x003161c6, // n0x0d58 c0x0000 (---------------) + I numata
+ 0x00305947, // n0x0d59 c0x0000 (---------------) + I obihiro
+ 0x00311445, // n0x0d5a c0x0000 (---------------) + I obira
+ 0x0025d045, // n0x0d5b c0x0000 (---------------) + I oketo
+ 0x00220b06, // n0x0d5c c0x0000 (---------------) + I okoppe
+ 0x002b9045, // n0x0d5d c0x0000 (---------------) + I otaru
+ 0x002acb85, // n0x0d5e c0x0000 (---------------) + I otobe
+ 0x002c4a07, // n0x0d5f c0x0000 (---------------) + I otofuke
+ 0x0025cbc9, // n0x0d60 c0x0000 (---------------) + I otoineppu
+ 0x002f3c44, // n0x0d61 c0x0000 (---------------) + I oumu
+ 0x0035f085, // n0x0d62 c0x0000 (---------------) + I ozora
+ 0x002c2c45, // n0x0d63 c0x0000 (---------------) + I pippu
+ 0x0028eb48, // n0x0d64 c0x0000 (---------------) + I rankoshi
+ 0x002a5545, // n0x0d65 c0x0000 (---------------) + I rebun
+ 0x002b4809, // n0x0d66 c0x0000 (---------------) + I rikubetsu
+ 0x00294c47, // n0x0d67 c0x0000 (---------------) + I rishiri
+ 0x00294c4b, // n0x0d68 c0x0000 (---------------) + I rishirifuji
+ 0x00307c86, // n0x0d69 c0x0000 (---------------) + I saroma
+ 0x0021ecc9, // n0x0d6a c0x0000 (---------------) + I sarufutsu
+ 0x0024c908, // n0x0d6b c0x0000 (---------------) + I shakotan
+ 0x00252d45, // n0x0d6c c0x0000 (---------------) + I shari
+ 0x00301088, // n0x0d6d c0x0000 (---------------) + I shibecha
+ 0x00227648, // n0x0d6e c0x0000 (---------------) + I shibetsu
+ 0x00251507, // n0x0d6f c0x0000 (---------------) + I shikabe
+ 0x002d3e87, // n0x0d70 c0x0000 (---------------) + I shikaoi
+ 0x0023ca89, // n0x0d71 c0x0000 (---------------) + I shimamaki
+ 0x002521c7, // n0x0d72 c0x0000 (---------------) + I shimizu
+ 0x00266909, // n0x0d73 c0x0000 (---------------) + I shimokawa
+ 0x00287a0c, // n0x0d74 c0x0000 (---------------) + I shinshinotsu
+ 0x0028e1c8, // n0x0d75 c0x0000 (---------------) + I shintoku
+ 0x002d1609, // n0x0d76 c0x0000 (---------------) + I shiranuka
+ 0x002d2247, // n0x0d77 c0x0000 (---------------) + I shiraoi
+ 0x0026e449, // n0x0d78 c0x0000 (---------------) + I shiriuchi
+ 0x002e5a47, // n0x0d79 c0x0000 (---------------) + I sobetsu
+ 0x002ae8c8, // n0x0d7a c0x0000 (---------------) + I sunagawa
+ 0x0028ee85, // n0x0d7b c0x0000 (---------------) + I taiki
+ 0x0027cec6, // n0x0d7c c0x0000 (---------------) + I takasu
+ 0x002a8648, // n0x0d7d c0x0000 (---------------) + I takikawa
+ 0x00245108, // n0x0d7e c0x0000 (---------------) + I takinoue
+ 0x002af189, // n0x0d7f c0x0000 (---------------) + I teshikaga
+ 0x002acbc7, // n0x0d80 c0x0000 (---------------) + I tobetsu
+ 0x0028b345, // n0x0d81 c0x0000 (---------------) + I tohma
+ 0x002b9dc9, // n0x0d82 c0x0000 (---------------) + I tomakomai
+ 0x0036edc6, // n0x0d83 c0x0000 (---------------) + I tomari
+ 0x0027c344, // n0x0d84 c0x0000 (---------------) + I toya
+ 0x00346246, // n0x0d85 c0x0000 (---------------) + I toyako
+ 0x00371088, // n0x0d86 c0x0000 (---------------) + I toyotomi
+ 0x00244007, // n0x0d87 c0x0000 (---------------) + I toyoura
+ 0x002e1908, // n0x0d88 c0x0000 (---------------) + I tsubetsu
+ 0x0023ac89, // n0x0d89 c0x0000 (---------------) + I tsukigata
+ 0x0024e587, // n0x0d8a c0x0000 (---------------) + I urakawa
+ 0x00288ec6, // n0x0d8b c0x0000 (---------------) + I urausu
+ 0x00206c44, // n0x0d8c c0x0000 (---------------) + I uryu
+ 0x00200109, // n0x0d8d c0x0000 (---------------) + I utashinai
+ 0x00342f08, // n0x0d8e c0x0000 (---------------) + I wakkanai
+ 0x0024e6c7, // n0x0d8f c0x0000 (---------------) + I wassamu
+ 0x00311fc6, // n0x0d90 c0x0000 (---------------) + I yakumo
+ 0x00292f46, // n0x0d91 c0x0000 (---------------) + I yoichi
+ 0x0035e144, // n0x0d92 c0x0000 (---------------) + I aioi
+ 0x00341846, // n0x0d93 c0x0000 (---------------) + I akashi
+ 0x00207e03, // n0x0d94 c0x0000 (---------------) + I ako
+ 0x0038bd09, // n0x0d95 c0x0000 (---------------) + I amagasaki
+ 0x00394b46, // n0x0d96 c0x0000 (---------------) + I aogaki
+ 0x0028c7c5, // n0x0d97 c0x0000 (---------------) + I asago
+ 0x00289c86, // n0x0d98 c0x0000 (---------------) + I ashiya
+ 0x0028dcc5, // n0x0d99 c0x0000 (---------------) + I awaji
+ 0x0026d348, // n0x0d9a c0x0000 (---------------) + I fukusaki
+ 0x002faf87, // n0x0d9b c0x0000 (---------------) + I goshiki
+ 0x002f8c46, // n0x0d9c c0x0000 (---------------) + I harima
+ 0x0031c986, // n0x0d9d c0x0000 (---------------) + I himeji
+ 0x00312b88, // n0x0d9e c0x0000 (---------------) + I ichikawa
+ 0x0028ba47, // n0x0d9f c0x0000 (---------------) + I inagawa
+ 0x00288605, // n0x0da0 c0x0000 (---------------) + I itami
+ 0x0028a188, // n0x0da1 c0x0000 (---------------) + I kakogawa
+ 0x003744c8, // n0x0da2 c0x0000 (---------------) + I kamigori
+ 0x00304f08, // n0x0da3 c0x0000 (---------------) + I kamikawa
+ 0x0021f105, // n0x0da4 c0x0000 (---------------) + I kasai
+ 0x0027cf46, // n0x0da5 c0x0000 (---------------) + I kasuga
+ 0x0022ed09, // n0x0da6 c0x0000 (---------------) + I kawanishi
+ 0x0022ae44, // n0x0da7 c0x0000 (---------------) + I miki
+ 0x00359a0b, // n0x0da8 c0x0000 (---------------) + I minamiawaji
+ 0x0021ac8b, // n0x0da9 c0x0000 (---------------) + I nishinomiya
+ 0x002998c9, // n0x0daa c0x0000 (---------------) + I nishiwaki
+ 0x00207e83, // n0x0dab c0x0000 (---------------) + I ono
+ 0x002453c5, // n0x0dac c0x0000 (---------------) + I sanda
+ 0x00208a46, // n0x0dad c0x0000 (---------------) + I sannan
+ 0x002937c8, // n0x0dae c0x0000 (---------------) + I sasayama
+ 0x002349c4, // n0x0daf c0x0000 (---------------) + I sayo
+ 0x002e6a46, // n0x0db0 c0x0000 (---------------) + I shingu
+ 0x002ba609, // n0x0db1 c0x0000 (---------------) + I shinonsen
+ 0x003081c5, // n0x0db2 c0x0000 (---------------) + I shiso
+ 0x002c4946, // n0x0db3 c0x0000 (---------------) + I sumoto
+ 0x0025fa46, // n0x0db4 c0x0000 (---------------) + I taishi
+ 0x00209604, // n0x0db5 c0x0000 (---------------) + I taka
+ 0x002875ca, // n0x0db6 c0x0000 (---------------) + I takarazuka
+ 0x0028c708, // n0x0db7 c0x0000 (---------------) + I takasago
+ 0x00245106, // n0x0db8 c0x0000 (---------------) + I takino
+ 0x0035ee85, // n0x0db9 c0x0000 (---------------) + I tamba
+ 0x00203987, // n0x0dba c0x0000 (---------------) + I tatsuno
+ 0x0022e707, // n0x0dbb c0x0000 (---------------) + I toyooka
+ 0x00389944, // n0x0dbc c0x0000 (---------------) + I yabu
+ 0x0021aec7, // n0x0dbd c0x0000 (---------------) + I yashiro
+ 0x002a0604, // n0x0dbe c0x0000 (---------------) + I yoka
+ 0x00322c46, // n0x0dbf c0x0000 (---------------) + I yokawa
+ 0x00204943, // n0x0dc0 c0x0000 (---------------) + I ami
+ 0x002ad5c5, // n0x0dc1 c0x0000 (---------------) + I asahi
+ 0x00346e05, // n0x0dc2 c0x0000 (---------------) + I bando
+ 0x002e5708, // n0x0dc3 c0x0000 (---------------) + I chikusei
+ 0x0024ef05, // n0x0dc4 c0x0000 (---------------) + I daigo
+ 0x00268309, // n0x0dc5 c0x0000 (---------------) + I fujishiro
+ 0x0028d547, // n0x0dc6 c0x0000 (---------------) + I hitachi
+ 0x0029330b, // n0x0dc7 c0x0000 (---------------) + I hitachinaka
+ 0x0028d54c, // n0x0dc8 c0x0000 (---------------) + I hitachiomiya
+ 0x0028e50a, // n0x0dc9 c0x0000 (---------------) + I hitachiota
+ 0x00316ac7, // n0x0dca c0x0000 (---------------) + I ibaraki
+ 0x00200243, // n0x0dcb c0x0000 (---------------) + I ina
+ 0x00291508, // n0x0dcc c0x0000 (---------------) + I inashiki
+ 0x0021f2c5, // n0x0dcd c0x0000 (---------------) + I itako
+ 0x002cae85, // n0x0dce c0x0000 (---------------) + I iwama
+ 0x00207f44, // n0x0dcf c0x0000 (---------------) + I joso
+ 0x002ae7c6, // n0x0dd0 c0x0000 (---------------) + I kamisu
+ 0x0023b7c6, // n0x0dd1 c0x0000 (---------------) + I kasama
+ 0x00341887, // n0x0dd2 c0x0000 (---------------) + I kashima
+ 0x0020b60b, // n0x0dd3 c0x0000 (---------------) + I kasumigaura
+ 0x00232c84, // n0x0dd4 c0x0000 (---------------) + I koga
+ 0x0033ee44, // n0x0dd5 c0x0000 (---------------) + I miho
+ 0x00246f04, // n0x0dd6 c0x0000 (---------------) + I mito
+ 0x002b4f86, // n0x0dd7 c0x0000 (---------------) + I moriya
+ 0x00204884, // n0x0dd8 c0x0000 (---------------) + I naka
+ 0x00376c08, // n0x0dd9 c0x0000 (---------------) + I namegata
+ 0x00321385, // n0x0dda c0x0000 (---------------) + I oarai
+ 0x00204045, // n0x0ddb c0x0000 (---------------) + I ogawa
+ 0x00393607, // n0x0ddc c0x0000 (---------------) + I omitama
+ 0x00206c89, // n0x0ddd c0x0000 (---------------) + I ryugasaki
+ 0x003396c5, // n0x0dde c0x0000 (---------------) + I sakai
+ 0x0030cc0a, // n0x0ddf c0x0000 (---------------) + I sakuragawa
+ 0x0027bc89, // n0x0de0 c0x0000 (---------------) + I shimodate
+ 0x0027db0a, // n0x0de1 c0x0000 (---------------) + I shimotsuma
+ 0x00395689, // n0x0de2 c0x0000 (---------------) + I shirosato
+ 0x00339a04, // n0x0de3 c0x0000 (---------------) + I sowa
+ 0x0029fdc5, // n0x0de4 c0x0000 (---------------) + I suifu
+ 0x0029cec8, // n0x0de5 c0x0000 (---------------) + I takahagi
+ 0x002d0fcb, // n0x0de6 c0x0000 (---------------) + I tamatsukuri
+ 0x002df1c5, // n0x0de7 c0x0000 (---------------) + I tokai
+ 0x00352046, // n0x0de8 c0x0000 (---------------) + I tomobe
+ 0x0021a304, // n0x0de9 c0x0000 (---------------) + I tone
+ 0x00268846, // n0x0dea c0x0000 (---------------) + I toride
+ 0x0024e409, // n0x0deb c0x0000 (---------------) + I tsuchiura
+ 0x0032d387, // n0x0dec c0x0000 (---------------) + I tsukuba
+ 0x00387c88, // n0x0ded c0x0000 (---------------) + I uchihara
+ 0x002315c6, // n0x0dee c0x0000 (---------------) + I ushiku
+ 0x002e38c7, // n0x0def c0x0000 (---------------) + I yachiyo
+ 0x0026dc08, // n0x0df0 c0x0000 (---------------) + I yamagata
+ 0x00377b86, // n0x0df1 c0x0000 (---------------) + I yawara
+ 0x00260d44, // n0x0df2 c0x0000 (---------------) + I yuki
+ 0x0034af87, // n0x0df3 c0x0000 (---------------) + I anamizu
+ 0x00336fc5, // n0x0df4 c0x0000 (---------------) + I hakui
+ 0x0034be87, // n0x0df5 c0x0000 (---------------) + I hakusan
+ 0x00281f04, // n0x0df6 c0x0000 (---------------) + I kaga
+ 0x00226886, // n0x0df7 c0x0000 (---------------) + I kahoku
+ 0x0037ffc8, // n0x0df8 c0x0000 (---------------) + I kanazawa
+ 0x002826c8, // n0x0df9 c0x0000 (---------------) + I kawakita
+ 0x002be447, // n0x0dfa c0x0000 (---------------) + I komatsu
+ 0x0023fcc8, // n0x0dfb c0x0000 (---------------) + I nakanoto
+ 0x0027e905, // n0x0dfc c0x0000 (---------------) + I nanao
+ 0x00207cc4, // n0x0dfd c0x0000 (---------------) + I nomi
+ 0x00312a88, // n0x0dfe c0x0000 (---------------) + I nonoichi
+ 0x0023fdc4, // n0x0dff c0x0000 (---------------) + I noto
+ 0x0020f2c5, // n0x0e00 c0x0000 (---------------) + I shika
+ 0x002d2b84, // n0x0e01 c0x0000 (---------------) + I suzu
+ 0x0027d707, // n0x0e02 c0x0000 (---------------) + I tsubata
+ 0x0031c3c7, // n0x0e03 c0x0000 (---------------) + I tsurugi
+ 0x0026e588, // n0x0e04 c0x0000 (---------------) + I uchinada
+ 0x0028dd06, // n0x0e05 c0x0000 (---------------) + I wajima
+ 0x0024ee85, // n0x0e06 c0x0000 (---------------) + I fudai
+ 0x00268108, // n0x0e07 c0x0000 (---------------) + I fujisawa
+ 0x00364a08, // n0x0e08 c0x0000 (---------------) + I hanamaki
+ 0x0028b089, // n0x0e09 c0x0000 (---------------) + I hiraizumi
+ 0x0021af86, // n0x0e0a c0x0000 (---------------) + I hirono
+ 0x0029ba88, // n0x0e0b c0x0000 (---------------) + I ichinohe
+ 0x0038f78a, // n0x0e0c c0x0000 (---------------) + I ichinoseki
+ 0x00271fc8, // n0x0e0d c0x0000 (---------------) + I iwaizumi
+ 0x002c3b85, // n0x0e0e c0x0000 (---------------) + I iwate
+ 0x0031b686, // n0x0e0f c0x0000 (---------------) + I joboji
+ 0x0027bb48, // n0x0e10 c0x0000 (---------------) + I kamaishi
+ 0x0020deca, // n0x0e11 c0x0000 (---------------) + I kanegasaki
+ 0x0032a887, // n0x0e12 c0x0000 (---------------) + I karumai
+ 0x00273345, // n0x0e13 c0x0000 (---------------) + I kawai
+ 0x00209588, // n0x0e14 c0x0000 (---------------) + I kitakami
+ 0x00224704, // n0x0e15 c0x0000 (---------------) + I kuji
+ 0x00205d06, // n0x0e16 c0x0000 (---------------) + I kunohe
+ 0x002aa5c8, // n0x0e17 c0x0000 (---------------) + I kuzumaki
+ 0x00207d46, // n0x0e18 c0x0000 (---------------) + I miyako
+ 0x00304788, // n0x0e19 c0x0000 (---------------) + I mizusawa
+ 0x0022ac87, // n0x0e1a c0x0000 (---------------) + I morioka
+ 0x00205106, // n0x0e1b c0x0000 (---------------) + I ninohe
+ 0x00384f84, // n0x0e1c c0x0000 (---------------) + I noda
+ 0x0029a847, // n0x0e1d c0x0000 (---------------) + I ofunato
+ 0x002e13c4, // n0x0e1e c0x0000 (---------------) + I oshu
+ 0x0024e3c7, // n0x0e1f c0x0000 (---------------) + I otsuchi
+ 0x0023430d, // n0x0e20 c0x0000 (---------------) + I rikuzentakata
+ 0x0026d185, // n0x0e21 c0x0000 (---------------) + I shiwa
+ 0x002a71cb, // n0x0e22 c0x0000 (---------------) + I shizukuishi
+ 0x002fe746, // n0x0e23 c0x0000 (---------------) + I sumita
+ 0x0023c788, // n0x0e24 c0x0000 (---------------) + I tanohata
+ 0x00369804, // n0x0e25 c0x0000 (---------------) + I tono
+ 0x00264306, // n0x0e26 c0x0000 (---------------) + I yahaba
+ 0x0026b046, // n0x0e27 c0x0000 (---------------) + I yamada
+ 0x0036c587, // n0x0e28 c0x0000 (---------------) + I ayagawa
+ 0x00281d4d, // n0x0e29 c0x0000 (---------------) + I higashikagawa
+ 0x002982c7, // n0x0e2a c0x0000 (---------------) + I kanonji
+ 0x002e8108, // n0x0e2b c0x0000 (---------------) + I kotohira
+ 0x00257385, // n0x0e2c c0x0000 (---------------) + I manno
+ 0x00283488, // n0x0e2d c0x0000 (---------------) + I marugame
+ 0x002aeac6, // n0x0e2e c0x0000 (---------------) + I mitoyo
+ 0x0027e988, // n0x0e2f c0x0000 (---------------) + I naoshima
+ 0x0020ce46, // n0x0e30 c0x0000 (---------------) + I sanuki
+ 0x0031c2c7, // n0x0e31 c0x0000 (---------------) + I tadotsu
+ 0x0026e909, // n0x0e32 c0x0000 (---------------) + I takamatsu
+ 0x00369807, // n0x0e33 c0x0000 (---------------) + I tonosho
+ 0x002761c8, // n0x0e34 c0x0000 (---------------) + I uchinomi
+ 0x00261345, // n0x0e35 c0x0000 (---------------) + I utazu
+ 0x00212c08, // n0x0e36 c0x0000 (---------------) + I zentsuji
+ 0x00305dc5, // n0x0e37 c0x0000 (---------------) + I akune
+ 0x00236bc5, // n0x0e38 c0x0000 (---------------) + I amami
+ 0x0030fcc5, // n0x0e39 c0x0000 (---------------) + I hioki
+ 0x00209883, // n0x0e3a c0x0000 (---------------) + I isa
+ 0x0026f3c4, // n0x0e3b c0x0000 (---------------) + I isen
+ 0x00209785, // n0x0e3c c0x0000 (---------------) + I izumi
+ 0x00271789, // n0x0e3d c0x0000 (---------------) + I kagoshima
+ 0x002b4d06, // n0x0e3e c0x0000 (---------------) + I kanoya
+ 0x002c5c48, // n0x0e3f c0x0000 (---------------) + I kawanabe
+ 0x002d3c45, // n0x0e40 c0x0000 (---------------) + I kinko
+ 0x00314307, // n0x0e41 c0x0000 (---------------) + I kouyama
+ 0x002d3a4a, // n0x0e42 c0x0000 (---------------) + I makurazaki
+ 0x002c4889, // n0x0e43 c0x0000 (---------------) + I matsumoto
+ 0x002bf68a, // n0x0e44 c0x0000 (---------------) + I minamitane
+ 0x002bb148, // n0x0e45 c0x0000 (---------------) + I nakatane
+ 0x0021df8c, // n0x0e46 c0x0000 (---------------) + I nishinoomote
+ 0x0026f0cd, // n0x0e47 c0x0000 (---------------) + I satsumasendai
+ 0x002da843, // n0x0e48 c0x0000 (---------------) + I soo
+ 0x00304688, // n0x0e49 c0x0000 (---------------) + I tarumizu
+ 0x00211dc5, // n0x0e4a c0x0000 (---------------) + I yusui
+ 0x00343086, // n0x0e4b c0x0000 (---------------) + I aikawa
+ 0x0033ea46, // n0x0e4c c0x0000 (---------------) + I atsugi
+ 0x002c35c5, // n0x0e4d c0x0000 (---------------) + I ayase
+ 0x00383449, // n0x0e4e c0x0000 (---------------) + I chigasaki
+ 0x00240845, // n0x0e4f c0x0000 (---------------) + I ebina
+ 0x00268108, // n0x0e50 c0x0000 (---------------) + I fujisawa
+ 0x00265b06, // n0x0e51 c0x0000 (---------------) + I hadano
+ 0x00328686, // n0x0e52 c0x0000 (---------------) + I hakone
+ 0x0028c209, // n0x0e53 c0x0000 (---------------) + I hiratsuka
+ 0x00377387, // n0x0e54 c0x0000 (---------------) + I isehara
+ 0x00315c06, // n0x0e55 c0x0000 (---------------) + I kaisei
+ 0x002d39c8, // n0x0e56 c0x0000 (---------------) + I kamakura
+ 0x003823c8, // n0x0e57 c0x0000 (---------------) + I kiyokawa
+ 0x0032b7c7, // n0x0e58 c0x0000 (---------------) + I matsuda
+ 0x0025240e, // n0x0e59 c0x0000 (---------------) + I minamiashigara
+ 0x002aed05, // n0x0e5a c0x0000 (---------------) + I miura
+ 0x0023e405, // n0x0e5b c0x0000 (---------------) + I nakai
+ 0x00207c48, // n0x0e5c c0x0000 (---------------) + I ninomiya
+ 0x00202487, // n0x0e5d c0x0000 (---------------) + I odawara
+ 0x00238bc2, // n0x0e5e c0x0000 (---------------) + I oi
+ 0x002a6c04, // n0x0e5f c0x0000 (---------------) + I oiso
+ 0x003636ca, // n0x0e60 c0x0000 (---------------) + I sagamihara
+ 0x0024e788, // n0x0e61 c0x0000 (---------------) + I samukawa
+ 0x00337906, // n0x0e62 c0x0000 (---------------) + I tsukui
+ 0x00283b88, // n0x0e63 c0x0000 (---------------) + I yamakita
+ 0x0022afc6, // n0x0e64 c0x0000 (---------------) + I yamato
+ 0x00311d48, // n0x0e65 c0x0000 (---------------) + I yokosuka
+ 0x00297548, // n0x0e66 c0x0000 (---------------) + I yugawara
+ 0x00236b84, // n0x0e67 c0x0000 (---------------) + I zama
+ 0x003185c5, // n0x0e68 c0x0000 (---------------) + I zushi
+ 0x006735c4, // n0x0e69 c0x0001 (---------------) ! I city
+ 0x006735c4, // n0x0e6a c0x0001 (---------------) ! I city
+ 0x006735c4, // n0x0e6b c0x0001 (---------------) ! I city
+ 0x00201ac3, // n0x0e6c c0x0000 (---------------) + I aki
+ 0x002f3a86, // n0x0e6d c0x0000 (---------------) + I geisei
+ 0x002685c6, // n0x0e6e c0x0000 (---------------) + I hidaka
+ 0x0028888c, // n0x0e6f c0x0000 (---------------) + I higashitsuno
+ 0x00201b43, // n0x0e70 c0x0000 (---------------) + I ino
+ 0x002ab006, // n0x0e71 c0x0000 (---------------) + I kagami
+ 0x00204904, // n0x0e72 c0x0000 (---------------) + I kami
+ 0x0020e0c8, // n0x0e73 c0x0000 (---------------) + I kitagawa
+ 0x002ba445, // n0x0e74 c0x0000 (---------------) + I kochi
+ 0x003637c6, // n0x0e75 c0x0000 (---------------) + I mihara
+ 0x002a2e48, // n0x0e76 c0x0000 (---------------) + I motoyama
+ 0x002bc286, // n0x0e77 c0x0000 (---------------) + I muroto
+ 0x002f8bc6, // n0x0e78 c0x0000 (---------------) + I nahari
+ 0x00248448, // n0x0e79 c0x0000 (---------------) + I nakamura
+ 0x00374747, // n0x0e7a c0x0000 (---------------) + I nankoku
+ 0x00251f89, // n0x0e7b c0x0000 (---------------) + I nishitosa
+ 0x00328d8a, // n0x0e7c c0x0000 (---------------) + I niyodogawa
+ 0x00240384, // n0x0e7d c0x0000 (---------------) + I ochi
+ 0x0022ecc5, // n0x0e7e c0x0000 (---------------) + I okawa
+ 0x00286c45, // n0x0e7f c0x0000 (---------------) + I otoyo
+ 0x0022a786, // n0x0e80 c0x0000 (---------------) + I otsuki
+ 0x00240646, // n0x0e81 c0x0000 (---------------) + I sakawa
+ 0x00291906, // n0x0e82 c0x0000 (---------------) + I sukumo
+ 0x002d1e06, // n0x0e83 c0x0000 (---------------) + I susaki
+ 0x002520c4, // n0x0e84 c0x0000 (---------------) + I tosa
+ 0x002520cb, // n0x0e85 c0x0000 (---------------) + I tosashimizu
+ 0x00221244, // n0x0e86 c0x0000 (---------------) + I toyo
+ 0x00203a05, // n0x0e87 c0x0000 (---------------) + I tsuno
+ 0x00296285, // n0x0e88 c0x0000 (---------------) + I umaji
+ 0x00207006, // n0x0e89 c0x0000 (---------------) + I yasuda
+ 0x00209f08, // n0x0e8a c0x0000 (---------------) + I yusuhara
+ 0x0026ef87, // n0x0e8b c0x0000 (---------------) + I amakusa
+ 0x0034e684, // n0x0e8c c0x0000 (---------------) + I arao
+ 0x00209dc3, // n0x0e8d c0x0000 (---------------) + I aso
+ 0x002e3545, // n0x0e8e c0x0000 (---------------) + I choyo
+ 0x00351f07, // n0x0e8f c0x0000 (---------------) + I gyokuto
+ 0x0028f9c9, // n0x0e90 c0x0000 (---------------) + I hitoyoshi
+ 0x0026ee8b, // n0x0e91 c0x0000 (---------------) + I kamiamakusa
+ 0x00341887, // n0x0e92 c0x0000 (---------------) + I kashima
+ 0x0036c287, // n0x0e93 c0x0000 (---------------) + I kikuchi
+ 0x002d4744, // n0x0e94 c0x0000 (---------------) + I kosa
+ 0x002a2d48, // n0x0e95 c0x0000 (---------------) + I kumamoto
+ 0x00253387, // n0x0e96 c0x0000 (---------------) + I mashiki
+ 0x0028fc06, // n0x0e97 c0x0000 (---------------) + I mifune
+ 0x00260ac8, // n0x0e98 c0x0000 (---------------) + I minamata
+ 0x0029ee4b, // n0x0e99 c0x0000 (---------------) + I minamioguni
+ 0x0034cac6, // n0x0e9a c0x0000 (---------------) + I nagasu
+ 0x0020e649, // n0x0e9b c0x0000 (---------------) + I nishihara
+ 0x0029efc5, // n0x0e9c c0x0000 (---------------) + I oguni
+ 0x002e3dc3, // n0x0e9d c0x0000 (---------------) + I ozu
+ 0x002c4946, // n0x0e9e c0x0000 (---------------) + I sumoto
+ 0x0022ab88, // n0x0e9f c0x0000 (---------------) + I takamori
+ 0x0020cf03, // n0x0ea0 c0x0000 (---------------) + I uki
+ 0x002269c3, // n0x0ea1 c0x0000 (---------------) + I uto
+ 0x0026dc06, // n0x0ea2 c0x0000 (---------------) + I yamaga
+ 0x0022afc6, // n0x0ea3 c0x0000 (---------------) + I yamato
+ 0x00373e8a, // n0x0ea4 c0x0000 (---------------) + I yatsushiro
+ 0x00269e45, // n0x0ea5 c0x0000 (---------------) + I ayabe
+ 0x0026ae8b, // n0x0ea6 c0x0000 (---------------) + I fukuchiyama
+ 0x00289bcb, // n0x0ea7 c0x0000 (---------------) + I higashiyama
+ 0x00229783, // n0x0ea8 c0x0000 (---------------) + I ide
+ 0x00213cc3, // n0x0ea9 c0x0000 (---------------) + I ine
+ 0x00298c44, // n0x0eaa c0x0000 (---------------) + I joyo
+ 0x0023a807, // n0x0eab c0x0000 (---------------) + I kameoka
+ 0x0022ac04, // n0x0eac c0x0000 (---------------) + I kamo
+ 0x00206e44, // n0x0ead c0x0000 (---------------) + I kita
+ 0x002f1c04, // n0x0eae c0x0000 (---------------) + I kizu
+ 0x002722c8, // n0x0eaf c0x0000 (---------------) + I kumiyama
+ 0x0035edc8, // n0x0eb0 c0x0000 (---------------) + I kyotamba
+ 0x0030f889, // n0x0eb1 c0x0000 (---------------) + I kyotanabe
+ 0x003165c8, // n0x0eb2 c0x0000 (---------------) + I kyotango
+ 0x0030c047, // n0x0eb3 c0x0000 (---------------) + I maizuru
+ 0x00213fc6, // n0x0eb4 c0x0000 (---------------) + I minami
+ 0x002c588f, // n0x0eb5 c0x0000 (---------------) + I minamiyamashiro
+ 0x002aee46, // n0x0eb6 c0x0000 (---------------) + I miyazu
+ 0x002ba3c4, // n0x0eb7 c0x0000 (---------------) + I muko
+ 0x0035ec0a, // n0x0eb8 c0x0000 (---------------) + I nagaokakyo
+ 0x00351e07, // n0x0eb9 c0x0000 (---------------) + I nakagyo
+ 0x00204d46, // n0x0eba c0x0000 (---------------) + I nantan
+ 0x0027c389, // n0x0ebb c0x0000 (---------------) + I oyamazaki
+ 0x0030f805, // n0x0ebc c0x0000 (---------------) + I sakyo
+ 0x002c6f45, // n0x0ebd c0x0000 (---------------) + I seika
+ 0x0030f946, // n0x0ebe c0x0000 (---------------) + I tanabe
+ 0x00212d43, // n0x0ebf c0x0000 (---------------) + I uji
+ 0x00224749, // n0x0ec0 c0x0000 (---------------) + I ujitawara
+ 0x00214fc6, // n0x0ec1 c0x0000 (---------------) + I wazuka
+ 0x0023aa49, // n0x0ec2 c0x0000 (---------------) + I yamashina
+ 0x003804c6, // n0x0ec3 c0x0000 (---------------) + I yawata
+ 0x002ad5c5, // n0x0ec4 c0x0000 (---------------) + I asahi
+ 0x0035dd05, // n0x0ec5 c0x0000 (---------------) + I inabe
+ 0x00233203, // n0x0ec6 c0x0000 (---------------) + I ise
+ 0x0023a948, // n0x0ec7 c0x0000 (---------------) + I kameyama
+ 0x002406c7, // n0x0ec8 c0x0000 (---------------) + I kawagoe
+ 0x0037aec4, // n0x0ec9 c0x0000 (---------------) + I kiho
+ 0x0022a888, // n0x0eca c0x0000 (---------------) + I kisosaki
+ 0x002927c4, // n0x0ecb c0x0000 (---------------) + I kiwa
+ 0x002d4306, // n0x0ecc c0x0000 (---------------) + I komono
+ 0x00285906, // n0x0ecd c0x0000 (---------------) + I kumano
+ 0x0022f386, // n0x0ece c0x0000 (---------------) + I kuwana
+ 0x002b5109, // n0x0ecf c0x0000 (---------------) + I matsusaka
+ 0x002cae05, // n0x0ed0 c0x0000 (---------------) + I meiwa
+ 0x00292a46, // n0x0ed1 c0x0000 (---------------) + I mihama
+ 0x00370d89, // n0x0ed2 c0x0000 (---------------) + I minamiise
+ 0x002adc06, // n0x0ed3 c0x0000 (---------------) + I misugi
+ 0x00272346, // n0x0ed4 c0x0000 (---------------) + I miyama
+ 0x0036fe46, // n0x0ed5 c0x0000 (---------------) + I nabari
+ 0x0021f645, // n0x0ed6 c0x0000 (---------------) + I shima
+ 0x002d2b86, // n0x0ed7 c0x0000 (---------------) + I suzuka
+ 0x0031c2c4, // n0x0ed8 c0x0000 (---------------) + I tado
+ 0x0028ee85, // n0x0ed9 c0x0000 (---------------) + I taiki
+ 0x00245104, // n0x0eda c0x0000 (---------------) + I taki
+ 0x002f1b06, // n0x0edb c0x0000 (---------------) + I tamaki
+ 0x00395844, // n0x0edc c0x0000 (---------------) + I toba
+ 0x00203a03, // n0x0edd c0x0000 (---------------) + I tsu
+ 0x00274845, // n0x0ede c0x0000 (---------------) + I udono
+ 0x00227348, // n0x0edf c0x0000 (---------------) + I ureshino
+ 0x00253ec7, // n0x0ee0 c0x0000 (---------------) + I watarai
+ 0x00234a49, // n0x0ee1 c0x0000 (---------------) + I yokkaichi
+ 0x00274a88, // n0x0ee2 c0x0000 (---------------) + I furukawa
+ 0x002830d1, // n0x0ee3 c0x0000 (---------------) + I higashimatsushima
+ 0x0025faca, // n0x0ee4 c0x0000 (---------------) + I ishinomaki
+ 0x00316107, // n0x0ee5 c0x0000 (---------------) + I iwanuma
+ 0x0038d846, // n0x0ee6 c0x0000 (---------------) + I kakuda
+ 0x00204904, // n0x0ee7 c0x0000 (---------------) + I kami
+ 0x002a8748, // n0x0ee8 c0x0000 (---------------) + I kawasaki
+ 0x0034cc49, // n0x0ee9 c0x0000 (---------------) + I kesennuma
+ 0x0028b408, // n0x0eea c0x0000 (---------------) + I marumori
+ 0x0028328a, // n0x0eeb c0x0000 (---------------) + I matsushima
+ 0x002b45cd, // n0x0eec c0x0000 (---------------) + I minamisanriku
+ 0x0028b246, // n0x0eed c0x0000 (---------------) + I misato
+ 0x00248546, // n0x0eee c0x0000 (---------------) + I murata
+ 0x0029a906, // n0x0eef c0x0000 (---------------) + I natori
+ 0x00204047, // n0x0ef0 c0x0000 (---------------) + I ogawara
+ 0x002e81c5, // n0x0ef1 c0x0000 (---------------) + I ohira
+ 0x00295c07, // n0x0ef2 c0x0000 (---------------) + I onagawa
+ 0x0022a945, // n0x0ef3 c0x0000 (---------------) + I osaki
+ 0x00294d84, // n0x0ef4 c0x0000 (---------------) + I rifu
+ 0x0027c146, // n0x0ef5 c0x0000 (---------------) + I semine
+ 0x003416c7, // n0x0ef6 c0x0000 (---------------) + I shibata
+ 0x0022444d, // n0x0ef7 c0x0000 (---------------) + I shichikashuku
+ 0x0027ba87, // n0x0ef8 c0x0000 (---------------) + I shikama
+ 0x002471c8, // n0x0ef9 c0x0000 (---------------) + I shiogama
+ 0x00268409, // n0x0efa c0x0000 (---------------) + I shiroishi
+ 0x0031b586, // n0x0efb c0x0000 (---------------) + I tagajo
+ 0x002e7445, // n0x0efc c0x0000 (---------------) + I taiwa
+ 0x0020fe04, // n0x0efd c0x0000 (---------------) + I tome
+ 0x00371186, // n0x0efe c0x0000 (---------------) + I tomiya
+ 0x00389846, // n0x0eff c0x0000 (---------------) + I wakuya
+ 0x0024e906, // n0x0f00 c0x0000 (---------------) + I watari
+ 0x00286688, // n0x0f01 c0x0000 (---------------) + I yamamoto
+ 0x00395443, // n0x0f02 c0x0000 (---------------) + I zao
+ 0x00206fc3, // n0x0f03 c0x0000 (---------------) + I aya
+ 0x0036cf45, // n0x0f04 c0x0000 (---------------) + I ebino
+ 0x00375946, // n0x0f05 c0x0000 (---------------) + I gokase
+ 0x00297505, // n0x0f06 c0x0000 (---------------) + I hyuga
+ 0x002317c8, // n0x0f07 c0x0000 (---------------) + I kadogawa
+ 0x002882ca, // n0x0f08 c0x0000 (---------------) + I kawaminami
+ 0x00389a44, // n0x0f09 c0x0000 (---------------) + I kijo
+ 0x0020e0c8, // n0x0f0a c0x0000 (---------------) + I kitagawa
+ 0x002815c8, // n0x0f0b c0x0000 (---------------) + I kitakata
+ 0x00206e47, // n0x0f0c c0x0000 (---------------) + I kitaura
+ 0x002d3d09, // n0x0f0d c0x0000 (---------------) + I kobayashi
+ 0x002a2a48, // n0x0f0e c0x0000 (---------------) + I kunitomi
+ 0x0026d647, // n0x0f0f c0x0000 (---------------) + I kushima
+ 0x0027cdc6, // n0x0f10 c0x0000 (---------------) + I mimata
+ 0x00207d4a, // n0x0f11 c0x0000 (---------------) + I miyakonojo
+ 0x00371208, // n0x0f12 c0x0000 (---------------) + I miyazaki
+ 0x002ae609, // n0x0f13 c0x0000 (---------------) + I morotsuka
+ 0x0027fa88, // n0x0f14 c0x0000 (---------------) + I nichinan
+ 0x00217e09, // n0x0f15 c0x0000 (---------------) + I nishimera
+ 0x00346687, // n0x0f16 c0x0000 (---------------) + I nobeoka
+ 0x00302b45, // n0x0f17 c0x0000 (---------------) + I saito
+ 0x00329e86, // n0x0f18 c0x0000 (---------------) + I shiiba
+ 0x0028f5c8, // n0x0f19 c0x0000 (---------------) + I shintomi
+ 0x0023ae48, // n0x0f1a c0x0000 (---------------) + I takaharu
+ 0x002462c8, // n0x0f1b c0x0000 (---------------) + I takanabe
+ 0x002f9c88, // n0x0f1c c0x0000 (---------------) + I takazaki
+ 0x00203a05, // n0x0f1d c0x0000 (---------------) + I tsuno
+ 0x0022e144, // n0x0f1e c0x0000 (---------------) + I achi
+ 0x0034e008, // n0x0f1f c0x0000 (---------------) + I agematsu
+ 0x00204e44, // n0x0f20 c0x0000 (---------------) + I anan
+ 0x00395484, // n0x0f21 c0x0000 (---------------) + I aoki
+ 0x002ad5c5, // n0x0f22 c0x0000 (---------------) + I asahi
+ 0x0027ed87, // n0x0f23 c0x0000 (---------------) + I azumino
+ 0x00206a49, // n0x0f24 c0x0000 (---------------) + I chikuhoku
+ 0x0036c387, // n0x0f25 c0x0000 (---------------) + I chikuma
+ 0x0022e185, // n0x0f26 c0x0000 (---------------) + I chino
+ 0x00265906, // n0x0f27 c0x0000 (---------------) + I fujimi
+ 0x003317c6, // n0x0f28 c0x0000 (---------------) + I hakuba
+ 0x0020a004, // n0x0f29 c0x0000 (---------------) + I hara
+ 0x0028c546, // n0x0f2a c0x0000 (---------------) + I hiraya
+ 0x0024ecc4, // n0x0f2b c0x0000 (---------------) + I iida
+ 0x00256486, // n0x0f2c c0x0000 (---------------) + I iijima
+ 0x0034d2c6, // n0x0f2d c0x0000 (---------------) + I iiyama
+ 0x0020ec86, // n0x0f2e c0x0000 (---------------) + I iizuna
+ 0x00390e45, // n0x0f2f c0x0000 (---------------) + I ikeda
+ 0x00231687, // n0x0f30 c0x0000 (---------------) + I ikusaka
+ 0x00200243, // n0x0f31 c0x0000 (---------------) + I ina
+ 0x0031e389, // n0x0f32 c0x0000 (---------------) + I karuizawa
+ 0x00315908, // n0x0f33 c0x0000 (---------------) + I kawakami
+ 0x0022a884, // n0x0f34 c0x0000 (---------------) + I kiso
+ 0x0026d4cd, // n0x0f35 c0x0000 (---------------) + I kisofukushima
+ 0x002827c8, // n0x0f36 c0x0000 (---------------) + I kitaaiki
+ 0x002fa008, // n0x0f37 c0x0000 (---------------) + I komagane
+ 0x002ae586, // n0x0f38 c0x0000 (---------------) + I komoro
+ 0x0026ea09, // n0x0f39 c0x0000 (---------------) + I matsukawa
+ 0x002c4889, // n0x0f3a c0x0000 (---------------) + I matsumoto
+ 0x0024e105, // n0x0f3b c0x0000 (---------------) + I miasa
+ 0x002883ca, // n0x0f3c c0x0000 (---------------) + I minamiaiki
+ 0x0025b8ca, // n0x0f3d c0x0000 (---------------) + I minamimaki
+ 0x002b064c, // n0x0f3e c0x0000 (---------------) + I minamiminowa
+ 0x002b07c6, // n0x0f3f c0x0000 (---------------) + I minowa
+ 0x00266386, // n0x0f40 c0x0000 (---------------) + I miyada
+ 0x002af8c6, // n0x0f41 c0x0000 (---------------) + I miyota
+ 0x00258b89, // n0x0f42 c0x0000 (---------------) + I mochizuki
+ 0x0034e4c6, // n0x0f43 c0x0000 (---------------) + I nagano
+ 0x0028ba86, // n0x0f44 c0x0000 (---------------) + I nagawa
+ 0x00240906, // n0x0f45 c0x0000 (---------------) + I nagiso
+ 0x002934c8, // n0x0f46 c0x0000 (---------------) + I nakagawa
+ 0x0023fcc6, // n0x0f47 c0x0000 (---------------) + I nakano
+ 0x002fe9cb, // n0x0f48 c0x0000 (---------------) + I nozawaonsen
+ 0x0027ef05, // n0x0f49 c0x0000 (---------------) + I obuse
+ 0x00204045, // n0x0f4a c0x0000 (---------------) + I ogawa
+ 0x00266605, // n0x0f4b c0x0000 (---------------) + I okaya
+ 0x00353c06, // n0x0f4c c0x0000 (---------------) + I omachi
+ 0x00207d03, // n0x0f4d c0x0000 (---------------) + I omi
+ 0x0022f306, // n0x0f4e c0x0000 (---------------) + I ookuwa
+ 0x0027ba07, // n0x0f4f c0x0000 (---------------) + I ooshika
+ 0x002a8605, // n0x0f50 c0x0000 (---------------) + I otaki
+ 0x003389c5, // n0x0f51 c0x0000 (---------------) + I otari
+ 0x0036ba45, // n0x0f52 c0x0000 (---------------) + I sakae
+ 0x00201a06, // n0x0f53 c0x0000 (---------------) + I sakaki
+ 0x0024e1c4, // n0x0f54 c0x0000 (---------------) + I saku
+ 0x0035fb46, // n0x0f55 c0x0000 (---------------) + I sakuho
+ 0x00325709, // n0x0f56 c0x0000 (---------------) + I shimosuwa
+ 0x00353a8c, // n0x0f57 c0x0000 (---------------) + I shinanomachi
+ 0x00294ac8, // n0x0f58 c0x0000 (---------------) + I shiojiri
+ 0x002e5544, // n0x0f59 c0x0000 (---------------) + I suwa
+ 0x002d2806, // n0x0f5a c0x0000 (---------------) + I suzaka
+ 0x002fe846, // n0x0f5b c0x0000 (---------------) + I takagi
+ 0x0022ab88, // n0x0f5c c0x0000 (---------------) + I takamori
+ 0x0020f408, // n0x0f5d c0x0000 (---------------) + I takayama
+ 0x00353989, // n0x0f5e c0x0000 (---------------) + I tateshina
+ 0x00203987, // n0x0f5f c0x0000 (---------------) + I tatsuno
+ 0x002e5d09, // n0x0f60 c0x0000 (---------------) + I togakushi
+ 0x0025e506, // n0x0f61 c0x0000 (---------------) + I togura
+ 0x002204c4, // n0x0f62 c0x0000 (---------------) + I tomi
+ 0x00207204, // n0x0f63 c0x0000 (---------------) + I ueda
+ 0x0023e8c4, // n0x0f64 c0x0000 (---------------) + I wada
+ 0x0026dc08, // n0x0f65 c0x0000 (---------------) + I yamagata
+ 0x0020688a, // n0x0f66 c0x0000 (---------------) + I yamanouchi
+ 0x00339646, // n0x0f67 c0x0000 (---------------) + I yasaka
+ 0x0033e847, // n0x0f68 c0x0000 (---------------) + I yasuoka
+ 0x002e9747, // n0x0f69 c0x0000 (---------------) + I chijiwa
+ 0x0021edc5, // n0x0f6a c0x0000 (---------------) + I futsu
+ 0x00286c04, // n0x0f6b c0x0000 (---------------) + I goto
+ 0x0027a8c6, // n0x0f6c c0x0000 (---------------) + I hasami
+ 0x002e8206, // n0x0f6d c0x0000 (---------------) + I hirado
+ 0x00206743, // n0x0f6e c0x0000 (---------------) + I iki
+ 0x00315747, // n0x0f6f c0x0000 (---------------) + I isahaya
+ 0x003108c8, // n0x0f70 c0x0000 (---------------) + I kawatana
+ 0x0024e24a, // n0x0f71 c0x0000 (---------------) + I kuchinotsu
+ 0x002bcc48, // n0x0f72 c0x0000 (---------------) + I matsuura
+ 0x0036c108, // n0x0f73 c0x0000 (---------------) + I nagasaki
+ 0x00395885, // n0x0f74 c0x0000 (---------------) + I obama
+ 0x0023a1c5, // n0x0f75 c0x0000 (---------------) + I omura
+ 0x00366645, // n0x0f76 c0x0000 (---------------) + I oseto
+ 0x0021f186, // n0x0f77 c0x0000 (---------------) + I saikai
+ 0x0029be46, // n0x0f78 c0x0000 (---------------) + I sasebo
+ 0x002e5845, // n0x0f79 c0x0000 (---------------) + I seihi
+ 0x0038bf49, // n0x0f7a c0x0000 (---------------) + I shimabara
+ 0x00286a0c, // n0x0f7b c0x0000 (---------------) + I shinkamigoto
+ 0x00226a07, // n0x0f7c c0x0000 (---------------) + I togitsu
+ 0x00283308, // n0x0f7d c0x0000 (---------------) + I tsushima
+ 0x00279c05, // n0x0f7e c0x0000 (---------------) + I unzen
+ 0x006735c4, // n0x0f7f c0x0001 (---------------) ! I city
+ 0x00346e44, // n0x0f80 c0x0000 (---------------) + I ando
+ 0x0026e184, // n0x0f81 c0x0000 (---------------) + I gose
+ 0x0022e2c6, // n0x0f82 c0x0000 (---------------) + I heguri
+ 0x0028a74e, // n0x0f83 c0x0000 (---------------) + I higashiyoshino
+ 0x00344187, // n0x0f84 c0x0000 (---------------) + I ikaruga
+ 0x00204245, // n0x0f85 c0x0000 (---------------) + I ikoma
+ 0x0022adcc, // n0x0f86 c0x0000 (---------------) + I kamikitayama
+ 0x00292687, // n0x0f87 c0x0000 (---------------) + I kanmaki
+ 0x00341647, // n0x0f88 c0x0000 (---------------) + I kashiba
+ 0x00344809, // n0x0f89 c0x0000 (---------------) + I kashihara
+ 0x002133c9, // n0x0f8a c0x0000 (---------------) + I katsuragi
+ 0x00273345, // n0x0f8b c0x0000 (---------------) + I kawai
+ 0x00315908, // n0x0f8c c0x0000 (---------------) + I kawakami
+ 0x0022ed09, // n0x0f8d c0x0000 (---------------) + I kawanishi
+ 0x002c9885, // n0x0f8e c0x0000 (---------------) + I koryo
+ 0x002a8548, // n0x0f8f c0x0000 (---------------) + I kurotaki
+ 0x002b5f46, // n0x0f90 c0x0000 (---------------) + I mitsue
+ 0x002a5986, // n0x0f91 c0x0000 (---------------) + I miyake
+ 0x002da0c4, // n0x0f92 c0x0000 (---------------) + I nara
+ 0x00257448, // n0x0f93 c0x0000 (---------------) + I nosegawa
+ 0x002432c3, // n0x0f94 c0x0000 (---------------) + I oji
+ 0x00392684, // n0x0f95 c0x0000 (---------------) + I ouda
+ 0x002e35c5, // n0x0f96 c0x0000 (---------------) + I oyodo
+ 0x002f4d47, // n0x0f97 c0x0000 (---------------) + I sakurai
+ 0x00202285, // n0x0f98 c0x0000 (---------------) + I sango
+ 0x0038f649, // n0x0f99 c0x0000 (---------------) + I shimoichi
+ 0x00267dcd, // n0x0f9a c0x0000 (---------------) + I shimokitayama
+ 0x00285306, // n0x0f9b c0x0000 (---------------) + I shinjo
+ 0x00220944, // n0x0f9c c0x0000 (---------------) + I soni
+ 0x00335dc8, // n0x0f9d c0x0000 (---------------) + I takatori
+ 0x0025ca0a, // n0x0f9e c0x0000 (---------------) + I tawaramoto
+ 0x00203207, // n0x0f9f c0x0000 (---------------) + I tenkawa
+ 0x0027be45, // n0x0fa0 c0x0000 (---------------) + I tenri
+ 0x002070c3, // n0x0fa1 c0x0000 (---------------) + I uda
+ 0x00289d8e, // n0x0fa2 c0x0000 (---------------) + I yamatokoriyama
+ 0x0022afcc, // n0x0fa3 c0x0000 (---------------) + I yamatotakada
+ 0x0027b147, // n0x0fa4 c0x0000 (---------------) + I yamazoe
+ 0x0028a907, // n0x0fa5 c0x0000 (---------------) + I yoshino
+ 0x002015c3, // n0x0fa6 c0x0000 (---------------) + I aga
+ 0x0034e505, // n0x0fa7 c0x0000 (---------------) + I agano
+ 0x0026e185, // n0x0fa8 c0x0000 (---------------) + I gosen
+ 0x00283f48, // n0x0fa9 c0x0000 (---------------) + I itoigawa
+ 0x00281409, // n0x0faa c0x0000 (---------------) + I izumozaki
+ 0x0026a386, // n0x0fab c0x0000 (---------------) + I joetsu
+ 0x0022ac04, // n0x0fac c0x0000 (---------------) + I kamo
+ 0x00316046, // n0x0fad c0x0000 (---------------) + I kariwa
+ 0x0038218b, // n0x0fae c0x0000 (---------------) + I kashiwazaki
+ 0x002c460c, // n0x0faf c0x0000 (---------------) + I minamiuonuma
+ 0x0026c3c7, // n0x0fb0 c0x0000 (---------------) + I mitsuke
+ 0x002ba105, // n0x0fb1 c0x0000 (---------------) + I muika
+ 0x003743c8, // n0x0fb2 c0x0000 (---------------) + I murakami
+ 0x0032b585, // n0x0fb3 c0x0000 (---------------) + I myoko
+ 0x0035ec07, // n0x0fb4 c0x0000 (---------------) + I nagaoka
+ 0x0025f907, // n0x0fb5 c0x0000 (---------------) + I niigata
+ 0x002432c5, // n0x0fb6 c0x0000 (---------------) + I ojiya
+ 0x00207d03, // n0x0fb7 c0x0000 (---------------) + I omi
+ 0x002109c4, // n0x0fb8 c0x0000 (---------------) + I sado
+ 0x00203845, // n0x0fb9 c0x0000 (---------------) + I sanjo
+ 0x002f3b45, // n0x0fba c0x0000 (---------------) + I seiro
+ 0x002f3b46, // n0x0fbb c0x0000 (---------------) + I seirou
+ 0x00274588, // n0x0fbc c0x0000 (---------------) + I sekikawa
+ 0x003416c7, // n0x0fbd c0x0000 (---------------) + I shibata
+ 0x0033ed46, // n0x0fbe c0x0000 (---------------) + I tagami
+ 0x0031abc6, // n0x0fbf c0x0000 (---------------) + I tainai
+ 0x0030fc06, // n0x0fc0 c0x0000 (---------------) + I tochio
+ 0x002e1589, // n0x0fc1 c0x0000 (---------------) + I tokamachi
+ 0x0031adc7, // n0x0fc2 c0x0000 (---------------) + I tsubame
+ 0x00207306, // n0x0fc3 c0x0000 (---------------) + I tsunan
+ 0x002c4786, // n0x0fc4 c0x0000 (---------------) + I uonuma
+ 0x00243386, // n0x0fc5 c0x0000 (---------------) + I yahiko
+ 0x00298cc5, // n0x0fc6 c0x0000 (---------------) + I yoita
+ 0x00216b86, // n0x0fc7 c0x0000 (---------------) + I yuzawa
+ 0x0034c845, // n0x0fc8 c0x0000 (---------------) + I beppu
+ 0x002a55c8, // n0x0fc9 c0x0000 (---------------) + I bungoono
+ 0x0024facb, // n0x0fca c0x0000 (---------------) + I bungotakada
+ 0x0027a6c6, // n0x0fcb c0x0000 (---------------) + I hasama
+ 0x002e9784, // n0x0fcc c0x0000 (---------------) + I hiji
+ 0x00336449, // n0x0fcd c0x0000 (---------------) + I himeshima
+ 0x0028d544, // n0x0fce c0x0000 (---------------) + I hita
+ 0x002b5ec8, // n0x0fcf c0x0000 (---------------) + I kamitsue
+ 0x0027b5c7, // n0x0fd0 c0x0000 (---------------) + I kokonoe
+ 0x0026ff44, // n0x0fd1 c0x0000 (---------------) + I kuju
+ 0x002a0f48, // n0x0fd2 c0x0000 (---------------) + I kunisaki
+ 0x002a9c04, // n0x0fd3 c0x0000 (---------------) + I kusu
+ 0x00298d04, // n0x0fd4 c0x0000 (---------------) + I oita
+ 0x002a7585, // n0x0fd5 c0x0000 (---------------) + I saiki
+ 0x00335cc6, // n0x0fd6 c0x0000 (---------------) + I taketa
+ 0x00272207, // n0x0fd7 c0x0000 (---------------) + I tsukumi
+ 0x00222503, // n0x0fd8 c0x0000 (---------------) + I usa
+ 0x00288f85, // n0x0fd9 c0x0000 (---------------) + I usuki
+ 0x002ab604, // n0x0fda c0x0000 (---------------) + I yufu
+ 0x0023e446, // n0x0fdb c0x0000 (---------------) + I akaiwa
+ 0x0024e188, // n0x0fdc c0x0000 (---------------) + I asakuchi
+ 0x00310605, // n0x0fdd c0x0000 (---------------) + I bizen
+ 0x0027e449, // n0x0fde c0x0000 (---------------) + I hayashima
+ 0x002b9fc5, // n0x0fdf c0x0000 (---------------) + I ibara
+ 0x002ab008, // n0x0fe0 c0x0000 (---------------) + I kagamino
+ 0x00340b47, // n0x0fe1 c0x0000 (---------------) + I kasaoka
+ 0x0035c188, // n0x0fe2 c0x0000 (---------------) + I kibichuo
+ 0x002a0447, // n0x0fe3 c0x0000 (---------------) + I kumenan
+ 0x0025adc9, // n0x0fe4 c0x0000 (---------------) + I kurashiki
+ 0x002487c6, // n0x0fe5 c0x0000 (---------------) + I maniwa
+ 0x00303986, // n0x0fe6 c0x0000 (---------------) + I misaki
+ 0x00240904, // n0x0fe7 c0x0000 (---------------) + I nagi
+ 0x0027cd05, // n0x0fe8 c0x0000 (---------------) + I niimi
+ 0x002dac0c, // n0x0fe9 c0x0000 (---------------) + I nishiawakura
+ 0x00266607, // n0x0fea c0x0000 (---------------) + I okayama
+ 0x00266c47, // n0x0feb c0x0000 (---------------) + I satosho
+ 0x002e9608, // n0x0fec c0x0000 (---------------) + I setouchi
+ 0x00285306, // n0x0fed c0x0000 (---------------) + I shinjo
+ 0x00340f44, // n0x0fee c0x0000 (---------------) + I shoo
+ 0x0030ad04, // n0x0fef c0x0000 (---------------) + I soja
+ 0x0023c909, // n0x0ff0 c0x0000 (---------------) + I takahashi
+ 0x002af9c6, // n0x0ff1 c0x0000 (---------------) + I tamano
+ 0x002601c7, // n0x0ff2 c0x0000 (---------------) + I tsuyama
+ 0x00295d44, // n0x0ff3 c0x0000 (---------------) + I wake
+ 0x002b4e06, // n0x0ff4 c0x0000 (---------------) + I yakage
+ 0x00342185, // n0x0ff5 c0x0000 (---------------) + I aguni
+ 0x0028d847, // n0x0ff6 c0x0000 (---------------) + I ginowan
+ 0x002fe946, // n0x0ff7 c0x0000 (---------------) + I ginoza
+ 0x00239cc9, // n0x0ff8 c0x0000 (---------------) + I gushikami
+ 0x002b0487, // n0x0ff9 c0x0000 (---------------) + I haebaru
+ 0x002545c7, // n0x0ffa c0x0000 (---------------) + I higashi
+ 0x0028c086, // n0x0ffb c0x0000 (---------------) + I hirara
+ 0x00231145, // n0x0ffc c0x0000 (---------------) + I iheya
+ 0x0026cf88, // n0x0ffd c0x0000 (---------------) + I ishigaki
+ 0x00214e48, // n0x0ffe c0x0000 (---------------) + I ishikawa
+ 0x0022a5c6, // n0x0fff c0x0000 (---------------) + I itoman
+ 0x00310645, // n0x1000 c0x0000 (---------------) + I izena
+ 0x002f0446, // n0x1001 c0x0000 (---------------) + I kadena
+ 0x00201b03, // n0x1002 c0x0000 (---------------) + I kin
+ 0x00283dc9, // n0x1003 c0x0000 (---------------) + I kitadaito
+ 0x0029168e, // n0x1004 c0x0000 (---------------) + I kitanakagusuku
+ 0x0029f548, // n0x1005 c0x0000 (---------------) + I kumejima
+ 0x002928c8, // n0x1006 c0x0000 (---------------) + I kunigami
+ 0x0022a3cb, // n0x1007 c0x0000 (---------------) + I minamidaito
+ 0x0027e686, // n0x1008 c0x0000 (---------------) + I motobu
+ 0x002402c4, // n0x1009 c0x0000 (---------------) + I nago
+ 0x0026ecc4, // n0x100a c0x0000 (---------------) + I naha
+ 0x0029178a, // n0x100b c0x0000 (---------------) + I nakagusuku
+ 0x0020d7c7, // n0x100c c0x0000 (---------------) + I nakijin
+ 0x002073c5, // n0x100d c0x0000 (---------------) + I nanjo
+ 0x0020e649, // n0x100e c0x0000 (---------------) + I nishihara
+ 0x002a6885, // n0x100f c0x0000 (---------------) + I ogimi
+ 0x003954c7, // n0x1010 c0x0000 (---------------) + I okinawa
+ 0x002988c4, // n0x1011 c0x0000 (---------------) + I onna
+ 0x00265dc7, // n0x1012 c0x0000 (---------------) + I shimoji
+ 0x003162c8, // n0x1013 c0x0000 (---------------) + I taketomi
+ 0x002a43c6, // n0x1014 c0x0000 (---------------) + I tarama
+ 0x002065c9, // n0x1015 c0x0000 (---------------) + I tokashiki
+ 0x002a2b4a, // n0x1016 c0x0000 (---------------) + I tomigusuku
+ 0x0020d746, // n0x1017 c0x0000 (---------------) + I tonaki
+ 0x00282306, // n0x1018 c0x0000 (---------------) + I urasoe
+ 0x00296205, // n0x1019 c0x0000 (---------------) + I uruma
+ 0x00361285, // n0x101a c0x0000 (---------------) + I yaese
+ 0x00305787, // n0x101b c0x0000 (---------------) + I yomitan
+ 0x00309c08, // n0x101c c0x0000 (---------------) + I yonabaru
+ 0x003420c8, // n0x101d c0x0000 (---------------) + I yonaguni
+ 0x00236b86, // n0x101e c0x0000 (---------------) + I zamami
+ 0x0031d9c5, // n0x101f c0x0000 (---------------) + I abeno
+ 0x002403ce, // n0x1020 c0x0000 (---------------) + I chihayaakasaka
+ 0x002be904, // n0x1021 c0x0000 (---------------) + I chuo
+ 0x0022a545, // n0x1022 c0x0000 (---------------) + I daito
+ 0x00265289, // n0x1023 c0x0000 (---------------) + I fujiidera
+ 0x00273148, // n0x1024 c0x0000 (---------------) + I habikino
+ 0x00278046, // n0x1025 c0x0000 (---------------) + I hannan
+ 0x0028630c, // n0x1026 c0x0000 (---------------) + I higashiosaka
+ 0x00287ed0, // n0x1027 c0x0000 (---------------) + I higashisumiyoshi
+ 0x0028a38f, // n0x1028 c0x0000 (---------------) + I higashiyodogawa
+ 0x0028b7c8, // n0x1029 c0x0000 (---------------) + I hirakata
+ 0x00316ac7, // n0x102a c0x0000 (---------------) + I ibaraki
+ 0x00390e45, // n0x102b c0x0000 (---------------) + I ikeda
+ 0x00209785, // n0x102c c0x0000 (---------------) + I izumi
+ 0x00272089, // n0x102d c0x0000 (---------------) + I izumiotsu
+ 0x00209789, // n0x102e c0x0000 (---------------) + I izumisano
+ 0x00216406, // n0x102f c0x0000 (---------------) + I kadoma
+ 0x002df247, // n0x1030 c0x0000 (---------------) + I kaizuka
+ 0x0037c505, // n0x1031 c0x0000 (---------------) + I kanan
+ 0x00375c89, // n0x1032 c0x0000 (---------------) + I kashiwara
+ 0x003175c6, // n0x1033 c0x0000 (---------------) + I katano
+ 0x0034e30d, // n0x1034 c0x0000 (---------------) + I kawachinagano
+ 0x0026d109, // n0x1035 c0x0000 (---------------) + I kishiwada
+ 0x00206e44, // n0x1036 c0x0000 (---------------) + I kita
+ 0x0029f2c8, // n0x1037 c0x0000 (---------------) + I kumatori
+ 0x0034e0c9, // n0x1038 c0x0000 (---------------) + I matsubara
+ 0x00339806, // n0x1039 c0x0000 (---------------) + I minato
+ 0x00265a05, // n0x103a c0x0000 (---------------) + I minoh
+ 0x00303986, // n0x103b c0x0000 (---------------) + I misaki
+ 0x00387b49, // n0x103c c0x0000 (---------------) + I moriguchi
+ 0x003896c8, // n0x103d c0x0000 (---------------) + I neyagawa
+ 0x00208b85, // n0x103e c0x0000 (---------------) + I nishi
+ 0x00257444, // n0x103f c0x0000 (---------------) + I nose
+ 0x002864cb, // n0x1040 c0x0000 (---------------) + I osakasayama
+ 0x003396c5, // n0x1041 c0x0000 (---------------) + I sakai
+ 0x00286606, // n0x1042 c0x0000 (---------------) + I sayama
+ 0x0026f406, // n0x1043 c0x0000 (---------------) + I sennan
+ 0x00243d86, // n0x1044 c0x0000 (---------------) + I settsu
+ 0x003260cb, // n0x1045 c0x0000 (---------------) + I shijonawate
+ 0x0027e549, // n0x1046 c0x0000 (---------------) + I shimamoto
+ 0x002e5b85, // n0x1047 c0x0000 (---------------) + I suita
+ 0x0035bf47, // n0x1048 c0x0000 (---------------) + I tadaoka
+ 0x0025fa46, // n0x1049 c0x0000 (---------------) + I taishi
+ 0x002345c6, // n0x104a c0x0000 (---------------) + I tajiri
+ 0x0026df08, // n0x104b c0x0000 (---------------) + I takaishi
+ 0x00376d89, // n0x104c c0x0000 (---------------) + I takatsuki
+ 0x00246f8c, // n0x104d c0x0000 (---------------) + I tondabayashi
+ 0x00351d08, // n0x104e c0x0000 (---------------) + I toyonaka
+ 0x002260c6, // n0x104f c0x0000 (---------------) + I toyono
+ 0x00330543, // n0x1050 c0x0000 (---------------) + I yao
+ 0x0027ebc6, // n0x1051 c0x0000 (---------------) + I ariake
+ 0x0025ac85, // n0x1052 c0x0000 (---------------) + I arita
+ 0x0026b1c8, // n0x1053 c0x0000 (---------------) + I fukudomi
+ 0x0021b746, // n0x1054 c0x0000 (---------------) + I genkai
+ 0x0028da88, // n0x1055 c0x0000 (---------------) + I hamatama
+ 0x00228245, // n0x1056 c0x0000 (---------------) + I hizen
+ 0x00352985, // n0x1057 c0x0000 (---------------) + I imari
+ 0x002877c8, // n0x1058 c0x0000 (---------------) + I kamimine
+ 0x002d2c87, // n0x1059 c0x0000 (---------------) + I kanzaki
+ 0x0033e987, // n0x105a c0x0000 (---------------) + I karatsu
+ 0x00341887, // n0x105b c0x0000 (---------------) + I kashima
+ 0x0022aa08, // n0x105c c0x0000 (---------------) + I kitagata
+ 0x00316c08, // n0x105d c0x0000 (---------------) + I kitahata
+ 0x00253286, // n0x105e c0x0000 (---------------) + I kiyama
+ 0x002f1947, // n0x105f c0x0000 (---------------) + I kouhoku
+ 0x002896c7, // n0x1060 c0x0000 (---------------) + I kyuragi
+ 0x00325dca, // n0x1061 c0x0000 (---------------) + I nishiarita
+ 0x00212803, // n0x1062 c0x0000 (---------------) + I ogi
+ 0x00353c06, // n0x1063 c0x0000 (---------------) + I omachi
+ 0x002069c5, // n0x1064 c0x0000 (---------------) + I ouchi
+ 0x00275104, // n0x1065 c0x0000 (---------------) + I saga
+ 0x00268409, // n0x1066 c0x0000 (---------------) + I shiroishi
+ 0x0025ad44, // n0x1067 c0x0000 (---------------) + I taku
+ 0x00253f44, // n0x1068 c0x0000 (---------------) + I tara
+ 0x002891c4, // n0x1069 c0x0000 (---------------) + I tosu
+ 0x0028a90b, // n0x106a c0x0000 (---------------) + I yoshinogari
+ 0x0034e247, // n0x106b c0x0000 (---------------) + I arakawa
+ 0x00240605, // n0x106c c0x0000 (---------------) + I asaka
+ 0x00280ac8, // n0x106d c0x0000 (---------------) + I chichibu
+ 0x00265906, // n0x106e c0x0000 (---------------) + I fujimi
+ 0x00265908, // n0x106f c0x0000 (---------------) + I fujimino
+ 0x00269d86, // n0x1070 c0x0000 (---------------) + I fukaya
+ 0x00278e45, // n0x1071 c0x0000 (---------------) + I hanno
+ 0x002794c5, // n0x1072 c0x0000 (---------------) + I hanyu
+ 0x0027b306, // n0x1073 c0x0000 (---------------) + I hasuda
+ 0x0027b788, // n0x1074 c0x0000 (---------------) + I hatogaya
+ 0x0027c2c8, // n0x1075 c0x0000 (---------------) + I hatoyama
+ 0x002685c6, // n0x1076 c0x0000 (---------------) + I hidaka
+ 0x0028090f, // n0x1077 c0x0000 (---------------) + I higashichichibu
+ 0x00283890, // n0x1078 c0x0000 (---------------) + I higashimatsuyama
+ 0x002028c5, // n0x1079 c0x0000 (---------------) + I honjo
+ 0x00200243, // n0x107a c0x0000 (---------------) + I ina
+ 0x002797c5, // n0x107b c0x0000 (---------------) + I iruma
+ 0x0030b3c8, // n0x107c c0x0000 (---------------) + I iwatsuki
+ 0x00209689, // n0x107d c0x0000 (---------------) + I kamiizumi
+ 0x00304f08, // n0x107e c0x0000 (---------------) + I kamikawa
+ 0x00340c88, // n0x107f c0x0000 (---------------) + I kamisato
+ 0x00391808, // n0x1080 c0x0000 (---------------) + I kasukabe
+ 0x002406c7, // n0x1081 c0x0000 (---------------) + I kawagoe
+ 0x002655c9, // n0x1082 c0x0000 (---------------) + I kawaguchi
+ 0x0028dc88, // n0x1083 c0x0000 (---------------) + I kawajima
+ 0x00369344, // n0x1084 c0x0000 (---------------) + I kazo
+ 0x00289048, // n0x1085 c0x0000 (---------------) + I kitamoto
+ 0x0028ec09, // n0x1086 c0x0000 (---------------) + I koshigaya
+ 0x00307ec7, // n0x1087 c0x0000 (---------------) + I kounosu
+ 0x00293e04, // n0x1088 c0x0000 (---------------) + I kuki
+ 0x0036c448, // n0x1089 c0x0000 (---------------) + I kumagaya
+ 0x0023144a, // n0x108a c0x0000 (---------------) + I matsubushi
+ 0x0031ef46, // n0x108b c0x0000 (---------------) + I minano
+ 0x0028b246, // n0x108c c0x0000 (---------------) + I misato
+ 0x0021ae49, // n0x108d c0x0000 (---------------) + I miyashiro
+ 0x00288107, // n0x108e c0x0000 (---------------) + I miyoshi
+ 0x002b62c8, // n0x108f c0x0000 (---------------) + I moroyama
+ 0x00201588, // n0x1090 c0x0000 (---------------) + I nagatoro
+ 0x00342d88, // n0x1091 c0x0000 (---------------) + I namegawa
+ 0x003721c5, // n0x1092 c0x0000 (---------------) + I niiza
+ 0x00362605, // n0x1093 c0x0000 (---------------) + I ogano
+ 0x00204045, // n0x1094 c0x0000 (---------------) + I ogawa
+ 0x0026e145, // n0x1095 c0x0000 (---------------) + I ogose
+ 0x00259347, // n0x1096 c0x0000 (---------------) + I okegawa
+ 0x00207d05, // n0x1097 c0x0000 (---------------) + I omiya
+ 0x002a8605, // n0x1098 c0x0000 (---------------) + I otaki
+ 0x00386ac6, // n0x1099 c0x0000 (---------------) + I ranzan
+ 0x00304e47, // n0x109a c0x0000 (---------------) + I ryokami
+ 0x002d0f07, // n0x109b c0x0000 (---------------) + I saitama
+ 0x00231746, // n0x109c c0x0000 (---------------) + I sakado
+ 0x002bb685, // n0x109d c0x0000 (---------------) + I satte
+ 0x00286606, // n0x109e c0x0000 (---------------) + I sayama
+ 0x002066c5, // n0x109f c0x0000 (---------------) + I shiki
+ 0x00298148, // n0x10a0 c0x0000 (---------------) + I shiraoka
+ 0x002cc0c4, // n0x10a1 c0x0000 (---------------) + I soka
+ 0x002adc86, // n0x10a2 c0x0000 (---------------) + I sugito
+ 0x00312e84, // n0x10a3 c0x0000 (---------------) + I toda
+ 0x002add88, // n0x10a4 c0x0000 (---------------) + I tokigawa
+ 0x00302c0a, // n0x10a5 c0x0000 (---------------) + I tokorozawa
+ 0x0027700c, // n0x10a6 c0x0000 (---------------) + I tsurugashima
+ 0x0020b805, // n0x10a7 c0x0000 (---------------) + I urawa
+ 0x00204106, // n0x10a8 c0x0000 (---------------) + I warabi
+ 0x00247146, // n0x10a9 c0x0000 (---------------) + I yashio
+ 0x002f5606, // n0x10aa c0x0000 (---------------) + I yokoze
+ 0x00226144, // n0x10ab c0x0000 (---------------) + I yono
+ 0x00375b45, // n0x10ac c0x0000 (---------------) + I yorii
+ 0x00269bc7, // n0x10ad c0x0000 (---------------) + I yoshida
+ 0x00288189, // n0x10ae c0x0000 (---------------) + I yoshikawa
+ 0x0028fac7, // n0x10af c0x0000 (---------------) + I yoshimi
+ 0x006735c4, // n0x10b0 c0x0001 (---------------) ! I city
+ 0x006735c4, // n0x10b1 c0x0001 (---------------) ! I city
+ 0x002f59c5, // n0x10b2 c0x0000 (---------------) + I aisho
+ 0x0023dd44, // n0x10b3 c0x0000 (---------------) + I gamo
+ 0x00285cca, // n0x10b4 c0x0000 (---------------) + I higashiomi
+ 0x00265786, // n0x10b5 c0x0000 (---------------) + I hikone
+ 0x00346344, // n0x10b6 c0x0000 (---------------) + I koka
+ 0x00204cc5, // n0x10b7 c0x0000 (---------------) + I konan
+ 0x002d7bc5, // n0x10b8 c0x0000 (---------------) + I kosei
+ 0x002e8104, // n0x10b9 c0x0000 (---------------) + I koto
+ 0x0026f047, // n0x10ba c0x0000 (---------------) + I kusatsu
+ 0x002b9f47, // n0x10bb c0x0000 (---------------) + I maibara
+ 0x002b4f88, // n0x10bc c0x0000 (---------------) + I moriyama
+ 0x0036df48, // n0x10bd c0x0000 (---------------) + I nagahama
+ 0x00208b89, // n0x10be c0x0000 (---------------) + I nishiazai
+ 0x003176c8, // n0x10bf c0x0000 (---------------) + I notogawa
+ 0x00285e8b, // n0x10c0 c0x0000 (---------------) + I omihachiman
+ 0x0022a784, // n0x10c1 c0x0000 (---------------) + I otsu
+ 0x0025e445, // n0x10c2 c0x0000 (---------------) + I ritto
+ 0x00258dc5, // n0x10c3 c0x0000 (---------------) + I ryuoh
+ 0x00341809, // n0x10c4 c0x0000 (---------------) + I takashima
+ 0x00376d89, // n0x10c5 c0x0000 (---------------) + I takatsuki
+ 0x00336348, // n0x10c6 c0x0000 (---------------) + I torahime
+ 0x00233d48, // n0x10c7 c0x0000 (---------------) + I toyosato
+ 0x00207004, // n0x10c8 c0x0000 (---------------) + I yasu
+ 0x002fe885, // n0x10c9 c0x0000 (---------------) + I akagi
+ 0x002068c3, // n0x10ca c0x0000 (---------------) + I ama
+ 0x0022a745, // n0x10cb c0x0000 (---------------) + I gotsu
+ 0x00292ac6, // n0x10cc c0x0000 (---------------) + I hamada
+ 0x0028124c, // n0x10cd c0x0000 (---------------) + I higashiizumo
+ 0x00214ec6, // n0x10ce c0x0000 (---------------) + I hikawa
+ 0x002fb046, // n0x10cf c0x0000 (---------------) + I hikimi
+ 0x002782c5, // n0x10d0 c0x0000 (---------------) + I izumo
+ 0x00201a88, // n0x10d1 c0x0000 (---------------) + I kakinoki
+ 0x002a2fc6, // n0x10d2 c0x0000 (---------------) + I masuda
+ 0x0038d9c6, // n0x10d3 c0x0000 (---------------) + I matsue
+ 0x0028b246, // n0x10d4 c0x0000 (---------------) + I misato
+ 0x0021f48c, // n0x10d5 c0x0000 (---------------) + I nishinoshima
+ 0x0024eac4, // n0x10d6 c0x0000 (---------------) + I ohda
+ 0x0030fd4a, // n0x10d7 c0x0000 (---------------) + I okinoshima
+ 0x00278208, // n0x10d8 c0x0000 (---------------) + I okuizumo
+ 0x00281087, // n0x10d9 c0x0000 (---------------) + I shimane
+ 0x00260c46, // n0x10da c0x0000 (---------------) + I tamayu
+ 0x002e5507, // n0x10db c0x0000 (---------------) + I tsuwano
+ 0x002ca4c5, // n0x10dc c0x0000 (---------------) + I unnan
+ 0x00311fc6, // n0x10dd c0x0000 (---------------) + I yakumo
+ 0x0033ba46, // n0x10de c0x0000 (---------------) + I yasugi
+ 0x00365987, // n0x10df c0x0000 (---------------) + I yatsuka
+ 0x00253f84, // n0x10e0 c0x0000 (---------------) + I arai
+ 0x0027d805, // n0x10e1 c0x0000 (---------------) + I atami
+ 0x00265284, // n0x10e2 c0x0000 (---------------) + I fuji
+ 0x00294e07, // n0x10e3 c0x0000 (---------------) + I fujieda
+ 0x002654c8, // n0x10e4 c0x0000 (---------------) + I fujikawa
+ 0x0026620a, // n0x10e5 c0x0000 (---------------) + I fujinomiya
+ 0x0026ce07, // n0x10e6 c0x0000 (---------------) + I fukuroi
+ 0x00271047, // n0x10e7 c0x0000 (---------------) + I gotemba
+ 0x00316a47, // n0x10e8 c0x0000 (---------------) + I haibara
+ 0x0032b6c9, // n0x10e9 c0x0000 (---------------) + I hamamatsu
+ 0x0028124a, // n0x10ea c0x0000 (---------------) + I higashiizu
+ 0x00220483, // n0x10eb c0x0000 (---------------) + I ito
+ 0x00253e85, // n0x10ec c0x0000 (---------------) + I iwata
+ 0x00209783, // n0x10ed c0x0000 (---------------) + I izu
+ 0x002f1c49, // n0x10ee c0x0000 (---------------) + I izunokuni
+ 0x002c7008, // n0x10ef c0x0000 (---------------) + I kakegawa
+ 0x002d17c7, // n0x10f0 c0x0000 (---------------) + I kannami
+ 0x00305009, // n0x10f1 c0x0000 (---------------) + I kawanehon
+ 0x00214f46, // n0x10f2 c0x0000 (---------------) + I kawazu
+ 0x0025fcc8, // n0x10f3 c0x0000 (---------------) + I kikugawa
+ 0x002d4745, // n0x10f4 c0x0000 (---------------) + I kosai
+ 0x00364b0a, // n0x10f5 c0x0000 (---------------) + I makinohara
+ 0x00355209, // n0x10f6 c0x0000 (---------------) + I matsuzaki
+ 0x00246c49, // n0x10f7 c0x0000 (---------------) + I minamiizu
+ 0x00392cc7, // n0x10f8 c0x0000 (---------------) + I mishima
+ 0x0028b509, // n0x10f9 c0x0000 (---------------) + I morimachi
+ 0x0020eb88, // n0x10fa c0x0000 (---------------) + I nishiizu
+ 0x002d2986, // n0x10fb c0x0000 (---------------) + I numazu
+ 0x002042c8, // n0x10fc c0x0000 (---------------) + I omaezaki
+ 0x0034e807, // n0x10fd c0x0000 (---------------) + I shimada
+ 0x002521c7, // n0x10fe c0x0000 (---------------) + I shimizu
+ 0x0027bc87, // n0x10ff c0x0000 (---------------) + I shimoda
+ 0x002b4b88, // n0x1100 c0x0000 (---------------) + I shizuoka
+ 0x002d2686, // n0x1101 c0x0000 (---------------) + I susono
+ 0x00231205, // n0x1102 c0x0000 (---------------) + I yaizu
+ 0x00269bc7, // n0x1103 c0x0000 (---------------) + I yoshida
+ 0x00281e08, // n0x1104 c0x0000 (---------------) + I ashikaga
+ 0x0030f544, // n0x1105 c0x0000 (---------------) + I bato
+ 0x002d6e04, // n0x1106 c0x0000 (---------------) + I haga
+ 0x00315b07, // n0x1107 c0x0000 (---------------) + I ichikai
+ 0x00255847, // n0x1108 c0x0000 (---------------) + I iwafune
+ 0x0022eb8a, // n0x1109 c0x0000 (---------------) + I kaminokawa
+ 0x002d2906, // n0x110a c0x0000 (---------------) + I kanuma
+ 0x0027afca, // n0x110b c0x0000 (---------------) + I karasuyama
+ 0x002a6b47, // n0x110c c0x0000 (---------------) + I kuroiso
+ 0x002be6c7, // n0x110d c0x0000 (---------------) + I mashiko
+ 0x0024fa44, // n0x110e c0x0000 (---------------) + I mibu
+ 0x002669c4, // n0x110f c0x0000 (---------------) + I moka
+ 0x0021ff46, // n0x1110 c0x0000 (---------------) + I motegi
+ 0x002efac4, // n0x1111 c0x0000 (---------------) + I nasu
+ 0x002efacc, // n0x1112 c0x0000 (---------------) + I nasushiobara
+ 0x0033e185, // n0x1113 c0x0000 (---------------) + I nikko
+ 0x0020f249, // n0x1114 c0x0000 (---------------) + I nishikata
+ 0x00267304, // n0x1115 c0x0000 (---------------) + I nogi
+ 0x002e81c5, // n0x1116 c0x0000 (---------------) + I ohira
+ 0x0025c988, // n0x1117 c0x0000 (---------------) + I ohtawara
+ 0x0027c385, // n0x1118 c0x0000 (---------------) + I oyama
+ 0x002f4d46, // n0x1119 c0x0000 (---------------) + I sakura
+ 0x002098c4, // n0x111a c0x0000 (---------------) + I sano
+ 0x0027924a, // n0x111b c0x0000 (---------------) + I shimotsuke
+ 0x002d80c6, // n0x111c c0x0000 (---------------) + I shioya
+ 0x00316d8a, // n0x111d c0x0000 (---------------) + I takanezawa
+ 0x0030f5c7, // n0x111e c0x0000 (---------------) + I tochigi
+ 0x0021d305, // n0x111f c0x0000 (---------------) + I tsuga
+ 0x00212d45, // n0x1120 c0x0000 (---------------) + I ujiie
+ 0x0021ee0a, // n0x1121 c0x0000 (---------------) + I utsunomiya
+ 0x0028c645, // n0x1122 c0x0000 (---------------) + I yaita
+ 0x00272046, // n0x1123 c0x0000 (---------------) + I aizumi
+ 0x00204e44, // n0x1124 c0x0000 (---------------) + I anan
+ 0x002a0706, // n0x1125 c0x0000 (---------------) + I ichiba
+ 0x00305845, // n0x1126 c0x0000 (---------------) + I itano
+ 0x0021b806, // n0x1127 c0x0000 (---------------) + I kainan
+ 0x002be44c, // n0x1128 c0x0000 (---------------) + I komatsushima
+ 0x002b644a, // n0x1129 c0x0000 (---------------) + I matsushige
+ 0x0025b9c4, // n0x112a c0x0000 (---------------) + I mima
+ 0x00213fc6, // n0x112b c0x0000 (---------------) + I minami
+ 0x00288107, // n0x112c c0x0000 (---------------) + I miyoshi
+ 0x002b9b44, // n0x112d c0x0000 (---------------) + I mugi
+ 0x002934c8, // n0x112e c0x0000 (---------------) + I nakagawa
+ 0x00376a06, // n0x112f c0x0000 (---------------) + I naruto
+ 0x00240249, // n0x1130 c0x0000 (---------------) + I sanagochi
+ 0x002c2189, // n0x1131 c0x0000 (---------------) + I shishikui
+ 0x0028e2c9, // n0x1132 c0x0000 (---------------) + I tokushima
+ 0x00359bc6, // n0x1133 c0x0000 (---------------) + I wajiki
+ 0x0034e906, // n0x1134 c0x0000 (---------------) + I adachi
+ 0x00204407, // n0x1135 c0x0000 (---------------) + I akiruno
+ 0x0038be88, // n0x1136 c0x0000 (---------------) + I akishima
+ 0x0034e709, // n0x1137 c0x0000 (---------------) + I aogashima
+ 0x0034e247, // n0x1138 c0x0000 (---------------) + I arakawa
+ 0x0027e786, // n0x1139 c0x0000 (---------------) + I bunkyo
+ 0x002e3947, // n0x113a c0x0000 (---------------) + I chiyoda
+ 0x0029a7c5, // n0x113b c0x0000 (---------------) + I chofu
+ 0x002be904, // n0x113c c0x0000 (---------------) + I chuo
+ 0x00203fc7, // n0x113d c0x0000 (---------------) + I edogawa
+ 0x002ab685, // n0x113e c0x0000 (---------------) + I fuchu
+ 0x00275045, // n0x113f c0x0000 (---------------) + I fussa
+ 0x00389ec7, // n0x1140 c0x0000 (---------------) + I hachijo
+ 0x00243188, // n0x1141 c0x0000 (---------------) + I hachioji
+ 0x00374346, // n0x1142 c0x0000 (---------------) + I hamura
+ 0x00282c8d, // n0x1143 c0x0000 (---------------) + I higashikurume
+ 0x0028414f, // n0x1144 c0x0000 (---------------) + I higashimurayama
+ 0x00289bcd, // n0x1145 c0x0000 (---------------) + I higashiyamato
+ 0x0021ad44, // n0x1146 c0x0000 (---------------) + I hino
+ 0x00227446, // n0x1147 c0x0000 (---------------) + I hinode
+ 0x002bcf88, // n0x1148 c0x0000 (---------------) + I hinohara
+ 0x002408c5, // n0x1149 c0x0000 (---------------) + I inagi
+ 0x00325f88, // n0x114a c0x0000 (---------------) + I itabashi
+ 0x002513ca, // n0x114b c0x0000 (---------------) + I katsushika
+ 0x00206e44, // n0x114c c0x0000 (---------------) + I kita
+ 0x002978c6, // n0x114d c0x0000 (---------------) + I kiyose
+ 0x002a3687, // n0x114e c0x0000 (---------------) + I kodaira
+ 0x00232c87, // n0x114f c0x0000 (---------------) + I koganei
+ 0x00374809, // n0x1150 c0x0000 (---------------) + I kokubunji
+ 0x00204285, // n0x1151 c0x0000 (---------------) + I komae
+ 0x002e8104, // n0x1152 c0x0000 (---------------) + I koto
+ 0x0031850a, // n0x1153 c0x0000 (---------------) + I kouzushima
+ 0x002a2089, // n0x1154 c0x0000 (---------------) + I kunitachi
+ 0x0028b607, // n0x1155 c0x0000 (---------------) + I machida
+ 0x002b1286, // n0x1156 c0x0000 (---------------) + I meguro
+ 0x00339806, // n0x1157 c0x0000 (---------------) + I minato
+ 0x002fe7c6, // n0x1158 c0x0000 (---------------) + I mitaka
+ 0x0034b046, // n0x1159 c0x0000 (---------------) + I mizuho
+ 0x002bc90f, // n0x115a c0x0000 (---------------) + I musashimurayama
+ 0x002bce49, // n0x115b c0x0000 (---------------) + I musashino
+ 0x0023fcc6, // n0x115c c0x0000 (---------------) + I nakano
+ 0x003528c6, // n0x115d c0x0000 (---------------) + I nerima
+ 0x00301849, // n0x115e c0x0000 (---------------) + I ogasawara
+ 0x002f1a47, // n0x115f c0x0000 (---------------) + I okutama
+ 0x0020b3c3, // n0x1160 c0x0000 (---------------) + I ome
+ 0x0021f606, // n0x1161 c0x0000 (---------------) + I oshima
+ 0x00203943, // n0x1162 c0x0000 (---------------) + I ota
+ 0x002c3488, // n0x1163 c0x0000 (---------------) + I setagaya
+ 0x002e3787, // n0x1164 c0x0000 (---------------) + I shibuya
+ 0x0028b9c9, // n0x1165 c0x0000 (---------------) + I shinagawa
+ 0x00285788, // n0x1166 c0x0000 (---------------) + I shinjuku
+ 0x0033eac8, // n0x1167 c0x0000 (---------------) + I suginami
+ 0x00289246, // n0x1168 c0x0000 (---------------) + I sumida
+ 0x0032da89, // n0x1169 c0x0000 (---------------) + I tachikawa
+ 0x002e5c45, // n0x116a c0x0000 (---------------) + I taito
+ 0x00260c44, // n0x116b c0x0000 (---------------) + I tama
+ 0x002d0a47, // n0x116c c0x0000 (---------------) + I toshima
+ 0x00258c05, // n0x116d c0x0000 (---------------) + I chizu
+ 0x0021ad44, // n0x116e c0x0000 (---------------) + I hino
+ 0x00274b88, // n0x116f c0x0000 (---------------) + I kawahara
+ 0x0020da84, // n0x1170 c0x0000 (---------------) + I koge
+ 0x002eaf87, // n0x1171 c0x0000 (---------------) + I kotoura
+ 0x003337c6, // n0x1172 c0x0000 (---------------) + I misasa
+ 0x002c6705, // n0x1173 c0x0000 (---------------) + I nanbu
+ 0x0027fa88, // n0x1174 c0x0000 (---------------) + I nichinan
+ 0x003396cb, // n0x1175 c0x0000 (---------------) + I sakaiminato
+ 0x002e2787, // n0x1176 c0x0000 (---------------) + I tottori
+ 0x0021f086, // n0x1177 c0x0000 (---------------) + I wakasa
+ 0x002aeec4, // n0x1178 c0x0000 (---------------) + I yazu
+ 0x0032b186, // n0x1179 c0x0000 (---------------) + I yonago
+ 0x002ad5c5, // n0x117a c0x0000 (---------------) + I asahi
+ 0x002ab685, // n0x117b c0x0000 (---------------) + I fuchu
+ 0x0026c2c9, // n0x117c c0x0000 (---------------) + I fukumitsu
+ 0x0026ec49, // n0x117d c0x0000 (---------------) + I funahashi
+ 0x00252204, // n0x117e c0x0000 (---------------) + I himi
+ 0x00252245, // n0x117f c0x0000 (---------------) + I imizu
+ 0x00214005, // n0x1180 c0x0000 (---------------) + I inami
+ 0x00364986, // n0x1181 c0x0000 (---------------) + I johana
+ 0x00315a08, // n0x1182 c0x0000 (---------------) + I kamiichi
+ 0x002a4c46, // n0x1183 c0x0000 (---------------) + I kurobe
+ 0x0031070b, // n0x1184 c0x0000 (---------------) + I nakaniikawa
+ 0x0029894a, // n0x1185 c0x0000 (---------------) + I namerikawa
+ 0x002e14c5, // n0x1186 c0x0000 (---------------) + I nanto
+ 0x00279546, // n0x1187 c0x0000 (---------------) + I nyuzen
+ 0x0031d945, // n0x1188 c0x0000 (---------------) + I oyabe
+ 0x00205705, // n0x1189 c0x0000 (---------------) + I taira
+ 0x00281747, // n0x118a c0x0000 (---------------) + I takaoka
+ 0x0038a708, // n0x118b c0x0000 (---------------) + I tateyama
+ 0x0027b804, // n0x118c c0x0000 (---------------) + I toga
+ 0x0024e006, // n0x118d c0x0000 (---------------) + I tonami
+ 0x0027c346, // n0x118e c0x0000 (---------------) + I toyama
+ 0x0020ed47, // n0x118f c0x0000 (---------------) + I unazuki
+ 0x002e3d84, // n0x1190 c0x0000 (---------------) + I uozu
+ 0x0026b046, // n0x1191 c0x0000 (---------------) + I yamada
+ 0x00250d45, // n0x1192 c0x0000 (---------------) + I arida
+ 0x00250d49, // n0x1193 c0x0000 (---------------) + I aridagawa
+ 0x0034eb04, // n0x1194 c0x0000 (---------------) + I gobo
+ 0x00286d89, // n0x1195 c0x0000 (---------------) + I hashimoto
+ 0x002685c6, // n0x1196 c0x0000 (---------------) + I hidaka
+ 0x002a9408, // n0x1197 c0x0000 (---------------) + I hirogawa
+ 0x00214005, // n0x1198 c0x0000 (---------------) + I inami
+ 0x002e9845, // n0x1199 c0x0000 (---------------) + I iwade
+ 0x0021b806, // n0x119a c0x0000 (---------------) + I kainan
+ 0x00246e89, // n0x119b c0x0000 (---------------) + I kamitonda
+ 0x002133c9, // n0x119c c0x0000 (---------------) + I katsuragi
+ 0x002fb0c6, // n0x119d c0x0000 (---------------) + I kimino
+ 0x00273248, // n0x119e c0x0000 (---------------) + I kinokawa
+ 0x0022aec8, // n0x119f c0x0000 (---------------) + I kitayama
+ 0x0031d904, // n0x11a0 c0x0000 (---------------) + I koya
+ 0x0034b544, // n0x11a1 c0x0000 (---------------) + I koza
+ 0x0034b548, // n0x11a2 c0x0000 (---------------) + I kozagawa
+ 0x003000c8, // n0x11a3 c0x0000 (---------------) + I kudoyama
+ 0x002e5e09, // n0x11a4 c0x0000 (---------------) + I kushimoto
+ 0x00292a46, // n0x11a5 c0x0000 (---------------) + I mihama
+ 0x0028b246, // n0x11a6 c0x0000 (---------------) + I misato
+ 0x002fd2cd, // n0x11a7 c0x0000 (---------------) + I nachikatsuura
+ 0x002e6a46, // n0x11a8 c0x0000 (---------------) + I shingu
+ 0x002a1409, // n0x11a9 c0x0000 (---------------) + I shirahama
+ 0x00353dc5, // n0x11aa c0x0000 (---------------) + I taiji
+ 0x0030f946, // n0x11ab c0x0000 (---------------) + I tanabe
+ 0x0032dc48, // n0x11ac c0x0000 (---------------) + I wakayama
+ 0x002f9ac5, // n0x11ad c0x0000 (---------------) + I yuasa
+ 0x00289704, // n0x11ae c0x0000 (---------------) + I yura
+ 0x002ad5c5, // n0x11af c0x0000 (---------------) + I asahi
+ 0x0026e788, // n0x11b0 c0x0000 (---------------) + I funagata
+ 0x00285a89, // n0x11b1 c0x0000 (---------------) + I higashine
+ 0x00265344, // n0x11b2 c0x0000 (---------------) + I iide
+ 0x00226886, // n0x11b3 c0x0000 (---------------) + I kahoku
+ 0x003467ca, // n0x11b4 c0x0000 (---------------) + I kaminoyama
+ 0x002150c8, // n0x11b5 c0x0000 (---------------) + I kaneyama
+ 0x0022ed09, // n0x11b6 c0x0000 (---------------) + I kawanishi
+ 0x002f8d4a, // n0x11b7 c0x0000 (---------------) + I mamurogawa
+ 0x00304f86, // n0x11b8 c0x0000 (---------------) + I mikawa
+ 0x00284308, // n0x11b9 c0x0000 (---------------) + I murayama
+ 0x0035e9c5, // n0x11ba c0x0000 (---------------) + I nagai
+ 0x00355088, // n0x11bb c0x0000 (---------------) + I nakayama
+ 0x002a0545, // n0x11bc c0x0000 (---------------) + I nanyo
+ 0x00214e09, // n0x11bd c0x0000 (---------------) + I nishikawa
+ 0x0034fd89, // n0x11be c0x0000 (---------------) + I obanazawa
+ 0x00210a82, // n0x11bf c0x0000 (---------------) + I oe
+ 0x0029efc5, // n0x11c0 c0x0000 (---------------) + I oguni
+ 0x00258e86, // n0x11c1 c0x0000 (---------------) + I ohkura
+ 0x00268507, // n0x11c2 c0x0000 (---------------) + I oishida
+ 0x00275105, // n0x11c3 c0x0000 (---------------) + I sagae
+ 0x002f9b86, // n0x11c4 c0x0000 (---------------) + I sakata
+ 0x00359408, // n0x11c5 c0x0000 (---------------) + I sakegawa
+ 0x00285306, // n0x11c6 c0x0000 (---------------) + I shinjo
+ 0x0029cd89, // n0x11c7 c0x0000 (---------------) + I shirataka
+ 0x00266d46, // n0x11c8 c0x0000 (---------------) + I shonai
+ 0x0026dd88, // n0x11c9 c0x0000 (---------------) + I takahata
+ 0x00294245, // n0x11ca c0x0000 (---------------) + I tendo
+ 0x00247ec6, // n0x11cb c0x0000 (---------------) + I tozawa
+ 0x003052c8, // n0x11cc c0x0000 (---------------) + I tsuruoka
+ 0x0026dc08, // n0x11cd c0x0000 (---------------) + I yamagata
+ 0x0034d348, // n0x11ce c0x0000 (---------------) + I yamanobe
+ 0x002212c8, // n0x11cf c0x0000 (---------------) + I yonezawa
+ 0x00216b84, // n0x11d0 c0x0000 (---------------) + I yuza
+ 0x0021bc43, // n0x11d1 c0x0000 (---------------) + I abu
+ 0x0029cfc4, // n0x11d2 c0x0000 (---------------) + I hagi
+ 0x002af4c6, // n0x11d3 c0x0000 (---------------) + I hikari
+ 0x0029a804, // n0x11d4 c0x0000 (---------------) + I hofu
+ 0x00292807, // n0x11d5 c0x0000 (---------------) + I iwakuni
+ 0x0038d8c9, // n0x11d6 c0x0000 (---------------) + I kudamatsu
+ 0x002ae205, // n0x11d7 c0x0000 (---------------) + I mitou
+ 0x00201586, // n0x11d8 c0x0000 (---------------) + I nagato
+ 0x0021f606, // n0x11d9 c0x0000 (---------------) + I oshima
+ 0x002743cb, // n0x11da c0x0000 (---------------) + I shimonoseki
+ 0x002e1406, // n0x11db c0x0000 (---------------) + I shunan
+ 0x003003c6, // n0x11dc c0x0000 (---------------) + I tabuse
+ 0x00286f48, // n0x11dd c0x0000 (---------------) + I tokuyama
+ 0x00338906, // n0x11de c0x0000 (---------------) + I toyota
+ 0x002b48c3, // n0x11df c0x0000 (---------------) + I ube
+ 0x00213ec3, // n0x11e0 c0x0000 (---------------) + I yuu
+ 0x002be904, // n0x11e1 c0x0000 (---------------) + I chuo
+ 0x002243c5, // n0x11e2 c0x0000 (---------------) + I doshi
+ 0x00289887, // n0x11e3 c0x0000 (---------------) + I fuefuki
+ 0x002654c8, // n0x11e4 c0x0000 (---------------) + I fujikawa
+ 0x002654cf, // n0x11e5 c0x0000 (---------------) + I fujikawaguchiko
+ 0x00269acb, // n0x11e6 c0x0000 (---------------) + I fujiyoshida
+ 0x00315808, // n0x11e7 c0x0000 (---------------) + I hayakawa
+ 0x00226906, // n0x11e8 c0x0000 (---------------) + I hokuto
+ 0x00312b8e, // n0x11e9 c0x0000 (---------------) + I ichikawamisato
+ 0x0021b803, // n0x11ea c0x0000 (---------------) + I kai
+ 0x0024cf84, // n0x11eb c0x0000 (---------------) + I kofu
+ 0x002e1385, // n0x11ec c0x0000 (---------------) + I koshu
+ 0x002e4a46, // n0x11ed c0x0000 (---------------) + I kosuge
+ 0x0027a9cb, // n0x11ee c0x0000 (---------------) + I minami-alps
+ 0x0027ee46, // n0x11ef c0x0000 (---------------) + I minobu
+ 0x00204889, // n0x11f0 c0x0000 (---------------) + I nakamichi
+ 0x002c6705, // n0x11f1 c0x0000 (---------------) + I nanbu
+ 0x00372f88, // n0x11f2 c0x0000 (---------------) + I narusawa
+ 0x00208608, // n0x11f3 c0x0000 (---------------) + I nirasaki
+ 0x0021328c, // n0x11f4 c0x0000 (---------------) + I nishikatsura
+ 0x0028a946, // n0x11f5 c0x0000 (---------------) + I oshino
+ 0x0022a786, // n0x11f6 c0x0000 (---------------) + I otsuki
+ 0x002b3885, // n0x11f7 c0x0000 (---------------) + I showa
+ 0x00275f88, // n0x11f8 c0x0000 (---------------) + I tabayama
+ 0x0026a445, // n0x11f9 c0x0000 (---------------) + I tsuru
+ 0x0038dac8, // n0x11fa c0x0000 (---------------) + I uenohara
+ 0x0028a00a, // n0x11fb c0x0000 (---------------) + I yamanakako
+ 0x0029b889, // n0x11fc c0x0000 (---------------) + I yamanashi
+ 0x006735c4, // n0x11fd c0x0001 (---------------) ! I city
+ 0x2d600742, // n0x11fe c0x00b5 (n0x11ff-n0x1200) o I co
+ 0x000e4188, // n0x11ff c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1200 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1201 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1202 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1203 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1204 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1205 c0x0000 (---------------) + I org
+ 0x00310603, // n0x1206 c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x1207 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1208 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1209 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x120a c0x0000 (---------------) + I info
+ 0x002170c3, // n0x120b c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x120c c0x0000 (---------------) + I org
+ 0x002362c3, // n0x120d c0x0000 (---------------) + I ass
+ 0x002729c4, // n0x120e c0x0000 (---------------) + I asso
+ 0x00222ac3, // n0x120f c0x0000 (---------------) + I com
+ 0x00228d44, // n0x1210 c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x1211 c0x0000 (---------------) + I edu
+ 0x003579c4, // n0x1212 c0x0000 (---------------) + I gouv
+ 0x0021e283, // n0x1213 c0x0000 (---------------) + I gov
+ 0x002753c7, // n0x1214 c0x0000 (---------------) + I medecin
+ 0x0023fa03, // n0x1215 c0x0000 (---------------) + I mil
+ 0x00207cc3, // n0x1216 c0x0000 (---------------) + I nom
+ 0x00265c08, // n0x1217 c0x0000 (---------------) + I notaires
+ 0x0021dcc3, // n0x1218 c0x0000 (---------------) + I org
+ 0x002e078b, // n0x1219 c0x0000 (---------------) + I pharmaciens
+ 0x002ca783, // n0x121a c0x0000 (---------------) + I prd
+ 0x0029abc6, // n0x121b c0x0000 (---------------) + I presse
+ 0x00208902, // n0x121c c0x0000 (---------------) + I tm
+ 0x0036b18b, // n0x121d c0x0000 (---------------) + I veterinaire
+ 0x002d75c3, // n0x121e c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x121f c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1220 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1221 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1222 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1223 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1224 c0x0000 (---------------) + I gov
+ 0x0021dcc3, // n0x1225 c0x0000 (---------------) + I org
+ 0x002117c3, // n0x1226 c0x0000 (---------------) + I rep
+ 0x00210103, // n0x1227 c0x0000 (---------------) + I tra
+ 0x00201e82, // n0x1228 c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x1229 c0x0000 (---------------) + blogspot
+ 0x002224c5, // n0x122a c0x0000 (---------------) + I busan
+ 0x002ea8c8, // n0x122b c0x0000 (---------------) + I chungbuk
+ 0x002ec1c8, // n0x122c c0x0000 (---------------) + I chungnam
+ 0x00200742, // n0x122d c0x0000 (---------------) + I co
+ 0x0032b905, // n0x122e c0x0000 (---------------) + I daegu
+ 0x0023e947, // n0x122f c0x0000 (---------------) + I daejeon
+ 0x002010c2, // n0x1230 c0x0000 (---------------) + I es
+ 0x00204ac7, // n0x1231 c0x0000 (---------------) + I gangwon
+ 0x00202342, // n0x1232 c0x0000 (---------------) + I go
+ 0x00370b47, // n0x1233 c0x0000 (---------------) + I gwangju
+ 0x00384c89, // n0x1234 c0x0000 (---------------) + I gyeongbuk
+ 0x0035e588, // n0x1235 c0x0000 (---------------) + I gyeonggi
+ 0x00342c09, // n0x1236 c0x0000 (---------------) + I gyeongnam
+ 0x00203802, // n0x1237 c0x0000 (---------------) + I hs
+ 0x002c2a87, // n0x1238 c0x0000 (---------------) + I incheon
+ 0x002fae44, // n0x1239 c0x0000 (---------------) + I jeju
+ 0x0023ea07, // n0x123a c0x0000 (---------------) + I jeonbuk
+ 0x00298847, // n0x123b c0x0000 (---------------) + I jeonnam
+ 0x002a3fc2, // n0x123c c0x0000 (---------------) + I kg
+ 0x0023fa03, // n0x123d c0x0000 (---------------) + I mil
+ 0x00209282, // n0x123e c0x0000 (---------------) + I ms
+ 0x00201082, // n0x123f c0x0000 (---------------) + I ne
+ 0x00200c42, // n0x1240 c0x0000 (---------------) + I or
+ 0x00214942, // n0x1241 c0x0000 (---------------) + I pe
+ 0x002030c2, // n0x1242 c0x0000 (---------------) + I re
+ 0x00200982, // n0x1243 c0x0000 (---------------) + I sc
+ 0x002baac5, // n0x1244 c0x0000 (---------------) + I seoul
+ 0x002376c5, // n0x1245 c0x0000 (---------------) + I ulsan
+ 0x00222ac3, // n0x1246 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1247 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1248 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1249 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x124a c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x124b c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x124c c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x124d c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x124e c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x124f c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1250 c0x0000 (---------------) + I org
+ 0x00000741, // n0x1251 c0x0000 (---------------) + c
+ 0x00222ac3, // n0x1252 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1253 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1254 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1255 c0x0000 (---------------) + I info
+ 0x00238c03, // n0x1256 c0x0000 (---------------) + I int
+ 0x002170c3, // n0x1257 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1258 c0x0000 (---------------) + I org
+ 0x00214943, // n0x1259 c0x0000 (---------------) + I per
+ 0x00222ac3, // n0x125a c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x125b c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x125c c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x125d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x125e c0x0000 (---------------) + I org
+ 0x00200742, // n0x125f c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1260 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1261 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1262 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1263 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1264 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1265 c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x1266 c0x0000 (---------------) + I ac
+ 0x002aa2c4, // n0x1267 c0x0000 (---------------) + I assn
+ 0x00222ac3, // n0x1268 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1269 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x126a c0x0000 (---------------) + I gov
+ 0x00221d03, // n0x126b c0x0000 (---------------) + I grp
+ 0x00294945, // n0x126c c0x0000 (---------------) + I hotel
+ 0x00238c03, // n0x126d c0x0000 (---------------) + I int
+ 0x003413c3, // n0x126e c0x0000 (---------------) + I ltd
+ 0x002170c3, // n0x126f c0x0000 (---------------) + I net
+ 0x00202303, // n0x1270 c0x0000 (---------------) + I ngo
+ 0x0021dcc3, // n0x1271 c0x0000 (---------------) + I org
+ 0x00206103, // n0x1272 c0x0000 (---------------) + I sch
+ 0x00240a03, // n0x1273 c0x0000 (---------------) + I soc
+ 0x00219fc3, // n0x1274 c0x0000 (---------------) + I web
+ 0x00222ac3, // n0x1275 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1276 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1277 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1278 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1279 c0x0000 (---------------) + I org
+ 0x00200742, // n0x127a c0x0000 (---------------) + I co
+ 0x0021dcc3, // n0x127b c0x0000 (---------------) + I org
+ 0x000e4188, // n0x127c c0x0000 (---------------) + blogspot
+ 0x0021e283, // n0x127d c0x0000 (---------------) + I gov
+ 0x000e4188, // n0x127e c0x0000 (---------------) + blogspot
+ 0x002a00c3, // n0x127f c0x0000 (---------------) + I asn
+ 0x00222ac3, // n0x1280 c0x0000 (---------------) + I com
+ 0x00224984, // n0x1281 c0x0000 (---------------) + I conf
+ 0x002d75c3, // n0x1282 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1283 c0x0000 (---------------) + I gov
+ 0x00206202, // n0x1284 c0x0000 (---------------) + I id
+ 0x0023fa03, // n0x1285 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1286 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1287 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1288 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1289 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x128a c0x0000 (---------------) + I gov
+ 0x00206202, // n0x128b c0x0000 (---------------) + I id
+ 0x0020b403, // n0x128c c0x0000 (---------------) + I med
+ 0x002170c3, // n0x128d c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x128e c0x0000 (---------------) + I org
+ 0x002c65c3, // n0x128f c0x0000 (---------------) + I plc
+ 0x00206103, // n0x1290 c0x0000 (---------------) + I sch
+ 0x00201e82, // n0x1291 c0x0000 (---------------) + I ac
+ 0x00200742, // n0x1292 c0x0000 (---------------) + I co
+ 0x0021e283, // n0x1293 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1294 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1295 c0x0000 (---------------) + I org
+ 0x0029abc5, // n0x1296 c0x0000 (---------------) + I press
+ 0x002729c4, // n0x1297 c0x0000 (---------------) + I asso
+ 0x00208902, // n0x1298 c0x0000 (---------------) + I tm
+ 0x000e4188, // n0x1299 c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x129a c0x0000 (---------------) + I ac
+ 0x00200742, // n0x129b c0x0000 (---------------) + I co
+ 0x002d75c3, // n0x129c c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x129d c0x0000 (---------------) + I gov
+ 0x00226ac3, // n0x129e c0x0000 (---------------) + I its
+ 0x002170c3, // n0x129f c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12a0 c0x0000 (---------------) + I org
+ 0x002cba44, // n0x12a1 c0x0000 (---------------) + I priv
+ 0x00222ac3, // n0x12a2 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x12a3 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x12a4 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x12a5 c0x0000 (---------------) + I mil
+ 0x00207cc3, // n0x12a6 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x12a7 c0x0000 (---------------) + I org
+ 0x002ca783, // n0x12a8 c0x0000 (---------------) + I prd
+ 0x00208902, // n0x12a9 c0x0000 (---------------) + I tm
+ 0x000e4188, // n0x12aa c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x12ab c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x12ac c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x12ad c0x0000 (---------------) + I gov
+ 0x00200303, // n0x12ae c0x0000 (---------------) + I inf
+ 0x00298944, // n0x12af c0x0000 (---------------) + I name
+ 0x002170c3, // n0x12b0 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12b1 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x12b2 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x12b3 c0x0000 (---------------) + I edu
+ 0x003579c4, // n0x12b4 c0x0000 (---------------) + I gouv
+ 0x0021e283, // n0x12b5 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x12b6 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12b7 c0x0000 (---------------) + I org
+ 0x0029abc6, // n0x12b8 c0x0000 (---------------) + I presse
+ 0x002d75c3, // n0x12b9 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x12ba c0x0000 (---------------) + I gov
+ 0x00023403, // n0x12bb c0x0000 (---------------) + nyc
+ 0x0021dcc3, // n0x12bc c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x12bd c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x12be c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x12bf c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x12c0 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12c1 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x12c2 c0x0000 (---------------) + blogspot
+ 0x0021e283, // n0x12c3 c0x0000 (---------------) + I gov
+ 0x00222ac3, // n0x12c4 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x12c5 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x12c6 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x12c7 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12c8 c0x0000 (---------------) + I org
+ 0x35622ac3, // n0x12c9 c0x00d5 (n0x12cd-n0x12ce) + I com
+ 0x002d75c3, // n0x12ca c0x0000 (---------------) + I edu
+ 0x002170c3, // n0x12cb c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x12cc c0x0000 (---------------) + I org
+ 0x000e4188, // n0x12cd c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x12ce c0x0000 (---------------) + I ac
+ 0x00200742, // n0x12cf c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x12d0 c0x0000 (---------------) + I com
+ 0x0021e283, // n0x12d1 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x12d2 c0x0000 (---------------) + I net
+ 0x00200c42, // n0x12d3 c0x0000 (---------------) + I or
+ 0x0021dcc3, // n0x12d4 c0x0000 (---------------) + I org
+ 0x002f2787, // n0x12d5 c0x0000 (---------------) + I academy
+ 0x0033c2cb, // n0x12d6 c0x0000 (---------------) + I agriculture
+ 0x00205743, // n0x12d7 c0x0000 (---------------) + I air
+ 0x0022c148, // n0x12d8 c0x0000 (---------------) + I airguard
+ 0x0038bc07, // n0x12d9 c0x0000 (---------------) + I alabama
+ 0x00267646, // n0x12da c0x0000 (---------------) + I alaska
+ 0x002b6e85, // n0x12db c0x0000 (---------------) + I amber
+ 0x003541c9, // n0x12dc c0x0000 (---------------) + I ambulance
+ 0x002757c8, // n0x12dd c0x0000 (---------------) + I american
+ 0x002757c9, // n0x12de c0x0000 (---------------) + I americana
+ 0x002757d0, // n0x12df c0x0000 (---------------) + I americanantiques
+ 0x0031aecb, // n0x12e0 c0x0000 (---------------) + I americanart
+ 0x002b6cc9, // n0x12e1 c0x0000 (---------------) + I amsterdam
+ 0x00208f03, // n0x12e2 c0x0000 (---------------) + I and
+ 0x003228c9, // n0x12e3 c0x0000 (---------------) + I annefrank
+ 0x00225786, // n0x12e4 c0x0000 (---------------) + I anthro
+ 0x0022578c, // n0x12e5 c0x0000 (---------------) + I anthropology
+ 0x00222588, // n0x12e6 c0x0000 (---------------) + I antiques
+ 0x00392b08, // n0x12e7 c0x0000 (---------------) + I aquarium
+ 0x00256f09, // n0x12e8 c0x0000 (---------------) + I arboretum
+ 0x0029690e, // n0x12e9 c0x0000 (---------------) + I archaeological
+ 0x003429cb, // n0x12ea c0x0000 (---------------) + I archaeology
+ 0x002a52cc, // n0x12eb c0x0000 (---------------) + I architecture
+ 0x00200603, // n0x12ec c0x0000 (---------------) + I art
+ 0x0031b0cc, // n0x12ed c0x0000 (---------------) + I artanddesign
+ 0x0037a6c9, // n0x12ee c0x0000 (---------------) + I artcenter
+ 0x00200607, // n0x12ef c0x0000 (---------------) + I artdeco
+ 0x0037548c, // n0x12f0 c0x0000 (---------------) + I arteducation
+ 0x0022654a, // n0x12f1 c0x0000 (---------------) + I artgallery
+ 0x00246584, // n0x12f2 c0x0000 (---------------) + I arts
+ 0x0024658d, // n0x12f3 c0x0000 (---------------) + I artsandcrafts
+ 0x0037a588, // n0x12f4 c0x0000 (---------------) + I asmatart
+ 0x0034db0d, // n0x12f5 c0x0000 (---------------) + I assassination
+ 0x0037ac46, // n0x12f6 c0x0000 (---------------) + I assisi
+ 0x002bbacb, // n0x12f7 c0x0000 (---------------) + I association
+ 0x0032b3c9, // n0x12f8 c0x0000 (---------------) + I astronomy
+ 0x0032d947, // n0x12f9 c0x0000 (---------------) + I atlanta
+ 0x0030ce46, // n0x12fa c0x0000 (---------------) + I austin
+ 0x002ee489, // n0x12fb c0x0000 (---------------) + I australia
+ 0x0030494a, // n0x12fc c0x0000 (---------------) + I automotive
+ 0x00328bc8, // n0x12fd c0x0000 (---------------) + I aviation
+ 0x0025db04, // n0x12fe c0x0000 (---------------) + I axis
+ 0x0035ef47, // n0x12ff c0x0000 (---------------) + I badajoz
+ 0x00264407, // n0x1300 c0x0000 (---------------) + I baghdad
+ 0x00295f44, // n0x1301 c0x0000 (---------------) + I bahn
+ 0x0023d9c4, // n0x1302 c0x0000 (---------------) + I bale
+ 0x002708c9, // n0x1303 c0x0000 (---------------) + I baltimore
+ 0x0036bf49, // n0x1304 c0x0000 (---------------) + I barcelona
+ 0x002ed5c8, // n0x1305 c0x0000 (---------------) + I baseball
+ 0x0020a445, // n0x1306 c0x0000 (---------------) + I basel
+ 0x002dbe45, // n0x1307 c0x0000 (---------------) + I baths
+ 0x00205a86, // n0x1308 c0x0000 (---------------) + I bauern
+ 0x00246449, // n0x1309 c0x0000 (---------------) + I beauxarts
+ 0x0035a18d, // n0x130a c0x0000 (---------------) + I beeldengeluid
+ 0x002a4d48, // n0x130b c0x0000 (---------------) + I bellevue
+ 0x00205987, // n0x130c c0x0000 (---------------) + I bergbau
+ 0x002b6f08, // n0x130d c0x0000 (---------------) + I berkeley
+ 0x00251d46, // n0x130e c0x0000 (---------------) + I berlin
+ 0x00353844, // n0x130f c0x0000 (---------------) + I bern
+ 0x003628c5, // n0x1310 c0x0000 (---------------) + I bible
+ 0x00394a46, // n0x1311 c0x0000 (---------------) + I bilbao
+ 0x00396b04, // n0x1312 c0x0000 (---------------) + I bill
+ 0x00200507, // n0x1313 c0x0000 (---------------) + I birdart
+ 0x00201cca, // n0x1314 c0x0000 (---------------) + I birthplace
+ 0x0020bcc4, // n0x1315 c0x0000 (---------------) + I bonn
+ 0x0020d686, // n0x1316 c0x0000 (---------------) + I boston
+ 0x0020e2c9, // n0x1317 c0x0000 (---------------) + I botanical
+ 0x0020e2cf, // n0x1318 c0x0000 (---------------) + I botanicalgarden
+ 0x0020e88d, // n0x1319 c0x0000 (---------------) + I botanicgarden
+ 0x002107c6, // n0x131a c0x0000 (---------------) + I botany
+ 0x00213b10, // n0x131b c0x0000 (---------------) + I brandywinevalley
+ 0x00214a86, // n0x131c c0x0000 (---------------) + I brasil
+ 0x002159c7, // n0x131d c0x0000 (---------------) + I bristol
+ 0x00215d47, // n0x131e c0x0000 (---------------) + I british
+ 0x00215d4f, // n0x131f c0x0000 (---------------) + I britishcolumbia
+ 0x00216d09, // n0x1320 c0x0000 (---------------) + I broadcast
+ 0x0021b106, // n0x1321 c0x0000 (---------------) + I brunel
+ 0x0021be87, // n0x1322 c0x0000 (---------------) + I brussel
+ 0x0021be88, // n0x1323 c0x0000 (---------------) + I brussels
+ 0x0021c809, // n0x1324 c0x0000 (---------------) + I bruxelles
+ 0x002c67c8, // n0x1325 c0x0000 (---------------) + I building
+ 0x002c2fc7, // n0x1326 c0x0000 (---------------) + I burghof
+ 0x002224c3, // n0x1327 c0x0000 (---------------) + I bus
+ 0x00280c46, // n0x1328 c0x0000 (---------------) + I bushey
+ 0x0027d948, // n0x1329 c0x0000 (---------------) + I cadaques
+ 0x00296bca, // n0x132a c0x0000 (---------------) + I california
+ 0x0021a089, // n0x132b c0x0000 (---------------) + I cambridge
+ 0x00208ec3, // n0x132c c0x0000 (---------------) + I can
+ 0x00309786, // n0x132d c0x0000 (---------------) + I canada
+ 0x0024de4a, // n0x132e c0x0000 (---------------) + I capebreton
+ 0x0031bb87, // n0x132f c0x0000 (---------------) + I carrier
+ 0x003752ca, // n0x1330 c0x0000 (---------------) + I cartoonart
+ 0x0038390e, // n0x1331 c0x0000 (---------------) + I casadelamoneda
+ 0x00216e46, // n0x1332 c0x0000 (---------------) + I castle
+ 0x0021c487, // n0x1333 c0x0000 (---------------) + I castres
+ 0x0020f006, // n0x1334 c0x0000 (---------------) + I celtic
+ 0x00233a06, // n0x1335 c0x0000 (---------------) + I center
+ 0x0036240b, // n0x1336 c0x0000 (---------------) + I chattanooga
+ 0x002503ca, // n0x1337 c0x0000 (---------------) + I cheltenham
+ 0x002eeccd, // n0x1338 c0x0000 (---------------) + I chesapeakebay
+ 0x0034e9c7, // n0x1339 c0x0000 (---------------) + I chicago
+ 0x00262bc8, // n0x133a c0x0000 (---------------) + I children
+ 0x00262bc9, // n0x133b c0x0000 (---------------) + I childrens
+ 0x00262bcf, // n0x133c c0x0000 (---------------) + I childrensgarden
+ 0x0029300c, // n0x133d c0x0000 (---------------) + I chiropractic
+ 0x00299309, // n0x133e c0x0000 (---------------) + I chocolate
+ 0x0022d30e, // n0x133f c0x0000 (---------------) + I christiansburg
+ 0x002754ca, // n0x1340 c0x0000 (---------------) + I cincinnati
+ 0x00343506, // n0x1341 c0x0000 (---------------) + I cinema
+ 0x00323506, // n0x1342 c0x0000 (---------------) + I circus
+ 0x0034564c, // n0x1343 c0x0000 (---------------) + I civilisation
+ 0x0034878c, // n0x1344 c0x0000 (---------------) + I civilization
+ 0x003518c8, // n0x1345 c0x0000 (---------------) + I civilwar
+ 0x00369707, // n0x1346 c0x0000 (---------------) + I clinton
+ 0x0020af45, // n0x1347 c0x0000 (---------------) + I clock
+ 0x00387ec4, // n0x1348 c0x0000 (---------------) + I coal
+ 0x0032ac0e, // n0x1349 c0x0000 (---------------) + I coastaldefence
+ 0x00200744, // n0x134a c0x0000 (---------------) + I cody
+ 0x00232107, // n0x134b c0x0000 (---------------) + I coldwar
+ 0x0024f38a, // n0x134c c0x0000 (---------------) + I collection
+ 0x00221854, // n0x134d c0x0000 (---------------) + I colonialwilliamsburg
+ 0x00221f0f, // n0x134e c0x0000 (---------------) + I coloradoplateau
+ 0x00215f08, // n0x134f c0x0000 (---------------) + I columbia
+ 0x00222388, // n0x1350 c0x0000 (---------------) + I columbus
+ 0x002b354d, // n0x1351 c0x0000 (---------------) + I communication
+ 0x002b354e, // n0x1352 c0x0000 (---------------) + I communications
+ 0x00222ac9, // n0x1353 c0x0000 (---------------) + I community
+ 0x002236c8, // n0x1354 c0x0000 (---------------) + I computer
+ 0x002236cf, // n0x1355 c0x0000 (---------------) + I computerhistory
+ 0x0022624c, // n0x1356 c0x0000 (---------------) + I contemporary
+ 0x0022624f, // n0x1357 c0x0000 (---------------) + I contemporaryart
+ 0x00227187, // n0x1358 c0x0000 (---------------) + I convent
+ 0x00229bca, // n0x1359 c0x0000 (---------------) + I copenhagen
+ 0x0021300b, // n0x135a c0x0000 (---------------) + I corporation
+ 0x0022b908, // n0x135b c0x0000 (---------------) + I corvette
+ 0x0022c347, // n0x135c c0x0000 (---------------) + I costume
+ 0x00362c0d, // n0x135d c0x0000 (---------------) + I countryestate
+ 0x002f8886, // n0x135e c0x0000 (---------------) + I county
+ 0x00246746, // n0x135f c0x0000 (---------------) + I crafts
+ 0x0022f189, // n0x1360 c0x0000 (---------------) + I cranbrook
+ 0x002e63c8, // n0x1361 c0x0000 (---------------) + I creation
+ 0x00233808, // n0x1362 c0x0000 (---------------) + I cultural
+ 0x0023380e, // n0x1363 c0x0000 (---------------) + I culturalcenter
+ 0x00323687, // n0x1364 c0x0000 (---------------) + I culture
+ 0x00343d85, // n0x1365 c0x0000 (---------------) + I cyber
+ 0x002dcac5, // n0x1366 c0x0000 (---------------) + I cymru
+ 0x00270d04, // n0x1367 c0x0000 (---------------) + I dali
+ 0x00267906, // n0x1368 c0x0000 (---------------) + I dallas
+ 0x002ed4c8, // n0x1369 c0x0000 (---------------) + I database
+ 0x0034d6c3, // n0x136a c0x0000 (---------------) + I ddr
+ 0x00255ace, // n0x136b c0x0000 (---------------) + I decorativearts
+ 0x00362fc8, // n0x136c c0x0000 (---------------) + I delaware
+ 0x0029f90b, // n0x136d c0x0000 (---------------) + I delmenhorst
+ 0x00311707, // n0x136e c0x0000 (---------------) + I denmark
+ 0x002b8f85, // n0x136f c0x0000 (---------------) + I depot
+ 0x00232f46, // n0x1370 c0x0000 (---------------) + I design
+ 0x00295507, // n0x1371 c0x0000 (---------------) + I detroit
+ 0x002dff48, // n0x1372 c0x0000 (---------------) + I dinosaur
+ 0x00304c89, // n0x1373 c0x0000 (---------------) + I discovery
+ 0x002250c5, // n0x1374 c0x0000 (---------------) + I dolls
+ 0x00274888, // n0x1375 c0x0000 (---------------) + I donostia
+ 0x0038a206, // n0x1376 c0x0000 (---------------) + I durham
+ 0x00367b0a, // n0x1377 c0x0000 (---------------) + I eastafrica
+ 0x0032ab09, // n0x1378 c0x0000 (---------------) + I eastcoast
+ 0x00375549, // n0x1379 c0x0000 (---------------) + I education
+ 0x0037554b, // n0x137a c0x0000 (---------------) + I educational
+ 0x002958c8, // n0x137b c0x0000 (---------------) + I egyptian
+ 0x00295e09, // n0x137c c0x0000 (---------------) + I eisenbahn
+ 0x0020a506, // n0x137d c0x0000 (---------------) + I elburg
+ 0x0034cf0a, // n0x137e c0x0000 (---------------) + I elvendrell
+ 0x0032af4a, // n0x137f c0x0000 (---------------) + I embroidery
+ 0x00229dcc, // n0x1380 c0x0000 (---------------) + I encyclopedic
+ 0x0037b187, // n0x1381 c0x0000 (---------------) + I england
+ 0x00384a8a, // n0x1382 c0x0000 (---------------) + I entomology
+ 0x0030de4b, // n0x1383 c0x0000 (---------------) + I environment
+ 0x0030de59, // n0x1384 c0x0000 (---------------) + I environmentalconservation
+ 0x003543c8, // n0x1385 c0x0000 (---------------) + I epilepsy
+ 0x0029ac45, // n0x1386 c0x0000 (---------------) + I essex
+ 0x002b1846, // n0x1387 c0x0000 (---------------) + I estate
+ 0x002f6409, // n0x1388 c0x0000 (---------------) + I ethnology
+ 0x0020bb46, // n0x1389 c0x0000 (---------------) + I exeter
+ 0x00326eca, // n0x138a c0x0000 (---------------) + I exhibition
+ 0x00311c06, // n0x138b c0x0000 (---------------) + I family
+ 0x0021f804, // n0x138c c0x0000 (---------------) + I farm
+ 0x0021f80d, // n0x138d c0x0000 (---------------) + I farmequipment
+ 0x00366b07, // n0x138e c0x0000 (---------------) + I farmers
+ 0x002fb8c9, // n0x138f c0x0000 (---------------) + I farmstead
+ 0x002099c5, // n0x1390 c0x0000 (---------------) + I field
+ 0x00338588, // n0x1391 c0x0000 (---------------) + I figueres
+ 0x00349f49, // n0x1392 c0x0000 (---------------) + I filatelia
+ 0x00356104, // n0x1393 c0x0000 (---------------) + I film
+ 0x003707c7, // n0x1394 c0x0000 (---------------) + I fineart
+ 0x003707c8, // n0x1395 c0x0000 (---------------) + I finearts
+ 0x00236407, // n0x1396 c0x0000 (---------------) + I finland
+ 0x00252b88, // n0x1397 c0x0000 (---------------) + I flanders
+ 0x0023c447, // n0x1398 c0x0000 (---------------) + I florida
+ 0x002e7685, // n0x1399 c0x0000 (---------------) + I force
+ 0x002457cc, // n0x139a c0x0000 (---------------) + I fortmissoula
+ 0x00245e89, // n0x139b c0x0000 (---------------) + I fortworth
+ 0x00303b8a, // n0x139c c0x0000 (---------------) + I foundation
+ 0x00377209, // n0x139d c0x0000 (---------------) + I francaise
+ 0x003229c9, // n0x139e c0x0000 (---------------) + I frankfurt
+ 0x0035268c, // n0x139f c0x0000 (---------------) + I franziskaner
+ 0x00209c8b, // n0x13a0 c0x0000 (---------------) + I freemasonry
+ 0x00248cc8, // n0x13a1 c0x0000 (---------------) + I freiburg
+ 0x002493c8, // n0x13a2 c0x0000 (---------------) + I fribourg
+ 0x0024c784, // n0x13a3 c0x0000 (---------------) + I frog
+ 0x0026fc08, // n0x13a4 c0x0000 (---------------) + I fundacio
+ 0x00272f09, // n0x13a5 c0x0000 (---------------) + I furniture
+ 0x00226607, // n0x13a6 c0x0000 (---------------) + I gallery
+ 0x0020e506, // n0x13a7 c0x0000 (---------------) + I garden
+ 0x0021a607, // n0x13a8 c0x0000 (---------------) + I gateway
+ 0x00268b89, // n0x13a9 c0x0000 (---------------) + I geelvinck
+ 0x002a634b, // n0x13aa c0x0000 (---------------) + I gemological
+ 0x00309a87, // n0x13ab c0x0000 (---------------) + I geology
+ 0x002f8287, // n0x13ac c0x0000 (---------------) + I georgia
+ 0x00267387, // n0x13ad c0x0000 (---------------) + I giessen
+ 0x0034da84, // n0x13ae c0x0000 (---------------) + I glas
+ 0x0034da85, // n0x13af c0x0000 (---------------) + I glass
+ 0x00292d05, // n0x13b0 c0x0000 (---------------) + I gorge
+ 0x00320b0b, // n0x13b1 c0x0000 (---------------) + I grandrapids
+ 0x00380d44, // n0x13b2 c0x0000 (---------------) + I graz
+ 0x002e6b48, // n0x13b3 c0x0000 (---------------) + I guernsey
+ 0x002a19ca, // n0x13b4 c0x0000 (---------------) + I halloffame
+ 0x0038a2c7, // n0x13b5 c0x0000 (---------------) + I hamburg
+ 0x00372047, // n0x13b6 c0x0000 (---------------) + I handson
+ 0x0027a252, // n0x13b7 c0x0000 (---------------) + I harvestcelebration
+ 0x00256386, // n0x13b8 c0x0000 (---------------) + I hawaii
+ 0x00205e06, // n0x13b9 c0x0000 (---------------) + I health
+ 0x002f518e, // n0x13ba c0x0000 (---------------) + I heimatunduhren
+ 0x00257ac6, // n0x13bb c0x0000 (---------------) + I hellas
+ 0x00209408, // n0x13bc c0x0000 (---------------) + I helsinki
+ 0x0027f4cf, // n0x13bd c0x0000 (---------------) + I hembygdsforbund
+ 0x0034dec8, // n0x13be c0x0000 (---------------) + I heritage
+ 0x0025de08, // n0x13bf c0x0000 (---------------) + I histoire
+ 0x002a794a, // n0x13c0 c0x0000 (---------------) + I historical
+ 0x002a7951, // n0x13c1 c0x0000 (---------------) + I historicalsociety
+ 0x0028cfce, // n0x13c2 c0x0000 (---------------) + I historichouses
+ 0x0024268a, // n0x13c3 c0x0000 (---------------) + I historisch
+ 0x0024268c, // n0x13c4 c0x0000 (---------------) + I historisches
+ 0x002238c7, // n0x13c5 c0x0000 (---------------) + I history
+ 0x002238d0, // n0x13c6 c0x0000 (---------------) + I historyofscience
+ 0x0038f308, // n0x13c7 c0x0000 (---------------) + I horology
+ 0x0021da05, // n0x13c8 c0x0000 (---------------) + I house
+ 0x002d8f0a, // n0x13c9 c0x0000 (---------------) + I humanities
+ 0x00396b4c, // n0x13ca c0x0000 (---------------) + I illustration
+ 0x003365cd, // n0x13cb c0x0000 (---------------) + I imageandsound
+ 0x002d5b06, // n0x13cc c0x0000 (---------------) + I indian
+ 0x002d5b07, // n0x13cd c0x0000 (---------------) + I indiana
+ 0x002d5b0c, // n0x13ce c0x0000 (---------------) + I indianapolis
+ 0x00356ecc, // n0x13cf c0x0000 (---------------) + I indianmarket
+ 0x00238c0c, // n0x13d0 c0x0000 (---------------) + I intelligence
+ 0x0027d28b, // n0x13d1 c0x0000 (---------------) + I interactive
+ 0x00273f44, // n0x13d2 c0x0000 (---------------) + I iraq
+ 0x0021afc4, // n0x13d3 c0x0000 (---------------) + I iron
+ 0x00357ec9, // n0x13d4 c0x0000 (---------------) + I isleofman
+ 0x002bc087, // n0x13d5 c0x0000 (---------------) + I jamison
+ 0x002207c9, // n0x13d6 c0x0000 (---------------) + I jefferson
+ 0x00337649, // n0x13d7 c0x0000 (---------------) + I jerusalem
+ 0x0034f487, // n0x13d8 c0x0000 (---------------) + I jewelry
+ 0x0038cb46, // n0x13d9 c0x0000 (---------------) + I jewish
+ 0x0038cb49, // n0x13da c0x0000 (---------------) + I jewishart
+ 0x00297743, // n0x13db c0x0000 (---------------) + I jfk
+ 0x0028540a, // n0x13dc c0x0000 (---------------) + I journalism
+ 0x0036e9c7, // n0x13dd c0x0000 (---------------) + I judaica
+ 0x0035f38b, // n0x13de c0x0000 (---------------) + I judygarland
+ 0x002eeb4a, // n0x13df c0x0000 (---------------) + I juedisches
+ 0x00370c84, // n0x13e0 c0x0000 (---------------) + I juif
+ 0x002df386, // n0x13e1 c0x0000 (---------------) + I karate
+ 0x002af549, // n0x13e2 c0x0000 (---------------) + I karikatur
+ 0x00394c44, // n0x13e3 c0x0000 (---------------) + I kids
+ 0x0033e24a, // n0x13e4 c0x0000 (---------------) + I koebenhavn
+ 0x0021f385, // n0x13e5 c0x0000 (---------------) + I koeln
+ 0x002a3845, // n0x13e6 c0x0000 (---------------) + I kunst
+ 0x002a384d, // n0x13e7 c0x0000 (---------------) + I kunstsammlung
+ 0x002a3b8e, // n0x13e8 c0x0000 (---------------) + I kunstunddesign
+ 0x002feec5, // n0x13e9 c0x0000 (---------------) + I labor
+ 0x0037c986, // n0x13ea c0x0000 (---------------) + I labour
+ 0x00323a07, // n0x13eb c0x0000 (---------------) + I lajolla
+ 0x00261a4a, // n0x13ec c0x0000 (---------------) + I lancashire
+ 0x0035b146, // n0x13ed c0x0000 (---------------) + I landes
+ 0x002856c4, // n0x13ee c0x0000 (---------------) + I lans
+ 0x002d0287, // n0x13ef c0x0000 (---------------) + I larsson
+ 0x0029eacb, // n0x13f0 c0x0000 (---------------) + I lewismiller
+ 0x00251e07, // n0x13f1 c0x0000 (---------------) + I lincoln
+ 0x00329ac4, // n0x13f2 c0x0000 (---------------) + I linz
+ 0x002a1d46, // n0x13f3 c0x0000 (---------------) + I living
+ 0x002a1d4d, // n0x13f4 c0x0000 (---------------) + I livinghistory
+ 0x0032254c, // n0x13f5 c0x0000 (---------------) + I localhistory
+ 0x00340906, // n0x13f6 c0x0000 (---------------) + I london
+ 0x002a4f4a, // n0x13f7 c0x0000 (---------------) + I losangeles
+ 0x002116c6, // n0x13f8 c0x0000 (---------------) + I louvre
+ 0x00259d88, // n0x13f9 c0x0000 (---------------) + I loyalist
+ 0x0021d747, // n0x13fa c0x0000 (---------------) + I lucerne
+ 0x00222d8a, // n0x13fb c0x0000 (---------------) + I luxembourg
+ 0x00226c86, // n0x13fc c0x0000 (---------------) + I luzern
+ 0x0026b0c3, // n0x13fd c0x0000 (---------------) + I mad
+ 0x00300a86, // n0x13fe c0x0000 (---------------) + I madrid
+ 0x0026aa08, // n0x13ff c0x0000 (---------------) + I mallorca
+ 0x0028608a, // n0x1400 c0x0000 (---------------) + I manchester
+ 0x00266747, // n0x1401 c0x0000 (---------------) + I mansion
+ 0x00266748, // n0x1402 c0x0000 (---------------) + I mansions
+ 0x0026d784, // n0x1403 c0x0000 (---------------) + I manx
+ 0x00277287, // n0x1404 c0x0000 (---------------) + I marburg
+ 0x0036ee48, // n0x1405 c0x0000 (---------------) + I maritime
+ 0x00237048, // n0x1406 c0x0000 (---------------) + I maritimo
+ 0x00256588, // n0x1407 c0x0000 (---------------) + I maryland
+ 0x003372ca, // n0x1408 c0x0000 (---------------) + I marylhurst
+ 0x002dc385, // n0x1409 c0x0000 (---------------) + I media
+ 0x002e7087, // n0x140a c0x0000 (---------------) + I medical
+ 0x002424d3, // n0x140b c0x0000 (---------------) + I medizinhistorisches
+ 0x00257946, // n0x140c c0x0000 (---------------) + I meeres
+ 0x002edac8, // n0x140d c0x0000 (---------------) + I memorial
+ 0x00219509, // n0x140e c0x0000 (---------------) + I mesaverde
+ 0x00204988, // n0x140f c0x0000 (---------------) + I michigan
+ 0x002892cb, // n0x1410 c0x0000 (---------------) + I midatlantic
+ 0x002a6948, // n0x1411 c0x0000 (---------------) + I military
+ 0x0029ec04, // n0x1412 c0x0000 (---------------) + I mill
+ 0x002878c6, // n0x1413 c0x0000 (---------------) + I miners
+ 0x002e7a46, // n0x1414 c0x0000 (---------------) + I mining
+ 0x00335b09, // n0x1415 c0x0000 (---------------) + I minnesota
+ 0x002ad807, // n0x1416 c0x0000 (---------------) + I missile
+ 0x002458c8, // n0x1417 c0x0000 (---------------) + I missoula
+ 0x00291a06, // n0x1418 c0x0000 (---------------) + I modern
+ 0x0021cfc4, // n0x1419 c0x0000 (---------------) + I moma
+ 0x002b6185, // n0x141a c0x0000 (---------------) + I money
+ 0x002b02c8, // n0x141b c0x0000 (---------------) + I monmouth
+ 0x002b094a, // n0x141c c0x0000 (---------------) + I monticello
+ 0x002b1648, // n0x141d c0x0000 (---------------) + I montreal
+ 0x002b68c6, // n0x141e c0x0000 (---------------) + I moscow
+ 0x0028678a, // n0x141f c0x0000 (---------------) + I motorcycle
+ 0x002f3cc8, // n0x1420 c0x0000 (---------------) + I muenchen
+ 0x002b9948, // n0x1421 c0x0000 (---------------) + I muenster
+ 0x002ba948, // n0x1422 c0x0000 (---------------) + I mulhouse
+ 0x002bb346, // n0x1423 c0x0000 (---------------) + I muncie
+ 0x002bd186, // n0x1424 c0x0000 (---------------) + I museet
+ 0x0030e7cc, // n0x1425 c0x0000 (---------------) + I museumcenter
+ 0x002bd650, // n0x1426 c0x0000 (---------------) + I museumvereniging
+ 0x00337b85, // n0x1427 c0x0000 (---------------) + I music
+ 0x002dd9c8, // n0x1428 c0x0000 (---------------) + I national
+ 0x002dd9d0, // n0x1429 c0x0000 (---------------) + I nationalfirearms
+ 0x0034dcd0, // n0x142a c0x0000 (---------------) + I nationalheritage
+ 0x0027564e, // n0x142b c0x0000 (---------------) + I nativeamerican
+ 0x0030e44e, // n0x142c c0x0000 (---------------) + I naturalhistory
+ 0x0030e454, // n0x142d c0x0000 (---------------) + I naturalhistorymuseum
+ 0x00240dcf, // n0x142e c0x0000 (---------------) + I naturalsciences
+ 0x00241186, // n0x142f c0x0000 (---------------) + I nature
+ 0x002f4951, // n0x1430 c0x0000 (---------------) + I naturhistorisches
+ 0x0031cfd3, // n0x1431 c0x0000 (---------------) + I natuurwetenschappen
+ 0x0031d448, // n0x1432 c0x0000 (---------------) + I naumburg
+ 0x00341c05, // n0x1433 c0x0000 (---------------) + I naval
+ 0x00262448, // n0x1434 c0x0000 (---------------) + I nebraska
+ 0x0021d585, // n0x1435 c0x0000 (---------------) + I neues
+ 0x002e60cc, // n0x1436 c0x0000 (---------------) + I newhampshire
+ 0x00221609, // n0x1437 c0x0000 (---------------) + I newjersey
+ 0x00231f49, // n0x1438 c0x0000 (---------------) + I newmexico
+ 0x0021a387, // n0x1439 c0x0000 (---------------) + I newport
+ 0x00366d49, // n0x143a c0x0000 (---------------) + I newspaper
+ 0x00237f07, // n0x143b c0x0000 (---------------) + I newyork
+ 0x00203d46, // n0x143c c0x0000 (---------------) + I niepce
+ 0x003626c7, // n0x143d c0x0000 (---------------) + I norfolk
+ 0x002f0dc5, // n0x143e c0x0000 (---------------) + I north
+ 0x00345903, // n0x143f c0x0000 (---------------) + I nrw
+ 0x0034d889, // n0x1440 c0x0000 (---------------) + I nuernberg
+ 0x002e9309, // n0x1441 c0x0000 (---------------) + I nuremberg
+ 0x00223403, // n0x1442 c0x0000 (---------------) + I nyc
+ 0x002108c4, // n0x1443 c0x0000 (---------------) + I nyny
+ 0x0034100d, // n0x1444 c0x0000 (---------------) + I oceanographic
+ 0x0034384f, // n0x1445 c0x0000 (---------------) + I oceanographique
+ 0x002e3085, // n0x1446 c0x0000 (---------------) + I omaha
+ 0x003023c6, // n0x1447 c0x0000 (---------------) + I online
+ 0x0032a587, // n0x1448 c0x0000 (---------------) + I ontario
+ 0x0032ce07, // n0x1449 c0x0000 (---------------) + I openair
+ 0x00276b86, // n0x144a c0x0000 (---------------) + I oregon
+ 0x00276b8b, // n0x144b c0x0000 (---------------) + I oregontrail
+ 0x0028e6c5, // n0x144c c0x0000 (---------------) + I otago
+ 0x00364046, // n0x144d c0x0000 (---------------) + I oxford
+ 0x0031ba07, // n0x144e c0x0000 (---------------) + I pacific
+ 0x0025d709, // n0x144f c0x0000 (---------------) + I paderborn
+ 0x0036ce06, // n0x1450 c0x0000 (---------------) + I palace
+ 0x002052c5, // n0x1451 c0x0000 (---------------) + I paleo
+ 0x00324b4b, // n0x1452 c0x0000 (---------------) + I palmsprings
+ 0x00221d86, // n0x1453 c0x0000 (---------------) + I panama
+ 0x0025b445, // n0x1454 c0x0000 (---------------) + I paris
+ 0x002a3348, // n0x1455 c0x0000 (---------------) + I pasadena
+ 0x002dc948, // n0x1456 c0x0000 (---------------) + I pharmacy
+ 0x0036150c, // n0x1457 c0x0000 (---------------) + I philadelphia
+ 0x00361510, // n0x1458 c0x0000 (---------------) + I philadelphiaarea
+ 0x002bf909, // n0x1459 c0x0000 (---------------) + I philately
+ 0x002bfd47, // n0x145a c0x0000 (---------------) + I phoenix
+ 0x002c014b, // n0x145b c0x0000 (---------------) + I photography
+ 0x002c1506, // n0x145c c0x0000 (---------------) + I pilots
+ 0x002c2e8a, // n0x145d c0x0000 (---------------) + I pittsburgh
+ 0x002c438b, // n0x145e c0x0000 (---------------) + I planetarium
+ 0x002c4bca, // n0x145f c0x0000 (---------------) + I plantation
+ 0x002c4e46, // n0x1460 c0x0000 (---------------) + I plants
+ 0x002c6485, // n0x1461 c0x0000 (---------------) + I plaza
+ 0x0036e706, // n0x1462 c0x0000 (---------------) + I portal
+ 0x00266f08, // n0x1463 c0x0000 (---------------) + I portland
+ 0x0021a44a, // n0x1464 c0x0000 (---------------) + I portlligat
+ 0x002b31dc, // n0x1465 c0x0000 (---------------) + I posts-and-telecommunications
+ 0x002ca84c, // n0x1466 c0x0000 (---------------) + I preservation
+ 0x002cab48, // n0x1467 c0x0000 (---------------) + I presidio
+ 0x0029abc5, // n0x1468 c0x0000 (---------------) + I press
+ 0x002cc587, // n0x1469 c0x0000 (---------------) + I project
+ 0x00296546, // n0x146a c0x0000 (---------------) + I public
+ 0x0034c905, // n0x146b c0x0000 (---------------) + I pubol
+ 0x00211186, // n0x146c c0x0000 (---------------) + I quebec
+ 0x00276d48, // n0x146d c0x0000 (---------------) + I railroad
+ 0x00253fc7, // n0x146e c0x0000 (---------------) + I railway
+ 0x00296808, // n0x146f c0x0000 (---------------) + I research
+ 0x0021c58a, // n0x1470 c0x0000 (---------------) + I resistance
+ 0x0033cb8c, // n0x1471 c0x0000 (---------------) + I riodejaneiro
+ 0x0033ce09, // n0x1472 c0x0000 (---------------) + I rochester
+ 0x00201707, // n0x1473 c0x0000 (---------------) + I rockart
+ 0x0023fb84, // n0x1474 c0x0000 (---------------) + I roma
+ 0x0023afc6, // n0x1475 c0x0000 (---------------) + I russia
+ 0x002c508a, // n0x1476 c0x0000 (---------------) + I saintlouis
+ 0x00337745, // n0x1477 c0x0000 (---------------) + I salem
+ 0x0034250c, // n0x1478 c0x0000 (---------------) + I salvadordali
+ 0x0034ec88, // n0x1479 c0x0000 (---------------) + I salzburg
+ 0x00270ec8, // n0x147a c0x0000 (---------------) + I sandiego
+ 0x0023774c, // n0x147b c0x0000 (---------------) + I sanfrancisco
+ 0x0020bfcc, // n0x147c c0x0000 (---------------) + I santabarbara
+ 0x0020c3c9, // n0x147d c0x0000 (---------------) + I santacruz
+ 0x0020c607, // n0x147e c0x0000 (---------------) + I santafe
+ 0x0035768c, // n0x147f c0x0000 (---------------) + I saskatchewan
+ 0x002251c4, // n0x1480 c0x0000 (---------------) + I satx
+ 0x0022de0a, // n0x1481 c0x0000 (---------------) + I savannahga
+ 0x0020cb8c, // n0x1482 c0x0000 (---------------) + I schlesisches
+ 0x00354a4b, // n0x1483 c0x0000 (---------------) + I schoenbrunn
+ 0x0025e7cb, // n0x1484 c0x0000 (---------------) + I schokoladen
+ 0x00235406, // n0x1485 c0x0000 (---------------) + I school
+ 0x0023b2c7, // n0x1486 c0x0000 (---------------) + I schweiz
+ 0x00223b07, // n0x1487 c0x0000 (---------------) + I science
+ 0x00223b0f, // n0x1488 c0x0000 (---------------) + I science-fiction
+ 0x002d6251, // n0x1489 c0x0000 (---------------) + I scienceandhistory
+ 0x0037f9d2, // n0x148a c0x0000 (---------------) + I scienceandindustry
+ 0x0023ce0d, // n0x148b c0x0000 (---------------) + I sciencecenter
+ 0x0023ce0e, // n0x148c c0x0000 (---------------) + I sciencecenters
+ 0x0023d14e, // n0x148d c0x0000 (---------------) + I sciencehistory
+ 0x00240f88, // n0x148e c0x0000 (---------------) + I sciences
+ 0x00240f92, // n0x148f c0x0000 (---------------) + I sciencesnaturelles
+ 0x00237988, // n0x1490 c0x0000 (---------------) + I scotland
+ 0x002dfc87, // n0x1491 c0x0000 (---------------) + I seaport
+ 0x0023920a, // n0x1492 c0x0000 (---------------) + I settlement
+ 0x0020fb88, // n0x1493 c0x0000 (---------------) + I settlers
+ 0x00257a85, // n0x1494 c0x0000 (---------------) + I shell
+ 0x0025918a, // n0x1495 c0x0000 (---------------) + I sherbrooke
+ 0x0037ad47, // n0x1496 c0x0000 (---------------) + I sibenik
+ 0x0036b904, // n0x1497 c0x0000 (---------------) + I silk
+ 0x00207b43, // n0x1498 c0x0000 (---------------) + I ski
+ 0x002e3345, // n0x1499 c0x0000 (---------------) + I skole
+ 0x002a7bc7, // n0x149a c0x0000 (---------------) + I society
+ 0x002d84c7, // n0x149b c0x0000 (---------------) + I sologne
+ 0x003367ce, // n0x149c c0x0000 (---------------) + I soundandvision
+ 0x0031f30d, // n0x149d c0x0000 (---------------) + I southcarolina
+ 0x00320d89, // n0x149e c0x0000 (---------------) + I southwest
+ 0x002101c5, // n0x149f c0x0000 (---------------) + I space
+ 0x00349a43, // n0x14a0 c0x0000 (---------------) + I spy
+ 0x00275b86, // n0x14a1 c0x0000 (---------------) + I square
+ 0x00248205, // n0x14a2 c0x0000 (---------------) + I stadt
+ 0x0029fb48, // n0x14a3 c0x0000 (---------------) + I stalbans
+ 0x00232449, // n0x14a4 c0x0000 (---------------) + I starnberg
+ 0x002b1885, // n0x14a5 c0x0000 (---------------) + I state
+ 0x00362e0f, // n0x14a6 c0x0000 (---------------) + I stateofdelaware
+ 0x002c62c7, // n0x14a7 c0x0000 (---------------) + I station
+ 0x00354105, // n0x14a8 c0x0000 (---------------) + I steam
+ 0x002f2f8a, // n0x14a9 c0x0000 (---------------) + I steiermark
+ 0x002a83c6, // n0x14aa c0x0000 (---------------) + I stjohn
+ 0x00259f09, // n0x14ab c0x0000 (---------------) + I stockholm
+ 0x002cf98c, // n0x14ac c0x0000 (---------------) + I stpetersburg
+ 0x002d0749, // n0x14ad c0x0000 (---------------) + I stuttgart
+ 0x00211e46, // n0x14ae c0x0000 (---------------) + I suisse
+ 0x002a17cc, // n0x14af c0x0000 (---------------) + I surgeonshall
+ 0x002d1c86, // n0x14b0 c0x0000 (---------------) + I surrey
+ 0x002d4988, // n0x14b1 c0x0000 (---------------) + I svizzera
+ 0x002d4b86, // n0x14b2 c0x0000 (---------------) + I sweden
+ 0x00368086, // n0x14b3 c0x0000 (---------------) + I sydney
+ 0x0024ca44, // n0x14b4 c0x0000 (---------------) + I tank
+ 0x00249043, // n0x14b5 c0x0000 (---------------) + I tcm
+ 0x0029d58a, // n0x14b6 c0x0000 (---------------) + I technology
+ 0x002994d1, // n0x14b7 c0x0000 (---------------) + I telekommunikation
+ 0x00248a4a, // n0x14b8 c0x0000 (---------------) + I television
+ 0x00209085, // n0x14b9 c0x0000 (---------------) + I texas
+ 0x00326307, // n0x14ba c0x0000 (---------------) + I textile
+ 0x002f9147, // n0x14bb c0x0000 (---------------) + I theater
+ 0x0036ef44, // n0x14bc c0x0000 (---------------) + I time
+ 0x0036ef4b, // n0x14bd c0x0000 (---------------) + I timekeeping
+ 0x0035e408, // n0x14be c0x0000 (---------------) + I topology
+ 0x0029f3c6, // n0x14bf c0x0000 (---------------) + I torino
+ 0x002e9685, // n0x14c0 c0x0000 (---------------) + I touch
+ 0x0021abc4, // n0x14c1 c0x0000 (---------------) + I town
+ 0x0027c9c9, // n0x14c2 c0x0000 (---------------) + I transport
+ 0x00242e84, // n0x14c3 c0x0000 (---------------) + I tree
+ 0x00211c47, // n0x14c4 c0x0000 (---------------) + I trolley
+ 0x00313185, // n0x14c5 c0x0000 (---------------) + I trust
+ 0x00313187, // n0x14c6 c0x0000 (---------------) + I trustee
+ 0x002f53c5, // n0x14c7 c0x0000 (---------------) + I uhren
+ 0x00213f43, // n0x14c8 c0x0000 (---------------) + I ulm
+ 0x002dfb48, // n0x14c9 c0x0000 (---------------) + I undersea
+ 0x0029f04a, // n0x14ca c0x0000 (---------------) + I university
+ 0x00222503, // n0x14cb c0x0000 (---------------) + I usa
+ 0x0022250a, // n0x14cc c0x0000 (---------------) + I usantiques
+ 0x0038c1c6, // n0x14cd c0x0000 (---------------) + I usarts
+ 0x00362b8f, // n0x14ce c0x0000 (---------------) + I uscountryestate
+ 0x00323609, // n0x14cf c0x0000 (---------------) + I usculture
+ 0x00255a50, // n0x14d0 c0x0000 (---------------) + I usdecorativearts
+ 0x002ada08, // n0x14d1 c0x0000 (---------------) + I usgarden
+ 0x002b7209, // n0x14d2 c0x0000 (---------------) + I ushistory
+ 0x00282ac7, // n0x14d3 c0x0000 (---------------) + I ushuaia
+ 0x002a1ccf, // n0x14d4 c0x0000 (---------------) + I uslivinghistory
+ 0x0026a544, // n0x14d5 c0x0000 (---------------) + I utah
+ 0x00357a44, // n0x14d6 c0x0000 (---------------) + I uvic
+ 0x00213d86, // n0x14d7 c0x0000 (---------------) + I valley
+ 0x002292c6, // n0x14d8 c0x0000 (---------------) + I vantaa
+ 0x0038deca, // n0x14d9 c0x0000 (---------------) + I versailles
+ 0x002c7686, // n0x14da c0x0000 (---------------) + I viking
+ 0x00327d47, // n0x14db c0x0000 (---------------) + I village
+ 0x002dd148, // n0x14dc c0x0000 (---------------) + I virginia
+ 0x002dd347, // n0x14dd c0x0000 (---------------) + I virtual
+ 0x002dd507, // n0x14de c0x0000 (---------------) + I virtuel
+ 0x00332eca, // n0x14df c0x0000 (---------------) + I vlaanderen
+ 0x002df98b, // n0x14e0 c0x0000 (---------------) + I volkenkunde
+ 0x0036cbc5, // n0x14e1 c0x0000 (---------------) + I wales
+ 0x0037f608, // n0x14e2 c0x0000 (---------------) + I wallonie
+ 0x00202543, // n0x14e3 c0x0000 (---------------) + I war
+ 0x0025108c, // n0x14e4 c0x0000 (---------------) + I washingtondc
+ 0x0020accf, // n0x14e5 c0x0000 (---------------) + I watch-and-clock
+ 0x0025fe4d, // n0x14e6 c0x0000 (---------------) + I watchandclock
+ 0x0036d147, // n0x14e7 c0x0000 (---------------) + I western
+ 0x00320ec9, // n0x14e8 c0x0000 (---------------) + I westfalen
+ 0x0029b447, // n0x14e9 c0x0000 (---------------) + I whaling
+ 0x00247748, // n0x14ea c0x0000 (---------------) + I wildlife
+ 0x00221a4c, // n0x14eb c0x0000 (---------------) + I williamsburg
+ 0x002a0248, // n0x14ec c0x0000 (---------------) + I windmill
+ 0x0029b088, // n0x14ed c0x0000 (---------------) + I workshop
+ 0x002f608e, // n0x14ee c0x0000 (---------------) + I xn--9dbhblg6di
+ 0x003083d4, // n0x14ef c0x0000 (---------------) + I xn--comunicaes-v6a2o
+ 0x003088e4, // n0x14f0 c0x0000 (---------------) + I xn--correios-e-telecomunicaes-ghc29a
+ 0x0032844a, // n0x14f1 c0x0000 (---------------) + I xn--h1aegh
+ 0x0034760b, // n0x14f2 c0x0000 (---------------) + I xn--lns-qla
+ 0x00237fc4, // n0x14f3 c0x0000 (---------------) + I york
+ 0x00237fc9, // n0x14f4 c0x0000 (---------------) + I yorkshire
+ 0x00297948, // n0x14f5 c0x0000 (---------------) + I yosemite
+ 0x00235bc5, // n0x14f6 c0x0000 (---------------) + I youth
+ 0x0038ba0a, // n0x14f7 c0x0000 (---------------) + I zoological
+ 0x00363207, // n0x14f8 c0x0000 (---------------) + I zoology
+ 0x002751c4, // n0x14f9 c0x0000 (---------------) + I aero
+ 0x00310603, // n0x14fa c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x14fb c0x0000 (---------------) + I com
+ 0x00228d44, // n0x14fc c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x14fd c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x14fe c0x0000 (---------------) + I gov
+ 0x00200304, // n0x14ff c0x0000 (---------------) + I info
+ 0x00238c03, // n0x1500 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x1501 c0x0000 (---------------) + I mil
+ 0x002bd646, // n0x1502 c0x0000 (---------------) + I museum
+ 0x00298944, // n0x1503 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1504 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1505 c0x0000 (---------------) + I org
+ 0x00218243, // n0x1506 c0x0000 (---------------) + I pro
+ 0x00201e82, // n0x1507 c0x0000 (---------------) + I ac
+ 0x00310603, // n0x1508 c0x0000 (---------------) + I biz
+ 0x00200742, // n0x1509 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x150a c0x0000 (---------------) + I com
+ 0x00228d44, // n0x150b c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x150c c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x150d c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x150e c0x0000 (---------------) + I int
+ 0x002bd646, // n0x150f c0x0000 (---------------) + I museum
+ 0x002170c3, // n0x1510 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1511 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1512 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1513 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1514 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x1515 c0x0000 (---------------) + I gob
+ 0x002170c3, // n0x1516 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1517 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1518 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1519 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x151a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x151b c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x151c c0x0000 (---------------) + I mil
+ 0x00298944, // n0x151d c0x0000 (---------------) + I name
+ 0x002170c3, // n0x151e c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x151f c0x0000 (---------------) + I org
+ 0x006ed3c8, // n0x1520 c0x0001 (---------------) ! I teledata
+ 0x002055c2, // n0x1521 c0x0000 (---------------) + I ca
+ 0x0021aa82, // n0x1522 c0x0000 (---------------) + I cc
+ 0x00200742, // n0x1523 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1524 c0x0000 (---------------) + I com
+ 0x0022fb02, // n0x1525 c0x0000 (---------------) + I dr
+ 0x00200242, // n0x1526 c0x0000 (---------------) + I in
+ 0x00200304, // n0x1527 c0x0000 (---------------) + I info
+ 0x00203604, // n0x1528 c0x0000 (---------------) + I mobi
+ 0x0020a682, // n0x1529 c0x0000 (---------------) + I mx
+ 0x00298944, // n0x152a c0x0000 (---------------) + I name
+ 0x00200c42, // n0x152b c0x0000 (---------------) + I or
+ 0x0021dcc3, // n0x152c c0x0000 (---------------) + I org
+ 0x00218243, // n0x152d c0x0000 (---------------) + I pro
+ 0x00235406, // n0x152e c0x0000 (---------------) + I school
+ 0x0020bf42, // n0x152f c0x0000 (---------------) + I tv
+ 0x00209f42, // n0x1530 c0x0000 (---------------) + I us
+ 0x0020ba82, // n0x1531 c0x0000 (---------------) + I ws
+ 0x37e19443, // n0x1532 c0x00df (n0x1534-n0x1535) o I her
+ 0x382238c3, // n0x1533 c0x00e0 (n0x1535-n0x1536) o I his
+ 0x000439c6, // n0x1534 c0x0000 (---------------) + forgot
+ 0x000439c6, // n0x1535 c0x0000 (---------------) + forgot
+ 0x002729c4, // n0x1536 c0x0000 (---------------) + I asso
+ 0x0010054c, // n0x1537 c0x0000 (---------------) + at-band-camp
+ 0x0006c90c, // n0x1538 c0x0000 (---------------) + azure-mobile
+ 0x000aef0d, // n0x1539 c0x0000 (---------------) + azurewebsites
+ 0x000d0087, // n0x153a c0x0000 (---------------) + blogdns
+ 0x00017a88, // n0x153b c0x0000 (---------------) + broke-it
+ 0x0001d90a, // n0x153c c0x0000 (---------------) + buyshouses
+ 0x38e2a085, // n0x153d c0x00e3 (n0x1568-n0x1569) o I cdn77
+ 0x0002a089, // n0x153e c0x0000 (---------------) + cdn77-ssl
+ 0x00192608, // n0x153f c0x0000 (---------------) + cloudapp
+ 0x0019058a, // n0x1540 c0x0000 (---------------) + cloudfront
+ 0x00146fc8, // n0x1541 c0x0000 (---------------) + dnsalias
+ 0x0006a247, // n0x1542 c0x0000 (---------------) + dnsdojo
+ 0x0018a587, // n0x1543 c0x0000 (---------------) + does-it
+ 0x0015fdc9, // n0x1544 c0x0000 (---------------) + dontexist
+ 0x000007c8, // n0x1545 c0x0000 (---------------) + dynalias
+ 0x00078989, // n0x1546 c0x0000 (---------------) + dynathome
+ 0x0009428d, // n0x1547 c0x0000 (---------------) + endofinternet
+ 0x39287d46, // n0x1548 c0x00e4 (n0x1569-n0x156b) o I fastly
+ 0x0004dac7, // n0x1549 c0x0000 (---------------) + from-az
+ 0x0004f247, // n0x154a c0x0000 (---------------) + from-co
+ 0x00056b87, // n0x154b c0x0000 (---------------) + from-la
+ 0x0005c687, // n0x154c c0x0000 (---------------) + from-ny
+ 0x00005a42, // n0x154d c0x0000 (---------------) + gb
+ 0x00104507, // n0x154e c0x0000 (---------------) + gets-it
+ 0x0005058c, // n0x154f c0x0000 (---------------) + ham-radio-op
+ 0x00085007, // n0x1550 c0x0000 (---------------) + homeftp
+ 0x000909c6, // n0x1551 c0x0000 (---------------) + homeip
+ 0x00090ec9, // n0x1552 c0x0000 (---------------) + homelinux
+ 0x00091b88, // n0x1553 c0x0000 (---------------) + homeunix
+ 0x00017d42, // n0x1554 c0x0000 (---------------) + hu
+ 0x00000242, // n0x1555 c0x0000 (---------------) + in
+ 0x0005554b, // n0x1556 c0x0000 (---------------) + in-the-band
+ 0x00166909, // n0x1557 c0x0000 (---------------) + is-a-chef
+ 0x00052e49, // n0x1558 c0x0000 (---------------) + is-a-geek
+ 0x00191648, // n0x1559 c0x0000 (---------------) + isa-geek
+ 0x000990c2, // n0x155a c0x0000 (---------------) + jp
+ 0x00183609, // n0x155b c0x0000 (---------------) + kicks-ass
+ 0x00019c4d, // n0x155c c0x0000 (---------------) + office-on-the
+ 0x000c7e07, // n0x155d c0x0000 (---------------) + podzone
+ 0x0004294d, // n0x155e c0x0000 (---------------) + scrapper-site
+ 0x00002e82, // n0x155f c0x0000 (---------------) + se
+ 0x00130f46, // n0x1560 c0x0000 (---------------) + selfip
+ 0x0007efc8, // n0x1561 c0x0000 (---------------) + sells-it
+ 0x00079088, // n0x1562 c0x0000 (---------------) + servebbs
+ 0x00161348, // n0x1563 c0x0000 (---------------) + serveftp
+ 0x0003f688, // n0x1564 c0x0000 (---------------) + thruhere
+ 0x0000cf02, // n0x1565 c0x0000 (---------------) + uk
+ 0x00110e86, // n0x1566 c0x0000 (---------------) + webhop
+ 0x000043c2, // n0x1567 c0x0000 (---------------) + za
+ 0x00000581, // n0x1568 c0x0000 (---------------) + r
+ 0x396cbe44, // n0x1569 c0x00e5 (n0x156b-n0x156d) o I prod
+ 0x39a2a203, // n0x156a c0x00e6 (n0x156d-n0x1570) o I ssl
+ 0x00000181, // n0x156b c0x0000 (---------------) + a
+ 0x00189c06, // n0x156c c0x0000 (---------------) + global
+ 0x00000181, // n0x156d c0x0000 (---------------) + a
+ 0x00000001, // n0x156e c0x0000 (---------------) + b
+ 0x00189c06, // n0x156f c0x0000 (---------------) + global
+ 0x00246584, // n0x1570 c0x0000 (---------------) + I arts
+ 0x00222ac3, // n0x1571 c0x0000 (---------------) + I com
+ 0x00238544, // n0x1572 c0x0000 (---------------) + I firm
+ 0x00200304, // n0x1573 c0x0000 (---------------) + I info
+ 0x002170c3, // n0x1574 c0x0000 (---------------) + I net
+ 0x002193c5, // n0x1575 c0x0000 (---------------) + I other
+ 0x00214943, // n0x1576 c0x0000 (---------------) + I per
+ 0x002e6343, // n0x1577 c0x0000 (---------------) + I rec
+ 0x002cf4c5, // n0x1578 c0x0000 (---------------) + I store
+ 0x00219fc3, // n0x1579 c0x0000 (---------------) + I web
+ 0x3a622ac3, // n0x157a c0x00e9 (n0x1583-n0x1584) + I com
+ 0x002d75c3, // n0x157b c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x157c c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x157d c0x0000 (---------------) + I mil
+ 0x00203604, // n0x157e c0x0000 (---------------) + I mobi
+ 0x00298944, // n0x157f c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1580 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1581 c0x0000 (---------------) + I org
+ 0x00206103, // n0x1582 c0x0000 (---------------) + I sch
+ 0x000e4188, // n0x1583 c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x1584 c0x0000 (---------------) + blogspot
+ 0x003401c2, // n0x1585 c0x0000 (---------------) + I bv
+ 0x00000742, // n0x1586 c0x0000 (---------------) + co
+ 0x3b21b502, // n0x1587 c0x00ec (n0x185d-n0x185e) + I aa
+ 0x00301488, // n0x1588 c0x0000 (---------------) + I aarborte
+ 0x0021b986, // n0x1589 c0x0000 (---------------) + I aejrie
+ 0x002aa946, // n0x158a c0x0000 (---------------) + I afjord
+ 0x0021b307, // n0x158b c0x0000 (---------------) + I agdenes
+ 0x3b6076c2, // n0x158c c0x00ed (n0x185e-n0x185f) + I ah
+ 0x3ba2b548, // n0x158d c0x00ee (n0x185f-n0x1860) o I akershus
+ 0x0031a9ca, // n0x158e c0x0000 (---------------) + I aknoluokta
+ 0x0024d548, // n0x158f c0x0000 (---------------) + I akrehamn
+ 0x00200882, // n0x1590 c0x0000 (---------------) + I al
+ 0x0036e809, // n0x1591 c0x0000 (---------------) + I alaheadju
+ 0x0036cc07, // n0x1592 c0x0000 (---------------) + I alesund
+ 0x0020e486, // n0x1593 c0x0000 (---------------) + I algard
+ 0x00219749, // n0x1594 c0x0000 (---------------) + I alstahaug
+ 0x0028ee04, // n0x1595 c0x0000 (---------------) + I alta
+ 0x002ab3c6, // n0x1596 c0x0000 (---------------) + I alvdal
+ 0x002aad44, // n0x1597 c0x0000 (---------------) + I amli
+ 0x0025cb44, // n0x1598 c0x0000 (---------------) + I amot
+ 0x00245409, // n0x1599 c0x0000 (---------------) + I andasuolo
+ 0x002ee906, // n0x159a c0x0000 (---------------) + I andebu
+ 0x0034bfc5, // n0x159b c0x0000 (---------------) + I andoy
+ 0x00279985, // n0x159c c0x0000 (---------------) + I ardal
+ 0x002a6dc7, // n0x159d c0x0000 (---------------) + I aremark
+ 0x00275c47, // n0x159e c0x0000 (---------------) + I arendal
+ 0x002798c4, // n0x159f c0x0000 (---------------) + I arna
+ 0x0021b546, // n0x15a0 c0x0000 (---------------) + I aseral
+ 0x00212605, // n0x15a1 c0x0000 (---------------) + I asker
+ 0x0027d585, // n0x15a2 c0x0000 (---------------) + I askim
+ 0x0031d885, // n0x15a3 c0x0000 (---------------) + I askoy
+ 0x002dbc07, // n0x15a4 c0x0000 (---------------) + I askvoll
+ 0x00311545, // n0x15a5 c0x0000 (---------------) + I asnes
+ 0x002f1489, // n0x15a6 c0x0000 (---------------) + I audnedaln
+ 0x00271305, // n0x15a7 c0x0000 (---------------) + I aukra
+ 0x002e0084, // n0x15a8 c0x0000 (---------------) + I aure
+ 0x0035b087, // n0x15a9 c0x0000 (---------------) + I aurland
+ 0x0036d70e, // n0x15aa c0x0000 (---------------) + I aurskog-holand
+ 0x003298c9, // n0x15ab c0x0000 (---------------) + I austevoll
+ 0x002f5049, // n0x15ac c0x0000 (---------------) + I austrheim
+ 0x0030dc86, // n0x15ad c0x0000 (---------------) + I averoy
+ 0x00321688, // n0x15ae c0x0000 (---------------) + I badaddja
+ 0x00329f8b, // n0x15af c0x0000 (---------------) + I bahcavuotna
+ 0x002a080c, // n0x15b0 c0x0000 (---------------) + I bahccavuotna
+ 0x00323c86, // n0x15b1 c0x0000 (---------------) + I baidar
+ 0x00342887, // n0x15b2 c0x0000 (---------------) + I bajddar
+ 0x0035dbc5, // n0x15b3 c0x0000 (---------------) + I balat
+ 0x0023d9ca, // n0x15b4 c0x0000 (---------------) + I balestrand
+ 0x0030b989, // n0x15b5 c0x0000 (---------------) + I ballangen
+ 0x00268ec9, // n0x15b6 c0x0000 (---------------) + I balsfjord
+ 0x002c8706, // n0x15b7 c0x0000 (---------------) + I bamble
+ 0x002d37c5, // n0x15b8 c0x0000 (---------------) + I bardu
+ 0x002b0545, // n0x15b9 c0x0000 (---------------) + I barum
+ 0x0031a6c9, // n0x15ba c0x0000 (---------------) + I batsfjord
+ 0x0035ddcb, // n0x15bb c0x0000 (---------------) + I bearalvahki
+ 0x00269f06, // n0x15bc c0x0000 (---------------) + I beardu
+ 0x00319a86, // n0x15bd c0x0000 (---------------) + I beiarn
+ 0x00204704, // n0x15be c0x0000 (---------------) + I berg
+ 0x00279f06, // n0x15bf c0x0000 (---------------) + I bergen
+ 0x00343e08, // n0x15c0 c0x0000 (---------------) + I berlevag
+ 0x00389486, // n0x15c1 c0x0000 (---------------) + I bievat
+ 0x00219046, // n0x15c2 c0x0000 (---------------) + I bindal
+ 0x00200f48, // n0x15c3 c0x0000 (---------------) + I birkenes
+ 0x00202647, // n0x15c4 c0x0000 (---------------) + I bjarkoy
+ 0x002033c9, // n0x15c5 c0x0000 (---------------) + I bjerkreim
+ 0x00205005, // n0x15c6 c0x0000 (---------------) + I bjugn
+ 0x000e4188, // n0x15c7 c0x0000 (---------------) + blogspot
+ 0x0038a504, // n0x15c8 c0x0000 (---------------) + I bodo
+ 0x0029bf44, // n0x15c9 c0x0000 (---------------) + I bokn
+ 0x0020aac5, // n0x15ca c0x0000 (---------------) + I bomlo
+ 0x0037dc49, // n0x15cb c0x0000 (---------------) + I bremanger
+ 0x00218ac7, // n0x15cc c0x0000 (---------------) + I bronnoy
+ 0x00218acb, // n0x15cd c0x0000 (---------------) + I bronnoysund
+ 0x0021a7ca, // n0x15ce c0x0000 (---------------) + I brumunddal
+ 0x0021d4c5, // n0x15cf c0x0000 (---------------) + I bryne
+ 0x3be0a582, // n0x15d0 c0x00ef (n0x1860-n0x1861) + I bu
+ 0x002eea07, // n0x15d1 c0x0000 (---------------) + I budejju
+ 0x3c318ec8, // n0x15d2 c0x00f0 (n0x1861-n0x1862) o I buskerud
+ 0x00251987, // n0x15d3 c0x0000 (---------------) + I bygland
+ 0x0025f245, // n0x15d4 c0x0000 (---------------) + I bykle
+ 0x0032234a, // n0x15d5 c0x0000 (---------------) + I cahcesuolo
+ 0x00000742, // n0x15d6 c0x0000 (---------------) + co
+ 0x0024fd0b, // n0x15d7 c0x0000 (---------------) + I davvenjarga
+ 0x0024eb4a, // n0x15d8 c0x0000 (---------------) + I davvesiida
+ 0x002e9206, // n0x15d9 c0x0000 (---------------) + I deatnu
+ 0x002b8f83, // n0x15da c0x0000 (---------------) + I dep
+ 0x0023404d, // n0x15db c0x0000 (---------------) + I dielddanuorri
+ 0x0023e18c, // n0x15dc c0x0000 (---------------) + I divtasvuodna
+ 0x002690cd, // n0x15dd c0x0000 (---------------) + I divttasvuotna
+ 0x002f8b05, // n0x15de c0x0000 (---------------) + I donna
+ 0x002396c5, // n0x15df c0x0000 (---------------) + I dovre
+ 0x0034d707, // n0x15e0 c0x0000 (---------------) + I drammen
+ 0x002edfc9, // n0x15e1 c0x0000 (---------------) + I drangedal
+ 0x0031a8c6, // n0x15e2 c0x0000 (---------------) + I drobak
+ 0x002c7505, // n0x15e3 c0x0000 (---------------) + I dyroy
+ 0x0021e888, // n0x15e4 c0x0000 (---------------) + I egersund
+ 0x00262743, // n0x15e5 c0x0000 (---------------) + I eid
+ 0x002ec4c8, // n0x15e6 c0x0000 (---------------) + I eidfjord
+ 0x00279e08, // n0x15e7 c0x0000 (---------------) + I eidsberg
+ 0x002ac2c7, // n0x15e8 c0x0000 (---------------) + I eidskog
+ 0x00262748, // n0x15e9 c0x0000 (---------------) + I eidsvoll
+ 0x00383049, // n0x15ea c0x0000 (---------------) + I eigersund
+ 0x00227ec7, // n0x15eb c0x0000 (---------------) + I elverum
+ 0x00300e87, // n0x15ec c0x0000 (---------------) + I enebakk
+ 0x002674c8, // n0x15ed c0x0000 (---------------) + I engerdal
+ 0x002569c4, // n0x15ee c0x0000 (---------------) + I etne
+ 0x002569c7, // n0x15ef c0x0000 (---------------) + I etnedal
+ 0x0037ab48, // n0x15f0 c0x0000 (---------------) + I evenassi
+ 0x0030a446, // n0x15f1 c0x0000 (---------------) + I evenes
+ 0x00201f0f, // n0x15f2 c0x0000 (---------------) + I evje-og-hornnes
+ 0x00345a07, // n0x15f3 c0x0000 (---------------) + I farsund
+ 0x002c3146, // n0x15f4 c0x0000 (---------------) + I fauske
+ 0x0020c745, // n0x15f5 c0x0000 (---------------) + I fedje
+ 0x00234803, // n0x15f6 c0x0000 (---------------) + I fet
+ 0x00324187, // n0x15f7 c0x0000 (---------------) + I fetsund
+ 0x00235ec3, // n0x15f8 c0x0000 (---------------) + I fhs
+ 0x002365c6, // n0x15f9 c0x0000 (---------------) + I finnoy
+ 0x00238f06, // n0x15fa c0x0000 (---------------) + I fitjar
+ 0x00239986, // n0x15fb c0x0000 (---------------) + I fjaler
+ 0x0027e245, // n0x15fc c0x0000 (---------------) + I fjell
+ 0x00252b83, // n0x15fd c0x0000 (---------------) + I fla
+ 0x0035be08, // n0x15fe c0x0000 (---------------) + I flakstad
+ 0x00353389, // n0x15ff c0x0000 (---------------) + I flatanger
+ 0x0036500b, // n0x1600 c0x0000 (---------------) + I flekkefjord
+ 0x00239b08, // n0x1601 c0x0000 (---------------) + I flesberg
+ 0x0023c105, // n0x1602 c0x0000 (---------------) + I flora
+ 0x0023d4c5, // n0x1603 c0x0000 (---------------) + I floro
+ 0x3c758002, // n0x1604 c0x00f1 (n0x1862-n0x1863) + I fm
+ 0x00362789, // n0x1605 c0x0000 (---------------) + I folkebibl
+ 0x00241787, // n0x1606 c0x0000 (---------------) + I folldal
+ 0x003640c5, // n0x1607 c0x0000 (---------------) + I forde
+ 0x00245307, // n0x1608 c0x0000 (---------------) + I forsand
+ 0x00247586, // n0x1609 c0x0000 (---------------) + I fosnes
+ 0x0034af05, // n0x160a c0x0000 (---------------) + I frana
+ 0x0024804b, // n0x160b c0x0000 (---------------) + I fredrikstad
+ 0x00248cc4, // n0x160c c0x0000 (---------------) + I frei
+ 0x0024d105, // n0x160d c0x0000 (---------------) + I frogn
+ 0x0024d247, // n0x160e c0x0000 (---------------) + I froland
+ 0x00263e06, // n0x160f c0x0000 (---------------) + I frosta
+ 0x00264245, // n0x1610 c0x0000 (---------------) + I froya
+ 0x0026fe07, // n0x1611 c0x0000 (---------------) + I fuoisku
+ 0x00270587, // n0x1612 c0x0000 (---------------) + I fuossko
+ 0x002a7504, // n0x1613 c0x0000 (---------------) + I fusa
+ 0x0027798a, // n0x1614 c0x0000 (---------------) + I fylkesbibl
+ 0x00277e48, // n0x1615 c0x0000 (---------------) + I fyresdal
+ 0x0035ea49, // n0x1616 c0x0000 (---------------) + I gaivuotna
+ 0x00215b85, // n0x1617 c0x0000 (---------------) + I galsa
+ 0x0024ff46, // n0x1618 c0x0000 (---------------) + I gamvik
+ 0x00343fca, // n0x1619 c0x0000 (---------------) + I gangaviika
+ 0x003442c6, // n0x161a c0x0000 (---------------) + I gaular
+ 0x002542c7, // n0x161b c0x0000 (---------------) + I gausdal
+ 0x0035e70d, // n0x161c c0x0000 (---------------) + I giehtavuoatna
+ 0x00220049, // n0x161d c0x0000 (---------------) + I gildeskal
+ 0x00347945, // n0x161e c0x0000 (---------------) + I giske
+ 0x0030ee07, // n0x161f c0x0000 (---------------) + I gjemnes
+ 0x002ed908, // n0x1620 c0x0000 (---------------) + I gjerdrum
+ 0x0031c188, // n0x1621 c0x0000 (---------------) + I gjerstad
+ 0x0031d607, // n0x1622 c0x0000 (---------------) + I gjesdal
+ 0x003446c6, // n0x1623 c0x0000 (---------------) + I gjovik
+ 0x00395287, // n0x1624 c0x0000 (---------------) + I gloppen
+ 0x00238a83, // n0x1625 c0x0000 (---------------) + I gol
+ 0x00320b04, // n0x1626 c0x0000 (---------------) + I gran
+ 0x00348cc5, // n0x1627 c0x0000 (---------------) + I grane
+ 0x00376107, // n0x1628 c0x0000 (---------------) + I granvin
+ 0x00379f89, // n0x1629 c0x0000 (---------------) + I gratangen
+ 0x002136c8, // n0x162a c0x0000 (---------------) + I grimstad
+ 0x002c77c5, // n0x162b c0x0000 (---------------) + I grong
+ 0x00222fc4, // n0x162c c0x0000 (---------------) + I grue
+ 0x00331485, // n0x162d c0x0000 (---------------) + I gulen
+ 0x002373cd, // n0x162e c0x0000 (---------------) + I guovdageaidnu
+ 0x00202dc2, // n0x162f c0x0000 (---------------) + I ha
+ 0x00289a46, // n0x1630 c0x0000 (---------------) + I habmer
+ 0x00330e86, // n0x1631 c0x0000 (---------------) + I hadsel
+ 0x002f5e0a, // n0x1632 c0x0000 (---------------) + I hagebostad
+ 0x0035b7c6, // n0x1633 c0x0000 (---------------) + I halden
+ 0x00363605, // n0x1634 c0x0000 (---------------) + I halsa
+ 0x0036e045, // n0x1635 c0x0000 (---------------) + I hamar
+ 0x0036e047, // n0x1636 c0x0000 (---------------) + I hamaroy
+ 0x0036794c, // n0x1637 c0x0000 (---------------) + I hammarfeasta
+ 0x002ef38a, // n0x1638 c0x0000 (---------------) + I hammerfest
+ 0x002796c6, // n0x1639 c0x0000 (---------------) + I hapmir
+ 0x002ae105, // n0x163a c0x0000 (---------------) + I haram
+ 0x00279d46, // n0x163b c0x0000 (---------------) + I hareid
+ 0x0027a087, // n0x163c c0x0000 (---------------) + I harstad
+ 0x0027b486, // n0x163d c0x0000 (---------------) + I hasvik
+ 0x0027e14c, // n0x163e c0x0000 (---------------) + I hattfjelldal
+ 0x00219889, // n0x163f c0x0000 (---------------) + I haugesund
+ 0x3ca9bc07, // n0x1640 c0x00f2 (n0x1863-n0x1866) o I hedmark
+ 0x0027f885, // n0x1641 c0x0000 (---------------) + I hemne
+ 0x0027f886, // n0x1642 c0x0000 (---------------) + I hemnes
+ 0x0027fc88, // n0x1643 c0x0000 (---------------) + I hemsedal
+ 0x0022db45, // n0x1644 c0x0000 (---------------) + I herad
+ 0x0028fd85, // n0x1645 c0x0000 (---------------) + I hitra
+ 0x0028ffc8, // n0x1646 c0x0000 (---------------) + I hjartdal
+ 0x002901ca, // n0x1647 c0x0000 (---------------) + I hjelmeland
+ 0x3ce0cc02, // n0x1648 c0x00f3 (n0x1866-n0x1867) + I hl
+ 0x3d206182, // n0x1649 c0x00f4 (n0x1867-n0x1868) + I hm
+ 0x002536c5, // n0x164a c0x0000 (---------------) + I hobol
+ 0x0029a803, // n0x164b c0x0000 (---------------) + I hof
+ 0x002c7348, // n0x164c c0x0000 (---------------) + I hokksund
+ 0x002351c3, // n0x164d c0x0000 (---------------) + I hol
+ 0x00290444, // n0x164e c0x0000 (---------------) + I hole
+ 0x0025a04b, // n0x164f c0x0000 (---------------) + I holmestrand
+ 0x00271dc8, // n0x1650 c0x0000 (---------------) + I holtalen
+ 0x00292148, // n0x1651 c0x0000 (---------------) + I honefoss
+ 0x3d6f84c9, // n0x1652 c0x00f5 (n0x1868-n0x1869) o I hordaland
+ 0x002939c9, // n0x1653 c0x0000 (---------------) + I hornindal
+ 0x00294186, // n0x1654 c0x0000 (---------------) + I horten
+ 0x00295188, // n0x1655 c0x0000 (---------------) + I hoyanger
+ 0x00295389, // n0x1656 c0x0000 (---------------) + I hoylandet
+ 0x00296046, // n0x1657 c0x0000 (---------------) + I hurdal
+ 0x002961c5, // n0x1658 c0x0000 (---------------) + I hurum
+ 0x003512c6, // n0x1659 c0x0000 (---------------) + I hvaler
+ 0x0036b589, // n0x165a c0x0000 (---------------) + I hyllestad
+ 0x0030a287, // n0x165b c0x0000 (---------------) + I ibestad
+ 0x00247d86, // n0x165c c0x0000 (---------------) + I idrett
+ 0x002f3407, // n0x165d c0x0000 (---------------) + I inderoy
+ 0x00304b07, // n0x165e c0x0000 (---------------) + I iveland
+ 0x0025f644, // n0x165f c0x0000 (---------------) + I ivgu
+ 0x3da14c09, // n0x1660 c0x00f6 (n0x1869-n0x186a) + I jan-mayen
+ 0x002b4408, // n0x1661 c0x0000 (---------------) + I jessheim
+ 0x00345148, // n0x1662 c0x0000 (---------------) + I jevnaker
+ 0x002326c7, // n0x1663 c0x0000 (---------------) + I jolster
+ 0x002afb86, // n0x1664 c0x0000 (---------------) + I jondal
+ 0x0038a009, // n0x1665 c0x0000 (---------------) + I jorpeland
+ 0x002ab887, // n0x1666 c0x0000 (---------------) + I kafjord
+ 0x0022e84a, // n0x1667 c0x0000 (---------------) + I karasjohka
+ 0x002d8848, // n0x1668 c0x0000 (---------------) + I karasjok
+ 0x0023eb87, // n0x1669 c0x0000 (---------------) + I karlsoy
+ 0x00305646, // n0x166a c0x0000 (---------------) + I karmoy
+ 0x002eaa8a, // n0x166b c0x0000 (---------------) + I kautokeino
+ 0x0023cc48, // n0x166c c0x0000 (---------------) + I kirkenes
+ 0x0025ef45, // n0x166d c0x0000 (---------------) + I klabu
+ 0x0021cd85, // n0x166e c0x0000 (---------------) + I klepp
+ 0x002ac947, // n0x166f c0x0000 (---------------) + I kommune
+ 0x002b7dc9, // n0x1670 c0x0000 (---------------) + I kongsberg
+ 0x002b814b, // n0x1671 c0x0000 (---------------) + I kongsvinger
+ 0x002c83c8, // n0x1672 c0x0000 (---------------) + I kopervik
+ 0x00271389, // n0x1673 c0x0000 (---------------) + I kraanghke
+ 0x0023a407, // n0x1674 c0x0000 (---------------) + I kragero
+ 0x0029db0c, // n0x1675 c0x0000 (---------------) + I kristiansand
+ 0x0029df8c, // n0x1676 c0x0000 (---------------) + I kristiansund
+ 0x0029e28a, // n0x1677 c0x0000 (---------------) + I krodsherad
+ 0x0029e50c, // n0x1678 c0x0000 (---------------) + I krokstadelva
+ 0x002aa8c8, // n0x1679 c0x0000 (---------------) + I kvafjord
+ 0x002aaac8, // n0x167a c0x0000 (---------------) + I kvalsund
+ 0x002aacc4, // n0x167b c0x0000 (---------------) + I kvam
+ 0x002aba49, // n0x167c c0x0000 (---------------) + I kvanangen
+ 0x002abc89, // n0x167d c0x0000 (---------------) + I kvinesdal
+ 0x002abeca, // n0x167e c0x0000 (---------------) + I kvinnherad
+ 0x002ac149, // n0x167f c0x0000 (---------------) + I kviteseid
+ 0x002ac487, // n0x1680 c0x0000 (---------------) + I kvitsoy
+ 0x00381bcc, // n0x1681 c0x0000 (---------------) + I laakesvuemie
+ 0x00207686, // n0x1682 c0x0000 (---------------) + I lahppi
+ 0x00256cc8, // n0x1683 c0x0000 (---------------) + I langevag
+ 0x00344386, // n0x1684 c0x0000 (---------------) + I lardal
+ 0x002d7a86, // n0x1685 c0x0000 (---------------) + I larvik
+ 0x00347847, // n0x1686 c0x0000 (---------------) + I lavagis
+ 0x00358888, // n0x1687 c0x0000 (---------------) + I lavangen
+ 0x002f020b, // n0x1688 c0x0000 (---------------) + I leangaviika
+ 0x00251847, // n0x1689 c0x0000 (---------------) + I lebesby
+ 0x00227989, // n0x168a c0x0000 (---------------) + I leikanger
+ 0x002386c9, // n0x168b c0x0000 (---------------) + I leirfjord
+ 0x00244b87, // n0x168c c0x0000 (---------------) + I leirvik
+ 0x00203c44, // n0x168d c0x0000 (---------------) + I leka
+ 0x0034b3c7, // n0x168e c0x0000 (---------------) + I leksvik
+ 0x00364686, // n0x168f c0x0000 (---------------) + I lenvik
+ 0x00364246, // n0x1690 c0x0000 (---------------) + I lerdal
+ 0x002a5105, // n0x1691 c0x0000 (---------------) + I lesja
+ 0x00326448, // n0x1692 c0x0000 (---------------) + I levanger
+ 0x002a6004, // n0x1693 c0x0000 (---------------) + I lier
+ 0x002a6006, // n0x1694 c0x0000 (---------------) + I lierne
+ 0x002ef24b, // n0x1695 c0x0000 (---------------) + I lillehammer
+ 0x00270d89, // n0x1696 c0x0000 (---------------) + I lillesand
+ 0x0031d786, // n0x1697 c0x0000 (---------------) + I lindas
+ 0x0031dc09, // n0x1698 c0x0000 (---------------) + I lindesnes
+ 0x002dbd86, // n0x1699 c0x0000 (---------------) + I loabat
+ 0x002455c8, // n0x169a c0x0000 (---------------) + I lodingen
+ 0x00260a43, // n0x169b c0x0000 (---------------) + I lom
+ 0x0031b945, // n0x169c c0x0000 (---------------) + I loppa
+ 0x003444c9, // n0x169d c0x0000 (---------------) + I lorenskog
+ 0x0034d145, // n0x169e c0x0000 (---------------) + I loten
+ 0x002d8b84, // n0x169f c0x0000 (---------------) + I lund
+ 0x00263646, // n0x16a0 c0x0000 (---------------) + I lunner
+ 0x0029cb85, // n0x16a1 c0x0000 (---------------) + I luroy
+ 0x002c7bc6, // n0x16a2 c0x0000 (---------------) + I luster
+ 0x002e1c87, // n0x16a3 c0x0000 (---------------) + I lyngdal
+ 0x0029c206, // n0x16a4 c0x0000 (---------------) + I lyngen
+ 0x0028448b, // n0x16a5 c0x0000 (---------------) + I malatvuopmi
+ 0x0034ce07, // n0x16a6 c0x0000 (---------------) + I malselv
+ 0x00307d86, // n0x16a7 c0x0000 (---------------) + I malvik
+ 0x00358046, // n0x16a8 c0x0000 (---------------) + I mandal
+ 0x002a6e86, // n0x16a9 c0x0000 (---------------) + I marker
+ 0x00279889, // n0x16aa c0x0000 (---------------) + I marnardal
+ 0x003419ca, // n0x16ab c0x0000 (---------------) + I masfjorden
+ 0x00314445, // n0x16ac c0x0000 (---------------) + I masoy
+ 0x0020f58d, // n0x16ad c0x0000 (---------------) + I matta-varjjat
+ 0x002902c6, // n0x16ae c0x0000 (---------------) + I meland
+ 0x002d8246, // n0x16af c0x0000 (---------------) + I meldal
+ 0x002a1bc6, // n0x16b0 c0x0000 (---------------) + I melhus
+ 0x00259d05, // n0x16b1 c0x0000 (---------------) + I meloy
+ 0x0022b487, // n0x16b2 c0x0000 (---------------) + I meraker
+ 0x002886c7, // n0x16b3 c0x0000 (---------------) + I midsund
+ 0x002061ce, // n0x16b4 c0x0000 (---------------) + I midtre-gauldal
+ 0x0023fa03, // n0x16b5 c0x0000 (---------------) + I mil
+ 0x002afb49, // n0x16b6 c0x0000 (---------------) + I mjondalen
+ 0x002f3689, // n0x16b7 c0x0000 (---------------) + I mo-i-rana
+ 0x0023ddc7, // n0x16b8 c0x0000 (---------------) + I moareke
+ 0x00208487, // n0x16b9 c0x0000 (---------------) + I modalen
+ 0x003120c5, // n0x16ba c0x0000 (---------------) + I modum
+ 0x0029f845, // n0x16bb c0x0000 (---------------) + I molde
+ 0x3de70a0f, // n0x16bc c0x00f7 (n0x186a-n0x186c) o I more-og-romsdal
+ 0x002b7447, // n0x16bd c0x0000 (---------------) + I mosjoen
+ 0x002b7608, // n0x16be c0x0000 (---------------) + I moskenes
+ 0x002b7b44, // n0x16bf c0x0000 (---------------) + I moss
+ 0x002b8006, // n0x16c0 c0x0000 (---------------) + I mosvik
+ 0x3e2dcb42, // n0x16c1 c0x00f8 (n0x186c-n0x186d) + I mr
+ 0x002bb5c6, // n0x16c2 c0x0000 (---------------) + I muosat
+ 0x002bd646, // n0x16c3 c0x0000 (---------------) + I museum
+ 0x002f054e, // n0x16c4 c0x0000 (---------------) + I naamesjevuemie
+ 0x002ec30a, // n0x16c5 c0x0000 (---------------) + I namdalseid
+ 0x0021c1c6, // n0x16c6 c0x0000 (---------------) + I namsos
+ 0x00232b4a, // n0x16c7 c0x0000 (---------------) + I namsskogan
+ 0x002b2489, // n0x16c8 c0x0000 (---------------) + I nannestad
+ 0x002ff685, // n0x16c9 c0x0000 (---------------) + I naroy
+ 0x0037c388, // n0x16ca c0x0000 (---------------) + I narviika
+ 0x00393206, // n0x16cb c0x0000 (---------------) + I narvik
+ 0x0031e108, // n0x16cc c0x0000 (---------------) + I naustdal
+ 0x00354f08, // n0x16cd c0x0000 (---------------) + I navuotna
+ 0x00395acb, // n0x16ce c0x0000 (---------------) + I nedre-eiker
+ 0x0021b405, // n0x16cf c0x0000 (---------------) + I nesna
+ 0x003115c8, // n0x16d0 c0x0000 (---------------) + I nesodden
+ 0x0020108c, // n0x16d1 c0x0000 (---------------) + I nesoddtangen
+ 0x0025f107, // n0x16d2 c0x0000 (---------------) + I nesseby
+ 0x00239146, // n0x16d3 c0x0000 (---------------) + I nesset
+ 0x002e6588, // n0x16d4 c0x0000 (---------------) + I nissedal
+ 0x002677c8, // n0x16d5 c0x0000 (---------------) + I nittedal
+ 0x3e636482, // n0x16d6 c0x00f9 (n0x186d-n0x186e) + I nl
+ 0x002ab18b, // n0x16d7 c0x0000 (---------------) + I nord-aurdal
+ 0x00200c09, // n0x16d8 c0x0000 (---------------) + I nord-fron
+ 0x003473c9, // n0x16d9 c0x0000 (---------------) + I nord-odal
+ 0x0031da87, // n0x16da c0x0000 (---------------) + I norddal
+ 0x002befc8, // n0x16db c0x0000 (---------------) + I nordkapp
+ 0x3ea3dfc8, // n0x16dc c0x00fa (n0x186e-n0x1872) o I nordland
+ 0x002c5f0b, // n0x16dd c0x0000 (---------------) + I nordre-land
+ 0x003914c9, // n0x16de c0x0000 (---------------) + I nordreisa
+ 0x002113cd, // n0x16df c0x0000 (---------------) + I nore-og-uvdal
+ 0x0023fdc8, // n0x16e0 c0x0000 (---------------) + I notodden
+ 0x00288b08, // n0x16e1 c0x0000 (---------------) + I notteroy
+ 0x3ee00e02, // n0x16e2 c0x00fb (n0x1872-n0x1873) + I nt
+ 0x00396f44, // n0x16e3 c0x0000 (---------------) + I odda
+ 0x3f209982, // n0x16e4 c0x00fc (n0x1873-n0x1874) + I of
+ 0x002d89c6, // n0x16e5 c0x0000 (---------------) + I oksnes
+ 0x3f600a02, // n0x16e6 c0x00fd (n0x1874-n0x1875) + I ol
+ 0x0021d00a, // n0x16e7 c0x0000 (---------------) + I omasvuotna
+ 0x0029b206, // n0x16e8 c0x0000 (---------------) + I oppdal
+ 0x00220b88, // n0x16e9 c0x0000 (---------------) + I oppegard
+ 0x00241b48, // n0x16ea c0x0000 (---------------) + I orkanger
+ 0x00321986, // n0x16eb c0x0000 (---------------) + I orkdal
+ 0x0032e206, // n0x16ec c0x0000 (---------------) + I orland
+ 0x002ce006, // n0x16ed c0x0000 (---------------) + I orskog
+ 0x0029fac5, // n0x16ee c0x0000 (---------------) + I orsta
+ 0x0022be04, // n0x16ef c0x0000 (---------------) + I osen
+ 0x3fab8a04, // n0x16f0 c0x00fe (n0x1875-n0x1876) + I oslo
+ 0x00207f86, // n0x16f1 c0x0000 (---------------) + I osoyro
+ 0x002586c7, // n0x16f2 c0x0000 (---------------) + I osteroy
+ 0x3fed6987, // n0x16f3 c0x00ff (n0x1876-n0x1877) o I ostfold
+ 0x0020300b, // n0x16f4 c0x0000 (---------------) + I ostre-toten
+ 0x0036dac9, // n0x16f5 c0x0000 (---------------) + I overhalla
+ 0x0023970a, // n0x16f6 c0x0000 (---------------) + I ovre-eiker
+ 0x002f3544, // n0x16f7 c0x0000 (---------------) + I oyer
+ 0x00300d08, // n0x16f8 c0x0000 (---------------) + I oygarden
+ 0x00247b4d, // n0x16f9 c0x0000 (---------------) + I oystre-slidre
+ 0x002c9e89, // n0x16fa c0x0000 (---------------) + I porsanger
+ 0x002ca0c8, // n0x16fb c0x0000 (---------------) + I porsangu
+ 0x002ca349, // n0x16fc c0x0000 (---------------) + I porsgrunn
+ 0x002cba44, // n0x16fd c0x0000 (---------------) + I priv
+ 0x00212ec4, // n0x16fe c0x0000 (---------------) + I rade
+ 0x00254d85, // n0x16ff c0x0000 (---------------) + I radoy
+ 0x0035f14b, // n0x1700 c0x0000 (---------------) + I rahkkeravju
+ 0x00271d46, // n0x1701 c0x0000 (---------------) + I raholt
+ 0x002a2305, // n0x1702 c0x0000 (---------------) + I raisa
+ 0x0032a2c9, // n0x1703 c0x0000 (---------------) + I rakkestad
+ 0x0021b608, // n0x1704 c0x0000 (---------------) + I ralingen
+ 0x0025abc4, // n0x1705 c0x0000 (---------------) + I rana
+ 0x0023db49, // n0x1706 c0x0000 (---------------) + I randaberg
+ 0x0026a685, // n0x1707 c0x0000 (---------------) + I rauma
+ 0x00275c88, // n0x1708 c0x0000 (---------------) + I rendalen
+ 0x0033c507, // n0x1709 c0x0000 (---------------) + I rennebu
+ 0x002f5448, // n0x170a c0x0000 (---------------) + I rennesoy
+ 0x002af746, // n0x170b c0x0000 (---------------) + I rindal
+ 0x003247c7, // n0x170c c0x0000 (---------------) + I ringebu
+ 0x002a8c49, // n0x170d c0x0000 (---------------) + I ringerike
+ 0x00324cc9, // n0x170e c0x0000 (---------------) + I ringsaker
+ 0x0025b4c5, // n0x170f c0x0000 (---------------) + I risor
+ 0x002346c5, // n0x1710 c0x0000 (---------------) + I rissa
+ 0x4021d702, // n0x1711 c0x0100 (n0x1877-n0x1878) + I rl
+ 0x002dfe44, // n0x1712 c0x0000 (---------------) + I roan
+ 0x0036a305, // n0x1713 c0x0000 (---------------) + I rodoy
+ 0x0033c1c6, // n0x1714 c0x0000 (---------------) + I rollag
+ 0x00302a85, // n0x1715 c0x0000 (---------------) + I romsa
+ 0x0023d587, // n0x1716 c0x0000 (---------------) + I romskog
+ 0x002e3245, // n0x1717 c0x0000 (---------------) + I roros
+ 0x00263e44, // n0x1718 c0x0000 (---------------) + I rost
+ 0x0030dd46, // n0x1719 c0x0000 (---------------) + I royken
+ 0x002c7587, // n0x171a c0x0000 (---------------) + I royrvik
+ 0x002dcb86, // n0x171b c0x0000 (---------------) + I ruovat
+ 0x00268ac5, // n0x171c c0x0000 (---------------) + I rygge
+ 0x0031ce08, // n0x171d c0x0000 (---------------) + I salangen
+ 0x0031de05, // n0x171e c0x0000 (---------------) + I salat
+ 0x00341347, // n0x171f c0x0000 (---------------) + I saltdal
+ 0x0035b289, // n0x1720 c0x0000 (---------------) + I samnanger
+ 0x0029dd0a, // n0x1721 c0x0000 (---------------) + I sandefjord
+ 0x0023bac7, // n0x1722 c0x0000 (---------------) + I sandnes
+ 0x0023bacc, // n0x1723 c0x0000 (---------------) + I sandnessjoen
+ 0x0034bf86, // n0x1724 c0x0000 (---------------) + I sandoy
+ 0x0021db49, // n0x1725 c0x0000 (---------------) + I sarpsborg
+ 0x0022d7c5, // n0x1726 c0x0000 (---------------) + I sauda
+ 0x0022da88, // n0x1727 c0x0000 (---------------) + I sauherad
+ 0x0020a4c3, // n0x1728 c0x0000 (---------------) + I sel
+ 0x0020a4c5, // n0x1729 c0x0000 (---------------) + I selbu
+ 0x002fad85, // n0x172a c0x0000 (---------------) + I selje
+ 0x00245cc7, // n0x172b c0x0000 (---------------) + I seljord
+ 0x4060f182, // n0x172c c0x0101 (n0x1878-n0x1879) + I sf
+ 0x0023b647, // n0x172d c0x0000 (---------------) + I siellak
+ 0x002b79c6, // n0x172e c0x0000 (---------------) + I sigdal
+ 0x00214b46, // n0x172f c0x0000 (---------------) + I siljan
+ 0x002c1186, // n0x1730 c0x0000 (---------------) + I sirdal
+ 0x00267706, // n0x1731 c0x0000 (---------------) + I skanit
+ 0x00310108, // n0x1732 c0x0000 (---------------) + I skanland
+ 0x00262585, // n0x1733 c0x0000 (---------------) + I skaun
+ 0x002c3207, // n0x1734 c0x0000 (---------------) + I skedsmo
+ 0x002c320d, // n0x1735 c0x0000 (---------------) + I skedsmokorset
+ 0x00207b43, // n0x1736 c0x0000 (---------------) + I ski
+ 0x00207b45, // n0x1737 c0x0000 (---------------) + I skien
+ 0x002f2d47, // n0x1738 c0x0000 (---------------) + I skierva
+ 0x0036b048, // n0x1739 c0x0000 (---------------) + I skiptvet
+ 0x0036ac05, // n0x173a c0x0000 (---------------) + I skjak
+ 0x0030c348, // n0x173b c0x0000 (---------------) + I skjervoy
+ 0x002206c6, // n0x173c c0x0000 (---------------) + I skodje
+ 0x0022a247, // n0x173d c0x0000 (---------------) + I slattum
+ 0x00285605, // n0x173e c0x0000 (---------------) + I smola
+ 0x0021b486, // n0x173f c0x0000 (---------------) + I snaase
+ 0x00340405, // n0x1740 c0x0000 (---------------) + I snasa
+ 0x002aa34a, // n0x1741 c0x0000 (---------------) + I snillfjord
+ 0x002c4f86, // n0x1742 c0x0000 (---------------) + I snoasa
+ 0x002141c7, // n0x1743 c0x0000 (---------------) + I sogndal
+ 0x00308285, // n0x1744 c0x0000 (---------------) + I sogne
+ 0x002d9347, // n0x1745 c0x0000 (---------------) + I sokndal
+ 0x002d0204, // n0x1746 c0x0000 (---------------) + I sola
+ 0x002d8b06, // n0x1747 c0x0000 (---------------) + I solund
+ 0x002da005, // n0x1748 c0x0000 (---------------) + I somna
+ 0x002ee70b, // n0x1749 c0x0000 (---------------) + I sondre-land
+ 0x00364509, // n0x174a c0x0000 (---------------) + I songdalen
+ 0x0037184a, // n0x174b c0x0000 (---------------) + I sor-aurdal
+ 0x0025b548, // n0x174c c0x0000 (---------------) + I sor-fron
+ 0x002e0f48, // n0x174d c0x0000 (---------------) + I sor-odal
+ 0x002e888c, // n0x174e c0x0000 (---------------) + I sor-varanger
+ 0x002ec987, // n0x174f c0x0000 (---------------) + I sorfold
+ 0x00315608, // n0x1750 c0x0000 (---------------) + I sorreisa
+ 0x0031a288, // n0x1751 c0x0000 (---------------) + I sortland
+ 0x0031ee45, // n0x1752 c0x0000 (---------------) + I sorum
+ 0x002ac70a, // n0x1753 c0x0000 (---------------) + I spjelkavik
+ 0x00349a49, // n0x1754 c0x0000 (---------------) + I spydeberg
+ 0x40a023c2, // n0x1755 c0x0102 (n0x1879-n0x187a) + I st
+ 0x00309986, // n0x1756 c0x0000 (---------------) + I stange
+ 0x0029ca04, // n0x1757 c0x0000 (---------------) + I stat
+ 0x0029e909, // n0x1758 c0x0000 (---------------) + I stathelle
+ 0x002c92c9, // n0x1759 c0x0000 (---------------) + I stavanger
+ 0x002d97c7, // n0x175a c0x0000 (---------------) + I stavern
+ 0x0025c087, // n0x175b c0x0000 (---------------) + I steigen
+ 0x003374c9, // n0x175c c0x0000 (---------------) + I steinkjer
+ 0x00202b88, // n0x175d c0x0000 (---------------) + I stjordal
+ 0x00202b8f, // n0x175e c0x0000 (---------------) + I stjordalshalsen
+ 0x00228bc6, // n0x175f c0x0000 (---------------) + I stokke
+ 0x0023f24b, // n0x1760 c0x0000 (---------------) + I stor-elvdal
+ 0x002cf305, // n0x1761 c0x0000 (---------------) + I stord
+ 0x002cf307, // n0x1762 c0x0000 (---------------) + I stordal
+ 0x002cf749, // n0x1763 c0x0000 (---------------) + I storfjord
+ 0x0023dac6, // n0x1764 c0x0000 (---------------) + I strand
+ 0x0023dac7, // n0x1765 c0x0000 (---------------) + I stranda
+ 0x0037fd45, // n0x1766 c0x0000 (---------------) + I stryn
+ 0x00224fc4, // n0x1767 c0x0000 (---------------) + I sula
+ 0x00226b46, // n0x1768 c0x0000 (---------------) + I suldal
+ 0x00218c84, // n0x1769 c0x0000 (---------------) + I sund
+ 0x002a9c87, // n0x176a c0x0000 (---------------) + I sunndal
+ 0x002d1a88, // n0x176b c0x0000 (---------------) + I surnadal
+ 0x40ed36c8, // n0x176c c0x0103 (n0x187a-n0x187b) + I svalbard
+ 0x002d4485, // n0x176d c0x0000 (---------------) + I sveio
+ 0x002d45c7, // n0x176e c0x0000 (---------------) + I svelvik
+ 0x00354549, // n0x176f c0x0000 (---------------) + I sykkylven
+ 0x00204e04, // n0x1770 c0x0000 (---------------) + I tana
+ 0x00204e08, // n0x1771 c0x0000 (---------------) + I tananger
+ 0x4122ba88, // n0x1772 c0x0104 (n0x187b-n0x187d) o I telemark
+ 0x0036ef44, // n0x1773 c0x0000 (---------------) + I time
+ 0x00225c08, // n0x1774 c0x0000 (---------------) + I tingvoll
+ 0x0030cf04, // n0x1775 c0x0000 (---------------) + I tinn
+ 0x0021fb09, // n0x1776 c0x0000 (---------------) + I tjeldsund
+ 0x00259c45, // n0x1777 c0x0000 (---------------) + I tjome
+ 0x41608902, // n0x1778 c0x0105 (n0x187d-n0x187e) + I tm
+ 0x00228c05, // n0x1779 c0x0000 (---------------) + I tokke
+ 0x00215ac5, // n0x177a c0x0000 (---------------) + I tolga
+ 0x00204608, // n0x177b c0x0000 (---------------) + I tonsberg
+ 0x00226fc7, // n0x177c c0x0000 (---------------) + I torsken
+ 0x41a02402, // n0x177d c0x0106 (n0x187e-n0x187f) + I tr
+ 0x0025ab85, // n0x177e c0x0000 (---------------) + I trana
+ 0x00263106, // n0x177f c0x0000 (---------------) + I tranby
+ 0x00278cc6, // n0x1780 c0x0000 (---------------) + I tranoy
+ 0x002dfe08, // n0x1781 c0x0000 (---------------) + I troandin
+ 0x002e4348, // n0x1782 c0x0000 (---------------) + I trogstad
+ 0x00302a46, // n0x1783 c0x0000 (---------------) + I tromsa
+ 0x0030ac06, // n0x1784 c0x0000 (---------------) + I tromso
+ 0x00214709, // n0x1785 c0x0000 (---------------) + I trondheim
+ 0x0036b846, // n0x1786 c0x0000 (---------------) + I trysil
+ 0x00394d4b, // n0x1787 c0x0000 (---------------) + I tvedestrand
+ 0x00222c85, // n0x1788 c0x0000 (---------------) + I tydal
+ 0x0020fac6, // n0x1789 c0x0000 (---------------) + I tynset
+ 0x00224108, // n0x178a c0x0000 (---------------) + I tysfjord
+ 0x00234886, // n0x178b c0x0000 (---------------) + I tysnes
+ 0x002f8986, // n0x178c c0x0000 (---------------) + I tysvar
+ 0x00210dca, // n0x178d c0x0000 (---------------) + I ullensaker
+ 0x002bab8a, // n0x178e c0x0000 (---------------) + I ullensvang
+ 0x0025bd05, // n0x178f c0x0000 (---------------) + I ulvik
+ 0x00215307, // n0x1790 c0x0000 (---------------) + I unjarga
+ 0x002d0d06, // n0x1791 c0x0000 (---------------) + I utsira
+ 0x41e013c2, // n0x1792 c0x0107 (n0x187f-n0x1880) + I va
+ 0x002f2e87, // n0x1793 c0x0000 (---------------) + I vaapste
+ 0x00262a85, // n0x1794 c0x0000 (---------------) + I vadso
+ 0x00343f44, // n0x1795 c0x0000 (---------------) + I vaga
+ 0x00343f45, // n0x1796 c0x0000 (---------------) + I vagan
+ 0x00300c06, // n0x1797 c0x0000 (---------------) + I vagsoy
+ 0x0033f147, // n0x1798 c0x0000 (---------------) + I vaksdal
+ 0x00213d85, // n0x1799 c0x0000 (---------------) + I valle
+ 0x002bad04, // n0x179a c0x0000 (---------------) + I vang
+ 0x0025c3c8, // n0x179b c0x0000 (---------------) + I vanylven
+ 0x002f8a45, // n0x179c c0x0000 (---------------) + I vardo
+ 0x00280307, // n0x179d c0x0000 (---------------) + I varggat
+ 0x002cf005, // n0x179e c0x0000 (---------------) + I varoy
+ 0x00310385, // n0x179f c0x0000 (---------------) + I vefsn
+ 0x00212004, // n0x17a0 c0x0000 (---------------) + I vega
+ 0x00299e89, // n0x17a1 c0x0000 (---------------) + I vegarshei
+ 0x00212448, // n0x17a2 c0x0000 (---------------) + I vennesla
+ 0x00212286, // n0x17a3 c0x0000 (---------------) + I verdal
+ 0x00386a06, // n0x17a4 c0x0000 (---------------) + I verran
+ 0x002b9c46, // n0x17a5 c0x0000 (---------------) + I vestby
+ 0x422d9c88, // n0x17a6 c0x0108 (n0x1880-n0x1881) o I vestfold
+ 0x002d9e87, // n0x17a7 c0x0000 (---------------) + I vestnes
+ 0x002da30d, // n0x17a8 c0x0000 (---------------) + I vestre-slidre
+ 0x002da90c, // n0x17a9 c0x0000 (---------------) + I vestre-toten
+ 0x002daf09, // n0x17aa c0x0000 (---------------) + I vestvagoy
+ 0x002db149, // n0x17ab c0x0000 (---------------) + I vevelstad
+ 0x4273b4c2, // n0x17ac c0x0109 (n0x1881-n0x1882) + I vf
+ 0x0038c903, // n0x17ad c0x0000 (---------------) + I vgs
+ 0x00244c83, // n0x17ae c0x0000 (---------------) + I vik
+ 0x00364745, // n0x17af c0x0000 (---------------) + I vikna
+ 0x0037620a, // n0x17b0 c0x0000 (---------------) + I vindafjord
+ 0x00302906, // n0x17b1 c0x0000 (---------------) + I voagat
+ 0x002df605, // n0x17b2 c0x0000 (---------------) + I volda
+ 0x002e21c4, // n0x17b3 c0x0000 (---------------) + I voss
+ 0x002e21cb, // n0x17b4 c0x0000 (---------------) + I vossevangen
+ 0x002f6e0c, // n0x17b5 c0x0000 (---------------) + I xn--andy-ira
+ 0x002f764c, // n0x17b6 c0x0000 (---------------) + I xn--asky-ira
+ 0x002f7955, // n0x17b7 c0x0000 (---------------) + I xn--aurskog-hland-jnb
+ 0x002f984d, // n0x17b8 c0x0000 (---------------) + I xn--avery-yua
+ 0x002fbb0f, // n0x17b9 c0x0000 (---------------) + I xn--bdddj-mrabd
+ 0x002fbed2, // n0x17ba c0x0000 (---------------) + I xn--bearalvhki-y4a
+ 0x002fc34f, // n0x17bb c0x0000 (---------------) + I xn--berlevg-jxa
+ 0x002fc712, // n0x17bc c0x0000 (---------------) + I xn--bhcavuotna-s4a
+ 0x002fcb93, // n0x17bd c0x0000 (---------------) + I xn--bhccavuotna-k7a
+ 0x002fd04d, // n0x17be c0x0000 (---------------) + I xn--bidr-5nac
+ 0x002fd60d, // n0x17bf c0x0000 (---------------) + I xn--bievt-0qa
+ 0x002fd94e, // n0x17c0 c0x0000 (---------------) + I xn--bjarky-fya
+ 0x002fde0e, // n0x17c1 c0x0000 (---------------) + I xn--bjddar-pta
+ 0x002fec8c, // n0x17c2 c0x0000 (---------------) + I xn--blt-elab
+ 0x002ff00c, // n0x17c3 c0x0000 (---------------) + I xn--bmlo-gra
+ 0x002ff44b, // n0x17c4 c0x0000 (---------------) + I xn--bod-2na
+ 0x002ff7ce, // n0x17c5 c0x0000 (---------------) + I xn--brnny-wuac
+ 0x00301b92, // n0x17c6 c0x0000 (---------------) + I xn--brnnysund-m8ac
+ 0x003026cc, // n0x17c7 c0x0000 (---------------) + I xn--brum-voa
+ 0x00302e90, // n0x17c8 c0x0000 (---------------) + I xn--btsfjord-9za
+ 0x003138d2, // n0x17c9 c0x0000 (---------------) + I xn--davvenjrga-y4a
+ 0x0031458c, // n0x17ca c0x0000 (---------------) + I xn--dnna-gra
+ 0x00314a4d, // n0x17cb c0x0000 (---------------) + I xn--drbak-wua
+ 0x00314d8c, // n0x17cc c0x0000 (---------------) + I xn--dyry-ira
+ 0x00317b91, // n0x17cd c0x0000 (---------------) + I xn--eveni-0qa01ga
+ 0x00319c0d, // n0x17ce c0x0000 (---------------) + I xn--finny-yua
+ 0x0031f74d, // n0x17cf c0x0000 (---------------) + I xn--fjord-lra
+ 0x0031fd4a, // n0x17d0 c0x0000 (---------------) + I xn--fl-zia
+ 0x0031ffcc, // n0x17d1 c0x0000 (---------------) + I xn--flor-jra
+ 0x003208cc, // n0x17d2 c0x0000 (---------------) + I xn--frde-gra
+ 0x0032110c, // n0x17d3 c0x0000 (---------------) + I xn--frna-woa
+ 0x00321b0c, // n0x17d4 c0x0000 (---------------) + I xn--frya-hra
+ 0x00324f13, // n0x17d5 c0x0000 (---------------) + I xn--ggaviika-8ya47h
+ 0x00326650, // n0x17d6 c0x0000 (---------------) + I xn--gildeskl-g0a
+ 0x00326a50, // n0x17d7 c0x0000 (---------------) + I xn--givuotna-8ya
+ 0x0032714d, // n0x17d8 c0x0000 (---------------) + I xn--gjvik-wua
+ 0x0032748c, // n0x17d9 c0x0000 (---------------) + I xn--gls-elac
+ 0x00328189, // n0x17da c0x0000 (---------------) + I xn--h-2fa
+ 0x0032900d, // n0x17db c0x0000 (---------------) + I xn--hbmer-xqa
+ 0x00329353, // n0x17dc c0x0000 (---------------) + I xn--hcesuolo-7ya35b
+ 0x0032c151, // n0x17dd c0x0000 (---------------) + I xn--hgebostad-g3a
+ 0x0032c593, // n0x17de c0x0000 (---------------) + I xn--hmmrfeasta-s4ac
+ 0x0032e38f, // n0x17df c0x0000 (---------------) + I xn--hnefoss-q1a
+ 0x0032e74c, // n0x17e0 c0x0000 (---------------) + I xn--hobl-ira
+ 0x0032ea4f, // n0x17e1 c0x0000 (---------------) + I xn--holtlen-hxa
+ 0x0032ee0d, // n0x17e2 c0x0000 (---------------) + I xn--hpmir-xqa
+ 0x0032f40f, // n0x17e3 c0x0000 (---------------) + I xn--hyanger-q1a
+ 0x0032f7d0, // n0x17e4 c0x0000 (---------------) + I xn--hylandet-54a
+ 0x0033024e, // n0x17e5 c0x0000 (---------------) + I xn--indery-fya
+ 0x00332a0e, // n0x17e6 c0x0000 (---------------) + I xn--jlster-bya
+ 0x00333150, // n0x17e7 c0x0000 (---------------) + I xn--jrpeland-54a
+ 0x00333e8d, // n0x17e8 c0x0000 (---------------) + I xn--karmy-yua
+ 0x0033480e, // n0x17e9 c0x0000 (---------------) + I xn--kfjord-iua
+ 0x00334b8c, // n0x17ea c0x0000 (---------------) + I xn--klbu-woa
+ 0x00336b53, // n0x17eb c0x0000 (---------------) + I xn--koluokta-7ya57h
+ 0x0033934e, // n0x17ec c0x0000 (---------------) + I xn--krager-gya
+ 0x00339b10, // n0x17ed c0x0000 (---------------) + I xn--kranghke-b0a
+ 0x00339f11, // n0x17ee c0x0000 (---------------) + I xn--krdsherad-m8a
+ 0x0033a34f, // n0x17ef c0x0000 (---------------) + I xn--krehamn-dxa
+ 0x0033a713, // n0x17f0 c0x0000 (---------------) + I xn--krjohka-hwab49j
+ 0x0033b04d, // n0x17f1 c0x0000 (---------------) + I xn--ksnes-uua
+ 0x0033b38f, // n0x17f2 c0x0000 (---------------) + I xn--kvfjord-nxa
+ 0x0033b74e, // n0x17f3 c0x0000 (---------------) + I xn--kvitsy-fya
+ 0x0033d050, // n0x17f4 c0x0000 (---------------) + I xn--kvnangen-k0a
+ 0x0033d449, // n0x17f5 c0x0000 (---------------) + I xn--l-1fa
+ 0x0033e4d0, // n0x17f6 c0x0000 (---------------) + I xn--laheadju-7ya
+ 0x0033f30f, // n0x17f7 c0x0000 (---------------) + I xn--langevg-jxa
+ 0x0033f98f, // n0x17f8 c0x0000 (---------------) + I xn--ldingen-q1a
+ 0x0033fd52, // n0x17f9 c0x0000 (---------------) + I xn--leagaviika-52b
+ 0x00344a4e, // n0x17fa c0x0000 (---------------) + I xn--lesund-hua
+ 0x0034534d, // n0x17fb c0x0000 (---------------) + I xn--lgrd-poac
+ 0x00345bcd, // n0x17fc c0x0000 (---------------) + I xn--lhppi-xqa
+ 0x00345f0d, // n0x17fd c0x0000 (---------------) + I xn--linds-pra
+ 0x00347a8d, // n0x17fe c0x0000 (---------------) + I xn--loabt-0qa
+ 0x00347dcd, // n0x17ff c0x0000 (---------------) + I xn--lrdal-sra
+ 0x00348110, // n0x1800 c0x0000 (---------------) + I xn--lrenskog-54a
+ 0x0034850b, // n0x1801 c0x0000 (---------------) + I xn--lt-liac
+ 0x00348a8c, // n0x1802 c0x0000 (---------------) + I xn--lten-gra
+ 0x00348e0c, // n0x1803 c0x0000 (---------------) + I xn--lury-ira
+ 0x0034910c, // n0x1804 c0x0000 (---------------) + I xn--mely-ira
+ 0x0034940e, // n0x1805 c0x0000 (---------------) + I xn--merker-kua
+ 0x00356890, // n0x1806 c0x0000 (---------------) + I xn--mjndalen-64a
+ 0x003581d2, // n0x1807 c0x0000 (---------------) + I xn--mlatvuopmi-s4a
+ 0x0035864b, // n0x1808 c0x0000 (---------------) + I xn--mli-tla
+ 0x00358a8e, // n0x1809 c0x0000 (---------------) + I xn--mlselv-iua
+ 0x00358e0e, // n0x180a c0x0000 (---------------) + I xn--moreke-jua
+ 0x0035960e, // n0x180b c0x0000 (---------------) + I xn--mosjen-eya
+ 0x0035a4cb, // n0x180c c0x0000 (---------------) + I xn--mot-tla
+ 0x42b5a856, // n0x180d c0x010a (n0x1882-n0x1884) o I xn--mre-og-romsdal-qqb
+ 0x0035b4cd, // n0x180e c0x0000 (---------------) + I xn--msy-ula0h
+ 0x0035b954, // n0x180f c0x0000 (---------------) + I xn--mtta-vrjjat-k7af
+ 0x0035c38d, // n0x1810 c0x0000 (---------------) + I xn--muost-0qa
+ 0x0035d715, // n0x1811 c0x0000 (---------------) + I xn--nmesjevuemie-tcba
+ 0x0036060d, // n0x1812 c0x0000 (---------------) + I xn--nry-yla5g
+ 0x00360f8f, // n0x1813 c0x0000 (---------------) + I xn--nttery-byae
+ 0x00361bcf, // n0x1814 c0x0000 (---------------) + I xn--nvuotna-hwa
+ 0x003652cf, // n0x1815 c0x0000 (---------------) + I xn--oppegrd-ixa
+ 0x0036568e, // n0x1816 c0x0000 (---------------) + I xn--ostery-fya
+ 0x00365d4d, // n0x1817 c0x0000 (---------------) + I xn--osyro-wua
+ 0x00368211, // n0x1818 c0x0000 (---------------) + I xn--porsgu-sta26f
+ 0x0036f34c, // n0x1819 c0x0000 (---------------) + I xn--rady-ira
+ 0x0036f64c, // n0x181a c0x0000 (---------------) + I xn--rdal-poa
+ 0x0036f94b, // n0x181b c0x0000 (---------------) + I xn--rde-ula
+ 0x0036fc0c, // n0x181c c0x0000 (---------------) + I xn--rdy-0nab
+ 0x0036ffcf, // n0x181d c0x0000 (---------------) + I xn--rennesy-v1a
+ 0x00370392, // n0x181e c0x0000 (---------------) + I xn--rhkkervju-01af
+ 0x00371acd, // n0x181f c0x0000 (---------------) + I xn--rholt-mra
+ 0x00372d0c, // n0x1820 c0x0000 (---------------) + I xn--risa-5na
+ 0x0037318c, // n0x1821 c0x0000 (---------------) + I xn--risr-ira
+ 0x0037348d, // n0x1822 c0x0000 (---------------) + I xn--rland-uua
+ 0x003737cf, // n0x1823 c0x0000 (---------------) + I xn--rlingen-mxa
+ 0x00373b8e, // n0x1824 c0x0000 (---------------) + I xn--rmskog-bya
+ 0x00375ecc, // n0x1825 c0x0000 (---------------) + I xn--rros-gra
+ 0x0037648d, // n0x1826 c0x0000 (---------------) + I xn--rskog-uua
+ 0x003767cb, // n0x1827 c0x0000 (---------------) + I xn--rst-0na
+ 0x00376fcc, // n0x1828 c0x0000 (---------------) + I xn--rsta-fra
+ 0x0037754d, // n0x1829 c0x0000 (---------------) + I xn--ryken-vua
+ 0x0037788e, // n0x182a c0x0000 (---------------) + I xn--ryrvik-bya
+ 0x00377d09, // n0x182b c0x0000 (---------------) + I xn--s-1fa
+ 0x00378b53, // n0x182c c0x0000 (---------------) + I xn--sandnessjen-ogb
+ 0x0037940d, // n0x182d c0x0000 (---------------) + I xn--sandy-yua
+ 0x0037974d, // n0x182e c0x0000 (---------------) + I xn--seral-lra
+ 0x00379d4c, // n0x182f c0x0000 (---------------) + I xn--sgne-gra
+ 0x0037a1ce, // n0x1830 c0x0000 (---------------) + I xn--skierv-uta
+ 0x0037b34f, // n0x1831 c0x0000 (---------------) + I xn--skjervy-v1a
+ 0x0037b70c, // n0x1832 c0x0000 (---------------) + I xn--skjk-soa
+ 0x0037ba0d, // n0x1833 c0x0000 (---------------) + I xn--sknit-yqa
+ 0x0037bd4f, // n0x1834 c0x0000 (---------------) + I xn--sknland-fxa
+ 0x0037c10c, // n0x1835 c0x0000 (---------------) + I xn--slat-5na
+ 0x0037c74c, // n0x1836 c0x0000 (---------------) + I xn--slt-elab
+ 0x0037cb0c, // n0x1837 c0x0000 (---------------) + I xn--smla-hra
+ 0x0037ce0c, // n0x1838 c0x0000 (---------------) + I xn--smna-gra
+ 0x0037d4cd, // n0x1839 c0x0000 (---------------) + I xn--snase-nra
+ 0x0037d812, // n0x183a c0x0000 (---------------) + I xn--sndre-land-0cb
+ 0x0037de8c, // n0x183b c0x0000 (---------------) + I xn--snes-poa
+ 0x0037e18c, // n0x183c c0x0000 (---------------) + I xn--snsa-roa
+ 0x0037e491, // n0x183d c0x0000 (---------------) + I xn--sr-aurdal-l8a
+ 0x0037e8cf, // n0x183e c0x0000 (---------------) + I xn--sr-fron-q1a
+ 0x0037ec8f, // n0x183f c0x0000 (---------------) + I xn--sr-odal-q1a
+ 0x0037f053, // n0x1840 c0x0000 (---------------) + I xn--sr-varanger-ggb
+ 0x003801ce, // n0x1841 c0x0000 (---------------) + I xn--srfold-bya
+ 0x0038074f, // n0x1842 c0x0000 (---------------) + I xn--srreisa-q1a
+ 0x00380b0c, // n0x1843 c0x0000 (---------------) + I xn--srum-gra
+ 0x42f80e4e, // n0x1844 c0x010b (n0x1884-n0x1885) o I xn--stfold-9xa
+ 0x003811cf, // n0x1845 c0x0000 (---------------) + I xn--stjrdal-s1a
+ 0x00381596, // n0x1846 c0x0000 (---------------) + I xn--stjrdalshalsen-sqb
+ 0x00382652, // n0x1847 c0x0000 (---------------) + I xn--stre-toten-zcb
+ 0x0038448c, // n0x1848 c0x0000 (---------------) + I xn--tjme-hra
+ 0x003850cf, // n0x1849 c0x0000 (---------------) + I xn--tnsberg-q1a
+ 0x0038574d, // n0x184a c0x0000 (---------------) + I xn--trany-yua
+ 0x00385a8f, // n0x184b c0x0000 (---------------) + I xn--trgstad-r1a
+ 0x00385e4c, // n0x184c c0x0000 (---------------) + I xn--trna-woa
+ 0x0038614d, // n0x184d c0x0000 (---------------) + I xn--troms-zua
+ 0x0038648d, // n0x184e c0x0000 (---------------) + I xn--tysvr-vra
+ 0x003876ce, // n0x184f c0x0000 (---------------) + I xn--unjrga-rta
+ 0x0038850c, // n0x1850 c0x0000 (---------------) + I xn--vads-jra
+ 0x0038880c, // n0x1851 c0x0000 (---------------) + I xn--vard-jra
+ 0x00388b10, // n0x1852 c0x0000 (---------------) + I xn--vegrshei-c0a
+ 0x0038b251, // n0x1853 c0x0000 (---------------) + I xn--vestvgy-ixa6o
+ 0x0038b68b, // n0x1854 c0x0000 (---------------) + I xn--vg-yiab
+ 0x0038c50c, // n0x1855 c0x0000 (---------------) + I xn--vgan-qoa
+ 0x0038c80e, // n0x1856 c0x0000 (---------------) + I xn--vgsy-qoa0j
+ 0x0038e511, // n0x1857 c0x0000 (---------------) + I xn--vre-eiker-k8a
+ 0x0038e94e, // n0x1858 c0x0000 (---------------) + I xn--vrggt-xqad
+ 0x0038eccd, // n0x1859 c0x0000 (---------------) + I xn--vry-yla5g
+ 0x00392fcb, // n0x185a c0x0000 (---------------) + I xn--yer-zna
+ 0x00393c0f, // n0x185b c0x0000 (---------------) + I xn--ygarden-p1a
+ 0x00394594, // n0x185c c0x0000 (---------------) + I xn--ystre-slidre-ujb
+ 0x0026cd02, // n0x185d c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x185e c0x0000 (---------------) + I gs
+ 0x00201083, // n0x185f c0x0000 (---------------) + I nes
+ 0x0026cd02, // n0x1860 c0x0000 (---------------) + I gs
+ 0x00201083, // n0x1861 c0x0000 (---------------) + I nes
+ 0x0026cd02, // n0x1862 c0x0000 (---------------) + I gs
+ 0x00202382, // n0x1863 c0x0000 (---------------) + I os
+ 0x00351305, // n0x1864 c0x0000 (---------------) + I valer
+ 0x0038e20c, // n0x1865 c0x0000 (---------------) + I xn--vler-qoa
+ 0x0026cd02, // n0x1866 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1867 c0x0000 (---------------) + I gs
+ 0x00202382, // n0x1868 c0x0000 (---------------) + I os
+ 0x0026cd02, // n0x1869 c0x0000 (---------------) + I gs
+ 0x00280105, // n0x186a c0x0000 (---------------) + I heroy
+ 0x0029dd05, // n0x186b c0x0000 (---------------) + I sande
+ 0x0026cd02, // n0x186c c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x186d c0x0000 (---------------) + I gs
+ 0x0020a702, // n0x186e c0x0000 (---------------) + I bo
+ 0x00280105, // n0x186f c0x0000 (---------------) + I heroy
+ 0x002fa209, // n0x1870 c0x0000 (---------------) + I xn--b-5ga
+ 0x0032be4c, // n0x1871 c0x0000 (---------------) + I xn--hery-ira
+ 0x0026cd02, // n0x1872 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1873 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1874 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1875 c0x0000 (---------------) + I gs
+ 0x00351305, // n0x1876 c0x0000 (---------------) + I valer
+ 0x0026cd02, // n0x1877 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1878 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x1879 c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x187a c0x0000 (---------------) + I gs
+ 0x0020a702, // n0x187b c0x0000 (---------------) + I bo
+ 0x002fa209, // n0x187c c0x0000 (---------------) + I xn--b-5ga
+ 0x0026cd02, // n0x187d c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x187e c0x0000 (---------------) + I gs
+ 0x0026cd02, // n0x187f c0x0000 (---------------) + I gs
+ 0x0029dd05, // n0x1880 c0x0000 (---------------) + I sande
+ 0x0026cd02, // n0x1881 c0x0000 (---------------) + I gs
+ 0x0029dd05, // n0x1882 c0x0000 (---------------) + I sande
+ 0x0032be4c, // n0x1883 c0x0000 (---------------) + I xn--hery-ira
+ 0x0038e20c, // n0x1884 c0x0000 (---------------) + I xn--vler-qoa
+ 0x00310603, // n0x1885 c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x1886 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1887 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1888 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1889 c0x0000 (---------------) + I info
+ 0x002170c3, // n0x188a c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x188b c0x0000 (---------------) + I org
+ 0x00166bc8, // n0x188c c0x0000 (---------------) + merseine
+ 0x0007c1c4, // n0x188d c0x0000 (---------------) + mine
+ 0x000cb2c8, // n0x188e c0x0000 (---------------) + shacknet
+ 0x00201e82, // n0x188f c0x0000 (---------------) + I ac
+ 0x43e00742, // n0x1890 c0x010f (n0x189f-n0x18a0) + I co
+ 0x002319c3, // n0x1891 c0x0000 (---------------) + I cri
+ 0x00252f84, // n0x1892 c0x0000 (---------------) + I geek
+ 0x002012c3, // n0x1893 c0x0000 (---------------) + I gen
+ 0x0021e284, // n0x1894 c0x0000 (---------------) + I govt
+ 0x00205e06, // n0x1895 c0x0000 (---------------) + I health
+ 0x002d2e03, // n0x1896 c0x0000 (---------------) + I iwi
+ 0x002d2dc4, // n0x1897 c0x0000 (---------------) + I kiwi
+ 0x002701c5, // n0x1898 c0x0000 (---------------) + I maori
+ 0x0023fa03, // n0x1899 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x189a c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x189b c0x0000 (---------------) + I org
+ 0x002647ca, // n0x189c c0x0000 (---------------) + I parliament
+ 0x00235406, // n0x189d c0x0000 (---------------) + I school
+ 0x0035918c, // n0x189e c0x0000 (---------------) + I xn--mori-qsa
+ 0x000e4188, // n0x189f c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x18a0 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x18a1 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x18a2 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x18a3 c0x0000 (---------------) + I gov
+ 0x0020b403, // n0x18a4 c0x0000 (---------------) + I med
+ 0x002bd646, // n0x18a5 c0x0000 (---------------) + I museum
+ 0x002170c3, // n0x18a6 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x18a7 c0x0000 (---------------) + I org
+ 0x00218243, // n0x18a8 c0x0000 (---------------) + I pro
+ 0x00004342, // n0x18a9 c0x0000 (---------------) + ae
+ 0x000d0087, // n0x18aa c0x0000 (---------------) + blogdns
+ 0x001619c8, // n0x18ab c0x0000 (---------------) + blogsite
+ 0x00076592, // n0x18ac c0x0000 (---------------) + boldlygoingnowhere
+ 0x44a2a085, // n0x18ad c0x0112 (n0x18e3-n0x18e5) o I cdn77
+ 0x44f0720c, // n0x18ae c0x0113 (n0x18e5-n0x18e6) o I cdn77-secure
+ 0x00146fc8, // n0x18af c0x0000 (---------------) + dnsalias
+ 0x0006a247, // n0x18b0 c0x0000 (---------------) + dnsdojo
+ 0x00010a4b, // n0x18b1 c0x0000 (---------------) + doesntexist
+ 0x0015fdc9, // n0x18b2 c0x0000 (---------------) + dontexist
+ 0x00146ec7, // n0x18b3 c0x0000 (---------------) + doomdns
+ 0x0006a147, // n0x18b4 c0x0000 (---------------) + duckdns
+ 0x00194fc6, // n0x18b5 c0x0000 (---------------) + dvrdns
+ 0x000007c8, // n0x18b6 c0x0000 (---------------) + dynalias
+ 0x45409ac6, // n0x18b7 c0x0115 (n0x18e7-n0x18e9) + dyndns
+ 0x0009428d, // n0x18b8 c0x0000 (---------------) + endofinternet
+ 0x000f3e50, // n0x18b9 c0x0000 (---------------) + endoftheinternet
+ 0x4581d5c2, // n0x18ba c0x0116 (n0x18e9-n0x1920) + eu
+ 0x00057807, // n0x18bb c0x0000 (---------------) + from-me
+ 0x00083589, // n0x18bc c0x0000 (---------------) + game-host
+ 0x00043a86, // n0x18bd c0x0000 (---------------) + gotdns
+ 0x0002ea02, // n0x18be c0x0000 (---------------) + hk
+ 0x0013eeca, // n0x18bf c0x0000 (---------------) + hobby-site
+ 0x0000b387, // n0x18c0 c0x0000 (---------------) + homedns
+ 0x00085007, // n0x18c1 c0x0000 (---------------) + homeftp
+ 0x00090ec9, // n0x18c2 c0x0000 (---------------) + homelinux
+ 0x00091b88, // n0x18c3 c0x0000 (---------------) + homeunix
+ 0x000c528e, // n0x18c4 c0x0000 (---------------) + is-a-bruinsfan
+ 0x00008d8e, // n0x18c5 c0x0000 (---------------) + is-a-candidate
+ 0x0000eecf, // n0x18c6 c0x0000 (---------------) + is-a-celticsfan
+ 0x00166909, // n0x18c7 c0x0000 (---------------) + is-a-chef
+ 0x00052e49, // n0x18c8 c0x0000 (---------------) + is-a-geek
+ 0x0006b8cb, // n0x18c9 c0x0000 (---------------) + is-a-knight
+ 0x001259cf, // n0x18ca c0x0000 (---------------) + is-a-linux-user
+ 0x0008480c, // n0x18cb c0x0000 (---------------) + is-a-patsfan
+ 0x000d23cb, // n0x18cc c0x0000 (---------------) + is-a-soxfan
+ 0x00103ac8, // n0x18cd c0x0000 (---------------) + is-found
+ 0x000d6887, // n0x18ce c0x0000 (---------------) + is-lost
+ 0x000e2908, // n0x18cf c0x0000 (---------------) + is-saved
+ 0x0012148b, // n0x18d0 c0x0000 (---------------) + is-very-bad
+ 0x00127b0c, // n0x18d1 c0x0000 (---------------) + is-very-evil
+ 0x00130a4c, // n0x18d2 c0x0000 (---------------) + is-very-good
+ 0x0013904c, // n0x18d3 c0x0000 (---------------) + is-very-nice
+ 0x0013bb8d, // n0x18d4 c0x0000 (---------------) + is-very-sweet
+ 0x00191648, // n0x18d5 c0x0000 (---------------) + isa-geek
+ 0x00183609, // n0x18d6 c0x0000 (---------------) + kicks-ass
+ 0x0016a6cb, // n0x18d7 c0x0000 (---------------) + misconfused
+ 0x000c7e07, // n0x18d8 c0x0000 (---------------) + podzone
+ 0x0016184a, // n0x18d9 c0x0000 (---------------) + readmyblog
+ 0x00130f46, // n0x18da c0x0000 (---------------) + selfip
+ 0x00084dcd, // n0x18db c0x0000 (---------------) + sellsyourhome
+ 0x00079088, // n0x18dc c0x0000 (---------------) + servebbs
+ 0x00161348, // n0x18dd c0x0000 (---------------) + serveftp
+ 0x00011f49, // n0x18de c0x0000 (---------------) + servegame
+ 0x000d044c, // n0x18df c0x0000 (---------------) + stuff-4-sale
+ 0x00009f42, // n0x18e0 c0x0000 (---------------) + us
+ 0x00110e86, // n0x18e1 c0x0000 (---------------) + webhop
+ 0x000043c2, // n0x18e2 c0x0000 (---------------) + za
+ 0x00000741, // n0x18e3 c0x0000 (---------------) + c
+ 0x000060c3, // n0x18e4 c0x0000 (---------------) + rsc
+ 0x45374606, // n0x18e5 c0x0114 (n0x18e6-n0x18e7) o I origin
+ 0x0002a203, // n0x18e6 c0x0000 (---------------) + ssl
+ 0x00002342, // n0x18e7 c0x0000 (---------------) + go
+ 0x0000b384, // n0x18e8 c0x0000 (---------------) + home
+ 0x00000882, // n0x18e9 c0x0000 (---------------) + al
+ 0x000729c4, // n0x18ea c0x0000 (---------------) + asso
+ 0x00001642, // n0x18eb c0x0000 (---------------) + at
+ 0x00005ac2, // n0x18ec c0x0000 (---------------) + au
+ 0x00004702, // n0x18ed c0x0000 (---------------) + be
+ 0x00155e02, // n0x18ee c0x0000 (---------------) + bg
+ 0x000055c2, // n0x18ef c0x0000 (---------------) + ca
+ 0x0002a082, // n0x18f0 c0x0000 (---------------) + cd
+ 0x00004a02, // n0x18f1 c0x0000 (---------------) + ch
+ 0x000211c2, // n0x18f2 c0x0000 (---------------) + cn
+ 0x00029e42, // n0x18f3 c0x0000 (---------------) + cy
+ 0x00014442, // n0x18f4 c0x0000 (---------------) + cz
+ 0x000006c2, // n0x18f5 c0x0000 (---------------) + de
+ 0x00071742, // n0x18f6 c0x0000 (---------------) + dk
+ 0x000d75c3, // n0x18f7 c0x0000 (---------------) + edu
+ 0x00006042, // n0x18f8 c0x0000 (---------------) + ee
+ 0x000010c2, // n0x18f9 c0x0000 (---------------) + es
+ 0x000099c2, // n0x18fa c0x0000 (---------------) + fi
+ 0x00000d42, // n0x18fb c0x0000 (---------------) + fr
+ 0x0000dc82, // n0x18fc c0x0000 (---------------) + gr
+ 0x00025842, // n0x18fd c0x0000 (---------------) + hr
+ 0x00017d42, // n0x18fe c0x0000 (---------------) + hu
+ 0x00000e82, // n0x18ff c0x0000 (---------------) + ie
+ 0x000036c2, // n0x1900 c0x0000 (---------------) + il
+ 0x00000242, // n0x1901 c0x0000 (---------------) + in
+ 0x00038c03, // n0x1902 c0x0000 (---------------) + int
+ 0x00002b42, // n0x1903 c0x0000 (---------------) + is
+ 0x00006e82, // n0x1904 c0x0000 (---------------) + it
+ 0x000990c2, // n0x1905 c0x0000 (---------------) + jp
+ 0x000034c2, // n0x1906 c0x0000 (---------------) + kr
+ 0x00005ec2, // n0x1907 c0x0000 (---------------) + lt
+ 0x000071c2, // n0x1908 c0x0000 (---------------) + lu
+ 0x00027f02, // n0x1909 c0x0000 (---------------) + lv
+ 0x0003a6c2, // n0x190a c0x0000 (---------------) + mc
+ 0x00008942, // n0x190b c0x0000 (---------------) + me
+ 0x00156d82, // n0x190c c0x0000 (---------------) + mk
+ 0x00059642, // n0x190d c0x0000 (---------------) + mt
+ 0x00020282, // n0x190e c0x0000 (---------------) + my
+ 0x000170c3, // n0x190f c0x0000 (---------------) + net
+ 0x00001282, // n0x1910 c0x0000 (---------------) + ng
+ 0x00036482, // n0x1911 c0x0000 (---------------) + nl
+ 0x00000c02, // n0x1912 c0x0000 (---------------) + no
+ 0x000078c2, // n0x1913 c0x0000 (---------------) + nz
+ 0x0005b445, // n0x1914 c0x0000 (---------------) + paris
+ 0x00001e02, // n0x1915 c0x0000 (---------------) + pl
+ 0x00095982, // n0x1916 c0x0000 (---------------) + pt
+ 0x00123e03, // n0x1917 c0x0000 (---------------) + q-a
+ 0x00000d82, // n0x1918 c0x0000 (---------------) + ro
+ 0x000044c2, // n0x1919 c0x0000 (---------------) + ru
+ 0x00002e82, // n0x191a c0x0000 (---------------) + se
+ 0x00009182, // n0x191b c0x0000 (---------------) + si
+ 0x00007b42, // n0x191c c0x0000 (---------------) + sk
+ 0x00002402, // n0x191d c0x0000 (---------------) + tr
+ 0x0000cf02, // n0x191e c0x0000 (---------------) + uk
+ 0x00009f42, // n0x191f c0x0000 (---------------) + us
+ 0x0020c283, // n0x1920 c0x0000 (---------------) + I abo
+ 0x00201e82, // n0x1921 c0x0000 (---------------) + I ac
+ 0x00222ac3, // n0x1922 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1923 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x1924 c0x0000 (---------------) + I gob
+ 0x0020dc03, // n0x1925 c0x0000 (---------------) + I ing
+ 0x0020b403, // n0x1926 c0x0000 (---------------) + I med
+ 0x002170c3, // n0x1927 c0x0000 (---------------) + I net
+ 0x00207cc3, // n0x1928 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x1929 c0x0000 (---------------) + I org
+ 0x00280043, // n0x192a c0x0000 (---------------) + I sld
+ 0x000e4188, // n0x192b c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x192c c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x192d c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x192e c0x0000 (---------------) + I gob
+ 0x0023fa03, // n0x192f c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1930 c0x0000 (---------------) + I net
+ 0x00207cc3, // n0x1931 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x1932 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1933 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1934 c0x0000 (---------------) + I edu
+ 0x0021dcc3, // n0x1935 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1936 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1937 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1938 c0x0000 (---------------) + I gov
+ 0x00200041, // n0x1939 c0x0000 (---------------) + I i
+ 0x0023fa03, // n0x193a c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x193b c0x0000 (---------------) + I net
+ 0x00202303, // n0x193c c0x0000 (---------------) + I ngo
+ 0x0021dcc3, // n0x193d c0x0000 (---------------) + I org
+ 0x00310603, // n0x193e c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x193f c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1940 c0x0000 (---------------) + I edu
+ 0x002a1b43, // n0x1941 c0x0000 (---------------) + I fam
+ 0x0034eb03, // n0x1942 c0x0000 (---------------) + I gob
+ 0x00375943, // n0x1943 c0x0000 (---------------) + I gok
+ 0x0026f583, // n0x1944 c0x0000 (---------------) + I gon
+ 0x0028e783, // n0x1945 c0x0000 (---------------) + I gop
+ 0x00202343, // n0x1946 c0x0000 (---------------) + I gos
+ 0x0021e283, // n0x1947 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1948 c0x0000 (---------------) + I info
+ 0x002170c3, // n0x1949 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x194a c0x0000 (---------------) + I org
+ 0x00219fc3, // n0x194b c0x0000 (---------------) + I web
+ 0x002f8f84, // n0x194c c0x0000 (---------------) + I agro
+ 0x002375c3, // n0x194d c0x0000 (---------------) + I aid
+ 0x00000603, // n0x194e c0x0000 (---------------) + art
+ 0x0026a983, // n0x194f c0x0000 (---------------) + I atm
+ 0x0023e708, // n0x1950 c0x0000 (---------------) + I augustow
+ 0x002eaac4, // n0x1951 c0x0000 (---------------) + I auto
+ 0x0032d4ca, // n0x1952 c0x0000 (---------------) + I babia-gora
+ 0x0034d4c6, // n0x1953 c0x0000 (---------------) + I bedzin
+ 0x0035ad87, // n0x1954 c0x0000 (---------------) + I beskidy
+ 0x0021604a, // n0x1955 c0x0000 (---------------) + I bialowieza
+ 0x00228a89, // n0x1956 c0x0000 (---------------) + I bialystok
+ 0x0037f4c7, // n0x1957 c0x0000 (---------------) + I bielawa
+ 0x00382a8a, // n0x1958 c0x0000 (---------------) + I bieszczady
+ 0x00310603, // n0x1959 c0x0000 (---------------) + I biz
+ 0x0025374b, // n0x195a c0x0000 (---------------) + I boleslawiec
+ 0x00343289, // n0x195b c0x0000 (---------------) + I bydgoszcz
+ 0x002b9d45, // n0x195c c0x0000 (---------------) + I bytom
+ 0x002bb407, // n0x195d c0x0000 (---------------) + I cieszyn
+ 0x00000742, // n0x195e c0x0000 (---------------) + co
+ 0x00222ac3, // n0x195f c0x0000 (---------------) + I com
+ 0x002c8a87, // n0x1960 c0x0000 (---------------) + I czeladz
+ 0x00214445, // n0x1961 c0x0000 (---------------) + I czest
+ 0x002aae49, // n0x1962 c0x0000 (---------------) + I dlugoleka
+ 0x002d75c3, // n0x1963 c0x0000 (---------------) + I edu
+ 0x0021b206, // n0x1964 c0x0000 (---------------) + I elblag
+ 0x002aa003, // n0x1965 c0x0000 (---------------) + I elk
+ 0x000b7a43, // n0x1966 c0x0000 (---------------) + gda
+ 0x000e1246, // n0x1967 c0x0000 (---------------) + gdansk
+ 0x0016e4c6, // n0x1968 c0x0000 (---------------) + gdynia
+ 0x0014ee47, // n0x1969 c0x0000 (---------------) + gliwice
+ 0x00391046, // n0x196a c0x0000 (---------------) + I glogow
+ 0x002047c5, // n0x196b c0x0000 (---------------) + I gmina
+ 0x0031b347, // n0x196c c0x0000 (---------------) + I gniezno
+ 0x002b1487, // n0x196d c0x0000 (---------------) + I gorlice
+ 0x4761e283, // n0x196e c0x011d (n0x19f1-n0x1a20) + I gov
+ 0x003147c7, // n0x196f c0x0000 (---------------) + I grajewo
+ 0x0034b2c3, // n0x1970 c0x0000 (---------------) + I gsm
+ 0x0036cb05, // n0x1971 c0x0000 (---------------) + I ilawa
+ 0x00200304, // n0x1972 c0x0000 (---------------) + I info
+ 0x0020a8c8, // n0x1973 c0x0000 (---------------) + I jaworzno
+ 0x0020c80c, // n0x1974 c0x0000 (---------------) + I jelenia-gora
+ 0x00297b45, // n0x1975 c0x0000 (---------------) + I jgora
+ 0x002bed46, // n0x1976 c0x0000 (---------------) + I kalisz
+ 0x002c8947, // n0x1977 c0x0000 (---------------) + I karpacz
+ 0x002017c7, // n0x1978 c0x0000 (---------------) + I kartuzy
+ 0x00216a07, // n0x1979 c0x0000 (---------------) + I kaszuby
+ 0x00217888, // n0x197a c0x0000 (---------------) + I katowice
+ 0x0036730f, // n0x197b c0x0000 (---------------) + I kazimierz-dolny
+ 0x0023df05, // n0x197c c0x0000 (---------------) + I kepno
+ 0x00231ac7, // n0x197d c0x0000 (---------------) + I ketrzyn
+ 0x002a3547, // n0x197e c0x0000 (---------------) + I klodzko
+ 0x0029058a, // n0x197f c0x0000 (---------------) + I kobierzyce
+ 0x00295709, // n0x1980 c0x0000 (---------------) + I kolobrzeg
+ 0x002bbf45, // n0x1981 c0x0000 (---------------) + I konin
+ 0x002bdf8a, // n0x1982 c0x0000 (---------------) + I konskowola
+ 0x0011bec6, // n0x1983 c0x0000 (---------------) + krakow
+ 0x002aa085, // n0x1984 c0x0000 (---------------) + I kutno
+ 0x002be184, // n0x1985 c0x0000 (---------------) + I lapy
+ 0x002c8806, // n0x1986 c0x0000 (---------------) + I lebork
+ 0x00322207, // n0x1987 c0x0000 (---------------) + I legnica
+ 0x002bde07, // n0x1988 c0x0000 (---------------) + I lezajsk
+ 0x0036d448, // n0x1989 c0x0000 (---------------) + I limanowa
+ 0x002c6b85, // n0x198a c0x0000 (---------------) + I lomza
+ 0x00214346, // n0x198b c0x0000 (---------------) + I lowicz
+ 0x00218fc5, // n0x198c c0x0000 (---------------) + I lubin
+ 0x0029b345, // n0x198d c0x0000 (---------------) + I lukow
+ 0x00218f04, // n0x198e c0x0000 (---------------) + I mail
+ 0x00321887, // n0x198f c0x0000 (---------------) + I malbork
+ 0x0030ff4a, // n0x1990 c0x0000 (---------------) + I malopolska
+ 0x0020b988, // n0x1991 c0x0000 (---------------) + I mazowsze
+ 0x002d2a06, // n0x1992 c0x0000 (---------------) + I mazury
+ 0x0000b403, // n0x1993 c0x0000 (---------------) + med
+ 0x002dc385, // n0x1994 c0x0000 (---------------) + I media
+ 0x00232386, // n0x1995 c0x0000 (---------------) + I miasta
+ 0x00381e06, // n0x1996 c0x0000 (---------------) + I mielec
+ 0x002f0806, // n0x1997 c0x0000 (---------------) + I mielno
+ 0x0023fa03, // n0x1998 c0x0000 (---------------) + I mil
+ 0x00371d47, // n0x1999 c0x0000 (---------------) + I mragowo
+ 0x002a34c5, // n0x199a c0x0000 (---------------) + I naklo
+ 0x002170c3, // n0x199b c0x0000 (---------------) + I net
+ 0x0037f74d, // n0x199c c0x0000 (---------------) + I nieruchomosci
+ 0x00207cc3, // n0x199d c0x0000 (---------------) + I nom
+ 0x0036d548, // n0x199e c0x0000 (---------------) + I nowaruda
+ 0x00210944, // n0x199f c0x0000 (---------------) + I nysa
+ 0x00264105, // n0x19a0 c0x0000 (---------------) + I olawa
+ 0x00290486, // n0x19a1 c0x0000 (---------------) + I olecko
+ 0x002c1f06, // n0x19a2 c0x0000 (---------------) + I olkusz
+ 0x0020f9c7, // n0x19a3 c0x0000 (---------------) + I olsztyn
+ 0x00228dc7, // n0x19a4 c0x0000 (---------------) + I opoczno
+ 0x00246a05, // n0x19a5 c0x0000 (---------------) + I opole
+ 0x0021dcc3, // n0x19a6 c0x0000 (---------------) + I org
+ 0x00202387, // n0x19a7 c0x0000 (---------------) + I ostroda
+ 0x00203b09, // n0x19a8 c0x0000 (---------------) + I ostroleka
+ 0x002053c9, // n0x19a9 c0x0000 (---------------) + I ostrowiec
+ 0x0020818a, // n0x19aa c0x0000 (---------------) + I ostrowwlkp
+ 0x00203e02, // n0x19ab c0x0000 (---------------) + I pc
+ 0x0036cac4, // n0x19ac c0x0000 (---------------) + I pila
+ 0x002c2d84, // n0x19ad c0x0000 (---------------) + I pisz
+ 0x00210607, // n0x19ae c0x0000 (---------------) + I podhale
+ 0x0023b508, // n0x19af c0x0000 (---------------) + I podlasie
+ 0x002c8cc9, // n0x19b0 c0x0000 (---------------) + I polkowice
+ 0x00207a09, // n0x19b1 c0x0000 (---------------) + I pomorskie
+ 0x002c9507, // n0x19b2 c0x0000 (---------------) + I pomorze
+ 0x0026a886, // n0x19b3 c0x0000 (---------------) + I powiat
+ 0x000ca606, // n0x19b4 c0x0000 (---------------) + poznan
+ 0x002cba44, // n0x19b5 c0x0000 (---------------) + I priv
+ 0x002cbbca, // n0x19b6 c0x0000 (---------------) + I prochowice
+ 0x002cd2c8, // n0x19b7 c0x0000 (---------------) + I pruszkow
+ 0x002cdec9, // n0x19b8 c0x0000 (---------------) + I przeworsk
+ 0x00281bc6, // n0x19b9 c0x0000 (---------------) + I pulawy
+ 0x002e8285, // n0x19ba c0x0000 (---------------) + I radom
+ 0x0020b848, // n0x19bb c0x0000 (---------------) + I rawa-maz
+ 0x002b174a, // n0x19bc c0x0000 (---------------) + I realestate
+ 0x00241283, // n0x19bd c0x0000 (---------------) + I rel
+ 0x00226746, // n0x19be c0x0000 (---------------) + I rybnik
+ 0x002c9607, // n0x19bf c0x0000 (---------------) + I rzeszow
+ 0x0020b505, // n0x19c0 c0x0000 (---------------) + I sanok
+ 0x00375a45, // n0x19c1 c0x0000 (---------------) + I sejny
+ 0x0029acc3, // n0x19c2 c0x0000 (---------------) + I sex
+ 0x0029b184, // n0x19c3 c0x0000 (---------------) + I shop
+ 0x0021cd45, // n0x19c4 c0x0000 (---------------) + I sklep
+ 0x00270687, // n0x19c5 c0x0000 (---------------) + I skoczow
+ 0x00212585, // n0x19c6 c0x0000 (---------------) + I slask
+ 0x002c1646, // n0x19c7 c0x0000 (---------------) + I slupsk
+ 0x000ddd85, // n0x19c8 c0x0000 (---------------) + sopot
+ 0x0021c283, // n0x19c9 c0x0000 (---------------) + I sos
+ 0x0021c289, // n0x19ca c0x0000 (---------------) + I sosnowiec
+ 0x00263ecc, // n0x19cb c0x0000 (---------------) + I stalowa-wola
+ 0x0029720c, // n0x19cc c0x0000 (---------------) + I starachowice
+ 0x002b8648, // n0x19cd c0x0000 (---------------) + I stargard
+ 0x00325847, // n0x19ce c0x0000 (---------------) + I suwalki
+ 0x002d4d08, // n0x19cf c0x0000 (---------------) + I swidnica
+ 0x002d590a, // n0x19d0 c0x0000 (---------------) + I swiebodzin
+ 0x002d608b, // n0x19d1 c0x0000 (---------------) + I swinoujscie
+ 0x003433c8, // n0x19d2 c0x0000 (---------------) + I szczecin
+ 0x002bee48, // n0x19d3 c0x0000 (---------------) + I szczytno
+ 0x00207586, // n0x19d4 c0x0000 (---------------) + I szkola
+ 0x0030a185, // n0x19d5 c0x0000 (---------------) + I targi
+ 0x0030ebca, // n0x19d6 c0x0000 (---------------) + I tarnobrzeg
+ 0x0021e345, // n0x19d7 c0x0000 (---------------) + I tgory
+ 0x00208902, // n0x19d8 c0x0000 (---------------) + I tm
+ 0x002ae287, // n0x19d9 c0x0000 (---------------) + I tourism
+ 0x0027f186, // n0x19da c0x0000 (---------------) + I travel
+ 0x0033de05, // n0x19db c0x0000 (---------------) + I turek
+ 0x002d8689, // n0x19dc c0x0000 (---------------) + I turystyka
+ 0x0036b4c5, // n0x19dd c0x0000 (---------------) + I tychy
+ 0x0027af05, // n0x19de c0x0000 (---------------) + I ustka
+ 0x0036c6c9, // n0x19df c0x0000 (---------------) + I walbrzych
+ 0x00232206, // n0x19e0 c0x0000 (---------------) + I warmia
+ 0x00250f08, // n0x19e1 c0x0000 (---------------) + I warszawa
+ 0x002c7183, // n0x19e2 c0x0000 (---------------) + I waw
+ 0x00391186, // n0x19e3 c0x0000 (---------------) + I wegrow
+ 0x00263586, // n0x19e4 c0x0000 (---------------) + I wielun
+ 0x002e2d45, // n0x19e5 c0x0000 (---------------) + I wlocl
+ 0x002e2d49, // n0x19e6 c0x0000 (---------------) + I wloclawek
+ 0x0030b189, // n0x19e7 c0x0000 (---------------) + I wodzislaw
+ 0x002609c7, // n0x19e8 c0x0000 (---------------) + I wolomin
+ 0x000e2bc4, // n0x19e9 c0x0000 (---------------) + wroc
+ 0x002e2bc7, // n0x19ea c0x0000 (---------------) + I wroclaw
+ 0x00207909, // n0x19eb c0x0000 (---------------) + I zachpomor
+ 0x00216245, // n0x19ec c0x0000 (---------------) + I zagan
+ 0x0002bf08, // n0x19ed c0x0000 (---------------) + zakopane
+ 0x00310d85, // n0x19ee c0x0000 (---------------) + I zarow
+ 0x00217185, // n0x19ef c0x0000 (---------------) + I zgora
+ 0x0021e549, // n0x19f0 c0x0000 (---------------) + I zgorzelec
+ 0x002105c2, // n0x19f1 c0x0000 (---------------) + I ap
+ 0x00253e04, // n0x19f2 c0x0000 (---------------) + I griw
+ 0x00200b42, // n0x19f3 c0x0000 (---------------) + I ic
+ 0x00202b42, // n0x19f4 c0x0000 (---------------) + I is
+ 0x00268d85, // n0x19f5 c0x0000 (---------------) + I kmpsp
+ 0x002c1788, // n0x19f6 c0x0000 (---------------) + I konsulat
+ 0x00379185, // n0x19f7 c0x0000 (---------------) + I kppsp
+ 0x002ac643, // n0x19f8 c0x0000 (---------------) + I kwp
+ 0x002ac645, // n0x19f9 c0x0000 (---------------) + I kwpsp
+ 0x002bb7c3, // n0x19fa c0x0000 (---------------) + I mup
+ 0x0020a142, // n0x19fb c0x0000 (---------------) + I mw
+ 0x002d3fc4, // n0x19fc c0x0000 (---------------) + I oirm
+ 0x002f3c43, // n0x19fd c0x0000 (---------------) + I oum
+ 0x002052c2, // n0x19fe c0x0000 (---------------) + I pa
+ 0x0036be84, // n0x19ff c0x0000 (---------------) + I pinb
+ 0x002c3b43, // n0x1a00 c0x0000 (---------------) + I piw
+ 0x00203f02, // n0x1a01 c0x0000 (---------------) + I po
+ 0x002369c3, // n0x1a02 c0x0000 (---------------) + I psp
+ 0x0027ac04, // n0x1a03 c0x0000 (---------------) + I psse
+ 0x002a32c3, // n0x1a04 c0x0000 (---------------) + I pup
+ 0x00370ac4, // n0x1a05 c0x0000 (---------------) + I rzgw
+ 0x00201a02, // n0x1a06 c0x0000 (---------------) + I sa
+ 0x0025ebc3, // n0x1a07 c0x0000 (---------------) + I sdn
+ 0x002206c3, // n0x1a08 c0x0000 (---------------) + I sko
+ 0x00201102, // n0x1a09 c0x0000 (---------------) + I so
+ 0x002ceec2, // n0x1a0a c0x0000 (---------------) + I sr
+ 0x0030afc9, // n0x1a0b c0x0000 (---------------) + I starostwo
+ 0x00205082, // n0x1a0c c0x0000 (---------------) + I ug
+ 0x0031c4c4, // n0x1a0d c0x0000 (---------------) + I ugim
+ 0x00209802, // n0x1a0e c0x0000 (---------------) + I um
+ 0x0020b6c4, // n0x1a0f c0x0000 (---------------) + I umig
+ 0x0026a844, // n0x1a10 c0x0000 (---------------) + I upow
+ 0x00243ec4, // n0x1a11 c0x0000 (---------------) + I uppo
+ 0x00209f42, // n0x1a12 c0x0000 (---------------) + I us
+ 0x0022f3c2, // n0x1a13 c0x0000 (---------------) + I uw
+ 0x0020c583, // n0x1a14 c0x0000 (---------------) + I uzs
+ 0x00345983, // n0x1a15 c0x0000 (---------------) + I wif
+ 0x002310c4, // n0x1a16 c0x0000 (---------------) + I wiih
+ 0x00270804, // n0x1a17 c0x0000 (---------------) + I winb
+ 0x002b8984, // n0x1a18 c0x0000 (---------------) + I wios
+ 0x002c9784, // n0x1a19 c0x0000 (---------------) + I witd
+ 0x0030b383, // n0x1a1a c0x0000 (---------------) + I wiw
+ 0x002725c3, // n0x1a1b c0x0000 (---------------) + I wsa
+ 0x0031be44, // n0x1a1c c0x0000 (---------------) + I wskr
+ 0x002e3d44, // n0x1a1d c0x0000 (---------------) + I wuoz
+ 0x002e4546, // n0x1a1e c0x0000 (---------------) + I wzmiuw
+ 0x002c2042, // n0x1a1f c0x0000 (---------------) + I zp
+ 0x00200742, // n0x1a20 c0x0000 (---------------) + I co
+ 0x002d75c3, // n0x1a21 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a22 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1a23 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1a24 c0x0000 (---------------) + I org
+ 0x00201e82, // n0x1a25 c0x0000 (---------------) + I ac
+ 0x00310603, // n0x1a26 c0x0000 (---------------) + I biz
+ 0x00222ac3, // n0x1a27 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1a28 c0x0000 (---------------) + I edu
+ 0x00208883, // n0x1a29 c0x0000 (---------------) + I est
+ 0x0021e283, // n0x1a2a c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1a2b c0x0000 (---------------) + I info
+ 0x0030b284, // n0x1a2c c0x0000 (---------------) + I isla
+ 0x00298944, // n0x1a2d c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1a2e c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1a2f c0x0000 (---------------) + I org
+ 0x00218243, // n0x1a30 c0x0000 (---------------) + I pro
+ 0x002cc284, // n0x1a31 c0x0000 (---------------) + I prof
+ 0x002f2783, // n0x1a32 c0x0000 (---------------) + I aca
+ 0x0020c103, // n0x1a33 c0x0000 (---------------) + I bar
+ 0x0025b403, // n0x1a34 c0x0000 (---------------) + I cpa
+ 0x002674c3, // n0x1a35 c0x0000 (---------------) + I eng
+ 0x0029d803, // n0x1a36 c0x0000 (---------------) + I jur
+ 0x00253883, // n0x1a37 c0x0000 (---------------) + I law
+ 0x0020b403, // n0x1a38 c0x0000 (---------------) + I med
+ 0x00222ac3, // n0x1a39 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1a3a c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a3b c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1a3c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1a3d c0x0000 (---------------) + I org
+ 0x002c6b43, // n0x1a3e c0x0000 (---------------) + I plo
+ 0x00223f83, // n0x1a3f c0x0000 (---------------) + I sec
+ 0x000e4188, // n0x1a40 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1a41 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1a42 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a43 c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x1a44 c0x0000 (---------------) + I int
+ 0x002170c3, // n0x1a45 c0x0000 (---------------) + I net
+ 0x0022cb84, // n0x1a46 c0x0000 (---------------) + I nome
+ 0x0021dcc3, // n0x1a47 c0x0000 (---------------) + I org
+ 0x00296544, // n0x1a48 c0x0000 (---------------) + I publ
+ 0x00251645, // n0x1a49 c0x0000 (---------------) + I belau
+ 0x00200742, // n0x1a4a c0x0000 (---------------) + I co
+ 0x00203fc2, // n0x1a4b c0x0000 (---------------) + I ed
+ 0x00202342, // n0x1a4c c0x0000 (---------------) + I go
+ 0x00201082, // n0x1a4d c0x0000 (---------------) + I ne
+ 0x00200c42, // n0x1a4e c0x0000 (---------------) + I or
+ 0x00222ac3, // n0x1a4f c0x0000 (---------------) + I com
+ 0x00228d44, // n0x1a50 c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x1a51 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a52 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1a53 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1a54 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1a55 c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1a56 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1a57 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1a58 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a59 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1a5a c0x0000 (---------------) + I mil
+ 0x00298944, // n0x1a5b c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1a5c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1a5d c0x0000 (---------------) + I org
+ 0x00206103, // n0x1a5e c0x0000 (---------------) + I sch
+ 0x002729c4, // n0x1a5f c0x0000 (---------------) + I asso
+ 0x000e4188, // n0x1a60 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1a61 c0x0000 (---------------) + I com
+ 0x00207cc3, // n0x1a62 c0x0000 (---------------) + I nom
+ 0x00246584, // n0x1a63 c0x0000 (---------------) + I arts
+ 0x000e4188, // n0x1a64 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1a65 c0x0000 (---------------) + I com
+ 0x00238544, // n0x1a66 c0x0000 (---------------) + I firm
+ 0x00200304, // n0x1a67 c0x0000 (---------------) + I info
+ 0x00207cc3, // n0x1a68 c0x0000 (---------------) + I nom
+ 0x00200e02, // n0x1a69 c0x0000 (---------------) + I nt
+ 0x0021dcc3, // n0x1a6a c0x0000 (---------------) + I org
+ 0x002e6343, // n0x1a6b c0x0000 (---------------) + I rec
+ 0x002cf4c5, // n0x1a6c c0x0000 (---------------) + I store
+ 0x00208902, // n0x1a6d c0x0000 (---------------) + I tm
+ 0x002e3e83, // n0x1a6e c0x0000 (---------------) + I www
+ 0x00201e82, // n0x1a6f c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x1a70 c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x1a71 c0x0000 (---------------) + I co
+ 0x002d75c3, // n0x1a72 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1a73 c0x0000 (---------------) + I gov
+ 0x00200242, // n0x1a74 c0x0000 (---------------) + I in
+ 0x0021dcc3, // n0x1a75 c0x0000 (---------------) + I org
+ 0x00201e82, // n0x1a76 c0x0000 (---------------) + I ac
+ 0x00382c47, // n0x1a77 c0x0000 (---------------) + I adygeya
+ 0x0028ee05, // n0x1a78 c0x0000 (---------------) + I altai
+ 0x00248504, // n0x1a79 c0x0000 (---------------) + I amur
+ 0x0036ab06, // n0x1a7a c0x0000 (---------------) + I amursk
+ 0x002f314b, // n0x1a7b c0x0000 (---------------) + I arkhangelsk
+ 0x002e9b49, // n0x1a7c c0x0000 (---------------) + I astrakhan
+ 0x002bec86, // n0x1a7d c0x0000 (---------------) + I baikal
+ 0x00309449, // n0x1a7e c0x0000 (---------------) + I bashkiria
+ 0x002c1a08, // n0x1a7f c0x0000 (---------------) + I belgorod
+ 0x00200503, // n0x1a80 c0x0000 (---------------) + I bir
+ 0x000e4188, // n0x1a81 c0x0000 (---------------) + blogspot
+ 0x0021cc07, // n0x1a82 c0x0000 (---------------) + I bryansk
+ 0x0021bc88, // n0x1a83 c0x0000 (---------------) + I buryatia
+ 0x00355dc3, // n0x1a84 c0x0000 (---------------) + I cbg
+ 0x002503c4, // n0x1a85 c0x0000 (---------------) + I chel
+ 0x002539cb, // n0x1a86 c0x0000 (---------------) + I chelyabinsk
+ 0x002932c5, // n0x1a87 c0x0000 (---------------) + I chita
+ 0x002ab708, // n0x1a88 c0x0000 (---------------) + I chukotka
+ 0x00313689, // n0x1a89 c0x0000 (---------------) + I chuvashia
+ 0x00249083, // n0x1a8a c0x0000 (---------------) + I cmw
+ 0x00222ac3, // n0x1a8b c0x0000 (---------------) + I com
+ 0x00309888, // n0x1a8c c0x0000 (---------------) + I dagestan
+ 0x002d3887, // n0x1a8d c0x0000 (---------------) + I dudinka
+ 0x00311a46, // n0x1a8e c0x0000 (---------------) + I e-burg
+ 0x002d75c3, // n0x1a8f c0x0000 (---------------) + I edu
+ 0x0032aa47, // n0x1a90 c0x0000 (---------------) + I fareast
+ 0x0021e283, // n0x1a91 c0x0000 (---------------) + I gov
+ 0x003789c6, // n0x1a92 c0x0000 (---------------) + I grozny
+ 0x00238c03, // n0x1a93 c0x0000 (---------------) + I int
+ 0x00220587, // n0x1a94 c0x0000 (---------------) + I irkutsk
+ 0x002f2a47, // n0x1a95 c0x0000 (---------------) + I ivanovo
+ 0x00379007, // n0x1a96 c0x0000 (---------------) + I izhevsk
+ 0x00321805, // n0x1a97 c0x0000 (---------------) + I jamal
+ 0x00202683, // n0x1a98 c0x0000 (---------------) + I jar
+ 0x002a248b, // n0x1a99 c0x0000 (---------------) + I joshkar-ola
+ 0x00312348, // n0x1a9a c0x0000 (---------------) + I k-uralsk
+ 0x002201c8, // n0x1a9b c0x0000 (---------------) + I kalmykia
+ 0x00305446, // n0x1a9c c0x0000 (---------------) + I kaluga
+ 0x0023a649, // n0x1a9d c0x0000 (---------------) + I kamchatka
+ 0x00365ac7, // n0x1a9e c0x0000 (---------------) + I karelia
+ 0x002dec85, // n0x1a9f c0x0000 (---------------) + I kazan
+ 0x0022d2c4, // n0x1aa0 c0x0000 (---------------) + I kchr
+ 0x00271548, // n0x1aa1 c0x0000 (---------------) + I kemerovo
+ 0x00235f8a, // n0x1aa2 c0x0000 (---------------) + I khabarovsk
+ 0x002361c9, // n0x1aa3 c0x0000 (---------------) + I khakassia
+ 0x0025be03, // n0x1aa4 c0x0000 (---------------) + I khv
+ 0x00254bc5, // n0x1aa5 c0x0000 (---------------) + I kirov
+ 0x00325683, // n0x1aa6 c0x0000 (---------------) + I kms
+ 0x002b9586, // n0x1aa7 c0x0000 (---------------) + I koenig
+ 0x003935c4, // n0x1aa8 c0x0000 (---------------) + I komi
+ 0x002e2f48, // n0x1aa9 c0x0000 (---------------) + I kostroma
+ 0x0039334b, // n0x1aaa c0x0000 (---------------) + I krasnoyarsk
+ 0x00331845, // n0x1aab c0x0000 (---------------) + I kuban
+ 0x002a4ac6, // n0x1aac c0x0000 (---------------) + I kurgan
+ 0x002a8e85, // n0x1aad c0x0000 (---------------) + I kursk
+ 0x002a9608, // n0x1aae c0x0000 (---------------) + I kustanai
+ 0x002aa1c7, // n0x1aaf c0x0000 (---------------) + I kuzbass
+ 0x003414c7, // n0x1ab0 c0x0000 (---------------) + I lipetsk
+ 0x0032ddc7, // n0x1ab1 c0x0000 (---------------) + I magadan
+ 0x00271948, // n0x1ab2 c0x0000 (---------------) + I magnitka
+ 0x00216504, // n0x1ab3 c0x0000 (---------------) + I mari
+ 0x00216507, // n0x1ab4 c0x0000 (---------------) + I mari-el
+ 0x003529c6, // n0x1ab5 c0x0000 (---------------) + I marine
+ 0x0023fa03, // n0x1ab6 c0x0000 (---------------) + I mil
+ 0x002b4208, // n0x1ab7 c0x0000 (---------------) + I mordovia
+ 0x00233f43, // n0x1ab8 c0x0000 (---------------) + I msk
+ 0x002bbd88, // n0x1ab9 c0x0000 (---------------) + I murmansk
+ 0x002bf205, // n0x1aba c0x0000 (---------------) + I mytis
+ 0x00332688, // n0x1abb c0x0000 (---------------) + I nakhodka
+ 0x00375747, // n0x1abc c0x0000 (---------------) + I nalchik
+ 0x002170c3, // n0x1abd c0x0000 (---------------) + I net
+ 0x002effc3, // n0x1abe c0x0000 (---------------) + I nkz
+ 0x00278ec4, // n0x1abf c0x0000 (---------------) + I nnov
+ 0x00228f07, // n0x1ac0 c0x0000 (---------------) + I norilsk
+ 0x00201343, // n0x1ac1 c0x0000 (---------------) + I nov
+ 0x002f2b0b, // n0x1ac2 c0x0000 (---------------) + I novosibirsk
+ 0x00217803, // n0x1ac3 c0x0000 (---------------) + I nsk
+ 0x00233f04, // n0x1ac4 c0x0000 (---------------) + I omsk
+ 0x002cf548, // n0x1ac5 c0x0000 (---------------) + I orenburg
+ 0x0021dcc3, // n0x1ac6 c0x0000 (---------------) + I org
+ 0x002c98c5, // n0x1ac7 c0x0000 (---------------) + I oryol
+ 0x002e3305, // n0x1ac8 c0x0000 (---------------) + I oskol
+ 0x00332586, // n0x1ac9 c0x0000 (---------------) + I palana
+ 0x00395385, // n0x1aca c0x0000 (---------------) + I penza
+ 0x002bf5c4, // n0x1acb c0x0000 (---------------) + I perm
+ 0x00207742, // n0x1acc c0x0000 (---------------) + I pp
+ 0x002ce183, // n0x1acd c0x0000 (---------------) + I ptz
+ 0x002be20a, // n0x1ace c0x0000 (---------------) + I pyatigorsk
+ 0x002ef043, // n0x1acf c0x0000 (---------------) + I rnd
+ 0x0030c189, // n0x1ad0 c0x0000 (---------------) + I rubtsovsk
+ 0x003227c6, // n0x1ad1 c0x0000 (---------------) + I ryazan
+ 0x00217648, // n0x1ad2 c0x0000 (---------------) + I sakhalin
+ 0x0027a746, // n0x1ad3 c0x0000 (---------------) + I samara
+ 0x00218487, // n0x1ad4 c0x0000 (---------------) + I saratov
+ 0x002b7c08, // n0x1ad5 c0x0000 (---------------) + I simbirsk
+ 0x002ae3c8, // n0x1ad6 c0x0000 (---------------) + I smolensk
+ 0x002c5603, // n0x1ad7 c0x0000 (---------------) + I snz
+ 0x00268e43, // n0x1ad8 c0x0000 (---------------) + I spb
+ 0x002187c9, // n0x1ad9 c0x0000 (---------------) + I stavropol
+ 0x002daf83, // n0x1ada c0x0000 (---------------) + I stv
+ 0x002d0c06, // n0x1adb c0x0000 (---------------) + I surgut
+ 0x0028ea86, // n0x1adc c0x0000 (---------------) + I syzran
+ 0x002fe106, // n0x1add c0x0000 (---------------) + I tambov
+ 0x0031df09, // n0x1ade c0x0000 (---------------) + I tatarstan
+ 0x002e3fc4, // n0x1adf c0x0000 (---------------) + I test
+ 0x0020fe03, // n0x1ae0 c0x0000 (---------------) + I tom
+ 0x00233ec5, // n0x1ae1 c0x0000 (---------------) + I tomsk
+ 0x0038c2c9, // n0x1ae2 c0x0000 (---------------) + I tsaritsyn
+ 0x00220683, // n0x1ae3 c0x0000 (---------------) + I tsk
+ 0x002d78c4, // n0x1ae4 c0x0000 (---------------) + I tula
+ 0x002d9504, // n0x1ae5 c0x0000 (---------------) + I tuva
+ 0x00248304, // n0x1ae6 c0x0000 (---------------) + I tver
+ 0x002a7d06, // n0x1ae7 c0x0000 (---------------) + I tyumen
+ 0x00309dc3, // n0x1ae8 c0x0000 (---------------) + I udm
+ 0x00309dc8, // n0x1ae9 c0x0000 (---------------) + I udmurtia
+ 0x00245a08, // n0x1aea c0x0000 (---------------) + I ulan-ude
+ 0x002fe246, // n0x1aeb c0x0000 (---------------) + I vdonsk
+ 0x002dea8b, // n0x1aec c0x0000 (---------------) + I vladikavkaz
+ 0x002dedc8, // n0x1aed c0x0000 (---------------) + I vladimir
+ 0x002defcb, // n0x1aee c0x0000 (---------------) + I vladivostok
+ 0x002df749, // n0x1aef c0x0000 (---------------) + I volgograd
+ 0x002e1147, // n0x1af0 c0x0000 (---------------) + I vologda
+ 0x002e1e48, // n0x1af1 c0x0000 (---------------) + I voronezh
+ 0x002e2b03, // n0x1af2 c0x0000 (---------------) + I vrn
+ 0x0037fec6, // n0x1af3 c0x0000 (---------------) + I vyatka
+ 0x0020d4c7, // n0x1af4 c0x0000 (---------------) + I yakutia
+ 0x00284405, // n0x1af5 c0x0000 (---------------) + I yamal
+ 0x00332d09, // n0x1af6 c0x0000 (---------------) + I yaroslavl
+ 0x0036e1cd, // n0x1af7 c0x0000 (---------------) + I yekaterinburg
+ 0x00217491, // n0x1af8 c0x0000 (---------------) + I yuzhno-sakhalinsk
+ 0x0029adc5, // n0x1af9 c0x0000 (---------------) + I zgrad
+ 0x00201e82, // n0x1afa c0x0000 (---------------) + I ac
+ 0x00200742, // n0x1afb c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1afc c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1afd c0x0000 (---------------) + I edu
+ 0x003579c4, // n0x1afe c0x0000 (---------------) + I gouv
+ 0x0021e283, // n0x1aff c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x1b00 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x1b01 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1b02 c0x0000 (---------------) + I net
+ 0x00222ac3, // n0x1b03 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b04 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b05 c0x0000 (---------------) + I gov
+ 0x0020b403, // n0x1b06 c0x0000 (---------------) + I med
+ 0x002170c3, // n0x1b07 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b08 c0x0000 (---------------) + I org
+ 0x00296543, // n0x1b09 c0x0000 (---------------) + I pub
+ 0x00206103, // n0x1b0a c0x0000 (---------------) + I sch
+ 0x00222ac3, // n0x1b0b c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b0c c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b0d c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1b0e c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b0f c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1b10 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b11 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b12 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1b13 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b14 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1b15 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b16 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b17 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1b18 c0x0000 (---------------) + I info
+ 0x0020b403, // n0x1b19 c0x0000 (---------------) + I med
+ 0x002170c3, // n0x1b1a c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b1b c0x0000 (---------------) + I org
+ 0x0020bf42, // n0x1b1c c0x0000 (---------------) + I tv
+ 0x00200181, // n0x1b1d c0x0000 (---------------) + I a
+ 0x00201e82, // n0x1b1e c0x0000 (---------------) + I ac
+ 0x00200001, // n0x1b1f c0x0000 (---------------) + I b
+ 0x002fbc02, // n0x1b20 c0x0000 (---------------) + I bd
+ 0x000e4188, // n0x1b21 c0x0000 (---------------) + blogspot
+ 0x00213b05, // n0x1b22 c0x0000 (---------------) + I brand
+ 0x00200741, // n0x1b23 c0x0000 (---------------) + I c
+ 0x00022ac3, // n0x1b24 c0x0000 (---------------) + com
+ 0x002005c1, // n0x1b25 c0x0000 (---------------) + I d
+ 0x00200701, // n0x1b26 c0x0000 (---------------) + I e
+ 0x00200381, // n0x1b27 c0x0000 (---------------) + I f
+ 0x00235ec2, // n0x1b28 c0x0000 (---------------) + I fh
+ 0x00235ec4, // n0x1b29 c0x0000 (---------------) + I fhsk
+ 0x00351283, // n0x1b2a c0x0000 (---------------) + I fhv
+ 0x00200401, // n0x1b2b c0x0000 (---------------) + I g
+ 0x00200201, // n0x1b2c c0x0000 (---------------) + I h
+ 0x00200041, // n0x1b2d c0x0000 (---------------) + I i
+ 0x00201001, // n0x1b2e c0x0000 (---------------) + I k
+ 0x002c8587, // n0x1b2f c0x0000 (---------------) + I komforb
+ 0x002a8f8f, // n0x1b30 c0x0000 (---------------) + I kommunalforbund
+ 0x002b1a86, // n0x1b31 c0x0000 (---------------) + I komvux
+ 0x002008c1, // n0x1b32 c0x0000 (---------------) + I l
+ 0x00323b46, // n0x1b33 c0x0000 (---------------) + I lanbib
+ 0x002000c1, // n0x1b34 c0x0000 (---------------) + I m
+ 0x00200281, // n0x1b35 c0x0000 (---------------) + I n
+ 0x002f460e, // n0x1b36 c0x0000 (---------------) + I naturbruksgymn
+ 0x00200081, // n0x1b37 c0x0000 (---------------) + I o
+ 0x0021dcc3, // n0x1b38 c0x0000 (---------------) + I org
+ 0x00200b01, // n0x1b39 c0x0000 (---------------) + I p
+ 0x00290b05, // n0x1b3a c0x0000 (---------------) + I parti
+ 0x00207742, // n0x1b3b c0x0000 (---------------) + I pp
+ 0x0029abc5, // n0x1b3c c0x0000 (---------------) + I press
+ 0x00200581, // n0x1b3d c0x0000 (---------------) + I r
+ 0x002001c1, // n0x1b3e c0x0000 (---------------) + I s
+ 0x00200141, // n0x1b3f c0x0000 (---------------) + I t
+ 0x00208902, // n0x1b40 c0x0000 (---------------) + I tm
+ 0x00200101, // n0x1b41 c0x0000 (---------------) + I u
+ 0x00202541, // n0x1b42 c0x0000 (---------------) + I w
+ 0x00203ec1, // n0x1b43 c0x0000 (---------------) + I x
+ 0x00200801, // n0x1b44 c0x0000 (---------------) + I y
+ 0x00201901, // n0x1b45 c0x0000 (---------------) + I z
+ 0x000e4188, // n0x1b46 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1b47 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b48 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b49 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1b4a c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b4b c0x0000 (---------------) + I org
+ 0x00214943, // n0x1b4c c0x0000 (---------------) + I per
+ 0x00222ac3, // n0x1b4d c0x0000 (---------------) + I com
+ 0x0021e283, // n0x1b4e c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1b4f c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1b50 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b51 c0x0000 (---------------) + I org
+ 0x014c56c8, // n0x1b52 c0x0005 (---------------)* o platform
+ 0x000e4188, // n0x1b53 c0x0000 (---------------) + blogspot
+ 0x000e4188, // n0x1b54 c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1b55 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b56 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b57 c0x0000 (---------------) + I gov
+ 0x002170c3, // n0x1b58 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b59 c0x0000 (---------------) + I org
+ 0x00200603, // n0x1b5a c0x0000 (---------------) + I art
+ 0x000e4188, // n0x1b5b c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1b5c c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b5d c0x0000 (---------------) + I edu
+ 0x003579c4, // n0x1b5e c0x0000 (---------------) + I gouv
+ 0x0021dcc3, // n0x1b5f c0x0000 (---------------) + I org
+ 0x0028acc5, // n0x1b60 c0x0000 (---------------) + I perso
+ 0x0029f044, // n0x1b61 c0x0000 (---------------) + I univ
+ 0x00222ac3, // n0x1b62 c0x0000 (---------------) + I com
+ 0x002170c3, // n0x1b63 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b64 c0x0000 (---------------) + I org
+ 0x00200742, // n0x1b65 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1b66 c0x0000 (---------------) + I com
+ 0x00224f09, // n0x1b67 c0x0000 (---------------) + I consulado
+ 0x002d75c3, // n0x1b68 c0x0000 (---------------) + I edu
+ 0x00271109, // n0x1b69 c0x0000 (---------------) + I embaixada
+ 0x0021e283, // n0x1b6a c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1b6b c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1b6c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b6d c0x0000 (---------------) + I org
+ 0x002cb848, // n0x1b6e c0x0000 (---------------) + I principe
+ 0x0020fd47, // n0x1b6f c0x0000 (---------------) + I saotome
+ 0x002cf4c5, // n0x1b70 c0x0000 (---------------) + I store
+ 0x00382c47, // n0x1b71 c0x0000 (---------------) + I adygeya
+ 0x002f314b, // n0x1b72 c0x0000 (---------------) + I arkhangelsk
+ 0x00389cc8, // n0x1b73 c0x0000 (---------------) + I balashov
+ 0x00309449, // n0x1b74 c0x0000 (---------------) + I bashkiria
+ 0x0021cc07, // n0x1b75 c0x0000 (---------------) + I bryansk
+ 0x00309888, // n0x1b76 c0x0000 (---------------) + I dagestan
+ 0x003789c6, // n0x1b77 c0x0000 (---------------) + I grozny
+ 0x002f2a47, // n0x1b78 c0x0000 (---------------) + I ivanovo
+ 0x002201c8, // n0x1b79 c0x0000 (---------------) + I kalmykia
+ 0x00305446, // n0x1b7a c0x0000 (---------------) + I kaluga
+ 0x00365ac7, // n0x1b7b c0x0000 (---------------) + I karelia
+ 0x002361c9, // n0x1b7c c0x0000 (---------------) + I khakassia
+ 0x00384e89, // n0x1b7d c0x0000 (---------------) + I krasnodar
+ 0x002a4ac6, // n0x1b7e c0x0000 (---------------) + I kurgan
+ 0x00275dc5, // n0x1b7f c0x0000 (---------------) + I lenug
+ 0x002b4208, // n0x1b80 c0x0000 (---------------) + I mordovia
+ 0x00233f43, // n0x1b81 c0x0000 (---------------) + I msk
+ 0x002bbd88, // n0x1b82 c0x0000 (---------------) + I murmansk
+ 0x00375747, // n0x1b83 c0x0000 (---------------) + I nalchik
+ 0x00201343, // n0x1b84 c0x0000 (---------------) + I nov
+ 0x0032a707, // n0x1b85 c0x0000 (---------------) + I obninsk
+ 0x00395385, // n0x1b86 c0x0000 (---------------) + I penza
+ 0x002c8208, // n0x1b87 c0x0000 (---------------) + I pokrovsk
+ 0x00262b45, // n0x1b88 c0x0000 (---------------) + I sochi
+ 0x00268e43, // n0x1b89 c0x0000 (---------------) + I spb
+ 0x003547c9, // n0x1b8a c0x0000 (---------------) + I togliatti
+ 0x00295587, // n0x1b8b c0x0000 (---------------) + I troitsk
+ 0x002d78c4, // n0x1b8c c0x0000 (---------------) + I tula
+ 0x002d9504, // n0x1b8d c0x0000 (---------------) + I tuva
+ 0x002dea8b, // n0x1b8e c0x0000 (---------------) + I vladikavkaz
+ 0x002dedc8, // n0x1b8f c0x0000 (---------------) + I vladimir
+ 0x002e1147, // n0x1b90 c0x0000 (---------------) + I vologda
+ 0x00222ac3, // n0x1b91 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b92 c0x0000 (---------------) + I edu
+ 0x0034eb03, // n0x1b93 c0x0000 (---------------) + I gob
+ 0x0021dcc3, // n0x1b94 c0x0000 (---------------) + I org
+ 0x00230683, // n0x1b95 c0x0000 (---------------) + I red
+ 0x0021e283, // n0x1b96 c0x0000 (---------------) + I gov
+ 0x00222ac3, // n0x1b97 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1b98 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1b99 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1b9a c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1b9b c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1b9c c0x0000 (---------------) + I org
+ 0x00201e82, // n0x1b9d c0x0000 (---------------) + I ac
+ 0x00200742, // n0x1b9e c0x0000 (---------------) + I co
+ 0x0021dcc3, // n0x1b9f c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1ba0 c0x0000 (---------------) + blogspot
+ 0x00201e82, // n0x1ba1 c0x0000 (---------------) + I ac
+ 0x00200742, // n0x1ba2 c0x0000 (---------------) + I co
+ 0x00202342, // n0x1ba3 c0x0000 (---------------) + I go
+ 0x00200242, // n0x1ba4 c0x0000 (---------------) + I in
+ 0x00204802, // n0x1ba5 c0x0000 (---------------) + I mi
+ 0x002170c3, // n0x1ba6 c0x0000 (---------------) + I net
+ 0x00200c42, // n0x1ba7 c0x0000 (---------------) + I or
+ 0x00201e82, // n0x1ba8 c0x0000 (---------------) + I ac
+ 0x00310603, // n0x1ba9 c0x0000 (---------------) + I biz
+ 0x00200742, // n0x1baa c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1bab c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1bac c0x0000 (---------------) + I edu
+ 0x00202342, // n0x1bad c0x0000 (---------------) + I go
+ 0x0021e283, // n0x1bae c0x0000 (---------------) + I gov
+ 0x00238c03, // n0x1baf c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x1bb0 c0x0000 (---------------) + I mil
+ 0x00298944, // n0x1bb1 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1bb2 c0x0000 (---------------) + I net
+ 0x0020e3c3, // n0x1bb3 c0x0000 (---------------) + I nic
+ 0x0021dcc3, // n0x1bb4 c0x0000 (---------------) + I org
+ 0x002e3fc4, // n0x1bb5 c0x0000 (---------------) + I test
+ 0x00219fc3, // n0x1bb6 c0x0000 (---------------) + I web
+ 0x0021e283, // n0x1bb7 c0x0000 (---------------) + I gov
+ 0x00200742, // n0x1bb8 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1bb9 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1bba c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1bbb c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1bbc c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1bbd c0x0000 (---------------) + I net
+ 0x00207cc3, // n0x1bbe c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x1bbf c0x0000 (---------------) + I org
+ 0x002efd87, // n0x1bc0 c0x0000 (---------------) + I agrinet
+ 0x00222ac3, // n0x1bc1 c0x0000 (---------------) + I com
+ 0x00245b87, // n0x1bc2 c0x0000 (---------------) + I defense
+ 0x002d75c6, // n0x1bc3 c0x0000 (---------------) + I edunet
+ 0x00210e83, // n0x1bc4 c0x0000 (---------------) + I ens
+ 0x00236403, // n0x1bc5 c0x0000 (---------------) + I fin
+ 0x0021e283, // n0x1bc6 c0x0000 (---------------) + I gov
+ 0x00215703, // n0x1bc7 c0x0000 (---------------) + I ind
+ 0x00200304, // n0x1bc8 c0x0000 (---------------) + I info
+ 0x002c5104, // n0x1bc9 c0x0000 (---------------) + I intl
+ 0x002d4086, // n0x1bca c0x0000 (---------------) + I mincom
+ 0x0023ac03, // n0x1bcb c0x0000 (---------------) + I nat
+ 0x002170c3, // n0x1bcc c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1bcd c0x0000 (---------------) + I org
+ 0x0028acc5, // n0x1bce c0x0000 (---------------) + I perso
+ 0x003538c4, // n0x1bcf c0x0000 (---------------) + I rnrt
+ 0x002e6c03, // n0x1bd0 c0x0000 (---------------) + I rns
+ 0x00205b83, // n0x1bd1 c0x0000 (---------------) + I rnu
+ 0x002ae287, // n0x1bd2 c0x0000 (---------------) + I tourism
+ 0x0033c485, // n0x1bd3 c0x0000 (---------------) + I turen
+ 0x00222ac3, // n0x1bd4 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1bd5 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1bd6 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1bd7 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1bd8 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1bd9 c0x0000 (---------------) + I org
+ 0x00201482, // n0x1bda c0x0000 (---------------) + I av
+ 0x002791c3, // n0x1bdb c0x0000 (---------------) + I bbs
+ 0x00251643, // n0x1bdc c0x0000 (---------------) + I bel
+ 0x00310603, // n0x1bdd c0x0000 (---------------) + I biz
+ 0x51622ac3, // n0x1bde c0x0145 (n0x1bef-n0x1bf0) + I com
+ 0x0022fb02, // n0x1bdf c0x0000 (---------------) + I dr
+ 0x002d75c3, // n0x1be0 c0x0000 (---------------) + I edu
+ 0x002012c3, // n0x1be1 c0x0000 (---------------) + I gen
+ 0x0021e283, // n0x1be2 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1be3 c0x0000 (---------------) + I info
+ 0x00312503, // n0x1be4 c0x0000 (---------------) + I k12
+ 0x0023df03, // n0x1be5 c0x0000 (---------------) + I kep
+ 0x0023fa03, // n0x1be6 c0x0000 (---------------) + I mil
+ 0x00298944, // n0x1be7 c0x0000 (---------------) + I name
+ 0x51a1c742, // n0x1be8 c0x0146 (n0x1bf0-n0x1bf1) + I nc
+ 0x002170c3, // n0x1be9 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1bea c0x0000 (---------------) + I org
+ 0x00218943, // n0x1beb c0x0000 (---------------) + I pol
+ 0x0022ba83, // n0x1bec c0x0000 (---------------) + I tel
+ 0x0020bf42, // n0x1bed c0x0000 (---------------) + I tv
+ 0x00219fc3, // n0x1bee c0x0000 (---------------) + I web
+ 0x000e4188, // n0x1bef c0x0000 (---------------) + blogspot
+ 0x0021e283, // n0x1bf0 c0x0000 (---------------) + I gov
+ 0x002751c4, // n0x1bf1 c0x0000 (---------------) + I aero
+ 0x00310603, // n0x1bf2 c0x0000 (---------------) + I biz
+ 0x00200742, // n0x1bf3 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1bf4 c0x0000 (---------------) + I com
+ 0x00228d44, // n0x1bf5 c0x0000 (---------------) + I coop
+ 0x002d75c3, // n0x1bf6 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1bf7 c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1bf8 c0x0000 (---------------) + I info
+ 0x00238c03, // n0x1bf9 c0x0000 (---------------) + I int
+ 0x002a5d04, // n0x1bfa c0x0000 (---------------) + I jobs
+ 0x00203604, // n0x1bfb c0x0000 (---------------) + I mobi
+ 0x002bd646, // n0x1bfc c0x0000 (---------------) + I museum
+ 0x00298944, // n0x1bfd c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1bfe c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1bff c0x0000 (---------------) + I org
+ 0x00218243, // n0x1c00 c0x0000 (---------------) + I pro
+ 0x0027f186, // n0x1c01 c0x0000 (---------------) + I travel
+ 0x00041e4b, // n0x1c02 c0x0000 (---------------) + better-than
+ 0x00009ac6, // n0x1c03 c0x0000 (---------------) + dyndns
+ 0x00019e0a, // n0x1c04 c0x0000 (---------------) + on-the-web
+ 0x00171e8a, // n0x1c05 c0x0000 (---------------) + worse-than
+ 0x000e4188, // n0x1c06 c0x0000 (---------------) + blogspot
+ 0x002752c4, // n0x1c07 c0x0000 (---------------) + I club
+ 0x00222ac3, // n0x1c08 c0x0000 (---------------) + I com
+ 0x003105c4, // n0x1c09 c0x0000 (---------------) + I ebiz
+ 0x002d75c3, // n0x1c0a c0x0000 (---------------) + I edu
+ 0x00212084, // n0x1c0b c0x0000 (---------------) + I game
+ 0x0021e283, // n0x1c0c c0x0000 (---------------) + I gov
+ 0x00300b83, // n0x1c0d c0x0000 (---------------) + I idv
+ 0x0023fa03, // n0x1c0e c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1c0f c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1c10 c0x0000 (---------------) + I org
+ 0x0030d70b, // n0x1c11 c0x0000 (---------------) + I xn--czrw28b
+ 0x003867ca, // n0x1c12 c0x0000 (---------------) + I xn--uc0atv
+ 0x0039604c, // n0x1c13 c0x0000 (---------------) + I xn--zf0ao64a
+ 0x00201e82, // n0x1c14 c0x0000 (---------------) + I ac
+ 0x00200742, // n0x1c15 c0x0000 (---------------) + I co
+ 0x00202342, // n0x1c16 c0x0000 (---------------) + I go
+ 0x00294945, // n0x1c17 c0x0000 (---------------) + I hotel
+ 0x00200304, // n0x1c18 c0x0000 (---------------) + I info
+ 0x00208942, // n0x1c19 c0x0000 (---------------) + I me
+ 0x0023fa03, // n0x1c1a c0x0000 (---------------) + I mil
+ 0x00203604, // n0x1c1b c0x0000 (---------------) + I mobi
+ 0x00201082, // n0x1c1c c0x0000 (---------------) + I ne
+ 0x00200c42, // n0x1c1d c0x0000 (---------------) + I or
+ 0x00200982, // n0x1c1e c0x0000 (---------------) + I sc
+ 0x0020bf42, // n0x1c1f c0x0000 (---------------) + I tv
+ 0x0029d289, // n0x1c20 c0x0000 (---------------) + I cherkassy
+ 0x0028e908, // n0x1c21 c0x0000 (---------------) + I cherkasy
+ 0x00259889, // n0x1c22 c0x0000 (---------------) + I chernigov
+ 0x0025f489, // n0x1c23 c0x0000 (---------------) + I chernihiv
+ 0x0026b44a, // n0x1c24 c0x0000 (---------------) + I chernivtsi
+ 0x00367e8a, // n0x1c25 c0x0000 (---------------) + I chernovtsy
+ 0x00201782, // n0x1c26 c0x0000 (---------------) + I ck
+ 0x002211c2, // n0x1c27 c0x0000 (---------------) + I cn
+ 0x00200742, // n0x1c28 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1c29 c0x0000 (---------------) + I com
+ 0x0020c502, // n0x1c2a c0x0000 (---------------) + I cr
+ 0x00231c86, // n0x1c2b c0x0000 (---------------) + I crimea
+ 0x0033f802, // n0x1c2c c0x0000 (---------------) + I cv
+ 0x00209b82, // n0x1c2d c0x0000 (---------------) + I dn
+ 0x0036ad4e, // n0x1c2e c0x0000 (---------------) + I dnepropetrovsk
+ 0x0025ec0e, // n0x1c2f c0x0000 (---------------) + I dnipropetrovsk
+ 0x0026b2c7, // n0x1c30 c0x0000 (---------------) + I dominic
+ 0x003409c7, // n0x1c31 c0x0000 (---------------) + I donetsk
+ 0x00238b42, // n0x1c32 c0x0000 (---------------) + I dp
+ 0x002d75c3, // n0x1c33 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1c34 c0x0000 (---------------) + I gov
+ 0x00239ec2, // n0x1c35 c0x0000 (---------------) + I if
+ 0x00200242, // n0x1c36 c0x0000 (---------------) + I in
+ 0x0022cf4f, // n0x1c37 c0x0000 (---------------) + I ivano-frankivsk
+ 0x002176c2, // n0x1c38 c0x0000 (---------------) + I kh
+ 0x00244d07, // n0x1c39 c0x0000 (---------------) + I kharkiv
+ 0x0024cb07, // n0x1c3a c0x0000 (---------------) + I kharkov
+ 0x00250087, // n0x1c3b c0x0000 (---------------) + I kherson
+ 0x0025304c, // n0x1c3c c0x0000 (---------------) + I khmelnitskiy
+ 0x002581cc, // n0x1c3d c0x0000 (---------------) + I khmelnytskyi
+ 0x0037aac4, // n0x1c3e c0x0000 (---------------) + I kiev
+ 0x00254bca, // n0x1c3f c0x0000 (---------------) + I kirovograd
+ 0x00268d82, // n0x1c40 c0x0000 (---------------) + I km
+ 0x002034c2, // n0x1c41 c0x0000 (---------------) + I kr
+ 0x0029ed84, // n0x1c42 c0x0000 (---------------) + I krym
+ 0x00238082, // n0x1c43 c0x0000 (---------------) + I ks
+ 0x002aa8c2, // n0x1c44 c0x0000 (---------------) + I kv
+ 0x00258404, // n0x1c45 c0x0000 (---------------) + I kyiv
+ 0x0020e4c2, // n0x1c46 c0x0000 (---------------) + I lg
+ 0x00205ec2, // n0x1c47 c0x0000 (---------------) + I lt
+ 0x003054c7, // n0x1c48 c0x0000 (---------------) + I lugansk
+ 0x002e6745, // n0x1c49 c0x0000 (---------------) + I lutsk
+ 0x00227f02, // n0x1c4a c0x0000 (---------------) + I lv
+ 0x0022cec4, // n0x1c4b c0x0000 (---------------) + I lviv
+ 0x00356d82, // n0x1c4c c0x0000 (---------------) + I mk
+ 0x002f28c8, // n0x1c4d c0x0000 (---------------) + I mykolaiv
+ 0x002170c3, // n0x1c4e c0x0000 (---------------) + I net
+ 0x00342248, // n0x1c4f c0x0000 (---------------) + I nikolaev
+ 0x00200782, // n0x1c50 c0x0000 (---------------) + I od
+ 0x00227505, // n0x1c51 c0x0000 (---------------) + I odesa
+ 0x0035fa46, // n0x1c52 c0x0000 (---------------) + I odessa
+ 0x0021dcc3, // n0x1c53 c0x0000 (---------------) + I org
+ 0x00201e02, // n0x1c54 c0x0000 (---------------) + I pl
+ 0x002c8f07, // n0x1c55 c0x0000 (---------------) + I poltava
+ 0x00207742, // n0x1c56 c0x0000 (---------------) + I pp
+ 0x002cba85, // n0x1c57 c0x0000 (---------------) + I rivne
+ 0x00208085, // n0x1c58 c0x0000 (---------------) + I rovno
+ 0x00211fc2, // n0x1c59 c0x0000 (---------------) + I rv
+ 0x002046c2, // n0x1c5a c0x0000 (---------------) + I sb
+ 0x0035e2ca, // n0x1c5b c0x0000 (---------------) + I sebastopol
+ 0x0024688a, // n0x1c5c c0x0000 (---------------) + I sevastopol
+ 0x0023f582, // n0x1c5d c0x0000 (---------------) + I sm
+ 0x00308004, // n0x1c5e c0x0000 (---------------) + I sumy
+ 0x00203202, // n0x1c5f c0x0000 (---------------) + I te
+ 0x0036c988, // n0x1c60 c0x0000 (---------------) + I ternopil
+ 0x002018c2, // n0x1c61 c0x0000 (---------------) + I uz
+ 0x0036a1c8, // n0x1c62 c0x0000 (---------------) + I uzhgorod
+ 0x002dc087, // n0x1c63 c0x0000 (---------------) + I vinnica
+ 0x002dc4c9, // n0x1c64 c0x0000 (---------------) + I vinnytsia
+ 0x00208102, // n0x1c65 c0x0000 (---------------) + I vn
+ 0x002e1c05, // n0x1c66 c0x0000 (---------------) + I volyn
+ 0x0028edc5, // n0x1c67 c0x0000 (---------------) + I yalta
+ 0x002b214b, // n0x1c68 c0x0000 (---------------) + I zaporizhzhe
+ 0x002b2b8c, // n0x1c69 c0x0000 (---------------) + I zaporizhzhia
+ 0x00220408, // n0x1c6a c0x0000 (---------------) + I zhitomir
+ 0x002e1fc8, // n0x1c6b c0x0000 (---------------) + I zhytomyr
+ 0x002c2042, // n0x1c6c c0x0000 (---------------) + I zp
+ 0x0020fa82, // n0x1c6d c0x0000 (---------------) + I zt
+ 0x00201e82, // n0x1c6e c0x0000 (---------------) + I ac
+ 0x000e4188, // n0x1c6f c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x1c70 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1c71 c0x0000 (---------------) + I com
+ 0x00202342, // n0x1c72 c0x0000 (---------------) + I go
+ 0x00201082, // n0x1c73 c0x0000 (---------------) + I ne
+ 0x00200c42, // n0x1c74 c0x0000 (---------------) + I or
+ 0x0021dcc3, // n0x1c75 c0x0000 (---------------) + I org
+ 0x00200982, // n0x1c76 c0x0000 (---------------) + I sc
+ 0x00201e82, // n0x1c77 c0x0000 (---------------) + I ac
+ 0x53a00742, // n0x1c78 c0x014e (n0x1c82-n0x1c83) + I co
+ 0x53e1e283, // n0x1c79 c0x014f (n0x1c83-n0x1c84) + I gov
+ 0x003413c3, // n0x1c7a c0x0000 (---------------) + I ltd
+ 0x00208942, // n0x1c7b c0x0000 (---------------) + I me
+ 0x002170c3, // n0x1c7c c0x0000 (---------------) + I net
+ 0x002037c3, // n0x1c7d c0x0000 (---------------) + I nhs
+ 0x0021dcc3, // n0x1c7e c0x0000 (---------------) + I org
+ 0x002c65c3, // n0x1c7f c0x0000 (---------------) + I plc
+ 0x00218946, // n0x1c80 c0x0000 (---------------) + I police
+ 0x01606103, // n0x1c81 c0x0005 (---------------)* o I sch
+ 0x000e4188, // n0x1c82 c0x0000 (---------------) + blogspot
+ 0x00043bc7, // n0x1c83 c0x0000 (---------------) + service
+ 0x54601a42, // n0x1c84 c0x0151 (n0x1cc3-n0x1cc6) + I ak
+ 0x54a00882, // n0x1c85 c0x0152 (n0x1cc6-n0x1cc9) + I al
+ 0x54e00602, // n0x1c86 c0x0153 (n0x1cc9-n0x1ccc) + I ar
+ 0x55200182, // n0x1c87 c0x0154 (n0x1ccc-n0x1ccf) + I as
+ 0x55608cc2, // n0x1c88 c0x0155 (n0x1ccf-n0x1cd2) + I az
+ 0x55a055c2, // n0x1c89 c0x0156 (n0x1cd2-n0x1cd5) + I ca
+ 0x55e00742, // n0x1c8a c0x0157 (n0x1cd5-n0x1cd8) + I co
+ 0x56223d82, // n0x1c8b c0x0158 (n0x1cd8-n0x1cdb) + I ct
+ 0x56616e02, // n0x1c8c c0x0159 (n0x1cdb-n0x1cde) + I dc
+ 0x56a006c2, // n0x1c8d c0x015a (n0x1cde-n0x1ce1) + I de
+ 0x0025ec03, // n0x1c8e c0x0000 (---------------) + I dni
+ 0x0020c743, // n0x1c8f c0x0000 (---------------) + I fed
+ 0x56e39b02, // n0x1c90 c0x015b (n0x1ce1-n0x1ce4) + I fl
+ 0x57201602, // n0x1c91 c0x015c (n0x1ce4-n0x1ce7) + I ga
+ 0x57629702, // n0x1c92 c0x015d (n0x1ce7-n0x1cea) + I gu
+ 0x57a00202, // n0x1c93 c0x015e (n0x1cea-n0x1cec) + I hi
+ 0x57e00482, // n0x1c94 c0x015f (n0x1cec-n0x1cef) + I ia
+ 0x58206202, // n0x1c95 c0x0160 (n0x1cef-n0x1cf2) + I id
+ 0x586036c2, // n0x1c96 c0x0161 (n0x1cf2-n0x1cf5) + I il
+ 0x58a00242, // n0x1c97 c0x0162 (n0x1cf5-n0x1cf8) + I in
+ 0x000aa785, // n0x1c98 c0x0000 (---------------) + is-by
+ 0x00209883, // n0x1c99 c0x0000 (---------------) + I isa
+ 0x00394c44, // n0x1c9a c0x0000 (---------------) + I kids
+ 0x58e38082, // n0x1c9b c0x0163 (n0x1cf8-n0x1cfb) + I ks
+ 0x59229082, // n0x1c9c c0x0164 (n0x1cfb-n0x1cfe) + I ky
+ 0x59601e42, // n0x1c9d c0x0165 (n0x1cfe-n0x1d01) + I la
+ 0x0006700b, // n0x1c9e c0x0000 (---------------) + land-4-sale
+ 0x59a04302, // n0x1c9f c0x0166 (n0x1d01-n0x1d04) + I ma
+ 0x5a238602, // n0x1ca0 c0x0168 (n0x1d07-n0x1d0a) + I md
+ 0x5a608942, // n0x1ca1 c0x0169 (n0x1d0a-n0x1d0d) + I me
+ 0x5aa04802, // n0x1ca2 c0x016a (n0x1d0d-n0x1d10) + I mi
+ 0x5ae17082, // n0x1ca3 c0x016b (n0x1d10-n0x1d13) + I mn
+ 0x5b203602, // n0x1ca4 c0x016c (n0x1d13-n0x1d16) + I mo
+ 0x5b609282, // n0x1ca5 c0x016d (n0x1d16-n0x1d19) + I ms
+ 0x5ba59642, // n0x1ca6 c0x016e (n0x1d19-n0x1d1c) + I mt
+ 0x5be1c742, // n0x1ca7 c0x016f (n0x1d1c-n0x1d1f) + I nc
+ 0x5c208f42, // n0x1ca8 c0x0170 (n0x1d1f-n0x1d21) + I nd
+ 0x5c601082, // n0x1ca9 c0x0171 (n0x1d21-n0x1d24) + I ne
+ 0x5ca037c2, // n0x1caa c0x0172 (n0x1d24-n0x1d27) + I nh
+ 0x5ce02942, // n0x1cab c0x0173 (n0x1d27-n0x1d2a) + I nj
+ 0x5d233c42, // n0x1cac c0x0174 (n0x1d2a-n0x1d2d) + I nm
+ 0x002c55c3, // n0x1cad c0x0000 (---------------) + I nsn
+ 0x5d608802, // n0x1cae c0x0175 (n0x1d2d-n0x1d30) + I nv
+ 0x5da108c2, // n0x1caf c0x0176 (n0x1d30-n0x1d33) + I ny
+ 0x5de051c2, // n0x1cb0 c0x0177 (n0x1d33-n0x1d36) + I oh
+ 0x5e201bc2, // n0x1cb1 c0x0178 (n0x1d36-n0x1d39) + I ok
+ 0x5e600c42, // n0x1cb2 c0x0179 (n0x1d39-n0x1d3c) + I or
+ 0x5ea052c2, // n0x1cb3 c0x017a (n0x1d3c-n0x1d3f) + I pa
+ 0x5ee18242, // n0x1cb4 c0x017b (n0x1d3f-n0x1d42) + I pr
+ 0x5f202842, // n0x1cb5 c0x017c (n0x1d42-n0x1d45) + I ri
+ 0x5f600982, // n0x1cb6 c0x017d (n0x1d45-n0x1d48) + I sc
+ 0x5fa4f842, // n0x1cb7 c0x017e (n0x1d48-n0x1d4a) + I sd
+ 0x000d044c, // n0x1cb8 c0x0000 (---------------) + stuff-4-sale
+ 0x5fe1d1c2, // n0x1cb9 c0x017f (n0x1d4a-n0x1d4d) + I tn
+ 0x60225242, // n0x1cba c0x0180 (n0x1d4d-n0x1d50) + I tx
+ 0x60600102, // n0x1cbb c0x0181 (n0x1d50-n0x1d53) + I ut
+ 0x60a013c2, // n0x1cbc c0x0182 (n0x1d53-n0x1d56) + I va
+ 0x60e13602, // n0x1cbd c0x0183 (n0x1d56-n0x1d59) + I vi
+ 0x6121e302, // n0x1cbe c0x0184 (n0x1d59-n0x1d5c) + I vt
+ 0x61602542, // n0x1cbf c0x0185 (n0x1d5c-n0x1d5f) + I wa
+ 0x61a05502, // n0x1cc0 c0x0186 (n0x1d5f-n0x1d62) + I wi
+ 0x61e63902, // n0x1cc1 c0x0187 (n0x1d62-n0x1d63) + I wv
+ 0x62237f82, // n0x1cc2 c0x0188 (n0x1d63-n0x1d66) + I wy
+ 0x0021aa82, // n0x1cc3 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cc4 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cc5 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cc6 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cc7 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cc8 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cc9 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cca c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1ccb c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1ccc c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1ccd c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cce c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1ccf c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cd0 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cd1 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cd2 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cd3 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cd4 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cd5 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cd6 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cd7 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cd8 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cd9 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cda c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cdb c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cdc c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cdd c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cde c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cdf c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1ce0 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1ce1 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1ce2 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1ce3 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1ce4 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1ce5 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1ce6 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1ce7 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1ce8 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1ce9 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cea c0x0000 (---------------) + I cc
+ 0x00273c43, // n0x1ceb c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cec c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1ced c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cee c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cef c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cf0 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cf1 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cf2 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cf3 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cf4 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cf5 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cf6 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cf7 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cf8 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cf9 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cfa c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cfb c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cfc c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1cfd c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1cfe c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1cff c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d00 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d01 c0x0000 (---------------) + I cc
+ 0x59f12503, // n0x1d02 c0x0167 (n0x1d04-n0x1d07) + I k12
+ 0x00273c43, // n0x1d03 c0x0000 (---------------) + I lib
+ 0x002e8ec4, // n0x1d04 c0x0000 (---------------) + I chtr
+ 0x0028e806, // n0x1d05 c0x0000 (---------------) + I paroch
+ 0x002ce243, // n0x1d06 c0x0000 (---------------) + I pvt
+ 0x0021aa82, // n0x1d07 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d08 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d09 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d0a c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d0b c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d0c c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d0d c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d0e c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d0f c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d10 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d11 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d12 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d13 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d14 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d15 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d16 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d17 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d18 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d19 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d1a c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d1b c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d1c c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d1d c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d1e c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d1f c0x0000 (---------------) + I cc
+ 0x00273c43, // n0x1d20 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d21 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d22 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d23 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d24 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d25 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d26 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d27 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d28 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d29 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d2a c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d2b c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d2c c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d2d c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d2e c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d2f c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d30 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d31 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d32 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d33 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d34 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d35 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d36 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d37 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d38 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d39 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d3a c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d3b c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d3c c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d3d c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d3e c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d3f c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d40 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d41 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d42 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d43 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d44 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d45 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d46 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d47 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d48 c0x0000 (---------------) + I cc
+ 0x00273c43, // n0x1d49 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d4a c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d4b c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d4c c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d4d c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d4e c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d4f c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d50 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d51 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d52 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d53 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d54 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d55 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d56 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d57 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d58 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d59 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d5a c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d5b c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d5c c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d5d c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d5e c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d5f c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d60 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d61 c0x0000 (---------------) + I lib
+ 0x0021aa82, // n0x1d62 c0x0000 (---------------) + I cc
+ 0x0021aa82, // n0x1d63 c0x0000 (---------------) + I cc
+ 0x00312503, // n0x1d64 c0x0000 (---------------) + I k12
+ 0x00273c43, // n0x1d65 c0x0000 (---------------) + I lib
+ 0x62a22ac3, // n0x1d66 c0x018a (n0x1d6c-n0x1d6d) + I com
+ 0x002d75c3, // n0x1d67 c0x0000 (---------------) + I edu
+ 0x0032b9c3, // n0x1d68 c0x0000 (---------------) + I gub
+ 0x0023fa03, // n0x1d69 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1d6a c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d6b c0x0000 (---------------) + I org
+ 0x000e4188, // n0x1d6c c0x0000 (---------------) + blogspot
+ 0x00200742, // n0x1d6d c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1d6e c0x0000 (---------------) + I com
+ 0x002170c3, // n0x1d6f c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d70 c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1d71 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1d72 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1d73 c0x0000 (---------------) + I gov
+ 0x0023fa03, // n0x1d74 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1d75 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d76 c0x0000 (---------------) + I org
+ 0x00246584, // n0x1d77 c0x0000 (---------------) + I arts
+ 0x00200742, // n0x1d78 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1d79 c0x0000 (---------------) + I com
+ 0x0023ee43, // n0x1d7a c0x0000 (---------------) + I e12
+ 0x002d75c3, // n0x1d7b c0x0000 (---------------) + I edu
+ 0x00238544, // n0x1d7c c0x0000 (---------------) + I firm
+ 0x0034eb03, // n0x1d7d c0x0000 (---------------) + I gob
+ 0x0021e283, // n0x1d7e c0x0000 (---------------) + I gov
+ 0x00200304, // n0x1d7f c0x0000 (---------------) + I info
+ 0x00238c03, // n0x1d80 c0x0000 (---------------) + I int
+ 0x0023fa03, // n0x1d81 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1d82 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d83 c0x0000 (---------------) + I org
+ 0x002e6343, // n0x1d84 c0x0000 (---------------) + I rec
+ 0x002cf4c5, // n0x1d85 c0x0000 (---------------) + I store
+ 0x0029d583, // n0x1d86 c0x0000 (---------------) + I tec
+ 0x00219fc3, // n0x1d87 c0x0000 (---------------) + I web
+ 0x00200742, // n0x1d88 c0x0000 (---------------) + I co
+ 0x00222ac3, // n0x1d89 c0x0000 (---------------) + I com
+ 0x00312503, // n0x1d8a c0x0000 (---------------) + I k12
+ 0x002170c3, // n0x1d8b c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d8c c0x0000 (---------------) + I org
+ 0x00201e82, // n0x1d8d c0x0000 (---------------) + I ac
+ 0x00310603, // n0x1d8e c0x0000 (---------------) + I biz
+ 0x000e4188, // n0x1d8f c0x0000 (---------------) + blogspot
+ 0x00222ac3, // n0x1d90 c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1d91 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1d92 c0x0000 (---------------) + I gov
+ 0x00205e06, // n0x1d93 c0x0000 (---------------) + I health
+ 0x00200304, // n0x1d94 c0x0000 (---------------) + I info
+ 0x00238c03, // n0x1d95 c0x0000 (---------------) + I int
+ 0x00298944, // n0x1d96 c0x0000 (---------------) + I name
+ 0x002170c3, // n0x1d97 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d98 c0x0000 (---------------) + I org
+ 0x00218243, // n0x1d99 c0x0000 (---------------) + I pro
+ 0x00222ac3, // n0x1d9a c0x0000 (---------------) + I com
+ 0x002d75c3, // n0x1d9b c0x0000 (---------------) + I edu
+ 0x002170c3, // n0x1d9c c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1d9d c0x0000 (---------------) + I org
+ 0x00222ac3, // n0x1d9e c0x0000 (---------------) + I com
+ 0x00009ac6, // n0x1d9f c0x0000 (---------------) + dyndns
+ 0x002d75c3, // n0x1da0 c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1da1 c0x0000 (---------------) + I gov
+ 0x00108086, // n0x1da2 c0x0000 (---------------) + mypets
+ 0x002170c3, // n0x1da3 c0x0000 (---------------) + I net
+ 0x0021dcc3, // n0x1da4 c0x0000 (---------------) + I org
+ 0x002f1308, // n0x1da5 c0x0000 (---------------) + I xn--80au
+ 0x002f5c09, // n0x1da6 c0x0000 (---------------) + I xn--90azh
+ 0x00303289, // n0x1da7 c0x0000 (---------------) + I xn--c1avg
+ 0x00312fc8, // n0x1da8 c0x0000 (---------------) + I xn--d1at
+ 0x00362248, // n0x1da9 c0x0000 (---------------) + I xn--o1ac
+ 0x00362249, // n0x1daa c0x0000 (---------------) + I xn--o1ach
+ 0x00201e82, // n0x1dab c0x0000 (---------------) + I ac
+ 0x00301306, // n0x1dac c0x0000 (---------------) + I agrica
+ 0x00205e83, // n0x1dad c0x0000 (---------------) + I alt
+ 0x65200742, // n0x1dae c0x0194 (n0x1dbc-n0x1dbd) + I co
+ 0x002d75c3, // n0x1daf c0x0000 (---------------) + I edu
+ 0x0021e283, // n0x1db0 c0x0000 (---------------) + I gov
+ 0x002c6987, // n0x1db1 c0x0000 (---------------) + I grondar
+ 0x00253883, // n0x1db2 c0x0000 (---------------) + I law
+ 0x0023fa03, // n0x1db3 c0x0000 (---------------) + I mil
+ 0x002170c3, // n0x1db4 c0x0000 (---------------) + I net
+ 0x00202303, // n0x1db5 c0x0000 (---------------) + I ngo
+ 0x00208b83, // n0x1db6 c0x0000 (---------------) + I nis
+ 0x00207cc3, // n0x1db7 c0x0000 (---------------) + I nom
+ 0x0021dcc3, // n0x1db8 c0x0000 (---------------) + I org
+ 0x00235406, // n0x1db9 c0x0000 (---------------) + I school
+ 0x00208902, // n0x1dba c0x0000 (---------------) + I tm
+ 0x00219fc3, // n0x1dbb c0x0000 (---------------) + I web
+ 0x000e4188, // n0x1dbc c0x0000 (---------------) + blogspot
+}
+
+// children is the list of nodes' children, the parent's wildcard bit and the
+// parent's node type. If a node has no children then their children index
+// will be in the range [0, 6), depending on the wildcard bit and node type.
+//
+// The layout within the uint32, from MSB to LSB, is:
+// [ 1 bits] unused
+// [ 1 bits] wildcard bit
+// [ 2 bits] node type
+// [14 bits] high nodes index (exclusive) of children
+// [14 bits] low nodes index (inclusive) of children
+var children = [...]uint32{
+ 0x00000000, // c0x0000 (---------------) +
+ 0x10000000, // c0x0001 (---------------) !
+ 0x20000000, // c0x0002 (---------------) o
+ 0x40000000, // c0x0003 (---------------)* +
+ 0x50000000, // c0x0004 (---------------)* !
+ 0x60000000, // c0x0005 (---------------)* o
+ 0x014fc539, // c0x0006 (n0x0539-n0x053f) +
+ 0x0150053f, // c0x0007 (n0x053f-n0x0540) +
+ 0x01520540, // c0x0008 (n0x0540-n0x0548) +
+ 0x01684548, // c0x0009 (n0x0548-n0x05a1) +
+ 0x016985a1, // c0x000a (n0x05a1-n0x05a6) +
+ 0x016ac5a6, // c0x000b (n0x05a6-n0x05ab) +
+ 0x016bc5ab, // c0x000c (n0x05ab-n0x05af) +
+ 0x016d85af, // c0x000d (n0x05af-n0x05b6) +
+ 0x016dc5b6, // c0x000e (n0x05b6-n0x05b7) +
+ 0x016ec5b7, // c0x000f (n0x05b7-n0x05bb) +
+ 0x017045bb, // c0x0010 (n0x05bb-n0x05c1) +
+ 0x017285c1, // c0x0011 (n0x05c1-n0x05ca) +
+ 0x0172c5ca, // c0x0012 (n0x05ca-n0x05cb) +
+ 0x017445cb, // c0x0013 (n0x05cb-n0x05d1) +
+ 0x017485d1, // c0x0014 (n0x05d1-n0x05d2) +
+ 0x017645d2, // c0x0015 (n0x05d2-n0x05d9) +
+ 0x017685d9, // c0x0016 (n0x05d9-n0x05da) +
+ 0x017b05da, // c0x0017 (n0x05da-n0x05ec) +
+ 0x017b45ec, // c0x0018 (n0x05ec-n0x05ed) +
+ 0x017d45ed, // c0x0019 (n0x05ed-n0x05f5) +
+ 0x017e85f5, // c0x001a (n0x05f5-n0x05fa) +
+ 0x017ec5fa, // c0x001b (n0x05fa-n0x05fb) +
+ 0x0181c5fb, // c0x001c (n0x05fb-n0x0607) +
+ 0x01848607, // c0x001d (n0x0607-n0x0612) +
+ 0x01870612, // c0x001e (n0x0612-n0x061c) +
+ 0x0187861c, // c0x001f (n0x061c-n0x061e) +
+ 0x0187c61e, // c0x0020 (n0x061e-n0x061f) +
+ 0x0191061f, // c0x0021 (n0x061f-n0x0644) +
+ 0x01924644, // c0x0022 (n0x0644-n0x0649) +
+ 0x01938649, // c0x0023 (n0x0649-n0x064e) +
+ 0x0195464e, // c0x0024 (n0x064e-n0x0655) +
+ 0x01964655, // c0x0025 (n0x0655-n0x0659) +
+ 0x01978659, // c0x0026 (n0x0659-n0x065e) +
+ 0x0199c65e, // c0x0027 (n0x065e-n0x0667) +
+ 0x01ab4667, // c0x0028 (n0x0667-n0x06ad) +
+ 0x01ab86ad, // c0x0029 (n0x06ad-n0x06ae) +
+ 0x01acc6ae, // c0x002a (n0x06ae-n0x06b3) +
+ 0x01ae06b3, // c0x002b (n0x06b3-n0x06b8) +
+ 0x01ae86b8, // c0x002c (n0x06b8-n0x06ba) +
+ 0x01af86ba, // c0x002d (n0x06ba-n0x06be) +
+ 0x01afc6be, // c0x002e (n0x06be-n0x06bf) +
+ 0x01b146bf, // c0x002f (n0x06bf-n0x06c5) +
+ 0x01b586c5, // c0x0030 (n0x06c5-n0x06d6) +
+ 0x01b686d6, // c0x0031 (n0x06d6-n0x06da) +
+ 0x01b6c6da, // c0x0032 (n0x06da-n0x06db) +
+ 0x01b706db, // c0x0033 (n0x06db-n0x06dc) +
+ 0x01b746dc, // c0x0034 (n0x06dc-n0x06dd) +
+ 0x01bb06dd, // c0x0035 (n0x06dd-n0x06ec) +
+ 0x61bb46ec, // c0x0036 (n0x06ec-n0x06ed)* o
+ 0x01bc86ed, // c0x0037 (n0x06ed-n0x06f2) +
+ 0x01bd86f2, // c0x0038 (n0x06f2-n0x06f6) +
+ 0x01c8c6f6, // c0x0039 (n0x06f6-n0x0723) +
+ 0x21c90723, // c0x003a (n0x0723-n0x0724) o
+ 0x01c94724, // c0x003b (n0x0724-n0x0725) +
+ 0x01c98725, // c0x003c (n0x0725-n0x0726) +
+ 0x21c9c726, // c0x003d (n0x0726-n0x0727) o
+ 0x21ca0727, // c0x003e (n0x0727-n0x0728) o
+ 0x01cd4728, // c0x003f (n0x0728-n0x0735) +
+ 0x01cd8735, // c0x0040 (n0x0735-n0x0736) +
+ 0x01ffc736, // c0x0041 (n0x0736-n0x07ff) +
+ 0x220447ff, // c0x0042 (n0x07ff-n0x0811) o
+ 0x02068811, // c0x0043 (n0x0811-n0x081a) +
+ 0x0207081a, // c0x0044 (n0x081a-n0x081c) +
+ 0x2207481c, // c0x0045 (n0x081c-n0x081d) o
+ 0x0209081d, // c0x0046 (n0x081d-n0x0824) +
+ 0x020a8824, // c0x0047 (n0x0824-n0x082a) +
+ 0x020ac82a, // c0x0048 (n0x082a-n0x082b) +
+ 0x020bc82b, // c0x0049 (n0x082b-n0x082f) +
+ 0x020c482f, // c0x004a (n0x082f-n0x0831) +
+ 0x220f8831, // c0x004b (n0x0831-n0x083e) o
+ 0x020fc83e, // c0x004c (n0x083e-n0x083f) +
+ 0x0210083f, // c0x004d (n0x083f-n0x0840) +
+ 0x02120840, // c0x004e (n0x0840-n0x0848) +
+ 0x02124848, // c0x004f (n0x0848-n0x0849) +
+ 0x02138849, // c0x0050 (n0x0849-n0x084e) +
+ 0x0216084e, // c0x0051 (n0x084e-n0x0858) +
+ 0x02180858, // c0x0052 (n0x0858-n0x0860) +
+ 0x021b0860, // c0x0053 (n0x0860-n0x086c) +
+ 0x021d886c, // c0x0054 (n0x086c-n0x0876) +
+ 0x021dc876, // c0x0055 (n0x0876-n0x0877) +
+ 0x02200877, // c0x0056 (n0x0877-n0x0880) +
+ 0x02204880, // c0x0057 (n0x0880-n0x0881) +
+ 0x02218881, // c0x0058 (n0x0881-n0x0886) +
+ 0x0221c886, // c0x0059 (n0x0886-n0x0887) +
+ 0x0223c887, // c0x005a (n0x0887-n0x088f) +
+ 0x0224888f, // c0x005b (n0x088f-n0x0892) +
+ 0x022a8892, // c0x005c (n0x0892-n0x08aa) +
+ 0x022c48aa, // c0x005d (n0x08aa-n0x08b1) +
+ 0x022d08b1, // c0x005e (n0x08b1-n0x08b4) +
+ 0x022e48b4, // c0x005f (n0x08b4-n0x08b9) +
+ 0x022fc8b9, // c0x0060 (n0x08b9-n0x08bf) +
+ 0x023108bf, // c0x0061 (n0x08bf-n0x08c4) +
+ 0x023288c4, // c0x0062 (n0x08c4-n0x08ca) +
+ 0x023408ca, // c0x0063 (n0x08ca-n0x08d0) +
+ 0x023588d0, // c0x0064 (n0x08d0-n0x08d6) +
+ 0x023748d6, // c0x0065 (n0x08d6-n0x08dd) +
+ 0x023808dd, // c0x0066 (n0x08dd-n0x08e0) +
+ 0x023e08e0, // c0x0067 (n0x08e0-n0x08f8) +
+ 0x023f88f8, // c0x0068 (n0x08f8-n0x08fe) +
+ 0x0240c8fe, // c0x0069 (n0x08fe-n0x0903) +
+ 0x02450903, // c0x006a (n0x0903-n0x0914) +
+ 0x024d0914, // c0x006b (n0x0914-n0x0934) +
+ 0x024fc934, // c0x006c (n0x0934-n0x093f) +
+ 0x0250093f, // c0x006d (n0x093f-n0x0940) +
+ 0x02508940, // c0x006e (n0x0940-n0x0942) +
+ 0x6250c942, // c0x006f (n0x0942-n0x0943)* o
+ 0x22510943, // c0x0070 (n0x0943-n0x0944) o
+ 0x0252c944, // c0x0071 (n0x0944-n0x094b) +
+ 0x0253494b, // c0x0072 (n0x094b-n0x094d) +
+ 0x0256894d, // c0x0073 (n0x094d-n0x095a) +
+ 0x0259095a, // c0x0074 (n0x095a-n0x0964) +
+ 0x02594964, // c0x0075 (n0x0964-n0x0965) +
+ 0x025a0965, // c0x0076 (n0x0965-n0x0968) +
+ 0x025b8968, // c0x0077 (n0x0968-n0x096e) +
+ 0x025dc96e, // c0x0078 (n0x096e-n0x0977) +
+ 0x025fc977, // c0x0079 (n0x0977-n0x097f) +
+ 0x02bc097f, // c0x007a (n0x097f-n0x0af0) +
+ 0x02bccaf0, // c0x007b (n0x0af0-n0x0af3) +
+ 0x02becaf3, // c0x007c (n0x0af3-n0x0afb) +
+ 0x02da8afb, // c0x007d (n0x0afb-n0x0b6a) +
+ 0x02e78b6a, // c0x007e (n0x0b6a-n0x0b9e) +
+ 0x02ee8b9e, // c0x007f (n0x0b9e-n0x0bba) +
+ 0x02f40bba, // c0x0080 (n0x0bba-n0x0bd0) +
+ 0x03028bd0, // c0x0081 (n0x0bd0-n0x0c0a) +
+ 0x03080c0a, // c0x0082 (n0x0c0a-n0x0c20) +
+ 0x030bcc20, // c0x0083 (n0x0c20-n0x0c2f) +
+ 0x031b8c2f, // c0x0084 (n0x0c2f-n0x0c6e) +
+ 0x03284c6e, // c0x0085 (n0x0c6e-n0x0ca1) +
+ 0x0331cca1, // c0x0086 (n0x0ca1-n0x0cc7) +
+ 0x033accc7, // c0x0087 (n0x0cc7-n0x0ceb) +
+ 0x03410ceb, // c0x0088 (n0x0ceb-n0x0d04) +
+ 0x03648d04, // c0x0089 (n0x0d04-n0x0d92) +
+ 0x03700d92, // c0x008a (n0x0d92-n0x0dc0) +
+ 0x037ccdc0, // c0x008b (n0x0dc0-n0x0df3) +
+ 0x03818df3, // c0x008c (n0x0df3-n0x0e06) +
+ 0x038a0e06, // c0x008d (n0x0e06-n0x0e28) +
+ 0x038dce28, // c0x008e (n0x0e28-n0x0e37) +
+ 0x0392ce37, // c0x008f (n0x0e37-n0x0e4b) +
+ 0x039a4e4b, // c0x0090 (n0x0e4b-n0x0e69) +
+ 0x639a8e69, // c0x0091 (n0x0e69-n0x0e6a)* o
+ 0x639ace6a, // c0x0092 (n0x0e6a-n0x0e6b)* o
+ 0x639b0e6b, // c0x0093 (n0x0e6b-n0x0e6c)* o
+ 0x03a2ce6c, // c0x0094 (n0x0e6c-n0x0e8b) +
+ 0x03a94e8b, // c0x0095 (n0x0e8b-n0x0ea5) +
+ 0x03b10ea5, // c0x0096 (n0x0ea5-n0x0ec4) +
+ 0x03b88ec4, // c0x0097 (n0x0ec4-n0x0ee2) +
+ 0x03c0cee2, // c0x0098 (n0x0ee2-n0x0f03) +
+ 0x03c78f03, // c0x0099 (n0x0f03-n0x0f1e) +
+ 0x03da4f1e, // c0x009a (n0x0f1e-n0x0f69) +
+ 0x03dfcf69, // c0x009b (n0x0f69-n0x0f7f) +
+ 0x63e00f7f, // c0x009c (n0x0f7f-n0x0f80)* o
+ 0x03e98f80, // c0x009d (n0x0f80-n0x0fa6) +
+ 0x03f20fa6, // c0x009e (n0x0fa6-n0x0fc8) +
+ 0x03f6cfc8, // c0x009f (n0x0fc8-n0x0fdb) +
+ 0x03fd4fdb, // c0x00a0 (n0x0fdb-n0x0ff5) +
+ 0x0407cff5, // c0x00a1 (n0x0ff5-n0x101f) +
+ 0x0414501f, // c0x00a2 (n0x101f-n0x1051) +
+ 0x041ad051, // c0x00a3 (n0x1051-n0x106b) +
+ 0x042c106b, // c0x00a4 (n0x106b-n0x10b0) +
+ 0x642c50b0, // c0x00a5 (n0x10b0-n0x10b1)* o
+ 0x642c90b1, // c0x00a6 (n0x10b1-n0x10b2)* o
+ 0x043250b2, // c0x00a7 (n0x10b2-n0x10c9) +
+ 0x043810c9, // c0x00a8 (n0x10c9-n0x10e0) +
+ 0x044110e0, // c0x00a9 (n0x10e0-n0x1104) +
+ 0x0448d104, // c0x00aa (n0x1104-n0x1123) +
+ 0x044d1123, // c0x00ab (n0x1123-n0x1134) +
+ 0x045b5134, // c0x00ac (n0x1134-n0x116d) +
+ 0x045e916d, // c0x00ad (n0x116d-n0x117a) +
+ 0x0464917a, // c0x00ae (n0x117a-n0x1192) +
+ 0x046bd192, // c0x00af (n0x1192-n0x11af) +
+ 0x047451af, // c0x00b0 (n0x11af-n0x11d1) +
+ 0x047851d1, // c0x00b1 (n0x11d1-n0x11e1) +
+ 0x047f51e1, // c0x00b2 (n0x11e1-n0x11fd) +
+ 0x647f91fd, // c0x00b3 (n0x11fd-n0x11fe)* o
+ 0x647fd1fe, // c0x00b4 (n0x11fe-n0x11ff)* o
+ 0x248011ff, // c0x00b5 (n0x11ff-n0x1200) o
+ 0x04819200, // c0x00b6 (n0x1200-n0x1206) +
+ 0x04835206, // c0x00b7 (n0x1206-n0x120d) +
+ 0x0487920d, // c0x00b8 (n0x120d-n0x121e) +
+ 0x0488921e, // c0x00b9 (n0x121e-n0x1222) +
+ 0x048a1222, // c0x00ba (n0x1222-n0x1228) +
+ 0x04919228, // c0x00bb (n0x1228-n0x1246) +
+ 0x0492d246, // c0x00bc (n0x1246-n0x124b) +
+ 0x0494524b, // c0x00bd (n0x124b-n0x1251) +
+ 0x04969251, // c0x00be (n0x1251-n0x125a) +
+ 0x0497d25a, // c0x00bf (n0x125a-n0x125f) +
+ 0x0499525f, // c0x00c0 (n0x125f-n0x1265) +
+ 0x04999265, // c0x00c1 (n0x1265-n0x1266) +
+ 0x049d5266, // c0x00c2 (n0x1266-n0x1275) +
+ 0x049e9275, // c0x00c3 (n0x1275-n0x127a) +
+ 0x049f127a, // c0x00c4 (n0x127a-n0x127c) +
+ 0x049f927c, // c0x00c5 (n0x127c-n0x127e) +
+ 0x049fd27e, // c0x00c6 (n0x127e-n0x127f) +
+ 0x04a2127f, // c0x00c7 (n0x127f-n0x1288) +
+ 0x04a45288, // c0x00c8 (n0x1288-n0x1291) +
+ 0x04a5d291, // c0x00c9 (n0x1291-n0x1297) +
+ 0x04a65297, // c0x00ca (n0x1297-n0x1299) +
+ 0x04a69299, // c0x00cb (n0x1299-n0x129a) +
+ 0x04a8929a, // c0x00cc (n0x129a-n0x12a2) +
+ 0x04aa92a2, // c0x00cd (n0x12a2-n0x12aa) +
+ 0x04ac92aa, // c0x00ce (n0x12aa-n0x12b2) +
+ 0x04ae52b2, // c0x00cf (n0x12b2-n0x12b9) +
+ 0x04af52b9, // c0x00d0 (n0x12b9-n0x12bd) +
+ 0x04b092bd, // c0x00d1 (n0x12bd-n0x12c2) +
+ 0x04b112c2, // c0x00d2 (n0x12c2-n0x12c4) +
+ 0x04b252c4, // c0x00d3 (n0x12c4-n0x12c9) +
+ 0x04b352c9, // c0x00d4 (n0x12c9-n0x12cd) +
+ 0x04b392cd, // c0x00d5 (n0x12cd-n0x12ce) +
+ 0x04b552ce, // c0x00d6 (n0x12ce-n0x12d5) +
+ 0x053e52d5, // c0x00d7 (n0x12d5-n0x14f9) +
+ 0x0541d4f9, // c0x00d8 (n0x14f9-n0x1507) +
+ 0x05449507, // c0x00d9 (n0x1507-n0x1512) +
+ 0x05461512, // c0x00da (n0x1512-n0x1518) +
+ 0x05481518, // c0x00db (n0x1518-n0x1520) +
+ 0x65485520, // c0x00dc (n0x1520-n0x1521)* o
+ 0x054c9521, // c0x00dd (n0x1521-n0x1532) +
+ 0x054d1532, // c0x00de (n0x1532-n0x1534) +
+ 0x254d5534, // c0x00df (n0x1534-n0x1535) o
+ 0x254d9535, // c0x00e0 (n0x1535-n0x1536) o
+ 0x054dd536, // c0x00e1 (n0x1536-n0x1537) +
+ 0x055a1537, // c0x00e2 (n0x1537-n0x1568) +
+ 0x255a5568, // c0x00e3 (n0x1568-n0x1569) o
+ 0x255ad569, // c0x00e4 (n0x1569-n0x156b) o
+ 0x255b556b, // c0x00e5 (n0x156b-n0x156d) o
+ 0x255c156d, // c0x00e6 (n0x156d-n0x1570) o
+ 0x055e9570, // c0x00e7 (n0x1570-n0x157a) +
+ 0x0560d57a, // c0x00e8 (n0x157a-n0x1583) +
+ 0x05611583, // c0x00e9 (n0x1583-n0x1584) +
+ 0x0561d584, // c0x00ea (n0x1584-n0x1587) +
+ 0x06175587, // c0x00eb (n0x1587-n0x185d) +
+ 0x0617985d, // c0x00ec (n0x185d-n0x185e) +
+ 0x0617d85e, // c0x00ed (n0x185e-n0x185f) +
+ 0x2618185f, // c0x00ee (n0x185f-n0x1860) o
+ 0x06185860, // c0x00ef (n0x1860-n0x1861) +
+ 0x26189861, // c0x00f0 (n0x1861-n0x1862) o
+ 0x0618d862, // c0x00f1 (n0x1862-n0x1863) +
+ 0x26199863, // c0x00f2 (n0x1863-n0x1866) o
+ 0x0619d866, // c0x00f3 (n0x1866-n0x1867) +
+ 0x061a1867, // c0x00f4 (n0x1867-n0x1868) +
+ 0x261a5868, // c0x00f5 (n0x1868-n0x1869) o
+ 0x061a9869, // c0x00f6 (n0x1869-n0x186a) +
+ 0x261b186a, // c0x00f7 (n0x186a-n0x186c) o
+ 0x061b586c, // c0x00f8 (n0x186c-n0x186d) +
+ 0x061b986d, // c0x00f9 (n0x186d-n0x186e) +
+ 0x261c986e, // c0x00fa (n0x186e-n0x1872) o
+ 0x061cd872, // c0x00fb (n0x1872-n0x1873) +
+ 0x061d1873, // c0x00fc (n0x1873-n0x1874) +
+ 0x061d5874, // c0x00fd (n0x1874-n0x1875) +
+ 0x061d9875, // c0x00fe (n0x1875-n0x1876) +
+ 0x261dd876, // c0x00ff (n0x1876-n0x1877) o
+ 0x061e1877, // c0x0100 (n0x1877-n0x1878) +
+ 0x061e5878, // c0x0101 (n0x1878-n0x1879) +
+ 0x061e9879, // c0x0102 (n0x1879-n0x187a) +
+ 0x061ed87a, // c0x0103 (n0x187a-n0x187b) +
+ 0x261f587b, // c0x0104 (n0x187b-n0x187d) o
+ 0x061f987d, // c0x0105 (n0x187d-n0x187e) +
+ 0x061fd87e, // c0x0106 (n0x187e-n0x187f) +
+ 0x0620187f, // c0x0107 (n0x187f-n0x1880) +
+ 0x26205880, // c0x0108 (n0x1880-n0x1881) o
+ 0x06209881, // c0x0109 (n0x1881-n0x1882) +
+ 0x26211882, // c0x010a (n0x1882-n0x1884) o
+ 0x26215884, // c0x010b (n0x1884-n0x1885) o
+ 0x06231885, // c0x010c (n0x1885-n0x188c) +
+ 0x0623d88c, // c0x010d (n0x188c-n0x188f) +
+ 0x0627d88f, // c0x010e (n0x188f-n0x189f) +
+ 0x0628189f, // c0x010f (n0x189f-n0x18a0) +
+ 0x062a58a0, // c0x0110 (n0x18a0-n0x18a9) +
+ 0x0638d8a9, // c0x0111 (n0x18a9-n0x18e3) +
+ 0x263958e3, // c0x0112 (n0x18e3-n0x18e5) o
+ 0x263998e5, // c0x0113 (n0x18e5-n0x18e6) o
+ 0x2639d8e6, // c0x0114 (n0x18e6-n0x18e7) o
+ 0x063a58e7, // c0x0115 (n0x18e7-n0x18e9) +
+ 0x064818e9, // c0x0116 (n0x18e9-n0x1920) +
+ 0x064ad920, // c0x0117 (n0x1920-n0x192b) +
+ 0x064cd92b, // c0x0118 (n0x192b-n0x1933) +
+ 0x064d9933, // c0x0119 (n0x1933-n0x1936) +
+ 0x064f9936, // c0x011a (n0x1936-n0x193e) +
+ 0x0653193e, // c0x011b (n0x193e-n0x194c) +
+ 0x067c594c, // c0x011c (n0x194c-n0x19f1) +
+ 0x068819f1, // c0x011d (n0x19f1-n0x1a20) +
+ 0x06895a20, // c0x011e (n0x1a20-n0x1a25) +
+ 0x068c9a25, // c0x011f (n0x1a25-n0x1a32) +
+ 0x068e5a32, // c0x0120 (n0x1a32-n0x1a39) +
+ 0x06901a39, // c0x0121 (n0x1a39-n0x1a40) +
+ 0x06925a40, // c0x0122 (n0x1a40-n0x1a49) +
+ 0x0693da49, // c0x0123 (n0x1a49-n0x1a4f) +
+ 0x06959a4f, // c0x0124 (n0x1a4f-n0x1a56) +
+ 0x0697da56, // c0x0125 (n0x1a56-n0x1a5f) +
+ 0x0698da5f, // c0x0126 (n0x1a5f-n0x1a63) +
+ 0x069bda63, // c0x0127 (n0x1a63-n0x1a6f) +
+ 0x069d9a6f, // c0x0128 (n0x1a6f-n0x1a76) +
+ 0x06be9a76, // c0x0129 (n0x1a76-n0x1afa) +
+ 0x06c0dafa, // c0x012a (n0x1afa-n0x1b03) +
+ 0x06c2db03, // c0x012b (n0x1b03-n0x1b0b) +
+ 0x06c41b0b, // c0x012c (n0x1b0b-n0x1b10) +
+ 0x06c55b10, // c0x012d (n0x1b10-n0x1b15) +
+ 0x06c75b15, // c0x012e (n0x1b15-n0x1b1d) +
+ 0x06d19b1d, // c0x012f (n0x1b1d-n0x1b46) +
+ 0x06d35b46, // c0x0130 (n0x1b46-n0x1b4d) +
+ 0x06d4db4d, // c0x0131 (n0x1b4d-n0x1b53) +
+ 0x06d51b53, // c0x0132 (n0x1b53-n0x1b54) +
+ 0x06d55b54, // c0x0133 (n0x1b54-n0x1b55) +
+ 0x06d69b55, // c0x0134 (n0x1b55-n0x1b5a) +
+ 0x06d89b5a, // c0x0135 (n0x1b5a-n0x1b62) +
+ 0x06d95b62, // c0x0136 (n0x1b62-n0x1b65) +
+ 0x06dc5b65, // c0x0137 (n0x1b65-n0x1b71) +
+ 0x06e45b71, // c0x0138 (n0x1b71-n0x1b91) +
+ 0x06e59b91, // c0x0139 (n0x1b91-n0x1b96) +
+ 0x06e5db96, // c0x013a (n0x1b96-n0x1b97) +
+ 0x06e75b97, // c0x013b (n0x1b97-n0x1b9d) +
+ 0x06e81b9d, // c0x013c (n0x1b9d-n0x1ba0) +
+ 0x06e85ba0, // c0x013d (n0x1ba0-n0x1ba1) +
+ 0x06ea1ba1, // c0x013e (n0x1ba1-n0x1ba8) +
+ 0x06eddba8, // c0x013f (n0x1ba8-n0x1bb7) +
+ 0x06ee1bb7, // c0x0140 (n0x1bb7-n0x1bb8) +
+ 0x06f01bb8, // c0x0141 (n0x1bb8-n0x1bc0) +
+ 0x06f51bc0, // c0x0142 (n0x1bc0-n0x1bd4) +
+ 0x06f69bd4, // c0x0143 (n0x1bd4-n0x1bda) +
+ 0x06fbdbda, // c0x0144 (n0x1bda-n0x1bef) +
+ 0x06fc1bef, // c0x0145 (n0x1bef-n0x1bf0) +
+ 0x06fc5bf0, // c0x0146 (n0x1bf0-n0x1bf1) +
+ 0x07009bf1, // c0x0147 (n0x1bf1-n0x1c02) +
+ 0x07019c02, // c0x0148 (n0x1c02-n0x1c06) +
+ 0x07051c06, // c0x0149 (n0x1c06-n0x1c14) +
+ 0x07081c14, // c0x014a (n0x1c14-n0x1c20) +
+ 0x071b9c20, // c0x014b (n0x1c20-n0x1c6e) +
+ 0x071ddc6e, // c0x014c (n0x1c6e-n0x1c77) +
+ 0x07209c77, // c0x014d (n0x1c77-n0x1c82) +
+ 0x0720dc82, // c0x014e (n0x1c82-n0x1c83) +
+ 0x07211c83, // c0x014f (n0x1c83-n0x1c84) +
+ 0x0730dc84, // c0x0150 (n0x1c84-n0x1cc3) +
+ 0x07319cc3, // c0x0151 (n0x1cc3-n0x1cc6) +
+ 0x07325cc6, // c0x0152 (n0x1cc6-n0x1cc9) +
+ 0x07331cc9, // c0x0153 (n0x1cc9-n0x1ccc) +
+ 0x0733dccc, // c0x0154 (n0x1ccc-n0x1ccf) +
+ 0x07349ccf, // c0x0155 (n0x1ccf-n0x1cd2) +
+ 0x07355cd2, // c0x0156 (n0x1cd2-n0x1cd5) +
+ 0x07361cd5, // c0x0157 (n0x1cd5-n0x1cd8) +
+ 0x0736dcd8, // c0x0158 (n0x1cd8-n0x1cdb) +
+ 0x07379cdb, // c0x0159 (n0x1cdb-n0x1cde) +
+ 0x07385cde, // c0x015a (n0x1cde-n0x1ce1) +
+ 0x07391ce1, // c0x015b (n0x1ce1-n0x1ce4) +
+ 0x0739dce4, // c0x015c (n0x1ce4-n0x1ce7) +
+ 0x073a9ce7, // c0x015d (n0x1ce7-n0x1cea) +
+ 0x073b1cea, // c0x015e (n0x1cea-n0x1cec) +
+ 0x073bdcec, // c0x015f (n0x1cec-n0x1cef) +
+ 0x073c9cef, // c0x0160 (n0x1cef-n0x1cf2) +
+ 0x073d5cf2, // c0x0161 (n0x1cf2-n0x1cf5) +
+ 0x073e1cf5, // c0x0162 (n0x1cf5-n0x1cf8) +
+ 0x073edcf8, // c0x0163 (n0x1cf8-n0x1cfb) +
+ 0x073f9cfb, // c0x0164 (n0x1cfb-n0x1cfe) +
+ 0x07405cfe, // c0x0165 (n0x1cfe-n0x1d01) +
+ 0x07411d01, // c0x0166 (n0x1d01-n0x1d04) +
+ 0x0741dd04, // c0x0167 (n0x1d04-n0x1d07) +
+ 0x07429d07, // c0x0168 (n0x1d07-n0x1d0a) +
+ 0x07435d0a, // c0x0169 (n0x1d0a-n0x1d0d) +
+ 0x07441d0d, // c0x016a (n0x1d0d-n0x1d10) +
+ 0x0744dd10, // c0x016b (n0x1d10-n0x1d13) +
+ 0x07459d13, // c0x016c (n0x1d13-n0x1d16) +
+ 0x07465d16, // c0x016d (n0x1d16-n0x1d19) +
+ 0x07471d19, // c0x016e (n0x1d19-n0x1d1c) +
+ 0x0747dd1c, // c0x016f (n0x1d1c-n0x1d1f) +
+ 0x07485d1f, // c0x0170 (n0x1d1f-n0x1d21) +
+ 0x07491d21, // c0x0171 (n0x1d21-n0x1d24) +
+ 0x0749dd24, // c0x0172 (n0x1d24-n0x1d27) +
+ 0x074a9d27, // c0x0173 (n0x1d27-n0x1d2a) +
+ 0x074b5d2a, // c0x0174 (n0x1d2a-n0x1d2d) +
+ 0x074c1d2d, // c0x0175 (n0x1d2d-n0x1d30) +
+ 0x074cdd30, // c0x0176 (n0x1d30-n0x1d33) +
+ 0x074d9d33, // c0x0177 (n0x1d33-n0x1d36) +
+ 0x074e5d36, // c0x0178 (n0x1d36-n0x1d39) +
+ 0x074f1d39, // c0x0179 (n0x1d39-n0x1d3c) +
+ 0x074fdd3c, // c0x017a (n0x1d3c-n0x1d3f) +
+ 0x07509d3f, // c0x017b (n0x1d3f-n0x1d42) +
+ 0x07515d42, // c0x017c (n0x1d42-n0x1d45) +
+ 0x07521d45, // c0x017d (n0x1d45-n0x1d48) +
+ 0x07529d48, // c0x017e (n0x1d48-n0x1d4a) +
+ 0x07535d4a, // c0x017f (n0x1d4a-n0x1d4d) +
+ 0x07541d4d, // c0x0180 (n0x1d4d-n0x1d50) +
+ 0x0754dd50, // c0x0181 (n0x1d50-n0x1d53) +
+ 0x07559d53, // c0x0182 (n0x1d53-n0x1d56) +
+ 0x07565d56, // c0x0183 (n0x1d56-n0x1d59) +
+ 0x07571d59, // c0x0184 (n0x1d59-n0x1d5c) +
+ 0x0757dd5c, // c0x0185 (n0x1d5c-n0x1d5f) +
+ 0x07589d5f, // c0x0186 (n0x1d5f-n0x1d62) +
+ 0x0758dd62, // c0x0187 (n0x1d62-n0x1d63) +
+ 0x07599d63, // c0x0188 (n0x1d63-n0x1d66) +
+ 0x075b1d66, // c0x0189 (n0x1d66-n0x1d6c) +
+ 0x075b5d6c, // c0x018a (n0x1d6c-n0x1d6d) +
+ 0x075c5d6d, // c0x018b (n0x1d6d-n0x1d71) +
+ 0x075ddd71, // c0x018c (n0x1d71-n0x1d77) +
+ 0x07621d77, // c0x018d (n0x1d77-n0x1d88) +
+ 0x07635d88, // c0x018e (n0x1d88-n0x1d8d) +
+ 0x07669d8d, // c0x018f (n0x1d8d-n0x1d9a) +
+ 0x07679d9a, // c0x0190 (n0x1d9a-n0x1d9e) +
+ 0x07695d9e, // c0x0191 (n0x1d9e-n0x1da5) +
+ 0x076adda5, // c0x0192 (n0x1da5-n0x1dab) +
+ 0x276f1dab, // c0x0193 (n0x1dab-n0x1dbc) o
+ 0x076f5dbc, // c0x0194 (n0x1dbc-n0x1dbd) +
+}
+
+// max children 404 (capacity 511)
+// max text offset 26074 (capacity 32767)
+// max text length 36 (capacity 63)
+// max hi 7613 (capacity 16383)
+// max lo 7612 (capacity 16383)
diff --git a/publicsuffix/table_test.go b/publicsuffix/table_test.go
new file mode 100644
index 0000000..ec14b2d
--- /dev/null
+++ b/publicsuffix/table_test.go
@@ -0,0 +1,15206 @@
+// generated by go run gen.go; DO NOT EDIT
+
+package publicsuffix
+
+var rules = [...]string{
+ "ac",
+ "com.ac",
+ "edu.ac",
+ "gov.ac",
+ "net.ac",
+ "mil.ac",
+ "org.ac",
+ "ad",
+ "nom.ad",
+ "ae",
+ "co.ae",
+ "net.ae",
+ "org.ae",
+ "sch.ae",
+ "ac.ae",
+ "gov.ae",
+ "mil.ae",
+ "aero",
+ "accident-investigation.aero",
+ "accident-prevention.aero",
+ "aerobatic.aero",
+ "aeroclub.aero",
+ "aerodrome.aero",
+ "agents.aero",
+ "aircraft.aero",
+ "airline.aero",
+ "airport.aero",
+ "air-surveillance.aero",
+ "airtraffic.aero",
+ "air-traffic-control.aero",
+ "ambulance.aero",
+ "amusement.aero",
+ "association.aero",
+ "author.aero",
+ "ballooning.aero",
+ "broker.aero",
+ "caa.aero",
+ "cargo.aero",
+ "catering.aero",
+ "certification.aero",
+ "championship.aero",
+ "charter.aero",
+ "civilaviation.aero",
+ "club.aero",
+ "conference.aero",
+ "consultant.aero",
+ "consulting.aero",
+ "control.aero",
+ "council.aero",
+ "crew.aero",
+ "design.aero",
+ "dgca.aero",
+ "educator.aero",
+ "emergency.aero",
+ "engine.aero",
+ "engineer.aero",
+ "entertainment.aero",
+ "equipment.aero",
+ "exchange.aero",
+ "express.aero",
+ "federation.aero",
+ "flight.aero",
+ "freight.aero",
+ "fuel.aero",
+ "gliding.aero",
+ "government.aero",
+ "groundhandling.aero",
+ "group.aero",
+ "hanggliding.aero",
+ "homebuilt.aero",
+ "insurance.aero",
+ "journal.aero",
+ "journalist.aero",
+ "leasing.aero",
+ "logistics.aero",
+ "magazine.aero",
+ "maintenance.aero",
+ "marketplace.aero",
+ "media.aero",
+ "microlight.aero",
+ "modelling.aero",
+ "navigation.aero",
+ "parachuting.aero",
+ "paragliding.aero",
+ "passenger-association.aero",
+ "pilot.aero",
+ "press.aero",
+ "production.aero",
+ "recreation.aero",
+ "repbody.aero",
+ "res.aero",
+ "research.aero",
+ "rotorcraft.aero",
+ "safety.aero",
+ "scientist.aero",
+ "services.aero",
+ "show.aero",
+ "skydiving.aero",
+ "software.aero",
+ "student.aero",
+ "taxi.aero",
+ "trader.aero",
+ "trading.aero",
+ "trainer.aero",
+ "union.aero",
+ "workinggroup.aero",
+ "works.aero",
+ "af",
+ "gov.af",
+ "com.af",
+ "org.af",
+ "net.af",
+ "edu.af",
+ "ag",
+ "com.ag",
+ "org.ag",
+ "net.ag",
+ "co.ag",
+ "nom.ag",
+ "ai",
+ "off.ai",
+ "com.ai",
+ "net.ai",
+ "org.ai",
+ "al",
+ "com.al",
+ "edu.al",
+ "gov.al",
+ "mil.al",
+ "net.al",
+ "org.al",
+ "am",
+ "an",
+ "com.an",
+ "net.an",
+ "org.an",
+ "edu.an",
+ "ao",
+ "ed.ao",
+ "gv.ao",
+ "og.ao",
+ "co.ao",
+ "pb.ao",
+ "it.ao",
+ "aq",
+ "ar",
+ "com.ar",
+ "edu.ar",
+ "gob.ar",
+ "gov.ar",
+ "int.ar",
+ "mil.ar",
+ "net.ar",
+ "org.ar",
+ "tur.ar",
+ "arpa",
+ "e164.arpa",
+ "in-addr.arpa",
+ "ip6.arpa",
+ "iris.arpa",
+ "uri.arpa",
+ "urn.arpa",
+ "as",
+ "gov.as",
+ "asia",
+ "at",
+ "ac.at",
+ "co.at",
+ "gv.at",
+ "or.at",
+ "au",
+ "com.au",
+ "net.au",
+ "org.au",
+ "edu.au",
+ "gov.au",
+ "asn.au",
+ "id.au",
+ "info.au",
+ "conf.au",
+ "oz.au",
+ "act.au",
+ "nsw.au",
+ "nt.au",
+ "qld.au",
+ "sa.au",
+ "tas.au",
+ "vic.au",
+ "wa.au",
+ "act.edu.au",
+ "nsw.edu.au",
+ "nt.edu.au",
+ "qld.edu.au",
+ "sa.edu.au",
+ "tas.edu.au",
+ "vic.edu.au",
+ "wa.edu.au",
+ "qld.gov.au",
+ "sa.gov.au",
+ "tas.gov.au",
+ "vic.gov.au",
+ "wa.gov.au",
+ "aw",
+ "com.aw",
+ "ax",
+ "az",
+ "com.az",
+ "net.az",
+ "int.az",
+ "gov.az",
+ "org.az",
+ "edu.az",
+ "info.az",
+ "pp.az",
+ "mil.az",
+ "name.az",
+ "pro.az",
+ "biz.az",
+ "ba",
+ "org.ba",
+ "net.ba",
+ "edu.ba",
+ "gov.ba",
+ "mil.ba",
+ "unsa.ba",
+ "unbi.ba",
+ "co.ba",
+ "com.ba",
+ "rs.ba",
+ "bb",
+ "biz.bb",
+ "co.bb",
+ "com.bb",
+ "edu.bb",
+ "gov.bb",
+ "info.bb",
+ "net.bb",
+ "org.bb",
+ "store.bb",
+ "tv.bb",
+ "*.bd",
+ "be",
+ "ac.be",
+ "bf",
+ "gov.bf",
+ "bg",
+ "a.bg",
+ "b.bg",
+ "c.bg",
+ "d.bg",
+ "e.bg",
+ "f.bg",
+ "g.bg",
+ "h.bg",
+ "i.bg",
+ "j.bg",
+ "k.bg",
+ "l.bg",
+ "m.bg",
+ "n.bg",
+ "o.bg",
+ "p.bg",
+ "q.bg",
+ "r.bg",
+ "s.bg",
+ "t.bg",
+ "u.bg",
+ "v.bg",
+ "w.bg",
+ "x.bg",
+ "y.bg",
+ "z.bg",
+ "0.bg",
+ "1.bg",
+ "2.bg",
+ "3.bg",
+ "4.bg",
+ "5.bg",
+ "6.bg",
+ "7.bg",
+ "8.bg",
+ "9.bg",
+ "bh",
+ "com.bh",
+ "edu.bh",
+ "net.bh",
+ "org.bh",
+ "gov.bh",
+ "bi",
+ "co.bi",
+ "com.bi",
+ "edu.bi",
+ "or.bi",
+ "org.bi",
+ "biz",
+ "bj",
+ "asso.bj",
+ "barreau.bj",
+ "gouv.bj",
+ "bm",
+ "com.bm",
+ "edu.bm",
+ "gov.bm",
+ "net.bm",
+ "org.bm",
+ "*.bn",
+ "bo",
+ "com.bo",
+ "edu.bo",
+ "gov.bo",
+ "gob.bo",
+ "int.bo",
+ "org.bo",
+ "net.bo",
+ "mil.bo",
+ "tv.bo",
+ "br",
+ "adm.br",
+ "adv.br",
+ "agr.br",
+ "am.br",
+ "arq.br",
+ "art.br",
+ "ato.br",
+ "b.br",
+ "bio.br",
+ "blog.br",
+ "bmd.br",
+ "cim.br",
+ "cng.br",
+ "cnt.br",
+ "com.br",
+ "coop.br",
+ "ecn.br",
+ "eco.br",
+ "edu.br",
+ "emp.br",
+ "eng.br",
+ "esp.br",
+ "etc.br",
+ "eti.br",
+ "far.br",
+ "flog.br",
+ "fm.br",
+ "fnd.br",
+ "fot.br",
+ "fst.br",
+ "g12.br",
+ "ggf.br",
+ "gov.br",
+ "imb.br",
+ "ind.br",
+ "inf.br",
+ "jor.br",
+ "jus.br",
+ "leg.br",
+ "lel.br",
+ "mat.br",
+ "med.br",
+ "mil.br",
+ "mp.br",
+ "mus.br",
+ "net.br",
+ "*.nom.br",
+ "not.br",
+ "ntr.br",
+ "odo.br",
+ "org.br",
+ "ppg.br",
+ "pro.br",
+ "psc.br",
+ "psi.br",
+ "qsl.br",
+ "radio.br",
+ "rec.br",
+ "slg.br",
+ "srv.br",
+ "taxi.br",
+ "teo.br",
+ "tmp.br",
+ "trd.br",
+ "tur.br",
+ "tv.br",
+ "vet.br",
+ "vlog.br",
+ "wiki.br",
+ "zlg.br",
+ "bs",
+ "com.bs",
+ "net.bs",
+ "org.bs",
+ "edu.bs",
+ "gov.bs",
+ "bt",
+ "com.bt",
+ "edu.bt",
+ "gov.bt",
+ "net.bt",
+ "org.bt",
+ "bv",
+ "bw",
+ "co.bw",
+ "org.bw",
+ "by",
+ "gov.by",
+ "mil.by",
+ "com.by",
+ "of.by",
+ "bz",
+ "com.bz",
+ "net.bz",
+ "org.bz",
+ "edu.bz",
+ "gov.bz",
+ "ca",
+ "ab.ca",
+ "bc.ca",
+ "mb.ca",
+ "nb.ca",
+ "nf.ca",
+ "nl.ca",
+ "ns.ca",
+ "nt.ca",
+ "nu.ca",
+ "on.ca",
+ "pe.ca",
+ "qc.ca",
+ "sk.ca",
+ "yk.ca",
+ "gc.ca",
+ "cat",
+ "cc",
+ "cd",
+ "gov.cd",
+ "cf",
+ "cg",
+ "ch",
+ "ci",
+ "org.ci",
+ "or.ci",
+ "com.ci",
+ "co.ci",
+ "edu.ci",
+ "ed.ci",
+ "ac.ci",
+ "net.ci",
+ "go.ci",
+ "asso.ci",
+ "xn--aroport-bya.ci",
+ "int.ci",
+ "presse.ci",
+ "md.ci",
+ "gouv.ci",
+ "*.ck",
+ "!www.ck",
+ "cl",
+ "gov.cl",
+ "gob.cl",
+ "co.cl",
+ "mil.cl",
+ "cm",
+ "co.cm",
+ "com.cm",
+ "gov.cm",
+ "net.cm",
+ "cn",
+ "ac.cn",
+ "com.cn",
+ "edu.cn",
+ "gov.cn",
+ "net.cn",
+ "org.cn",
+ "mil.cn",
+ "xn--55qx5d.cn",
+ "xn--io0a7i.cn",
+ "xn--od0alg.cn",
+ "ah.cn",
+ "bj.cn",
+ "cq.cn",
+ "fj.cn",
+ "gd.cn",
+ "gs.cn",
+ "gz.cn",
+ "gx.cn",
+ "ha.cn",
+ "hb.cn",
+ "he.cn",
+ "hi.cn",
+ "hl.cn",
+ "hn.cn",
+ "jl.cn",
+ "js.cn",
+ "jx.cn",
+ "ln.cn",
+ "nm.cn",
+ "nx.cn",
+ "qh.cn",
+ "sc.cn",
+ "sd.cn",
+ "sh.cn",
+ "sn.cn",
+ "sx.cn",
+ "tj.cn",
+ "xj.cn",
+ "xz.cn",
+ "yn.cn",
+ "zj.cn",
+ "hk.cn",
+ "mo.cn",
+ "tw.cn",
+ "co",
+ "arts.co",
+ "com.co",
+ "edu.co",
+ "firm.co",
+ "gov.co",
+ "info.co",
+ "int.co",
+ "mil.co",
+ "net.co",
+ "nom.co",
+ "org.co",
+ "rec.co",
+ "web.co",
+ "com",
+ "coop",
+ "cr",
+ "ac.cr",
+ "co.cr",
+ "ed.cr",
+ "fi.cr",
+ "go.cr",
+ "or.cr",
+ "sa.cr",
+ "cu",
+ "com.cu",
+ "edu.cu",
+ "org.cu",
+ "net.cu",
+ "gov.cu",
+ "inf.cu",
+ "cv",
+ "cw",
+ "com.cw",
+ "edu.cw",
+ "net.cw",
+ "org.cw",
+ "cx",
+ "gov.cx",
+ "ac.cy",
+ "biz.cy",
+ "com.cy",
+ "ekloges.cy",
+ "gov.cy",
+ "ltd.cy",
+ "name.cy",
+ "net.cy",
+ "org.cy",
+ "parliament.cy",
+ "press.cy",
+ "pro.cy",
+ "tm.cy",
+ "cz",
+ "de",
+ "dj",
+ "dk",
+ "dm",
+ "com.dm",
+ "net.dm",
+ "org.dm",
+ "edu.dm",
+ "gov.dm",
+ "do",
+ "art.do",
+ "com.do",
+ "edu.do",
+ "gob.do",
+ "gov.do",
+ "mil.do",
+ "net.do",
+ "org.do",
+ "sld.do",
+ "web.do",
+ "dz",
+ "com.dz",
+ "org.dz",
+ "net.dz",
+ "gov.dz",
+ "edu.dz",
+ "asso.dz",
+ "pol.dz",
+ "art.dz",
+ "ec",
+ "com.ec",
+ "info.ec",
+ "net.ec",
+ "fin.ec",
+ "k12.ec",
+ "med.ec",
+ "pro.ec",
+ "org.ec",
+ "edu.ec",
+ "gov.ec",
+ "gob.ec",
+ "mil.ec",
+ "edu",
+ "ee",
+ "edu.ee",
+ "gov.ee",
+ "riik.ee",
+ "lib.ee",
+ "med.ee",
+ "com.ee",
+ "pri.ee",
+ "aip.ee",
+ "org.ee",
+ "fie.ee",
+ "eg",
+ "com.eg",
+ "edu.eg",
+ "eun.eg",
+ "gov.eg",
+ "mil.eg",
+ "name.eg",
+ "net.eg",
+ "org.eg",
+ "sci.eg",
+ "*.er",
+ "es",
+ "com.es",
+ "nom.es",
+ "org.es",
+ "gob.es",
+ "edu.es",
+ "et",
+ "com.et",
+ "gov.et",
+ "org.et",
+ "edu.et",
+ "biz.et",
+ "name.et",
+ "info.et",
+ "net.et",
+ "eu",
+ "fi",
+ "aland.fi",
+ "*.fj",
+ "*.fk",
+ "fm",
+ "fo",
+ "fr",
+ "com.fr",
+ "asso.fr",
+ "nom.fr",
+ "prd.fr",
+ "presse.fr",
+ "tm.fr",
+ "aeroport.fr",
+ "assedic.fr",
+ "avocat.fr",
+ "avoues.fr",
+ "cci.fr",
+ "chambagri.fr",
+ "chirurgiens-dentistes.fr",
+ "experts-comptables.fr",
+ "geometre-expert.fr",
+ "gouv.fr",
+ "greta.fr",
+ "huissier-justice.fr",
+ "medecin.fr",
+ "notaires.fr",
+ "pharmacien.fr",
+ "port.fr",
+ "veterinaire.fr",
+ "ga",
+ "gb",
+ "gd",
+ "ge",
+ "com.ge",
+ "edu.ge",
+ "gov.ge",
+ "org.ge",
+ "mil.ge",
+ "net.ge",
+ "pvt.ge",
+ "gf",
+ "gg",
+ "co.gg",
+ "net.gg",
+ "org.gg",
+ "gh",
+ "com.gh",
+ "edu.gh",
+ "gov.gh",
+ "org.gh",
+ "mil.gh",
+ "gi",
+ "com.gi",
+ "ltd.gi",
+ "gov.gi",
+ "mod.gi",
+ "edu.gi",
+ "org.gi",
+ "gl",
+ "co.gl",
+ "com.gl",
+ "edu.gl",
+ "net.gl",
+ "org.gl",
+ "gm",
+ "gn",
+ "ac.gn",
+ "com.gn",
+ "edu.gn",
+ "gov.gn",
+ "org.gn",
+ "net.gn",
+ "gov",
+ "gp",
+ "com.gp",
+ "net.gp",
+ "mobi.gp",
+ "edu.gp",
+ "org.gp",
+ "asso.gp",
+ "gq",
+ "gr",
+ "com.gr",
+ "edu.gr",
+ "net.gr",
+ "org.gr",
+ "gov.gr",
+ "gs",
+ "gt",
+ "com.gt",
+ "edu.gt",
+ "gob.gt",
+ "ind.gt",
+ "mil.gt",
+ "net.gt",
+ "org.gt",
+ "*.gu",
+ "gw",
+ "gy",
+ "co.gy",
+ "com.gy",
+ "net.gy",
+ "hk",
+ "com.hk",
+ "edu.hk",
+ "gov.hk",
+ "idv.hk",
+ "net.hk",
+ "org.hk",
+ "xn--55qx5d.hk",
+ "xn--wcvs22d.hk",
+ "xn--lcvr32d.hk",
+ "xn--mxtq1m.hk",
+ "xn--gmqw5a.hk",
+ "xn--ciqpn.hk",
+ "xn--gmq050i.hk",
+ "xn--zf0avx.hk",
+ "xn--io0a7i.hk",
+ "xn--mk0axi.hk",
+ "xn--od0alg.hk",
+ "xn--od0aq3b.hk",
+ "xn--tn0ag.hk",
+ "xn--uc0atv.hk",
+ "xn--uc0ay4a.hk",
+ "hm",
+ "hn",
+ "com.hn",
+ "edu.hn",
+ "org.hn",
+ "net.hn",
+ "mil.hn",
+ "gob.hn",
+ "hr",
+ "iz.hr",
+ "from.hr",
+ "name.hr",
+ "com.hr",
+ "ht",
+ "com.ht",
+ "shop.ht",
+ "firm.ht",
+ "info.ht",
+ "adult.ht",
+ "net.ht",
+ "pro.ht",
+ "org.ht",
+ "med.ht",
+ "art.ht",
+ "coop.ht",
+ "pol.ht",
+ "asso.ht",
+ "edu.ht",
+ "rel.ht",
+ "gouv.ht",
+ "perso.ht",
+ "hu",
+ "co.hu",
+ "info.hu",
+ "org.hu",
+ "priv.hu",
+ "sport.hu",
+ "tm.hu",
+ "2000.hu",
+ "agrar.hu",
+ "bolt.hu",
+ "casino.hu",
+ "city.hu",
+ "erotica.hu",
+ "erotika.hu",
+ "film.hu",
+ "forum.hu",
+ "games.hu",
+ "hotel.hu",
+ "ingatlan.hu",
+ "jogasz.hu",
+ "konyvelo.hu",
+ "lakas.hu",
+ "media.hu",
+ "news.hu",
+ "reklam.hu",
+ "sex.hu",
+ "shop.hu",
+ "suli.hu",
+ "szex.hu",
+ "tozsde.hu",
+ "utazas.hu",
+ "video.hu",
+ "id",
+ "ac.id",
+ "biz.id",
+ "co.id",
+ "desa.id",
+ "go.id",
+ "mil.id",
+ "my.id",
+ "net.id",
+ "or.id",
+ "sch.id",
+ "web.id",
+ "ie",
+ "gov.ie",
+ "*.il",
+ "im",
+ "ac.im",
+ "co.im",
+ "com.im",
+ "ltd.co.im",
+ "net.im",
+ "org.im",
+ "plc.co.im",
+ "tt.im",
+ "tv.im",
+ "in",
+ "co.in",
+ "firm.in",
+ "net.in",
+ "org.in",
+ "gen.in",
+ "ind.in",
+ "nic.in",
+ "ac.in",
+ "edu.in",
+ "res.in",
+ "gov.in",
+ "mil.in",
+ "info",
+ "int",
+ "eu.int",
+ "io",
+ "com.io",
+ "iq",
+ "gov.iq",
+ "edu.iq",
+ "mil.iq",
+ "com.iq",
+ "org.iq",
+ "net.iq",
+ "ir",
+ "ac.ir",
+ "co.ir",
+ "gov.ir",
+ "id.ir",
+ "net.ir",
+ "org.ir",
+ "sch.ir",
+ "xn--mgba3a4f16a.ir",
+ "xn--mgba3a4fra.ir",
+ "is",
+ "net.is",
+ "com.is",
+ "edu.is",
+ "gov.is",
+ "org.is",
+ "int.is",
+ "it",
+ "gov.it",
+ "edu.it",
+ "abr.it",
+ "abruzzo.it",
+ "aosta-valley.it",
+ "aostavalley.it",
+ "bas.it",
+ "basilicata.it",
+ "cal.it",
+ "calabria.it",
+ "cam.it",
+ "campania.it",
+ "emilia-romagna.it",
+ "emiliaromagna.it",
+ "emr.it",
+ "friuli-v-giulia.it",
+ "friuli-ve-giulia.it",
+ "friuli-vegiulia.it",
+ "friuli-venezia-giulia.it",
+ "friuli-veneziagiulia.it",
+ "friuli-vgiulia.it",
+ "friuliv-giulia.it",
+ "friulive-giulia.it",
+ "friulivegiulia.it",
+ "friulivenezia-giulia.it",
+ "friuliveneziagiulia.it",
+ "friulivgiulia.it",
+ "fvg.it",
+ "laz.it",
+ "lazio.it",
+ "lig.it",
+ "liguria.it",
+ "lom.it",
+ "lombardia.it",
+ "lombardy.it",
+ "lucania.it",
+ "mar.it",
+ "marche.it",
+ "mol.it",
+ "molise.it",
+ "piedmont.it",
+ "piemonte.it",
+ "pmn.it",
+ "pug.it",
+ "puglia.it",
+ "sar.it",
+ "sardegna.it",
+ "sardinia.it",
+ "sic.it",
+ "sicilia.it",
+ "sicily.it",
+ "taa.it",
+ "tos.it",
+ "toscana.it",
+ "trentino-a-adige.it",
+ "trentino-aadige.it",
+ "trentino-alto-adige.it",
+ "trentino-altoadige.it",
+ "trentino-s-tirol.it",
+ "trentino-stirol.it",
+ "trentino-sud-tirol.it",
+ "trentino-sudtirol.it",
+ "trentino-sued-tirol.it",
+ "trentino-suedtirol.it",
+ "trentinoa-adige.it",
+ "trentinoaadige.it",
+ "trentinoalto-adige.it",
+ "trentinoaltoadige.it",
+ "trentinos-tirol.it",
+ "trentinostirol.it",
+ "trentinosud-tirol.it",
+ "trentinosudtirol.it",
+ "trentinosued-tirol.it",
+ "trentinosuedtirol.it",
+ "tuscany.it",
+ "umb.it",
+ "umbria.it",
+ "val-d-aosta.it",
+ "val-daosta.it",
+ "vald-aosta.it",
+ "valdaosta.it",
+ "valle-aosta.it",
+ "valle-d-aosta.it",
+ "valle-daosta.it",
+ "valleaosta.it",
+ "valled-aosta.it",
+ "valledaosta.it",
+ "vallee-aoste.it",
+ "valleeaoste.it",
+ "vao.it",
+ "vda.it",
+ "ven.it",
+ "veneto.it",
+ "ag.it",
+ "agrigento.it",
+ "al.it",
+ "alessandria.it",
+ "alto-adige.it",
+ "altoadige.it",
+ "an.it",
+ "ancona.it",
+ "andria-barletta-trani.it",
+ "andria-trani-barletta.it",
+ "andriabarlettatrani.it",
+ "andriatranibarletta.it",
+ "ao.it",
+ "aosta.it",
+ "aoste.it",
+ "ap.it",
+ "aq.it",
+ "aquila.it",
+ "ar.it",
+ "arezzo.it",
+ "ascoli-piceno.it",
+ "ascolipiceno.it",
+ "asti.it",
+ "at.it",
+ "av.it",
+ "avellino.it",
+ "ba.it",
+ "balsan.it",
+ "bari.it",
+ "barletta-trani-andria.it",
+ "barlettatraniandria.it",
+ "belluno.it",
+ "benevento.it",
+ "bergamo.it",
+ "bg.it",
+ "bi.it",
+ "biella.it",
+ "bl.it",
+ "bn.it",
+ "bo.it",
+ "bologna.it",
+ "bolzano.it",
+ "bozen.it",
+ "br.it",
+ "brescia.it",
+ "brindisi.it",
+ "bs.it",
+ "bt.it",
+ "bz.it",
+ "ca.it",
+ "cagliari.it",
+ "caltanissetta.it",
+ "campidano-medio.it",
+ "campidanomedio.it",
+ "campobasso.it",
+ "carbonia-iglesias.it",
+ "carboniaiglesias.it",
+ "carrara-massa.it",
+ "carraramassa.it",
+ "caserta.it",
+ "catania.it",
+ "catanzaro.it",
+ "cb.it",
+ "ce.it",
+ "cesena-forli.it",
+ "cesenaforli.it",
+ "ch.it",
+ "chieti.it",
+ "ci.it",
+ "cl.it",
+ "cn.it",
+ "co.it",
+ "como.it",
+ "cosenza.it",
+ "cr.it",
+ "cremona.it",
+ "crotone.it",
+ "cs.it",
+ "ct.it",
+ "cuneo.it",
+ "cz.it",
+ "dell-ogliastra.it",
+ "dellogliastra.it",
+ "en.it",
+ "enna.it",
+ "fc.it",
+ "fe.it",
+ "fermo.it",
+ "ferrara.it",
+ "fg.it",
+ "fi.it",
+ "firenze.it",
+ "florence.it",
+ "fm.it",
+ "foggia.it",
+ "forli-cesena.it",
+ "forlicesena.it",
+ "fr.it",
+ "frosinone.it",
+ "ge.it",
+ "genoa.it",
+ "genova.it",
+ "go.it",
+ "gorizia.it",
+ "gr.it",
+ "grosseto.it",
+ "iglesias-carbonia.it",
+ "iglesiascarbonia.it",
+ "im.it",
+ "imperia.it",
+ "is.it",
+ "isernia.it",
+ "kr.it",
+ "la-spezia.it",
+ "laquila.it",
+ "laspezia.it",
+ "latina.it",
+ "lc.it",
+ "le.it",
+ "lecce.it",
+ "lecco.it",
+ "li.it",
+ "livorno.it",
+ "lo.it",
+ "lodi.it",
+ "lt.it",
+ "lu.it",
+ "lucca.it",
+ "macerata.it",
+ "mantova.it",
+ "massa-carrara.it",
+ "massacarrara.it",
+ "matera.it",
+ "mb.it",
+ "mc.it",
+ "me.it",
+ "medio-campidano.it",
+ "mediocampidano.it",
+ "messina.it",
+ "mi.it",
+ "milan.it",
+ "milano.it",
+ "mn.it",
+ "mo.it",
+ "modena.it",
+ "monza-brianza.it",
+ "monza-e-della-brianza.it",
+ "monza.it",
+ "monzabrianza.it",
+ "monzaebrianza.it",
+ "monzaedellabrianza.it",
+ "ms.it",
+ "mt.it",
+ "na.it",
+ "naples.it",
+ "napoli.it",
+ "no.it",
+ "novara.it",
+ "nu.it",
+ "nuoro.it",
+ "og.it",
+ "ogliastra.it",
+ "olbia-tempio.it",
+ "olbiatempio.it",
+ "or.it",
+ "oristano.it",
+ "ot.it",
+ "pa.it",
+ "padova.it",
+ "padua.it",
+ "palermo.it",
+ "parma.it",
+ "pavia.it",
+ "pc.it",
+ "pd.it",
+ "pe.it",
+ "perugia.it",
+ "pesaro-urbino.it",
+ "pesarourbino.it",
+ "pescara.it",
+ "pg.it",
+ "pi.it",
+ "piacenza.it",
+ "pisa.it",
+ "pistoia.it",
+ "pn.it",
+ "po.it",
+ "pordenone.it",
+ "potenza.it",
+ "pr.it",
+ "prato.it",
+ "pt.it",
+ "pu.it",
+ "pv.it",
+ "pz.it",
+ "ra.it",
+ "ragusa.it",
+ "ravenna.it",
+ "rc.it",
+ "re.it",
+ "reggio-calabria.it",
+ "reggio-emilia.it",
+ "reggiocalabria.it",
+ "reggioemilia.it",
+ "rg.it",
+ "ri.it",
+ "rieti.it",
+ "rimini.it",
+ "rm.it",
+ "rn.it",
+ "ro.it",
+ "roma.it",
+ "rome.it",
+ "rovigo.it",
+ "sa.it",
+ "salerno.it",
+ "sassari.it",
+ "savona.it",
+ "si.it",
+ "siena.it",
+ "siracusa.it",
+ "so.it",
+ "sondrio.it",
+ "sp.it",
+ "sr.it",
+ "ss.it",
+ "suedtirol.it",
+ "sv.it",
+ "ta.it",
+ "taranto.it",
+ "te.it",
+ "tempio-olbia.it",
+ "tempioolbia.it",
+ "teramo.it",
+ "terni.it",
+ "tn.it",
+ "to.it",
+ "torino.it",
+ "tp.it",
+ "tr.it",
+ "trani-andria-barletta.it",
+ "trani-barletta-andria.it",
+ "traniandriabarletta.it",
+ "tranibarlettaandria.it",
+ "trapani.it",
+ "trentino.it",
+ "trento.it",
+ "treviso.it",
+ "trieste.it",
+ "ts.it",
+ "turin.it",
+ "tv.it",
+ "ud.it",
+ "udine.it",
+ "urbino-pesaro.it",
+ "urbinopesaro.it",
+ "va.it",
+ "varese.it",
+ "vb.it",
+ "vc.it",
+ "ve.it",
+ "venezia.it",
+ "venice.it",
+ "verbania.it",
+ "vercelli.it",
+ "verona.it",
+ "vi.it",
+ "vibo-valentia.it",
+ "vibovalentia.it",
+ "vicenza.it",
+ "viterbo.it",
+ "vr.it",
+ "vs.it",
+ "vt.it",
+ "vv.it",
+ "je",
+ "co.je",
+ "net.je",
+ "org.je",
+ "*.jm",
+ "jo",
+ "com.jo",
+ "org.jo",
+ "net.jo",
+ "edu.jo",
+ "sch.jo",
+ "gov.jo",
+ "mil.jo",
+ "name.jo",
+ "jobs",
+ "jp",
+ "ac.jp",
+ "ad.jp",
+ "co.jp",
+ "ed.jp",
+ "go.jp",
+ "gr.jp",
+ "lg.jp",
+ "ne.jp",
+ "or.jp",
+ "aichi.jp",
+ "akita.jp",
+ "aomori.jp",
+ "chiba.jp",
+ "ehime.jp",
+ "fukui.jp",
+ "fukuoka.jp",
+ "fukushima.jp",
+ "gifu.jp",
+ "gunma.jp",
+ "hiroshima.jp",
+ "hokkaido.jp",
+ "hyogo.jp",
+ "ibaraki.jp",
+ "ishikawa.jp",
+ "iwate.jp",
+ "kagawa.jp",
+ "kagoshima.jp",
+ "kanagawa.jp",
+ "kochi.jp",
+ "kumamoto.jp",
+ "kyoto.jp",
+ "mie.jp",
+ "miyagi.jp",
+ "miyazaki.jp",
+ "nagano.jp",
+ "nagasaki.jp",
+ "nara.jp",
+ "niigata.jp",
+ "oita.jp",
+ "okayama.jp",
+ "okinawa.jp",
+ "osaka.jp",
+ "saga.jp",
+ "saitama.jp",
+ "shiga.jp",
+ "shimane.jp",
+ "shizuoka.jp",
+ "tochigi.jp",
+ "tokushima.jp",
+ "tokyo.jp",
+ "tottori.jp",
+ "toyama.jp",
+ "wakayama.jp",
+ "yamagata.jp",
+ "yamaguchi.jp",
+ "yamanashi.jp",
+ "xn--4pvxs.jp",
+ "xn--vgu402c.jp",
+ "xn--c3s14m.jp",
+ "xn--f6qx53a.jp",
+ "xn--8pvr4u.jp",
+ "xn--uist22h.jp",
+ "xn--djrs72d6uy.jp",
+ "xn--mkru45i.jp",
+ "xn--0trq7p7nn.jp",
+ "xn--8ltr62k.jp",
+ "xn--2m4a15e.jp",
+ "xn--efvn9s.jp",
+ "xn--32vp30h.jp",
+ "xn--4it797k.jp",
+ "xn--1lqs71d.jp",
+ "xn--5rtp49c.jp",
+ "xn--5js045d.jp",
+ "xn--ehqz56n.jp",
+ "xn--1lqs03n.jp",
+ "xn--qqqt11m.jp",
+ "xn--kbrq7o.jp",
+ "xn--pssu33l.jp",
+ "xn--ntsq17g.jp",
+ "xn--uisz3g.jp",
+ "xn--6btw5a.jp",
+ "xn--1ctwo.jp",
+ "xn--6orx2r.jp",
+ "xn--rht61e.jp",
+ "xn--rht27z.jp",
+ "xn--djty4k.jp",
+ "xn--nit225k.jp",
+ "xn--rht3d.jp",
+ "xn--klty5x.jp",
+ "xn--kltx9a.jp",
+ "xn--kltp7d.jp",
+ "xn--uuwu58a.jp",
+ "xn--zbx025d.jp",
+ "xn--ntso0iqx3a.jp",
+ "xn--elqq16h.jp",
+ "xn--4it168d.jp",
+ "xn--klt787d.jp",
+ "xn--rny31h.jp",
+ "xn--7t0a264c.jp",
+ "xn--5rtq34k.jp",
+ "xn--k7yn95e.jp",
+ "xn--tor131o.jp",
+ "xn--d5qv7z876c.jp",
+ "*.kawasaki.jp",
+ "*.kitakyushu.jp",
+ "*.kobe.jp",
+ "*.nagoya.jp",
+ "*.sapporo.jp",
+ "*.sendai.jp",
+ "*.yokohama.jp",
+ "!city.kawasaki.jp",
+ "!city.kitakyushu.jp",
+ "!city.kobe.jp",
+ "!city.nagoya.jp",
+ "!city.sapporo.jp",
+ "!city.sendai.jp",
+ "!city.yokohama.jp",
+ "aisai.aichi.jp",
+ "ama.aichi.jp",
+ "anjo.aichi.jp",
+ "asuke.aichi.jp",
+ "chiryu.aichi.jp",
+ "chita.aichi.jp",
+ "fuso.aichi.jp",
+ "gamagori.aichi.jp",
+ "handa.aichi.jp",
+ "hazu.aichi.jp",
+ "hekinan.aichi.jp",
+ "higashiura.aichi.jp",
+ "ichinomiya.aichi.jp",
+ "inazawa.aichi.jp",
+ "inuyama.aichi.jp",
+ "isshiki.aichi.jp",
+ "iwakura.aichi.jp",
+ "kanie.aichi.jp",
+ "kariya.aichi.jp",
+ "kasugai.aichi.jp",
+ "kira.aichi.jp",
+ "kiyosu.aichi.jp",
+ "komaki.aichi.jp",
+ "konan.aichi.jp",
+ "kota.aichi.jp",
+ "mihama.aichi.jp",
+ "miyoshi.aichi.jp",
+ "nishio.aichi.jp",
+ "nisshin.aichi.jp",
+ "obu.aichi.jp",
+ "oguchi.aichi.jp",
+ "oharu.aichi.jp",
+ "okazaki.aichi.jp",
+ "owariasahi.aichi.jp",
+ "seto.aichi.jp",
+ "shikatsu.aichi.jp",
+ "shinshiro.aichi.jp",
+ "shitara.aichi.jp",
+ "tahara.aichi.jp",
+ "takahama.aichi.jp",
+ "tobishima.aichi.jp",
+ "toei.aichi.jp",
+ "togo.aichi.jp",
+ "tokai.aichi.jp",
+ "tokoname.aichi.jp",
+ "toyoake.aichi.jp",
+ "toyohashi.aichi.jp",
+ "toyokawa.aichi.jp",
+ "toyone.aichi.jp",
+ "toyota.aichi.jp",
+ "tsushima.aichi.jp",
+ "yatomi.aichi.jp",
+ "akita.akita.jp",
+ "daisen.akita.jp",
+ "fujisato.akita.jp",
+ "gojome.akita.jp",
+ "hachirogata.akita.jp",
+ "happou.akita.jp",
+ "higashinaruse.akita.jp",
+ "honjo.akita.jp",
+ "honjyo.akita.jp",
+ "ikawa.akita.jp",
+ "kamikoani.akita.jp",
+ "kamioka.akita.jp",
+ "katagami.akita.jp",
+ "kazuno.akita.jp",
+ "kitaakita.akita.jp",
+ "kosaka.akita.jp",
+ "kyowa.akita.jp",
+ "misato.akita.jp",
+ "mitane.akita.jp",
+ "moriyoshi.akita.jp",
+ "nikaho.akita.jp",
+ "noshiro.akita.jp",
+ "odate.akita.jp",
+ "oga.akita.jp",
+ "ogata.akita.jp",
+ "semboku.akita.jp",
+ "yokote.akita.jp",
+ "yurihonjo.akita.jp",
+ "aomori.aomori.jp",
+ "gonohe.aomori.jp",
+ "hachinohe.aomori.jp",
+ "hashikami.aomori.jp",
+ "hiranai.aomori.jp",
+ "hirosaki.aomori.jp",
+ "itayanagi.aomori.jp",
+ "kuroishi.aomori.jp",
+ "misawa.aomori.jp",
+ "mutsu.aomori.jp",
+ "nakadomari.aomori.jp",
+ "noheji.aomori.jp",
+ "oirase.aomori.jp",
+ "owani.aomori.jp",
+ "rokunohe.aomori.jp",
+ "sannohe.aomori.jp",
+ "shichinohe.aomori.jp",
+ "shingo.aomori.jp",
+ "takko.aomori.jp",
+ "towada.aomori.jp",
+ "tsugaru.aomori.jp",
+ "tsuruta.aomori.jp",
+ "abiko.chiba.jp",
+ "asahi.chiba.jp",
+ "chonan.chiba.jp",
+ "chosei.chiba.jp",
+ "choshi.chiba.jp",
+ "chuo.chiba.jp",
+ "funabashi.chiba.jp",
+ "futtsu.chiba.jp",
+ "hanamigawa.chiba.jp",
+ "ichihara.chiba.jp",
+ "ichikawa.chiba.jp",
+ "ichinomiya.chiba.jp",
+ "inzai.chiba.jp",
+ "isumi.chiba.jp",
+ "kamagaya.chiba.jp",
+ "kamogawa.chiba.jp",
+ "kashiwa.chiba.jp",
+ "katori.chiba.jp",
+ "katsuura.chiba.jp",
+ "kimitsu.chiba.jp",
+ "kisarazu.chiba.jp",
+ "kozaki.chiba.jp",
+ "kujukuri.chiba.jp",
+ "kyonan.chiba.jp",
+ "matsudo.chiba.jp",
+ "midori.chiba.jp",
+ "mihama.chiba.jp",
+ "minamiboso.chiba.jp",
+ "mobara.chiba.jp",
+ "mutsuzawa.chiba.jp",
+ "nagara.chiba.jp",
+ "nagareyama.chiba.jp",
+ "narashino.chiba.jp",
+ "narita.chiba.jp",
+ "noda.chiba.jp",
+ "oamishirasato.chiba.jp",
+ "omigawa.chiba.jp",
+ "onjuku.chiba.jp",
+ "otaki.chiba.jp",
+ "sakae.chiba.jp",
+ "sakura.chiba.jp",
+ "shimofusa.chiba.jp",
+ "shirako.chiba.jp",
+ "shiroi.chiba.jp",
+ "shisui.chiba.jp",
+ "sodegaura.chiba.jp",
+ "sosa.chiba.jp",
+ "tako.chiba.jp",
+ "tateyama.chiba.jp",
+ "togane.chiba.jp",
+ "tohnosho.chiba.jp",
+ "tomisato.chiba.jp",
+ "urayasu.chiba.jp",
+ "yachimata.chiba.jp",
+ "yachiyo.chiba.jp",
+ "yokaichiba.chiba.jp",
+ "yokoshibahikari.chiba.jp",
+ "yotsukaido.chiba.jp",
+ "ainan.ehime.jp",
+ "honai.ehime.jp",
+ "ikata.ehime.jp",
+ "imabari.ehime.jp",
+ "iyo.ehime.jp",
+ "kamijima.ehime.jp",
+ "kihoku.ehime.jp",
+ "kumakogen.ehime.jp",
+ "masaki.ehime.jp",
+ "matsuno.ehime.jp",
+ "matsuyama.ehime.jp",
+ "namikata.ehime.jp",
+ "niihama.ehime.jp",
+ "ozu.ehime.jp",
+ "saijo.ehime.jp",
+ "seiyo.ehime.jp",
+ "shikokuchuo.ehime.jp",
+ "tobe.ehime.jp",
+ "toon.ehime.jp",
+ "uchiko.ehime.jp",
+ "uwajima.ehime.jp",
+ "yawatahama.ehime.jp",
+ "echizen.fukui.jp",
+ "eiheiji.fukui.jp",
+ "fukui.fukui.jp",
+ "ikeda.fukui.jp",
+ "katsuyama.fukui.jp",
+ "mihama.fukui.jp",
+ "minamiechizen.fukui.jp",
+ "obama.fukui.jp",
+ "ohi.fukui.jp",
+ "ono.fukui.jp",
+ "sabae.fukui.jp",
+ "sakai.fukui.jp",
+ "takahama.fukui.jp",
+ "tsuruga.fukui.jp",
+ "wakasa.fukui.jp",
+ "ashiya.fukuoka.jp",
+ "buzen.fukuoka.jp",
+ "chikugo.fukuoka.jp",
+ "chikuho.fukuoka.jp",
+ "chikujo.fukuoka.jp",
+ "chikushino.fukuoka.jp",
+ "chikuzen.fukuoka.jp",
+ "chuo.fukuoka.jp",
+ "dazaifu.fukuoka.jp",
+ "fukuchi.fukuoka.jp",
+ "hakata.fukuoka.jp",
+ "higashi.fukuoka.jp",
+ "hirokawa.fukuoka.jp",
+ "hisayama.fukuoka.jp",
+ "iizuka.fukuoka.jp",
+ "inatsuki.fukuoka.jp",
+ "kaho.fukuoka.jp",
+ "kasuga.fukuoka.jp",
+ "kasuya.fukuoka.jp",
+ "kawara.fukuoka.jp",
+ "keisen.fukuoka.jp",
+ "koga.fukuoka.jp",
+ "kurate.fukuoka.jp",
+ "kurogi.fukuoka.jp",
+ "kurume.fukuoka.jp",
+ "minami.fukuoka.jp",
+ "miyako.fukuoka.jp",
+ "miyama.fukuoka.jp",
+ "miyawaka.fukuoka.jp",
+ "mizumaki.fukuoka.jp",
+ "munakata.fukuoka.jp",
+ "nakagawa.fukuoka.jp",
+ "nakama.fukuoka.jp",
+ "nishi.fukuoka.jp",
+ "nogata.fukuoka.jp",
+ "ogori.fukuoka.jp",
+ "okagaki.fukuoka.jp",
+ "okawa.fukuoka.jp",
+ "oki.fukuoka.jp",
+ "omuta.fukuoka.jp",
+ "onga.fukuoka.jp",
+ "onojo.fukuoka.jp",
+ "oto.fukuoka.jp",
+ "saigawa.fukuoka.jp",
+ "sasaguri.fukuoka.jp",
+ "shingu.fukuoka.jp",
+ "shinyoshitomi.fukuoka.jp",
+ "shonai.fukuoka.jp",
+ "soeda.fukuoka.jp",
+ "sue.fukuoka.jp",
+ "tachiarai.fukuoka.jp",
+ "tagawa.fukuoka.jp",
+ "takata.fukuoka.jp",
+ "toho.fukuoka.jp",
+ "toyotsu.fukuoka.jp",
+ "tsuiki.fukuoka.jp",
+ "ukiha.fukuoka.jp",
+ "umi.fukuoka.jp",
+ "usui.fukuoka.jp",
+ "yamada.fukuoka.jp",
+ "yame.fukuoka.jp",
+ "yanagawa.fukuoka.jp",
+ "yukuhashi.fukuoka.jp",
+ "aizubange.fukushima.jp",
+ "aizumisato.fukushima.jp",
+ "aizuwakamatsu.fukushima.jp",
+ "asakawa.fukushima.jp",
+ "bandai.fukushima.jp",
+ "date.fukushima.jp",
+ "fukushima.fukushima.jp",
+ "furudono.fukushima.jp",
+ "futaba.fukushima.jp",
+ "hanawa.fukushima.jp",
+ "higashi.fukushima.jp",
+ "hirata.fukushima.jp",
+ "hirono.fukushima.jp",
+ "iitate.fukushima.jp",
+ "inawashiro.fukushima.jp",
+ "ishikawa.fukushima.jp",
+ "iwaki.fukushima.jp",
+ "izumizaki.fukushima.jp",
+ "kagamiishi.fukushima.jp",
+ "kaneyama.fukushima.jp",
+ "kawamata.fukushima.jp",
+ "kitakata.fukushima.jp",
+ "kitashiobara.fukushima.jp",
+ "koori.fukushima.jp",
+ "koriyama.fukushima.jp",
+ "kunimi.fukushima.jp",
+ "miharu.fukushima.jp",
+ "mishima.fukushima.jp",
+ "namie.fukushima.jp",
+ "nango.fukushima.jp",
+ "nishiaizu.fukushima.jp",
+ "nishigo.fukushima.jp",
+ "okuma.fukushima.jp",
+ "omotego.fukushima.jp",
+ "ono.fukushima.jp",
+ "otama.fukushima.jp",
+ "samegawa.fukushima.jp",
+ "shimogo.fukushima.jp",
+ "shirakawa.fukushima.jp",
+ "showa.fukushima.jp",
+ "soma.fukushima.jp",
+ "sukagawa.fukushima.jp",
+ "taishin.fukushima.jp",
+ "tamakawa.fukushima.jp",
+ "tanagura.fukushima.jp",
+ "tenei.fukushima.jp",
+ "yabuki.fukushima.jp",
+ "yamato.fukushima.jp",
+ "yamatsuri.fukushima.jp",
+ "yanaizu.fukushima.jp",
+ "yugawa.fukushima.jp",
+ "anpachi.gifu.jp",
+ "ena.gifu.jp",
+ "gifu.gifu.jp",
+ "ginan.gifu.jp",
+ "godo.gifu.jp",
+ "gujo.gifu.jp",
+ "hashima.gifu.jp",
+ "hichiso.gifu.jp",
+ "hida.gifu.jp",
+ "higashishirakawa.gifu.jp",
+ "ibigawa.gifu.jp",
+ "ikeda.gifu.jp",
+ "kakamigahara.gifu.jp",
+ "kani.gifu.jp",
+ "kasahara.gifu.jp",
+ "kasamatsu.gifu.jp",
+ "kawaue.gifu.jp",
+ "kitagata.gifu.jp",
+ "mino.gifu.jp",
+ "minokamo.gifu.jp",
+ "mitake.gifu.jp",
+ "mizunami.gifu.jp",
+ "motosu.gifu.jp",
+ "nakatsugawa.gifu.jp",
+ "ogaki.gifu.jp",
+ "sakahogi.gifu.jp",
+ "seki.gifu.jp",
+ "sekigahara.gifu.jp",
+ "shirakawa.gifu.jp",
+ "tajimi.gifu.jp",
+ "takayama.gifu.jp",
+ "tarui.gifu.jp",
+ "toki.gifu.jp",
+ "tomika.gifu.jp",
+ "wanouchi.gifu.jp",
+ "yamagata.gifu.jp",
+ "yaotsu.gifu.jp",
+ "yoro.gifu.jp",
+ "annaka.gunma.jp",
+ "chiyoda.gunma.jp",
+ "fujioka.gunma.jp",
+ "higashiagatsuma.gunma.jp",
+ "isesaki.gunma.jp",
+ "itakura.gunma.jp",
+ "kanna.gunma.jp",
+ "kanra.gunma.jp",
+ "katashina.gunma.jp",
+ "kawaba.gunma.jp",
+ "kiryu.gunma.jp",
+ "kusatsu.gunma.jp",
+ "maebashi.gunma.jp",
+ "meiwa.gunma.jp",
+ "midori.gunma.jp",
+ "minakami.gunma.jp",
+ "naganohara.gunma.jp",
+ "nakanojo.gunma.jp",
+ "nanmoku.gunma.jp",
+ "numata.gunma.jp",
+ "oizumi.gunma.jp",
+ "ora.gunma.jp",
+ "ota.gunma.jp",
+ "shibukawa.gunma.jp",
+ "shimonita.gunma.jp",
+ "shinto.gunma.jp",
+ "showa.gunma.jp",
+ "takasaki.gunma.jp",
+ "takayama.gunma.jp",
+ "tamamura.gunma.jp",
+ "tatebayashi.gunma.jp",
+ "tomioka.gunma.jp",
+ "tsukiyono.gunma.jp",
+ "tsumagoi.gunma.jp",
+ "ueno.gunma.jp",
+ "yoshioka.gunma.jp",
+ "asaminami.hiroshima.jp",
+ "daiwa.hiroshima.jp",
+ "etajima.hiroshima.jp",
+ "fuchu.hiroshima.jp",
+ "fukuyama.hiroshima.jp",
+ "hatsukaichi.hiroshima.jp",
+ "higashihiroshima.hiroshima.jp",
+ "hongo.hiroshima.jp",
+ "jinsekikogen.hiroshima.jp",
+ "kaita.hiroshima.jp",
+ "kui.hiroshima.jp",
+ "kumano.hiroshima.jp",
+ "kure.hiroshima.jp",
+ "mihara.hiroshima.jp",
+ "miyoshi.hiroshima.jp",
+ "naka.hiroshima.jp",
+ "onomichi.hiroshima.jp",
+ "osakikamijima.hiroshima.jp",
+ "otake.hiroshima.jp",
+ "saka.hiroshima.jp",
+ "sera.hiroshima.jp",
+ "seranishi.hiroshima.jp",
+ "shinichi.hiroshima.jp",
+ "shobara.hiroshima.jp",
+ "takehara.hiroshima.jp",
+ "abashiri.hokkaido.jp",
+ "abira.hokkaido.jp",
+ "aibetsu.hokkaido.jp",
+ "akabira.hokkaido.jp",
+ "akkeshi.hokkaido.jp",
+ "asahikawa.hokkaido.jp",
+ "ashibetsu.hokkaido.jp",
+ "ashoro.hokkaido.jp",
+ "assabu.hokkaido.jp",
+ "atsuma.hokkaido.jp",
+ "bibai.hokkaido.jp",
+ "biei.hokkaido.jp",
+ "bifuka.hokkaido.jp",
+ "bihoro.hokkaido.jp",
+ "biratori.hokkaido.jp",
+ "chippubetsu.hokkaido.jp",
+ "chitose.hokkaido.jp",
+ "date.hokkaido.jp",
+ "ebetsu.hokkaido.jp",
+ "embetsu.hokkaido.jp",
+ "eniwa.hokkaido.jp",
+ "erimo.hokkaido.jp",
+ "esan.hokkaido.jp",
+ "esashi.hokkaido.jp",
+ "fukagawa.hokkaido.jp",
+ "fukushima.hokkaido.jp",
+ "furano.hokkaido.jp",
+ "furubira.hokkaido.jp",
+ "haboro.hokkaido.jp",
+ "hakodate.hokkaido.jp",
+ "hamatonbetsu.hokkaido.jp",
+ "hidaka.hokkaido.jp",
+ "higashikagura.hokkaido.jp",
+ "higashikawa.hokkaido.jp",
+ "hiroo.hokkaido.jp",
+ "hokuryu.hokkaido.jp",
+ "hokuto.hokkaido.jp",
+ "honbetsu.hokkaido.jp",
+ "horokanai.hokkaido.jp",
+ "horonobe.hokkaido.jp",
+ "ikeda.hokkaido.jp",
+ "imakane.hokkaido.jp",
+ "ishikari.hokkaido.jp",
+ "iwamizawa.hokkaido.jp",
+ "iwanai.hokkaido.jp",
+ "kamifurano.hokkaido.jp",
+ "kamikawa.hokkaido.jp",
+ "kamishihoro.hokkaido.jp",
+ "kamisunagawa.hokkaido.jp",
+ "kamoenai.hokkaido.jp",
+ "kayabe.hokkaido.jp",
+ "kembuchi.hokkaido.jp",
+ "kikonai.hokkaido.jp",
+ "kimobetsu.hokkaido.jp",
+ "kitahiroshima.hokkaido.jp",
+ "kitami.hokkaido.jp",
+ "kiyosato.hokkaido.jp",
+ "koshimizu.hokkaido.jp",
+ "kunneppu.hokkaido.jp",
+ "kuriyama.hokkaido.jp",
+ "kuromatsunai.hokkaido.jp",
+ "kushiro.hokkaido.jp",
+ "kutchan.hokkaido.jp",
+ "kyowa.hokkaido.jp",
+ "mashike.hokkaido.jp",
+ "matsumae.hokkaido.jp",
+ "mikasa.hokkaido.jp",
+ "minamifurano.hokkaido.jp",
+ "mombetsu.hokkaido.jp",
+ "moseushi.hokkaido.jp",
+ "mukawa.hokkaido.jp",
+ "muroran.hokkaido.jp",
+ "naie.hokkaido.jp",
+ "nakagawa.hokkaido.jp",
+ "nakasatsunai.hokkaido.jp",
+ "nakatombetsu.hokkaido.jp",
+ "nanae.hokkaido.jp",
+ "nanporo.hokkaido.jp",
+ "nayoro.hokkaido.jp",
+ "nemuro.hokkaido.jp",
+ "niikappu.hokkaido.jp",
+ "niki.hokkaido.jp",
+ "nishiokoppe.hokkaido.jp",
+ "noboribetsu.hokkaido.jp",
+ "numata.hokkaido.jp",
+ "obihiro.hokkaido.jp",
+ "obira.hokkaido.jp",
+ "oketo.hokkaido.jp",
+ "okoppe.hokkaido.jp",
+ "otaru.hokkaido.jp",
+ "otobe.hokkaido.jp",
+ "otofuke.hokkaido.jp",
+ "otoineppu.hokkaido.jp",
+ "oumu.hokkaido.jp",
+ "ozora.hokkaido.jp",
+ "pippu.hokkaido.jp",
+ "rankoshi.hokkaido.jp",
+ "rebun.hokkaido.jp",
+ "rikubetsu.hokkaido.jp",
+ "rishiri.hokkaido.jp",
+ "rishirifuji.hokkaido.jp",
+ "saroma.hokkaido.jp",
+ "sarufutsu.hokkaido.jp",
+ "shakotan.hokkaido.jp",
+ "shari.hokkaido.jp",
+ "shibecha.hokkaido.jp",
+ "shibetsu.hokkaido.jp",
+ "shikabe.hokkaido.jp",
+ "shikaoi.hokkaido.jp",
+ "shimamaki.hokkaido.jp",
+ "shimizu.hokkaido.jp",
+ "shimokawa.hokkaido.jp",
+ "shinshinotsu.hokkaido.jp",
+ "shintoku.hokkaido.jp",
+ "shiranuka.hokkaido.jp",
+ "shiraoi.hokkaido.jp",
+ "shiriuchi.hokkaido.jp",
+ "sobetsu.hokkaido.jp",
+ "sunagawa.hokkaido.jp",
+ "taiki.hokkaido.jp",
+ "takasu.hokkaido.jp",
+ "takikawa.hokkaido.jp",
+ "takinoue.hokkaido.jp",
+ "teshikaga.hokkaido.jp",
+ "tobetsu.hokkaido.jp",
+ "tohma.hokkaido.jp",
+ "tomakomai.hokkaido.jp",
+ "tomari.hokkaido.jp",
+ "toya.hokkaido.jp",
+ "toyako.hokkaido.jp",
+ "toyotomi.hokkaido.jp",
+ "toyoura.hokkaido.jp",
+ "tsubetsu.hokkaido.jp",
+ "tsukigata.hokkaido.jp",
+ "urakawa.hokkaido.jp",
+ "urausu.hokkaido.jp",
+ "uryu.hokkaido.jp",
+ "utashinai.hokkaido.jp",
+ "wakkanai.hokkaido.jp",
+ "wassamu.hokkaido.jp",
+ "yakumo.hokkaido.jp",
+ "yoichi.hokkaido.jp",
+ "aioi.hyogo.jp",
+ "akashi.hyogo.jp",
+ "ako.hyogo.jp",
+ "amagasaki.hyogo.jp",
+ "aogaki.hyogo.jp",
+ "asago.hyogo.jp",
+ "ashiya.hyogo.jp",
+ "awaji.hyogo.jp",
+ "fukusaki.hyogo.jp",
+ "goshiki.hyogo.jp",
+ "harima.hyogo.jp",
+ "himeji.hyogo.jp",
+ "ichikawa.hyogo.jp",
+ "inagawa.hyogo.jp",
+ "itami.hyogo.jp",
+ "kakogawa.hyogo.jp",
+ "kamigori.hyogo.jp",
+ "kamikawa.hyogo.jp",
+ "kasai.hyogo.jp",
+ "kasuga.hyogo.jp",
+ "kawanishi.hyogo.jp",
+ "miki.hyogo.jp",
+ "minamiawaji.hyogo.jp",
+ "nishinomiya.hyogo.jp",
+ "nishiwaki.hyogo.jp",
+ "ono.hyogo.jp",
+ "sanda.hyogo.jp",
+ "sannan.hyogo.jp",
+ "sasayama.hyogo.jp",
+ "sayo.hyogo.jp",
+ "shingu.hyogo.jp",
+ "shinonsen.hyogo.jp",
+ "shiso.hyogo.jp",
+ "sumoto.hyogo.jp",
+ "taishi.hyogo.jp",
+ "taka.hyogo.jp",
+ "takarazuka.hyogo.jp",
+ "takasago.hyogo.jp",
+ "takino.hyogo.jp",
+ "tamba.hyogo.jp",
+ "tatsuno.hyogo.jp",
+ "toyooka.hyogo.jp",
+ "yabu.hyogo.jp",
+ "yashiro.hyogo.jp",
+ "yoka.hyogo.jp",
+ "yokawa.hyogo.jp",
+ "ami.ibaraki.jp",
+ "asahi.ibaraki.jp",
+ "bando.ibaraki.jp",
+ "chikusei.ibaraki.jp",
+ "daigo.ibaraki.jp",
+ "fujishiro.ibaraki.jp",
+ "hitachi.ibaraki.jp",
+ "hitachinaka.ibaraki.jp",
+ "hitachiomiya.ibaraki.jp",
+ "hitachiota.ibaraki.jp",
+ "ibaraki.ibaraki.jp",
+ "ina.ibaraki.jp",
+ "inashiki.ibaraki.jp",
+ "itako.ibaraki.jp",
+ "iwama.ibaraki.jp",
+ "joso.ibaraki.jp",
+ "kamisu.ibaraki.jp",
+ "kasama.ibaraki.jp",
+ "kashima.ibaraki.jp",
+ "kasumigaura.ibaraki.jp",
+ "koga.ibaraki.jp",
+ "miho.ibaraki.jp",
+ "mito.ibaraki.jp",
+ "moriya.ibaraki.jp",
+ "naka.ibaraki.jp",
+ "namegata.ibaraki.jp",
+ "oarai.ibaraki.jp",
+ "ogawa.ibaraki.jp",
+ "omitama.ibaraki.jp",
+ "ryugasaki.ibaraki.jp",
+ "sakai.ibaraki.jp",
+ "sakuragawa.ibaraki.jp",
+ "shimodate.ibaraki.jp",
+ "shimotsuma.ibaraki.jp",
+ "shirosato.ibaraki.jp",
+ "sowa.ibaraki.jp",
+ "suifu.ibaraki.jp",
+ "takahagi.ibaraki.jp",
+ "tamatsukuri.ibaraki.jp",
+ "tokai.ibaraki.jp",
+ "tomobe.ibaraki.jp",
+ "tone.ibaraki.jp",
+ "toride.ibaraki.jp",
+ "tsuchiura.ibaraki.jp",
+ "tsukuba.ibaraki.jp",
+ "uchihara.ibaraki.jp",
+ "ushiku.ibaraki.jp",
+ "yachiyo.ibaraki.jp",
+ "yamagata.ibaraki.jp",
+ "yawara.ibaraki.jp",
+ "yuki.ibaraki.jp",
+ "anamizu.ishikawa.jp",
+ "hakui.ishikawa.jp",
+ "hakusan.ishikawa.jp",
+ "kaga.ishikawa.jp",
+ "kahoku.ishikawa.jp",
+ "kanazawa.ishikawa.jp",
+ "kawakita.ishikawa.jp",
+ "komatsu.ishikawa.jp",
+ "nakanoto.ishikawa.jp",
+ "nanao.ishikawa.jp",
+ "nomi.ishikawa.jp",
+ "nonoichi.ishikawa.jp",
+ "noto.ishikawa.jp",
+ "shika.ishikawa.jp",
+ "suzu.ishikawa.jp",
+ "tsubata.ishikawa.jp",
+ "tsurugi.ishikawa.jp",
+ "uchinada.ishikawa.jp",
+ "wajima.ishikawa.jp",
+ "fudai.iwate.jp",
+ "fujisawa.iwate.jp",
+ "hanamaki.iwate.jp",
+ "hiraizumi.iwate.jp",
+ "hirono.iwate.jp",
+ "ichinohe.iwate.jp",
+ "ichinoseki.iwate.jp",
+ "iwaizumi.iwate.jp",
+ "iwate.iwate.jp",
+ "joboji.iwate.jp",
+ "kamaishi.iwate.jp",
+ "kanegasaki.iwate.jp",
+ "karumai.iwate.jp",
+ "kawai.iwate.jp",
+ "kitakami.iwate.jp",
+ "kuji.iwate.jp",
+ "kunohe.iwate.jp",
+ "kuzumaki.iwate.jp",
+ "miyako.iwate.jp",
+ "mizusawa.iwate.jp",
+ "morioka.iwate.jp",
+ "ninohe.iwate.jp",
+ "noda.iwate.jp",
+ "ofunato.iwate.jp",
+ "oshu.iwate.jp",
+ "otsuchi.iwate.jp",
+ "rikuzentakata.iwate.jp",
+ "shiwa.iwate.jp",
+ "shizukuishi.iwate.jp",
+ "sumita.iwate.jp",
+ "tanohata.iwate.jp",
+ "tono.iwate.jp",
+ "yahaba.iwate.jp",
+ "yamada.iwate.jp",
+ "ayagawa.kagawa.jp",
+ "higashikagawa.kagawa.jp",
+ "kanonji.kagawa.jp",
+ "kotohira.kagawa.jp",
+ "manno.kagawa.jp",
+ "marugame.kagawa.jp",
+ "mitoyo.kagawa.jp",
+ "naoshima.kagawa.jp",
+ "sanuki.kagawa.jp",
+ "tadotsu.kagawa.jp",
+ "takamatsu.kagawa.jp",
+ "tonosho.kagawa.jp",
+ "uchinomi.kagawa.jp",
+ "utazu.kagawa.jp",
+ "zentsuji.kagawa.jp",
+ "akune.kagoshima.jp",
+ "amami.kagoshima.jp",
+ "hioki.kagoshima.jp",
+ "isa.kagoshima.jp",
+ "isen.kagoshima.jp",
+ "izumi.kagoshima.jp",
+ "kagoshima.kagoshima.jp",
+ "kanoya.kagoshima.jp",
+ "kawanabe.kagoshima.jp",
+ "kinko.kagoshima.jp",
+ "kouyama.kagoshima.jp",
+ "makurazaki.kagoshima.jp",
+ "matsumoto.kagoshima.jp",
+ "minamitane.kagoshima.jp",
+ "nakatane.kagoshima.jp",
+ "nishinoomote.kagoshima.jp",
+ "satsumasendai.kagoshima.jp",
+ "soo.kagoshima.jp",
+ "tarumizu.kagoshima.jp",
+ "yusui.kagoshima.jp",
+ "aikawa.kanagawa.jp",
+ "atsugi.kanagawa.jp",
+ "ayase.kanagawa.jp",
+ "chigasaki.kanagawa.jp",
+ "ebina.kanagawa.jp",
+ "fujisawa.kanagawa.jp",
+ "hadano.kanagawa.jp",
+ "hakone.kanagawa.jp",
+ "hiratsuka.kanagawa.jp",
+ "isehara.kanagawa.jp",
+ "kaisei.kanagawa.jp",
+ "kamakura.kanagawa.jp",
+ "kiyokawa.kanagawa.jp",
+ "matsuda.kanagawa.jp",
+ "minamiashigara.kanagawa.jp",
+ "miura.kanagawa.jp",
+ "nakai.kanagawa.jp",
+ "ninomiya.kanagawa.jp",
+ "odawara.kanagawa.jp",
+ "oi.kanagawa.jp",
+ "oiso.kanagawa.jp",
+ "sagamihara.kanagawa.jp",
+ "samukawa.kanagawa.jp",
+ "tsukui.kanagawa.jp",
+ "yamakita.kanagawa.jp",
+ "yamato.kanagawa.jp",
+ "yokosuka.kanagawa.jp",
+ "yugawara.kanagawa.jp",
+ "zama.kanagawa.jp",
+ "zushi.kanagawa.jp",
+ "aki.kochi.jp",
+ "geisei.kochi.jp",
+ "hidaka.kochi.jp",
+ "higashitsuno.kochi.jp",
+ "ino.kochi.jp",
+ "kagami.kochi.jp",
+ "kami.kochi.jp",
+ "kitagawa.kochi.jp",
+ "kochi.kochi.jp",
+ "mihara.kochi.jp",
+ "motoyama.kochi.jp",
+ "muroto.kochi.jp",
+ "nahari.kochi.jp",
+ "nakamura.kochi.jp",
+ "nankoku.kochi.jp",
+ "nishitosa.kochi.jp",
+ "niyodogawa.kochi.jp",
+ "ochi.kochi.jp",
+ "okawa.kochi.jp",
+ "otoyo.kochi.jp",
+ "otsuki.kochi.jp",
+ "sakawa.kochi.jp",
+ "sukumo.kochi.jp",
+ "susaki.kochi.jp",
+ "tosa.kochi.jp",
+ "tosashimizu.kochi.jp",
+ "toyo.kochi.jp",
+ "tsuno.kochi.jp",
+ "umaji.kochi.jp",
+ "yasuda.kochi.jp",
+ "yusuhara.kochi.jp",
+ "amakusa.kumamoto.jp",
+ "arao.kumamoto.jp",
+ "aso.kumamoto.jp",
+ "choyo.kumamoto.jp",
+ "gyokuto.kumamoto.jp",
+ "hitoyoshi.kumamoto.jp",
+ "kamiamakusa.kumamoto.jp",
+ "kashima.kumamoto.jp",
+ "kikuchi.kumamoto.jp",
+ "kosa.kumamoto.jp",
+ "kumamoto.kumamoto.jp",
+ "mashiki.kumamoto.jp",
+ "mifune.kumamoto.jp",
+ "minamata.kumamoto.jp",
+ "minamioguni.kumamoto.jp",
+ "nagasu.kumamoto.jp",
+ "nishihara.kumamoto.jp",
+ "oguni.kumamoto.jp",
+ "ozu.kumamoto.jp",
+ "sumoto.kumamoto.jp",
+ "takamori.kumamoto.jp",
+ "uki.kumamoto.jp",
+ "uto.kumamoto.jp",
+ "yamaga.kumamoto.jp",
+ "yamato.kumamoto.jp",
+ "yatsushiro.kumamoto.jp",
+ "ayabe.kyoto.jp",
+ "fukuchiyama.kyoto.jp",
+ "higashiyama.kyoto.jp",
+ "ide.kyoto.jp",
+ "ine.kyoto.jp",
+ "joyo.kyoto.jp",
+ "kameoka.kyoto.jp",
+ "kamo.kyoto.jp",
+ "kita.kyoto.jp",
+ "kizu.kyoto.jp",
+ "kumiyama.kyoto.jp",
+ "kyotamba.kyoto.jp",
+ "kyotanabe.kyoto.jp",
+ "kyotango.kyoto.jp",
+ "maizuru.kyoto.jp",
+ "minami.kyoto.jp",
+ "minamiyamashiro.kyoto.jp",
+ "miyazu.kyoto.jp",
+ "muko.kyoto.jp",
+ "nagaokakyo.kyoto.jp",
+ "nakagyo.kyoto.jp",
+ "nantan.kyoto.jp",
+ "oyamazaki.kyoto.jp",
+ "sakyo.kyoto.jp",
+ "seika.kyoto.jp",
+ "tanabe.kyoto.jp",
+ "uji.kyoto.jp",
+ "ujitawara.kyoto.jp",
+ "wazuka.kyoto.jp",
+ "yamashina.kyoto.jp",
+ "yawata.kyoto.jp",
+ "asahi.mie.jp",
+ "inabe.mie.jp",
+ "ise.mie.jp",
+ "kameyama.mie.jp",
+ "kawagoe.mie.jp",
+ "kiho.mie.jp",
+ "kisosaki.mie.jp",
+ "kiwa.mie.jp",
+ "komono.mie.jp",
+ "kumano.mie.jp",
+ "kuwana.mie.jp",
+ "matsusaka.mie.jp",
+ "meiwa.mie.jp",
+ "mihama.mie.jp",
+ "minamiise.mie.jp",
+ "misugi.mie.jp",
+ "miyama.mie.jp",
+ "nabari.mie.jp",
+ "shima.mie.jp",
+ "suzuka.mie.jp",
+ "tado.mie.jp",
+ "taiki.mie.jp",
+ "taki.mie.jp",
+ "tamaki.mie.jp",
+ "toba.mie.jp",
+ "tsu.mie.jp",
+ "udono.mie.jp",
+ "ureshino.mie.jp",
+ "watarai.mie.jp",
+ "yokkaichi.mie.jp",
+ "furukawa.miyagi.jp",
+ "higashimatsushima.miyagi.jp",
+ "ishinomaki.miyagi.jp",
+ "iwanuma.miyagi.jp",
+ "kakuda.miyagi.jp",
+ "kami.miyagi.jp",
+ "kawasaki.miyagi.jp",
+ "kesennuma.miyagi.jp",
+ "marumori.miyagi.jp",
+ "matsushima.miyagi.jp",
+ "minamisanriku.miyagi.jp",
+ "misato.miyagi.jp",
+ "murata.miyagi.jp",
+ "natori.miyagi.jp",
+ "ogawara.miyagi.jp",
+ "ohira.miyagi.jp",
+ "onagawa.miyagi.jp",
+ "osaki.miyagi.jp",
+ "rifu.miyagi.jp",
+ "semine.miyagi.jp",
+ "shibata.miyagi.jp",
+ "shichikashuku.miyagi.jp",
+ "shikama.miyagi.jp",
+ "shiogama.miyagi.jp",
+ "shiroishi.miyagi.jp",
+ "tagajo.miyagi.jp",
+ "taiwa.miyagi.jp",
+ "tome.miyagi.jp",
+ "tomiya.miyagi.jp",
+ "wakuya.miyagi.jp",
+ "watari.miyagi.jp",
+ "yamamoto.miyagi.jp",
+ "zao.miyagi.jp",
+ "aya.miyazaki.jp",
+ "ebino.miyazaki.jp",
+ "gokase.miyazaki.jp",
+ "hyuga.miyazaki.jp",
+ "kadogawa.miyazaki.jp",
+ "kawaminami.miyazaki.jp",
+ "kijo.miyazaki.jp",
+ "kitagawa.miyazaki.jp",
+ "kitakata.miyazaki.jp",
+ "kitaura.miyazaki.jp",
+ "kobayashi.miyazaki.jp",
+ "kunitomi.miyazaki.jp",
+ "kushima.miyazaki.jp",
+ "mimata.miyazaki.jp",
+ "miyakonojo.miyazaki.jp",
+ "miyazaki.miyazaki.jp",
+ "morotsuka.miyazaki.jp",
+ "nichinan.miyazaki.jp",
+ "nishimera.miyazaki.jp",
+ "nobeoka.miyazaki.jp",
+ "saito.miyazaki.jp",
+ "shiiba.miyazaki.jp",
+ "shintomi.miyazaki.jp",
+ "takaharu.miyazaki.jp",
+ "takanabe.miyazaki.jp",
+ "takazaki.miyazaki.jp",
+ "tsuno.miyazaki.jp",
+ "achi.nagano.jp",
+ "agematsu.nagano.jp",
+ "anan.nagano.jp",
+ "aoki.nagano.jp",
+ "asahi.nagano.jp",
+ "azumino.nagano.jp",
+ "chikuhoku.nagano.jp",
+ "chikuma.nagano.jp",
+ "chino.nagano.jp",
+ "fujimi.nagano.jp",
+ "hakuba.nagano.jp",
+ "hara.nagano.jp",
+ "hiraya.nagano.jp",
+ "iida.nagano.jp",
+ "iijima.nagano.jp",
+ "iiyama.nagano.jp",
+ "iizuna.nagano.jp",
+ "ikeda.nagano.jp",
+ "ikusaka.nagano.jp",
+ "ina.nagano.jp",
+ "karuizawa.nagano.jp",
+ "kawakami.nagano.jp",
+ "kiso.nagano.jp",
+ "kisofukushima.nagano.jp",
+ "kitaaiki.nagano.jp",
+ "komagane.nagano.jp",
+ "komoro.nagano.jp",
+ "matsukawa.nagano.jp",
+ "matsumoto.nagano.jp",
+ "miasa.nagano.jp",
+ "minamiaiki.nagano.jp",
+ "minamimaki.nagano.jp",
+ "minamiminowa.nagano.jp",
+ "minowa.nagano.jp",
+ "miyada.nagano.jp",
+ "miyota.nagano.jp",
+ "mochizuki.nagano.jp",
+ "nagano.nagano.jp",
+ "nagawa.nagano.jp",
+ "nagiso.nagano.jp",
+ "nakagawa.nagano.jp",
+ "nakano.nagano.jp",
+ "nozawaonsen.nagano.jp",
+ "obuse.nagano.jp",
+ "ogawa.nagano.jp",
+ "okaya.nagano.jp",
+ "omachi.nagano.jp",
+ "omi.nagano.jp",
+ "ookuwa.nagano.jp",
+ "ooshika.nagano.jp",
+ "otaki.nagano.jp",
+ "otari.nagano.jp",
+ "sakae.nagano.jp",
+ "sakaki.nagano.jp",
+ "saku.nagano.jp",
+ "sakuho.nagano.jp",
+ "shimosuwa.nagano.jp",
+ "shinanomachi.nagano.jp",
+ "shiojiri.nagano.jp",
+ "suwa.nagano.jp",
+ "suzaka.nagano.jp",
+ "takagi.nagano.jp",
+ "takamori.nagano.jp",
+ "takayama.nagano.jp",
+ "tateshina.nagano.jp",
+ "tatsuno.nagano.jp",
+ "togakushi.nagano.jp",
+ "togura.nagano.jp",
+ "tomi.nagano.jp",
+ "ueda.nagano.jp",
+ "wada.nagano.jp",
+ "yamagata.nagano.jp",
+ "yamanouchi.nagano.jp",
+ "yasaka.nagano.jp",
+ "yasuoka.nagano.jp",
+ "chijiwa.nagasaki.jp",
+ "futsu.nagasaki.jp",
+ "goto.nagasaki.jp",
+ "hasami.nagasaki.jp",
+ "hirado.nagasaki.jp",
+ "iki.nagasaki.jp",
+ "isahaya.nagasaki.jp",
+ "kawatana.nagasaki.jp",
+ "kuchinotsu.nagasaki.jp",
+ "matsuura.nagasaki.jp",
+ "nagasaki.nagasaki.jp",
+ "obama.nagasaki.jp",
+ "omura.nagasaki.jp",
+ "oseto.nagasaki.jp",
+ "saikai.nagasaki.jp",
+ "sasebo.nagasaki.jp",
+ "seihi.nagasaki.jp",
+ "shimabara.nagasaki.jp",
+ "shinkamigoto.nagasaki.jp",
+ "togitsu.nagasaki.jp",
+ "tsushima.nagasaki.jp",
+ "unzen.nagasaki.jp",
+ "ando.nara.jp",
+ "gose.nara.jp",
+ "heguri.nara.jp",
+ "higashiyoshino.nara.jp",
+ "ikaruga.nara.jp",
+ "ikoma.nara.jp",
+ "kamikitayama.nara.jp",
+ "kanmaki.nara.jp",
+ "kashiba.nara.jp",
+ "kashihara.nara.jp",
+ "katsuragi.nara.jp",
+ "kawai.nara.jp",
+ "kawakami.nara.jp",
+ "kawanishi.nara.jp",
+ "koryo.nara.jp",
+ "kurotaki.nara.jp",
+ "mitsue.nara.jp",
+ "miyake.nara.jp",
+ "nara.nara.jp",
+ "nosegawa.nara.jp",
+ "oji.nara.jp",
+ "ouda.nara.jp",
+ "oyodo.nara.jp",
+ "sakurai.nara.jp",
+ "sango.nara.jp",
+ "shimoichi.nara.jp",
+ "shimokitayama.nara.jp",
+ "shinjo.nara.jp",
+ "soni.nara.jp",
+ "takatori.nara.jp",
+ "tawaramoto.nara.jp",
+ "tenkawa.nara.jp",
+ "tenri.nara.jp",
+ "uda.nara.jp",
+ "yamatokoriyama.nara.jp",
+ "yamatotakada.nara.jp",
+ "yamazoe.nara.jp",
+ "yoshino.nara.jp",
+ "aga.niigata.jp",
+ "agano.niigata.jp",
+ "gosen.niigata.jp",
+ "itoigawa.niigata.jp",
+ "izumozaki.niigata.jp",
+ "joetsu.niigata.jp",
+ "kamo.niigata.jp",
+ "kariwa.niigata.jp",
+ "kashiwazaki.niigata.jp",
+ "minamiuonuma.niigata.jp",
+ "mitsuke.niigata.jp",
+ "muika.niigata.jp",
+ "murakami.niigata.jp",
+ "myoko.niigata.jp",
+ "nagaoka.niigata.jp",
+ "niigata.niigata.jp",
+ "ojiya.niigata.jp",
+ "omi.niigata.jp",
+ "sado.niigata.jp",
+ "sanjo.niigata.jp",
+ "seiro.niigata.jp",
+ "seirou.niigata.jp",
+ "sekikawa.niigata.jp",
+ "shibata.niigata.jp",
+ "tagami.niigata.jp",
+ "tainai.niigata.jp",
+ "tochio.niigata.jp",
+ "tokamachi.niigata.jp",
+ "tsubame.niigata.jp",
+ "tsunan.niigata.jp",
+ "uonuma.niigata.jp",
+ "yahiko.niigata.jp",
+ "yoita.niigata.jp",
+ "yuzawa.niigata.jp",
+ "beppu.oita.jp",
+ "bungoono.oita.jp",
+ "bungotakada.oita.jp",
+ "hasama.oita.jp",
+ "hiji.oita.jp",
+ "himeshima.oita.jp",
+ "hita.oita.jp",
+ "kamitsue.oita.jp",
+ "kokonoe.oita.jp",
+ "kuju.oita.jp",
+ "kunisaki.oita.jp",
+ "kusu.oita.jp",
+ "oita.oita.jp",
+ "saiki.oita.jp",
+ "taketa.oita.jp",
+ "tsukumi.oita.jp",
+ "usa.oita.jp",
+ "usuki.oita.jp",
+ "yufu.oita.jp",
+ "akaiwa.okayama.jp",
+ "asakuchi.okayama.jp",
+ "bizen.okayama.jp",
+ "hayashima.okayama.jp",
+ "ibara.okayama.jp",
+ "kagamino.okayama.jp",
+ "kasaoka.okayama.jp",
+ "kibichuo.okayama.jp",
+ "kumenan.okayama.jp",
+ "kurashiki.okayama.jp",
+ "maniwa.okayama.jp",
+ "misaki.okayama.jp",
+ "nagi.okayama.jp",
+ "niimi.okayama.jp",
+ "nishiawakura.okayama.jp",
+ "okayama.okayama.jp",
+ "satosho.okayama.jp",
+ "setouchi.okayama.jp",
+ "shinjo.okayama.jp",
+ "shoo.okayama.jp",
+ "soja.okayama.jp",
+ "takahashi.okayama.jp",
+ "tamano.okayama.jp",
+ "tsuyama.okayama.jp",
+ "wake.okayama.jp",
+ "yakage.okayama.jp",
+ "aguni.okinawa.jp",
+ "ginowan.okinawa.jp",
+ "ginoza.okinawa.jp",
+ "gushikami.okinawa.jp",
+ "haebaru.okinawa.jp",
+ "higashi.okinawa.jp",
+ "hirara.okinawa.jp",
+ "iheya.okinawa.jp",
+ "ishigaki.okinawa.jp",
+ "ishikawa.okinawa.jp",
+ "itoman.okinawa.jp",
+ "izena.okinawa.jp",
+ "kadena.okinawa.jp",
+ "kin.okinawa.jp",
+ "kitadaito.okinawa.jp",
+ "kitanakagusuku.okinawa.jp",
+ "kumejima.okinawa.jp",
+ "kunigami.okinawa.jp",
+ "minamidaito.okinawa.jp",
+ "motobu.okinawa.jp",
+ "nago.okinawa.jp",
+ "naha.okinawa.jp",
+ "nakagusuku.okinawa.jp",
+ "nakijin.okinawa.jp",
+ "nanjo.okinawa.jp",
+ "nishihara.okinawa.jp",
+ "ogimi.okinawa.jp",
+ "okinawa.okinawa.jp",
+ "onna.okinawa.jp",
+ "shimoji.okinawa.jp",
+ "taketomi.okinawa.jp",
+ "tarama.okinawa.jp",
+ "tokashiki.okinawa.jp",
+ "tomigusuku.okinawa.jp",
+ "tonaki.okinawa.jp",
+ "urasoe.okinawa.jp",
+ "uruma.okinawa.jp",
+ "yaese.okinawa.jp",
+ "yomitan.okinawa.jp",
+ "yonabaru.okinawa.jp",
+ "yonaguni.okinawa.jp",
+ "zamami.okinawa.jp",
+ "abeno.osaka.jp",
+ "chihayaakasaka.osaka.jp",
+ "chuo.osaka.jp",
+ "daito.osaka.jp",
+ "fujiidera.osaka.jp",
+ "habikino.osaka.jp",
+ "hannan.osaka.jp",
+ "higashiosaka.osaka.jp",
+ "higashisumiyoshi.osaka.jp",
+ "higashiyodogawa.osaka.jp",
+ "hirakata.osaka.jp",
+ "ibaraki.osaka.jp",
+ "ikeda.osaka.jp",
+ "izumi.osaka.jp",
+ "izumiotsu.osaka.jp",
+ "izumisano.osaka.jp",
+ "kadoma.osaka.jp",
+ "kaizuka.osaka.jp",
+ "kanan.osaka.jp",
+ "kashiwara.osaka.jp",
+ "katano.osaka.jp",
+ "kawachinagano.osaka.jp",
+ "kishiwada.osaka.jp",
+ "kita.osaka.jp",
+ "kumatori.osaka.jp",
+ "matsubara.osaka.jp",
+ "minato.osaka.jp",
+ "minoh.osaka.jp",
+ "misaki.osaka.jp",
+ "moriguchi.osaka.jp",
+ "neyagawa.osaka.jp",
+ "nishi.osaka.jp",
+ "nose.osaka.jp",
+ "osakasayama.osaka.jp",
+ "sakai.osaka.jp",
+ "sayama.osaka.jp",
+ "sennan.osaka.jp",
+ "settsu.osaka.jp",
+ "shijonawate.osaka.jp",
+ "shimamoto.osaka.jp",
+ "suita.osaka.jp",
+ "tadaoka.osaka.jp",
+ "taishi.osaka.jp",
+ "tajiri.osaka.jp",
+ "takaishi.osaka.jp",
+ "takatsuki.osaka.jp",
+ "tondabayashi.osaka.jp",
+ "toyonaka.osaka.jp",
+ "toyono.osaka.jp",
+ "yao.osaka.jp",
+ "ariake.saga.jp",
+ "arita.saga.jp",
+ "fukudomi.saga.jp",
+ "genkai.saga.jp",
+ "hamatama.saga.jp",
+ "hizen.saga.jp",
+ "imari.saga.jp",
+ "kamimine.saga.jp",
+ "kanzaki.saga.jp",
+ "karatsu.saga.jp",
+ "kashima.saga.jp",
+ "kitagata.saga.jp",
+ "kitahata.saga.jp",
+ "kiyama.saga.jp",
+ "kouhoku.saga.jp",
+ "kyuragi.saga.jp",
+ "nishiarita.saga.jp",
+ "ogi.saga.jp",
+ "omachi.saga.jp",
+ "ouchi.saga.jp",
+ "saga.saga.jp",
+ "shiroishi.saga.jp",
+ "taku.saga.jp",
+ "tara.saga.jp",
+ "tosu.saga.jp",
+ "yoshinogari.saga.jp",
+ "arakawa.saitama.jp",
+ "asaka.saitama.jp",
+ "chichibu.saitama.jp",
+ "fujimi.saitama.jp",
+ "fujimino.saitama.jp",
+ "fukaya.saitama.jp",
+ "hanno.saitama.jp",
+ "hanyu.saitama.jp",
+ "hasuda.saitama.jp",
+ "hatogaya.saitama.jp",
+ "hatoyama.saitama.jp",
+ "hidaka.saitama.jp",
+ "higashichichibu.saitama.jp",
+ "higashimatsuyama.saitama.jp",
+ "honjo.saitama.jp",
+ "ina.saitama.jp",
+ "iruma.saitama.jp",
+ "iwatsuki.saitama.jp",
+ "kamiizumi.saitama.jp",
+ "kamikawa.saitama.jp",
+ "kamisato.saitama.jp",
+ "kasukabe.saitama.jp",
+ "kawagoe.saitama.jp",
+ "kawaguchi.saitama.jp",
+ "kawajima.saitama.jp",
+ "kazo.saitama.jp",
+ "kitamoto.saitama.jp",
+ "koshigaya.saitama.jp",
+ "kounosu.saitama.jp",
+ "kuki.saitama.jp",
+ "kumagaya.saitama.jp",
+ "matsubushi.saitama.jp",
+ "minano.saitama.jp",
+ "misato.saitama.jp",
+ "miyashiro.saitama.jp",
+ "miyoshi.saitama.jp",
+ "moroyama.saitama.jp",
+ "nagatoro.saitama.jp",
+ "namegawa.saitama.jp",
+ "niiza.saitama.jp",
+ "ogano.saitama.jp",
+ "ogawa.saitama.jp",
+ "ogose.saitama.jp",
+ "okegawa.saitama.jp",
+ "omiya.saitama.jp",
+ "otaki.saitama.jp",
+ "ranzan.saitama.jp",
+ "ryokami.saitama.jp",
+ "saitama.saitama.jp",
+ "sakado.saitama.jp",
+ "satte.saitama.jp",
+ "sayama.saitama.jp",
+ "shiki.saitama.jp",
+ "shiraoka.saitama.jp",
+ "soka.saitama.jp",
+ "sugito.saitama.jp",
+ "toda.saitama.jp",
+ "tokigawa.saitama.jp",
+ "tokorozawa.saitama.jp",
+ "tsurugashima.saitama.jp",
+ "urawa.saitama.jp",
+ "warabi.saitama.jp",
+ "yashio.saitama.jp",
+ "yokoze.saitama.jp",
+ "yono.saitama.jp",
+ "yorii.saitama.jp",
+ "yoshida.saitama.jp",
+ "yoshikawa.saitama.jp",
+ "yoshimi.saitama.jp",
+ "aisho.shiga.jp",
+ "gamo.shiga.jp",
+ "higashiomi.shiga.jp",
+ "hikone.shiga.jp",
+ "koka.shiga.jp",
+ "konan.shiga.jp",
+ "kosei.shiga.jp",
+ "koto.shiga.jp",
+ "kusatsu.shiga.jp",
+ "maibara.shiga.jp",
+ "moriyama.shiga.jp",
+ "nagahama.shiga.jp",
+ "nishiazai.shiga.jp",
+ "notogawa.shiga.jp",
+ "omihachiman.shiga.jp",
+ "otsu.shiga.jp",
+ "ritto.shiga.jp",
+ "ryuoh.shiga.jp",
+ "takashima.shiga.jp",
+ "takatsuki.shiga.jp",
+ "torahime.shiga.jp",
+ "toyosato.shiga.jp",
+ "yasu.shiga.jp",
+ "akagi.shimane.jp",
+ "ama.shimane.jp",
+ "gotsu.shimane.jp",
+ "hamada.shimane.jp",
+ "higashiizumo.shimane.jp",
+ "hikawa.shimane.jp",
+ "hikimi.shimane.jp",
+ "izumo.shimane.jp",
+ "kakinoki.shimane.jp",
+ "masuda.shimane.jp",
+ "matsue.shimane.jp",
+ "misato.shimane.jp",
+ "nishinoshima.shimane.jp",
+ "ohda.shimane.jp",
+ "okinoshima.shimane.jp",
+ "okuizumo.shimane.jp",
+ "shimane.shimane.jp",
+ "tamayu.shimane.jp",
+ "tsuwano.shimane.jp",
+ "unnan.shimane.jp",
+ "yakumo.shimane.jp",
+ "yasugi.shimane.jp",
+ "yatsuka.shimane.jp",
+ "arai.shizuoka.jp",
+ "atami.shizuoka.jp",
+ "fuji.shizuoka.jp",
+ "fujieda.shizuoka.jp",
+ "fujikawa.shizuoka.jp",
+ "fujinomiya.shizuoka.jp",
+ "fukuroi.shizuoka.jp",
+ "gotemba.shizuoka.jp",
+ "haibara.shizuoka.jp",
+ "hamamatsu.shizuoka.jp",
+ "higashiizu.shizuoka.jp",
+ "ito.shizuoka.jp",
+ "iwata.shizuoka.jp",
+ "izu.shizuoka.jp",
+ "izunokuni.shizuoka.jp",
+ "kakegawa.shizuoka.jp",
+ "kannami.shizuoka.jp",
+ "kawanehon.shizuoka.jp",
+ "kawazu.shizuoka.jp",
+ "kikugawa.shizuoka.jp",
+ "kosai.shizuoka.jp",
+ "makinohara.shizuoka.jp",
+ "matsuzaki.shizuoka.jp",
+ "minamiizu.shizuoka.jp",
+ "mishima.shizuoka.jp",
+ "morimachi.shizuoka.jp",
+ "nishiizu.shizuoka.jp",
+ "numazu.shizuoka.jp",
+ "omaezaki.shizuoka.jp",
+ "shimada.shizuoka.jp",
+ "shimizu.shizuoka.jp",
+ "shimoda.shizuoka.jp",
+ "shizuoka.shizuoka.jp",
+ "susono.shizuoka.jp",
+ "yaizu.shizuoka.jp",
+ "yoshida.shizuoka.jp",
+ "ashikaga.tochigi.jp",
+ "bato.tochigi.jp",
+ "haga.tochigi.jp",
+ "ichikai.tochigi.jp",
+ "iwafune.tochigi.jp",
+ "kaminokawa.tochigi.jp",
+ "kanuma.tochigi.jp",
+ "karasuyama.tochigi.jp",
+ "kuroiso.tochigi.jp",
+ "mashiko.tochigi.jp",
+ "mibu.tochigi.jp",
+ "moka.tochigi.jp",
+ "motegi.tochigi.jp",
+ "nasu.tochigi.jp",
+ "nasushiobara.tochigi.jp",
+ "nikko.tochigi.jp",
+ "nishikata.tochigi.jp",
+ "nogi.tochigi.jp",
+ "ohira.tochigi.jp",
+ "ohtawara.tochigi.jp",
+ "oyama.tochigi.jp",
+ "sakura.tochigi.jp",
+ "sano.tochigi.jp",
+ "shimotsuke.tochigi.jp",
+ "shioya.tochigi.jp",
+ "takanezawa.tochigi.jp",
+ "tochigi.tochigi.jp",
+ "tsuga.tochigi.jp",
+ "ujiie.tochigi.jp",
+ "utsunomiya.tochigi.jp",
+ "yaita.tochigi.jp",
+ "aizumi.tokushima.jp",
+ "anan.tokushima.jp",
+ "ichiba.tokushima.jp",
+ "itano.tokushima.jp",
+ "kainan.tokushima.jp",
+ "komatsushima.tokushima.jp",
+ "matsushige.tokushima.jp",
+ "mima.tokushima.jp",
+ "minami.tokushima.jp",
+ "miyoshi.tokushima.jp",
+ "mugi.tokushima.jp",
+ "nakagawa.tokushima.jp",
+ "naruto.tokushima.jp",
+ "sanagochi.tokushima.jp",
+ "shishikui.tokushima.jp",
+ "tokushima.tokushima.jp",
+ "wajiki.tokushima.jp",
+ "adachi.tokyo.jp",
+ "akiruno.tokyo.jp",
+ "akishima.tokyo.jp",
+ "aogashima.tokyo.jp",
+ "arakawa.tokyo.jp",
+ "bunkyo.tokyo.jp",
+ "chiyoda.tokyo.jp",
+ "chofu.tokyo.jp",
+ "chuo.tokyo.jp",
+ "edogawa.tokyo.jp",
+ "fuchu.tokyo.jp",
+ "fussa.tokyo.jp",
+ "hachijo.tokyo.jp",
+ "hachioji.tokyo.jp",
+ "hamura.tokyo.jp",
+ "higashikurume.tokyo.jp",
+ "higashimurayama.tokyo.jp",
+ "higashiyamato.tokyo.jp",
+ "hino.tokyo.jp",
+ "hinode.tokyo.jp",
+ "hinohara.tokyo.jp",
+ "inagi.tokyo.jp",
+ "itabashi.tokyo.jp",
+ "katsushika.tokyo.jp",
+ "kita.tokyo.jp",
+ "kiyose.tokyo.jp",
+ "kodaira.tokyo.jp",
+ "koganei.tokyo.jp",
+ "kokubunji.tokyo.jp",
+ "komae.tokyo.jp",
+ "koto.tokyo.jp",
+ "kouzushima.tokyo.jp",
+ "kunitachi.tokyo.jp",
+ "machida.tokyo.jp",
+ "meguro.tokyo.jp",
+ "minato.tokyo.jp",
+ "mitaka.tokyo.jp",
+ "mizuho.tokyo.jp",
+ "musashimurayama.tokyo.jp",
+ "musashino.tokyo.jp",
+ "nakano.tokyo.jp",
+ "nerima.tokyo.jp",
+ "ogasawara.tokyo.jp",
+ "okutama.tokyo.jp",
+ "ome.tokyo.jp",
+ "oshima.tokyo.jp",
+ "ota.tokyo.jp",
+ "setagaya.tokyo.jp",
+ "shibuya.tokyo.jp",
+ "shinagawa.tokyo.jp",
+ "shinjuku.tokyo.jp",
+ "suginami.tokyo.jp",
+ "sumida.tokyo.jp",
+ "tachikawa.tokyo.jp",
+ "taito.tokyo.jp",
+ "tama.tokyo.jp",
+ "toshima.tokyo.jp",
+ "chizu.tottori.jp",
+ "hino.tottori.jp",
+ "kawahara.tottori.jp",
+ "koge.tottori.jp",
+ "kotoura.tottori.jp",
+ "misasa.tottori.jp",
+ "nanbu.tottori.jp",
+ "nichinan.tottori.jp",
+ "sakaiminato.tottori.jp",
+ "tottori.tottori.jp",
+ "wakasa.tottori.jp",
+ "yazu.tottori.jp",
+ "yonago.tottori.jp",
+ "asahi.toyama.jp",
+ "fuchu.toyama.jp",
+ "fukumitsu.toyama.jp",
+ "funahashi.toyama.jp",
+ "himi.toyama.jp",
+ "imizu.toyama.jp",
+ "inami.toyama.jp",
+ "johana.toyama.jp",
+ "kamiichi.toyama.jp",
+ "kurobe.toyama.jp",
+ "nakaniikawa.toyama.jp",
+ "namerikawa.toyama.jp",
+ "nanto.toyama.jp",
+ "nyuzen.toyama.jp",
+ "oyabe.toyama.jp",
+ "taira.toyama.jp",
+ "takaoka.toyama.jp",
+ "tateyama.toyama.jp",
+ "toga.toyama.jp",
+ "tonami.toyama.jp",
+ "toyama.toyama.jp",
+ "unazuki.toyama.jp",
+ "uozu.toyama.jp",
+ "yamada.toyama.jp",
+ "arida.wakayama.jp",
+ "aridagawa.wakayama.jp",
+ "gobo.wakayama.jp",
+ "hashimoto.wakayama.jp",
+ "hidaka.wakayama.jp",
+ "hirogawa.wakayama.jp",
+ "inami.wakayama.jp",
+ "iwade.wakayama.jp",
+ "kainan.wakayama.jp",
+ "kamitonda.wakayama.jp",
+ "katsuragi.wakayama.jp",
+ "kimino.wakayama.jp",
+ "kinokawa.wakayama.jp",
+ "kitayama.wakayama.jp",
+ "koya.wakayama.jp",
+ "koza.wakayama.jp",
+ "kozagawa.wakayama.jp",
+ "kudoyama.wakayama.jp",
+ "kushimoto.wakayama.jp",
+ "mihama.wakayama.jp",
+ "misato.wakayama.jp",
+ "nachikatsuura.wakayama.jp",
+ "shingu.wakayama.jp",
+ "shirahama.wakayama.jp",
+ "taiji.wakayama.jp",
+ "tanabe.wakayama.jp",
+ "wakayama.wakayama.jp",
+ "yuasa.wakayama.jp",
+ "yura.wakayama.jp",
+ "asahi.yamagata.jp",
+ "funagata.yamagata.jp",
+ "higashine.yamagata.jp",
+ "iide.yamagata.jp",
+ "kahoku.yamagata.jp",
+ "kaminoyama.yamagata.jp",
+ "kaneyama.yamagata.jp",
+ "kawanishi.yamagata.jp",
+ "mamurogawa.yamagata.jp",
+ "mikawa.yamagata.jp",
+ "murayama.yamagata.jp",
+ "nagai.yamagata.jp",
+ "nakayama.yamagata.jp",
+ "nanyo.yamagata.jp",
+ "nishikawa.yamagata.jp",
+ "obanazawa.yamagata.jp",
+ "oe.yamagata.jp",
+ "oguni.yamagata.jp",
+ "ohkura.yamagata.jp",
+ "oishida.yamagata.jp",
+ "sagae.yamagata.jp",
+ "sakata.yamagata.jp",
+ "sakegawa.yamagata.jp",
+ "shinjo.yamagata.jp",
+ "shirataka.yamagata.jp",
+ "shonai.yamagata.jp",
+ "takahata.yamagata.jp",
+ "tendo.yamagata.jp",
+ "tozawa.yamagata.jp",
+ "tsuruoka.yamagata.jp",
+ "yamagata.yamagata.jp",
+ "yamanobe.yamagata.jp",
+ "yonezawa.yamagata.jp",
+ "yuza.yamagata.jp",
+ "abu.yamaguchi.jp",
+ "hagi.yamaguchi.jp",
+ "hikari.yamaguchi.jp",
+ "hofu.yamaguchi.jp",
+ "iwakuni.yamaguchi.jp",
+ "kudamatsu.yamaguchi.jp",
+ "mitou.yamaguchi.jp",
+ "nagato.yamaguchi.jp",
+ "oshima.yamaguchi.jp",
+ "shimonoseki.yamaguchi.jp",
+ "shunan.yamaguchi.jp",
+ "tabuse.yamaguchi.jp",
+ "tokuyama.yamaguchi.jp",
+ "toyota.yamaguchi.jp",
+ "ube.yamaguchi.jp",
+ "yuu.yamaguchi.jp",
+ "chuo.yamanashi.jp",
+ "doshi.yamanashi.jp",
+ "fuefuki.yamanashi.jp",
+ "fujikawa.yamanashi.jp",
+ "fujikawaguchiko.yamanashi.jp",
+ "fujiyoshida.yamanashi.jp",
+ "hayakawa.yamanashi.jp",
+ "hokuto.yamanashi.jp",
+ "ichikawamisato.yamanashi.jp",
+ "kai.yamanashi.jp",
+ "kofu.yamanashi.jp",
+ "koshu.yamanashi.jp",
+ "kosuge.yamanashi.jp",
+ "minami-alps.yamanashi.jp",
+ "minobu.yamanashi.jp",
+ "nakamichi.yamanashi.jp",
+ "nanbu.yamanashi.jp",
+ "narusawa.yamanashi.jp",
+ "nirasaki.yamanashi.jp",
+ "nishikatsura.yamanashi.jp",
+ "oshino.yamanashi.jp",
+ "otsuki.yamanashi.jp",
+ "showa.yamanashi.jp",
+ "tabayama.yamanashi.jp",
+ "tsuru.yamanashi.jp",
+ "uenohara.yamanashi.jp",
+ "yamanakako.yamanashi.jp",
+ "yamanashi.yamanashi.jp",
+ "*.ke",
+ "kg",
+ "org.kg",
+ "net.kg",
+ "com.kg",
+ "edu.kg",
+ "gov.kg",
+ "mil.kg",
+ "*.kh",
+ "ki",
+ "edu.ki",
+ "biz.ki",
+ "net.ki",
+ "org.ki",
+ "gov.ki",
+ "info.ki",
+ "com.ki",
+ "km",
+ "org.km",
+ "nom.km",
+ "gov.km",
+ "prd.km",
+ "tm.km",
+ "edu.km",
+ "mil.km",
+ "ass.km",
+ "com.km",
+ "coop.km",
+ "asso.km",
+ "presse.km",
+ "medecin.km",
+ "notaires.km",
+ "pharmaciens.km",
+ "veterinaire.km",
+ "gouv.km",
+ "kn",
+ "net.kn",
+ "org.kn",
+ "edu.kn",
+ "gov.kn",
+ "kp",
+ "com.kp",
+ "edu.kp",
+ "gov.kp",
+ "org.kp",
+ "rep.kp",
+ "tra.kp",
+ "kr",
+ "ac.kr",
+ "co.kr",
+ "es.kr",
+ "go.kr",
+ "hs.kr",
+ "kg.kr",
+ "mil.kr",
+ "ms.kr",
+ "ne.kr",
+ "or.kr",
+ "pe.kr",
+ "re.kr",
+ "sc.kr",
+ "busan.kr",
+ "chungbuk.kr",
+ "chungnam.kr",
+ "daegu.kr",
+ "daejeon.kr",
+ "gangwon.kr",
+ "gwangju.kr",
+ "gyeongbuk.kr",
+ "gyeonggi.kr",
+ "gyeongnam.kr",
+ "incheon.kr",
+ "jeju.kr",
+ "jeonbuk.kr",
+ "jeonnam.kr",
+ "seoul.kr",
+ "ulsan.kr",
+ "*.kw",
+ "ky",
+ "edu.ky",
+ "gov.ky",
+ "com.ky",
+ "org.ky",
+ "net.ky",
+ "kz",
+ "org.kz",
+ "edu.kz",
+ "net.kz",
+ "gov.kz",
+ "mil.kz",
+ "com.kz",
+ "la",
+ "int.la",
+ "net.la",
+ "info.la",
+ "edu.la",
+ "gov.la",
+ "per.la",
+ "com.la",
+ "org.la",
+ "lb",
+ "com.lb",
+ "edu.lb",
+ "gov.lb",
+ "net.lb",
+ "org.lb",
+ "lc",
+ "com.lc",
+ "net.lc",
+ "co.lc",
+ "org.lc",
+ "edu.lc",
+ "gov.lc",
+ "li",
+ "lk",
+ "gov.lk",
+ "sch.lk",
+ "net.lk",
+ "int.lk",
+ "com.lk",
+ "org.lk",
+ "edu.lk",
+ "ngo.lk",
+ "soc.lk",
+ "web.lk",
+ "ltd.lk",
+ "assn.lk",
+ "grp.lk",
+ "hotel.lk",
+ "ac.lk",
+ "lr",
+ "com.lr",
+ "edu.lr",
+ "gov.lr",
+ "org.lr",
+ "net.lr",
+ "ls",
+ "co.ls",
+ "org.ls",
+ "lt",
+ "gov.lt",
+ "lu",
+ "lv",
+ "com.lv",
+ "edu.lv",
+ "gov.lv",
+ "org.lv",
+ "mil.lv",
+ "id.lv",
+ "net.lv",
+ "asn.lv",
+ "conf.lv",
+ "ly",
+ "com.ly",
+ "net.ly",
+ "gov.ly",
+ "plc.ly",
+ "edu.ly",
+ "sch.ly",
+ "med.ly",
+ "org.ly",
+ "id.ly",
+ "ma",
+ "co.ma",
+ "net.ma",
+ "gov.ma",
+ "org.ma",
+ "ac.ma",
+ "press.ma",
+ "mc",
+ "tm.mc",
+ "asso.mc",
+ "md",
+ "me",
+ "co.me",
+ "net.me",
+ "org.me",
+ "edu.me",
+ "ac.me",
+ "gov.me",
+ "its.me",
+ "priv.me",
+ "mg",
+ "org.mg",
+ "nom.mg",
+ "gov.mg",
+ "prd.mg",
+ "tm.mg",
+ "edu.mg",
+ "mil.mg",
+ "com.mg",
+ "mh",
+ "mil",
+ "mk",
+ "com.mk",
+ "org.mk",
+ "net.mk",
+ "edu.mk",
+ "gov.mk",
+ "inf.mk",
+ "name.mk",
+ "ml",
+ "com.ml",
+ "edu.ml",
+ "gouv.ml",
+ "gov.ml",
+ "net.ml",
+ "org.ml",
+ "presse.ml",
+ "*.mm",
+ "mn",
+ "gov.mn",
+ "edu.mn",
+ "org.mn",
+ "mo",
+ "com.mo",
+ "net.mo",
+ "org.mo",
+ "edu.mo",
+ "gov.mo",
+ "mobi",
+ "mp",
+ "mq",
+ "mr",
+ "gov.mr",
+ "ms",
+ "com.ms",
+ "edu.ms",
+ "gov.ms",
+ "net.ms",
+ "org.ms",
+ "mt",
+ "com.mt",
+ "edu.mt",
+ "net.mt",
+ "org.mt",
+ "mu",
+ "com.mu",
+ "net.mu",
+ "org.mu",
+ "gov.mu",
+ "ac.mu",
+ "co.mu",
+ "or.mu",
+ "museum",
+ "academy.museum",
+ "agriculture.museum",
+ "air.museum",
+ "airguard.museum",
+ "alabama.museum",
+ "alaska.museum",
+ "amber.museum",
+ "ambulance.museum",
+ "american.museum",
+ "americana.museum",
+ "americanantiques.museum",
+ "americanart.museum",
+ "amsterdam.museum",
+ "and.museum",
+ "annefrank.museum",
+ "anthro.museum",
+ "anthropology.museum",
+ "antiques.museum",
+ "aquarium.museum",
+ "arboretum.museum",
+ "archaeological.museum",
+ "archaeology.museum",
+ "architecture.museum",
+ "art.museum",
+ "artanddesign.museum",
+ "artcenter.museum",
+ "artdeco.museum",
+ "arteducation.museum",
+ "artgallery.museum",
+ "arts.museum",
+ "artsandcrafts.museum",
+ "asmatart.museum",
+ "assassination.museum",
+ "assisi.museum",
+ "association.museum",
+ "astronomy.museum",
+ "atlanta.museum",
+ "austin.museum",
+ "australia.museum",
+ "automotive.museum",
+ "aviation.museum",
+ "axis.museum",
+ "badajoz.museum",
+ "baghdad.museum",
+ "bahn.museum",
+ "bale.museum",
+ "baltimore.museum",
+ "barcelona.museum",
+ "baseball.museum",
+ "basel.museum",
+ "baths.museum",
+ "bauern.museum",
+ "beauxarts.museum",
+ "beeldengeluid.museum",
+ "bellevue.museum",
+ "bergbau.museum",
+ "berkeley.museum",
+ "berlin.museum",
+ "bern.museum",
+ "bible.museum",
+ "bilbao.museum",
+ "bill.museum",
+ "birdart.museum",
+ "birthplace.museum",
+ "bonn.museum",
+ "boston.museum",
+ "botanical.museum",
+ "botanicalgarden.museum",
+ "botanicgarden.museum",
+ "botany.museum",
+ "brandywinevalley.museum",
+ "brasil.museum",
+ "bristol.museum",
+ "british.museum",
+ "britishcolumbia.museum",
+ "broadcast.museum",
+ "brunel.museum",
+ "brussel.museum",
+ "brussels.museum",
+ "bruxelles.museum",
+ "building.museum",
+ "burghof.museum",
+ "bus.museum",
+ "bushey.museum",
+ "cadaques.museum",
+ "california.museum",
+ "cambridge.museum",
+ "can.museum",
+ "canada.museum",
+ "capebreton.museum",
+ "carrier.museum",
+ "cartoonart.museum",
+ "casadelamoneda.museum",
+ "castle.museum",
+ "castres.museum",
+ "celtic.museum",
+ "center.museum",
+ "chattanooga.museum",
+ "cheltenham.museum",
+ "chesapeakebay.museum",
+ "chicago.museum",
+ "children.museum",
+ "childrens.museum",
+ "childrensgarden.museum",
+ "chiropractic.museum",
+ "chocolate.museum",
+ "christiansburg.museum",
+ "cincinnati.museum",
+ "cinema.museum",
+ "circus.museum",
+ "civilisation.museum",
+ "civilization.museum",
+ "civilwar.museum",
+ "clinton.museum",
+ "clock.museum",
+ "coal.museum",
+ "coastaldefence.museum",
+ "cody.museum",
+ "coldwar.museum",
+ "collection.museum",
+ "colonialwilliamsburg.museum",
+ "coloradoplateau.museum",
+ "columbia.museum",
+ "columbus.museum",
+ "communication.museum",
+ "communications.museum",
+ "community.museum",
+ "computer.museum",
+ "computerhistory.museum",
+ "xn--comunicaes-v6a2o.museum",
+ "contemporary.museum",
+ "contemporaryart.museum",
+ "convent.museum",
+ "copenhagen.museum",
+ "corporation.museum",
+ "xn--correios-e-telecomunicaes-ghc29a.museum",
+ "corvette.museum",
+ "costume.museum",
+ "countryestate.museum",
+ "county.museum",
+ "crafts.museum",
+ "cranbrook.museum",
+ "creation.museum",
+ "cultural.museum",
+ "culturalcenter.museum",
+ "culture.museum",
+ "cyber.museum",
+ "cymru.museum",
+ "dali.museum",
+ "dallas.museum",
+ "database.museum",
+ "ddr.museum",
+ "decorativearts.museum",
+ "delaware.museum",
+ "delmenhorst.museum",
+ "denmark.museum",
+ "depot.museum",
+ "design.museum",
+ "detroit.museum",
+ "dinosaur.museum",
+ "discovery.museum",
+ "dolls.museum",
+ "donostia.museum",
+ "durham.museum",
+ "eastafrica.museum",
+ "eastcoast.museum",
+ "education.museum",
+ "educational.museum",
+ "egyptian.museum",
+ "eisenbahn.museum",
+ "elburg.museum",
+ "elvendrell.museum",
+ "embroidery.museum",
+ "encyclopedic.museum",
+ "england.museum",
+ "entomology.museum",
+ "environment.museum",
+ "environmentalconservation.museum",
+ "epilepsy.museum",
+ "essex.museum",
+ "estate.museum",
+ "ethnology.museum",
+ "exeter.museum",
+ "exhibition.museum",
+ "family.museum",
+ "farm.museum",
+ "farmequipment.museum",
+ "farmers.museum",
+ "farmstead.museum",
+ "field.museum",
+ "figueres.museum",
+ "filatelia.museum",
+ "film.museum",
+ "fineart.museum",
+ "finearts.museum",
+ "finland.museum",
+ "flanders.museum",
+ "florida.museum",
+ "force.museum",
+ "fortmissoula.museum",
+ "fortworth.museum",
+ "foundation.museum",
+ "francaise.museum",
+ "frankfurt.museum",
+ "franziskaner.museum",
+ "freemasonry.museum",
+ "freiburg.museum",
+ "fribourg.museum",
+ "frog.museum",
+ "fundacio.museum",
+ "furniture.museum",
+ "gallery.museum",
+ "garden.museum",
+ "gateway.museum",
+ "geelvinck.museum",
+ "gemological.museum",
+ "geology.museum",
+ "georgia.museum",
+ "giessen.museum",
+ "glas.museum",
+ "glass.museum",
+ "gorge.museum",
+ "grandrapids.museum",
+ "graz.museum",
+ "guernsey.museum",
+ "halloffame.museum",
+ "hamburg.museum",
+ "handson.museum",
+ "harvestcelebration.museum",
+ "hawaii.museum",
+ "health.museum",
+ "heimatunduhren.museum",
+ "hellas.museum",
+ "helsinki.museum",
+ "hembygdsforbund.museum",
+ "heritage.museum",
+ "histoire.museum",
+ "historical.museum",
+ "historicalsociety.museum",
+ "historichouses.museum",
+ "historisch.museum",
+ "historisches.museum",
+ "history.museum",
+ "historyofscience.museum",
+ "horology.museum",
+ "house.museum",
+ "humanities.museum",
+ "illustration.museum",
+ "imageandsound.museum",
+ "indian.museum",
+ "indiana.museum",
+ "indianapolis.museum",
+ "indianmarket.museum",
+ "intelligence.museum",
+ "interactive.museum",
+ "iraq.museum",
+ "iron.museum",
+ "isleofman.museum",
+ "jamison.museum",
+ "jefferson.museum",
+ "jerusalem.museum",
+ "jewelry.museum",
+ "jewish.museum",
+ "jewishart.museum",
+ "jfk.museum",
+ "journalism.museum",
+ "judaica.museum",
+ "judygarland.museum",
+ "juedisches.museum",
+ "juif.museum",
+ "karate.museum",
+ "karikatur.museum",
+ "kids.museum",
+ "koebenhavn.museum",
+ "koeln.museum",
+ "kunst.museum",
+ "kunstsammlung.museum",
+ "kunstunddesign.museum",
+ "labor.museum",
+ "labour.museum",
+ "lajolla.museum",
+ "lancashire.museum",
+ "landes.museum",
+ "lans.museum",
+ "xn--lns-qla.museum",
+ "larsson.museum",
+ "lewismiller.museum",
+ "lincoln.museum",
+ "linz.museum",
+ "living.museum",
+ "livinghistory.museum",
+ "localhistory.museum",
+ "london.museum",
+ "losangeles.museum",
+ "louvre.museum",
+ "loyalist.museum",
+ "lucerne.museum",
+ "luxembourg.museum",
+ "luzern.museum",
+ "mad.museum",
+ "madrid.museum",
+ "mallorca.museum",
+ "manchester.museum",
+ "mansion.museum",
+ "mansions.museum",
+ "manx.museum",
+ "marburg.museum",
+ "maritime.museum",
+ "maritimo.museum",
+ "maryland.museum",
+ "marylhurst.museum",
+ "media.museum",
+ "medical.museum",
+ "medizinhistorisches.museum",
+ "meeres.museum",
+ "memorial.museum",
+ "mesaverde.museum",
+ "michigan.museum",
+ "midatlantic.museum",
+ "military.museum",
+ "mill.museum",
+ "miners.museum",
+ "mining.museum",
+ "minnesota.museum",
+ "missile.museum",
+ "missoula.museum",
+ "modern.museum",
+ "moma.museum",
+ "money.museum",
+ "monmouth.museum",
+ "monticello.museum",
+ "montreal.museum",
+ "moscow.museum",
+ "motorcycle.museum",
+ "muenchen.museum",
+ "muenster.museum",
+ "mulhouse.museum",
+ "muncie.museum",
+ "museet.museum",
+ "museumcenter.museum",
+ "museumvereniging.museum",
+ "music.museum",
+ "national.museum",
+ "nationalfirearms.museum",
+ "nationalheritage.museum",
+ "nativeamerican.museum",
+ "naturalhistory.museum",
+ "naturalhistorymuseum.museum",
+ "naturalsciences.museum",
+ "nature.museum",
+ "naturhistorisches.museum",
+ "natuurwetenschappen.museum",
+ "naumburg.museum",
+ "naval.museum",
+ "nebraska.museum",
+ "neues.museum",
+ "newhampshire.museum",
+ "newjersey.museum",
+ "newmexico.museum",
+ "newport.museum",
+ "newspaper.museum",
+ "newyork.museum",
+ "niepce.museum",
+ "norfolk.museum",
+ "north.museum",
+ "nrw.museum",
+ "nuernberg.museum",
+ "nuremberg.museum",
+ "nyc.museum",
+ "nyny.museum",
+ "oceanographic.museum",
+ "oceanographique.museum",
+ "omaha.museum",
+ "online.museum",
+ "ontario.museum",
+ "openair.museum",
+ "oregon.museum",
+ "oregontrail.museum",
+ "otago.museum",
+ "oxford.museum",
+ "pacific.museum",
+ "paderborn.museum",
+ "palace.museum",
+ "paleo.museum",
+ "palmsprings.museum",
+ "panama.museum",
+ "paris.museum",
+ "pasadena.museum",
+ "pharmacy.museum",
+ "philadelphia.museum",
+ "philadelphiaarea.museum",
+ "philately.museum",
+ "phoenix.museum",
+ "photography.museum",
+ "pilots.museum",
+ "pittsburgh.museum",
+ "planetarium.museum",
+ "plantation.museum",
+ "plants.museum",
+ "plaza.museum",
+ "portal.museum",
+ "portland.museum",
+ "portlligat.museum",
+ "posts-and-telecommunications.museum",
+ "preservation.museum",
+ "presidio.museum",
+ "press.museum",
+ "project.museum",
+ "public.museum",
+ "pubol.museum",
+ "quebec.museum",
+ "railroad.museum",
+ "railway.museum",
+ "research.museum",
+ "resistance.museum",
+ "riodejaneiro.museum",
+ "rochester.museum",
+ "rockart.museum",
+ "roma.museum",
+ "russia.museum",
+ "saintlouis.museum",
+ "salem.museum",
+ "salvadordali.museum",
+ "salzburg.museum",
+ "sandiego.museum",
+ "sanfrancisco.museum",
+ "santabarbara.museum",
+ "santacruz.museum",
+ "santafe.museum",
+ "saskatchewan.museum",
+ "satx.museum",
+ "savannahga.museum",
+ "schlesisches.museum",
+ "schoenbrunn.museum",
+ "schokoladen.museum",
+ "school.museum",
+ "schweiz.museum",
+ "science.museum",
+ "scienceandhistory.museum",
+ "scienceandindustry.museum",
+ "sciencecenter.museum",
+ "sciencecenters.museum",
+ "science-fiction.museum",
+ "sciencehistory.museum",
+ "sciences.museum",
+ "sciencesnaturelles.museum",
+ "scotland.museum",
+ "seaport.museum",
+ "settlement.museum",
+ "settlers.museum",
+ "shell.museum",
+ "sherbrooke.museum",
+ "sibenik.museum",
+ "silk.museum",
+ "ski.museum",
+ "skole.museum",
+ "society.museum",
+ "sologne.museum",
+ "soundandvision.museum",
+ "southcarolina.museum",
+ "southwest.museum",
+ "space.museum",
+ "spy.museum",
+ "square.museum",
+ "stadt.museum",
+ "stalbans.museum",
+ "starnberg.museum",
+ "state.museum",
+ "stateofdelaware.museum",
+ "station.museum",
+ "steam.museum",
+ "steiermark.museum",
+ "stjohn.museum",
+ "stockholm.museum",
+ "stpetersburg.museum",
+ "stuttgart.museum",
+ "suisse.museum",
+ "surgeonshall.museum",
+ "surrey.museum",
+ "svizzera.museum",
+ "sweden.museum",
+ "sydney.museum",
+ "tank.museum",
+ "tcm.museum",
+ "technology.museum",
+ "telekommunikation.museum",
+ "television.museum",
+ "texas.museum",
+ "textile.museum",
+ "theater.museum",
+ "time.museum",
+ "timekeeping.museum",
+ "topology.museum",
+ "torino.museum",
+ "touch.museum",
+ "town.museum",
+ "transport.museum",
+ "tree.museum",
+ "trolley.museum",
+ "trust.museum",
+ "trustee.museum",
+ "uhren.museum",
+ "ulm.museum",
+ "undersea.museum",
+ "university.museum",
+ "usa.museum",
+ "usantiques.museum",
+ "usarts.museum",
+ "uscountryestate.museum",
+ "usculture.museum",
+ "usdecorativearts.museum",
+ "usgarden.museum",
+ "ushistory.museum",
+ "ushuaia.museum",
+ "uslivinghistory.museum",
+ "utah.museum",
+ "uvic.museum",
+ "valley.museum",
+ "vantaa.museum",
+ "versailles.museum",
+ "viking.museum",
+ "village.museum",
+ "virginia.museum",
+ "virtual.museum",
+ "virtuel.museum",
+ "vlaanderen.museum",
+ "volkenkunde.museum",
+ "wales.museum",
+ "wallonie.museum",
+ "war.museum",
+ "washingtondc.museum",
+ "watchandclock.museum",
+ "watch-and-clock.museum",
+ "western.museum",
+ "westfalen.museum",
+ "whaling.museum",
+ "wildlife.museum",
+ "williamsburg.museum",
+ "windmill.museum",
+ "workshop.museum",
+ "york.museum",
+ "yorkshire.museum",
+ "yosemite.museum",
+ "youth.museum",
+ "zoological.museum",
+ "zoology.museum",
+ "xn--9dbhblg6di.museum",
+ "xn--h1aegh.museum",
+ "mv",
+ "aero.mv",
+ "biz.mv",
+ "com.mv",
+ "coop.mv",
+ "edu.mv",
+ "gov.mv",
+ "info.mv",
+ "int.mv",
+ "mil.mv",
+ "museum.mv",
+ "name.mv",
+ "net.mv",
+ "org.mv",
+ "pro.mv",
+ "mw",
+ "ac.mw",
+ "biz.mw",
+ "co.mw",
+ "com.mw",
+ "coop.mw",
+ "edu.mw",
+ "gov.mw",
+ "int.mw",
+ "museum.mw",
+ "net.mw",
+ "org.mw",
+ "mx",
+ "com.mx",
+ "org.mx",
+ "gob.mx",
+ "edu.mx",
+ "net.mx",
+ "my",
+ "com.my",
+ "net.my",
+ "org.my",
+ "gov.my",
+ "edu.my",
+ "mil.my",
+ "name.my",
+ "*.mz",
+ "!teledata.mz",
+ "na",
+ "info.na",
+ "pro.na",
+ "name.na",
+ "school.na",
+ "or.na",
+ "dr.na",
+ "us.na",
+ "mx.na",
+ "ca.na",
+ "in.na",
+ "cc.na",
+ "tv.na",
+ "ws.na",
+ "mobi.na",
+ "co.na",
+ "com.na",
+ "org.na",
+ "name",
+ "nc",
+ "asso.nc",
+ "ne",
+ "net",
+ "nf",
+ "com.nf",
+ "net.nf",
+ "per.nf",
+ "rec.nf",
+ "web.nf",
+ "arts.nf",
+ "firm.nf",
+ "info.nf",
+ "other.nf",
+ "store.nf",
+ "ng",
+ "com.ng",
+ "edu.ng",
+ "name.ng",
+ "net.ng",
+ "org.ng",
+ "sch.ng",
+ "gov.ng",
+ "mil.ng",
+ "mobi.ng",
+ "*.ni",
+ "nl",
+ "bv.nl",
+ "no",
+ "fhs.no",
+ "vgs.no",
+ "fylkesbibl.no",
+ "folkebibl.no",
+ "museum.no",
+ "idrett.no",
+ "priv.no",
+ "mil.no",
+ "stat.no",
+ "dep.no",
+ "kommune.no",
+ "herad.no",
+ "aa.no",
+ "ah.no",
+ "bu.no",
+ "fm.no",
+ "hl.no",
+ "hm.no",
+ "jan-mayen.no",
+ "mr.no",
+ "nl.no",
+ "nt.no",
+ "of.no",
+ "ol.no",
+ "oslo.no",
+ "rl.no",
+ "sf.no",
+ "st.no",
+ "svalbard.no",
+ "tm.no",
+ "tr.no",
+ "va.no",
+ "vf.no",
+ "gs.aa.no",
+ "gs.ah.no",
+ "gs.bu.no",
+ "gs.fm.no",
+ "gs.hl.no",
+ "gs.hm.no",
+ "gs.jan-mayen.no",
+ "gs.mr.no",
+ "gs.nl.no",
+ "gs.nt.no",
+ "gs.of.no",
+ "gs.ol.no",
+ "gs.oslo.no",
+ "gs.rl.no",
+ "gs.sf.no",
+ "gs.st.no",
+ "gs.svalbard.no",
+ "gs.tm.no",
+ "gs.tr.no",
+ "gs.va.no",
+ "gs.vf.no",
+ "akrehamn.no",
+ "xn--krehamn-dxa.no",
+ "algard.no",
+ "xn--lgrd-poac.no",
+ "arna.no",
+ "brumunddal.no",
+ "bryne.no",
+ "bronnoysund.no",
+ "xn--brnnysund-m8ac.no",
+ "drobak.no",
+ "xn--drbak-wua.no",
+ "egersund.no",
+ "fetsund.no",
+ "floro.no",
+ "xn--flor-jra.no",
+ "fredrikstad.no",
+ "hokksund.no",
+ "honefoss.no",
+ "xn--hnefoss-q1a.no",
+ "jessheim.no",
+ "jorpeland.no",
+ "xn--jrpeland-54a.no",
+ "kirkenes.no",
+ "kopervik.no",
+ "krokstadelva.no",
+ "langevag.no",
+ "xn--langevg-jxa.no",
+ "leirvik.no",
+ "mjondalen.no",
+ "xn--mjndalen-64a.no",
+ "mo-i-rana.no",
+ "mosjoen.no",
+ "xn--mosjen-eya.no",
+ "nesoddtangen.no",
+ "orkanger.no",
+ "osoyro.no",
+ "xn--osyro-wua.no",
+ "raholt.no",
+ "xn--rholt-mra.no",
+ "sandnessjoen.no",
+ "xn--sandnessjen-ogb.no",
+ "skedsmokorset.no",
+ "slattum.no",
+ "spjelkavik.no",
+ "stathelle.no",
+ "stavern.no",
+ "stjordalshalsen.no",
+ "xn--stjrdalshalsen-sqb.no",
+ "tananger.no",
+ "tranby.no",
+ "vossevangen.no",
+ "afjord.no",
+ "xn--fjord-lra.no",
+ "agdenes.no",
+ "al.no",
+ "xn--l-1fa.no",
+ "alesund.no",
+ "xn--lesund-hua.no",
+ "alstahaug.no",
+ "alta.no",
+ "xn--lt-liac.no",
+ "alaheadju.no",
+ "xn--laheadju-7ya.no",
+ "alvdal.no",
+ "amli.no",
+ "xn--mli-tla.no",
+ "amot.no",
+ "xn--mot-tla.no",
+ "andebu.no",
+ "andoy.no",
+ "xn--andy-ira.no",
+ "andasuolo.no",
+ "ardal.no",
+ "xn--rdal-poa.no",
+ "aremark.no",
+ "arendal.no",
+ "xn--s-1fa.no",
+ "aseral.no",
+ "xn--seral-lra.no",
+ "asker.no",
+ "askim.no",
+ "askvoll.no",
+ "askoy.no",
+ "xn--asky-ira.no",
+ "asnes.no",
+ "xn--snes-poa.no",
+ "audnedaln.no",
+ "aukra.no",
+ "aure.no",
+ "aurland.no",
+ "aurskog-holand.no",
+ "xn--aurskog-hland-jnb.no",
+ "austevoll.no",
+ "austrheim.no",
+ "averoy.no",
+ "xn--avery-yua.no",
+ "balestrand.no",
+ "ballangen.no",
+ "balat.no",
+ "xn--blt-elab.no",
+ "balsfjord.no",
+ "bahccavuotna.no",
+ "xn--bhccavuotna-k7a.no",
+ "bamble.no",
+ "bardu.no",
+ "beardu.no",
+ "beiarn.no",
+ "bajddar.no",
+ "xn--bjddar-pta.no",
+ "baidar.no",
+ "xn--bidr-5nac.no",
+ "berg.no",
+ "bergen.no",
+ "berlevag.no",
+ "xn--berlevg-jxa.no",
+ "bearalvahki.no",
+ "xn--bearalvhki-y4a.no",
+ "bindal.no",
+ "birkenes.no",
+ "bjarkoy.no",
+ "xn--bjarky-fya.no",
+ "bjerkreim.no",
+ "bjugn.no",
+ "bodo.no",
+ "xn--bod-2na.no",
+ "badaddja.no",
+ "xn--bdddj-mrabd.no",
+ "budejju.no",
+ "bokn.no",
+ "bremanger.no",
+ "bronnoy.no",
+ "xn--brnny-wuac.no",
+ "bygland.no",
+ "bykle.no",
+ "barum.no",
+ "xn--brum-voa.no",
+ "bo.telemark.no",
+ "xn--b-5ga.telemark.no",
+ "bo.nordland.no",
+ "xn--b-5ga.nordland.no",
+ "bievat.no",
+ "xn--bievt-0qa.no",
+ "bomlo.no",
+ "xn--bmlo-gra.no",
+ "batsfjord.no",
+ "xn--btsfjord-9za.no",
+ "bahcavuotna.no",
+ "xn--bhcavuotna-s4a.no",
+ "dovre.no",
+ "drammen.no",
+ "drangedal.no",
+ "dyroy.no",
+ "xn--dyry-ira.no",
+ "donna.no",
+ "xn--dnna-gra.no",
+ "eid.no",
+ "eidfjord.no",
+ "eidsberg.no",
+ "eidskog.no",
+ "eidsvoll.no",
+ "eigersund.no",
+ "elverum.no",
+ "enebakk.no",
+ "engerdal.no",
+ "etne.no",
+ "etnedal.no",
+ "evenes.no",
+ "evenassi.no",
+ "xn--eveni-0qa01ga.no",
+ "evje-og-hornnes.no",
+ "farsund.no",
+ "fauske.no",
+ "fuossko.no",
+ "fuoisku.no",
+ "fedje.no",
+ "fet.no",
+ "finnoy.no",
+ "xn--finny-yua.no",
+ "fitjar.no",
+ "fjaler.no",
+ "fjell.no",
+ "flakstad.no",
+ "flatanger.no",
+ "flekkefjord.no",
+ "flesberg.no",
+ "flora.no",
+ "fla.no",
+ "xn--fl-zia.no",
+ "folldal.no",
+ "forsand.no",
+ "fosnes.no",
+ "frei.no",
+ "frogn.no",
+ "froland.no",
+ "frosta.no",
+ "frana.no",
+ "xn--frna-woa.no",
+ "froya.no",
+ "xn--frya-hra.no",
+ "fusa.no",
+ "fyresdal.no",
+ "forde.no",
+ "xn--frde-gra.no",
+ "gamvik.no",
+ "gangaviika.no",
+ "xn--ggaviika-8ya47h.no",
+ "gaular.no",
+ "gausdal.no",
+ "gildeskal.no",
+ "xn--gildeskl-g0a.no",
+ "giske.no",
+ "gjemnes.no",
+ "gjerdrum.no",
+ "gjerstad.no",
+ "gjesdal.no",
+ "gjovik.no",
+ "xn--gjvik-wua.no",
+ "gloppen.no",
+ "gol.no",
+ "gran.no",
+ "grane.no",
+ "granvin.no",
+ "gratangen.no",
+ "grimstad.no",
+ "grong.no",
+ "kraanghke.no",
+ "xn--kranghke-b0a.no",
+ "grue.no",
+ "gulen.no",
+ "hadsel.no",
+ "halden.no",
+ "halsa.no",
+ "hamar.no",
+ "hamaroy.no",
+ "habmer.no",
+ "xn--hbmer-xqa.no",
+ "hapmir.no",
+ "xn--hpmir-xqa.no",
+ "hammerfest.no",
+ "hammarfeasta.no",
+ "xn--hmmrfeasta-s4ac.no",
+ "haram.no",
+ "hareid.no",
+ "harstad.no",
+ "hasvik.no",
+ "aknoluokta.no",
+ "xn--koluokta-7ya57h.no",
+ "hattfjelldal.no",
+ "aarborte.no",
+ "haugesund.no",
+ "hemne.no",
+ "hemnes.no",
+ "hemsedal.no",
+ "heroy.more-og-romsdal.no",
+ "xn--hery-ira.xn--mre-og-romsdal-qqb.no",
+ "heroy.nordland.no",
+ "xn--hery-ira.nordland.no",
+ "hitra.no",
+ "hjartdal.no",
+ "hjelmeland.no",
+ "hobol.no",
+ "xn--hobl-ira.no",
+ "hof.no",
+ "hol.no",
+ "hole.no",
+ "holmestrand.no",
+ "holtalen.no",
+ "xn--holtlen-hxa.no",
+ "hornindal.no",
+ "horten.no",
+ "hurdal.no",
+ "hurum.no",
+ "hvaler.no",
+ "hyllestad.no",
+ "hagebostad.no",
+ "xn--hgebostad-g3a.no",
+ "hoyanger.no",
+ "xn--hyanger-q1a.no",
+ "hoylandet.no",
+ "xn--hylandet-54a.no",
+ "ha.no",
+ "xn--h-2fa.no",
+ "ibestad.no",
+ "inderoy.no",
+ "xn--indery-fya.no",
+ "iveland.no",
+ "jevnaker.no",
+ "jondal.no",
+ "jolster.no",
+ "xn--jlster-bya.no",
+ "karasjok.no",
+ "karasjohka.no",
+ "xn--krjohka-hwab49j.no",
+ "karlsoy.no",
+ "galsa.no",
+ "xn--gls-elac.no",
+ "karmoy.no",
+ "xn--karmy-yua.no",
+ "kautokeino.no",
+ "guovdageaidnu.no",
+ "klepp.no",
+ "klabu.no",
+ "xn--klbu-woa.no",
+ "kongsberg.no",
+ "kongsvinger.no",
+ "kragero.no",
+ "xn--krager-gya.no",
+ "kristiansand.no",
+ "kristiansund.no",
+ "krodsherad.no",
+ "xn--krdsherad-m8a.no",
+ "kvalsund.no",
+ "rahkkeravju.no",
+ "xn--rhkkervju-01af.no",
+ "kvam.no",
+ "kvinesdal.no",
+ "kvinnherad.no",
+ "kviteseid.no",
+ "kvitsoy.no",
+ "xn--kvitsy-fya.no",
+ "kvafjord.no",
+ "xn--kvfjord-nxa.no",
+ "giehtavuoatna.no",
+ "kvanangen.no",
+ "xn--kvnangen-k0a.no",
+ "navuotna.no",
+ "xn--nvuotna-hwa.no",
+ "kafjord.no",
+ "xn--kfjord-iua.no",
+ "gaivuotna.no",
+ "xn--givuotna-8ya.no",
+ "larvik.no",
+ "lavangen.no",
+ "lavagis.no",
+ "loabat.no",
+ "xn--loabt-0qa.no",
+ "lebesby.no",
+ "davvesiida.no",
+ "leikanger.no",
+ "leirfjord.no",
+ "leka.no",
+ "leksvik.no",
+ "lenvik.no",
+ "leangaviika.no",
+ "xn--leagaviika-52b.no",
+ "lesja.no",
+ "levanger.no",
+ "lier.no",
+ "lierne.no",
+ "lillehammer.no",
+ "lillesand.no",
+ "lindesnes.no",
+ "lindas.no",
+ "xn--linds-pra.no",
+ "lom.no",
+ "loppa.no",
+ "lahppi.no",
+ "xn--lhppi-xqa.no",
+ "lund.no",
+ "lunner.no",
+ "luroy.no",
+ "xn--lury-ira.no",
+ "luster.no",
+ "lyngdal.no",
+ "lyngen.no",
+ "ivgu.no",
+ "lardal.no",
+ "lerdal.no",
+ "xn--lrdal-sra.no",
+ "lodingen.no",
+ "xn--ldingen-q1a.no",
+ "lorenskog.no",
+ "xn--lrenskog-54a.no",
+ "loten.no",
+ "xn--lten-gra.no",
+ "malvik.no",
+ "masoy.no",
+ "xn--msy-ula0h.no",
+ "muosat.no",
+ "xn--muost-0qa.no",
+ "mandal.no",
+ "marker.no",
+ "marnardal.no",
+ "masfjorden.no",
+ "meland.no",
+ "meldal.no",
+ "melhus.no",
+ "meloy.no",
+ "xn--mely-ira.no",
+ "meraker.no",
+ "xn--merker-kua.no",
+ "moareke.no",
+ "xn--moreke-jua.no",
+ "midsund.no",
+ "midtre-gauldal.no",
+ "modalen.no",
+ "modum.no",
+ "molde.no",
+ "moskenes.no",
+ "moss.no",
+ "mosvik.no",
+ "malselv.no",
+ "xn--mlselv-iua.no",
+ "malatvuopmi.no",
+ "xn--mlatvuopmi-s4a.no",
+ "namdalseid.no",
+ "aejrie.no",
+ "namsos.no",
+ "namsskogan.no",
+ "naamesjevuemie.no",
+ "xn--nmesjevuemie-tcba.no",
+ "laakesvuemie.no",
+ "nannestad.no",
+ "narvik.no",
+ "narviika.no",
+ "naustdal.no",
+ "nedre-eiker.no",
+ "nes.akershus.no",
+ "nes.buskerud.no",
+ "nesna.no",
+ "nesodden.no",
+ "nesseby.no",
+ "unjarga.no",
+ "xn--unjrga-rta.no",
+ "nesset.no",
+ "nissedal.no",
+ "nittedal.no",
+ "nord-aurdal.no",
+ "nord-fron.no",
+ "nord-odal.no",
+ "norddal.no",
+ "nordkapp.no",
+ "davvenjarga.no",
+ "xn--davvenjrga-y4a.no",
+ "nordre-land.no",
+ "nordreisa.no",
+ "raisa.no",
+ "xn--risa-5na.no",
+ "nore-og-uvdal.no",
+ "notodden.no",
+ "naroy.no",
+ "xn--nry-yla5g.no",
+ "notteroy.no",
+ "xn--nttery-byae.no",
+ "odda.no",
+ "oksnes.no",
+ "xn--ksnes-uua.no",
+ "oppdal.no",
+ "oppegard.no",
+ "xn--oppegrd-ixa.no",
+ "orkdal.no",
+ "orland.no",
+ "xn--rland-uua.no",
+ "orskog.no",
+ "xn--rskog-uua.no",
+ "orsta.no",
+ "xn--rsta-fra.no",
+ "os.hedmark.no",
+ "os.hordaland.no",
+ "osen.no",
+ "osteroy.no",
+ "xn--ostery-fya.no",
+ "ostre-toten.no",
+ "xn--stre-toten-zcb.no",
+ "overhalla.no",
+ "ovre-eiker.no",
+ "xn--vre-eiker-k8a.no",
+ "oyer.no",
+ "xn--yer-zna.no",
+ "oygarden.no",
+ "xn--ygarden-p1a.no",
+ "oystre-slidre.no",
+ "xn--ystre-slidre-ujb.no",
+ "porsanger.no",
+ "porsangu.no",
+ "xn--porsgu-sta26f.no",
+ "porsgrunn.no",
+ "radoy.no",
+ "xn--rady-ira.no",
+ "rakkestad.no",
+ "rana.no",
+ "ruovat.no",
+ "randaberg.no",
+ "rauma.no",
+ "rendalen.no",
+ "rennebu.no",
+ "rennesoy.no",
+ "xn--rennesy-v1a.no",
+ "rindal.no",
+ "ringebu.no",
+ "ringerike.no",
+ "ringsaker.no",
+ "rissa.no",
+ "risor.no",
+ "xn--risr-ira.no",
+ "roan.no",
+ "rollag.no",
+ "rygge.no",
+ "ralingen.no",
+ "xn--rlingen-mxa.no",
+ "rodoy.no",
+ "xn--rdy-0nab.no",
+ "romskog.no",
+ "xn--rmskog-bya.no",
+ "roros.no",
+ "xn--rros-gra.no",
+ "rost.no",
+ "xn--rst-0na.no",
+ "royken.no",
+ "xn--ryken-vua.no",
+ "royrvik.no",
+ "xn--ryrvik-bya.no",
+ "rade.no",
+ "xn--rde-ula.no",
+ "salangen.no",
+ "siellak.no",
+ "saltdal.no",
+ "salat.no",
+ "xn--slt-elab.no",
+ "xn--slat-5na.no",
+ "samnanger.no",
+ "sande.more-og-romsdal.no",
+ "sande.xn--mre-og-romsdal-qqb.no",
+ "sande.vestfold.no",
+ "sandefjord.no",
+ "sandnes.no",
+ "sandoy.no",
+ "xn--sandy-yua.no",
+ "sarpsborg.no",
+ "sauda.no",
+ "sauherad.no",
+ "sel.no",
+ "selbu.no",
+ "selje.no",
+ "seljord.no",
+ "sigdal.no",
+ "siljan.no",
+ "sirdal.no",
+ "skaun.no",
+ "skedsmo.no",
+ "ski.no",
+ "skien.no",
+ "skiptvet.no",
+ "skjervoy.no",
+ "xn--skjervy-v1a.no",
+ "skierva.no",
+ "xn--skierv-uta.no",
+ "skjak.no",
+ "xn--skjk-soa.no",
+ "skodje.no",
+ "skanland.no",
+ "xn--sknland-fxa.no",
+ "skanit.no",
+ "xn--sknit-yqa.no",
+ "smola.no",
+ "xn--smla-hra.no",
+ "snillfjord.no",
+ "snasa.no",
+ "xn--snsa-roa.no",
+ "snoasa.no",
+ "snaase.no",
+ "xn--snase-nra.no",
+ "sogndal.no",
+ "sokndal.no",
+ "sola.no",
+ "solund.no",
+ "songdalen.no",
+ "sortland.no",
+ "spydeberg.no",
+ "stange.no",
+ "stavanger.no",
+ "steigen.no",
+ "steinkjer.no",
+ "stjordal.no",
+ "xn--stjrdal-s1a.no",
+ "stokke.no",
+ "stor-elvdal.no",
+ "stord.no",
+ "stordal.no",
+ "storfjord.no",
+ "omasvuotna.no",
+ "strand.no",
+ "stranda.no",
+ "stryn.no",
+ "sula.no",
+ "suldal.no",
+ "sund.no",
+ "sunndal.no",
+ "surnadal.no",
+ "sveio.no",
+ "svelvik.no",
+ "sykkylven.no",
+ "sogne.no",
+ "xn--sgne-gra.no",
+ "somna.no",
+ "xn--smna-gra.no",
+ "sondre-land.no",
+ "xn--sndre-land-0cb.no",
+ "sor-aurdal.no",
+ "xn--sr-aurdal-l8a.no",
+ "sor-fron.no",
+ "xn--sr-fron-q1a.no",
+ "sor-odal.no",
+ "xn--sr-odal-q1a.no",
+ "sor-varanger.no",
+ "xn--sr-varanger-ggb.no",
+ "matta-varjjat.no",
+ "xn--mtta-vrjjat-k7af.no",
+ "sorfold.no",
+ "xn--srfold-bya.no",
+ "sorreisa.no",
+ "xn--srreisa-q1a.no",
+ "sorum.no",
+ "xn--srum-gra.no",
+ "tana.no",
+ "deatnu.no",
+ "time.no",
+ "tingvoll.no",
+ "tinn.no",
+ "tjeldsund.no",
+ "dielddanuorri.no",
+ "tjome.no",
+ "xn--tjme-hra.no",
+ "tokke.no",
+ "tolga.no",
+ "torsken.no",
+ "tranoy.no",
+ "xn--trany-yua.no",
+ "tromso.no",
+ "xn--troms-zua.no",
+ "tromsa.no",
+ "romsa.no",
+ "trondheim.no",
+ "troandin.no",
+ "trysil.no",
+ "trana.no",
+ "xn--trna-woa.no",
+ "trogstad.no",
+ "xn--trgstad-r1a.no",
+ "tvedestrand.no",
+ "tydal.no",
+ "tynset.no",
+ "tysfjord.no",
+ "divtasvuodna.no",
+ "divttasvuotna.no",
+ "tysnes.no",
+ "tysvar.no",
+ "xn--tysvr-vra.no",
+ "tonsberg.no",
+ "xn--tnsberg-q1a.no",
+ "ullensaker.no",
+ "ullensvang.no",
+ "ulvik.no",
+ "utsira.no",
+ "vadso.no",
+ "xn--vads-jra.no",
+ "cahcesuolo.no",
+ "xn--hcesuolo-7ya35b.no",
+ "vaksdal.no",
+ "valle.no",
+ "vang.no",
+ "vanylven.no",
+ "vardo.no",
+ "xn--vard-jra.no",
+ "varggat.no",
+ "xn--vrggt-xqad.no",
+ "vefsn.no",
+ "vaapste.no",
+ "vega.no",
+ "vegarshei.no",
+ "xn--vegrshei-c0a.no",
+ "vennesla.no",
+ "verdal.no",
+ "verran.no",
+ "vestby.no",
+ "vestnes.no",
+ "vestre-slidre.no",
+ "vestre-toten.no",
+ "vestvagoy.no",
+ "xn--vestvgy-ixa6o.no",
+ "vevelstad.no",
+ "vik.no",
+ "vikna.no",
+ "vindafjord.no",
+ "volda.no",
+ "voss.no",
+ "varoy.no",
+ "xn--vry-yla5g.no",
+ "vagan.no",
+ "xn--vgan-qoa.no",
+ "voagat.no",
+ "vagsoy.no",
+ "xn--vgsy-qoa0j.no",
+ "vaga.no",
+ "xn--vg-yiab.no",
+ "valer.ostfold.no",
+ "xn--vler-qoa.xn--stfold-9xa.no",
+ "valer.hedmark.no",
+ "xn--vler-qoa.hedmark.no",
+ "*.np",
+ "nr",
+ "biz.nr",
+ "info.nr",
+ "gov.nr",
+ "edu.nr",
+ "org.nr",
+ "net.nr",
+ "com.nr",
+ "nu",
+ "nz",
+ "ac.nz",
+ "co.nz",
+ "cri.nz",
+ "geek.nz",
+ "gen.nz",
+ "govt.nz",
+ "health.nz",
+ "iwi.nz",
+ "kiwi.nz",
+ "maori.nz",
+ "mil.nz",
+ "xn--mori-qsa.nz",
+ "net.nz",
+ "org.nz",
+ "parliament.nz",
+ "school.nz",
+ "om",
+ "co.om",
+ "com.om",
+ "edu.om",
+ "gov.om",
+ "med.om",
+ "museum.om",
+ "net.om",
+ "org.om",
+ "pro.om",
+ "org",
+ "pa",
+ "ac.pa",
+ "gob.pa",
+ "com.pa",
+ "org.pa",
+ "sld.pa",
+ "edu.pa",
+ "net.pa",
+ "ing.pa",
+ "abo.pa",
+ "med.pa",
+ "nom.pa",
+ "pe",
+ "edu.pe",
+ "gob.pe",
+ "nom.pe",
+ "mil.pe",
+ "org.pe",
+ "com.pe",
+ "net.pe",
+ "pf",
+ "com.pf",
+ "org.pf",
+ "edu.pf",
+ "*.pg",
+ "ph",
+ "com.ph",
+ "net.ph",
+ "org.ph",
+ "gov.ph",
+ "edu.ph",
+ "ngo.ph",
+ "mil.ph",
+ "i.ph",
+ "pk",
+ "com.pk",
+ "net.pk",
+ "edu.pk",
+ "org.pk",
+ "fam.pk",
+ "biz.pk",
+ "web.pk",
+ "gov.pk",
+ "gob.pk",
+ "gok.pk",
+ "gon.pk",
+ "gop.pk",
+ "gos.pk",
+ "info.pk",
+ "pl",
+ "com.pl",
+ "net.pl",
+ "org.pl",
+ "aid.pl",
+ "agro.pl",
+ "atm.pl",
+ "auto.pl",
+ "biz.pl",
+ "edu.pl",
+ "gmina.pl",
+ "gsm.pl",
+ "info.pl",
+ "mail.pl",
+ "miasta.pl",
+ "media.pl",
+ "mil.pl",
+ "nieruchomosci.pl",
+ "nom.pl",
+ "pc.pl",
+ "powiat.pl",
+ "priv.pl",
+ "realestate.pl",
+ "rel.pl",
+ "sex.pl",
+ "shop.pl",
+ "sklep.pl",
+ "sos.pl",
+ "szkola.pl",
+ "targi.pl",
+ "tm.pl",
+ "tourism.pl",
+ "travel.pl",
+ "turystyka.pl",
+ "gov.pl",
+ "ap.gov.pl",
+ "ic.gov.pl",
+ "is.gov.pl",
+ "us.gov.pl",
+ "kmpsp.gov.pl",
+ "kppsp.gov.pl",
+ "kwpsp.gov.pl",
+ "psp.gov.pl",
+ "wskr.gov.pl",
+ "kwp.gov.pl",
+ "mw.gov.pl",
+ "ug.gov.pl",
+ "um.gov.pl",
+ "umig.gov.pl",
+ "ugim.gov.pl",
+ "upow.gov.pl",
+ "uw.gov.pl",
+ "starostwo.gov.pl",
+ "pa.gov.pl",
+ "po.gov.pl",
+ "psse.gov.pl",
+ "pup.gov.pl",
+ "rzgw.gov.pl",
+ "sa.gov.pl",
+ "so.gov.pl",
+ "sr.gov.pl",
+ "wsa.gov.pl",
+ "sko.gov.pl",
+ "uzs.gov.pl",
+ "wiih.gov.pl",
+ "winb.gov.pl",
+ "pinb.gov.pl",
+ "wios.gov.pl",
+ "witd.gov.pl",
+ "wzmiuw.gov.pl",
+ "piw.gov.pl",
+ "wiw.gov.pl",
+ "griw.gov.pl",
+ "wif.gov.pl",
+ "oum.gov.pl",
+ "sdn.gov.pl",
+ "zp.gov.pl",
+ "uppo.gov.pl",
+ "mup.gov.pl",
+ "wuoz.gov.pl",
+ "konsulat.gov.pl",
+ "oirm.gov.pl",
+ "augustow.pl",
+ "babia-gora.pl",
+ "bedzin.pl",
+ "beskidy.pl",
+ "bialowieza.pl",
+ "bialystok.pl",
+ "bielawa.pl",
+ "bieszczady.pl",
+ "boleslawiec.pl",
+ "bydgoszcz.pl",
+ "bytom.pl",
+ "cieszyn.pl",
+ "czeladz.pl",
+ "czest.pl",
+ "dlugoleka.pl",
+ "elblag.pl",
+ "elk.pl",
+ "glogow.pl",
+ "gniezno.pl",
+ "gorlice.pl",
+ "grajewo.pl",
+ "ilawa.pl",
+ "jaworzno.pl",
+ "jelenia-gora.pl",
+ "jgora.pl",
+ "kalisz.pl",
+ "kazimierz-dolny.pl",
+ "karpacz.pl",
+ "kartuzy.pl",
+ "kaszuby.pl",
+ "katowice.pl",
+ "kepno.pl",
+ "ketrzyn.pl",
+ "klodzko.pl",
+ "kobierzyce.pl",
+ "kolobrzeg.pl",
+ "konin.pl",
+ "konskowola.pl",
+ "kutno.pl",
+ "lapy.pl",
+ "lebork.pl",
+ "legnica.pl",
+ "lezajsk.pl",
+ "limanowa.pl",
+ "lomza.pl",
+ "lowicz.pl",
+ "lubin.pl",
+ "lukow.pl",
+ "malbork.pl",
+ "malopolska.pl",
+ "mazowsze.pl",
+ "mazury.pl",
+ "mielec.pl",
+ "mielno.pl",
+ "mragowo.pl",
+ "naklo.pl",
+ "nowaruda.pl",
+ "nysa.pl",
+ "olawa.pl",
+ "olecko.pl",
+ "olkusz.pl",
+ "olsztyn.pl",
+ "opoczno.pl",
+ "opole.pl",
+ "ostroda.pl",
+ "ostroleka.pl",
+ "ostrowiec.pl",
+ "ostrowwlkp.pl",
+ "pila.pl",
+ "pisz.pl",
+ "podhale.pl",
+ "podlasie.pl",
+ "polkowice.pl",
+ "pomorze.pl",
+ "pomorskie.pl",
+ "prochowice.pl",
+ "pruszkow.pl",
+ "przeworsk.pl",
+ "pulawy.pl",
+ "radom.pl",
+ "rawa-maz.pl",
+ "rybnik.pl",
+ "rzeszow.pl",
+ "sanok.pl",
+ "sejny.pl",
+ "slask.pl",
+ "slupsk.pl",
+ "sosnowiec.pl",
+ "stalowa-wola.pl",
+ "skoczow.pl",
+ "starachowice.pl",
+ "stargard.pl",
+ "suwalki.pl",
+ "swidnica.pl",
+ "swiebodzin.pl",
+ "swinoujscie.pl",
+ "szczecin.pl",
+ "szczytno.pl",
+ "tarnobrzeg.pl",
+ "tgory.pl",
+ "turek.pl",
+ "tychy.pl",
+ "ustka.pl",
+ "walbrzych.pl",
+ "warmia.pl",
+ "warszawa.pl",
+ "waw.pl",
+ "wegrow.pl",
+ "wielun.pl",
+ "wlocl.pl",
+ "wloclawek.pl",
+ "wodzislaw.pl",
+ "wolomin.pl",
+ "wroclaw.pl",
+ "zachpomor.pl",
+ "zagan.pl",
+ "zarow.pl",
+ "zgora.pl",
+ "zgorzelec.pl",
+ "pm",
+ "pn",
+ "gov.pn",
+ "co.pn",
+ "org.pn",
+ "edu.pn",
+ "net.pn",
+ "post",
+ "pr",
+ "com.pr",
+ "net.pr",
+ "org.pr",
+ "gov.pr",
+ "edu.pr",
+ "isla.pr",
+ "pro.pr",
+ "biz.pr",
+ "info.pr",
+ "name.pr",
+ "est.pr",
+ "prof.pr",
+ "ac.pr",
+ "pro",
+ "aca.pro",
+ "bar.pro",
+ "cpa.pro",
+ "jur.pro",
+ "law.pro",
+ "med.pro",
+ "eng.pro",
+ "ps",
+ "edu.ps",
+ "gov.ps",
+ "sec.ps",
+ "plo.ps",
+ "com.ps",
+ "org.ps",
+ "net.ps",
+ "pt",
+ "net.pt",
+ "gov.pt",
+ "org.pt",
+ "edu.pt",
+ "int.pt",
+ "publ.pt",
+ "com.pt",
+ "nome.pt",
+ "pw",
+ "co.pw",
+ "ne.pw",
+ "or.pw",
+ "ed.pw",
+ "go.pw",
+ "belau.pw",
+ "py",
+ "com.py",
+ "coop.py",
+ "edu.py",
+ "gov.py",
+ "mil.py",
+ "net.py",
+ "org.py",
+ "qa",
+ "com.qa",
+ "edu.qa",
+ "gov.qa",
+ "mil.qa",
+ "name.qa",
+ "net.qa",
+ "org.qa",
+ "sch.qa",
+ "re",
+ "com.re",
+ "asso.re",
+ "nom.re",
+ "ro",
+ "com.ro",
+ "org.ro",
+ "tm.ro",
+ "nt.ro",
+ "nom.ro",
+ "info.ro",
+ "rec.ro",
+ "arts.ro",
+ "firm.ro",
+ "store.ro",
+ "www.ro",
+ "rs",
+ "co.rs",
+ "org.rs",
+ "edu.rs",
+ "ac.rs",
+ "gov.rs",
+ "in.rs",
+ "ru",
+ "ac.ru",
+ "com.ru",
+ "edu.ru",
+ "int.ru",
+ "net.ru",
+ "org.ru",
+ "pp.ru",
+ "adygeya.ru",
+ "altai.ru",
+ "amur.ru",
+ "arkhangelsk.ru",
+ "astrakhan.ru",
+ "bashkiria.ru",
+ "belgorod.ru",
+ "bir.ru",
+ "bryansk.ru",
+ "buryatia.ru",
+ "cbg.ru",
+ "chel.ru",
+ "chelyabinsk.ru",
+ "chita.ru",
+ "chukotka.ru",
+ "chuvashia.ru",
+ "dagestan.ru",
+ "dudinka.ru",
+ "e-burg.ru",
+ "grozny.ru",
+ "irkutsk.ru",
+ "ivanovo.ru",
+ "izhevsk.ru",
+ "jar.ru",
+ "joshkar-ola.ru",
+ "kalmykia.ru",
+ "kaluga.ru",
+ "kamchatka.ru",
+ "karelia.ru",
+ "kazan.ru",
+ "kchr.ru",
+ "kemerovo.ru",
+ "khabarovsk.ru",
+ "khakassia.ru",
+ "khv.ru",
+ "kirov.ru",
+ "koenig.ru",
+ "komi.ru",
+ "kostroma.ru",
+ "krasnoyarsk.ru",
+ "kuban.ru",
+ "kurgan.ru",
+ "kursk.ru",
+ "lipetsk.ru",
+ "magadan.ru",
+ "mari.ru",
+ "mari-el.ru",
+ "marine.ru",
+ "mordovia.ru",
+ "msk.ru",
+ "murmansk.ru",
+ "nalchik.ru",
+ "nnov.ru",
+ "nov.ru",
+ "novosibirsk.ru",
+ "nsk.ru",
+ "omsk.ru",
+ "orenburg.ru",
+ "oryol.ru",
+ "palana.ru",
+ "penza.ru",
+ "perm.ru",
+ "ptz.ru",
+ "rnd.ru",
+ "ryazan.ru",
+ "sakhalin.ru",
+ "samara.ru",
+ "saratov.ru",
+ "simbirsk.ru",
+ "smolensk.ru",
+ "spb.ru",
+ "stavropol.ru",
+ "stv.ru",
+ "surgut.ru",
+ "tambov.ru",
+ "tatarstan.ru",
+ "tom.ru",
+ "tomsk.ru",
+ "tsaritsyn.ru",
+ "tsk.ru",
+ "tula.ru",
+ "tuva.ru",
+ "tver.ru",
+ "tyumen.ru",
+ "udm.ru",
+ "udmurtia.ru",
+ "ulan-ude.ru",
+ "vladikavkaz.ru",
+ "vladimir.ru",
+ "vladivostok.ru",
+ "volgograd.ru",
+ "vologda.ru",
+ "voronezh.ru",
+ "vrn.ru",
+ "vyatka.ru",
+ "yakutia.ru",
+ "yamal.ru",
+ "yaroslavl.ru",
+ "yekaterinburg.ru",
+ "yuzhno-sakhalinsk.ru",
+ "amursk.ru",
+ "baikal.ru",
+ "cmw.ru",
+ "fareast.ru",
+ "jamal.ru",
+ "kms.ru",
+ "k-uralsk.ru",
+ "kustanai.ru",
+ "kuzbass.ru",
+ "magnitka.ru",
+ "mytis.ru",
+ "nakhodka.ru",
+ "nkz.ru",
+ "norilsk.ru",
+ "oskol.ru",
+ "pyatigorsk.ru",
+ "rubtsovsk.ru",
+ "snz.ru",
+ "syzran.ru",
+ "vdonsk.ru",
+ "zgrad.ru",
+ "gov.ru",
+ "mil.ru",
+ "test.ru",
+ "rw",
+ "gov.rw",
+ "net.rw",
+ "edu.rw",
+ "ac.rw",
+ "com.rw",
+ "co.rw",
+ "int.rw",
+ "mil.rw",
+ "gouv.rw",
+ "sa",
+ "com.sa",
+ "net.sa",
+ "org.sa",
+ "gov.sa",
+ "med.sa",
+ "pub.sa",
+ "edu.sa",
+ "sch.sa",
+ "sb",
+ "com.sb",
+ "edu.sb",
+ "gov.sb",
+ "net.sb",
+ "org.sb",
+ "sc",
+ "com.sc",
+ "gov.sc",
+ "net.sc",
+ "org.sc",
+ "edu.sc",
+ "sd",
+ "com.sd",
+ "net.sd",
+ "org.sd",
+ "edu.sd",
+ "med.sd",
+ "tv.sd",
+ "gov.sd",
+ "info.sd",
+ "se",
+ "a.se",
+ "ac.se",
+ "b.se",
+ "bd.se",
+ "brand.se",
+ "c.se",
+ "d.se",
+ "e.se",
+ "f.se",
+ "fh.se",
+ "fhsk.se",
+ "fhv.se",
+ "g.se",
+ "h.se",
+ "i.se",
+ "k.se",
+ "komforb.se",
+ "kommunalforbund.se",
+ "komvux.se",
+ "l.se",
+ "lanbib.se",
+ "m.se",
+ "n.se",
+ "naturbruksgymn.se",
+ "o.se",
+ "org.se",
+ "p.se",
+ "parti.se",
+ "pp.se",
+ "press.se",
+ "r.se",
+ "s.se",
+ "t.se",
+ "tm.se",
+ "u.se",
+ "w.se",
+ "x.se",
+ "y.se",
+ "z.se",
+ "sg",
+ "com.sg",
+ "net.sg",
+ "org.sg",
+ "gov.sg",
+ "edu.sg",
+ "per.sg",
+ "sh",
+ "com.sh",
+ "net.sh",
+ "gov.sh",
+ "org.sh",
+ "mil.sh",
+ "si",
+ "sj",
+ "sk",
+ "sl",
+ "com.sl",
+ "net.sl",
+ "edu.sl",
+ "gov.sl",
+ "org.sl",
+ "sm",
+ "sn",
+ "art.sn",
+ "com.sn",
+ "edu.sn",
+ "gouv.sn",
+ "org.sn",
+ "perso.sn",
+ "univ.sn",
+ "so",
+ "com.so",
+ "net.so",
+ "org.so",
+ "sr",
+ "st",
+ "co.st",
+ "com.st",
+ "consulado.st",
+ "edu.st",
+ "embaixada.st",
+ "gov.st",
+ "mil.st",
+ "net.st",
+ "org.st",
+ "principe.st",
+ "saotome.st",
+ "store.st",
+ "su",
+ "adygeya.su",
+ "arkhangelsk.su",
+ "balashov.su",
+ "bashkiria.su",
+ "bryansk.su",
+ "dagestan.su",
+ "grozny.su",
+ "ivanovo.su",
+ "kalmykia.su",
+ "kaluga.su",
+ "karelia.su",
+ "khakassia.su",
+ "krasnodar.su",
+ "kurgan.su",
+ "lenug.su",
+ "mordovia.su",
+ "msk.su",
+ "murmansk.su",
+ "nalchik.su",
+ "nov.su",
+ "obninsk.su",
+ "penza.su",
+ "pokrovsk.su",
+ "sochi.su",
+ "spb.su",
+ "togliatti.su",
+ "troitsk.su",
+ "tula.su",
+ "tuva.su",
+ "vladikavkaz.su",
+ "vladimir.su",
+ "vologda.su",
+ "sv",
+ "com.sv",
+ "edu.sv",
+ "gob.sv",
+ "org.sv",
+ "red.sv",
+ "sx",
+ "gov.sx",
+ "sy",
+ "edu.sy",
+ "gov.sy",
+ "net.sy",
+ "mil.sy",
+ "com.sy",
+ "org.sy",
+ "sz",
+ "co.sz",
+ "ac.sz",
+ "org.sz",
+ "tc",
+ "td",
+ "tel",
+ "tf",
+ "tg",
+ "th",
+ "ac.th",
+ "co.th",
+ "go.th",
+ "in.th",
+ "mi.th",
+ "net.th",
+ "or.th",
+ "tj",
+ "ac.tj",
+ "biz.tj",
+ "co.tj",
+ "com.tj",
+ "edu.tj",
+ "go.tj",
+ "gov.tj",
+ "int.tj",
+ "mil.tj",
+ "name.tj",
+ "net.tj",
+ "nic.tj",
+ "org.tj",
+ "test.tj",
+ "web.tj",
+ "tk",
+ "tl",
+ "gov.tl",
+ "tm",
+ "com.tm",
+ "co.tm",
+ "org.tm",
+ "net.tm",
+ "nom.tm",
+ "gov.tm",
+ "mil.tm",
+ "edu.tm",
+ "tn",
+ "com.tn",
+ "ens.tn",
+ "fin.tn",
+ "gov.tn",
+ "ind.tn",
+ "intl.tn",
+ "nat.tn",
+ "net.tn",
+ "org.tn",
+ "info.tn",
+ "perso.tn",
+ "tourism.tn",
+ "edunet.tn",
+ "rnrt.tn",
+ "rns.tn",
+ "rnu.tn",
+ "mincom.tn",
+ "agrinet.tn",
+ "defense.tn",
+ "turen.tn",
+ "to",
+ "com.to",
+ "gov.to",
+ "net.to",
+ "org.to",
+ "edu.to",
+ "mil.to",
+ "tp",
+ "tr",
+ "com.tr",
+ "info.tr",
+ "biz.tr",
+ "net.tr",
+ "org.tr",
+ "web.tr",
+ "gen.tr",
+ "tv.tr",
+ "av.tr",
+ "dr.tr",
+ "bbs.tr",
+ "name.tr",
+ "tel.tr",
+ "gov.tr",
+ "bel.tr",
+ "pol.tr",
+ "mil.tr",
+ "k12.tr",
+ "edu.tr",
+ "kep.tr",
+ "nc.tr",
+ "gov.nc.tr",
+ "travel",
+ "tt",
+ "co.tt",
+ "com.tt",
+ "org.tt",
+ "net.tt",
+ "biz.tt",
+ "info.tt",
+ "pro.tt",
+ "int.tt",
+ "coop.tt",
+ "jobs.tt",
+ "mobi.tt",
+ "travel.tt",
+ "museum.tt",
+ "aero.tt",
+ "name.tt",
+ "gov.tt",
+ "edu.tt",
+ "tv",
+ "tw",
+ "edu.tw",
+ "gov.tw",
+ "mil.tw",
+ "com.tw",
+ "net.tw",
+ "org.tw",
+ "idv.tw",
+ "game.tw",
+ "ebiz.tw",
+ "club.tw",
+ "xn--zf0ao64a.tw",
+ "xn--uc0atv.tw",
+ "xn--czrw28b.tw",
+ "tz",
+ "ac.tz",
+ "co.tz",
+ "go.tz",
+ "hotel.tz",
+ "info.tz",
+ "me.tz",
+ "mil.tz",
+ "mobi.tz",
+ "ne.tz",
+ "or.tz",
+ "sc.tz",
+ "tv.tz",
+ "ua",
+ "com.ua",
+ "edu.ua",
+ "gov.ua",
+ "in.ua",
+ "net.ua",
+ "org.ua",
+ "cherkassy.ua",
+ "cherkasy.ua",
+ "chernigov.ua",
+ "chernihiv.ua",
+ "chernivtsi.ua",
+ "chernovtsy.ua",
+ "ck.ua",
+ "cn.ua",
+ "cr.ua",
+ "crimea.ua",
+ "cv.ua",
+ "dn.ua",
+ "dnepropetrovsk.ua",
+ "dnipropetrovsk.ua",
+ "dominic.ua",
+ "donetsk.ua",
+ "dp.ua",
+ "if.ua",
+ "ivano-frankivsk.ua",
+ "kh.ua",
+ "kharkiv.ua",
+ "kharkov.ua",
+ "kherson.ua",
+ "khmelnitskiy.ua",
+ "khmelnytskyi.ua",
+ "kiev.ua",
+ "kirovograd.ua",
+ "km.ua",
+ "kr.ua",
+ "krym.ua",
+ "ks.ua",
+ "kv.ua",
+ "kyiv.ua",
+ "lg.ua",
+ "lt.ua",
+ "lugansk.ua",
+ "lutsk.ua",
+ "lv.ua",
+ "lviv.ua",
+ "mk.ua",
+ "mykolaiv.ua",
+ "nikolaev.ua",
+ "od.ua",
+ "odesa.ua",
+ "odessa.ua",
+ "pl.ua",
+ "poltava.ua",
+ "rivne.ua",
+ "rovno.ua",
+ "rv.ua",
+ "sb.ua",
+ "sebastopol.ua",
+ "sevastopol.ua",
+ "sm.ua",
+ "sumy.ua",
+ "te.ua",
+ "ternopil.ua",
+ "uz.ua",
+ "uzhgorod.ua",
+ "vinnica.ua",
+ "vinnytsia.ua",
+ "vn.ua",
+ "volyn.ua",
+ "yalta.ua",
+ "zaporizhzhe.ua",
+ "zaporizhzhia.ua",
+ "zhitomir.ua",
+ "zhytomyr.ua",
+ "zp.ua",
+ "zt.ua",
+ "co.ua",
+ "pp.ua",
+ "ug",
+ "co.ug",
+ "or.ug",
+ "ac.ug",
+ "sc.ug",
+ "go.ug",
+ "ne.ug",
+ "com.ug",
+ "org.ug",
+ "uk",
+ "ac.uk",
+ "co.uk",
+ "gov.uk",
+ "ltd.uk",
+ "me.uk",
+ "net.uk",
+ "nhs.uk",
+ "org.uk",
+ "plc.uk",
+ "police.uk",
+ "*.sch.uk",
+ "us",
+ "dni.us",
+ "fed.us",
+ "isa.us",
+ "kids.us",
+ "nsn.us",
+ "ak.us",
+ "al.us",
+ "ar.us",
+ "as.us",
+ "az.us",
+ "ca.us",
+ "co.us",
+ "ct.us",
+ "dc.us",
+ "de.us",
+ "fl.us",
+ "ga.us",
+ "gu.us",
+ "hi.us",
+ "ia.us",
+ "id.us",
+ "il.us",
+ "in.us",
+ "ks.us",
+ "ky.us",
+ "la.us",
+ "ma.us",
+ "md.us",
+ "me.us",
+ "mi.us",
+ "mn.us",
+ "mo.us",
+ "ms.us",
+ "mt.us",
+ "nc.us",
+ "nd.us",
+ "ne.us",
+ "nh.us",
+ "nj.us",
+ "nm.us",
+ "nv.us",
+ "ny.us",
+ "oh.us",
+ "ok.us",
+ "or.us",
+ "pa.us",
+ "pr.us",
+ "ri.us",
+ "sc.us",
+ "sd.us",
+ "tn.us",
+ "tx.us",
+ "ut.us",
+ "vi.us",
+ "vt.us",
+ "va.us",
+ "wa.us",
+ "wi.us",
+ "wv.us",
+ "wy.us",
+ "k12.ak.us",
+ "k12.al.us",
+ "k12.ar.us",
+ "k12.as.us",
+ "k12.az.us",
+ "k12.ca.us",
+ "k12.co.us",
+ "k12.ct.us",
+ "k12.dc.us",
+ "k12.de.us",
+ "k12.fl.us",
+ "k12.ga.us",
+ "k12.gu.us",
+ "k12.ia.us",
+ "k12.id.us",
+ "k12.il.us",
+ "k12.in.us",
+ "k12.ks.us",
+ "k12.ky.us",
+ "k12.la.us",
+ "k12.ma.us",
+ "k12.md.us",
+ "k12.me.us",
+ "k12.mi.us",
+ "k12.mn.us",
+ "k12.mo.us",
+ "k12.ms.us",
+ "k12.mt.us",
+ "k12.nc.us",
+ "k12.ne.us",
+ "k12.nh.us",
+ "k12.nj.us",
+ "k12.nm.us",
+ "k12.nv.us",
+ "k12.ny.us",
+ "k12.oh.us",
+ "k12.ok.us",
+ "k12.or.us",
+ "k12.pa.us",
+ "k12.pr.us",
+ "k12.ri.us",
+ "k12.sc.us",
+ "k12.tn.us",
+ "k12.tx.us",
+ "k12.ut.us",
+ "k12.vi.us",
+ "k12.vt.us",
+ "k12.va.us",
+ "k12.wa.us",
+ "k12.wi.us",
+ "k12.wy.us",
+ "cc.ak.us",
+ "cc.al.us",
+ "cc.ar.us",
+ "cc.as.us",
+ "cc.az.us",
+ "cc.ca.us",
+ "cc.co.us",
+ "cc.ct.us",
+ "cc.dc.us",
+ "cc.de.us",
+ "cc.fl.us",
+ "cc.ga.us",
+ "cc.gu.us",
+ "cc.hi.us",
+ "cc.ia.us",
+ "cc.id.us",
+ "cc.il.us",
+ "cc.in.us",
+ "cc.ks.us",
+ "cc.ky.us",
+ "cc.la.us",
+ "cc.ma.us",
+ "cc.md.us",
+ "cc.me.us",
+ "cc.mi.us",
+ "cc.mn.us",
+ "cc.mo.us",
+ "cc.ms.us",
+ "cc.mt.us",
+ "cc.nc.us",
+ "cc.nd.us",
+ "cc.ne.us",
+ "cc.nh.us",
+ "cc.nj.us",
+ "cc.nm.us",
+ "cc.nv.us",
+ "cc.ny.us",
+ "cc.oh.us",
+ "cc.ok.us",
+ "cc.or.us",
+ "cc.pa.us",
+ "cc.pr.us",
+ "cc.ri.us",
+ "cc.sc.us",
+ "cc.sd.us",
+ "cc.tn.us",
+ "cc.tx.us",
+ "cc.ut.us",
+ "cc.vi.us",
+ "cc.vt.us",
+ "cc.va.us",
+ "cc.wa.us",
+ "cc.wi.us",
+ "cc.wv.us",
+ "cc.wy.us",
+ "lib.ak.us",
+ "lib.al.us",
+ "lib.ar.us",
+ "lib.as.us",
+ "lib.az.us",
+ "lib.ca.us",
+ "lib.co.us",
+ "lib.ct.us",
+ "lib.dc.us",
+ "lib.de.us",
+ "lib.fl.us",
+ "lib.ga.us",
+ "lib.gu.us",
+ "lib.hi.us",
+ "lib.ia.us",
+ "lib.id.us",
+ "lib.il.us",
+ "lib.in.us",
+ "lib.ks.us",
+ "lib.ky.us",
+ "lib.la.us",
+ "lib.ma.us",
+ "lib.md.us",
+ "lib.me.us",
+ "lib.mi.us",
+ "lib.mn.us",
+ "lib.mo.us",
+ "lib.ms.us",
+ "lib.mt.us",
+ "lib.nc.us",
+ "lib.nd.us",
+ "lib.ne.us",
+ "lib.nh.us",
+ "lib.nj.us",
+ "lib.nm.us",
+ "lib.nv.us",
+ "lib.ny.us",
+ "lib.oh.us",
+ "lib.ok.us",
+ "lib.or.us",
+ "lib.pa.us",
+ "lib.pr.us",
+ "lib.ri.us",
+ "lib.sc.us",
+ "lib.sd.us",
+ "lib.tn.us",
+ "lib.tx.us",
+ "lib.ut.us",
+ "lib.vi.us",
+ "lib.vt.us",
+ "lib.va.us",
+ "lib.wa.us",
+ "lib.wi.us",
+ "lib.wy.us",
+ "pvt.k12.ma.us",
+ "chtr.k12.ma.us",
+ "paroch.k12.ma.us",
+ "uy",
+ "com.uy",
+ "edu.uy",
+ "gub.uy",
+ "mil.uy",
+ "net.uy",
+ "org.uy",
+ "uz",
+ "co.uz",
+ "com.uz",
+ "net.uz",
+ "org.uz",
+ "va",
+ "vc",
+ "com.vc",
+ "net.vc",
+ "org.vc",
+ "gov.vc",
+ "mil.vc",
+ "edu.vc",
+ "ve",
+ "arts.ve",
+ "co.ve",
+ "com.ve",
+ "e12.ve",
+ "edu.ve",
+ "firm.ve",
+ "gob.ve",
+ "gov.ve",
+ "info.ve",
+ "int.ve",
+ "mil.ve",
+ "net.ve",
+ "org.ve",
+ "rec.ve",
+ "store.ve",
+ "tec.ve",
+ "web.ve",
+ "vg",
+ "vi",
+ "co.vi",
+ "com.vi",
+ "k12.vi",
+ "net.vi",
+ "org.vi",
+ "vn",
+ "com.vn",
+ "net.vn",
+ "org.vn",
+ "edu.vn",
+ "gov.vn",
+ "int.vn",
+ "ac.vn",
+ "biz.vn",
+ "info.vn",
+ "name.vn",
+ "pro.vn",
+ "health.vn",
+ "vu",
+ "com.vu",
+ "edu.vu",
+ "net.vu",
+ "org.vu",
+ "wf",
+ "ws",
+ "com.ws",
+ "net.ws",
+ "org.ws",
+ "gov.ws",
+ "edu.ws",
+ "yt",
+ "xn--mgbaam7a8h",
+ "xn--y9a3aq",
+ "xn--54b7fta0cc",
+ "xn--90ais",
+ "xn--fiqs8s",
+ "xn--fiqz9s",
+ "xn--lgbbat1ad8j",
+ "xn--wgbh1c",
+ "xn--node",
+ "xn--qxam",
+ "xn--j6w193g",
+ "xn--h2brj9c",
+ "xn--mgbbh1a71e",
+ "xn--fpcrj9c3d",
+ "xn--gecrj9c",
+ "xn--s9brj9c",
+ "xn--45brj9c",
+ "xn--xkc2dl3a5ee0h",
+ "xn--mgba3a4f16a",
+ "xn--mgba3a4fra",
+ "xn--mgbtx2b",
+ "xn--mgbayh7gpa",
+ "xn--3e0b707e",
+ "xn--80ao21a",
+ "xn--fzc2c9e2c",
+ "xn--xkc2al3hye2a",
+ "xn--mgbc0a9azcg",
+ "xn--d1alf",
+ "xn--l1acc",
+ "xn--mix891f",
+ "xn--mix082f",
+ "xn--mgbx4cd0ab",
+ "xn--mgb9awbf",
+ "xn--mgbai9azgqp6j",
+ "xn--mgbai9a5eva00b",
+ "xn--ygbi2ammx",
+ "xn--90a3ac",
+ "xn--o1ac.xn--90a3ac",
+ "xn--c1avg.xn--90a3ac",
+ "xn--90azh.xn--90a3ac",
+ "xn--d1at.xn--90a3ac",
+ "xn--o1ach.xn--90a3ac",
+ "xn--80au.xn--90a3ac",
+ "xn--p1ai",
+ "xn--wgbl6a",
+ "xn--mgberp4a5d4ar",
+ "xn--mgberp4a5d4a87g",
+ "xn--mgbqly7c0a67fbc",
+ "xn--mgbqly7cvafr",
+ "xn--mgbpl2fh",
+ "xn--yfro4i67o",
+ "xn--clchc0ea0b2g2a9gcd",
+ "xn--ogbpf8fl",
+ "xn--mgbtf8fl",
+ "xn--o3cw4h",
+ "xn--pgbs0dh",
+ "xn--kpry57d",
+ "xn--kprw13d",
+ "xn--nnx388a",
+ "xn--j1amh",
+ "xn--mgb2ddes",
+ "xxx",
+ "*.ye",
+ "ac.za",
+ "agrica.za",
+ "alt.za",
+ "co.za",
+ "edu.za",
+ "gov.za",
+ "grondar.za",
+ "law.za",
+ "mil.za",
+ "net.za",
+ "ngo.za",
+ "nis.za",
+ "nom.za",
+ "org.za",
+ "school.za",
+ "tm.za",
+ "web.za",
+ "*.zm",
+ "*.zw",
+ "aaa",
+ "aarp",
+ "abb",
+ "abbott",
+ "able",
+ "abogado",
+ "academy",
+ "accenture",
+ "accountant",
+ "accountants",
+ "aco",
+ "active",
+ "actor",
+ "ads",
+ "adult",
+ "aeg",
+ "aetna",
+ "afl",
+ "africa",
+ "africamagic",
+ "agakhan",
+ "agency",
+ "aig",
+ "airforce",
+ "airtel",
+ "akdn",
+ "alibaba",
+ "alipay",
+ "allfinanz",
+ "ally",
+ "alsace",
+ "amica",
+ "amsterdam",
+ "analytics",
+ "android",
+ "anquan",
+ "apartments",
+ "app",
+ "apple",
+ "aquarelle",
+ "aramco",
+ "archi",
+ "army",
+ "arte",
+ "associates",
+ "attorney",
+ "auction",
+ "audi",
+ "audible",
+ "audio",
+ "author",
+ "auto",
+ "autos",
+ "avianca",
+ "aws",
+ "axa",
+ "azure",
+ "baby",
+ "baidu",
+ "band",
+ "bank",
+ "bar",
+ "barcelona",
+ "barclaycard",
+ "barclays",
+ "barefoot",
+ "bargains",
+ "bauhaus",
+ "bayern",
+ "bbc",
+ "bbva",
+ "bcg",
+ "bcn",
+ "beats",
+ "beer",
+ "bentley",
+ "berlin",
+ "best",
+ "bet",
+ "bharti",
+ "bible",
+ "bid",
+ "bike",
+ "bing",
+ "bingo",
+ "bio",
+ "black",
+ "blackfriday",
+ "blog",
+ "bloomberg",
+ "blue",
+ "bms",
+ "bmw",
+ "bnl",
+ "bnpparibas",
+ "boats",
+ "bom",
+ "bond",
+ "boo",
+ "boots",
+ "bosch",
+ "bostik",
+ "bot",
+ "boutique",
+ "bradesco",
+ "bridgestone",
+ "broadway",
+ "broker",
+ "brother",
+ "brussels",
+ "budapest",
+ "build",
+ "builders",
+ "business",
+ "buy",
+ "buzz",
+ "bzh",
+ "cab",
+ "cafe",
+ "cal",
+ "call",
+ "camera",
+ "camp",
+ "cancerresearch",
+ "canon",
+ "capetown",
+ "capital",
+ "car",
+ "caravan",
+ "cards",
+ "care",
+ "career",
+ "careers",
+ "cars",
+ "cartier",
+ "casa",
+ "cash",
+ "casino",
+ "catering",
+ "cba",
+ "cbn",
+ "cbre",
+ "ceb",
+ "center",
+ "ceo",
+ "cern",
+ "cfa",
+ "cfd",
+ "chanel",
+ "channel",
+ "chase",
+ "chat",
+ "cheap",
+ "chintai",
+ "chloe",
+ "christmas",
+ "chrome",
+ "church",
+ "cipriani",
+ "circle",
+ "cisco",
+ "citic",
+ "city",
+ "cityeats",
+ "claims",
+ "cleaning",
+ "click",
+ "clinic",
+ "clothing",
+ "cloud",
+ "club",
+ "clubmed",
+ "coach",
+ "codes",
+ "coffee",
+ "college",
+ "cologne",
+ "commbank",
+ "community",
+ "company",
+ "computer",
+ "comsec",
+ "condos",
+ "construction",
+ "consulting",
+ "contact",
+ "contractors",
+ "cooking",
+ "cookingchannel",
+ "cool",
+ "corsica",
+ "country",
+ "coupon",
+ "coupons",
+ "courses",
+ "credit",
+ "creditcard",
+ "creditunion",
+ "cricket",
+ "crown",
+ "crs",
+ "cruises",
+ "csc",
+ "cuisinella",
+ "cymru",
+ "cyou",
+ "dabur",
+ "dad",
+ "dance",
+ "date",
+ "dating",
+ "datsun",
+ "day",
+ "dclk",
+ "dds",
+ "deal",
+ "dealer",
+ "deals",
+ "degree",
+ "delivery",
+ "dell",
+ "delta",
+ "democrat",
+ "dental",
+ "dentist",
+ "desi",
+ "design",
+ "dev",
+ "diamonds",
+ "diet",
+ "digital",
+ "direct",
+ "directory",
+ "discount",
+ "dnp",
+ "docs",
+ "dog",
+ "doha",
+ "domains",
+ "doosan",
+ "dot",
+ "download",
+ "drive",
+ "dstv",
+ "dtv",
+ "dubai",
+ "dunlop",
+ "dupont",
+ "durban",
+ "dvag",
+ "earth",
+ "eat",
+ "edeka",
+ "education",
+ "email",
+ "emerck",
+ "energy",
+ "engineer",
+ "engineering",
+ "enterprises",
+ "epson",
+ "equipment",
+ "erni",
+ "esq",
+ "estate",
+ "eurovision",
+ "eus",
+ "events",
+ "everbank",
+ "exchange",
+ "expert",
+ "exposed",
+ "express",
+ "extraspace",
+ "fage",
+ "fail",
+ "fairwinds",
+ "faith",
+ "family",
+ "fan",
+ "fans",
+ "farm",
+ "fashion",
+ "fast",
+ "feedback",
+ "ferrero",
+ "film",
+ "final",
+ "finance",
+ "financial",
+ "fire",
+ "firestone",
+ "firmdale",
+ "fish",
+ "fishing",
+ "fit",
+ "fitness",
+ "flickr",
+ "flights",
+ "florist",
+ "flowers",
+ "flsmidth",
+ "fly",
+ "foo",
+ "foodnetwork",
+ "football",
+ "ford",
+ "forex",
+ "forsale",
+ "forum",
+ "foundation",
+ "frl",
+ "frogans",
+ "frontdoor",
+ "frontier",
+ "fund",
+ "furniture",
+ "futbol",
+ "fyi",
+ "gal",
+ "gallery",
+ "gallo",
+ "gallup",
+ "game",
+ "games",
+ "garden",
+ "gbiz",
+ "gdn",
+ "gea",
+ "gent",
+ "genting",
+ "ggee",
+ "gift",
+ "gifts",
+ "gives",
+ "giving",
+ "glass",
+ "gle",
+ "global",
+ "globo",
+ "gmail",
+ "gmo",
+ "gmx",
+ "gold",
+ "goldpoint",
+ "golf",
+ "goo",
+ "goodyear",
+ "goog",
+ "google",
+ "gop",
+ "got",
+ "gotv",
+ "grainger",
+ "graphics",
+ "gratis",
+ "green",
+ "gripe",
+ "group",
+ "gucci",
+ "guge",
+ "guide",
+ "guitars",
+ "guru",
+ "hamburg",
+ "hangout",
+ "haus",
+ "hdfcbank",
+ "health",
+ "healthcare",
+ "help",
+ "helsinki",
+ "here",
+ "hermes",
+ "hgtv",
+ "hiphop",
+ "hitachi",
+ "hiv",
+ "hkt",
+ "hockey",
+ "holdings",
+ "holiday",
+ "homedepot",
+ "homes",
+ "honda",
+ "horse",
+ "host",
+ "hosting",
+ "hoteles",
+ "hotmail",
+ "house",
+ "how",
+ "hsbc",
+ "htc",
+ "ibm",
+ "icbc",
+ "ice",
+ "icu",
+ "ifm",
+ "iinet",
+ "imdb",
+ "immo",
+ "immobilien",
+ "industries",
+ "infiniti",
+ "ing",
+ "ink",
+ "institute",
+ "insurance",
+ "insure",
+ "international",
+ "investments",
+ "ipiranga",
+ "irish",
+ "iselect",
+ "ist",
+ "istanbul",
+ "itau",
+ "iwc",
+ "jaguar",
+ "java",
+ "jcb",
+ "jcp",
+ "jetzt",
+ "jewelry",
+ "jio",
+ "jlc",
+ "jll",
+ "jmp",
+ "jnj",
+ "joburg",
+ "jot",
+ "joy",
+ "jpmorgan",
+ "jprs",
+ "juegos",
+ "kaufen",
+ "kddi",
+ "kerryhotels",
+ "kerrylogistics",
+ "kerryproperties",
+ "kfh",
+ "kim",
+ "kinder",
+ "kindle",
+ "kitchen",
+ "kiwi",
+ "koeln",
+ "komatsu",
+ "kpmg",
+ "kpn",
+ "krd",
+ "kred",
+ "kuokgroup",
+ "kyknet",
+ "kyoto",
+ "lacaixa",
+ "lamborghini",
+ "lancaster",
+ "land",
+ "landrover",
+ "lasalle",
+ "lat",
+ "latrobe",
+ "law",
+ "lawyer",
+ "lds",
+ "lease",
+ "leclerc",
+ "legal",
+ "lexus",
+ "lgbt",
+ "liaison",
+ "lidl",
+ "life",
+ "lifeinsurance",
+ "lifestyle",
+ "lighting",
+ "like",
+ "limited",
+ "limo",
+ "lincoln",
+ "linde",
+ "link",
+ "lipsy",
+ "live",
+ "lixil",
+ "loan",
+ "loans",
+ "locker",
+ "locus",
+ "lol",
+ "london",
+ "lotte",
+ "lotto",
+ "love",
+ "ltd",
+ "ltda",
+ "lupin",
+ "luxe",
+ "luxury",
+ "madrid",
+ "maif",
+ "maison",
+ "makeup",
+ "man",
+ "management",
+ "mango",
+ "market",
+ "marketing",
+ "markets",
+ "marriott",
+ "mba",
+ "media",
+ "meet",
+ "melbourne",
+ "meme",
+ "memorial",
+ "men",
+ "menu",
+ "meo",
+ "metlife",
+ "miami",
+ "microsoft",
+ "mini",
+ "mit",
+ "mlb",
+ "mls",
+ "mma",
+ "mnet",
+ "mobily",
+ "moda",
+ "moe",
+ "moi",
+ "mom",
+ "monash",
+ "money",
+ "montblanc",
+ "mormon",
+ "mortgage",
+ "moscow",
+ "moto",
+ "motorcycles",
+ "mov",
+ "movie",
+ "movistar",
+ "mtn",
+ "mtpc",
+ "mtr",
+ "multichoice",
+ "mutual",
+ "mutuelle",
+ "mzansimagic",
+ "nadex",
+ "nagoya",
+ "naspers",
+ "natura",
+ "navy",
+ "nec",
+ "netbank",
+ "netflix",
+ "network",
+ "neustar",
+ "new",
+ "news",
+ "next",
+ "nextdirect",
+ "nexus",
+ "ngo",
+ "nhk",
+ "nico",
+ "nikon",
+ "ninja",
+ "nissan",
+ "nokia",
+ "northwesternmutual",
+ "norton",
+ "now",
+ "nowruz",
+ "nowtv",
+ "nra",
+ "nrw",
+ "ntt",
+ "nyc",
+ "obi",
+ "observer",
+ "office",
+ "okinawa",
+ "olayan",
+ "olayangroup",
+ "ollo",
+ "omega",
+ "one",
+ "ong",
+ "onl",
+ "online",
+ "ooo",
+ "oracle",
+ "orange",
+ "organic",
+ "orientexpress",
+ "osaka",
+ "otsuka",
+ "ott",
+ "ovh",
+ "page",
+ "pamperedchef",
+ "panerai",
+ "paris",
+ "pars",
+ "partners",
+ "parts",
+ "party",
+ "passagens",
+ "payu",
+ "pccw",
+ "pet",
+ "pharmacy",
+ "philips",
+ "photo",
+ "photography",
+ "photos",
+ "physio",
+ "piaget",
+ "pics",
+ "pictet",
+ "pictures",
+ "pid",
+ "pin",
+ "ping",
+ "pink",
+ "pizza",
+ "place",
+ "play",
+ "playstation",
+ "plumbing",
+ "plus",
+ "pnc",
+ "pohl",
+ "poker",
+ "porn",
+ "praxi",
+ "press",
+ "prime",
+ "prod",
+ "productions",
+ "prof",
+ "promo",
+ "properties",
+ "property",
+ "protection",
+ "pub",
+ "qpon",
+ "quebec",
+ "quest",
+ "racing",
+ "read",
+ "realtor",
+ "realty",
+ "recipes",
+ "red",
+ "redstone",
+ "redumbrella",
+ "rehab",
+ "reise",
+ "reisen",
+ "reit",
+ "reliance",
+ "ren",
+ "rent",
+ "rentals",
+ "repair",
+ "report",
+ "republican",
+ "rest",
+ "restaurant",
+ "review",
+ "reviews",
+ "rexroth",
+ "rich",
+ "richardli",
+ "ricoh",
+ "ril",
+ "rio",
+ "rip",
+ "rocher",
+ "rocks",
+ "rodeo",
+ "room",
+ "rsvp",
+ "ruhr",
+ "run",
+ "rwe",
+ "ryukyu",
+ "saarland",
+ "safe",
+ "safety",
+ "sakura",
+ "sale",
+ "salon",
+ "samsung",
+ "sandvik",
+ "sandvikcoromant",
+ "sanofi",
+ "sap",
+ "sapo",
+ "sarl",
+ "sas",
+ "save",
+ "saxo",
+ "sbi",
+ "sbs",
+ "sca",
+ "scb",
+ "schmidt",
+ "scholarships",
+ "school",
+ "schule",
+ "schwarz",
+ "science",
+ "scor",
+ "scot",
+ "seat",
+ "security",
+ "seek",
+ "sener",
+ "services",
+ "sew",
+ "sex",
+ "sexy",
+ "sharp",
+ "shaw",
+ "shia",
+ "shiksha",
+ "shoes",
+ "shouji",
+ "show",
+ "shriram",
+ "silk",
+ "sina",
+ "singles",
+ "site",
+ "ski",
+ "skin",
+ "sky",
+ "skype",
+ "smile",
+ "sncf",
+ "soccer",
+ "social",
+ "softbank",
+ "software",
+ "sohu",
+ "solar",
+ "solutions",
+ "song",
+ "sony",
+ "soy",
+ "space",
+ "spiegel",
+ "spot",
+ "spreadbetting",
+ "srl",
+ "stada",
+ "star",
+ "starhub",
+ "statebank",
+ "statoil",
+ "stc",
+ "stcgroup",
+ "stockholm",
+ "storage",
+ "store",
+ "studio",
+ "study",
+ "style",
+ "sucks",
+ "supersport",
+ "supplies",
+ "supply",
+ "support",
+ "surf",
+ "surgery",
+ "suzuki",
+ "swatch",
+ "swiss",
+ "sydney",
+ "symantec",
+ "systems",
+ "tab",
+ "taipei",
+ "talk",
+ "taobao",
+ "tatamotors",
+ "tatar",
+ "tattoo",
+ "tax",
+ "taxi",
+ "tci",
+ "tdk",
+ "team",
+ "tech",
+ "technology",
+ "telecity",
+ "telefonica",
+ "temasek",
+ "tennis",
+ "teva",
+ "thd",
+ "theater",
+ "theatre",
+ "theguardian",
+ "tickets",
+ "tienda",
+ "tiffany",
+ "tips",
+ "tires",
+ "tirol",
+ "tmall",
+ "today",
+ "tokyo",
+ "tools",
+ "top",
+ "toray",
+ "toshiba",
+ "tours",
+ "town",
+ "toyota",
+ "toys",
+ "trade",
+ "trading",
+ "training",
+ "travelchannel",
+ "travelers",
+ "travelersinsurance",
+ "trust",
+ "trv",
+ "tube",
+ "tui",
+ "tunes",
+ "tushu",
+ "tvs",
+ "ubs",
+ "university",
+ "uno",
+ "uol",
+ "ups",
+ "vacations",
+ "vana",
+ "vegas",
+ "ventures",
+ "versicherung",
+ "vet",
+ "viajes",
+ "video",
+ "vig",
+ "viking",
+ "villas",
+ "vin",
+ "vip",
+ "virgin",
+ "vision",
+ "vista",
+ "vistaprint",
+ "viva",
+ "vlaanderen",
+ "vodka",
+ "volkswagen",
+ "vote",
+ "voting",
+ "voto",
+ "voyage",
+ "vuelos",
+ "wales",
+ "walter",
+ "wang",
+ "wanggou",
+ "warman",
+ "watch",
+ "watches",
+ "weather",
+ "weatherchannel",
+ "webcam",
+ "weber",
+ "website",
+ "wed",
+ "wedding",
+ "weibo",
+ "weir",
+ "whoswho",
+ "wien",
+ "wiki",
+ "williamhill",
+ "win",
+ "windows",
+ "wine",
+ "wme",
+ "work",
+ "works",
+ "world",
+ "wtc",
+ "wtf",
+ "xbox",
+ "xerox",
+ "xihuan",
+ "xin",
+ "xn--11b4c3d",
+ "xn--1ck2e1b",
+ "xn--1qqw23a",
+ "xn--30rr7y",
+ "xn--3bst00m",
+ "xn--3ds443g",
+ "xn--3oq18vl8pn36a",
+ "xn--3pxu8k",
+ "xn--42c2d9a",
+ "xn--45q11c",
+ "xn--4gbrim",
+ "xn--55qw42g",
+ "xn--55qx5d",
+ "xn--5tzm5g",
+ "xn--6frz82g",
+ "xn--6qq986b3xl",
+ "xn--80adxhks",
+ "xn--80asehdb",
+ "xn--80aswg",
+ "xn--8y0a063a",
+ "xn--9dbq2a",
+ "xn--9et52u",
+ "xn--9krt00a",
+ "xn--b4w605ferd",
+ "xn--bck1b9a5dre4c",
+ "xn--c1avg",
+ "xn--c2br7g",
+ "xn--cck2b3b",
+ "xn--cg4bki",
+ "xn--czr694b",
+ "xn--czrs0t",
+ "xn--czru2d",
+ "xn--d1acj3b",
+ "xn--eckvdtc9d",
+ "xn--efvy88h",
+ "xn--estv75g",
+ "xn--fct429k",
+ "xn--fhbei",
+ "xn--fiq228c5hs",
+ "xn--fiq64b",
+ "xn--fjq720a",
+ "xn--flw351e",
+ "xn--fzys8d69uvgm",
+ "xn--g2xx48c",
+ "xn--gckr3f0f",
+ "xn--hxt814e",
+ "xn--i1b6b1a6a2e",
+ "xn--imr513n",
+ "xn--io0a7i",
+ "xn--j1aef",
+ "xn--jlq61u9w7b",
+ "xn--jvr189m",
+ "xn--kcrx77d1x4a",
+ "xn--kpu716f",
+ "xn--kput3i",
+ "xn--mgba3a3ejt",
+ "xn--mgba7c0bbn0a",
+ "xn--mgbab2bd",
+ "xn--mgbb9fbpob",
+ "xn--mgbt3dhd",
+ "xn--mk1bu44c",
+ "xn--mxtq1m",
+ "xn--ngbc5azd",
+ "xn--ngbe9e0a",
+ "xn--nqv7f",
+ "xn--nqv7fs00ema",
+ "xn--nyqy26a",
+ "xn--p1acf",
+ "xn--pbt977c",
+ "xn--pssy2u",
+ "xn--q9jyb4c",
+ "xn--qcka1pmc",
+ "xn--rhqv96g",
+ "xn--rovu88b",
+ "xn--ses554g",
+ "xn--t60b56a",
+ "xn--tckwe",
+ "xn--unup4y",
+ "xn--vermgensberater-ctb",
+ "xn--vermgensberatung-pwb",
+ "xn--vhquv",
+ "xn--vuq861b",
+ "xn--w4r85el8fhu5dnra",
+ "xn--xhq521b",
+ "xn--zfr164b",
+ "xperia",
+ "xyz",
+ "yachts",
+ "yahoo",
+ "yamaxun",
+ "yandex",
+ "yodobashi",
+ "yoga",
+ "yokohama",
+ "you",
+ "youtube",
+ "yun",
+ "zappos",
+ "zara",
+ "zero",
+ "zip",
+ "zippo",
+ "zone",
+ "zuerich",
+ "cloudfront.net",
+ "ap-northeast-1.compute.amazonaws.com",
+ "ap-southeast-1.compute.amazonaws.com",
+ "ap-southeast-2.compute.amazonaws.com",
+ "cn-north-1.compute.amazonaws.cn",
+ "compute.amazonaws.cn",
+ "compute.amazonaws.com",
+ "compute-1.amazonaws.com",
+ "eu-west-1.compute.amazonaws.com",
+ "eu-central-1.compute.amazonaws.com",
+ "sa-east-1.compute.amazonaws.com",
+ "us-east-1.amazonaws.com",
+ "us-gov-west-1.compute.amazonaws.com",
+ "us-west-1.compute.amazonaws.com",
+ "us-west-2.compute.amazonaws.com",
+ "z-1.compute-1.amazonaws.com",
+ "z-2.compute-1.amazonaws.com",
+ "elasticbeanstalk.com",
+ "elb.amazonaws.com",
+ "s3.amazonaws.com",
+ "s3-ap-northeast-1.amazonaws.com",
+ "s3-ap-southeast-1.amazonaws.com",
+ "s3-ap-southeast-2.amazonaws.com",
+ "s3-external-1.amazonaws.com",
+ "s3-external-2.amazonaws.com",
+ "s3-fips-us-gov-west-1.amazonaws.com",
+ "s3-eu-central-1.amazonaws.com",
+ "s3-eu-west-1.amazonaws.com",
+ "s3-sa-east-1.amazonaws.com",
+ "s3-us-gov-west-1.amazonaws.com",
+ "s3-us-west-1.amazonaws.com",
+ "s3-us-west-2.amazonaws.com",
+ "s3.cn-north-1.amazonaws.com.cn",
+ "s3.eu-central-1.amazonaws.com",
+ "betainabox.com",
+ "ae.org",
+ "ar.com",
+ "br.com",
+ "cn.com",
+ "com.de",
+ "com.se",
+ "de.com",
+ "eu.com",
+ "gb.com",
+ "gb.net",
+ "hu.com",
+ "hu.net",
+ "jp.net",
+ "jpn.com",
+ "kr.com",
+ "mex.com",
+ "no.com",
+ "qc.com",
+ "ru.com",
+ "sa.com",
+ "se.com",
+ "se.net",
+ "uk.com",
+ "uk.net",
+ "us.com",
+ "uy.com",
+ "za.bz",
+ "za.com",
+ "africa.com",
+ "gr.com",
+ "in.net",
+ "us.org",
+ "co.com",
+ "c.la",
+ "cloudcontrolled.com",
+ "cloudcontrolapp.com",
+ "co.ca",
+ "c.cdn77.org",
+ "cdn77-ssl.net",
+ "r.cdn77.net",
+ "rsc.cdn77.org",
+ "ssl.origin.cdn77-secure.org",
+ "co.nl",
+ "co.no",
+ "*.platform.sh",
+ "cupcake.is",
+ "dreamhosters.com",
+ "duckdns.org",
+ "dyndns-at-home.com",
+ "dyndns-at-work.com",
+ "dyndns-blog.com",
+ "dyndns-free.com",
+ "dyndns-home.com",
+ "dyndns-ip.com",
+ "dyndns-mail.com",
+ "dyndns-office.com",
+ "dyndns-pics.com",
+ "dyndns-remote.com",
+ "dyndns-server.com",
+ "dyndns-web.com",
+ "dyndns-wiki.com",
+ "dyndns-work.com",
+ "dyndns.biz",
+ "dyndns.info",
+ "dyndns.org",
+ "dyndns.tv",
+ "at-band-camp.net",
+ "ath.cx",
+ "barrel-of-knowledge.info",
+ "barrell-of-knowledge.info",
+ "better-than.tv",
+ "blogdns.com",
+ "blogdns.net",
+ "blogdns.org",
+ "blogsite.org",
+ "boldlygoingnowhere.org",
+ "broke-it.net",
+ "buyshouses.net",
+ "cechire.com",
+ "dnsalias.com",
+ "dnsalias.net",
+ "dnsalias.org",
+ "dnsdojo.com",
+ "dnsdojo.net",
+ "dnsdojo.org",
+ "does-it.net",
+ "doesntexist.com",
+ "doesntexist.org",
+ "dontexist.com",
+ "dontexist.net",
+ "dontexist.org",
+ "doomdns.com",
+ "doomdns.org",
+ "dvrdns.org",
+ "dyn-o-saur.com",
+ "dynalias.com",
+ "dynalias.net",
+ "dynalias.org",
+ "dynathome.net",
+ "dyndns.ws",
+ "endofinternet.net",
+ "endofinternet.org",
+ "endoftheinternet.org",
+ "est-a-la-maison.com",
+ "est-a-la-masion.com",
+ "est-le-patron.com",
+ "est-mon-blogueur.com",
+ "for-better.biz",
+ "for-more.biz",
+ "for-our.info",
+ "for-some.biz",
+ "for-the.biz",
+ "forgot.her.name",
+ "forgot.his.name",
+ "from-ak.com",
+ "from-al.com",
+ "from-ar.com",
+ "from-az.net",
+ "from-ca.com",
+ "from-co.net",
+ "from-ct.com",
+ "from-dc.com",
+ "from-de.com",
+ "from-fl.com",
+ "from-ga.com",
+ "from-hi.com",
+ "from-ia.com",
+ "from-id.com",
+ "from-il.com",
+ "from-in.com",
+ "from-ks.com",
+ "from-ky.com",
+ "from-la.net",
+ "from-ma.com",
+ "from-md.com",
+ "from-me.org",
+ "from-mi.com",
+ "from-mn.com",
+ "from-mo.com",
+ "from-ms.com",
+ "from-mt.com",
+ "from-nc.com",
+ "from-nd.com",
+ "from-ne.com",
+ "from-nh.com",
+ "from-nj.com",
+ "from-nm.com",
+ "from-nv.com",
+ "from-ny.net",
+ "from-oh.com",
+ "from-ok.com",
+ "from-or.com",
+ "from-pa.com",
+ "from-pr.com",
+ "from-ri.com",
+ "from-sc.com",
+ "from-sd.com",
+ "from-tn.com",
+ "from-tx.com",
+ "from-ut.com",
+ "from-va.com",
+ "from-vt.com",
+ "from-wa.com",
+ "from-wi.com",
+ "from-wv.com",
+ "from-wy.com",
+ "ftpaccess.cc",
+ "fuettertdasnetz.de",
+ "game-host.org",
+ "game-server.cc",
+ "getmyip.com",
+ "gets-it.net",
+ "go.dyndns.org",
+ "gotdns.com",
+ "gotdns.org",
+ "groks-the.info",
+ "groks-this.info",
+ "ham-radio-op.net",
+ "here-for-more.info",
+ "hobby-site.com",
+ "hobby-site.org",
+ "home.dyndns.org",
+ "homedns.org",
+ "homeftp.net",
+ "homeftp.org",
+ "homeip.net",
+ "homelinux.com",
+ "homelinux.net",
+ "homelinux.org",
+ "homeunix.com",
+ "homeunix.net",
+ "homeunix.org",
+ "iamallama.com",
+ "in-the-band.net",
+ "is-a-anarchist.com",
+ "is-a-blogger.com",
+ "is-a-bookkeeper.com",
+ "is-a-bruinsfan.org",
+ "is-a-bulls-fan.com",
+ "is-a-candidate.org",
+ "is-a-caterer.com",
+ "is-a-celticsfan.org",
+ "is-a-chef.com",
+ "is-a-chef.net",
+ "is-a-chef.org",
+ "is-a-conservative.com",
+ "is-a-cpa.com",
+ "is-a-cubicle-slave.com",
+ "is-a-democrat.com",
+ "is-a-designer.com",
+ "is-a-doctor.com",
+ "is-a-financialadvisor.com",
+ "is-a-geek.com",
+ "is-a-geek.net",
+ "is-a-geek.org",
+ "is-a-green.com",
+ "is-a-guru.com",
+ "is-a-hard-worker.com",
+ "is-a-hunter.com",
+ "is-a-knight.org",
+ "is-a-landscaper.com",
+ "is-a-lawyer.com",
+ "is-a-liberal.com",
+ "is-a-libertarian.com",
+ "is-a-linux-user.org",
+ "is-a-llama.com",
+ "is-a-musician.com",
+ "is-a-nascarfan.com",
+ "is-a-nurse.com",
+ "is-a-painter.com",
+ "is-a-patsfan.org",
+ "is-a-personaltrainer.com",
+ "is-a-photographer.com",
+ "is-a-player.com",
+ "is-a-republican.com",
+ "is-a-rockstar.com",
+ "is-a-socialist.com",
+ "is-a-soxfan.org",
+ "is-a-student.com",
+ "is-a-teacher.com",
+ "is-a-techie.com",
+ "is-a-therapist.com",
+ "is-an-accountant.com",
+ "is-an-actor.com",
+ "is-an-actress.com",
+ "is-an-anarchist.com",
+ "is-an-artist.com",
+ "is-an-engineer.com",
+ "is-an-entertainer.com",
+ "is-by.us",
+ "is-certified.com",
+ "is-found.org",
+ "is-gone.com",
+ "is-into-anime.com",
+ "is-into-cars.com",
+ "is-into-cartoons.com",
+ "is-into-games.com",
+ "is-leet.com",
+ "is-lost.org",
+ "is-not-certified.com",
+ "is-saved.org",
+ "is-slick.com",
+ "is-uberleet.com",
+ "is-very-bad.org",
+ "is-very-evil.org",
+ "is-very-good.org",
+ "is-very-nice.org",
+ "is-very-sweet.org",
+ "is-with-theband.com",
+ "isa-geek.com",
+ "isa-geek.net",
+ "isa-geek.org",
+ "isa-hockeynut.com",
+ "issmarterthanyou.com",
+ "isteingeek.de",
+ "istmein.de",
+ "kicks-ass.net",
+ "kicks-ass.org",
+ "knowsitall.info",
+ "land-4-sale.us",
+ "lebtimnetz.de",
+ "leitungsen.de",
+ "likes-pie.com",
+ "likescandy.com",
+ "merseine.nu",
+ "mine.nu",
+ "misconfused.org",
+ "mypets.ws",
+ "myphotos.cc",
+ "neat-url.com",
+ "office-on-the.net",
+ "on-the-web.tv",
+ "podzone.net",
+ "podzone.org",
+ "readmyblog.org",
+ "saves-the-whales.com",
+ "scrapper-site.net",
+ "scrapping.cc",
+ "selfip.biz",
+ "selfip.com",
+ "selfip.info",
+ "selfip.net",
+ "selfip.org",
+ "sells-for-less.com",
+ "sells-for-u.com",
+ "sells-it.net",
+ "sellsyourhome.org",
+ "servebbs.com",
+ "servebbs.net",
+ "servebbs.org",
+ "serveftp.net",
+ "serveftp.org",
+ "servegame.org",
+ "shacknet.nu",
+ "simple-url.com",
+ "space-to-rent.com",
+ "stuff-4-sale.org",
+ "stuff-4-sale.us",
+ "teaches-yoga.com",
+ "thruhere.net",
+ "traeumtgerade.de",
+ "webhop.biz",
+ "webhop.info",
+ "webhop.net",
+ "webhop.org",
+ "worse-than.tv",
+ "writesthisblog.com",
+ "eu.org",
+ "al.eu.org",
+ "asso.eu.org",
+ "at.eu.org",
+ "au.eu.org",
+ "be.eu.org",
+ "bg.eu.org",
+ "ca.eu.org",
+ "cd.eu.org",
+ "ch.eu.org",
+ "cn.eu.org",
+ "cy.eu.org",
+ "cz.eu.org",
+ "de.eu.org",
+ "dk.eu.org",
+ "edu.eu.org",
+ "ee.eu.org",
+ "es.eu.org",
+ "fi.eu.org",
+ "fr.eu.org",
+ "gr.eu.org",
+ "hr.eu.org",
+ "hu.eu.org",
+ "ie.eu.org",
+ "il.eu.org",
+ "in.eu.org",
+ "int.eu.org",
+ "is.eu.org",
+ "it.eu.org",
+ "jp.eu.org",
+ "kr.eu.org",
+ "lt.eu.org",
+ "lu.eu.org",
+ "lv.eu.org",
+ "mc.eu.org",
+ "me.eu.org",
+ "mk.eu.org",
+ "mt.eu.org",
+ "my.eu.org",
+ "net.eu.org",
+ "ng.eu.org",
+ "nl.eu.org",
+ "no.eu.org",
+ "nz.eu.org",
+ "paris.eu.org",
+ "pl.eu.org",
+ "pt.eu.org",
+ "q-a.eu.org",
+ "ro.eu.org",
+ "ru.eu.org",
+ "se.eu.org",
+ "si.eu.org",
+ "sk.eu.org",
+ "tr.eu.org",
+ "uk.eu.org",
+ "us.eu.org",
+ "a.ssl.fastly.net",
+ "b.ssl.fastly.net",
+ "global.ssl.fastly.net",
+ "a.prod.fastly.net",
+ "global.prod.fastly.net",
+ "firebaseapp.com",
+ "flynnhub.com",
+ "service.gov.uk",
+ "github.io",
+ "githubusercontent.com",
+ "ro.com",
+ "appspot.com",
+ "blogspot.ae",
+ "blogspot.al",
+ "blogspot.am",
+ "blogspot.ba",
+ "blogspot.be",
+ "blogspot.bg",
+ "blogspot.bj",
+ "blogspot.ca",
+ "blogspot.cf",
+ "blogspot.ch",
+ "blogspot.cl",
+ "blogspot.co.at",
+ "blogspot.co.id",
+ "blogspot.co.il",
+ "blogspot.co.ke",
+ "blogspot.co.nz",
+ "blogspot.co.uk",
+ "blogspot.co.za",
+ "blogspot.com",
+ "blogspot.com.ar",
+ "blogspot.com.au",
+ "blogspot.com.br",
+ "blogspot.com.by",
+ "blogspot.com.co",
+ "blogspot.com.cy",
+ "blogspot.com.ee",
+ "blogspot.com.eg",
+ "blogspot.com.es",
+ "blogspot.com.mt",
+ "blogspot.com.ng",
+ "blogspot.com.tr",
+ "blogspot.com.uy",
+ "blogspot.cv",
+ "blogspot.cz",
+ "blogspot.de",
+ "blogspot.dk",
+ "blogspot.fi",
+ "blogspot.fr",
+ "blogspot.gr",
+ "blogspot.hk",
+ "blogspot.hr",
+ "blogspot.hu",
+ "blogspot.ie",
+ "blogspot.in",
+ "blogspot.is",
+ "blogspot.it",
+ "blogspot.jp",
+ "blogspot.kr",
+ "blogspot.li",
+ "blogspot.lt",
+ "blogspot.lu",
+ "blogspot.md",
+ "blogspot.mk",
+ "blogspot.mr",
+ "blogspot.mx",
+ "blogspot.my",
+ "blogspot.nl",
+ "blogspot.no",
+ "blogspot.pe",
+ "blogspot.pt",
+ "blogspot.qa",
+ "blogspot.re",
+ "blogspot.ro",
+ "blogspot.rs",
+ "blogspot.ru",
+ "blogspot.se",
+ "blogspot.sg",
+ "blogspot.si",
+ "blogspot.sk",
+ "blogspot.sn",
+ "blogspot.td",
+ "blogspot.tw",
+ "blogspot.ug",
+ "blogspot.vn",
+ "codespot.com",
+ "googleapis.com",
+ "googlecode.com",
+ "pagespeedmobilizer.com",
+ "withgoogle.com",
+ "herokuapp.com",
+ "herokussl.com",
+ "iki.fi",
+ "biz.at",
+ "info.at",
+ "co.pl",
+ "azurewebsites.net",
+ "azure-mobile.net",
+ "cloudapp.net",
+ "4u.com",
+ "nfshost.com",
+ "nyc.mn",
+ "nid.io",
+ "operaunite.com",
+ "outsystemscloud.com",
+ "art.pl",
+ "gliwice.pl",
+ "krakow.pl",
+ "poznan.pl",
+ "wroc.pl",
+ "zakopane.pl",
+ "priv.at",
+ "rhcloud.com",
+ "sinaapp.com",
+ "vipsinaapp.com",
+ "1kapp.com",
+ "gda.pl",
+ "gdansk.pl",
+ "gdynia.pl",
+ "med.pl",
+ "sopot.pl",
+ "hk.com",
+ "hk.org",
+ "ltd.hk",
+ "inc.hk",
+ "yolasite.com",
+ "za.net",
+ "za.org",
+}
+
+var nodeLabels = [...]string{
+ "aaa",
+ "aarp",
+ "abb",
+ "abbott",
+ "able",
+ "abogado",
+ "ac",
+ "academy",
+ "accenture",
+ "accountant",
+ "accountants",
+ "aco",
+ "active",
+ "actor",
+ "ad",
+ "ads",
+ "adult",
+ "ae",
+ "aeg",
+ "aero",
+ "aetna",
+ "af",
+ "afl",
+ "africa",
+ "africamagic",
+ "ag",
+ "agakhan",
+ "agency",
+ "ai",
+ "aig",
+ "airforce",
+ "airtel",
+ "akdn",
+ "al",
+ "alibaba",
+ "alipay",
+ "allfinanz",
+ "ally",
+ "alsace",
+ "am",
+ "amica",
+ "amsterdam",
+ "an",
+ "analytics",
+ "android",
+ "anquan",
+ "ao",
+ "apartments",
+ "app",
+ "apple",
+ "aq",
+ "aquarelle",
+ "ar",
+ "aramco",
+ "archi",
+ "army",
+ "arpa",
+ "arte",
+ "as",
+ "asia",
+ "associates",
+ "at",
+ "attorney",
+ "au",
+ "auction",
+ "audi",
+ "audible",
+ "audio",
+ "author",
+ "auto",
+ "autos",
+ "avianca",
+ "aw",
+ "aws",
+ "ax",
+ "axa",
+ "az",
+ "azure",
+ "ba",
+ "baby",
+ "baidu",
+ "band",
+ "bank",
+ "bar",
+ "barcelona",
+ "barclaycard",
+ "barclays",
+ "barefoot",
+ "bargains",
+ "bauhaus",
+ "bayern",
+ "bb",
+ "bbc",
+ "bbva",
+ "bcg",
+ "bcn",
+ "bd",
+ "be",
+ "beats",
+ "beer",
+ "bentley",
+ "berlin",
+ "best",
+ "bet",
+ "bf",
+ "bg",
+ "bh",
+ "bharti",
+ "bi",
+ "bible",
+ "bid",
+ "bike",
+ "bing",
+ "bingo",
+ "bio",
+ "biz",
+ "bj",
+ "black",
+ "blackfriday",
+ "blog",
+ "bloomberg",
+ "blue",
+ "bm",
+ "bms",
+ "bmw",
+ "bn",
+ "bnl",
+ "bnpparibas",
+ "bo",
+ "boats",
+ "bom",
+ "bond",
+ "boo",
+ "boots",
+ "bosch",
+ "bostik",
+ "bot",
+ "boutique",
+ "br",
+ "bradesco",
+ "bridgestone",
+ "broadway",
+ "broker",
+ "brother",
+ "brussels",
+ "bs",
+ "bt",
+ "budapest",
+ "build",
+ "builders",
+ "business",
+ "buy",
+ "buzz",
+ "bv",
+ "bw",
+ "by",
+ "bz",
+ "bzh",
+ "ca",
+ "cab",
+ "cafe",
+ "cal",
+ "call",
+ "camera",
+ "camp",
+ "cancerresearch",
+ "canon",
+ "capetown",
+ "capital",
+ "car",
+ "caravan",
+ "cards",
+ "care",
+ "career",
+ "careers",
+ "cars",
+ "cartier",
+ "casa",
+ "cash",
+ "casino",
+ "cat",
+ "catering",
+ "cba",
+ "cbn",
+ "cbre",
+ "cc",
+ "cd",
+ "ceb",
+ "center",
+ "ceo",
+ "cern",
+ "cf",
+ "cfa",
+ "cfd",
+ "cg",
+ "ch",
+ "chanel",
+ "channel",
+ "chase",
+ "chat",
+ "cheap",
+ "chintai",
+ "chloe",
+ "christmas",
+ "chrome",
+ "church",
+ "ci",
+ "cipriani",
+ "circle",
+ "cisco",
+ "citic",
+ "city",
+ "cityeats",
+ "ck",
+ "cl",
+ "claims",
+ "cleaning",
+ "click",
+ "clinic",
+ "clothing",
+ "cloud",
+ "club",
+ "clubmed",
+ "cm",
+ "cn",
+ "co",
+ "coach",
+ "codes",
+ "coffee",
+ "college",
+ "cologne",
+ "com",
+ "commbank",
+ "community",
+ "company",
+ "computer",
+ "comsec",
+ "condos",
+ "construction",
+ "consulting",
+ "contact",
+ "contractors",
+ "cooking",
+ "cookingchannel",
+ "cool",
+ "coop",
+ "corsica",
+ "country",
+ "coupon",
+ "coupons",
+ "courses",
+ "cr",
+ "credit",
+ "creditcard",
+ "creditunion",
+ "cricket",
+ "crown",
+ "crs",
+ "cruises",
+ "csc",
+ "cu",
+ "cuisinella",
+ "cv",
+ "cw",
+ "cx",
+ "cy",
+ "cymru",
+ "cyou",
+ "cz",
+ "dabur",
+ "dad",
+ "dance",
+ "date",
+ "dating",
+ "datsun",
+ "day",
+ "dclk",
+ "dds",
+ "de",
+ "deal",
+ "dealer",
+ "deals",
+ "degree",
+ "delivery",
+ "dell",
+ "delta",
+ "democrat",
+ "dental",
+ "dentist",
+ "desi",
+ "design",
+ "dev",
+ "diamonds",
+ "diet",
+ "digital",
+ "direct",
+ "directory",
+ "discount",
+ "dj",
+ "dk",
+ "dm",
+ "dnp",
+ "do",
+ "docs",
+ "dog",
+ "doha",
+ "domains",
+ "doosan",
+ "dot",
+ "download",
+ "drive",
+ "dstv",
+ "dtv",
+ "dubai",
+ "dunlop",
+ "dupont",
+ "durban",
+ "dvag",
+ "dz",
+ "earth",
+ "eat",
+ "ec",
+ "edeka",
+ "edu",
+ "education",
+ "ee",
+ "eg",
+ "email",
+ "emerck",
+ "energy",
+ "engineer",
+ "engineering",
+ "enterprises",
+ "epson",
+ "equipment",
+ "er",
+ "erni",
+ "es",
+ "esq",
+ "estate",
+ "et",
+ "eu",
+ "eurovision",
+ "eus",
+ "events",
+ "everbank",
+ "exchange",
+ "expert",
+ "exposed",
+ "express",
+ "extraspace",
+ "fage",
+ "fail",
+ "fairwinds",
+ "faith",
+ "family",
+ "fan",
+ "fans",
+ "farm",
+ "fashion",
+ "fast",
+ "feedback",
+ "ferrero",
+ "fi",
+ "film",
+ "final",
+ "finance",
+ "financial",
+ "fire",
+ "firestone",
+ "firmdale",
+ "fish",
+ "fishing",
+ "fit",
+ "fitness",
+ "fj",
+ "fk",
+ "flickr",
+ "flights",
+ "florist",
+ "flowers",
+ "flsmidth",
+ "fly",
+ "fm",
+ "fo",
+ "foo",
+ "foodnetwork",
+ "football",
+ "ford",
+ "forex",
+ "forsale",
+ "forum",
+ "foundation",
+ "fr",
+ "frl",
+ "frogans",
+ "frontdoor",
+ "frontier",
+ "fund",
+ "furniture",
+ "futbol",
+ "fyi",
+ "ga",
+ "gal",
+ "gallery",
+ "gallo",
+ "gallup",
+ "game",
+ "games",
+ "garden",
+ "gb",
+ "gbiz",
+ "gd",
+ "gdn",
+ "ge",
+ "gea",
+ "gent",
+ "genting",
+ "gf",
+ "gg",
+ "ggee",
+ "gh",
+ "gi",
+ "gift",
+ "gifts",
+ "gives",
+ "giving",
+ "gl",
+ "glass",
+ "gle",
+ "global",
+ "globo",
+ "gm",
+ "gmail",
+ "gmo",
+ "gmx",
+ "gn",
+ "gold",
+ "goldpoint",
+ "golf",
+ "goo",
+ "goodyear",
+ "goog",
+ "google",
+ "gop",
+ "got",
+ "gotv",
+ "gov",
+ "gp",
+ "gq",
+ "gr",
+ "grainger",
+ "graphics",
+ "gratis",
+ "green",
+ "gripe",
+ "group",
+ "gs",
+ "gt",
+ "gu",
+ "gucci",
+ "guge",
+ "guide",
+ "guitars",
+ "guru",
+ "gw",
+ "gy",
+ "hamburg",
+ "hangout",
+ "haus",
+ "hdfcbank",
+ "health",
+ "healthcare",
+ "help",
+ "helsinki",
+ "here",
+ "hermes",
+ "hgtv",
+ "hiphop",
+ "hitachi",
+ "hiv",
+ "hk",
+ "hkt",
+ "hm",
+ "hn",
+ "hockey",
+ "holdings",
+ "holiday",
+ "homedepot",
+ "homes",
+ "honda",
+ "horse",
+ "host",
+ "hosting",
+ "hoteles",
+ "hotmail",
+ "house",
+ "how",
+ "hr",
+ "hsbc",
+ "ht",
+ "htc",
+ "hu",
+ "ibm",
+ "icbc",
+ "ice",
+ "icu",
+ "id",
+ "ie",
+ "ifm",
+ "iinet",
+ "il",
+ "im",
+ "imdb",
+ "immo",
+ "immobilien",
+ "in",
+ "industries",
+ "infiniti",
+ "info",
+ "ing",
+ "ink",
+ "institute",
+ "insurance",
+ "insure",
+ "int",
+ "international",
+ "investments",
+ "io",
+ "ipiranga",
+ "iq",
+ "ir",
+ "irish",
+ "is",
+ "iselect",
+ "ist",
+ "istanbul",
+ "it",
+ "itau",
+ "iwc",
+ "jaguar",
+ "java",
+ "jcb",
+ "jcp",
+ "je",
+ "jetzt",
+ "jewelry",
+ "jio",
+ "jlc",
+ "jll",
+ "jm",
+ "jmp",
+ "jnj",
+ "jo",
+ "jobs",
+ "joburg",
+ "jot",
+ "joy",
+ "jp",
+ "jpmorgan",
+ "jprs",
+ "juegos",
+ "kaufen",
+ "kddi",
+ "ke",
+ "kerryhotels",
+ "kerrylogistics",
+ "kerryproperties",
+ "kfh",
+ "kg",
+ "kh",
+ "ki",
+ "kim",
+ "kinder",
+ "kindle",
+ "kitchen",
+ "kiwi",
+ "km",
+ "kn",
+ "koeln",
+ "komatsu",
+ "kp",
+ "kpmg",
+ "kpn",
+ "kr",
+ "krd",
+ "kred",
+ "kuokgroup",
+ "kw",
+ "ky",
+ "kyknet",
+ "kyoto",
+ "kz",
+ "la",
+ "lacaixa",
+ "lamborghini",
+ "lancaster",
+ "land",
+ "landrover",
+ "lasalle",
+ "lat",
+ "latrobe",
+ "law",
+ "lawyer",
+ "lb",
+ "lc",
+ "lds",
+ "lease",
+ "leclerc",
+ "legal",
+ "lexus",
+ "lgbt",
+ "li",
+ "liaison",
+ "lidl",
+ "life",
+ "lifeinsurance",
+ "lifestyle",
+ "lighting",
+ "like",
+ "limited",
+ "limo",
+ "lincoln",
+ "linde",
+ "link",
+ "lipsy",
+ "live",
+ "lixil",
+ "lk",
+ "loan",
+ "loans",
+ "locker",
+ "locus",
+ "lol",
+ "london",
+ "lotte",
+ "lotto",
+ "love",
+ "lr",
+ "ls",
+ "lt",
+ "ltd",
+ "ltda",
+ "lu",
+ "lupin",
+ "luxe",
+ "luxury",
+ "lv",
+ "ly",
+ "ma",
+ "madrid",
+ "maif",
+ "maison",
+ "makeup",
+ "man",
+ "management",
+ "mango",
+ "market",
+ "marketing",
+ "markets",
+ "marriott",
+ "mba",
+ "mc",
+ "md",
+ "me",
+ "media",
+ "meet",
+ "melbourne",
+ "meme",
+ "memorial",
+ "men",
+ "menu",
+ "meo",
+ "metlife",
+ "mg",
+ "mh",
+ "miami",
+ "microsoft",
+ "mil",
+ "mini",
+ "mit",
+ "mk",
+ "ml",
+ "mlb",
+ "mls",
+ "mm",
+ "mma",
+ "mn",
+ "mnet",
+ "mo",
+ "mobi",
+ "mobily",
+ "moda",
+ "moe",
+ "moi",
+ "mom",
+ "monash",
+ "money",
+ "montblanc",
+ "mormon",
+ "mortgage",
+ "moscow",
+ "moto",
+ "motorcycles",
+ "mov",
+ "movie",
+ "movistar",
+ "mp",
+ "mq",
+ "mr",
+ "ms",
+ "mt",
+ "mtn",
+ "mtpc",
+ "mtr",
+ "mu",
+ "multichoice",
+ "museum",
+ "mutual",
+ "mutuelle",
+ "mv",
+ "mw",
+ "mx",
+ "my",
+ "mz",
+ "mzansimagic",
+ "na",
+ "nadex",
+ "nagoya",
+ "name",
+ "naspers",
+ "natura",
+ "navy",
+ "nc",
+ "ne",
+ "nec",
+ "net",
+ "netbank",
+ "netflix",
+ "network",
+ "neustar",
+ "new",
+ "news",
+ "next",
+ "nextdirect",
+ "nexus",
+ "nf",
+ "ng",
+ "ngo",
+ "nhk",
+ "ni",
+ "nico",
+ "nikon",
+ "ninja",
+ "nissan",
+ "nl",
+ "no",
+ "nokia",
+ "northwesternmutual",
+ "norton",
+ "now",
+ "nowruz",
+ "nowtv",
+ "np",
+ "nr",
+ "nra",
+ "nrw",
+ "ntt",
+ "nu",
+ "nyc",
+ "nz",
+ "obi",
+ "observer",
+ "office",
+ "okinawa",
+ "olayan",
+ "olayangroup",
+ "ollo",
+ "om",
+ "omega",
+ "one",
+ "ong",
+ "onl",
+ "online",
+ "ooo",
+ "oracle",
+ "orange",
+ "org",
+ "organic",
+ "orientexpress",
+ "osaka",
+ "otsuka",
+ "ott",
+ "ovh",
+ "pa",
+ "page",
+ "pamperedchef",
+ "panerai",
+ "paris",
+ "pars",
+ "partners",
+ "parts",
+ "party",
+ "passagens",
+ "payu",
+ "pccw",
+ "pe",
+ "pet",
+ "pf",
+ "pg",
+ "ph",
+ "pharmacy",
+ "philips",
+ "photo",
+ "photography",
+ "photos",
+ "physio",
+ "piaget",
+ "pics",
+ "pictet",
+ "pictures",
+ "pid",
+ "pin",
+ "ping",
+ "pink",
+ "pizza",
+ "pk",
+ "pl",
+ "place",
+ "play",
+ "playstation",
+ "plumbing",
+ "plus",
+ "pm",
+ "pn",
+ "pnc",
+ "pohl",
+ "poker",
+ "porn",
+ "post",
+ "pr",
+ "praxi",
+ "press",
+ "prime",
+ "pro",
+ "prod",
+ "productions",
+ "prof",
+ "promo",
+ "properties",
+ "property",
+ "protection",
+ "ps",
+ "pt",
+ "pub",
+ "pw",
+ "py",
+ "qa",
+ "qpon",
+ "quebec",
+ "quest",
+ "racing",
+ "re",
+ "read",
+ "realtor",
+ "realty",
+ "recipes",
+ "red",
+ "redstone",
+ "redumbrella",
+ "rehab",
+ "reise",
+ "reisen",
+ "reit",
+ "reliance",
+ "ren",
+ "rent",
+ "rentals",
+ "repair",
+ "report",
+ "republican",
+ "rest",
+ "restaurant",
+ "review",
+ "reviews",
+ "rexroth",
+ "rich",
+ "richardli",
+ "ricoh",
+ "ril",
+ "rio",
+ "rip",
+ "ro",
+ "rocher",
+ "rocks",
+ "rodeo",
+ "room",
+ "rs",
+ "rsvp",
+ "ru",
+ "ruhr",
+ "run",
+ "rw",
+ "rwe",
+ "ryukyu",
+ "sa",
+ "saarland",
+ "safe",
+ "safety",
+ "sakura",
+ "sale",
+ "salon",
+ "samsung",
+ "sandvik",
+ "sandvikcoromant",
+ "sanofi",
+ "sap",
+ "sapo",
+ "sarl",
+ "sas",
+ "save",
+ "saxo",
+ "sb",
+ "sbi",
+ "sbs",
+ "sc",
+ "sca",
+ "scb",
+ "schmidt",
+ "scholarships",
+ "school",
+ "schule",
+ "schwarz",
+ "science",
+ "scor",
+ "scot",
+ "sd",
+ "se",
+ "seat",
+ "security",
+ "seek",
+ "sener",
+ "services",
+ "sew",
+ "sex",
+ "sexy",
+ "sg",
+ "sh",
+ "sharp",
+ "shaw",
+ "shia",
+ "shiksha",
+ "shoes",
+ "shouji",
+ "show",
+ "shriram",
+ "si",
+ "silk",
+ "sina",
+ "singles",
+ "site",
+ "sj",
+ "sk",
+ "ski",
+ "skin",
+ "sky",
+ "skype",
+ "sl",
+ "sm",
+ "smile",
+ "sn",
+ "sncf",
+ "so",
+ "soccer",
+ "social",
+ "softbank",
+ "software",
+ "sohu",
+ "solar",
+ "solutions",
+ "song",
+ "sony",
+ "soy",
+ "space",
+ "spiegel",
+ "spot",
+ "spreadbetting",
+ "sr",
+ "srl",
+ "st",
+ "stada",
+ "star",
+ "starhub",
+ "statebank",
+ "statoil",
+ "stc",
+ "stcgroup",
+ "stockholm",
+ "storage",
+ "store",
+ "studio",
+ "study",
+ "style",
+ "su",
+ "sucks",
+ "supersport",
+ "supplies",
+ "supply",
+ "support",
+ "surf",
+ "surgery",
+ "suzuki",
+ "sv",
+ "swatch",
+ "swiss",
+ "sx",
+ "sy",
+ "sydney",
+ "symantec",
+ "systems",
+ "sz",
+ "tab",
+ "taipei",
+ "talk",
+ "taobao",
+ "tatamotors",
+ "tatar",
+ "tattoo",
+ "tax",
+ "taxi",
+ "tc",
+ "tci",
+ "td",
+ "tdk",
+ "team",
+ "tech",
+ "technology",
+ "tel",
+ "telecity",
+ "telefonica",
+ "temasek",
+ "tennis",
+ "teva",
+ "tf",
+ "tg",
+ "th",
+ "thd",
+ "theater",
+ "theatre",
+ "theguardian",
+ "tickets",
+ "tienda",
+ "tiffany",
+ "tips",
+ "tires",
+ "tirol",
+ "tj",
+ "tk",
+ "tl",
+ "tm",
+ "tmall",
+ "tn",
+ "to",
+ "today",
+ "tokyo",
+ "tools",
+ "top",
+ "toray",
+ "toshiba",
+ "tours",
+ "town",
+ "toyota",
+ "toys",
+ "tp",
+ "tr",
+ "trade",
+ "trading",
+ "training",
+ "travel",
+ "travelchannel",
+ "travelers",
+ "travelersinsurance",
+ "trust",
+ "trv",
+ "tt",
+ "tube",
+ "tui",
+ "tunes",
+ "tushu",
+ "tv",
+ "tvs",
+ "tw",
+ "tz",
+ "ua",
+ "ubs",
+ "ug",
+ "uk",
+ "university",
+ "uno",
+ "uol",
+ "ups",
+ "us",
+ "uy",
+ "uz",
+ "va",
+ "vacations",
+ "vana",
+ "vc",
+ "ve",
+ "vegas",
+ "ventures",
+ "versicherung",
+ "vet",
+ "vg",
+ "vi",
+ "viajes",
+ "video",
+ "vig",
+ "viking",
+ "villas",
+ "vin",
+ "vip",
+ "virgin",
+ "vision",
+ "vista",
+ "vistaprint",
+ "viva",
+ "vlaanderen",
+ "vn",
+ "vodka",
+ "volkswagen",
+ "vote",
+ "voting",
+ "voto",
+ "voyage",
+ "vu",
+ "vuelos",
+ "wales",
+ "walter",
+ "wang",
+ "wanggou",
+ "warman",
+ "watch",
+ "watches",
+ "weather",
+ "weatherchannel",
+ "webcam",
+ "weber",
+ "website",
+ "wed",
+ "wedding",
+ "weibo",
+ "weir",
+ "wf",
+ "whoswho",
+ "wien",
+ "wiki",
+ "williamhill",
+ "win",
+ "windows",
+ "wine",
+ "wme",
+ "work",
+ "works",
+ "world",
+ "ws",
+ "wtc",
+ "wtf",
+ "xbox",
+ "xerox",
+ "xihuan",
+ "xin",
+ "xn--11b4c3d",
+ "xn--1ck2e1b",
+ "xn--1qqw23a",
+ "xn--30rr7y",
+ "xn--3bst00m",
+ "xn--3ds443g",
+ "xn--3e0b707e",
+ "xn--3oq18vl8pn36a",
+ "xn--3pxu8k",
+ "xn--42c2d9a",
+ "xn--45brj9c",
+ "xn--45q11c",
+ "xn--4gbrim",
+ "xn--54b7fta0cc",
+ "xn--55qw42g",
+ "xn--55qx5d",
+ "xn--5tzm5g",
+ "xn--6frz82g",
+ "xn--6qq986b3xl",
+ "xn--80adxhks",
+ "xn--80ao21a",
+ "xn--80asehdb",
+ "xn--80aswg",
+ "xn--8y0a063a",
+ "xn--90a3ac",
+ "xn--90ais",
+ "xn--9dbq2a",
+ "xn--9et52u",
+ "xn--9krt00a",
+ "xn--b4w605ferd",
+ "xn--bck1b9a5dre4c",
+ "xn--c1avg",
+ "xn--c2br7g",
+ "xn--cck2b3b",
+ "xn--cg4bki",
+ "xn--clchc0ea0b2g2a9gcd",
+ "xn--czr694b",
+ "xn--czrs0t",
+ "xn--czru2d",
+ "xn--d1acj3b",
+ "xn--d1alf",
+ "xn--eckvdtc9d",
+ "xn--efvy88h",
+ "xn--estv75g",
+ "xn--fct429k",
+ "xn--fhbei",
+ "xn--fiq228c5hs",
+ "xn--fiq64b",
+ "xn--fiqs8s",
+ "xn--fiqz9s",
+ "xn--fjq720a",
+ "xn--flw351e",
+ "xn--fpcrj9c3d",
+ "xn--fzc2c9e2c",
+ "xn--fzys8d69uvgm",
+ "xn--g2xx48c",
+ "xn--gckr3f0f",
+ "xn--gecrj9c",
+ "xn--h2brj9c",
+ "xn--hxt814e",
+ "xn--i1b6b1a6a2e",
+ "xn--imr513n",
+ "xn--io0a7i",
+ "xn--j1aef",
+ "xn--j1amh",
+ "xn--j6w193g",
+ "xn--jlq61u9w7b",
+ "xn--jvr189m",
+ "xn--kcrx77d1x4a",
+ "xn--kprw13d",
+ "xn--kpry57d",
+ "xn--kpu716f",
+ "xn--kput3i",
+ "xn--l1acc",
+ "xn--lgbbat1ad8j",
+ "xn--mgb2ddes",
+ "xn--mgb9awbf",
+ "xn--mgba3a3ejt",
+ "xn--mgba3a4f16a",
+ "xn--mgba3a4fra",
+ "xn--mgba7c0bbn0a",
+ "xn--mgbaam7a8h",
+ "xn--mgbab2bd",
+ "xn--mgbai9a5eva00b",
+ "xn--mgbai9azgqp6j",
+ "xn--mgbayh7gpa",
+ "xn--mgbb9fbpob",
+ "xn--mgbbh1a71e",
+ "xn--mgbc0a9azcg",
+ "xn--mgberp4a5d4a87g",
+ "xn--mgberp4a5d4ar",
+ "xn--mgbpl2fh",
+ "xn--mgbqly7c0a67fbc",
+ "xn--mgbqly7cvafr",
+ "xn--mgbt3dhd",
+ "xn--mgbtf8fl",
+ "xn--mgbtx2b",
+ "xn--mgbx4cd0ab",
+ "xn--mix082f",
+ "xn--mix891f",
+ "xn--mk1bu44c",
+ "xn--mxtq1m",
+ "xn--ngbc5azd",
+ "xn--ngbe9e0a",
+ "xn--nnx388a",
+ "xn--node",
+ "xn--nqv7f",
+ "xn--nqv7fs00ema",
+ "xn--nyqy26a",
+ "xn--o3cw4h",
+ "xn--ogbpf8fl",
+ "xn--p1acf",
+ "xn--p1ai",
+ "xn--pbt977c",
+ "xn--pgbs0dh",
+ "xn--pssy2u",
+ "xn--q9jyb4c",
+ "xn--qcka1pmc",
+ "xn--qxam",
+ "xn--rhqv96g",
+ "xn--rovu88b",
+ "xn--s9brj9c",
+ "xn--ses554g",
+ "xn--t60b56a",
+ "xn--tckwe",
+ "xn--unup4y",
+ "xn--vermgensberater-ctb",
+ "xn--vermgensberatung-pwb",
+ "xn--vhquv",
+ "xn--vuq861b",
+ "xn--w4r85el8fhu5dnra",
+ "xn--wgbh1c",
+ "xn--wgbl6a",
+ "xn--xhq521b",
+ "xn--xkc2al3hye2a",
+ "xn--xkc2dl3a5ee0h",
+ "xn--y9a3aq",
+ "xn--yfro4i67o",
+ "xn--ygbi2ammx",
+ "xn--zfr164b",
+ "xperia",
+ "xxx",
+ "xyz",
+ "yachts",
+ "yahoo",
+ "yamaxun",
+ "yandex",
+ "ye",
+ "yodobashi",
+ "yoga",
+ "yokohama",
+ "you",
+ "youtube",
+ "yt",
+ "yun",
+ "za",
+ "zappos",
+ "zara",
+ "zero",
+ "zip",
+ "zippo",
+ "zm",
+ "zone",
+ "zuerich",
+ "zw",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "nom",
+ "ac",
+ "blogspot",
+ "co",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "sch",
+ "accident-investigation",
+ "accident-prevention",
+ "aerobatic",
+ "aeroclub",
+ "aerodrome",
+ "agents",
+ "air-surveillance",
+ "air-traffic-control",
+ "aircraft",
+ "airline",
+ "airport",
+ "airtraffic",
+ "ambulance",
+ "amusement",
+ "association",
+ "author",
+ "ballooning",
+ "broker",
+ "caa",
+ "cargo",
+ "catering",
+ "certification",
+ "championship",
+ "charter",
+ "civilaviation",
+ "club",
+ "conference",
+ "consultant",
+ "consulting",
+ "control",
+ "council",
+ "crew",
+ "design",
+ "dgca",
+ "educator",
+ "emergency",
+ "engine",
+ "engineer",
+ "entertainment",
+ "equipment",
+ "exchange",
+ "express",
+ "federation",
+ "flight",
+ "freight",
+ "fuel",
+ "gliding",
+ "government",
+ "groundhandling",
+ "group",
+ "hanggliding",
+ "homebuilt",
+ "insurance",
+ "journal",
+ "journalist",
+ "leasing",
+ "logistics",
+ "magazine",
+ "maintenance",
+ "marketplace",
+ "media",
+ "microlight",
+ "modelling",
+ "navigation",
+ "parachuting",
+ "paragliding",
+ "passenger-association",
+ "pilot",
+ "press",
+ "production",
+ "recreation",
+ "repbody",
+ "res",
+ "research",
+ "rotorcraft",
+ "safety",
+ "scientist",
+ "services",
+ "show",
+ "skydiving",
+ "software",
+ "student",
+ "taxi",
+ "trader",
+ "trading",
+ "trainer",
+ "union",
+ "workinggroup",
+ "works",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "co",
+ "com",
+ "net",
+ "nom",
+ "org",
+ "com",
+ "net",
+ "off",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "net",
+ "org",
+ "co",
+ "ed",
+ "gv",
+ "it",
+ "og",
+ "pb",
+ "com",
+ "edu",
+ "gob",
+ "gov",
+ "int",
+ "mil",
+ "net",
+ "org",
+ "tur",
+ "blogspot",
+ "e164",
+ "in-addr",
+ "ip6",
+ "iris",
+ "uri",
+ "urn",
+ "gov",
+ "ac",
+ "biz",
+ "co",
+ "gv",
+ "info",
+ "or",
+ "priv",
+ "blogspot",
+ "act",
+ "asn",
+ "com",
+ "conf",
+ "edu",
+ "gov",
+ "id",
+ "info",
+ "net",
+ "nsw",
+ "nt",
+ "org",
+ "oz",
+ "qld",
+ "sa",
+ "tas",
+ "vic",
+ "wa",
+ "blogspot",
+ "act",
+ "nsw",
+ "nt",
+ "qld",
+ "sa",
+ "tas",
+ "vic",
+ "wa",
+ "qld",
+ "sa",
+ "tas",
+ "vic",
+ "wa",
+ "com",
+ "biz",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "int",
+ "mil",
+ "name",
+ "net",
+ "org",
+ "pp",
+ "pro",
+ "blogspot",
+ "co",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "rs",
+ "unbi",
+ "unsa",
+ "biz",
+ "co",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "net",
+ "org",
+ "store",
+ "tv",
+ "ac",
+ "blogspot",
+ "gov",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "a",
+ "b",
+ "blogspot",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "co",
+ "com",
+ "edu",
+ "or",
+ "org",
+ "dyndns",
+ "for-better",
+ "for-more",
+ "for-some",
+ "for-the",
+ "selfip",
+ "webhop",
+ "asso",
+ "barreau",
+ "blogspot",
+ "gouv",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gob",
+ "gov",
+ "int",
+ "mil",
+ "net",
+ "org",
+ "tv",
+ "adm",
+ "adv",
+ "agr",
+ "am",
+ "arq",
+ "art",
+ "ato",
+ "b",
+ "bio",
+ "blog",
+ "bmd",
+ "cim",
+ "cng",
+ "cnt",
+ "com",
+ "coop",
+ "ecn",
+ "eco",
+ "edu",
+ "emp",
+ "eng",
+ "esp",
+ "etc",
+ "eti",
+ "far",
+ "flog",
+ "fm",
+ "fnd",
+ "fot",
+ "fst",
+ "g12",
+ "ggf",
+ "gov",
+ "imb",
+ "ind",
+ "inf",
+ "jor",
+ "jus",
+ "leg",
+ "lel",
+ "mat",
+ "med",
+ "mil",
+ "mp",
+ "mus",
+ "net",
+ "nom",
+ "not",
+ "ntr",
+ "odo",
+ "org",
+ "ppg",
+ "pro",
+ "psc",
+ "psi",
+ "qsl",
+ "radio",
+ "rec",
+ "slg",
+ "srv",
+ "taxi",
+ "teo",
+ "tmp",
+ "trd",
+ "tur",
+ "tv",
+ "vet",
+ "vlog",
+ "wiki",
+ "zlg",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "co",
+ "org",
+ "com",
+ "gov",
+ "mil",
+ "of",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "za",
+ "ab",
+ "bc",
+ "blogspot",
+ "co",
+ "gc",
+ "mb",
+ "nb",
+ "nf",
+ "nl",
+ "ns",
+ "nt",
+ "nu",
+ "on",
+ "pe",
+ "qc",
+ "sk",
+ "yk",
+ "ftpaccess",
+ "game-server",
+ "myphotos",
+ "scrapping",
+ "gov",
+ "blogspot",
+ "blogspot",
+ "ac",
+ "asso",
+ "co",
+ "com",
+ "ed",
+ "edu",
+ "go",
+ "gouv",
+ "int",
+ "md",
+ "net",
+ "or",
+ "org",
+ "presse",
+ "xn--aroport-bya",
+ "www",
+ "blogspot",
+ "co",
+ "gob",
+ "gov",
+ "mil",
+ "co",
+ "com",
+ "gov",
+ "net",
+ "ac",
+ "ah",
+ "amazonaws",
+ "bj",
+ "com",
+ "cq",
+ "edu",
+ "fj",
+ "gd",
+ "gov",
+ "gs",
+ "gx",
+ "gz",
+ "ha",
+ "hb",
+ "he",
+ "hi",
+ "hk",
+ "hl",
+ "hn",
+ "jl",
+ "js",
+ "jx",
+ "ln",
+ "mil",
+ "mo",
+ "net",
+ "nm",
+ "nx",
+ "org",
+ "qh",
+ "sc",
+ "sd",
+ "sh",
+ "sn",
+ "sx",
+ "tj",
+ "tw",
+ "xj",
+ "xn--55qx5d",
+ "xn--io0a7i",
+ "xn--od0alg",
+ "xz",
+ "yn",
+ "zj",
+ "compute",
+ "cn-north-1",
+ "amazonaws",
+ "cn-north-1",
+ "s3",
+ "arts",
+ "com",
+ "edu",
+ "firm",
+ "gov",
+ "info",
+ "int",
+ "mil",
+ "net",
+ "nom",
+ "org",
+ "rec",
+ "web",
+ "blogspot",
+ "1kapp",
+ "4u",
+ "africa",
+ "amazonaws",
+ "appspot",
+ "ar",
+ "betainabox",
+ "blogdns",
+ "blogspot",
+ "br",
+ "cechire",
+ "cloudcontrolapp",
+ "cloudcontrolled",
+ "cn",
+ "co",
+ "codespot",
+ "de",
+ "dnsalias",
+ "dnsdojo",
+ "doesntexist",
+ "dontexist",
+ "doomdns",
+ "dreamhosters",
+ "dyn-o-saur",
+ "dynalias",
+ "dyndns-at-home",
+ "dyndns-at-work",
+ "dyndns-blog",
+ "dyndns-free",
+ "dyndns-home",
+ "dyndns-ip",
+ "dyndns-mail",
+ "dyndns-office",
+ "dyndns-pics",
+ "dyndns-remote",
+ "dyndns-server",
+ "dyndns-web",
+ "dyndns-wiki",
+ "dyndns-work",
+ "elasticbeanstalk",
+ "est-a-la-maison",
+ "est-a-la-masion",
+ "est-le-patron",
+ "est-mon-blogueur",
+ "eu",
+ "firebaseapp",
+ "flynnhub",
+ "from-ak",
+ "from-al",
+ "from-ar",
+ "from-ca",
+ "from-ct",
+ "from-dc",
+ "from-de",
+ "from-fl",
+ "from-ga",
+ "from-hi",
+ "from-ia",
+ "from-id",
+ "from-il",
+ "from-in",
+ "from-ks",
+ "from-ky",
+ "from-ma",
+ "from-md",
+ "from-mi",
+ "from-mn",
+ "from-mo",
+ "from-ms",
+ "from-mt",
+ "from-nc",
+ "from-nd",
+ "from-ne",
+ "from-nh",
+ "from-nj",
+ "from-nm",
+ "from-nv",
+ "from-oh",
+ "from-ok",
+ "from-or",
+ "from-pa",
+ "from-pr",
+ "from-ri",
+ "from-sc",
+ "from-sd",
+ "from-tn",
+ "from-tx",
+ "from-ut",
+ "from-va",
+ "from-vt",
+ "from-wa",
+ "from-wi",
+ "from-wv",
+ "from-wy",
+ "gb",
+ "getmyip",
+ "githubusercontent",
+ "googleapis",
+ "googlecode",
+ "gotdns",
+ "gr",
+ "herokuapp",
+ "herokussl",
+ "hk",
+ "hobby-site",
+ "homelinux",
+ "homeunix",
+ "hu",
+ "iamallama",
+ "is-a-anarchist",
+ "is-a-blogger",
+ "is-a-bookkeeper",
+ "is-a-bulls-fan",
+ "is-a-caterer",
+ "is-a-chef",
+ "is-a-conservative",
+ "is-a-cpa",
+ "is-a-cubicle-slave",
+ "is-a-democrat",
+ "is-a-designer",
+ "is-a-doctor",
+ "is-a-financialadvisor",
+ "is-a-geek",
+ "is-a-green",
+ "is-a-guru",
+ "is-a-hard-worker",
+ "is-a-hunter",
+ "is-a-landscaper",
+ "is-a-lawyer",
+ "is-a-liberal",
+ "is-a-libertarian",
+ "is-a-llama",
+ "is-a-musician",
+ "is-a-nascarfan",
+ "is-a-nurse",
+ "is-a-painter",
+ "is-a-personaltrainer",
+ "is-a-photographer",
+ "is-a-player",
+ "is-a-republican",
+ "is-a-rockstar",
+ "is-a-socialist",
+ "is-a-student",
+ "is-a-teacher",
+ "is-a-techie",
+ "is-a-therapist",
+ "is-an-accountant",
+ "is-an-actor",
+ "is-an-actress",
+ "is-an-anarchist",
+ "is-an-artist",
+ "is-an-engineer",
+ "is-an-entertainer",
+ "is-certified",
+ "is-gone",
+ "is-into-anime",
+ "is-into-cars",
+ "is-into-cartoons",
+ "is-into-games",
+ "is-leet",
+ "is-not-certified",
+ "is-slick",
+ "is-uberleet",
+ "is-with-theband",
+ "isa-geek",
+ "isa-hockeynut",
+ "issmarterthanyou",
+ "jpn",
+ "kr",
+ "likes-pie",
+ "likescandy",
+ "mex",
+ "neat-url",
+ "nfshost",
+ "no",
+ "operaunite",
+ "outsystemscloud",
+ "pagespeedmobilizer",
+ "qc",
+ "rhcloud",
+ "ro",
+ "ru",
+ "sa",
+ "saves-the-whales",
+ "se",
+ "selfip",
+ "sells-for-less",
+ "sells-for-u",
+ "servebbs",
+ "simple-url",
+ "sinaapp",
+ "space-to-rent",
+ "teaches-yoga",
+ "uk",
+ "us",
+ "uy",
+ "vipsinaapp",
+ "withgoogle",
+ "writesthisblog",
+ "yolasite",
+ "za",
+ "compute",
+ "compute-1",
+ "elb",
+ "eu-central-1",
+ "s3",
+ "s3-ap-northeast-1",
+ "s3-ap-southeast-1",
+ "s3-ap-southeast-2",
+ "s3-eu-central-1",
+ "s3-eu-west-1",
+ "s3-external-1",
+ "s3-external-2",
+ "s3-fips-us-gov-west-1",
+ "s3-sa-east-1",
+ "s3-us-gov-west-1",
+ "s3-us-west-1",
+ "s3-us-west-2",
+ "us-east-1",
+ "ap-northeast-1",
+ "ap-southeast-1",
+ "ap-southeast-2",
+ "eu-central-1",
+ "eu-west-1",
+ "sa-east-1",
+ "us-gov-west-1",
+ "us-west-1",
+ "us-west-2",
+ "z-1",
+ "z-2",
+ "s3",
+ "ac",
+ "co",
+ "ed",
+ "fi",
+ "go",
+ "or",
+ "sa",
+ "com",
+ "edu",
+ "gov",
+ "inf",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "net",
+ "org",
+ "ath",
+ "gov",
+ "ac",
+ "biz",
+ "com",
+ "ekloges",
+ "gov",
+ "ltd",
+ "name",
+ "net",
+ "org",
+ "parliament",
+ "press",
+ "pro",
+ "tm",
+ "blogspot",
+ "blogspot",
+ "blogspot",
+ "com",
+ "fuettertdasnetz",
+ "isteingeek",
+ "istmein",
+ "lebtimnetz",
+ "leitungsen",
+ "traeumtgerade",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "art",
+ "com",
+ "edu",
+ "gob",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "sld",
+ "web",
+ "art",
+ "asso",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "pol",
+ "com",
+ "edu",
+ "fin",
+ "gob",
+ "gov",
+ "info",
+ "k12",
+ "med",
+ "mil",
+ "net",
+ "org",
+ "pro",
+ "aip",
+ "com",
+ "edu",
+ "fie",
+ "gov",
+ "lib",
+ "med",
+ "org",
+ "pri",
+ "riik",
+ "blogspot",
+ "com",
+ "edu",
+ "eun",
+ "gov",
+ "mil",
+ "name",
+ "net",
+ "org",
+ "sci",
+ "blogspot",
+ "com",
+ "edu",
+ "gob",
+ "nom",
+ "org",
+ "blogspot",
+ "biz",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "name",
+ "net",
+ "org",
+ "aland",
+ "blogspot",
+ "iki",
+ "aeroport",
+ "assedic",
+ "asso",
+ "avocat",
+ "avoues",
+ "blogspot",
+ "cci",
+ "chambagri",
+ "chirurgiens-dentistes",
+ "com",
+ "experts-comptables",
+ "geometre-expert",
+ "gouv",
+ "greta",
+ "huissier-justice",
+ "medecin",
+ "nom",
+ "notaires",
+ "pharmacien",
+ "port",
+ "prd",
+ "presse",
+ "tm",
+ "veterinaire",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "pvt",
+ "co",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "ltd",
+ "mod",
+ "org",
+ "co",
+ "com",
+ "edu",
+ "net",
+ "org",
+ "ac",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "asso",
+ "com",
+ "edu",
+ "mobi",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gob",
+ "ind",
+ "mil",
+ "net",
+ "org",
+ "co",
+ "com",
+ "net",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "idv",
+ "inc",
+ "ltd",
+ "net",
+ "org",
+ "xn--55qx5d",
+ "xn--ciqpn",
+ "xn--gmq050i",
+ "xn--gmqw5a",
+ "xn--io0a7i",
+ "xn--lcvr32d",
+ "xn--mk0axi",
+ "xn--mxtq1m",
+ "xn--od0alg",
+ "xn--od0aq3b",
+ "xn--tn0ag",
+ "xn--uc0atv",
+ "xn--uc0ay4a",
+ "xn--wcvs22d",
+ "xn--zf0avx",
+ "com",
+ "edu",
+ "gob",
+ "mil",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "from",
+ "iz",
+ "name",
+ "adult",
+ "art",
+ "asso",
+ "com",
+ "coop",
+ "edu",
+ "firm",
+ "gouv",
+ "info",
+ "med",
+ "net",
+ "org",
+ "perso",
+ "pol",
+ "pro",
+ "rel",
+ "shop",
+ "2000",
+ "agrar",
+ "blogspot",
+ "bolt",
+ "casino",
+ "city",
+ "co",
+ "erotica",
+ "erotika",
+ "film",
+ "forum",
+ "games",
+ "hotel",
+ "info",
+ "ingatlan",
+ "jogasz",
+ "konyvelo",
+ "lakas",
+ "media",
+ "news",
+ "org",
+ "priv",
+ "reklam",
+ "sex",
+ "shop",
+ "sport",
+ "suli",
+ "szex",
+ "tm",
+ "tozsde",
+ "utazas",
+ "video",
+ "ac",
+ "biz",
+ "co",
+ "desa",
+ "go",
+ "mil",
+ "my",
+ "net",
+ "or",
+ "sch",
+ "web",
+ "blogspot",
+ "blogspot",
+ "gov",
+ "co",
+ "blogspot",
+ "ac",
+ "co",
+ "com",
+ "net",
+ "org",
+ "tt",
+ "tv",
+ "ltd",
+ "plc",
+ "ac",
+ "blogspot",
+ "co",
+ "edu",
+ "firm",
+ "gen",
+ "gov",
+ "ind",
+ "mil",
+ "net",
+ "nic",
+ "org",
+ "res",
+ "barrel-of-knowledge",
+ "barrell-of-knowledge",
+ "dyndns",
+ "for-our",
+ "groks-the",
+ "groks-this",
+ "here-for-more",
+ "knowsitall",
+ "selfip",
+ "webhop",
+ "eu",
+ "com",
+ "github",
+ "nid",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "ac",
+ "co",
+ "gov",
+ "id",
+ "net",
+ "org",
+ "sch",
+ "xn--mgba3a4f16a",
+ "xn--mgba3a4fra",
+ "blogspot",
+ "com",
+ "cupcake",
+ "edu",
+ "gov",
+ "int",
+ "net",
+ "org",
+ "abr",
+ "abruzzo",
+ "ag",
+ "agrigento",
+ "al",
+ "alessandria",
+ "alto-adige",
+ "altoadige",
+ "an",
+ "ancona",
+ "andria-barletta-trani",
+ "andria-trani-barletta",
+ "andriabarlettatrani",
+ "andriatranibarletta",
+ "ao",
+ "aosta",
+ "aosta-valley",
+ "aostavalley",
+ "aoste",
+ "ap",
+ "aq",
+ "aquila",
+ "ar",
+ "arezzo",
+ "ascoli-piceno",
+ "ascolipiceno",
+ "asti",
+ "at",
+ "av",
+ "avellino",
+ "ba",
+ "balsan",
+ "bari",
+ "barletta-trani-andria",
+ "barlettatraniandria",
+ "bas",
+ "basilicata",
+ "belluno",
+ "benevento",
+ "bergamo",
+ "bg",
+ "bi",
+ "biella",
+ "bl",
+ "blogspot",
+ "bn",
+ "bo",
+ "bologna",
+ "bolzano",
+ "bozen",
+ "br",
+ "brescia",
+ "brindisi",
+ "bs",
+ "bt",
+ "bz",
+ "ca",
+ "cagliari",
+ "cal",
+ "calabria",
+ "caltanissetta",
+ "cam",
+ "campania",
+ "campidano-medio",
+ "campidanomedio",
+ "campobasso",
+ "carbonia-iglesias",
+ "carboniaiglesias",
+ "carrara-massa",
+ "carraramassa",
+ "caserta",
+ "catania",
+ "catanzaro",
+ "cb",
+ "ce",
+ "cesena-forli",
+ "cesenaforli",
+ "ch",
+ "chieti",
+ "ci",
+ "cl",
+ "cn",
+ "co",
+ "como",
+ "cosenza",
+ "cr",
+ "cremona",
+ "crotone",
+ "cs",
+ "ct",
+ "cuneo",
+ "cz",
+ "dell-ogliastra",
+ "dellogliastra",
+ "edu",
+ "emilia-romagna",
+ "emiliaromagna",
+ "emr",
+ "en",
+ "enna",
+ "fc",
+ "fe",
+ "fermo",
+ "ferrara",
+ "fg",
+ "fi",
+ "firenze",
+ "florence",
+ "fm",
+ "foggia",
+ "forli-cesena",
+ "forlicesena",
+ "fr",
+ "friuli-v-giulia",
+ "friuli-ve-giulia",
+ "friuli-vegiulia",
+ "friuli-venezia-giulia",
+ "friuli-veneziagiulia",
+ "friuli-vgiulia",
+ "friuliv-giulia",
+ "friulive-giulia",
+ "friulivegiulia",
+ "friulivenezia-giulia",
+ "friuliveneziagiulia",
+ "friulivgiulia",
+ "frosinone",
+ "fvg",
+ "ge",
+ "genoa",
+ "genova",
+ "go",
+ "gorizia",
+ "gov",
+ "gr",
+ "grosseto",
+ "iglesias-carbonia",
+ "iglesiascarbonia",
+ "im",
+ "imperia",
+ "is",
+ "isernia",
+ "kr",
+ "la-spezia",
+ "laquila",
+ "laspezia",
+ "latina",
+ "laz",
+ "lazio",
+ "lc",
+ "le",
+ "lecce",
+ "lecco",
+ "li",
+ "lig",
+ "liguria",
+ "livorno",
+ "lo",
+ "lodi",
+ "lom",
+ "lombardia",
+ "lombardy",
+ "lt",
+ "lu",
+ "lucania",
+ "lucca",
+ "macerata",
+ "mantova",
+ "mar",
+ "marche",
+ "massa-carrara",
+ "massacarrara",
+ "matera",
+ "mb",
+ "mc",
+ "me",
+ "medio-campidano",
+ "mediocampidano",
+ "messina",
+ "mi",
+ "milan",
+ "milano",
+ "mn",
+ "mo",
+ "modena",
+ "mol",
+ "molise",
+ "monza",
+ "monza-brianza",
+ "monza-e-della-brianza",
+ "monzabrianza",
+ "monzaebrianza",
+ "monzaedellabrianza",
+ "ms",
+ "mt",
+ "na",
+ "naples",
+ "napoli",
+ "no",
+ "novara",
+ "nu",
+ "nuoro",
+ "og",
+ "ogliastra",
+ "olbia-tempio",
+ "olbiatempio",
+ "or",
+ "oristano",
+ "ot",
+ "pa",
+ "padova",
+ "padua",
+ "palermo",
+ "parma",
+ "pavia",
+ "pc",
+ "pd",
+ "pe",
+ "perugia",
+ "pesaro-urbino",
+ "pesarourbino",
+ "pescara",
+ "pg",
+ "pi",
+ "piacenza",
+ "piedmont",
+ "piemonte",
+ "pisa",
+ "pistoia",
+ "pmn",
+ "pn",
+ "po",
+ "pordenone",
+ "potenza",
+ "pr",
+ "prato",
+ "pt",
+ "pu",
+ "pug",
+ "puglia",
+ "pv",
+ "pz",
+ "ra",
+ "ragusa",
+ "ravenna",
+ "rc",
+ "re",
+ "reggio-calabria",
+ "reggio-emilia",
+ "reggiocalabria",
+ "reggioemilia",
+ "rg",
+ "ri",
+ "rieti",
+ "rimini",
+ "rm",
+ "rn",
+ "ro",
+ "roma",
+ "rome",
+ "rovigo",
+ "sa",
+ "salerno",
+ "sar",
+ "sardegna",
+ "sardinia",
+ "sassari",
+ "savona",
+ "si",
+ "sic",
+ "sicilia",
+ "sicily",
+ "siena",
+ "siracusa",
+ "so",
+ "sondrio",
+ "sp",
+ "sr",
+ "ss",
+ "suedtirol",
+ "sv",
+ "ta",
+ "taa",
+ "taranto",
+ "te",
+ "tempio-olbia",
+ "tempioolbia",
+ "teramo",
+ "terni",
+ "tn",
+ "to",
+ "torino",
+ "tos",
+ "toscana",
+ "tp",
+ "tr",
+ "trani-andria-barletta",
+ "trani-barletta-andria",
+ "traniandriabarletta",
+ "tranibarlettaandria",
+ "trapani",
+ "trentino",
+ "trentino-a-adige",
+ "trentino-aadige",
+ "trentino-alto-adige",
+ "trentino-altoadige",
+ "trentino-s-tirol",
+ "trentino-stirol",
+ "trentino-sud-tirol",
+ "trentino-sudtirol",
+ "trentino-sued-tirol",
+ "trentino-suedtirol",
+ "trentinoa-adige",
+ "trentinoaadige",
+ "trentinoalto-adige",
+ "trentinoaltoadige",
+ "trentinos-tirol",
+ "trentinostirol",
+ "trentinosud-tirol",
+ "trentinosudtirol",
+ "trentinosued-tirol",
+ "trentinosuedtirol",
+ "trento",
+ "treviso",
+ "trieste",
+ "ts",
+ "turin",
+ "tuscany",
+ "tv",
+ "ud",
+ "udine",
+ "umb",
+ "umbria",
+ "urbino-pesaro",
+ "urbinopesaro",
+ "va",
+ "val-d-aosta",
+ "val-daosta",
+ "vald-aosta",
+ "valdaosta",
+ "valle-aosta",
+ "valle-d-aosta",
+ "valle-daosta",
+ "valleaosta",
+ "valled-aosta",
+ "valledaosta",
+ "vallee-aoste",
+ "valleeaoste",
+ "vao",
+ "varese",
+ "vb",
+ "vc",
+ "vda",
+ "ve",
+ "ven",
+ "veneto",
+ "venezia",
+ "venice",
+ "verbania",
+ "vercelli",
+ "verona",
+ "vi",
+ "vibo-valentia",
+ "vibovalentia",
+ "vicenza",
+ "viterbo",
+ "vr",
+ "vs",
+ "vt",
+ "vv",
+ "co",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "name",
+ "net",
+ "org",
+ "sch",
+ "ac",
+ "ad",
+ "aichi",
+ "akita",
+ "aomori",
+ "blogspot",
+ "chiba",
+ "co",
+ "ed",
+ "ehime",
+ "fukui",
+ "fukuoka",
+ "fukushima",
+ "gifu",
+ "go",
+ "gr",
+ "gunma",
+ "hiroshima",
+ "hokkaido",
+ "hyogo",
+ "ibaraki",
+ "ishikawa",
+ "iwate",
+ "kagawa",
+ "kagoshima",
+ "kanagawa",
+ "kawasaki",
+ "kitakyushu",
+ "kobe",
+ "kochi",
+ "kumamoto",
+ "kyoto",
+ "lg",
+ "mie",
+ "miyagi",
+ "miyazaki",
+ "nagano",
+ "nagasaki",
+ "nagoya",
+ "nara",
+ "ne",
+ "niigata",
+ "oita",
+ "okayama",
+ "okinawa",
+ "or",
+ "osaka",
+ "saga",
+ "saitama",
+ "sapporo",
+ "sendai",
+ "shiga",
+ "shimane",
+ "shizuoka",
+ "tochigi",
+ "tokushima",
+ "tokyo",
+ "tottori",
+ "toyama",
+ "wakayama",
+ "xn--0trq7p7nn",
+ "xn--1ctwo",
+ "xn--1lqs03n",
+ "xn--1lqs71d",
+ "xn--2m4a15e",
+ "xn--32vp30h",
+ "xn--4it168d",
+ "xn--4it797k",
+ "xn--4pvxs",
+ "xn--5js045d",
+ "xn--5rtp49c",
+ "xn--5rtq34k",
+ "xn--6btw5a",
+ "xn--6orx2r",
+ "xn--7t0a264c",
+ "xn--8ltr62k",
+ "xn--8pvr4u",
+ "xn--c3s14m",
+ "xn--d5qv7z876c",
+ "xn--djrs72d6uy",
+ "xn--djty4k",
+ "xn--efvn9s",
+ "xn--ehqz56n",
+ "xn--elqq16h",
+ "xn--f6qx53a",
+ "xn--k7yn95e",
+ "xn--kbrq7o",
+ "xn--klt787d",
+ "xn--kltp7d",
+ "xn--kltx9a",
+ "xn--klty5x",
+ "xn--mkru45i",
+ "xn--nit225k",
+ "xn--ntso0iqx3a",
+ "xn--ntsq17g",
+ "xn--pssu33l",
+ "xn--qqqt11m",
+ "xn--rht27z",
+ "xn--rht3d",
+ "xn--rht61e",
+ "xn--rny31h",
+ "xn--tor131o",
+ "xn--uist22h",
+ "xn--uisz3g",
+ "xn--uuwu58a",
+ "xn--vgu402c",
+ "xn--zbx025d",
+ "yamagata",
+ "yamaguchi",
+ "yamanashi",
+ "yokohama",
+ "aisai",
+ "ama",
+ "anjo",
+ "asuke",
+ "chiryu",
+ "chita",
+ "fuso",
+ "gamagori",
+ "handa",
+ "hazu",
+ "hekinan",
+ "higashiura",
+ "ichinomiya",
+ "inazawa",
+ "inuyama",
+ "isshiki",
+ "iwakura",
+ "kanie",
+ "kariya",
+ "kasugai",
+ "kira",
+ "kiyosu",
+ "komaki",
+ "konan",
+ "kota",
+ "mihama",
+ "miyoshi",
+ "nishio",
+ "nisshin",
+ "obu",
+ "oguchi",
+ "oharu",
+ "okazaki",
+ "owariasahi",
+ "seto",
+ "shikatsu",
+ "shinshiro",
+ "shitara",
+ "tahara",
+ "takahama",
+ "tobishima",
+ "toei",
+ "togo",
+ "tokai",
+ "tokoname",
+ "toyoake",
+ "toyohashi",
+ "toyokawa",
+ "toyone",
+ "toyota",
+ "tsushima",
+ "yatomi",
+ "akita",
+ "daisen",
+ "fujisato",
+ "gojome",
+ "hachirogata",
+ "happou",
+ "higashinaruse",
+ "honjo",
+ "honjyo",
+ "ikawa",
+ "kamikoani",
+ "kamioka",
+ "katagami",
+ "kazuno",
+ "kitaakita",
+ "kosaka",
+ "kyowa",
+ "misato",
+ "mitane",
+ "moriyoshi",
+ "nikaho",
+ "noshiro",
+ "odate",
+ "oga",
+ "ogata",
+ "semboku",
+ "yokote",
+ "yurihonjo",
+ "aomori",
+ "gonohe",
+ "hachinohe",
+ "hashikami",
+ "hiranai",
+ "hirosaki",
+ "itayanagi",
+ "kuroishi",
+ "misawa",
+ "mutsu",
+ "nakadomari",
+ "noheji",
+ "oirase",
+ "owani",
+ "rokunohe",
+ "sannohe",
+ "shichinohe",
+ "shingo",
+ "takko",
+ "towada",
+ "tsugaru",
+ "tsuruta",
+ "abiko",
+ "asahi",
+ "chonan",
+ "chosei",
+ "choshi",
+ "chuo",
+ "funabashi",
+ "futtsu",
+ "hanamigawa",
+ "ichihara",
+ "ichikawa",
+ "ichinomiya",
+ "inzai",
+ "isumi",
+ "kamagaya",
+ "kamogawa",
+ "kashiwa",
+ "katori",
+ "katsuura",
+ "kimitsu",
+ "kisarazu",
+ "kozaki",
+ "kujukuri",
+ "kyonan",
+ "matsudo",
+ "midori",
+ "mihama",
+ "minamiboso",
+ "mobara",
+ "mutsuzawa",
+ "nagara",
+ "nagareyama",
+ "narashino",
+ "narita",
+ "noda",
+ "oamishirasato",
+ "omigawa",
+ "onjuku",
+ "otaki",
+ "sakae",
+ "sakura",
+ "shimofusa",
+ "shirako",
+ "shiroi",
+ "shisui",
+ "sodegaura",
+ "sosa",
+ "tako",
+ "tateyama",
+ "togane",
+ "tohnosho",
+ "tomisato",
+ "urayasu",
+ "yachimata",
+ "yachiyo",
+ "yokaichiba",
+ "yokoshibahikari",
+ "yotsukaido",
+ "ainan",
+ "honai",
+ "ikata",
+ "imabari",
+ "iyo",
+ "kamijima",
+ "kihoku",
+ "kumakogen",
+ "masaki",
+ "matsuno",
+ "matsuyama",
+ "namikata",
+ "niihama",
+ "ozu",
+ "saijo",
+ "seiyo",
+ "shikokuchuo",
+ "tobe",
+ "toon",
+ "uchiko",
+ "uwajima",
+ "yawatahama",
+ "echizen",
+ "eiheiji",
+ "fukui",
+ "ikeda",
+ "katsuyama",
+ "mihama",
+ "minamiechizen",
+ "obama",
+ "ohi",
+ "ono",
+ "sabae",
+ "sakai",
+ "takahama",
+ "tsuruga",
+ "wakasa",
+ "ashiya",
+ "buzen",
+ "chikugo",
+ "chikuho",
+ "chikujo",
+ "chikushino",
+ "chikuzen",
+ "chuo",
+ "dazaifu",
+ "fukuchi",
+ "hakata",
+ "higashi",
+ "hirokawa",
+ "hisayama",
+ "iizuka",
+ "inatsuki",
+ "kaho",
+ "kasuga",
+ "kasuya",
+ "kawara",
+ "keisen",
+ "koga",
+ "kurate",
+ "kurogi",
+ "kurume",
+ "minami",
+ "miyako",
+ "miyama",
+ "miyawaka",
+ "mizumaki",
+ "munakata",
+ "nakagawa",
+ "nakama",
+ "nishi",
+ "nogata",
+ "ogori",
+ "okagaki",
+ "okawa",
+ "oki",
+ "omuta",
+ "onga",
+ "onojo",
+ "oto",
+ "saigawa",
+ "sasaguri",
+ "shingu",
+ "shinyoshitomi",
+ "shonai",
+ "soeda",
+ "sue",
+ "tachiarai",
+ "tagawa",
+ "takata",
+ "toho",
+ "toyotsu",
+ "tsuiki",
+ "ukiha",
+ "umi",
+ "usui",
+ "yamada",
+ "yame",
+ "yanagawa",
+ "yukuhashi",
+ "aizubange",
+ "aizumisato",
+ "aizuwakamatsu",
+ "asakawa",
+ "bandai",
+ "date",
+ "fukushima",
+ "furudono",
+ "futaba",
+ "hanawa",
+ "higashi",
+ "hirata",
+ "hirono",
+ "iitate",
+ "inawashiro",
+ "ishikawa",
+ "iwaki",
+ "izumizaki",
+ "kagamiishi",
+ "kaneyama",
+ "kawamata",
+ "kitakata",
+ "kitashiobara",
+ "koori",
+ "koriyama",
+ "kunimi",
+ "miharu",
+ "mishima",
+ "namie",
+ "nango",
+ "nishiaizu",
+ "nishigo",
+ "okuma",
+ "omotego",
+ "ono",
+ "otama",
+ "samegawa",
+ "shimogo",
+ "shirakawa",
+ "showa",
+ "soma",
+ "sukagawa",
+ "taishin",
+ "tamakawa",
+ "tanagura",
+ "tenei",
+ "yabuki",
+ "yamato",
+ "yamatsuri",
+ "yanaizu",
+ "yugawa",
+ "anpachi",
+ "ena",
+ "gifu",
+ "ginan",
+ "godo",
+ "gujo",
+ "hashima",
+ "hichiso",
+ "hida",
+ "higashishirakawa",
+ "ibigawa",
+ "ikeda",
+ "kakamigahara",
+ "kani",
+ "kasahara",
+ "kasamatsu",
+ "kawaue",
+ "kitagata",
+ "mino",
+ "minokamo",
+ "mitake",
+ "mizunami",
+ "motosu",
+ "nakatsugawa",
+ "ogaki",
+ "sakahogi",
+ "seki",
+ "sekigahara",
+ "shirakawa",
+ "tajimi",
+ "takayama",
+ "tarui",
+ "toki",
+ "tomika",
+ "wanouchi",
+ "yamagata",
+ "yaotsu",
+ "yoro",
+ "annaka",
+ "chiyoda",
+ "fujioka",
+ "higashiagatsuma",
+ "isesaki",
+ "itakura",
+ "kanna",
+ "kanra",
+ "katashina",
+ "kawaba",
+ "kiryu",
+ "kusatsu",
+ "maebashi",
+ "meiwa",
+ "midori",
+ "minakami",
+ "naganohara",
+ "nakanojo",
+ "nanmoku",
+ "numata",
+ "oizumi",
+ "ora",
+ "ota",
+ "shibukawa",
+ "shimonita",
+ "shinto",
+ "showa",
+ "takasaki",
+ "takayama",
+ "tamamura",
+ "tatebayashi",
+ "tomioka",
+ "tsukiyono",
+ "tsumagoi",
+ "ueno",
+ "yoshioka",
+ "asaminami",
+ "daiwa",
+ "etajima",
+ "fuchu",
+ "fukuyama",
+ "hatsukaichi",
+ "higashihiroshima",
+ "hongo",
+ "jinsekikogen",
+ "kaita",
+ "kui",
+ "kumano",
+ "kure",
+ "mihara",
+ "miyoshi",
+ "naka",
+ "onomichi",
+ "osakikamijima",
+ "otake",
+ "saka",
+ "sera",
+ "seranishi",
+ "shinichi",
+ "shobara",
+ "takehara",
+ "abashiri",
+ "abira",
+ "aibetsu",
+ "akabira",
+ "akkeshi",
+ "asahikawa",
+ "ashibetsu",
+ "ashoro",
+ "assabu",
+ "atsuma",
+ "bibai",
+ "biei",
+ "bifuka",
+ "bihoro",
+ "biratori",
+ "chippubetsu",
+ "chitose",
+ "date",
+ "ebetsu",
+ "embetsu",
+ "eniwa",
+ "erimo",
+ "esan",
+ "esashi",
+ "fukagawa",
+ "fukushima",
+ "furano",
+ "furubira",
+ "haboro",
+ "hakodate",
+ "hamatonbetsu",
+ "hidaka",
+ "higashikagura",
+ "higashikawa",
+ "hiroo",
+ "hokuryu",
+ "hokuto",
+ "honbetsu",
+ "horokanai",
+ "horonobe",
+ "ikeda",
+ "imakane",
+ "ishikari",
+ "iwamizawa",
+ "iwanai",
+ "kamifurano",
+ "kamikawa",
+ "kamishihoro",
+ "kamisunagawa",
+ "kamoenai",
+ "kayabe",
+ "kembuchi",
+ "kikonai",
+ "kimobetsu",
+ "kitahiroshima",
+ "kitami",
+ "kiyosato",
+ "koshimizu",
+ "kunneppu",
+ "kuriyama",
+ "kuromatsunai",
+ "kushiro",
+ "kutchan",
+ "kyowa",
+ "mashike",
+ "matsumae",
+ "mikasa",
+ "minamifurano",
+ "mombetsu",
+ "moseushi",
+ "mukawa",
+ "muroran",
+ "naie",
+ "nakagawa",
+ "nakasatsunai",
+ "nakatombetsu",
+ "nanae",
+ "nanporo",
+ "nayoro",
+ "nemuro",
+ "niikappu",
+ "niki",
+ "nishiokoppe",
+ "noboribetsu",
+ "numata",
+ "obihiro",
+ "obira",
+ "oketo",
+ "okoppe",
+ "otaru",
+ "otobe",
+ "otofuke",
+ "otoineppu",
+ "oumu",
+ "ozora",
+ "pippu",
+ "rankoshi",
+ "rebun",
+ "rikubetsu",
+ "rishiri",
+ "rishirifuji",
+ "saroma",
+ "sarufutsu",
+ "shakotan",
+ "shari",
+ "shibecha",
+ "shibetsu",
+ "shikabe",
+ "shikaoi",
+ "shimamaki",
+ "shimizu",
+ "shimokawa",
+ "shinshinotsu",
+ "shintoku",
+ "shiranuka",
+ "shiraoi",
+ "shiriuchi",
+ "sobetsu",
+ "sunagawa",
+ "taiki",
+ "takasu",
+ "takikawa",
+ "takinoue",
+ "teshikaga",
+ "tobetsu",
+ "tohma",
+ "tomakomai",
+ "tomari",
+ "toya",
+ "toyako",
+ "toyotomi",
+ "toyoura",
+ "tsubetsu",
+ "tsukigata",
+ "urakawa",
+ "urausu",
+ "uryu",
+ "utashinai",
+ "wakkanai",
+ "wassamu",
+ "yakumo",
+ "yoichi",
+ "aioi",
+ "akashi",
+ "ako",
+ "amagasaki",
+ "aogaki",
+ "asago",
+ "ashiya",
+ "awaji",
+ "fukusaki",
+ "goshiki",
+ "harima",
+ "himeji",
+ "ichikawa",
+ "inagawa",
+ "itami",
+ "kakogawa",
+ "kamigori",
+ "kamikawa",
+ "kasai",
+ "kasuga",
+ "kawanishi",
+ "miki",
+ "minamiawaji",
+ "nishinomiya",
+ "nishiwaki",
+ "ono",
+ "sanda",
+ "sannan",
+ "sasayama",
+ "sayo",
+ "shingu",
+ "shinonsen",
+ "shiso",
+ "sumoto",
+ "taishi",
+ "taka",
+ "takarazuka",
+ "takasago",
+ "takino",
+ "tamba",
+ "tatsuno",
+ "toyooka",
+ "yabu",
+ "yashiro",
+ "yoka",
+ "yokawa",
+ "ami",
+ "asahi",
+ "bando",
+ "chikusei",
+ "daigo",
+ "fujishiro",
+ "hitachi",
+ "hitachinaka",
+ "hitachiomiya",
+ "hitachiota",
+ "ibaraki",
+ "ina",
+ "inashiki",
+ "itako",
+ "iwama",
+ "joso",
+ "kamisu",
+ "kasama",
+ "kashima",
+ "kasumigaura",
+ "koga",
+ "miho",
+ "mito",
+ "moriya",
+ "naka",
+ "namegata",
+ "oarai",
+ "ogawa",
+ "omitama",
+ "ryugasaki",
+ "sakai",
+ "sakuragawa",
+ "shimodate",
+ "shimotsuma",
+ "shirosato",
+ "sowa",
+ "suifu",
+ "takahagi",
+ "tamatsukuri",
+ "tokai",
+ "tomobe",
+ "tone",
+ "toride",
+ "tsuchiura",
+ "tsukuba",
+ "uchihara",
+ "ushiku",
+ "yachiyo",
+ "yamagata",
+ "yawara",
+ "yuki",
+ "anamizu",
+ "hakui",
+ "hakusan",
+ "kaga",
+ "kahoku",
+ "kanazawa",
+ "kawakita",
+ "komatsu",
+ "nakanoto",
+ "nanao",
+ "nomi",
+ "nonoichi",
+ "noto",
+ "shika",
+ "suzu",
+ "tsubata",
+ "tsurugi",
+ "uchinada",
+ "wajima",
+ "fudai",
+ "fujisawa",
+ "hanamaki",
+ "hiraizumi",
+ "hirono",
+ "ichinohe",
+ "ichinoseki",
+ "iwaizumi",
+ "iwate",
+ "joboji",
+ "kamaishi",
+ "kanegasaki",
+ "karumai",
+ "kawai",
+ "kitakami",
+ "kuji",
+ "kunohe",
+ "kuzumaki",
+ "miyako",
+ "mizusawa",
+ "morioka",
+ "ninohe",
+ "noda",
+ "ofunato",
+ "oshu",
+ "otsuchi",
+ "rikuzentakata",
+ "shiwa",
+ "shizukuishi",
+ "sumita",
+ "tanohata",
+ "tono",
+ "yahaba",
+ "yamada",
+ "ayagawa",
+ "higashikagawa",
+ "kanonji",
+ "kotohira",
+ "manno",
+ "marugame",
+ "mitoyo",
+ "naoshima",
+ "sanuki",
+ "tadotsu",
+ "takamatsu",
+ "tonosho",
+ "uchinomi",
+ "utazu",
+ "zentsuji",
+ "akune",
+ "amami",
+ "hioki",
+ "isa",
+ "isen",
+ "izumi",
+ "kagoshima",
+ "kanoya",
+ "kawanabe",
+ "kinko",
+ "kouyama",
+ "makurazaki",
+ "matsumoto",
+ "minamitane",
+ "nakatane",
+ "nishinoomote",
+ "satsumasendai",
+ "soo",
+ "tarumizu",
+ "yusui",
+ "aikawa",
+ "atsugi",
+ "ayase",
+ "chigasaki",
+ "ebina",
+ "fujisawa",
+ "hadano",
+ "hakone",
+ "hiratsuka",
+ "isehara",
+ "kaisei",
+ "kamakura",
+ "kiyokawa",
+ "matsuda",
+ "minamiashigara",
+ "miura",
+ "nakai",
+ "ninomiya",
+ "odawara",
+ "oi",
+ "oiso",
+ "sagamihara",
+ "samukawa",
+ "tsukui",
+ "yamakita",
+ "yamato",
+ "yokosuka",
+ "yugawara",
+ "zama",
+ "zushi",
+ "city",
+ "city",
+ "city",
+ "aki",
+ "geisei",
+ "hidaka",
+ "higashitsuno",
+ "ino",
+ "kagami",
+ "kami",
+ "kitagawa",
+ "kochi",
+ "mihara",
+ "motoyama",
+ "muroto",
+ "nahari",
+ "nakamura",
+ "nankoku",
+ "nishitosa",
+ "niyodogawa",
+ "ochi",
+ "okawa",
+ "otoyo",
+ "otsuki",
+ "sakawa",
+ "sukumo",
+ "susaki",
+ "tosa",
+ "tosashimizu",
+ "toyo",
+ "tsuno",
+ "umaji",
+ "yasuda",
+ "yusuhara",
+ "amakusa",
+ "arao",
+ "aso",
+ "choyo",
+ "gyokuto",
+ "hitoyoshi",
+ "kamiamakusa",
+ "kashima",
+ "kikuchi",
+ "kosa",
+ "kumamoto",
+ "mashiki",
+ "mifune",
+ "minamata",
+ "minamioguni",
+ "nagasu",
+ "nishihara",
+ "oguni",
+ "ozu",
+ "sumoto",
+ "takamori",
+ "uki",
+ "uto",
+ "yamaga",
+ "yamato",
+ "yatsushiro",
+ "ayabe",
+ "fukuchiyama",
+ "higashiyama",
+ "ide",
+ "ine",
+ "joyo",
+ "kameoka",
+ "kamo",
+ "kita",
+ "kizu",
+ "kumiyama",
+ "kyotamba",
+ "kyotanabe",
+ "kyotango",
+ "maizuru",
+ "minami",
+ "minamiyamashiro",
+ "miyazu",
+ "muko",
+ "nagaokakyo",
+ "nakagyo",
+ "nantan",
+ "oyamazaki",
+ "sakyo",
+ "seika",
+ "tanabe",
+ "uji",
+ "ujitawara",
+ "wazuka",
+ "yamashina",
+ "yawata",
+ "asahi",
+ "inabe",
+ "ise",
+ "kameyama",
+ "kawagoe",
+ "kiho",
+ "kisosaki",
+ "kiwa",
+ "komono",
+ "kumano",
+ "kuwana",
+ "matsusaka",
+ "meiwa",
+ "mihama",
+ "minamiise",
+ "misugi",
+ "miyama",
+ "nabari",
+ "shima",
+ "suzuka",
+ "tado",
+ "taiki",
+ "taki",
+ "tamaki",
+ "toba",
+ "tsu",
+ "udono",
+ "ureshino",
+ "watarai",
+ "yokkaichi",
+ "furukawa",
+ "higashimatsushima",
+ "ishinomaki",
+ "iwanuma",
+ "kakuda",
+ "kami",
+ "kawasaki",
+ "kesennuma",
+ "marumori",
+ "matsushima",
+ "minamisanriku",
+ "misato",
+ "murata",
+ "natori",
+ "ogawara",
+ "ohira",
+ "onagawa",
+ "osaki",
+ "rifu",
+ "semine",
+ "shibata",
+ "shichikashuku",
+ "shikama",
+ "shiogama",
+ "shiroishi",
+ "tagajo",
+ "taiwa",
+ "tome",
+ "tomiya",
+ "wakuya",
+ "watari",
+ "yamamoto",
+ "zao",
+ "aya",
+ "ebino",
+ "gokase",
+ "hyuga",
+ "kadogawa",
+ "kawaminami",
+ "kijo",
+ "kitagawa",
+ "kitakata",
+ "kitaura",
+ "kobayashi",
+ "kunitomi",
+ "kushima",
+ "mimata",
+ "miyakonojo",
+ "miyazaki",
+ "morotsuka",
+ "nichinan",
+ "nishimera",
+ "nobeoka",
+ "saito",
+ "shiiba",
+ "shintomi",
+ "takaharu",
+ "takanabe",
+ "takazaki",
+ "tsuno",
+ "achi",
+ "agematsu",
+ "anan",
+ "aoki",
+ "asahi",
+ "azumino",
+ "chikuhoku",
+ "chikuma",
+ "chino",
+ "fujimi",
+ "hakuba",
+ "hara",
+ "hiraya",
+ "iida",
+ "iijima",
+ "iiyama",
+ "iizuna",
+ "ikeda",
+ "ikusaka",
+ "ina",
+ "karuizawa",
+ "kawakami",
+ "kiso",
+ "kisofukushima",
+ "kitaaiki",
+ "komagane",
+ "komoro",
+ "matsukawa",
+ "matsumoto",
+ "miasa",
+ "minamiaiki",
+ "minamimaki",
+ "minamiminowa",
+ "minowa",
+ "miyada",
+ "miyota",
+ "mochizuki",
+ "nagano",
+ "nagawa",
+ "nagiso",
+ "nakagawa",
+ "nakano",
+ "nozawaonsen",
+ "obuse",
+ "ogawa",
+ "okaya",
+ "omachi",
+ "omi",
+ "ookuwa",
+ "ooshika",
+ "otaki",
+ "otari",
+ "sakae",
+ "sakaki",
+ "saku",
+ "sakuho",
+ "shimosuwa",
+ "shinanomachi",
+ "shiojiri",
+ "suwa",
+ "suzaka",
+ "takagi",
+ "takamori",
+ "takayama",
+ "tateshina",
+ "tatsuno",
+ "togakushi",
+ "togura",
+ "tomi",
+ "ueda",
+ "wada",
+ "yamagata",
+ "yamanouchi",
+ "yasaka",
+ "yasuoka",
+ "chijiwa",
+ "futsu",
+ "goto",
+ "hasami",
+ "hirado",
+ "iki",
+ "isahaya",
+ "kawatana",
+ "kuchinotsu",
+ "matsuura",
+ "nagasaki",
+ "obama",
+ "omura",
+ "oseto",
+ "saikai",
+ "sasebo",
+ "seihi",
+ "shimabara",
+ "shinkamigoto",
+ "togitsu",
+ "tsushima",
+ "unzen",
+ "city",
+ "ando",
+ "gose",
+ "heguri",
+ "higashiyoshino",
+ "ikaruga",
+ "ikoma",
+ "kamikitayama",
+ "kanmaki",
+ "kashiba",
+ "kashihara",
+ "katsuragi",
+ "kawai",
+ "kawakami",
+ "kawanishi",
+ "koryo",
+ "kurotaki",
+ "mitsue",
+ "miyake",
+ "nara",
+ "nosegawa",
+ "oji",
+ "ouda",
+ "oyodo",
+ "sakurai",
+ "sango",
+ "shimoichi",
+ "shimokitayama",
+ "shinjo",
+ "soni",
+ "takatori",
+ "tawaramoto",
+ "tenkawa",
+ "tenri",
+ "uda",
+ "yamatokoriyama",
+ "yamatotakada",
+ "yamazoe",
+ "yoshino",
+ "aga",
+ "agano",
+ "gosen",
+ "itoigawa",
+ "izumozaki",
+ "joetsu",
+ "kamo",
+ "kariwa",
+ "kashiwazaki",
+ "minamiuonuma",
+ "mitsuke",
+ "muika",
+ "murakami",
+ "myoko",
+ "nagaoka",
+ "niigata",
+ "ojiya",
+ "omi",
+ "sado",
+ "sanjo",
+ "seiro",
+ "seirou",
+ "sekikawa",
+ "shibata",
+ "tagami",
+ "tainai",
+ "tochio",
+ "tokamachi",
+ "tsubame",
+ "tsunan",
+ "uonuma",
+ "yahiko",
+ "yoita",
+ "yuzawa",
+ "beppu",
+ "bungoono",
+ "bungotakada",
+ "hasama",
+ "hiji",
+ "himeshima",
+ "hita",
+ "kamitsue",
+ "kokonoe",
+ "kuju",
+ "kunisaki",
+ "kusu",
+ "oita",
+ "saiki",
+ "taketa",
+ "tsukumi",
+ "usa",
+ "usuki",
+ "yufu",
+ "akaiwa",
+ "asakuchi",
+ "bizen",
+ "hayashima",
+ "ibara",
+ "kagamino",
+ "kasaoka",
+ "kibichuo",
+ "kumenan",
+ "kurashiki",
+ "maniwa",
+ "misaki",
+ "nagi",
+ "niimi",
+ "nishiawakura",
+ "okayama",
+ "satosho",
+ "setouchi",
+ "shinjo",
+ "shoo",
+ "soja",
+ "takahashi",
+ "tamano",
+ "tsuyama",
+ "wake",
+ "yakage",
+ "aguni",
+ "ginowan",
+ "ginoza",
+ "gushikami",
+ "haebaru",
+ "higashi",
+ "hirara",
+ "iheya",
+ "ishigaki",
+ "ishikawa",
+ "itoman",
+ "izena",
+ "kadena",
+ "kin",
+ "kitadaito",
+ "kitanakagusuku",
+ "kumejima",
+ "kunigami",
+ "minamidaito",
+ "motobu",
+ "nago",
+ "naha",
+ "nakagusuku",
+ "nakijin",
+ "nanjo",
+ "nishihara",
+ "ogimi",
+ "okinawa",
+ "onna",
+ "shimoji",
+ "taketomi",
+ "tarama",
+ "tokashiki",
+ "tomigusuku",
+ "tonaki",
+ "urasoe",
+ "uruma",
+ "yaese",
+ "yomitan",
+ "yonabaru",
+ "yonaguni",
+ "zamami",
+ "abeno",
+ "chihayaakasaka",
+ "chuo",
+ "daito",
+ "fujiidera",
+ "habikino",
+ "hannan",
+ "higashiosaka",
+ "higashisumiyoshi",
+ "higashiyodogawa",
+ "hirakata",
+ "ibaraki",
+ "ikeda",
+ "izumi",
+ "izumiotsu",
+ "izumisano",
+ "kadoma",
+ "kaizuka",
+ "kanan",
+ "kashiwara",
+ "katano",
+ "kawachinagano",
+ "kishiwada",
+ "kita",
+ "kumatori",
+ "matsubara",
+ "minato",
+ "minoh",
+ "misaki",
+ "moriguchi",
+ "neyagawa",
+ "nishi",
+ "nose",
+ "osakasayama",
+ "sakai",
+ "sayama",
+ "sennan",
+ "settsu",
+ "shijonawate",
+ "shimamoto",
+ "suita",
+ "tadaoka",
+ "taishi",
+ "tajiri",
+ "takaishi",
+ "takatsuki",
+ "tondabayashi",
+ "toyonaka",
+ "toyono",
+ "yao",
+ "ariake",
+ "arita",
+ "fukudomi",
+ "genkai",
+ "hamatama",
+ "hizen",
+ "imari",
+ "kamimine",
+ "kanzaki",
+ "karatsu",
+ "kashima",
+ "kitagata",
+ "kitahata",
+ "kiyama",
+ "kouhoku",
+ "kyuragi",
+ "nishiarita",
+ "ogi",
+ "omachi",
+ "ouchi",
+ "saga",
+ "shiroishi",
+ "taku",
+ "tara",
+ "tosu",
+ "yoshinogari",
+ "arakawa",
+ "asaka",
+ "chichibu",
+ "fujimi",
+ "fujimino",
+ "fukaya",
+ "hanno",
+ "hanyu",
+ "hasuda",
+ "hatogaya",
+ "hatoyama",
+ "hidaka",
+ "higashichichibu",
+ "higashimatsuyama",
+ "honjo",
+ "ina",
+ "iruma",
+ "iwatsuki",
+ "kamiizumi",
+ "kamikawa",
+ "kamisato",
+ "kasukabe",
+ "kawagoe",
+ "kawaguchi",
+ "kawajima",
+ "kazo",
+ "kitamoto",
+ "koshigaya",
+ "kounosu",
+ "kuki",
+ "kumagaya",
+ "matsubushi",
+ "minano",
+ "misato",
+ "miyashiro",
+ "miyoshi",
+ "moroyama",
+ "nagatoro",
+ "namegawa",
+ "niiza",
+ "ogano",
+ "ogawa",
+ "ogose",
+ "okegawa",
+ "omiya",
+ "otaki",
+ "ranzan",
+ "ryokami",
+ "saitama",
+ "sakado",
+ "satte",
+ "sayama",
+ "shiki",
+ "shiraoka",
+ "soka",
+ "sugito",
+ "toda",
+ "tokigawa",
+ "tokorozawa",
+ "tsurugashima",
+ "urawa",
+ "warabi",
+ "yashio",
+ "yokoze",
+ "yono",
+ "yorii",
+ "yoshida",
+ "yoshikawa",
+ "yoshimi",
+ "city",
+ "city",
+ "aisho",
+ "gamo",
+ "higashiomi",
+ "hikone",
+ "koka",
+ "konan",
+ "kosei",
+ "koto",
+ "kusatsu",
+ "maibara",
+ "moriyama",
+ "nagahama",
+ "nishiazai",
+ "notogawa",
+ "omihachiman",
+ "otsu",
+ "ritto",
+ "ryuoh",
+ "takashima",
+ "takatsuki",
+ "torahime",
+ "toyosato",
+ "yasu",
+ "akagi",
+ "ama",
+ "gotsu",
+ "hamada",
+ "higashiizumo",
+ "hikawa",
+ "hikimi",
+ "izumo",
+ "kakinoki",
+ "masuda",
+ "matsue",
+ "misato",
+ "nishinoshima",
+ "ohda",
+ "okinoshima",
+ "okuizumo",
+ "shimane",
+ "tamayu",
+ "tsuwano",
+ "unnan",
+ "yakumo",
+ "yasugi",
+ "yatsuka",
+ "arai",
+ "atami",
+ "fuji",
+ "fujieda",
+ "fujikawa",
+ "fujinomiya",
+ "fukuroi",
+ "gotemba",
+ "haibara",
+ "hamamatsu",
+ "higashiizu",
+ "ito",
+ "iwata",
+ "izu",
+ "izunokuni",
+ "kakegawa",
+ "kannami",
+ "kawanehon",
+ "kawazu",
+ "kikugawa",
+ "kosai",
+ "makinohara",
+ "matsuzaki",
+ "minamiizu",
+ "mishima",
+ "morimachi",
+ "nishiizu",
+ "numazu",
+ "omaezaki",
+ "shimada",
+ "shimizu",
+ "shimoda",
+ "shizuoka",
+ "susono",
+ "yaizu",
+ "yoshida",
+ "ashikaga",
+ "bato",
+ "haga",
+ "ichikai",
+ "iwafune",
+ "kaminokawa",
+ "kanuma",
+ "karasuyama",
+ "kuroiso",
+ "mashiko",
+ "mibu",
+ "moka",
+ "motegi",
+ "nasu",
+ "nasushiobara",
+ "nikko",
+ "nishikata",
+ "nogi",
+ "ohira",
+ "ohtawara",
+ "oyama",
+ "sakura",
+ "sano",
+ "shimotsuke",
+ "shioya",
+ "takanezawa",
+ "tochigi",
+ "tsuga",
+ "ujiie",
+ "utsunomiya",
+ "yaita",
+ "aizumi",
+ "anan",
+ "ichiba",
+ "itano",
+ "kainan",
+ "komatsushima",
+ "matsushige",
+ "mima",
+ "minami",
+ "miyoshi",
+ "mugi",
+ "nakagawa",
+ "naruto",
+ "sanagochi",
+ "shishikui",
+ "tokushima",
+ "wajiki",
+ "adachi",
+ "akiruno",
+ "akishima",
+ "aogashima",
+ "arakawa",
+ "bunkyo",
+ "chiyoda",
+ "chofu",
+ "chuo",
+ "edogawa",
+ "fuchu",
+ "fussa",
+ "hachijo",
+ "hachioji",
+ "hamura",
+ "higashikurume",
+ "higashimurayama",
+ "higashiyamato",
+ "hino",
+ "hinode",
+ "hinohara",
+ "inagi",
+ "itabashi",
+ "katsushika",
+ "kita",
+ "kiyose",
+ "kodaira",
+ "koganei",
+ "kokubunji",
+ "komae",
+ "koto",
+ "kouzushima",
+ "kunitachi",
+ "machida",
+ "meguro",
+ "minato",
+ "mitaka",
+ "mizuho",
+ "musashimurayama",
+ "musashino",
+ "nakano",
+ "nerima",
+ "ogasawara",
+ "okutama",
+ "ome",
+ "oshima",
+ "ota",
+ "setagaya",
+ "shibuya",
+ "shinagawa",
+ "shinjuku",
+ "suginami",
+ "sumida",
+ "tachikawa",
+ "taito",
+ "tama",
+ "toshima",
+ "chizu",
+ "hino",
+ "kawahara",
+ "koge",
+ "kotoura",
+ "misasa",
+ "nanbu",
+ "nichinan",
+ "sakaiminato",
+ "tottori",
+ "wakasa",
+ "yazu",
+ "yonago",
+ "asahi",
+ "fuchu",
+ "fukumitsu",
+ "funahashi",
+ "himi",
+ "imizu",
+ "inami",
+ "johana",
+ "kamiichi",
+ "kurobe",
+ "nakaniikawa",
+ "namerikawa",
+ "nanto",
+ "nyuzen",
+ "oyabe",
+ "taira",
+ "takaoka",
+ "tateyama",
+ "toga",
+ "tonami",
+ "toyama",
+ "unazuki",
+ "uozu",
+ "yamada",
+ "arida",
+ "aridagawa",
+ "gobo",
+ "hashimoto",
+ "hidaka",
+ "hirogawa",
+ "inami",
+ "iwade",
+ "kainan",
+ "kamitonda",
+ "katsuragi",
+ "kimino",
+ "kinokawa",
+ "kitayama",
+ "koya",
+ "koza",
+ "kozagawa",
+ "kudoyama",
+ "kushimoto",
+ "mihama",
+ "misato",
+ "nachikatsuura",
+ "shingu",
+ "shirahama",
+ "taiji",
+ "tanabe",
+ "wakayama",
+ "yuasa",
+ "yura",
+ "asahi",
+ "funagata",
+ "higashine",
+ "iide",
+ "kahoku",
+ "kaminoyama",
+ "kaneyama",
+ "kawanishi",
+ "mamurogawa",
+ "mikawa",
+ "murayama",
+ "nagai",
+ "nakayama",
+ "nanyo",
+ "nishikawa",
+ "obanazawa",
+ "oe",
+ "oguni",
+ "ohkura",
+ "oishida",
+ "sagae",
+ "sakata",
+ "sakegawa",
+ "shinjo",
+ "shirataka",
+ "shonai",
+ "takahata",
+ "tendo",
+ "tozawa",
+ "tsuruoka",
+ "yamagata",
+ "yamanobe",
+ "yonezawa",
+ "yuza",
+ "abu",
+ "hagi",
+ "hikari",
+ "hofu",
+ "iwakuni",
+ "kudamatsu",
+ "mitou",
+ "nagato",
+ "oshima",
+ "shimonoseki",
+ "shunan",
+ "tabuse",
+ "tokuyama",
+ "toyota",
+ "ube",
+ "yuu",
+ "chuo",
+ "doshi",
+ "fuefuki",
+ "fujikawa",
+ "fujikawaguchiko",
+ "fujiyoshida",
+ "hayakawa",
+ "hokuto",
+ "ichikawamisato",
+ "kai",
+ "kofu",
+ "koshu",
+ "kosuge",
+ "minami-alps",
+ "minobu",
+ "nakamichi",
+ "nanbu",
+ "narusawa",
+ "nirasaki",
+ "nishikatsura",
+ "oshino",
+ "otsuki",
+ "showa",
+ "tabayama",
+ "tsuru",
+ "uenohara",
+ "yamanakako",
+ "yamanashi",
+ "city",
+ "co",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "biz",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "net",
+ "org",
+ "ass",
+ "asso",
+ "com",
+ "coop",
+ "edu",
+ "gouv",
+ "gov",
+ "medecin",
+ "mil",
+ "nom",
+ "notaires",
+ "org",
+ "pharmaciens",
+ "prd",
+ "presse",
+ "tm",
+ "veterinaire",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "org",
+ "rep",
+ "tra",
+ "ac",
+ "blogspot",
+ "busan",
+ "chungbuk",
+ "chungnam",
+ "co",
+ "daegu",
+ "daejeon",
+ "es",
+ "gangwon",
+ "go",
+ "gwangju",
+ "gyeongbuk",
+ "gyeonggi",
+ "gyeongnam",
+ "hs",
+ "incheon",
+ "jeju",
+ "jeonbuk",
+ "jeonnam",
+ "kg",
+ "mil",
+ "ms",
+ "ne",
+ "or",
+ "pe",
+ "re",
+ "sc",
+ "seoul",
+ "ulsan",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "c",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "int",
+ "net",
+ "org",
+ "per",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "co",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "blogspot",
+ "ac",
+ "assn",
+ "com",
+ "edu",
+ "gov",
+ "grp",
+ "hotel",
+ "int",
+ "ltd",
+ "net",
+ "ngo",
+ "org",
+ "sch",
+ "soc",
+ "web",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "co",
+ "org",
+ "blogspot",
+ "gov",
+ "blogspot",
+ "asn",
+ "com",
+ "conf",
+ "edu",
+ "gov",
+ "id",
+ "mil",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "id",
+ "med",
+ "net",
+ "org",
+ "plc",
+ "sch",
+ "ac",
+ "co",
+ "gov",
+ "net",
+ "org",
+ "press",
+ "asso",
+ "tm",
+ "blogspot",
+ "ac",
+ "co",
+ "edu",
+ "gov",
+ "its",
+ "net",
+ "org",
+ "priv",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "nom",
+ "org",
+ "prd",
+ "tm",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "inf",
+ "name",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gouv",
+ "gov",
+ "net",
+ "org",
+ "presse",
+ "edu",
+ "gov",
+ "nyc",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "blogspot",
+ "gov",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "net",
+ "org",
+ "blogspot",
+ "ac",
+ "co",
+ "com",
+ "gov",
+ "net",
+ "or",
+ "org",
+ "academy",
+ "agriculture",
+ "air",
+ "airguard",
+ "alabama",
+ "alaska",
+ "amber",
+ "ambulance",
+ "american",
+ "americana",
+ "americanantiques",
+ "americanart",
+ "amsterdam",
+ "and",
+ "annefrank",
+ "anthro",
+ "anthropology",
+ "antiques",
+ "aquarium",
+ "arboretum",
+ "archaeological",
+ "archaeology",
+ "architecture",
+ "art",
+ "artanddesign",
+ "artcenter",
+ "artdeco",
+ "arteducation",
+ "artgallery",
+ "arts",
+ "artsandcrafts",
+ "asmatart",
+ "assassination",
+ "assisi",
+ "association",
+ "astronomy",
+ "atlanta",
+ "austin",
+ "australia",
+ "automotive",
+ "aviation",
+ "axis",
+ "badajoz",
+ "baghdad",
+ "bahn",
+ "bale",
+ "baltimore",
+ "barcelona",
+ "baseball",
+ "basel",
+ "baths",
+ "bauern",
+ "beauxarts",
+ "beeldengeluid",
+ "bellevue",
+ "bergbau",
+ "berkeley",
+ "berlin",
+ "bern",
+ "bible",
+ "bilbao",
+ "bill",
+ "birdart",
+ "birthplace",
+ "bonn",
+ "boston",
+ "botanical",
+ "botanicalgarden",
+ "botanicgarden",
+ "botany",
+ "brandywinevalley",
+ "brasil",
+ "bristol",
+ "british",
+ "britishcolumbia",
+ "broadcast",
+ "brunel",
+ "brussel",
+ "brussels",
+ "bruxelles",
+ "building",
+ "burghof",
+ "bus",
+ "bushey",
+ "cadaques",
+ "california",
+ "cambridge",
+ "can",
+ "canada",
+ "capebreton",
+ "carrier",
+ "cartoonart",
+ "casadelamoneda",
+ "castle",
+ "castres",
+ "celtic",
+ "center",
+ "chattanooga",
+ "cheltenham",
+ "chesapeakebay",
+ "chicago",
+ "children",
+ "childrens",
+ "childrensgarden",
+ "chiropractic",
+ "chocolate",
+ "christiansburg",
+ "cincinnati",
+ "cinema",
+ "circus",
+ "civilisation",
+ "civilization",
+ "civilwar",
+ "clinton",
+ "clock",
+ "coal",
+ "coastaldefence",
+ "cody",
+ "coldwar",
+ "collection",
+ "colonialwilliamsburg",
+ "coloradoplateau",
+ "columbia",
+ "columbus",
+ "communication",
+ "communications",
+ "community",
+ "computer",
+ "computerhistory",
+ "contemporary",
+ "contemporaryart",
+ "convent",
+ "copenhagen",
+ "corporation",
+ "corvette",
+ "costume",
+ "countryestate",
+ "county",
+ "crafts",
+ "cranbrook",
+ "creation",
+ "cultural",
+ "culturalcenter",
+ "culture",
+ "cyber",
+ "cymru",
+ "dali",
+ "dallas",
+ "database",
+ "ddr",
+ "decorativearts",
+ "delaware",
+ "delmenhorst",
+ "denmark",
+ "depot",
+ "design",
+ "detroit",
+ "dinosaur",
+ "discovery",
+ "dolls",
+ "donostia",
+ "durham",
+ "eastafrica",
+ "eastcoast",
+ "education",
+ "educational",
+ "egyptian",
+ "eisenbahn",
+ "elburg",
+ "elvendrell",
+ "embroidery",
+ "encyclopedic",
+ "england",
+ "entomology",
+ "environment",
+ "environmentalconservation",
+ "epilepsy",
+ "essex",
+ "estate",
+ "ethnology",
+ "exeter",
+ "exhibition",
+ "family",
+ "farm",
+ "farmequipment",
+ "farmers",
+ "farmstead",
+ "field",
+ "figueres",
+ "filatelia",
+ "film",
+ "fineart",
+ "finearts",
+ "finland",
+ "flanders",
+ "florida",
+ "force",
+ "fortmissoula",
+ "fortworth",
+ "foundation",
+ "francaise",
+ "frankfurt",
+ "franziskaner",
+ "freemasonry",
+ "freiburg",
+ "fribourg",
+ "frog",
+ "fundacio",
+ "furniture",
+ "gallery",
+ "garden",
+ "gateway",
+ "geelvinck",
+ "gemological",
+ "geology",
+ "georgia",
+ "giessen",
+ "glas",
+ "glass",
+ "gorge",
+ "grandrapids",
+ "graz",
+ "guernsey",
+ "halloffame",
+ "hamburg",
+ "handson",
+ "harvestcelebration",
+ "hawaii",
+ "health",
+ "heimatunduhren",
+ "hellas",
+ "helsinki",
+ "hembygdsforbund",
+ "heritage",
+ "histoire",
+ "historical",
+ "historicalsociety",
+ "historichouses",
+ "historisch",
+ "historisches",
+ "history",
+ "historyofscience",
+ "horology",
+ "house",
+ "humanities",
+ "illustration",
+ "imageandsound",
+ "indian",
+ "indiana",
+ "indianapolis",
+ "indianmarket",
+ "intelligence",
+ "interactive",
+ "iraq",
+ "iron",
+ "isleofman",
+ "jamison",
+ "jefferson",
+ "jerusalem",
+ "jewelry",
+ "jewish",
+ "jewishart",
+ "jfk",
+ "journalism",
+ "judaica",
+ "judygarland",
+ "juedisches",
+ "juif",
+ "karate",
+ "karikatur",
+ "kids",
+ "koebenhavn",
+ "koeln",
+ "kunst",
+ "kunstsammlung",
+ "kunstunddesign",
+ "labor",
+ "labour",
+ "lajolla",
+ "lancashire",
+ "landes",
+ "lans",
+ "larsson",
+ "lewismiller",
+ "lincoln",
+ "linz",
+ "living",
+ "livinghistory",
+ "localhistory",
+ "london",
+ "losangeles",
+ "louvre",
+ "loyalist",
+ "lucerne",
+ "luxembourg",
+ "luzern",
+ "mad",
+ "madrid",
+ "mallorca",
+ "manchester",
+ "mansion",
+ "mansions",
+ "manx",
+ "marburg",
+ "maritime",
+ "maritimo",
+ "maryland",
+ "marylhurst",
+ "media",
+ "medical",
+ "medizinhistorisches",
+ "meeres",
+ "memorial",
+ "mesaverde",
+ "michigan",
+ "midatlantic",
+ "military",
+ "mill",
+ "miners",
+ "mining",
+ "minnesota",
+ "missile",
+ "missoula",
+ "modern",
+ "moma",
+ "money",
+ "monmouth",
+ "monticello",
+ "montreal",
+ "moscow",
+ "motorcycle",
+ "muenchen",
+ "muenster",
+ "mulhouse",
+ "muncie",
+ "museet",
+ "museumcenter",
+ "museumvereniging",
+ "music",
+ "national",
+ "nationalfirearms",
+ "nationalheritage",
+ "nativeamerican",
+ "naturalhistory",
+ "naturalhistorymuseum",
+ "naturalsciences",
+ "nature",
+ "naturhistorisches",
+ "natuurwetenschappen",
+ "naumburg",
+ "naval",
+ "nebraska",
+ "neues",
+ "newhampshire",
+ "newjersey",
+ "newmexico",
+ "newport",
+ "newspaper",
+ "newyork",
+ "niepce",
+ "norfolk",
+ "north",
+ "nrw",
+ "nuernberg",
+ "nuremberg",
+ "nyc",
+ "nyny",
+ "oceanographic",
+ "oceanographique",
+ "omaha",
+ "online",
+ "ontario",
+ "openair",
+ "oregon",
+ "oregontrail",
+ "otago",
+ "oxford",
+ "pacific",
+ "paderborn",
+ "palace",
+ "paleo",
+ "palmsprings",
+ "panama",
+ "paris",
+ "pasadena",
+ "pharmacy",
+ "philadelphia",
+ "philadelphiaarea",
+ "philately",
+ "phoenix",
+ "photography",
+ "pilots",
+ "pittsburgh",
+ "planetarium",
+ "plantation",
+ "plants",
+ "plaza",
+ "portal",
+ "portland",
+ "portlligat",
+ "posts-and-telecommunications",
+ "preservation",
+ "presidio",
+ "press",
+ "project",
+ "public",
+ "pubol",
+ "quebec",
+ "railroad",
+ "railway",
+ "research",
+ "resistance",
+ "riodejaneiro",
+ "rochester",
+ "rockart",
+ "roma",
+ "russia",
+ "saintlouis",
+ "salem",
+ "salvadordali",
+ "salzburg",
+ "sandiego",
+ "sanfrancisco",
+ "santabarbara",
+ "santacruz",
+ "santafe",
+ "saskatchewan",
+ "satx",
+ "savannahga",
+ "schlesisches",
+ "schoenbrunn",
+ "schokoladen",
+ "school",
+ "schweiz",
+ "science",
+ "science-fiction",
+ "scienceandhistory",
+ "scienceandindustry",
+ "sciencecenter",
+ "sciencecenters",
+ "sciencehistory",
+ "sciences",
+ "sciencesnaturelles",
+ "scotland",
+ "seaport",
+ "settlement",
+ "settlers",
+ "shell",
+ "sherbrooke",
+ "sibenik",
+ "silk",
+ "ski",
+ "skole",
+ "society",
+ "sologne",
+ "soundandvision",
+ "southcarolina",
+ "southwest",
+ "space",
+ "spy",
+ "square",
+ "stadt",
+ "stalbans",
+ "starnberg",
+ "state",
+ "stateofdelaware",
+ "station",
+ "steam",
+ "steiermark",
+ "stjohn",
+ "stockholm",
+ "stpetersburg",
+ "stuttgart",
+ "suisse",
+ "surgeonshall",
+ "surrey",
+ "svizzera",
+ "sweden",
+ "sydney",
+ "tank",
+ "tcm",
+ "technology",
+ "telekommunikation",
+ "television",
+ "texas",
+ "textile",
+ "theater",
+ "time",
+ "timekeeping",
+ "topology",
+ "torino",
+ "touch",
+ "town",
+ "transport",
+ "tree",
+ "trolley",
+ "trust",
+ "trustee",
+ "uhren",
+ "ulm",
+ "undersea",
+ "university",
+ "usa",
+ "usantiques",
+ "usarts",
+ "uscountryestate",
+ "usculture",
+ "usdecorativearts",
+ "usgarden",
+ "ushistory",
+ "ushuaia",
+ "uslivinghistory",
+ "utah",
+ "uvic",
+ "valley",
+ "vantaa",
+ "versailles",
+ "viking",
+ "village",
+ "virginia",
+ "virtual",
+ "virtuel",
+ "vlaanderen",
+ "volkenkunde",
+ "wales",
+ "wallonie",
+ "war",
+ "washingtondc",
+ "watch-and-clock",
+ "watchandclock",
+ "western",
+ "westfalen",
+ "whaling",
+ "wildlife",
+ "williamsburg",
+ "windmill",
+ "workshop",
+ "xn--9dbhblg6di",
+ "xn--comunicaes-v6a2o",
+ "xn--correios-e-telecomunicaes-ghc29a",
+ "xn--h1aegh",
+ "xn--lns-qla",
+ "york",
+ "yorkshire",
+ "yosemite",
+ "youth",
+ "zoological",
+ "zoology",
+ "aero",
+ "biz",
+ "com",
+ "coop",
+ "edu",
+ "gov",
+ "info",
+ "int",
+ "mil",
+ "museum",
+ "name",
+ "net",
+ "org",
+ "pro",
+ "ac",
+ "biz",
+ "co",
+ "com",
+ "coop",
+ "edu",
+ "gov",
+ "int",
+ "museum",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "gob",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "name",
+ "net",
+ "org",
+ "teledata",
+ "ca",
+ "cc",
+ "co",
+ "com",
+ "dr",
+ "in",
+ "info",
+ "mobi",
+ "mx",
+ "name",
+ "or",
+ "org",
+ "pro",
+ "school",
+ "tv",
+ "us",
+ "ws",
+ "her",
+ "his",
+ "forgot",
+ "forgot",
+ "asso",
+ "at-band-camp",
+ "azure-mobile",
+ "azurewebsites",
+ "blogdns",
+ "broke-it",
+ "buyshouses",
+ "cdn77",
+ "cdn77-ssl",
+ "cloudapp",
+ "cloudfront",
+ "dnsalias",
+ "dnsdojo",
+ "does-it",
+ "dontexist",
+ "dynalias",
+ "dynathome",
+ "endofinternet",
+ "fastly",
+ "from-az",
+ "from-co",
+ "from-la",
+ "from-ny",
+ "gb",
+ "gets-it",
+ "ham-radio-op",
+ "homeftp",
+ "homeip",
+ "homelinux",
+ "homeunix",
+ "hu",
+ "in",
+ "in-the-band",
+ "is-a-chef",
+ "is-a-geek",
+ "isa-geek",
+ "jp",
+ "kicks-ass",
+ "office-on-the",
+ "podzone",
+ "scrapper-site",
+ "se",
+ "selfip",
+ "sells-it",
+ "servebbs",
+ "serveftp",
+ "thruhere",
+ "uk",
+ "webhop",
+ "za",
+ "r",
+ "prod",
+ "ssl",
+ "a",
+ "global",
+ "a",
+ "b",
+ "global",
+ "arts",
+ "com",
+ "firm",
+ "info",
+ "net",
+ "other",
+ "per",
+ "rec",
+ "store",
+ "web",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "mobi",
+ "name",
+ "net",
+ "org",
+ "sch",
+ "blogspot",
+ "blogspot",
+ "bv",
+ "co",
+ "aa",
+ "aarborte",
+ "aejrie",
+ "afjord",
+ "agdenes",
+ "ah",
+ "akershus",
+ "aknoluokta",
+ "akrehamn",
+ "al",
+ "alaheadju",
+ "alesund",
+ "algard",
+ "alstahaug",
+ "alta",
+ "alvdal",
+ "amli",
+ "amot",
+ "andasuolo",
+ "andebu",
+ "andoy",
+ "ardal",
+ "aremark",
+ "arendal",
+ "arna",
+ "aseral",
+ "asker",
+ "askim",
+ "askoy",
+ "askvoll",
+ "asnes",
+ "audnedaln",
+ "aukra",
+ "aure",
+ "aurland",
+ "aurskog-holand",
+ "austevoll",
+ "austrheim",
+ "averoy",
+ "badaddja",
+ "bahcavuotna",
+ "bahccavuotna",
+ "baidar",
+ "bajddar",
+ "balat",
+ "balestrand",
+ "ballangen",
+ "balsfjord",
+ "bamble",
+ "bardu",
+ "barum",
+ "batsfjord",
+ "bearalvahki",
+ "beardu",
+ "beiarn",
+ "berg",
+ "bergen",
+ "berlevag",
+ "bievat",
+ "bindal",
+ "birkenes",
+ "bjarkoy",
+ "bjerkreim",
+ "bjugn",
+ "blogspot",
+ "bodo",
+ "bokn",
+ "bomlo",
+ "bremanger",
+ "bronnoy",
+ "bronnoysund",
+ "brumunddal",
+ "bryne",
+ "bu",
+ "budejju",
+ "buskerud",
+ "bygland",
+ "bykle",
+ "cahcesuolo",
+ "co",
+ "davvenjarga",
+ "davvesiida",
+ "deatnu",
+ "dep",
+ "dielddanuorri",
+ "divtasvuodna",
+ "divttasvuotna",
+ "donna",
+ "dovre",
+ "drammen",
+ "drangedal",
+ "drobak",
+ "dyroy",
+ "egersund",
+ "eid",
+ "eidfjord",
+ "eidsberg",
+ "eidskog",
+ "eidsvoll",
+ "eigersund",
+ "elverum",
+ "enebakk",
+ "engerdal",
+ "etne",
+ "etnedal",
+ "evenassi",
+ "evenes",
+ "evje-og-hornnes",
+ "farsund",
+ "fauske",
+ "fedje",
+ "fet",
+ "fetsund",
+ "fhs",
+ "finnoy",
+ "fitjar",
+ "fjaler",
+ "fjell",
+ "fla",
+ "flakstad",
+ "flatanger",
+ "flekkefjord",
+ "flesberg",
+ "flora",
+ "floro",
+ "fm",
+ "folkebibl",
+ "folldal",
+ "forde",
+ "forsand",
+ "fosnes",
+ "frana",
+ "fredrikstad",
+ "frei",
+ "frogn",
+ "froland",
+ "frosta",
+ "froya",
+ "fuoisku",
+ "fuossko",
+ "fusa",
+ "fylkesbibl",
+ "fyresdal",
+ "gaivuotna",
+ "galsa",
+ "gamvik",
+ "gangaviika",
+ "gaular",
+ "gausdal",
+ "giehtavuoatna",
+ "gildeskal",
+ "giske",
+ "gjemnes",
+ "gjerdrum",
+ "gjerstad",
+ "gjesdal",
+ "gjovik",
+ "gloppen",
+ "gol",
+ "gran",
+ "grane",
+ "granvin",
+ "gratangen",
+ "grimstad",
+ "grong",
+ "grue",
+ "gulen",
+ "guovdageaidnu",
+ "ha",
+ "habmer",
+ "hadsel",
+ "hagebostad",
+ "halden",
+ "halsa",
+ "hamar",
+ "hamaroy",
+ "hammarfeasta",
+ "hammerfest",
+ "hapmir",
+ "haram",
+ "hareid",
+ "harstad",
+ "hasvik",
+ "hattfjelldal",
+ "haugesund",
+ "hedmark",
+ "hemne",
+ "hemnes",
+ "hemsedal",
+ "herad",
+ "hitra",
+ "hjartdal",
+ "hjelmeland",
+ "hl",
+ "hm",
+ "hobol",
+ "hof",
+ "hokksund",
+ "hol",
+ "hole",
+ "holmestrand",
+ "holtalen",
+ "honefoss",
+ "hordaland",
+ "hornindal",
+ "horten",
+ "hoyanger",
+ "hoylandet",
+ "hurdal",
+ "hurum",
+ "hvaler",
+ "hyllestad",
+ "ibestad",
+ "idrett",
+ "inderoy",
+ "iveland",
+ "ivgu",
+ "jan-mayen",
+ "jessheim",
+ "jevnaker",
+ "jolster",
+ "jondal",
+ "jorpeland",
+ "kafjord",
+ "karasjohka",
+ "karasjok",
+ "karlsoy",
+ "karmoy",
+ "kautokeino",
+ "kirkenes",
+ "klabu",
+ "klepp",
+ "kommune",
+ "kongsberg",
+ "kongsvinger",
+ "kopervik",
+ "kraanghke",
+ "kragero",
+ "kristiansand",
+ "kristiansund",
+ "krodsherad",
+ "krokstadelva",
+ "kvafjord",
+ "kvalsund",
+ "kvam",
+ "kvanangen",
+ "kvinesdal",
+ "kvinnherad",
+ "kviteseid",
+ "kvitsoy",
+ "laakesvuemie",
+ "lahppi",
+ "langevag",
+ "lardal",
+ "larvik",
+ "lavagis",
+ "lavangen",
+ "leangaviika",
+ "lebesby",
+ "leikanger",
+ "leirfjord",
+ "leirvik",
+ "leka",
+ "leksvik",
+ "lenvik",
+ "lerdal",
+ "lesja",
+ "levanger",
+ "lier",
+ "lierne",
+ "lillehammer",
+ "lillesand",
+ "lindas",
+ "lindesnes",
+ "loabat",
+ "lodingen",
+ "lom",
+ "loppa",
+ "lorenskog",
+ "loten",
+ "lund",
+ "lunner",
+ "luroy",
+ "luster",
+ "lyngdal",
+ "lyngen",
+ "malatvuopmi",
+ "malselv",
+ "malvik",
+ "mandal",
+ "marker",
+ "marnardal",
+ "masfjorden",
+ "masoy",
+ "matta-varjjat",
+ "meland",
+ "meldal",
+ "melhus",
+ "meloy",
+ "meraker",
+ "midsund",
+ "midtre-gauldal",
+ "mil",
+ "mjondalen",
+ "mo-i-rana",
+ "moareke",
+ "modalen",
+ "modum",
+ "molde",
+ "more-og-romsdal",
+ "mosjoen",
+ "moskenes",
+ "moss",
+ "mosvik",
+ "mr",
+ "muosat",
+ "museum",
+ "naamesjevuemie",
+ "namdalseid",
+ "namsos",
+ "namsskogan",
+ "nannestad",
+ "naroy",
+ "narviika",
+ "narvik",
+ "naustdal",
+ "navuotna",
+ "nedre-eiker",
+ "nesna",
+ "nesodden",
+ "nesoddtangen",
+ "nesseby",
+ "nesset",
+ "nissedal",
+ "nittedal",
+ "nl",
+ "nord-aurdal",
+ "nord-fron",
+ "nord-odal",
+ "norddal",
+ "nordkapp",
+ "nordland",
+ "nordre-land",
+ "nordreisa",
+ "nore-og-uvdal",
+ "notodden",
+ "notteroy",
+ "nt",
+ "odda",
+ "of",
+ "oksnes",
+ "ol",
+ "omasvuotna",
+ "oppdal",
+ "oppegard",
+ "orkanger",
+ "orkdal",
+ "orland",
+ "orskog",
+ "orsta",
+ "osen",
+ "oslo",
+ "osoyro",
+ "osteroy",
+ "ostfold",
+ "ostre-toten",
+ "overhalla",
+ "ovre-eiker",
+ "oyer",
+ "oygarden",
+ "oystre-slidre",
+ "porsanger",
+ "porsangu",
+ "porsgrunn",
+ "priv",
+ "rade",
+ "radoy",
+ "rahkkeravju",
+ "raholt",
+ "raisa",
+ "rakkestad",
+ "ralingen",
+ "rana",
+ "randaberg",
+ "rauma",
+ "rendalen",
+ "rennebu",
+ "rennesoy",
+ "rindal",
+ "ringebu",
+ "ringerike",
+ "ringsaker",
+ "risor",
+ "rissa",
+ "rl",
+ "roan",
+ "rodoy",
+ "rollag",
+ "romsa",
+ "romskog",
+ "roros",
+ "rost",
+ "royken",
+ "royrvik",
+ "ruovat",
+ "rygge",
+ "salangen",
+ "salat",
+ "saltdal",
+ "samnanger",
+ "sandefjord",
+ "sandnes",
+ "sandnessjoen",
+ "sandoy",
+ "sarpsborg",
+ "sauda",
+ "sauherad",
+ "sel",
+ "selbu",
+ "selje",
+ "seljord",
+ "sf",
+ "siellak",
+ "sigdal",
+ "siljan",
+ "sirdal",
+ "skanit",
+ "skanland",
+ "skaun",
+ "skedsmo",
+ "skedsmokorset",
+ "ski",
+ "skien",
+ "skierva",
+ "skiptvet",
+ "skjak",
+ "skjervoy",
+ "skodje",
+ "slattum",
+ "smola",
+ "snaase",
+ "snasa",
+ "snillfjord",
+ "snoasa",
+ "sogndal",
+ "sogne",
+ "sokndal",
+ "sola",
+ "solund",
+ "somna",
+ "sondre-land",
+ "songdalen",
+ "sor-aurdal",
+ "sor-fron",
+ "sor-odal",
+ "sor-varanger",
+ "sorfold",
+ "sorreisa",
+ "sortland",
+ "sorum",
+ "spjelkavik",
+ "spydeberg",
+ "st",
+ "stange",
+ "stat",
+ "stathelle",
+ "stavanger",
+ "stavern",
+ "steigen",
+ "steinkjer",
+ "stjordal",
+ "stjordalshalsen",
+ "stokke",
+ "stor-elvdal",
+ "stord",
+ "stordal",
+ "storfjord",
+ "strand",
+ "stranda",
+ "stryn",
+ "sula",
+ "suldal",
+ "sund",
+ "sunndal",
+ "surnadal",
+ "svalbard",
+ "sveio",
+ "svelvik",
+ "sykkylven",
+ "tana",
+ "tananger",
+ "telemark",
+ "time",
+ "tingvoll",
+ "tinn",
+ "tjeldsund",
+ "tjome",
+ "tm",
+ "tokke",
+ "tolga",
+ "tonsberg",
+ "torsken",
+ "tr",
+ "trana",
+ "tranby",
+ "tranoy",
+ "troandin",
+ "trogstad",
+ "tromsa",
+ "tromso",
+ "trondheim",
+ "trysil",
+ "tvedestrand",
+ "tydal",
+ "tynset",
+ "tysfjord",
+ "tysnes",
+ "tysvar",
+ "ullensaker",
+ "ullensvang",
+ "ulvik",
+ "unjarga",
+ "utsira",
+ "va",
+ "vaapste",
+ "vadso",
+ "vaga",
+ "vagan",
+ "vagsoy",
+ "vaksdal",
+ "valle",
+ "vang",
+ "vanylven",
+ "vardo",
+ "varggat",
+ "varoy",
+ "vefsn",
+ "vega",
+ "vegarshei",
+ "vennesla",
+ "verdal",
+ "verran",
+ "vestby",
+ "vestfold",
+ "vestnes",
+ "vestre-slidre",
+ "vestre-toten",
+ "vestvagoy",
+ "vevelstad",
+ "vf",
+ "vgs",
+ "vik",
+ "vikna",
+ "vindafjord",
+ "voagat",
+ "volda",
+ "voss",
+ "vossevangen",
+ "xn--andy-ira",
+ "xn--asky-ira",
+ "xn--aurskog-hland-jnb",
+ "xn--avery-yua",
+ "xn--bdddj-mrabd",
+ "xn--bearalvhki-y4a",
+ "xn--berlevg-jxa",
+ "xn--bhcavuotna-s4a",
+ "xn--bhccavuotna-k7a",
+ "xn--bidr-5nac",
+ "xn--bievt-0qa",
+ "xn--bjarky-fya",
+ "xn--bjddar-pta",
+ "xn--blt-elab",
+ "xn--bmlo-gra",
+ "xn--bod-2na",
+ "xn--brnny-wuac",
+ "xn--brnnysund-m8ac",
+ "xn--brum-voa",
+ "xn--btsfjord-9za",
+ "xn--davvenjrga-y4a",
+ "xn--dnna-gra",
+ "xn--drbak-wua",
+ "xn--dyry-ira",
+ "xn--eveni-0qa01ga",
+ "xn--finny-yua",
+ "xn--fjord-lra",
+ "xn--fl-zia",
+ "xn--flor-jra",
+ "xn--frde-gra",
+ "xn--frna-woa",
+ "xn--frya-hra",
+ "xn--ggaviika-8ya47h",
+ "xn--gildeskl-g0a",
+ "xn--givuotna-8ya",
+ "xn--gjvik-wua",
+ "xn--gls-elac",
+ "xn--h-2fa",
+ "xn--hbmer-xqa",
+ "xn--hcesuolo-7ya35b",
+ "xn--hgebostad-g3a",
+ "xn--hmmrfeasta-s4ac",
+ "xn--hnefoss-q1a",
+ "xn--hobl-ira",
+ "xn--holtlen-hxa",
+ "xn--hpmir-xqa",
+ "xn--hyanger-q1a",
+ "xn--hylandet-54a",
+ "xn--indery-fya",
+ "xn--jlster-bya",
+ "xn--jrpeland-54a",
+ "xn--karmy-yua",
+ "xn--kfjord-iua",
+ "xn--klbu-woa",
+ "xn--koluokta-7ya57h",
+ "xn--krager-gya",
+ "xn--kranghke-b0a",
+ "xn--krdsherad-m8a",
+ "xn--krehamn-dxa",
+ "xn--krjohka-hwab49j",
+ "xn--ksnes-uua",
+ "xn--kvfjord-nxa",
+ "xn--kvitsy-fya",
+ "xn--kvnangen-k0a",
+ "xn--l-1fa",
+ "xn--laheadju-7ya",
+ "xn--langevg-jxa",
+ "xn--ldingen-q1a",
+ "xn--leagaviika-52b",
+ "xn--lesund-hua",
+ "xn--lgrd-poac",
+ "xn--lhppi-xqa",
+ "xn--linds-pra",
+ "xn--loabt-0qa",
+ "xn--lrdal-sra",
+ "xn--lrenskog-54a",
+ "xn--lt-liac",
+ "xn--lten-gra",
+ "xn--lury-ira",
+ "xn--mely-ira",
+ "xn--merker-kua",
+ "xn--mjndalen-64a",
+ "xn--mlatvuopmi-s4a",
+ "xn--mli-tla",
+ "xn--mlselv-iua",
+ "xn--moreke-jua",
+ "xn--mosjen-eya",
+ "xn--mot-tla",
+ "xn--mre-og-romsdal-qqb",
+ "xn--msy-ula0h",
+ "xn--mtta-vrjjat-k7af",
+ "xn--muost-0qa",
+ "xn--nmesjevuemie-tcba",
+ "xn--nry-yla5g",
+ "xn--nttery-byae",
+ "xn--nvuotna-hwa",
+ "xn--oppegrd-ixa",
+ "xn--ostery-fya",
+ "xn--osyro-wua",
+ "xn--porsgu-sta26f",
+ "xn--rady-ira",
+ "xn--rdal-poa",
+ "xn--rde-ula",
+ "xn--rdy-0nab",
+ "xn--rennesy-v1a",
+ "xn--rhkkervju-01af",
+ "xn--rholt-mra",
+ "xn--risa-5na",
+ "xn--risr-ira",
+ "xn--rland-uua",
+ "xn--rlingen-mxa",
+ "xn--rmskog-bya",
+ "xn--rros-gra",
+ "xn--rskog-uua",
+ "xn--rst-0na",
+ "xn--rsta-fra",
+ "xn--ryken-vua",
+ "xn--ryrvik-bya",
+ "xn--s-1fa",
+ "xn--sandnessjen-ogb",
+ "xn--sandy-yua",
+ "xn--seral-lra",
+ "xn--sgne-gra",
+ "xn--skierv-uta",
+ "xn--skjervy-v1a",
+ "xn--skjk-soa",
+ "xn--sknit-yqa",
+ "xn--sknland-fxa",
+ "xn--slat-5na",
+ "xn--slt-elab",
+ "xn--smla-hra",
+ "xn--smna-gra",
+ "xn--snase-nra",
+ "xn--sndre-land-0cb",
+ "xn--snes-poa",
+ "xn--snsa-roa",
+ "xn--sr-aurdal-l8a",
+ "xn--sr-fron-q1a",
+ "xn--sr-odal-q1a",
+ "xn--sr-varanger-ggb",
+ "xn--srfold-bya",
+ "xn--srreisa-q1a",
+ "xn--srum-gra",
+ "xn--stfold-9xa",
+ "xn--stjrdal-s1a",
+ "xn--stjrdalshalsen-sqb",
+ "xn--stre-toten-zcb",
+ "xn--tjme-hra",
+ "xn--tnsberg-q1a",
+ "xn--trany-yua",
+ "xn--trgstad-r1a",
+ "xn--trna-woa",
+ "xn--troms-zua",
+ "xn--tysvr-vra",
+ "xn--unjrga-rta",
+ "xn--vads-jra",
+ "xn--vard-jra",
+ "xn--vegrshei-c0a",
+ "xn--vestvgy-ixa6o",
+ "xn--vg-yiab",
+ "xn--vgan-qoa",
+ "xn--vgsy-qoa0j",
+ "xn--vre-eiker-k8a",
+ "xn--vrggt-xqad",
+ "xn--vry-yla5g",
+ "xn--yer-zna",
+ "xn--ygarden-p1a",
+ "xn--ystre-slidre-ujb",
+ "gs",
+ "gs",
+ "nes",
+ "gs",
+ "nes",
+ "gs",
+ "os",
+ "valer",
+ "xn--vler-qoa",
+ "gs",
+ "gs",
+ "os",
+ "gs",
+ "heroy",
+ "sande",
+ "gs",
+ "gs",
+ "bo",
+ "heroy",
+ "xn--b-5ga",
+ "xn--hery-ira",
+ "gs",
+ "gs",
+ "gs",
+ "gs",
+ "valer",
+ "gs",
+ "gs",
+ "gs",
+ "gs",
+ "bo",
+ "xn--b-5ga",
+ "gs",
+ "gs",
+ "gs",
+ "sande",
+ "gs",
+ "sande",
+ "xn--hery-ira",
+ "xn--vler-qoa",
+ "biz",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "net",
+ "org",
+ "merseine",
+ "mine",
+ "shacknet",
+ "ac",
+ "co",
+ "cri",
+ "geek",
+ "gen",
+ "govt",
+ "health",
+ "iwi",
+ "kiwi",
+ "maori",
+ "mil",
+ "net",
+ "org",
+ "parliament",
+ "school",
+ "xn--mori-qsa",
+ "blogspot",
+ "co",
+ "com",
+ "edu",
+ "gov",
+ "med",
+ "museum",
+ "net",
+ "org",
+ "pro",
+ "ae",
+ "blogdns",
+ "blogsite",
+ "boldlygoingnowhere",
+ "cdn77",
+ "cdn77-secure",
+ "dnsalias",
+ "dnsdojo",
+ "doesntexist",
+ "dontexist",
+ "doomdns",
+ "duckdns",
+ "dvrdns",
+ "dynalias",
+ "dyndns",
+ "endofinternet",
+ "endoftheinternet",
+ "eu",
+ "from-me",
+ "game-host",
+ "gotdns",
+ "hk",
+ "hobby-site",
+ "homedns",
+ "homeftp",
+ "homelinux",
+ "homeunix",
+ "is-a-bruinsfan",
+ "is-a-candidate",
+ "is-a-celticsfan",
+ "is-a-chef",
+ "is-a-geek",
+ "is-a-knight",
+ "is-a-linux-user",
+ "is-a-patsfan",
+ "is-a-soxfan",
+ "is-found",
+ "is-lost",
+ "is-saved",
+ "is-very-bad",
+ "is-very-evil",
+ "is-very-good",
+ "is-very-nice",
+ "is-very-sweet",
+ "isa-geek",
+ "kicks-ass",
+ "misconfused",
+ "podzone",
+ "readmyblog",
+ "selfip",
+ "sellsyourhome",
+ "servebbs",
+ "serveftp",
+ "servegame",
+ "stuff-4-sale",
+ "us",
+ "webhop",
+ "za",
+ "c",
+ "rsc",
+ "origin",
+ "ssl",
+ "go",
+ "home",
+ "al",
+ "asso",
+ "at",
+ "au",
+ "be",
+ "bg",
+ "ca",
+ "cd",
+ "ch",
+ "cn",
+ "cy",
+ "cz",
+ "de",
+ "dk",
+ "edu",
+ "ee",
+ "es",
+ "fi",
+ "fr",
+ "gr",
+ "hr",
+ "hu",
+ "ie",
+ "il",
+ "in",
+ "int",
+ "is",
+ "it",
+ "jp",
+ "kr",
+ "lt",
+ "lu",
+ "lv",
+ "mc",
+ "me",
+ "mk",
+ "mt",
+ "my",
+ "net",
+ "ng",
+ "nl",
+ "no",
+ "nz",
+ "paris",
+ "pl",
+ "pt",
+ "q-a",
+ "ro",
+ "ru",
+ "se",
+ "si",
+ "sk",
+ "tr",
+ "uk",
+ "us",
+ "abo",
+ "ac",
+ "com",
+ "edu",
+ "gob",
+ "ing",
+ "med",
+ "net",
+ "nom",
+ "org",
+ "sld",
+ "blogspot",
+ "com",
+ "edu",
+ "gob",
+ "mil",
+ "net",
+ "nom",
+ "org",
+ "com",
+ "edu",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "i",
+ "mil",
+ "net",
+ "ngo",
+ "org",
+ "biz",
+ "com",
+ "edu",
+ "fam",
+ "gob",
+ "gok",
+ "gon",
+ "gop",
+ "gos",
+ "gov",
+ "info",
+ "net",
+ "org",
+ "web",
+ "agro",
+ "aid",
+ "art",
+ "atm",
+ "augustow",
+ "auto",
+ "babia-gora",
+ "bedzin",
+ "beskidy",
+ "bialowieza",
+ "bialystok",
+ "bielawa",
+ "bieszczady",
+ "biz",
+ "boleslawiec",
+ "bydgoszcz",
+ "bytom",
+ "cieszyn",
+ "co",
+ "com",
+ "czeladz",
+ "czest",
+ "dlugoleka",
+ "edu",
+ "elblag",
+ "elk",
+ "gda",
+ "gdansk",
+ "gdynia",
+ "gliwice",
+ "glogow",
+ "gmina",
+ "gniezno",
+ "gorlice",
+ "gov",
+ "grajewo",
+ "gsm",
+ "ilawa",
+ "info",
+ "jaworzno",
+ "jelenia-gora",
+ "jgora",
+ "kalisz",
+ "karpacz",
+ "kartuzy",
+ "kaszuby",
+ "katowice",
+ "kazimierz-dolny",
+ "kepno",
+ "ketrzyn",
+ "klodzko",
+ "kobierzyce",
+ "kolobrzeg",
+ "konin",
+ "konskowola",
+ "krakow",
+ "kutno",
+ "lapy",
+ "lebork",
+ "legnica",
+ "lezajsk",
+ "limanowa",
+ "lomza",
+ "lowicz",
+ "lubin",
+ "lukow",
+ "mail",
+ "malbork",
+ "malopolska",
+ "mazowsze",
+ "mazury",
+ "med",
+ "media",
+ "miasta",
+ "mielec",
+ "mielno",
+ "mil",
+ "mragowo",
+ "naklo",
+ "net",
+ "nieruchomosci",
+ "nom",
+ "nowaruda",
+ "nysa",
+ "olawa",
+ "olecko",
+ "olkusz",
+ "olsztyn",
+ "opoczno",
+ "opole",
+ "org",
+ "ostroda",
+ "ostroleka",
+ "ostrowiec",
+ "ostrowwlkp",
+ "pc",
+ "pila",
+ "pisz",
+ "podhale",
+ "podlasie",
+ "polkowice",
+ "pomorskie",
+ "pomorze",
+ "powiat",
+ "poznan",
+ "priv",
+ "prochowice",
+ "pruszkow",
+ "przeworsk",
+ "pulawy",
+ "radom",
+ "rawa-maz",
+ "realestate",
+ "rel",
+ "rybnik",
+ "rzeszow",
+ "sanok",
+ "sejny",
+ "sex",
+ "shop",
+ "sklep",
+ "skoczow",
+ "slask",
+ "slupsk",
+ "sopot",
+ "sos",
+ "sosnowiec",
+ "stalowa-wola",
+ "starachowice",
+ "stargard",
+ "suwalki",
+ "swidnica",
+ "swiebodzin",
+ "swinoujscie",
+ "szczecin",
+ "szczytno",
+ "szkola",
+ "targi",
+ "tarnobrzeg",
+ "tgory",
+ "tm",
+ "tourism",
+ "travel",
+ "turek",
+ "turystyka",
+ "tychy",
+ "ustka",
+ "walbrzych",
+ "warmia",
+ "warszawa",
+ "waw",
+ "wegrow",
+ "wielun",
+ "wlocl",
+ "wloclawek",
+ "wodzislaw",
+ "wolomin",
+ "wroc",
+ "wroclaw",
+ "zachpomor",
+ "zagan",
+ "zakopane",
+ "zarow",
+ "zgora",
+ "zgorzelec",
+ "ap",
+ "griw",
+ "ic",
+ "is",
+ "kmpsp",
+ "konsulat",
+ "kppsp",
+ "kwp",
+ "kwpsp",
+ "mup",
+ "mw",
+ "oirm",
+ "oum",
+ "pa",
+ "pinb",
+ "piw",
+ "po",
+ "psp",
+ "psse",
+ "pup",
+ "rzgw",
+ "sa",
+ "sdn",
+ "sko",
+ "so",
+ "sr",
+ "starostwo",
+ "ug",
+ "ugim",
+ "um",
+ "umig",
+ "upow",
+ "uppo",
+ "us",
+ "uw",
+ "uzs",
+ "wif",
+ "wiih",
+ "winb",
+ "wios",
+ "witd",
+ "wiw",
+ "wsa",
+ "wskr",
+ "wuoz",
+ "wzmiuw",
+ "zp",
+ "co",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "ac",
+ "biz",
+ "com",
+ "edu",
+ "est",
+ "gov",
+ "info",
+ "isla",
+ "name",
+ "net",
+ "org",
+ "pro",
+ "prof",
+ "aca",
+ "bar",
+ "cpa",
+ "eng",
+ "jur",
+ "law",
+ "med",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "plo",
+ "sec",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "int",
+ "net",
+ "nome",
+ "org",
+ "publ",
+ "belau",
+ "co",
+ "ed",
+ "go",
+ "ne",
+ "or",
+ "com",
+ "coop",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "name",
+ "net",
+ "org",
+ "sch",
+ "asso",
+ "blogspot",
+ "com",
+ "nom",
+ "arts",
+ "blogspot",
+ "com",
+ "firm",
+ "info",
+ "nom",
+ "nt",
+ "org",
+ "rec",
+ "store",
+ "tm",
+ "www",
+ "ac",
+ "blogspot",
+ "co",
+ "edu",
+ "gov",
+ "in",
+ "org",
+ "ac",
+ "adygeya",
+ "altai",
+ "amur",
+ "amursk",
+ "arkhangelsk",
+ "astrakhan",
+ "baikal",
+ "bashkiria",
+ "belgorod",
+ "bir",
+ "blogspot",
+ "bryansk",
+ "buryatia",
+ "cbg",
+ "chel",
+ "chelyabinsk",
+ "chita",
+ "chukotka",
+ "chuvashia",
+ "cmw",
+ "com",
+ "dagestan",
+ "dudinka",
+ "e-burg",
+ "edu",
+ "fareast",
+ "gov",
+ "grozny",
+ "int",
+ "irkutsk",
+ "ivanovo",
+ "izhevsk",
+ "jamal",
+ "jar",
+ "joshkar-ola",
+ "k-uralsk",
+ "kalmykia",
+ "kaluga",
+ "kamchatka",
+ "karelia",
+ "kazan",
+ "kchr",
+ "kemerovo",
+ "khabarovsk",
+ "khakassia",
+ "khv",
+ "kirov",
+ "kms",
+ "koenig",
+ "komi",
+ "kostroma",
+ "krasnoyarsk",
+ "kuban",
+ "kurgan",
+ "kursk",
+ "kustanai",
+ "kuzbass",
+ "lipetsk",
+ "magadan",
+ "magnitka",
+ "mari",
+ "mari-el",
+ "marine",
+ "mil",
+ "mordovia",
+ "msk",
+ "murmansk",
+ "mytis",
+ "nakhodka",
+ "nalchik",
+ "net",
+ "nkz",
+ "nnov",
+ "norilsk",
+ "nov",
+ "novosibirsk",
+ "nsk",
+ "omsk",
+ "orenburg",
+ "org",
+ "oryol",
+ "oskol",
+ "palana",
+ "penza",
+ "perm",
+ "pp",
+ "ptz",
+ "pyatigorsk",
+ "rnd",
+ "rubtsovsk",
+ "ryazan",
+ "sakhalin",
+ "samara",
+ "saratov",
+ "simbirsk",
+ "smolensk",
+ "snz",
+ "spb",
+ "stavropol",
+ "stv",
+ "surgut",
+ "syzran",
+ "tambov",
+ "tatarstan",
+ "test",
+ "tom",
+ "tomsk",
+ "tsaritsyn",
+ "tsk",
+ "tula",
+ "tuva",
+ "tver",
+ "tyumen",
+ "udm",
+ "udmurtia",
+ "ulan-ude",
+ "vdonsk",
+ "vladikavkaz",
+ "vladimir",
+ "vladivostok",
+ "volgograd",
+ "vologda",
+ "voronezh",
+ "vrn",
+ "vyatka",
+ "yakutia",
+ "yamal",
+ "yaroslavl",
+ "yekaterinburg",
+ "yuzhno-sakhalinsk",
+ "zgrad",
+ "ac",
+ "co",
+ "com",
+ "edu",
+ "gouv",
+ "gov",
+ "int",
+ "mil",
+ "net",
+ "com",
+ "edu",
+ "gov",
+ "med",
+ "net",
+ "org",
+ "pub",
+ "sch",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "info",
+ "med",
+ "net",
+ "org",
+ "tv",
+ "a",
+ "ac",
+ "b",
+ "bd",
+ "blogspot",
+ "brand",
+ "c",
+ "com",
+ "d",
+ "e",
+ "f",
+ "fh",
+ "fhsk",
+ "fhv",
+ "g",
+ "h",
+ "i",
+ "k",
+ "komforb",
+ "kommunalforbund",
+ "komvux",
+ "l",
+ "lanbib",
+ "m",
+ "n",
+ "naturbruksgymn",
+ "o",
+ "org",
+ "p",
+ "parti",
+ "pp",
+ "press",
+ "r",
+ "s",
+ "t",
+ "tm",
+ "u",
+ "w",
+ "x",
+ "y",
+ "z",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "per",
+ "com",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "platform",
+ "blogspot",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "net",
+ "org",
+ "art",
+ "blogspot",
+ "com",
+ "edu",
+ "gouv",
+ "org",
+ "perso",
+ "univ",
+ "com",
+ "net",
+ "org",
+ "co",
+ "com",
+ "consulado",
+ "edu",
+ "embaixada",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "principe",
+ "saotome",
+ "store",
+ "adygeya",
+ "arkhangelsk",
+ "balashov",
+ "bashkiria",
+ "bryansk",
+ "dagestan",
+ "grozny",
+ "ivanovo",
+ "kalmykia",
+ "kaluga",
+ "karelia",
+ "khakassia",
+ "krasnodar",
+ "kurgan",
+ "lenug",
+ "mordovia",
+ "msk",
+ "murmansk",
+ "nalchik",
+ "nov",
+ "obninsk",
+ "penza",
+ "pokrovsk",
+ "sochi",
+ "spb",
+ "togliatti",
+ "troitsk",
+ "tula",
+ "tuva",
+ "vladikavkaz",
+ "vladimir",
+ "vologda",
+ "com",
+ "edu",
+ "gob",
+ "org",
+ "red",
+ "gov",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "ac",
+ "co",
+ "org",
+ "blogspot",
+ "ac",
+ "co",
+ "go",
+ "in",
+ "mi",
+ "net",
+ "or",
+ "ac",
+ "biz",
+ "co",
+ "com",
+ "edu",
+ "go",
+ "gov",
+ "int",
+ "mil",
+ "name",
+ "net",
+ "nic",
+ "org",
+ "test",
+ "web",
+ "gov",
+ "co",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "nom",
+ "org",
+ "agrinet",
+ "com",
+ "defense",
+ "edunet",
+ "ens",
+ "fin",
+ "gov",
+ "ind",
+ "info",
+ "intl",
+ "mincom",
+ "nat",
+ "net",
+ "org",
+ "perso",
+ "rnrt",
+ "rns",
+ "rnu",
+ "tourism",
+ "turen",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "av",
+ "bbs",
+ "bel",
+ "biz",
+ "com",
+ "dr",
+ "edu",
+ "gen",
+ "gov",
+ "info",
+ "k12",
+ "kep",
+ "mil",
+ "name",
+ "nc",
+ "net",
+ "org",
+ "pol",
+ "tel",
+ "tv",
+ "web",
+ "blogspot",
+ "gov",
+ "aero",
+ "biz",
+ "co",
+ "com",
+ "coop",
+ "edu",
+ "gov",
+ "info",
+ "int",
+ "jobs",
+ "mobi",
+ "museum",
+ "name",
+ "net",
+ "org",
+ "pro",
+ "travel",
+ "better-than",
+ "dyndns",
+ "on-the-web",
+ "worse-than",
+ "blogspot",
+ "club",
+ "com",
+ "ebiz",
+ "edu",
+ "game",
+ "gov",
+ "idv",
+ "mil",
+ "net",
+ "org",
+ "xn--czrw28b",
+ "xn--uc0atv",
+ "xn--zf0ao64a",
+ "ac",
+ "co",
+ "go",
+ "hotel",
+ "info",
+ "me",
+ "mil",
+ "mobi",
+ "ne",
+ "or",
+ "sc",
+ "tv",
+ "cherkassy",
+ "cherkasy",
+ "chernigov",
+ "chernihiv",
+ "chernivtsi",
+ "chernovtsy",
+ "ck",
+ "cn",
+ "co",
+ "com",
+ "cr",
+ "crimea",
+ "cv",
+ "dn",
+ "dnepropetrovsk",
+ "dnipropetrovsk",
+ "dominic",
+ "donetsk",
+ "dp",
+ "edu",
+ "gov",
+ "if",
+ "in",
+ "ivano-frankivsk",
+ "kh",
+ "kharkiv",
+ "kharkov",
+ "kherson",
+ "khmelnitskiy",
+ "khmelnytskyi",
+ "kiev",
+ "kirovograd",
+ "km",
+ "kr",
+ "krym",
+ "ks",
+ "kv",
+ "kyiv",
+ "lg",
+ "lt",
+ "lugansk",
+ "lutsk",
+ "lv",
+ "lviv",
+ "mk",
+ "mykolaiv",
+ "net",
+ "nikolaev",
+ "od",
+ "odesa",
+ "odessa",
+ "org",
+ "pl",
+ "poltava",
+ "pp",
+ "rivne",
+ "rovno",
+ "rv",
+ "sb",
+ "sebastopol",
+ "sevastopol",
+ "sm",
+ "sumy",
+ "te",
+ "ternopil",
+ "uz",
+ "uzhgorod",
+ "vinnica",
+ "vinnytsia",
+ "vn",
+ "volyn",
+ "yalta",
+ "zaporizhzhe",
+ "zaporizhzhia",
+ "zhitomir",
+ "zhytomyr",
+ "zp",
+ "zt",
+ "ac",
+ "blogspot",
+ "co",
+ "com",
+ "go",
+ "ne",
+ "or",
+ "org",
+ "sc",
+ "ac",
+ "co",
+ "gov",
+ "ltd",
+ "me",
+ "net",
+ "nhs",
+ "org",
+ "plc",
+ "police",
+ "sch",
+ "blogspot",
+ "service",
+ "ak",
+ "al",
+ "ar",
+ "as",
+ "az",
+ "ca",
+ "co",
+ "ct",
+ "dc",
+ "de",
+ "dni",
+ "fed",
+ "fl",
+ "ga",
+ "gu",
+ "hi",
+ "ia",
+ "id",
+ "il",
+ "in",
+ "is-by",
+ "isa",
+ "kids",
+ "ks",
+ "ky",
+ "la",
+ "land-4-sale",
+ "ma",
+ "md",
+ "me",
+ "mi",
+ "mn",
+ "mo",
+ "ms",
+ "mt",
+ "nc",
+ "nd",
+ "ne",
+ "nh",
+ "nj",
+ "nm",
+ "nsn",
+ "nv",
+ "ny",
+ "oh",
+ "ok",
+ "or",
+ "pa",
+ "pr",
+ "ri",
+ "sc",
+ "sd",
+ "stuff-4-sale",
+ "tn",
+ "tx",
+ "ut",
+ "va",
+ "vi",
+ "vt",
+ "wa",
+ "wi",
+ "wv",
+ "wy",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "chtr",
+ "paroch",
+ "pvt",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "k12",
+ "lib",
+ "cc",
+ "cc",
+ "k12",
+ "lib",
+ "com",
+ "edu",
+ "gub",
+ "mil",
+ "net",
+ "org",
+ "blogspot",
+ "co",
+ "com",
+ "net",
+ "org",
+ "com",
+ "edu",
+ "gov",
+ "mil",
+ "net",
+ "org",
+ "arts",
+ "co",
+ "com",
+ "e12",
+ "edu",
+ "firm",
+ "gob",
+ "gov",
+ "info",
+ "int",
+ "mil",
+ "net",
+ "org",
+ "rec",
+ "store",
+ "tec",
+ "web",
+ "co",
+ "com",
+ "k12",
+ "net",
+ "org",
+ "ac",
+ "biz",
+ "blogspot",
+ "com",
+ "edu",
+ "gov",
+ "health",
+ "info",
+ "int",
+ "name",
+ "net",
+ "org",
+ "pro",
+ "com",
+ "edu",
+ "net",
+ "org",
+ "com",
+ "dyndns",
+ "edu",
+ "gov",
+ "mypets",
+ "net",
+ "org",
+ "xn--80au",
+ "xn--90azh",
+ "xn--c1avg",
+ "xn--d1at",
+ "xn--o1ac",
+ "xn--o1ach",
+ "ac",
+ "agrica",
+ "alt",
+ "co",
+ "edu",
+ "gov",
+ "grondar",
+ "law",
+ "mil",
+ "net",
+ "ngo",
+ "nis",
+ "nom",
+ "org",
+ "school",
+ "tm",
+ "web",
+ "blogspot",
+}
diff --git a/trace/events.go b/trace/events.go
new file mode 100644
index 0000000..fe3af66
--- /dev/null
+++ b/trace/events.go
@@ -0,0 +1,518 @@
+// Copyright 2015 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.
+
+package trace
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "io"
+ "log"
+ "net/http"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "text/tabwriter"
+ "time"
+)
+
+var eventsTmpl = template.Must(template.New("events").Funcs(template.FuncMap{
+ "elapsed": elapsed,
+ "trimSpace": strings.TrimSpace,
+}).Parse(eventsHTML))
+
+const maxEventsPerLog = 100
+
+type bucket struct {
+ MaxErrAge time.Duration
+ String string
+}
+
+var buckets = []bucket{
+ {0, "total"},
+ {10 * time.Second, "errs<10s"},
+ {1 * time.Minute, "errs<1m"},
+ {10 * time.Minute, "errs<10m"},
+ {1 * time.Hour, "errs<1h"},
+ {10 * time.Hour, "errs<10h"},
+ {24000 * time.Hour, "errors"},
+}
+
+func renderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
+ now := time.Now()
+ data := &struct {
+ Families []string // family names
+ Buckets []bucket
+ Counts [][]int // eventLog count per family/bucket
+
+ // Set when a bucket has been selected.
+ Family string
+ Bucket int
+ EventLogs eventLogs
+ Expanded bool
+ }{
+ Buckets: buckets,
+ }
+
+ data.Families = make([]string, 0, len(families))
+ famMu.RLock()
+ for name := range families {
+ data.Families = append(data.Families, name)
+ }
+ famMu.RUnlock()
+ sort.Strings(data.Families)
+
+ // Count the number of eventLogs in each family for each error age.
+ data.Counts = make([][]int, len(data.Families))
+ for i, name := range data.Families {
+ // TODO(sameer): move this loop under the family lock.
+ f := getEventFamily(name)
+ data.Counts[i] = make([]int, len(data.Buckets))
+ for j, b := range data.Buckets {
+ data.Counts[i][j] = f.Count(now, b.MaxErrAge)
+ }
+ }
+
+ var ok bool
+ data.Family, data.Bucket, ok = parseEventsArgs(req)
+ if !ok {
+ // No-op
+ } else {
+ data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
+ }
+ if data.EventLogs != nil {
+ defer data.EventLogs.Free()
+ sort.Sort(data.EventLogs)
+ }
+ if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
+ data.Expanded = exp
+ }
+
+ famMu.RLock()
+ defer famMu.RUnlock()
+ if err := eventsTmpl.Execute(w, data); err != nil {
+ log.Printf("net/trace: Failed executing template: %v", err)
+ }
+}
+
+func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
+ fam, bStr := req.FormValue("fam"), req.FormValue("b")
+ if fam == "" || bStr == "" {
+ return "", 0, false
+ }
+ b, err := strconv.Atoi(bStr)
+ if err != nil || b < 0 || b >= len(buckets) {
+ return "", 0, false
+ }
+ return fam, b, true
+}
+
+// An EventLog provides a log of events associated with a specific object.
+type EventLog interface {
+ // Printf formats its arguments with fmt.Sprintf and adds the
+ // result to the event log.
+ Printf(format string, a ...interface{})
+
+ // Errorf is like Printf, but it marks this event as an error.
+ Errorf(format string, a ...interface{})
+
+ // Finish declares that this event log is complete.
+ // The event log should not be used after calling this method.
+ Finish()
+}
+
+// NewEventLog returns a new EventLog with the specified family name
+// and title.
+func NewEventLog(family, title string) EventLog {
+ el := newEventLog()
+ el.ref()
+ el.Family, el.Title = family, title
+ el.Start = time.Now()
+ el.events = make([]logEntry, 0, maxEventsPerLog)
+ el.stack = make([]uintptr, 32)
+ n := runtime.Callers(2, el.stack)
+ el.stack = el.stack[:n]
+
+ getEventFamily(family).add(el)
+ return el
+}
+
+func (el *eventLog) Finish() {
+ getEventFamily(el.Family).remove(el)
+ el.unref() // matches ref in New
+}
+
+var (
+ famMu sync.RWMutex
+ families = make(map[string]*eventFamily) // family name => family
+)
+
+func getEventFamily(fam string) *eventFamily {
+ famMu.Lock()
+ defer famMu.Unlock()
+ f := families[fam]
+ if f == nil {
+ f = &eventFamily{}
+ families[fam] = f
+ }
+ return f
+}
+
+type eventFamily struct {
+ mu sync.RWMutex
+ eventLogs eventLogs
+}
+
+func (f *eventFamily) add(el *eventLog) {
+ f.mu.Lock()
+ f.eventLogs = append(f.eventLogs, el)
+ f.mu.Unlock()
+}
+
+func (f *eventFamily) remove(el *eventLog) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ for i, el0 := range f.eventLogs {
+ if el == el0 {
+ copy(f.eventLogs[i:], f.eventLogs[i+1:])
+ f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
+ return
+ }
+ }
+}
+
+func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
+ f.mu.RLock()
+ defer f.mu.RUnlock()
+ for _, el := range f.eventLogs {
+ if el.hasRecentError(now, maxErrAge) {
+ n++
+ }
+ }
+ return
+}
+
+func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
+ f.mu.RLock()
+ defer f.mu.RUnlock()
+ els = make(eventLogs, 0, len(f.eventLogs))
+ for _, el := range f.eventLogs {
+ if el.hasRecentError(now, maxErrAge) {
+ el.ref()
+ els = append(els, el)
+ }
+ }
+ return
+}
+
+type eventLogs []*eventLog
+
+// Free calls unref on each element of the list.
+func (els eventLogs) Free() {
+ for _, el := range els {
+ el.unref()
+ }
+}
+
+// eventLogs may be sorted in reverse chronological order.
+func (els eventLogs) Len() int { return len(els) }
+func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
+func (els eventLogs) Swap(i, j int) { els[i], els[j] = els[j], els[i] }
+
+// A logEntry is a timestamped log entry in an event log.
+type logEntry struct {
+ When time.Time
+ Elapsed time.Duration // since previous event in log
+ NewDay bool // whether this event is on a different day to the previous event
+ What string
+ IsErr bool
+}
+
+// WhenString returns a string representation of the elapsed time of the event.
+// It will include the date if midnight was crossed.
+func (e logEntry) WhenString() string {
+ if e.NewDay {
+ return e.When.Format("2006/01/02 15:04:05.000000")
+ }
+ return e.When.Format("15:04:05.000000")
+}
+
+// An eventLog represents an active event log.
+type eventLog struct {
+ // Family is the top-level grouping of event logs to which this belongs.
+ Family string
+
+ // Title is the title of this event log.
+ Title string
+
+ // Timing information.
+ Start time.Time
+
+ // Call stack where this event log was created.
+ stack []uintptr
+
+ // Append-only sequence of events.
+ //
+ // TODO(sameer): change this to a ring buffer to avoid the array copy
+ // when we hit maxEventsPerLog.
+ mu sync.RWMutex
+ events []logEntry
+ LastErrorTime time.Time
+ discarded int
+
+ refs int32 // how many buckets this is in
+}
+
+func (el *eventLog) reset() {
+ // Clear all but the mutex. Mutexes may not be copied, even when unlocked.
+ el.Family = ""
+ el.Title = ""
+ el.Start = time.Time{}
+ el.stack = nil
+ el.events = nil
+ el.LastErrorTime = time.Time{}
+ el.discarded = 0
+ el.refs = 0
+}
+
+func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
+ if maxErrAge == 0 {
+ return true
+ }
+ el.mu.RLock()
+ defer el.mu.RUnlock()
+ return now.Sub(el.LastErrorTime) < maxErrAge
+}
+
+// delta returns the elapsed time since the last event or the log start,
+// and whether it spans midnight.
+// L >= el.mu
+func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
+ if len(el.events) == 0 {
+ return t.Sub(el.Start), false
+ }
+ prev := el.events[len(el.events)-1].When
+ return t.Sub(prev), prev.Day() != t.Day()
+
+}
+
+func (el *eventLog) Printf(format string, a ...interface{}) {
+ el.printf(false, format, a...)
+}
+
+func (el *eventLog) Errorf(format string, a ...interface{}) {
+ el.printf(true, format, a...)
+}
+
+func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
+ e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
+ el.mu.Lock()
+ e.Elapsed, e.NewDay = el.delta(e.When)
+ if len(el.events) < maxEventsPerLog {
+ el.events = append(el.events, e)
+ } else {
+ // Discard the oldest event.
+ if el.discarded == 0 {
+ // el.discarded starts at two to count for the event it
+ // is replacing, plus the next one that we are about to
+ // drop.
+ el.discarded = 2
+ } else {
+ el.discarded++
+ }
+ // TODO(sameer): if this causes allocations on a critical path,
+ // change eventLog.What to be a fmt.Stringer, as in trace.go.
+ el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
+ // The timestamp of the discarded meta-event should be
+ // the time of the last event it is representing.
+ el.events[0].When = el.events[1].When
+ copy(el.events[1:], el.events[2:])
+ el.events[maxEventsPerLog-1] = e
+ }
+ if e.IsErr {
+ el.LastErrorTime = e.When
+ }
+ el.mu.Unlock()
+}
+
+func (el *eventLog) ref() {
+ atomic.AddInt32(&el.refs, 1)
+}
+
+func (el *eventLog) unref() {
+ if atomic.AddInt32(&el.refs, -1) == 0 {
+ freeEventLog(el)
+ }
+}
+
+func (el *eventLog) When() string {
+ return el.Start.Format("2006/01/02 15:04:05.000000")
+}
+
+func (el *eventLog) ElapsedTime() string {
+ elapsed := time.Since(el.Start)
+ return fmt.Sprintf("%.6f", elapsed.Seconds())
+}
+
+func (el *eventLog) Stack() string {
+ buf := new(bytes.Buffer)
+ tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
+ printStackRecord(tw, el.stack)
+ tw.Flush()
+ return buf.String()
+}
+
+// printStackRecord prints the function + source line information
+// for a single stack trace.
+// Adapted from runtime/pprof/pprof.go.
+func printStackRecord(w io.Writer, stk []uintptr) {
+ for _, pc := range stk {
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ continue
+ }
+ file, line := f.FileLine(pc)
+ name := f.Name()
+ // Hide runtime.goexit and any runtime functions at the beginning.
+ if strings.HasPrefix(name, "runtime.") {
+ continue
+ }
+ fmt.Fprintf(w, "# %s\t%s:%d\n", name, file, line)
+ }
+}
+
+func (el *eventLog) Events() []logEntry {
+ el.mu.RLock()
+ defer el.mu.RUnlock()
+ return el.events
+}
+
+// freeEventLogs is a freelist of *eventLog
+var freeEventLogs = make(chan *eventLog, 1000)
+
+// newEventLog returns a event log ready to use.
+func newEventLog() *eventLog {
+ select {
+ case el := <-freeEventLogs:
+ return el
+ default:
+ return new(eventLog)
+ }
+}
+
+// freeEventLog adds el to freeEventLogs if there's room.
+// This is non-blocking.
+func freeEventLog(el *eventLog) {
+ el.reset()
+ select {
+ case freeEventLogs <- el:
+ default:
+ }
+}
+
+const eventsHTML = `
+<html>
+ <head>
+ <title>events</title>
+ </head>
+ <style type="text/css">
+ body {
+ font-family: sans-serif;
+ }
+ table#req-status td.family {
+ padding-right: 2em;
+ }
+ table#req-status td.active {
+ padding-right: 1em;
+ }
+ table#req-status td.empty {
+ color: #aaa;
+ }
+ table#reqs {
+ margin-top: 1em;
+ }
+ table#reqs tr.first {
+ {{if $.Expanded}}font-weight: bold;{{end}}
+ }
+ table#reqs td {
+ font-family: monospace;
+ }
+ table#reqs td.when {
+ text-align: right;
+ white-space: nowrap;
+ }
+ table#reqs td.elapsed {
+ padding: 0 0.5em;
+ text-align: right;
+ white-space: pre;
+ width: 10em;
+ }
+ address {
+ font-size: smaller;
+ margin-top: 5em;
+ }
+ </style>
+ <body>
+
+<h1>/debug/events</h1>
+
+<table id="req-status">
+ {{range $i, $fam := .Families}}
+ <tr>
+ <td class="family">{{$fam}}</td>
+
+ {{range $j, $bucket := $.Buckets}}
+ {{$n := index $.Counts $i $j}}
+ <td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
+ {{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
+ [{{$n}} {{$bucket.String}}]
+ {{if $n}}</a>{{end}}
+ </td>
+ {{end}}
+
+ </tr>{{end}}
+</table>
+
+{{if $.EventLogs}}
+<hr />
+<h3>Family: {{$.Family}}</h3>
+
+{{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
+[Summary]{{if $.Expanded}}</a>{{end}}
+
+{{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
+[Expanded]{{if not $.Expanded}}</a>{{end}}
+
+<table id="reqs">
+ <tr><th>When</th><th>Elapsed</th></tr>
+ {{range $el := $.EventLogs}}
+ <tr class="first">
+ <td class="when">{{$el.When}}</td>
+ <td class="elapsed">{{$el.ElapsedTime}}</td>
+ <td>{{$el.Title}}
+ </tr>
+ {{if $.Expanded}}
+ <tr>
+ <td class="when"></td>
+ <td class="elapsed"></td>
+ <td><pre>{{$el.Stack|trimSpace}}</pre></td>
+ </tr>
+ {{range $el.Events}}
+ <tr>
+ <td class="when">{{.WhenString}}</td>
+ <td class="elapsed">{{elapsed .Elapsed}}</td>
+ <td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
+ </tr>
+ {{end}}
+ {{end}}
+ {{end}}
+</table>
+{{end}}
+ </body>
+</html>
+`
diff --git a/trace/histogram.go b/trace/histogram.go
new file mode 100644
index 0000000..bb42aa5
--- /dev/null
+++ b/trace/histogram.go
@@ -0,0 +1,356 @@
+// Copyright 2015 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.
+
+package trace
+
+// This file implements histogramming for RPC statistics collection.
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "log"
+ "math"
+
+ "golang.org/x/net/internal/timeseries"
+)
+
+const (
+ bucketCount = 38
+)
+
+// histogram keeps counts of values in buckets that are spaced
+// out in powers of 2: 0-1, 2-3, 4-7...
+// histogram implements timeseries.Observable
+type histogram struct {
+ sum int64 // running total of measurements
+ sumOfSquares float64 // square of running total
+ buckets []int64 // bucketed values for histogram
+ value int // holds a single value as an optimization
+ valueCount int64 // number of values recorded for single value
+}
+
+// AddMeasurement records a value measurement observation to the histogram.
+func (h *histogram) addMeasurement(value int64) {
+ // TODO: assert invariant
+ h.sum += value
+ h.sumOfSquares += float64(value) * float64(value)
+
+ bucketIndex := getBucket(value)
+
+ if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) {
+ h.value = bucketIndex
+ h.valueCount++
+ } else {
+ h.allocateBuckets()
+ h.buckets[bucketIndex]++
+ }
+}
+
+func (h *histogram) allocateBuckets() {
+ if h.buckets == nil {
+ h.buckets = make([]int64, bucketCount)
+ h.buckets[h.value] = h.valueCount
+ h.value = 0
+ h.valueCount = -1
+ }
+}
+
+func log2(i int64) int {
+ n := 0
+ for ; i >= 0x100; i >>= 8 {
+ n += 8
+ }
+ for ; i > 0; i >>= 1 {
+ n += 1
+ }
+ return n
+}
+
+func getBucket(i int64) (index int) {
+ index = log2(i) - 1
+ if index < 0 {
+ index = 0
+ }
+ if index >= bucketCount {
+ index = bucketCount - 1
+ }
+ return
+}
+
+// Total returns the number of recorded observations.
+func (h *histogram) total() (total int64) {
+ if h.valueCount >= 0 {
+ total = h.valueCount
+ }
+ for _, val := range h.buckets {
+ total += int64(val)
+ }
+ return
+}
+
+// Average returns the average value of recorded observations.
+func (h *histogram) average() float64 {
+ t := h.total()
+ if t == 0 {
+ return 0
+ }
+ return float64(h.sum) / float64(t)
+}
+
+// Variance returns the variance of recorded observations.
+func (h *histogram) variance() float64 {
+ t := float64(h.total())
+ if t == 0 {
+ return 0
+ }
+ s := float64(h.sum) / t
+ return h.sumOfSquares/t - s*s
+}
+
+// StandardDeviation returns the standard deviation of recorded observations.
+func (h *histogram) standardDeviation() float64 {
+ return math.Sqrt(h.variance())
+}
+
+// PercentileBoundary estimates the value that the given fraction of recorded
+// observations are less than.
+func (h *histogram) percentileBoundary(percentile float64) int64 {
+ total := h.total()
+
+ // Corner cases (make sure result is strictly less than Total())
+ if total == 0 {
+ return 0
+ } else if total == 1 {
+ return int64(h.average())
+ }
+
+ percentOfTotal := round(float64(total) * percentile)
+ var runningTotal int64
+
+ for i := range h.buckets {
+ value := h.buckets[i]
+ runningTotal += value
+ if runningTotal == percentOfTotal {
+ // We hit an exact bucket boundary. If the next bucket has data, it is a
+ // good estimate of the value. If the bucket is empty, we interpolate the
+ // midpoint between the next bucket's boundary and the next non-zero
+ // bucket. If the remaining buckets are all empty, then we use the
+ // boundary for the next bucket as the estimate.
+ j := uint8(i + 1)
+ min := bucketBoundary(j)
+ if runningTotal < total {
+ for h.buckets[j] == 0 {
+ j++
+ }
+ }
+ max := bucketBoundary(j)
+ return min + round(float64(max-min)/2)
+ } else if runningTotal > percentOfTotal {
+ // The value is in this bucket. Interpolate the value.
+ delta := runningTotal - percentOfTotal
+ percentBucket := float64(value-delta) / float64(value)
+ bucketMin := bucketBoundary(uint8(i))
+ nextBucketMin := bucketBoundary(uint8(i + 1))
+ bucketSize := nextBucketMin - bucketMin
+ return bucketMin + round(percentBucket*float64(bucketSize))
+ }
+ }
+ return bucketBoundary(bucketCount - 1)
+}
+
+// Median returns the estimated median of the observed values.
+func (h *histogram) median() int64 {
+ return h.percentileBoundary(0.5)
+}
+
+// Add adds other to h.
+func (h *histogram) Add(other timeseries.Observable) {
+ o := other.(*histogram)
+ if o.valueCount == 0 {
+ // Other histogram is empty
+ } else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value {
+ // Both have a single bucketed value, aggregate them
+ h.valueCount += o.valueCount
+ } else {
+ // Two different values necessitate buckets in this histogram
+ h.allocateBuckets()
+ if o.valueCount >= 0 {
+ h.buckets[o.value] += o.valueCount
+ } else {
+ for i := range h.buckets {
+ h.buckets[i] += o.buckets[i]
+ }
+ }
+ }
+ h.sumOfSquares += o.sumOfSquares
+ h.sum += o.sum
+}
+
+// Clear resets the histogram to an empty state, removing all observed values.
+func (h *histogram) Clear() {
+ h.buckets = nil
+ h.value = 0
+ h.valueCount = 0
+ h.sum = 0
+ h.sumOfSquares = 0
+}
+
+// CopyFrom copies from other, which must be a *histogram, into h.
+func (h *histogram) CopyFrom(other timeseries.Observable) {
+ o := other.(*histogram)
+ if o.valueCount == -1 {
+ h.allocateBuckets()
+ copy(h.buckets, o.buckets)
+ }
+ h.sum = o.sum
+ h.sumOfSquares = o.sumOfSquares
+ h.value = o.value
+ h.valueCount = o.valueCount
+}
+
+// Multiply scales the histogram by the specified ratio.
+func (h *histogram) Multiply(ratio float64) {
+ if h.valueCount == -1 {
+ for i := range h.buckets {
+ h.buckets[i] = int64(float64(h.buckets[i]) * ratio)
+ }
+ } else {
+ h.valueCount = int64(float64(h.valueCount) * ratio)
+ }
+ h.sum = int64(float64(h.sum) * ratio)
+ h.sumOfSquares = h.sumOfSquares * ratio
+}
+
+// New creates a new histogram.
+func (h *histogram) New() timeseries.Observable {
+ r := new(histogram)
+ r.Clear()
+ return r
+}
+
+func (h *histogram) String() string {
+ return fmt.Sprintf("%d, %f, %d, %d, %v",
+ h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets)
+}
+
+// round returns the closest int64 to the argument
+func round(in float64) int64 {
+ return int64(math.Floor(in + 0.5))
+}
+
+// bucketBoundary returns the first value in the bucket.
+func bucketBoundary(bucket uint8) int64 {
+ if bucket == 0 {
+ return 0
+ }
+ return 1 << bucket
+}
+
+// bucketData holds data about a specific bucket for use in distTmpl.
+type bucketData struct {
+ Lower, Upper int64
+ N int64
+ Pct, CumulativePct float64
+ GraphWidth int
+}
+
+// data holds data about a Distribution for use in distTmpl.
+type data struct {
+ Buckets []*bucketData
+ Count, Median int64
+ Mean, StandardDeviation float64
+}
+
+// maxHTMLBarWidth is the maximum width of the HTML bar for visualizing buckets.
+const maxHTMLBarWidth = 350.0
+
+// newData returns data representing h for use in distTmpl.
+func (h *histogram) newData() *data {
+ // Force the allocation of buckets to simplify the rendering implementation
+ h.allocateBuckets()
+ // We scale the bars on the right so that the largest bar is
+ // maxHTMLBarWidth pixels in width.
+ maxBucket := int64(0)
+ for _, n := range h.buckets {
+ if n > maxBucket {
+ maxBucket = n
+ }
+ }
+ total := h.total()
+ barsizeMult := maxHTMLBarWidth / float64(maxBucket)
+ var pctMult float64
+ if total == 0 {
+ pctMult = 1.0
+ } else {
+ pctMult = 100.0 / float64(total)
+ }
+
+ buckets := make([]*bucketData, len(h.buckets))
+ runningTotal := int64(0)
+ for i, n := range h.buckets {
+ if n == 0 {
+ continue
+ }
+ runningTotal += n
+ var upperBound int64
+ if i < bucketCount-1 {
+ upperBound = bucketBoundary(uint8(i + 1))
+ } else {
+ upperBound = math.MaxInt64
+ }
+ buckets[i] = &bucketData{
+ Lower: bucketBoundary(uint8(i)),
+ Upper: upperBound,
+ N: n,
+ Pct: float64(n) * pctMult,
+ CumulativePct: float64(runningTotal) * pctMult,
+ GraphWidth: int(float64(n) * barsizeMult),
+ }
+ }
+ return &data{
+ Buckets: buckets,
+ Count: total,
+ Median: h.median(),
+ Mean: h.average(),
+ StandardDeviation: h.standardDeviation(),
+ }
+}
+
+func (h *histogram) html() template.HTML {
+ buf := new(bytes.Buffer)
+ if err := distTmpl.Execute(buf, h.newData()); err != nil {
+ buf.Reset()
+ log.Printf("net/trace: couldn't execute template: %v", err)
+ }
+ return template.HTML(buf.String())
+}
+
+// Input: data
+var distTmpl = template.Must(template.New("distTmpl").Parse(`
+<table>
+<tr>
+ <td style="padding:0.25em">Count: {{.Count}}</td>
+ <td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
+ <td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
+ <td style="padding:0.25em">Median: {{.Median}}</td>
+</tr>
+</table>
+<hr>
+<table>
+{{range $b := .Buckets}}
+{{if $b}}
+ <tr>
+ <td style="padding:0 0 0 0.25em">[</td>
+ <td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
+ <td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
+ <td style="text-align:right;padding:0 0.25em">{{.N}}</td>
+ <td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
+ <td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
+ <td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>
+ </tr>
+{{end}}
+{{end}}
+</table>
+`))
diff --git a/trace/histogram_test.go b/trace/histogram_test.go
new file mode 100644
index 0000000..d384b93
--- /dev/null
+++ b/trace/histogram_test.go
@@ -0,0 +1,325 @@
+// Copyright 2015 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.
+
+package trace
+
+import (
+ "math"
+ "testing"
+)
+
+type sumTest struct {
+ value int64
+ sum int64
+ sumOfSquares float64
+ total int64
+}
+
+var sumTests = []sumTest{
+ {100, 100, 10000, 1},
+ {50, 150, 12500, 2},
+ {50, 200, 15000, 3},
+ {50, 250, 17500, 4},
+}
+
+type bucketingTest struct {
+ in int64
+ log int
+ bucket int
+}
+
+var bucketingTests = []bucketingTest{
+ {0, 0, 0},
+ {1, 1, 0},
+ {2, 2, 1},
+ {3, 2, 1},
+ {4, 3, 2},
+ {1000, 10, 9},
+ {1023, 10, 9},
+ {1024, 11, 10},
+ {1000000, 20, 19},
+}
+
+type multiplyTest struct {
+ in int64
+ ratio float64
+ expectedSum int64
+ expectedTotal int64
+ expectedSumOfSquares float64
+}
+
+var multiplyTests = []multiplyTest{
+ {15, 2.5, 37, 2, 562.5},
+ {128, 4.6, 758, 13, 77953.9},
+}
+
+type percentileTest struct {
+ fraction float64
+ expected int64
+}
+
+var percentileTests = []percentileTest{
+ {0.25, 48},
+ {0.5, 96},
+ {0.6, 109},
+ {0.75, 128},
+ {0.90, 205},
+ {0.95, 230},
+ {0.99, 256},
+}
+
+func TestSum(t *testing.T) {
+ var h histogram
+
+ for _, test := range sumTests {
+ h.addMeasurement(test.value)
+ sum := h.sum
+ if sum != test.sum {
+ t.Errorf("h.Sum = %v WANT: %v", sum, test.sum)
+ }
+
+ sumOfSquares := h.sumOfSquares
+ if sumOfSquares != test.sumOfSquares {
+ t.Errorf("h.SumOfSquares = %v WANT: %v", sumOfSquares, test.sumOfSquares)
+ }
+
+ total := h.total()
+ if total != test.total {
+ t.Errorf("h.Total = %v WANT: %v", total, test.total)
+ }
+ }
+}
+
+func TestMultiply(t *testing.T) {
+ var h histogram
+ for i, test := range multiplyTests {
+ h.addMeasurement(test.in)
+ h.Multiply(test.ratio)
+ if h.sum != test.expectedSum {
+ t.Errorf("#%v: h.sum = %v WANT: %v", i, h.sum, test.expectedSum)
+ }
+ if h.total() != test.expectedTotal {
+ t.Errorf("#%v: h.total = %v WANT: %v", i, h.total(), test.expectedTotal)
+ }
+ if h.sumOfSquares != test.expectedSumOfSquares {
+ t.Errorf("#%v: h.SumOfSquares = %v WANT: %v", i, test.expectedSumOfSquares, h.sumOfSquares)
+ }
+ }
+}
+
+func TestBucketingFunctions(t *testing.T) {
+ for _, test := range bucketingTests {
+ log := log2(test.in)
+ if log != test.log {
+ t.Errorf("log2 = %v WANT: %v", log, test.log)
+ }
+
+ bucket := getBucket(test.in)
+ if bucket != test.bucket {
+ t.Errorf("getBucket = %v WANT: %v", bucket, test.bucket)
+ }
+ }
+}
+
+func TestAverage(t *testing.T) {
+ a := new(histogram)
+ average := a.average()
+ if average != 0 {
+ t.Errorf("Average of empty histogram was %v WANT: 0", average)
+ }
+
+ a.addMeasurement(1)
+ a.addMeasurement(1)
+ a.addMeasurement(3)
+ const expected = float64(5) / float64(3)
+ average = a.average()
+
+ if !isApproximate(average, expected) {
+ t.Errorf("Average = %g WANT: %v", average, expected)
+ }
+}
+
+func TestStandardDeviation(t *testing.T) {
+ a := new(histogram)
+ add(a, 10, 1<<4)
+ add(a, 10, 1<<5)
+ add(a, 10, 1<<6)
+ stdDev := a.standardDeviation()
+ const expected = 19.95
+
+ if !isApproximate(stdDev, expected) {
+ t.Errorf("StandardDeviation = %v WANT: %v", stdDev, expected)
+ }
+
+ // No values
+ a = new(histogram)
+ stdDev = a.standardDeviation()
+
+ if !isApproximate(stdDev, 0) {
+ t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
+ }
+
+ add(a, 1, 1<<4)
+ if !isApproximate(stdDev, 0) {
+ t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
+ }
+
+ add(a, 10, 1<<4)
+ if !isApproximate(stdDev, 0) {
+ t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
+ }
+}
+
+func TestPercentileBoundary(t *testing.T) {
+ a := new(histogram)
+ add(a, 5, 1<<4)
+ add(a, 10, 1<<6)
+ add(a, 5, 1<<7)
+
+ for _, test := range percentileTests {
+ percentile := a.percentileBoundary(test.fraction)
+ if percentile != test.expected {
+ t.Errorf("h.PercentileBoundary (fraction=%v) = %v WANT: %v", test.fraction, percentile, test.expected)
+ }
+ }
+}
+
+func TestCopyFrom(t *testing.T) {
+ a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
+ b := histogram{6, 36, []int64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, 5, -1}
+
+ a.CopyFrom(&b)
+
+ if a.String() != b.String() {
+ t.Errorf("a.String = %s WANT: %s", a.String(), b.String())
+ }
+}
+
+func TestClear(t *testing.T) {
+ a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
+
+ a.Clear()
+
+ expected := "0, 0.000000, 0, 0, []"
+ if a.String() != expected {
+ t.Errorf("a.String = %s WANT %s", a.String(), expected)
+ }
+}
+
+func TestNew(t *testing.T) {
+ a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
+ b := a.New()
+
+ expected := "0, 0.000000, 0, 0, []"
+ if b.(*histogram).String() != expected {
+ t.Errorf("b.(*histogram).String = %s WANT: %s", b.(*histogram).String(), expected)
+ }
+}
+
+func TestAdd(t *testing.T) {
+ // The tests here depend on the associativity of addMeasurement and Add.
+ // Add empty observation
+ a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
+ b := a.New()
+
+ expected := a.String()
+ a.Add(b)
+ if a.String() != expected {
+ t.Errorf("a.String = %s WANT: %s", a.String(), expected)
+ }
+
+ // Add same bucketed value, no new buckets
+ c := new(histogram)
+ d := new(histogram)
+ e := new(histogram)
+ c.addMeasurement(12)
+ d.addMeasurement(11)
+ e.addMeasurement(12)
+ e.addMeasurement(11)
+ c.Add(d)
+ if c.String() != e.String() {
+ t.Errorf("c.String = %s WANT: %s", c.String(), e.String())
+ }
+
+ // Add bucketed values
+ f := new(histogram)
+ g := new(histogram)
+ h := new(histogram)
+ f.addMeasurement(4)
+ f.addMeasurement(12)
+ f.addMeasurement(100)
+ g.addMeasurement(18)
+ g.addMeasurement(36)
+ g.addMeasurement(255)
+ h.addMeasurement(4)
+ h.addMeasurement(12)
+ h.addMeasurement(100)
+ h.addMeasurement(18)
+ h.addMeasurement(36)
+ h.addMeasurement(255)
+ f.Add(g)
+ if f.String() != h.String() {
+ t.Errorf("f.String = %q WANT: %q", f.String(), h.String())
+ }
+
+ // add buckets to no buckets
+ i := new(histogram)
+ j := new(histogram)
+ k := new(histogram)
+ j.addMeasurement(18)
+ j.addMeasurement(36)
+ j.addMeasurement(255)
+ k.addMeasurement(18)
+ k.addMeasurement(36)
+ k.addMeasurement(255)
+ i.Add(j)
+ if i.String() != k.String() {
+ t.Errorf("i.String = %q WANT: %q", i.String(), k.String())
+ }
+
+ // add buckets to single value (no overlap)
+ l := new(histogram)
+ m := new(histogram)
+ n := new(histogram)
+ l.addMeasurement(0)
+ m.addMeasurement(18)
+ m.addMeasurement(36)
+ m.addMeasurement(255)
+ n.addMeasurement(0)
+ n.addMeasurement(18)
+ n.addMeasurement(36)
+ n.addMeasurement(255)
+ l.Add(m)
+ if l.String() != n.String() {
+ t.Errorf("l.String = %q WANT: %q", l.String(), n.String())
+ }
+
+ // mixed order
+ o := new(histogram)
+ p := new(histogram)
+ o.addMeasurement(0)
+ o.addMeasurement(2)
+ o.addMeasurement(0)
+ p.addMeasurement(0)
+ p.addMeasurement(0)
+ p.addMeasurement(2)
+ if o.String() != p.String() {
+ t.Errorf("o.String = %q WANT: %q", o.String(), p.String())
+ }
+}
+
+func add(h *histogram, times int, val int64) {
+ for i := 0; i < times; i++ {
+ h.addMeasurement(val)
+ }
+}
+
+func isApproximate(x, y float64) bool {
+ return math.Abs(x-y) < 1e-2
+}
diff --git a/trace/trace.go b/trace/trace.go
new file mode 100644
index 0000000..fab5e9e
--- /dev/null
+++ b/trace/trace.go
@@ -0,0 +1,1052 @@
+// Copyright 2015 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.
+
+/*
+Package trace implements tracing of requests and long-lived objects.
+It exports HTTP interfaces on /debug/requests and /debug/events.
+
+A trace.Trace provides tracing for short-lived objects, usually requests.
+A request handler might be implemented like this:
+
+ func fooHandler(w http.ResponseWriter, req *http.Request) {
+ tr := trace.New("mypkg.Foo", req.URL.Path)
+ defer tr.Finish()
+ ...
+ tr.LazyPrintf("some event %q happened", str)
+ ...
+ if err := somethingImportant(); err != nil {
+ tr.LazyPrintf("somethingImportant failed: %v", err)
+ tr.SetError()
+ }
+ }
+
+The /debug/requests HTTP endpoint organizes the traces by family,
+errors, and duration. It also provides histogram of request duration
+for each family.
+
+A trace.EventLog provides tracing for long-lived objects, such as RPC
+connections.
+
+ // A Fetcher fetches URL paths for a single domain.
+ type Fetcher struct {
+ domain string
+ events *trace.EventLog
+ }
+
+ func NewFetcher(domain string) *Fetcher {
+ return &Fetcher{
+ domain,
+ trace.NewEventLog("mypkg.Fetcher", domain),
+ }
+ }
+
+ func (f *Fetcher) Fetch(path string) (string, error) {
+ resp, err := http.Get("http://"+domain+"/"+path)
+ if err != nil {
+ f.events.Errorf("Get(%q) = %v", path, err)
+ return
+ }
+ f.events.Printf("Get(%q) = %s", path, resp.Code)
+ ...
+ }
+
+ func (f *Fetcher) Close() error {
+ f.events.Finish()
+ }
+
+The /debug/events HTTP endpoint organizes the event logs by family and
+by time since the last error. The expanded view displays recent log
+entries and the log's call stack.
+*/
+package trace // import "golang.org/x/net/trace"
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "runtime"
+ "sort"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/internal/timeseries"
+)
+
+// DebugUseAfterFinish controls whether to debug uses of Trace values after finishing.
+// FOR DEBUGGING ONLY. This will slow down the program.
+var DebugUseAfterFinish = false
+
+// AuthRequest determines whether a specific request is permitted to load the /debug/requests page.
+// It returns two bools; the first indicates whether the page may be viewed at all,
+// and the second indicates whether sensitive events will be shown.
+//
+// AuthRequest may be replaced by a program to customise its authorisation requirements.
+//
+// The default AuthRequest function returns (true, true) iff the request comes from localhost/127.0.0.1/[::1].
+var AuthRequest = func(req *http.Request) (any, sensitive bool) {
+ host, _, err := net.SplitHostPort(req.RemoteAddr)
+ switch {
+ case err != nil: // Badly formed address; fail closed.
+ return false, false
+ case host == "localhost" || host == "127.0.0.1" || host == "::1":
+ return true, true
+ default:
+ return false, false
+ }
+}
+
+func init() {
+ http.HandleFunc("/debug/requests", func(w http.ResponseWriter, req *http.Request) {
+ any, sensitive := AuthRequest(req)
+ if !any {
+ http.Error(w, "not allowed", http.StatusUnauthorized)
+ return
+ }
+ render(w, req, sensitive)
+ })
+ http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) {
+ any, sensitive := AuthRequest(req)
+ if !any {
+ http.Error(w, "not allowed", http.StatusUnauthorized)
+ return
+ }
+ renderEvents(w, req, sensitive)
+ })
+}
+
+// render renders the HTML page.
+// req may be nil.
+func render(w io.Writer, req *http.Request, sensitive bool) {
+ data := &struct {
+ Families []string
+ ActiveTraceCount map[string]int
+ CompletedTraces map[string]*family
+
+ // Set when a bucket has been selected.
+ Traces traceList
+ Family string
+ Bucket int
+ Expanded bool
+ Traced bool
+ Active bool
+ ShowSensitive bool // whether to show sensitive events
+
+ Histogram template.HTML
+ HistogramWindow string // e.g. "last minute", "last hour", "all time"
+
+ // If non-zero, the set of traces is a partial set,
+ // and this is the total number.
+ Total int
+ }{
+ CompletedTraces: completedTraces,
+ }
+
+ data.ShowSensitive = sensitive
+ if req != nil {
+ // Allow show_sensitive=0 to force hiding of sensitive data for testing.
+ // This only goes one way; you can't use show_sensitive=1 to see things.
+ if req.FormValue("show_sensitive") == "0" {
+ data.ShowSensitive = false
+ }
+
+ if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
+ data.Expanded = exp
+ }
+ if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil {
+ data.Traced = exp
+ }
+ }
+
+ completedMu.RLock()
+ data.Families = make([]string, 0, len(completedTraces))
+ for fam, _ := range completedTraces {
+ data.Families = append(data.Families, fam)
+ }
+ completedMu.RUnlock()
+ sort.Strings(data.Families)
+
+ // We are careful here to minimize the time spent locking activeMu,
+ // since that lock is required every time an RPC starts and finishes.
+ data.ActiveTraceCount = make(map[string]int, len(data.Families))
+ activeMu.RLock()
+ for fam, s := range activeTraces {
+ data.ActiveTraceCount[fam] = s.Len()
+ }
+ activeMu.RUnlock()
+
+ var ok bool
+ data.Family, data.Bucket, ok = parseArgs(req)
+ switch {
+ case !ok:
+ // No-op
+ case data.Bucket == -1:
+ data.Active = true
+ n := data.ActiveTraceCount[data.Family]
+ data.Traces = getActiveTraces(data.Family)
+ if len(data.Traces) < n {
+ data.Total = n
+ }
+ case data.Bucket < bucketsPerFamily:
+ if b := lookupBucket(data.Family, data.Bucket); b != nil {
+ data.Traces = b.Copy(data.Traced)
+ }
+ default:
+ if f := getFamily(data.Family, false); f != nil {
+ var obs timeseries.Observable
+ f.LatencyMu.RLock()
+ switch o := data.Bucket - bucketsPerFamily; o {
+ case 0:
+ obs = f.Latency.Minute()
+ data.HistogramWindow = "last minute"
+ case 1:
+ obs = f.Latency.Hour()
+ data.HistogramWindow = "last hour"
+ case 2:
+ obs = f.Latency.Total()
+ data.HistogramWindow = "all time"
+ }
+ f.LatencyMu.RUnlock()
+ if obs != nil {
+ data.Histogram = obs.(*histogram).html()
+ }
+ }
+ }
+
+ if data.Traces != nil {
+ defer data.Traces.Free()
+ sort.Sort(data.Traces)
+ }
+
+ completedMu.RLock()
+ defer completedMu.RUnlock()
+ if err := pageTmpl.ExecuteTemplate(w, "Page", data); err != nil {
+ log.Printf("net/trace: Failed executing template: %v", err)
+ }
+}
+
+func parseArgs(req *http.Request) (fam string, b int, ok bool) {
+ if req == nil {
+ return "", 0, false
+ }
+ fam, bStr := req.FormValue("fam"), req.FormValue("b")
+ if fam == "" || bStr == "" {
+ return "", 0, false
+ }
+ b, err := strconv.Atoi(bStr)
+ if err != nil || b < -1 {
+ return "", 0, false
+ }
+
+ return fam, b, true
+}
+
+func lookupBucket(fam string, b int) *traceBucket {
+ f := getFamily(fam, false)
+ if f == nil || b < 0 || b >= len(f.Buckets) {
+ return nil
+ }
+ return f.Buckets[b]
+}
+
+type contextKeyT string
+
+var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
+
+// NewContext returns a copy of the parent context
+// and associates it with a Trace.
+func NewContext(ctx context.Context, tr Trace) context.Context {
+ return context.WithValue(ctx, contextKey, tr)
+}
+
+// FromContext returns the Trace bound to the context, if any.
+func FromContext(ctx context.Context) (tr Trace, ok bool) {
+ tr, ok = ctx.Value(contextKey).(Trace)
+ return
+}
+
+// Trace represents an active request.
+type Trace interface {
+ // LazyLog adds x to the event log. It will be evaluated each time the
+ // /debug/requests page is rendered. Any memory referenced by x will be
+ // pinned until the trace is finished and later discarded.
+ LazyLog(x fmt.Stringer, sensitive bool)
+
+ // LazyPrintf evaluates its arguments with fmt.Sprintf each time the
+ // /debug/requests page is rendered. Any memory referenced by a will be
+ // pinned until the trace is finished and later discarded.
+ LazyPrintf(format string, a ...interface{})
+
+ // SetError declares that this trace resulted in an error.
+ SetError()
+
+ // SetRecycler sets a recycler for the trace.
+ // f will be called for each event passed to LazyLog at a time when
+ // it is no longer required, whether while the trace is still active
+ // and the event is discarded, or when a completed trace is discarded.
+ SetRecycler(f func(interface{}))
+
+ // SetTraceInfo sets the trace info for the trace.
+ // This is currently unused.
+ SetTraceInfo(traceID, spanID uint64)
+
+ // SetMaxEvents sets the maximum number of events that will be stored
+ // in the trace. This has no effect if any events have already been
+ // added to the trace.
+ SetMaxEvents(m int)
+
+ // Finish declares that this trace is complete.
+ // The trace should not be used after calling this method.
+ Finish()
+}
+
+type lazySprintf struct {
+ format string
+ a []interface{}
+}
+
+func (l *lazySprintf) String() string {
+ return fmt.Sprintf(l.format, l.a...)
+}
+
+// New returns a new Trace with the specified family and title.
+func New(family, title string) Trace {
+ tr := newTrace()
+ tr.ref()
+ tr.Family, tr.Title = family, title
+ tr.Start = time.Now()
+ tr.events = make([]event, 0, maxEventsPerTrace)
+
+ activeMu.RLock()
+ s := activeTraces[tr.Family]
+ activeMu.RUnlock()
+ if s == nil {
+ activeMu.Lock()
+ s = activeTraces[tr.Family] // check again
+ if s == nil {
+ s = new(traceSet)
+ activeTraces[tr.Family] = s
+ }
+ activeMu.Unlock()
+ }
+ s.Add(tr)
+
+ // Trigger allocation of the completed trace structure for this family.
+ // This will cause the family to be present in the request page during
+ // the first trace of this family. We don't care about the return value,
+ // nor is there any need for this to run inline, so we execute it in its
+ // own goroutine, but only if the family isn't allocated yet.
+ completedMu.RLock()
+ if _, ok := completedTraces[tr.Family]; !ok {
+ go allocFamily(tr.Family)
+ }
+ completedMu.RUnlock()
+
+ return tr
+}
+
+func (tr *trace) Finish() {
+ tr.Elapsed = time.Now().Sub(tr.Start)
+ if DebugUseAfterFinish {
+ buf := make([]byte, 4<<10) // 4 KB should be enough
+ n := runtime.Stack(buf, false)
+ tr.finishStack = buf[:n]
+ }
+
+ activeMu.RLock()
+ m := activeTraces[tr.Family]
+ activeMu.RUnlock()
+ m.Remove(tr)
+
+ f := getFamily(tr.Family, true)
+ for _, b := range f.Buckets {
+ if b.Cond.match(tr) {
+ b.Add(tr)
+ }
+ }
+ // Add a sample of elapsed time as microseconds to the family's timeseries
+ h := new(histogram)
+ h.addMeasurement(tr.Elapsed.Nanoseconds() / 1e3)
+ f.LatencyMu.Lock()
+ f.Latency.Add(h)
+ f.LatencyMu.Unlock()
+
+ tr.unref() // matches ref in New
+}
+
+const (
+ bucketsPerFamily = 9
+ tracesPerBucket = 10
+ maxActiveTraces = 20 // Maximum number of active traces to show.
+ maxEventsPerTrace = 10
+ numHistogramBuckets = 38
+)
+
+var (
+ // The active traces.
+ activeMu sync.RWMutex
+ activeTraces = make(map[string]*traceSet) // family -> traces
+
+ // Families of completed traces.
+ completedMu sync.RWMutex
+ completedTraces = make(map[string]*family) // family -> traces
+)
+
+type traceSet struct {
+ mu sync.RWMutex
+ m map[*trace]bool
+
+ // We could avoid the entire map scan in FirstN by having a slice of all the traces
+ // ordered by start time, and an index into that from the trace struct, with a periodic
+ // repack of the slice after enough traces finish; we could also use a skip list or similar.
+ // However, that would shift some of the expense from /debug/requests time to RPC time,
+ // which is probably the wrong trade-off.
+}
+
+func (ts *traceSet) Len() int {
+ ts.mu.RLock()
+ defer ts.mu.RUnlock()
+ return len(ts.m)
+}
+
+func (ts *traceSet) Add(tr *trace) {
+ ts.mu.Lock()
+ if ts.m == nil {
+ ts.m = make(map[*trace]bool)
+ }
+ ts.m[tr] = true
+ ts.mu.Unlock()
+}
+
+func (ts *traceSet) Remove(tr *trace) {
+ ts.mu.Lock()
+ delete(ts.m, tr)
+ ts.mu.Unlock()
+}
+
+// FirstN returns the first n traces ordered by time.
+func (ts *traceSet) FirstN(n int) traceList {
+ ts.mu.RLock()
+ defer ts.mu.RUnlock()
+
+ if n > len(ts.m) {
+ n = len(ts.m)
+ }
+ trl := make(traceList, 0, n)
+
+ // Fast path for when no selectivity is needed.
+ if n == len(ts.m) {
+ for tr := range ts.m {
+ tr.ref()
+ trl = append(trl, tr)
+ }
+ sort.Sort(trl)
+ return trl
+ }
+
+ // Pick the oldest n traces.
+ // This is inefficient. See the comment in the traceSet struct.
+ for tr := range ts.m {
+ // Put the first n traces into trl in the order they occur.
+ // When we have n, sort trl, and thereafter maintain its order.
+ if len(trl) < n {
+ tr.ref()
+ trl = append(trl, tr)
+ if len(trl) == n {
+ // This is guaranteed to happen exactly once during this loop.
+ sort.Sort(trl)
+ }
+ continue
+ }
+ if tr.Start.After(trl[n-1].Start) {
+ continue
+ }
+
+ // Find where to insert this one.
+ tr.ref()
+ i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) })
+ trl[n-1].unref()
+ copy(trl[i+1:], trl[i:])
+ trl[i] = tr
+ }
+
+ return trl
+}
+
+func getActiveTraces(fam string) traceList {
+ activeMu.RLock()
+ s := activeTraces[fam]
+ activeMu.RUnlock()
+ if s == nil {
+ return nil
+ }
+ return s.FirstN(maxActiveTraces)
+}
+
+func getFamily(fam string, allocNew bool) *family {
+ completedMu.RLock()
+ f := completedTraces[fam]
+ completedMu.RUnlock()
+ if f == nil && allocNew {
+ f = allocFamily(fam)
+ }
+ return f
+}
+
+func allocFamily(fam string) *family {
+ completedMu.Lock()
+ defer completedMu.Unlock()
+ f := completedTraces[fam]
+ if f == nil {
+ f = newFamily()
+ completedTraces[fam] = f
+ }
+ return f
+}
+
+// family represents a set of trace buckets and associated latency information.
+type family struct {
+ // traces may occur in multiple buckets.
+ Buckets [bucketsPerFamily]*traceBucket
+
+ // latency time series
+ LatencyMu sync.RWMutex
+ Latency *timeseries.MinuteHourSeries
+}
+
+func newFamily() *family {
+ return &family{
+ Buckets: [bucketsPerFamily]*traceBucket{
+ {Cond: minCond(0)},
+ {Cond: minCond(50 * time.Millisecond)},
+ {Cond: minCond(100 * time.Millisecond)},
+ {Cond: minCond(200 * time.Millisecond)},
+ {Cond: minCond(500 * time.Millisecond)},
+ {Cond: minCond(1 * time.Second)},
+ {Cond: minCond(10 * time.Second)},
+ {Cond: minCond(100 * time.Second)},
+ {Cond: errorCond{}},
+ },
+ Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }),
+ }
+}
+
+// traceBucket represents a size-capped bucket of historic traces,
+// along with a condition for a trace to belong to the bucket.
+type traceBucket struct {
+ Cond cond
+
+ // Ring buffer implementation of a fixed-size FIFO queue.
+ mu sync.RWMutex
+ buf [tracesPerBucket]*trace
+ start int // < tracesPerBucket
+ length int // <= tracesPerBucket
+}
+
+func (b *traceBucket) Add(tr *trace) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ i := b.start + b.length
+ if i >= tracesPerBucket {
+ i -= tracesPerBucket
+ }
+ if b.length == tracesPerBucket {
+ // "Remove" an element from the bucket.
+ b.buf[i].unref()
+ b.start++
+ if b.start == tracesPerBucket {
+ b.start = 0
+ }
+ }
+ b.buf[i] = tr
+ if b.length < tracesPerBucket {
+ b.length++
+ }
+ tr.ref()
+}
+
+// Copy returns a copy of the traces in the bucket.
+// If tracedOnly is true, only the traces with trace information will be returned.
+// The logs will be ref'd before returning; the caller should call
+// the Free method when it is done with them.
+// TODO(dsymonds): keep track of traced requests in separate buckets.
+func (b *traceBucket) Copy(tracedOnly bool) traceList {
+ b.mu.RLock()
+ defer b.mu.RUnlock()
+
+ trl := make(traceList, 0, b.length)
+ for i, x := 0, b.start; i < b.length; i++ {
+ tr := b.buf[x]
+ if !tracedOnly || tr.spanID != 0 {
+ tr.ref()
+ trl = append(trl, tr)
+ }
+ x++
+ if x == b.length {
+ x = 0
+ }
+ }
+ return trl
+}
+
+func (b *traceBucket) Empty() bool {
+ b.mu.RLock()
+ defer b.mu.RUnlock()
+ return b.length == 0
+}
+
+// cond represents a condition on a trace.
+type cond interface {
+ match(t *trace) bool
+ String() string
+}
+
+type minCond time.Duration
+
+func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) }
+func (m minCond) String() string { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) }
+
+type errorCond struct{}
+
+func (e errorCond) match(t *trace) bool { return t.IsError }
+func (e errorCond) String() string { return "errors" }
+
+type traceList []*trace
+
+// Free calls unref on each element of the list.
+func (trl traceList) Free() {
+ for _, t := range trl {
+ t.unref()
+ }
+}
+
+// traceList may be sorted in reverse chronological order.
+func (trl traceList) Len() int { return len(trl) }
+func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) }
+func (trl traceList) Swap(i, j int) { trl[i], trl[j] = trl[j], trl[i] }
+
+// An event is a timestamped log entry in a trace.
+type event struct {
+ When time.Time
+ Elapsed time.Duration // since previous event in trace
+ NewDay bool // whether this event is on a different day to the previous event
+ Recyclable bool // whether this event was passed via LazyLog
+ What interface{} // string or fmt.Stringer
+ Sensitive bool // whether this event contains sensitive information
+}
+
+// WhenString returns a string representation of the elapsed time of the event.
+// It will include the date if midnight was crossed.
+func (e event) WhenString() string {
+ if e.NewDay {
+ return e.When.Format("2006/01/02 15:04:05.000000")
+ }
+ return e.When.Format("15:04:05.000000")
+}
+
+// discarded represents a number of discarded events.
+// It is stored as *discarded to make it easier to update in-place.
+type discarded int
+
+func (d *discarded) String() string {
+ return fmt.Sprintf("(%d events discarded)", int(*d))
+}
+
+// trace represents an active or complete request,
+// either sent or received by this program.
+type trace struct {
+ // Family is the top-level grouping of traces to which this belongs.
+ Family string
+
+ // Title is the title of this trace.
+ Title string
+
+ // Timing information.
+ Start time.Time
+ Elapsed time.Duration // zero while active
+
+ // Trace information if non-zero.
+ traceID uint64
+ spanID uint64
+
+ // Whether this trace resulted in an error.
+ IsError bool
+
+ // Append-only sequence of events (modulo discards).
+ mu sync.RWMutex
+ events []event
+
+ refs int32 // how many buckets this is in
+ recycler func(interface{})
+ disc discarded // scratch space to avoid allocation
+
+ finishStack []byte // where finish was called, if DebugUseAfterFinish is set
+}
+
+func (tr *trace) reset() {
+ // Clear all but the mutex. Mutexes may not be copied, even when unlocked.
+ tr.Family = ""
+ tr.Title = ""
+ tr.Start = time.Time{}
+ tr.Elapsed = 0
+ tr.traceID = 0
+ tr.spanID = 0
+ tr.IsError = false
+ tr.events = nil
+ tr.refs = 0
+ tr.recycler = nil
+ tr.disc = 0
+ tr.finishStack = nil
+}
+
+// delta returns the elapsed time since the last event or the trace start,
+// and whether it spans midnight.
+// L >= tr.mu
+func (tr *trace) delta(t time.Time) (time.Duration, bool) {
+ if len(tr.events) == 0 {
+ return t.Sub(tr.Start), false
+ }
+ prev := tr.events[len(tr.events)-1].When
+ return t.Sub(prev), prev.Day() != t.Day()
+}
+
+func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
+ if DebugUseAfterFinish && tr.finishStack != nil {
+ buf := make([]byte, 4<<10) // 4 KB should be enough
+ n := runtime.Stack(buf, false)
+ log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n])
+ }
+
+ /*
+ NOTE TO DEBUGGERS
+
+ If you are here because your program panicked in this code,
+ it is almost definitely the fault of code using this package,
+ and very unlikely to be the fault of this code.
+
+ The most likely scenario is that some code elsewhere is using
+ a requestz.Trace after its Finish method is called.
+ You can temporarily set the DebugUseAfterFinish var
+ to help discover where that is; do not leave that var set,
+ since it makes this package much less efficient.
+ */
+
+ e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive}
+ tr.mu.Lock()
+ e.Elapsed, e.NewDay = tr.delta(e.When)
+ if len(tr.events) < cap(tr.events) {
+ tr.events = append(tr.events, e)
+ } else {
+ // Discard the middle events.
+ di := int((cap(tr.events) - 1) / 2)
+ if d, ok := tr.events[di].What.(*discarded); ok {
+ (*d)++
+ } else {
+ // disc starts at two to count for the event it is replacing,
+ // plus the next one that we are about to drop.
+ tr.disc = 2
+ if tr.recycler != nil && tr.events[di].Recyclable {
+ go tr.recycler(tr.events[di].What)
+ }
+ tr.events[di].What = &tr.disc
+ }
+ // The timestamp of the discarded meta-event should be
+ // the time of the last event it is representing.
+ tr.events[di].When = tr.events[di+1].When
+
+ if tr.recycler != nil && tr.events[di+1].Recyclable {
+ go tr.recycler(tr.events[di+1].What)
+ }
+ copy(tr.events[di+1:], tr.events[di+2:])
+ tr.events[cap(tr.events)-1] = e
+ }
+ tr.mu.Unlock()
+}
+
+func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) {
+ tr.addEvent(x, true, sensitive)
+}
+
+func (tr *trace) LazyPrintf(format string, a ...interface{}) {
+ tr.addEvent(&lazySprintf{format, a}, false, false)
+}
+
+func (tr *trace) SetError() { tr.IsError = true }
+
+func (tr *trace) SetRecycler(f func(interface{})) {
+ tr.recycler = f
+}
+
+func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
+ tr.traceID, tr.spanID = traceID, spanID
+}
+
+func (tr *trace) SetMaxEvents(m int) {
+ // Always keep at least three events: first, discarded count, last.
+ if len(tr.events) == 0 && m > 3 {
+ tr.events = make([]event, 0, m)
+ }
+}
+
+func (tr *trace) ref() {
+ atomic.AddInt32(&tr.refs, 1)
+}
+
+func (tr *trace) unref() {
+ if atomic.AddInt32(&tr.refs, -1) == 0 {
+ if tr.recycler != nil {
+ // freeTrace clears tr, so we hold tr.recycler and tr.events here.
+ go func(f func(interface{}), es []event) {
+ for _, e := range es {
+ if e.Recyclable {
+ f(e.What)
+ }
+ }
+ }(tr.recycler, tr.events)
+ }
+
+ freeTrace(tr)
+ }
+}
+
+func (tr *trace) When() string {
+ return tr.Start.Format("2006/01/02 15:04:05.000000")
+}
+
+func (tr *trace) ElapsedTime() string {
+ t := tr.Elapsed
+ if t == 0 {
+ // Active trace.
+ t = time.Since(tr.Start)
+ }
+ return fmt.Sprintf("%.6f", t.Seconds())
+}
+
+func (tr *trace) Events() []event {
+ tr.mu.RLock()
+ defer tr.mu.RUnlock()
+ return tr.events
+}
+
+var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool?
+
+// newTrace returns a trace ready to use.
+func newTrace() *trace {
+ select {
+ case tr := <-traceFreeList:
+ return tr
+ default:
+ return new(trace)
+ }
+}
+
+// freeTrace adds tr to traceFreeList if there's room.
+// This is non-blocking.
+func freeTrace(tr *trace) {
+ if DebugUseAfterFinish {
+ return // never reuse
+ }
+ tr.reset()
+ select {
+ case traceFreeList <- tr:
+ default:
+ }
+}
+
+func elapsed(d time.Duration) string {
+ b := []byte(fmt.Sprintf("%.6f", d.Seconds()))
+
+ // For subsecond durations, blank all zeros before decimal point,
+ // and all zeros between the decimal point and the first non-zero digit.
+ if d < time.Second {
+ dot := bytes.IndexByte(b, '.')
+ for i := 0; i < dot; i++ {
+ b[i] = ' '
+ }
+ for i := dot + 1; i < len(b); i++ {
+ if b[i] == '0' {
+ b[i] = ' '
+ } else {
+ break
+ }
+ }
+ }
+
+ return string(b)
+}
+
+var pageTmpl = template.Must(template.New("Page").Funcs(template.FuncMap{
+ "elapsed": elapsed,
+ "add": func(a, b int) int { return a + b },
+}).Parse(pageHTML))
+
+const pageHTML = `
+{{template "Prolog" .}}
+{{template "StatusTable" .}}
+{{template "Epilog" .}}
+
+{{define "Prolog"}}
+<html>
+ <head>
+ <title>/debug/requests</title>
+ <style type="text/css">
+ body {
+ font-family: sans-serif;
+ }
+ table#tr-status td.family {
+ padding-right: 2em;
+ }
+ table#tr-status td.active {
+ padding-right: 1em;
+ }
+ table#tr-status td.latency-first {
+ padding-left: 1em;
+ }
+ table#tr-status td.empty {
+ color: #aaa;
+ }
+ table#reqs {
+ margin-top: 1em;
+ }
+ table#reqs tr.first {
+ {{if $.Expanded}}font-weight: bold;{{end}}
+ }
+ table#reqs td {
+ font-family: monospace;
+ }
+ table#reqs td.when {
+ text-align: right;
+ white-space: nowrap;
+ }
+ table#reqs td.elapsed {
+ padding: 0 0.5em;
+ text-align: right;
+ white-space: pre;
+ width: 10em;
+ }
+ address {
+ font-size: smaller;
+ margin-top: 5em;
+ }
+ </style>
+ </head>
+ <body>
+
+<h1>/debug/requests</h1>
+{{end}} {{/* end of Prolog */}}
+
+{{define "StatusTable"}}
+<table id="tr-status">
+ {{range $fam := .Families}}
+ <tr>
+ <td class="family">{{$fam}}</td>
+
+ {{$n := index $.ActiveTraceCount $fam}}
+ <td class="active {{if not $n}}empty{{end}}">
+ {{if $n}}<a href="/debug/requests?fam={{$fam}}&b=-1{{if $.Expanded}}&exp=1{{end}}">{{end}}
+ [{{$n}} active]
+ {{if $n}}</a>{{end}}
+ </td>
+
+ {{$f := index $.CompletedTraces $fam}}
+ {{range $i, $b := $f.Buckets}}
+ {{$empty := $b.Empty}}
+ <td {{if $empty}}class="empty"{{end}}>
+ {{if not $empty}}<a href="/debug/requests?fam={{$fam}}&b={{$i}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
+ [{{.Cond}}]
+ {{if not $empty}}</a>{{end}}
+ </td>
+ {{end}}
+
+ {{$nb := len $f.Buckets}}
+ <td class="latency-first">
+ <a href="/debug/requests?fam={{$fam}}&b={{$nb}}">[minute]</a>
+ </td>
+ <td>
+ <a href="/debug/requests?fam={{$fam}}&b={{add $nb 1}}">[hour]</a>
+ </td>
+ <td>
+ <a href="/debug/requests?fam={{$fam}}&b={{add $nb 2}}">[total]</a>
+ </td>
+
+ </tr>
+ {{end}}
+</table>
+{{end}} {{/* end of StatusTable */}}
+
+{{define "Epilog"}}
+{{if $.Traces}}
+<hr />
+<h3>Family: {{$.Family}}</h3>
+
+{{if or $.Expanded $.Traced}}
+ <a href="/debug/requests?fam={{$.Family}}&b={{$.Bucket}}">[Normal/Summary]</a>
+{{else}}
+ [Normal/Summary]
+{{end}}
+
+{{if or (not $.Expanded) $.Traced}}
+ <a href="/debug/requests?fam={{$.Family}}&b={{$.Bucket}}&exp=1">[Normal/Expanded]</a>
+{{else}}
+ [Normal/Expanded]
+{{end}}
+
+{{if not $.Active}}
+ {{if or $.Expanded (not $.Traced)}}
+ <a href="/debug/requests?fam={{$.Family}}&b={{$.Bucket}}&rtraced=1">[Traced/Summary]</a>
+ {{else}}
+ [Traced/Summary]
+ {{end}}
+ {{if or (not $.Expanded) (not $.Traced)}}
+ <a href="/debug/requests?fam={{$.Family}}&b={{$.Bucket}}&exp=1&rtraced=1">[Traced/Expanded]</a>
+ {{else}}
+ [Traced/Expanded]
+ {{end}}
+{{end}}
+
+{{if $.Total}}
+<p><em>Showing <b>{{len $.Traces}}</b> of <b>{{$.Total}}</b> traces.</em></p>
+{{end}}
+
+<table id="reqs">
+ <caption>
+ {{if $.Active}}Active{{else}}Completed{{end}} Requests
+ </caption>
+ <tr><th>When</th><th>Elapsed (s)</th></tr>
+ {{range $tr := $.Traces}}
+ <tr class="first">
+ <td class="when">{{$tr.When}}</td>
+ <td class="elapsed">{{$tr.ElapsedTime}}</td>
+ <td>{{$tr.Title}}</td>
+ {{/* TODO: include traceID/spanID */}}
+ </tr>
+ {{if $.Expanded}}
+ {{range $tr.Events}}
+ <tr>
+ <td class="when">{{.WhenString}}</td>
+ <td class="elapsed">{{elapsed .Elapsed}}</td>
+ <td>{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}<em>[redacted]</em>{{end}}</td>
+ </tr>
+ {{end}}
+ {{end}}
+ {{end}}
+</table>
+{{end}} {{/* if $.Traces */}}
+
+{{if $.Histogram}}
+<h4>Latency (µs) of {{$.Family}} over {{$.HistogramWindow}}</h4>
+{{$.Histogram}}
+{{end}} {{/* if $.Histogram */}}
+
+ </body>
+</html>
+{{end}} {{/* end of Epilog */}}
+`
diff --git a/trace/trace_test.go b/trace/trace_test.go
new file mode 100644
index 0000000..c2f5fcb
--- /dev/null
+++ b/trace/trace_test.go
@@ -0,0 +1,46 @@
+// Copyright 2015 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.
+
+package trace
+
+import (
+ "reflect"
+ "testing"
+)
+
+type s struct{}
+
+func (s) String() string { return "lazy string" }
+
+// TestReset checks whether all the fields are zeroed after reset.
+func TestReset(t *testing.T) {
+ tr := New("foo", "bar")
+ tr.LazyLog(s{}, false)
+ tr.LazyPrintf("%d", 1)
+ tr.SetRecycler(func(_ interface{}) {})
+ tr.SetTraceInfo(3, 4)
+ tr.SetMaxEvents(100)
+ tr.SetError()
+ tr.Finish()
+
+ tr.(*trace).reset()
+
+ if !reflect.DeepEqual(tr, new(trace)) {
+ t.Errorf("reset didn't clear all fields: %+v", tr)
+ }
+}
+
+// TestResetLog checks whether all the fields are zeroed after reset.
+func TestResetLog(t *testing.T) {
+ el := NewEventLog("foo", "bar")
+ el.Printf("message")
+ el.Errorf("error")
+ el.Finish()
+
+ el.(*eventLog).reset()
+
+ if !reflect.DeepEqual(el, new(eventLog)) {
+ t.Errorf("reset didn't clear all fields: %+v", el)
+ }
+}
diff --git a/webdav/file.go b/webdav/file.go
new file mode 100644
index 0000000..9ba1ca1
--- /dev/null
+++ b/webdav/file.go
@@ -0,0 +1,795 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "io"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+// slashClean is equivalent to but slightly more efficient than
+// path.Clean("/" + name).
+func slashClean(name string) string {
+ if name == "" || name[0] != '/' {
+ name = "/" + name
+ }
+ return path.Clean(name)
+}
+
+// A FileSystem implements access to a collection of named files. The elements
+// in a file path are separated by slash ('/', U+002F) characters, regardless
+// of host operating system convention.
+//
+// Each method has the same semantics as the os package's function of the same
+// name.
+//
+// Note that the os.Rename documentation says that "OS-specific restrictions
+// might apply". In particular, whether or not renaming a file or directory
+// overwriting another existing file or directory is an error is OS-dependent.
+type FileSystem interface {
+ Mkdir(name string, perm os.FileMode) error
+ OpenFile(name string, flag int, perm os.FileMode) (File, error)
+ RemoveAll(name string) error
+ Rename(oldName, newName string) error
+ Stat(name string) (os.FileInfo, error)
+}
+
+// A File is returned by a FileSystem's OpenFile method and can be served by a
+// Handler.
+//
+// A File may optionally implement the DeadPropsHolder interface, if it can
+// load and save dead properties.
+type File interface {
+ http.File
+ io.Writer
+}
+
+// A Dir implements FileSystem using the native file system restricted to a
+// specific directory tree.
+//
+// While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's
+// string value is a filename on the native file system, not a URL, so it is
+// separated by filepath.Separator, which isn't necessarily '/'.
+//
+// An empty Dir is treated as ".".
+type Dir string
+
+func (d Dir) resolve(name string) string {
+ // This implementation is based on Dir.Open's code in the standard net/http package.
+ if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
+ strings.Contains(name, "\x00") {
+ return ""
+ }
+ dir := string(d)
+ if dir == "" {
+ dir = "."
+ }
+ return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
+}
+
+func (d Dir) Mkdir(name string, perm os.FileMode) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ return os.Mkdir(name, perm)
+}
+
+func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ f, err := os.OpenFile(name, flag, perm)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func (d Dir) RemoveAll(name string) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ if name == filepath.Clean(string(d)) {
+ // Prohibit removing the virtual root directory.
+ return os.ErrInvalid
+ }
+ return os.RemoveAll(name)
+}
+
+func (d Dir) Rename(oldName, newName string) error {
+ if oldName = d.resolve(oldName); oldName == "" {
+ return os.ErrNotExist
+ }
+ if newName = d.resolve(newName); newName == "" {
+ return os.ErrNotExist
+ }
+ if root := filepath.Clean(string(d)); root == oldName || root == newName {
+ // Prohibit renaming from or to the virtual root directory.
+ return os.ErrInvalid
+ }
+ return os.Rename(oldName, newName)
+}
+
+func (d Dir) Stat(name string) (os.FileInfo, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ return os.Stat(name)
+}
+
+// NewMemFS returns a new in-memory FileSystem implementation.
+func NewMemFS() FileSystem {
+ return &memFS{
+ root: memFSNode{
+ children: make(map[string]*memFSNode),
+ mode: 0660 | os.ModeDir,
+ modTime: time.Now(),
+ },
+ }
+}
+
+// A memFS implements FileSystem, storing all metadata and actual file data
+// in-memory. No limits on filesystem size are used, so it is not recommended
+// this be used where the clients are untrusted.
+//
+// Concurrent access is permitted. The tree structure is protected by a mutex,
+// and each node's contents and metadata are protected by a per-node mutex.
+//
+// TODO: Enforce file permissions.
+type memFS struct {
+ mu sync.Mutex
+ root memFSNode
+}
+
+// TODO: clean up and rationalize the walk/find code.
+
+// walk walks the directory tree for the fullname, calling f at each step. If f
+// returns an error, the walk will be aborted and return that same error.
+//
+// dir is the directory at that step, frag is the name fragment, and final is
+// whether it is the final step. For example, walking "/foo/bar/x" will result
+// in 3 calls to f:
+// - "/", "foo", false
+// - "/foo/", "bar", false
+// - "/foo/bar/", "x", true
+// The frag argument will be empty only if dir is the root node and the walk
+// ends at that root node.
+func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
+ original := fullname
+ fullname = slashClean(fullname)
+
+ // Strip any leading "/"s to make fullname a relative path, as the walk
+ // starts at fs.root.
+ if fullname[0] == '/' {
+ fullname = fullname[1:]
+ }
+ dir := &fs.root
+
+ for {
+ frag, remaining := fullname, ""
+ i := strings.IndexRune(fullname, '/')
+ final := i < 0
+ if !final {
+ frag, remaining = fullname[:i], fullname[i+1:]
+ }
+ if frag == "" && dir != &fs.root {
+ panic("webdav: empty path fragment for a clean path")
+ }
+ if err := f(dir, frag, final); err != nil {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: err,
+ }
+ }
+ if final {
+ break
+ }
+ child := dir.children[frag]
+ if child == nil {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: os.ErrNotExist,
+ }
+ }
+ if !child.mode.IsDir() {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: os.ErrInvalid,
+ }
+ }
+ dir, fullname = child, remaining
+ }
+ return nil
+}
+
+// find returns the parent of the named node and the relative name fragment
+// from the parent to the child. For example, if finding "/foo/bar/baz" then
+// parent will be the node for "/foo/bar" and frag will be "baz".
+//
+// If the fullname names the root node, then parent, frag and err will be zero.
+//
+// find returns an error if the parent does not already exist or the parent
+// isn't a directory, but it will not return an error per se if the child does
+// not already exist. The error returned is either nil or an *os.PathError
+// whose Op is op.
+func (fs *memFS) find(op, fullname string) (parent *memFSNode, frag string, err error) {
+ err = fs.walk(op, fullname, func(parent0 *memFSNode, frag0 string, final bool) error {
+ if !final {
+ return nil
+ }
+ if frag0 != "" {
+ parent, frag = parent0, frag0
+ }
+ return nil
+ })
+ return parent, frag, err
+}
+
+func (fs *memFS) Mkdir(name string, perm os.FileMode) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("mkdir", name)
+ if err != nil {
+ return err
+ }
+ if dir == nil {
+ // We can't create the root.
+ return os.ErrInvalid
+ }
+ if _, ok := dir.children[frag]; ok {
+ return os.ErrExist
+ }
+ dir.children[frag] = &memFSNode{
+ children: make(map[string]*memFSNode),
+ mode: perm.Perm() | os.ModeDir,
+ modTime: time.Now(),
+ }
+ return nil
+}
+
+func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("open", name)
+ if err != nil {
+ return nil, err
+ }
+ var n *memFSNode
+ if dir == nil {
+ // We're opening the root.
+ if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
+ return nil, os.ErrPermission
+ }
+ n, frag = &fs.root, "/"
+
+ } else {
+ n = dir.children[frag]
+ if flag&(os.O_SYNC|os.O_APPEND) != 0 {
+ // memFile doesn't support these flags yet.
+ return nil, os.ErrInvalid
+ }
+ if flag&os.O_CREATE != 0 {
+ if flag&os.O_EXCL != 0 && n != nil {
+ return nil, os.ErrExist
+ }
+ if n == nil {
+ n = &memFSNode{
+ mode: perm.Perm(),
+ }
+ dir.children[frag] = n
+ }
+ }
+ if n == nil {
+ return nil, os.ErrNotExist
+ }
+ if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
+ n.mu.Lock()
+ n.data = nil
+ n.mu.Unlock()
+ }
+ }
+
+ children := make([]os.FileInfo, 0, len(n.children))
+ for cName, c := range n.children {
+ children = append(children, c.stat(cName))
+ }
+ return &memFile{
+ n: n,
+ nameSnapshot: frag,
+ childrenSnapshot: children,
+ }, nil
+}
+
+func (fs *memFS) RemoveAll(name string) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("remove", name)
+ if err != nil {
+ return err
+ }
+ if dir == nil {
+ // We can't remove the root.
+ return os.ErrInvalid
+ }
+ delete(dir.children, frag)
+ return nil
+}
+
+func (fs *memFS) Rename(oldName, newName string) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ oldName = slashClean(oldName)
+ newName = slashClean(newName)
+ if oldName == newName {
+ return nil
+ }
+ if strings.HasPrefix(newName, oldName+"/") {
+ // We can't rename oldName to be a sub-directory of itself.
+ return os.ErrInvalid
+ }
+
+ oDir, oFrag, err := fs.find("rename", oldName)
+ if err != nil {
+ return err
+ }
+ if oDir == nil {
+ // We can't rename from the root.
+ return os.ErrInvalid
+ }
+
+ nDir, nFrag, err := fs.find("rename", newName)
+ if err != nil {
+ return err
+ }
+ if nDir == nil {
+ // We can't rename to the root.
+ return os.ErrInvalid
+ }
+
+ oNode, ok := oDir.children[oFrag]
+ if !ok {
+ return os.ErrNotExist
+ }
+ if oNode.children != nil {
+ if nNode, ok := nDir.children[nFrag]; ok {
+ if nNode.children == nil {
+ return errNotADirectory
+ }
+ if len(nNode.children) != 0 {
+ return errDirectoryNotEmpty
+ }
+ }
+ }
+ delete(oDir.children, oFrag)
+ nDir.children[nFrag] = oNode
+ return nil
+}
+
+func (fs *memFS) Stat(name string) (os.FileInfo, error) {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("stat", name)
+ if err != nil {
+ return nil, err
+ }
+ if dir == nil {
+ // We're stat'ting the root.
+ return fs.root.stat("/"), nil
+ }
+ if n, ok := dir.children[frag]; ok {
+ return n.stat(path.Base(name)), nil
+ }
+ return nil, os.ErrNotExist
+}
+
+// A memFSNode represents a single entry in the in-memory filesystem and also
+// implements os.FileInfo.
+type memFSNode struct {
+ // children is protected by memFS.mu.
+ children map[string]*memFSNode
+
+ mu sync.Mutex
+ data []byte
+ mode os.FileMode
+ modTime time.Time
+ deadProps map[xml.Name]Property
+}
+
+func (n *memFSNode) stat(name string) *memFileInfo {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ return &memFileInfo{
+ name: name,
+ size: int64(len(n.data)),
+ mode: n.mode,
+ modTime: n.modTime,
+ }
+}
+
+func (n *memFSNode) DeadProps() (map[xml.Name]Property, error) {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ if len(n.deadProps) == 0 {
+ return nil, nil
+ }
+ ret := make(map[xml.Name]Property, len(n.deadProps))
+ for k, v := range n.deadProps {
+ ret[k] = v
+ }
+ return ret, nil
+}
+
+func (n *memFSNode) Patch(patches []Proppatch) ([]Propstat, error) {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ pstat := Propstat{Status: http.StatusOK}
+ for _, patch := range patches {
+ for _, p := range patch.Props {
+ pstat.Props = append(pstat.Props, Property{XMLName: p.XMLName})
+ if patch.Remove {
+ delete(n.deadProps, p.XMLName)
+ continue
+ }
+ if n.deadProps == nil {
+ n.deadProps = map[xml.Name]Property{}
+ }
+ n.deadProps[p.XMLName] = p
+ }
+ }
+ return []Propstat{pstat}, nil
+}
+
+type memFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+func (f *memFileInfo) Name() string { return f.name }
+func (f *memFileInfo) Size() int64 { return f.size }
+func (f *memFileInfo) Mode() os.FileMode { return f.mode }
+func (f *memFileInfo) ModTime() time.Time { return f.modTime }
+func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() }
+func (f *memFileInfo) Sys() interface{} { return nil }
+
+// A memFile is a File implementation for a memFSNode. It is a per-file (not
+// per-node) read/write position, and a snapshot of the memFS' tree structure
+// (a node's name and children) for that node.
+type memFile struct {
+ n *memFSNode
+ nameSnapshot string
+ childrenSnapshot []os.FileInfo
+ // pos is protected by n.mu.
+ pos int
+}
+
+// A *memFile implements the optional DeadPropsHolder interface.
+var _ DeadPropsHolder = (*memFile)(nil)
+
+func (f *memFile) DeadProps() (map[xml.Name]Property, error) { return f.n.DeadProps() }
+func (f *memFile) Patch(patches []Proppatch) ([]Propstat, error) { return f.n.Patch(patches) }
+
+func (f *memFile) Close() error {
+ return nil
+}
+
+func (f *memFile) Read(p []byte) (int, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
+ if f.pos >= len(f.n.data) {
+ return 0, io.EOF
+ }
+ n := copy(p, f.n.data[f.pos:])
+ f.pos += n
+ return n, nil
+}
+
+func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ if !f.n.mode.IsDir() {
+ return nil, os.ErrInvalid
+ }
+ old := f.pos
+ if old >= len(f.childrenSnapshot) {
+ // The os.File Readdir docs say that at the end of a directory,
+ // the error is io.EOF if count > 0 and nil if count <= 0.
+ if count > 0 {
+ return nil, io.EOF
+ }
+ return nil, nil
+ }
+ if count > 0 {
+ f.pos += count
+ if f.pos > len(f.childrenSnapshot) {
+ f.pos = len(f.childrenSnapshot)
+ }
+ } else {
+ f.pos = len(f.childrenSnapshot)
+ old = 0
+ }
+ return f.childrenSnapshot[old:f.pos], nil
+}
+
+func (f *memFile) Seek(offset int64, whence int) (int64, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ npos := f.pos
+ // TODO: How to handle offsets greater than the size of system int?
+ switch whence {
+ case os.SEEK_SET:
+ npos = int(offset)
+ case os.SEEK_CUR:
+ npos += int(offset)
+ case os.SEEK_END:
+ npos = len(f.n.data) + int(offset)
+ default:
+ npos = -1
+ }
+ if npos < 0 {
+ return 0, os.ErrInvalid
+ }
+ f.pos = npos
+ return int64(f.pos), nil
+}
+
+func (f *memFile) Stat() (os.FileInfo, error) {
+ return f.n.stat(f.nameSnapshot), nil
+}
+
+func (f *memFile) Write(p []byte) (int, error) {
+ lenp := len(p)
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
+ if f.pos < len(f.n.data) {
+ n := copy(f.n.data[f.pos:], p)
+ f.pos += n
+ p = p[n:]
+ } else if f.pos > len(f.n.data) {
+ // Write permits the creation of holes, if we've seek'ed past the
+ // existing end of file.
+ if f.pos <= cap(f.n.data) {
+ oldLen := len(f.n.data)
+ f.n.data = f.n.data[:f.pos]
+ hole := f.n.data[oldLen:]
+ for i := range hole {
+ hole[i] = 0
+ }
+ } else {
+ d := make([]byte, f.pos, f.pos+len(p))
+ copy(d, f.n.data)
+ f.n.data = d
+ }
+ }
+
+ if len(p) > 0 {
+ // We should only get here if f.pos == len(f.n.data).
+ f.n.data = append(f.n.data, p...)
+ f.pos = len(f.n.data)
+ }
+ f.n.modTime = time.Now()
+ return lenp, nil
+}
+
+// moveFiles moves files and/or directories from src to dst.
+//
+// See section 9.9.4 for when various HTTP status codes apply.
+func moveFiles(fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
+ created := false
+ if _, err := fs.Stat(dst); err != nil {
+ if !os.IsNotExist(err) {
+ return http.StatusForbidden, err
+ }
+ created = true
+ } else if overwrite {
+ // Section 9.9.3 says that "If a resource exists at the destination
+ // and the Overwrite header is "T", then prior to performing the move,
+ // the server must perform a DELETE with "Depth: infinity" on the
+ // destination resource.
+ if err := fs.RemoveAll(dst); err != nil {
+ return http.StatusForbidden, err
+ }
+ } else {
+ return http.StatusPreconditionFailed, os.ErrExist
+ }
+ if err := fs.Rename(src, dst); err != nil {
+ return http.StatusForbidden, err
+ }
+ if created {
+ return http.StatusCreated, nil
+ }
+ return http.StatusNoContent, nil
+}
+
+func copyProps(dst, src File) error {
+ d, ok := dst.(DeadPropsHolder)
+ if !ok {
+ return nil
+ }
+ s, ok := src.(DeadPropsHolder)
+ if !ok {
+ return nil
+ }
+ m, err := s.DeadProps()
+ if err != nil {
+ return err
+ }
+ props := make([]Property, 0, len(m))
+ for _, prop := range m {
+ props = append(props, prop)
+ }
+ _, err = d.Patch([]Proppatch{{Props: props}})
+ return err
+}
+
+// copyFiles copies files and/or directories from src to dst.
+//
+// See section 9.8.5 for when various HTTP status codes apply.
+func copyFiles(fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
+ if recursion == 1000 {
+ return http.StatusInternalServerError, errRecursionTooDeep
+ }
+ recursion++
+
+ // TODO: section 9.8.3 says that "Note that an infinite-depth COPY of /A/
+ // into /A/B/ could lead to infinite recursion if not handled correctly."
+
+ srcFile, err := fs.OpenFile(src, os.O_RDONLY, 0)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusInternalServerError, err
+ }
+ defer srcFile.Close()
+ srcStat, err := srcFile.Stat()
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusInternalServerError, err
+ }
+ srcPerm := srcStat.Mode() & os.ModePerm
+
+ created := false
+ if _, err := fs.Stat(dst); err != nil {
+ if os.IsNotExist(err) {
+ created = true
+ } else {
+ return http.StatusForbidden, err
+ }
+ } else {
+ if !overwrite {
+ return http.StatusPreconditionFailed, os.ErrExist
+ }
+ if err := fs.RemoveAll(dst); err != nil && !os.IsNotExist(err) {
+ return http.StatusForbidden, err
+ }
+ }
+
+ if srcStat.IsDir() {
+ if err := fs.Mkdir(dst, srcPerm); err != nil {
+ return http.StatusForbidden, err
+ }
+ if depth == infiniteDepth {
+ children, err := srcFile.Readdir(-1)
+ if err != nil {
+ return http.StatusForbidden, err
+ }
+ for _, c := range children {
+ name := c.Name()
+ s := path.Join(src, name)
+ d := path.Join(dst, name)
+ cStatus, cErr := copyFiles(fs, s, d, overwrite, depth, recursion)
+ if cErr != nil {
+ // TODO: MultiStatus.
+ return cStatus, cErr
+ }
+ }
+ }
+
+ } else {
+ dstFile, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusConflict, err
+ }
+ return http.StatusForbidden, err
+
+ }
+ _, copyErr := io.Copy(dstFile, srcFile)
+ propsErr := copyProps(dstFile, srcFile)
+ closeErr := dstFile.Close()
+ if copyErr != nil {
+ return http.StatusInternalServerError, copyErr
+ }
+ if propsErr != nil {
+ return http.StatusInternalServerError, propsErr
+ }
+ if closeErr != nil {
+ return http.StatusInternalServerError, closeErr
+ }
+ }
+
+ if created {
+ return http.StatusCreated, nil
+ }
+ return http.StatusNoContent, nil
+}
+
+// walkFS traverses filesystem fs starting at name up to depth levels.
+//
+// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
+// walkFS calls walkFn. If a visited file system node is a directory and
+// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
+func walkFS(fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+ // This implementation is based on Walk's code in the standard path/filepath package.
+ err := walkFn(name, info, nil)
+ if err != nil {
+ if info.IsDir() && err == filepath.SkipDir {
+ return nil
+ }
+ return err
+ }
+ if !info.IsDir() || depth == 0 {
+ return nil
+ }
+ if depth == 1 {
+ depth = 0
+ }
+
+ // Read directory names.
+ f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ if err != nil {
+ return walkFn(name, info, err)
+ }
+ fileInfos, err := f.Readdir(0)
+ f.Close()
+ if err != nil {
+ return walkFn(name, info, err)
+ }
+
+ for _, fileInfo := range fileInfos {
+ filename := path.Join(name, fileInfo.Name())
+ fileInfo, err := fs.Stat(filename)
+ if err != nil {
+ if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
+ return err
+ }
+ } else {
+ err = walkFS(fs, depth, filename, fileInfo, walkFn)
+ if err != nil {
+ if !fileInfo.IsDir() || err != filepath.SkipDir {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/webdav/file_test.go b/webdav/file_test.go
new file mode 100644
index 0000000..99547e1
--- /dev/null
+++ b/webdav/file_test.go
@@ -0,0 +1,1167 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+func TestSlashClean(t *testing.T) {
+ testCases := []string{
+ "",
+ ".",
+ "/",
+ "/./",
+ "//",
+ "//.",
+ "//a",
+ "/a",
+ "/a/b/c",
+ "/a//b/./../c/d/",
+ "a",
+ "a/b/c",
+ }
+ for _, tc := range testCases {
+ got := slashClean(tc)
+ want := path.Clean("/" + tc)
+ if got != want {
+ t.Errorf("tc=%q: got %q, want %q", tc, got, want)
+ }
+ }
+}
+
+func TestDirResolve(t *testing.T) {
+ testCases := []struct {
+ dir, name, want string
+ }{
+ {"/", "", "/"},
+ {"/", "/", "/"},
+ {"/", ".", "/"},
+ {"/", "./a", "/a"},
+ {"/", "..", "/"},
+ {"/", "..", "/"},
+ {"/", "../", "/"},
+ {"/", "../.", "/"},
+ {"/", "../a", "/a"},
+ {"/", "../..", "/"},
+ {"/", "../bar/a", "/bar/a"},
+ {"/", "../baz/a", "/baz/a"},
+ {"/", "...", "/..."},
+ {"/", ".../a", "/.../a"},
+ {"/", ".../..", "/"},
+ {"/", "a", "/a"},
+ {"/", "a/./b", "/a/b"},
+ {"/", "a/../../b", "/b"},
+ {"/", "a/../b", "/b"},
+ {"/", "a/b", "/a/b"},
+ {"/", "a/b/c/../../d", "/a/d"},
+ {"/", "a/b/c/../../../d", "/d"},
+ {"/", "a/b/c/../../../../d", "/d"},
+ {"/", "a/b/c/d", "/a/b/c/d"},
+
+ {"/foo/bar", "", "/foo/bar"},
+ {"/foo/bar", "/", "/foo/bar"},
+ {"/foo/bar", ".", "/foo/bar"},
+ {"/foo/bar", "./a", "/foo/bar/a"},
+ {"/foo/bar", "..", "/foo/bar"},
+ {"/foo/bar", "../", "/foo/bar"},
+ {"/foo/bar", "../.", "/foo/bar"},
+ {"/foo/bar", "../a", "/foo/bar/a"},
+ {"/foo/bar", "../..", "/foo/bar"},
+ {"/foo/bar", "../bar/a", "/foo/bar/bar/a"},
+ {"/foo/bar", "../baz/a", "/foo/bar/baz/a"},
+ {"/foo/bar", "...", "/foo/bar/..."},
+ {"/foo/bar", ".../a", "/foo/bar/.../a"},
+ {"/foo/bar", ".../..", "/foo/bar"},
+ {"/foo/bar", "a", "/foo/bar/a"},
+ {"/foo/bar", "a/./b", "/foo/bar/a/b"},
+ {"/foo/bar", "a/../../b", "/foo/bar/b"},
+ {"/foo/bar", "a/../b", "/foo/bar/b"},
+ {"/foo/bar", "a/b", "/foo/bar/a/b"},
+ {"/foo/bar", "a/b/c/../../d", "/foo/bar/a/d"},
+ {"/foo/bar", "a/b/c/../../../d", "/foo/bar/d"},
+ {"/foo/bar", "a/b/c/../../../../d", "/foo/bar/d"},
+ {"/foo/bar", "a/b/c/d", "/foo/bar/a/b/c/d"},
+
+ {"/foo/bar/", "", "/foo/bar"},
+ {"/foo/bar/", "/", "/foo/bar"},
+ {"/foo/bar/", ".", "/foo/bar"},
+ {"/foo/bar/", "./a", "/foo/bar/a"},
+ {"/foo/bar/", "..", "/foo/bar"},
+
+ {"/foo//bar///", "", "/foo/bar"},
+ {"/foo//bar///", "/", "/foo/bar"},
+ {"/foo//bar///", ".", "/foo/bar"},
+ {"/foo//bar///", "./a", "/foo/bar/a"},
+ {"/foo//bar///", "..", "/foo/bar"},
+
+ {"/x/y/z", "ab/c\x00d/ef", ""},
+
+ {".", "", "."},
+ {".", "/", "."},
+ {".", ".", "."},
+ {".", "./a", "a"},
+ {".", "..", "."},
+ {".", "..", "."},
+ {".", "../", "."},
+ {".", "../.", "."},
+ {".", "../a", "a"},
+ {".", "../..", "."},
+ {".", "../bar/a", "bar/a"},
+ {".", "../baz/a", "baz/a"},
+ {".", "...", "..."},
+ {".", ".../a", ".../a"},
+ {".", ".../..", "."},
+ {".", "a", "a"},
+ {".", "a/./b", "a/b"},
+ {".", "a/../../b", "b"},
+ {".", "a/../b", "b"},
+ {".", "a/b", "a/b"},
+ {".", "a/b/c/../../d", "a/d"},
+ {".", "a/b/c/../../../d", "d"},
+ {".", "a/b/c/../../../../d", "d"},
+ {".", "a/b/c/d", "a/b/c/d"},
+
+ {"", "", "."},
+ {"", "/", "."},
+ {"", ".", "."},
+ {"", "./a", "a"},
+ {"", "..", "."},
+ }
+
+ for _, tc := range testCases {
+ d := Dir(filepath.FromSlash(tc.dir))
+ if got := filepath.ToSlash(d.resolve(tc.name)); got != tc.want {
+ t.Errorf("dir=%q, name=%q: got %q, want %q", tc.dir, tc.name, got, tc.want)
+ }
+ }
+}
+
+func TestWalk(t *testing.T) {
+ type walkStep struct {
+ name, frag string
+ final bool
+ }
+
+ testCases := []struct {
+ dir string
+ want []walkStep
+ }{
+ {"", []walkStep{
+ {"", "", true},
+ }},
+ {"/", []walkStep{
+ {"", "", true},
+ }},
+ {"/a", []walkStep{
+ {"", "a", true},
+ }},
+ {"/a/", []walkStep{
+ {"", "a", true},
+ }},
+ {"/a/b", []walkStep{
+ {"", "a", false},
+ {"a", "b", true},
+ }},
+ {"/a/b/", []walkStep{
+ {"", "a", false},
+ {"a", "b", true},
+ }},
+ {"/a/b/c", []walkStep{
+ {"", "a", false},
+ {"a", "b", false},
+ {"b", "c", true},
+ }},
+ // The following test case is the one mentioned explicitly
+ // in the method description.
+ {"/foo/bar/x", []walkStep{
+ {"", "foo", false},
+ {"foo", "bar", false},
+ {"bar", "x", true},
+ }},
+ }
+
+ for _, tc := range testCases {
+ fs := NewMemFS().(*memFS)
+
+ parts := strings.Split(tc.dir, "/")
+ for p := 2; p < len(parts); p++ {
+ d := strings.Join(parts[:p], "/")
+ if err := fs.Mkdir(d, 0666); err != nil {
+ t.Errorf("tc.dir=%q: mkdir: %q: %v", tc.dir, d, err)
+ }
+ }
+
+ i, prevFrag := 0, ""
+ err := fs.walk("test", tc.dir, func(dir *memFSNode, frag string, final bool) error {
+ got := walkStep{
+ name: prevFrag,
+ frag: frag,
+ final: final,
+ }
+ want := tc.want[i]
+
+ if got != want {
+ return fmt.Errorf("got %+v, want %+v", got, want)
+ }
+ i, prevFrag = i+1, frag
+ return nil
+ })
+ if err != nil {
+ t.Errorf("tc.dir=%q: %v", tc.dir, err)
+ }
+ }
+}
+
+// find appends to ss the names of the named file and its children. It is
+// analogous to the Unix find command.
+//
+// The returned strings are not guaranteed to be in any particular order.
+func find(ss []string, fs FileSystem, name string) ([]string, error) {
+ stat, err := fs.Stat(name)
+ if err != nil {
+ return nil, err
+ }
+ ss = append(ss, name)
+ if stat.IsDir() {
+ f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ children, err := f.Readdir(-1)
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range children {
+ ss, err = find(ss, fs, path.Join(name, c.Name()))
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return ss, nil
+}
+
+func testFS(t *testing.T, fs FileSystem) {
+ errStr := func(err error) string {
+ switch {
+ case os.IsExist(err):
+ return "errExist"
+ case os.IsNotExist(err):
+ return "errNotExist"
+ case err != nil:
+ return "err"
+ }
+ return "ok"
+ }
+
+ // The non-"find" non-"stat" test cases should change the file system state. The
+ // indentation of the "find"s and "stat"s helps distinguish such test cases.
+ testCases := []string{
+ " stat / want dir",
+ " stat /a want errNotExist",
+ " stat /d want errNotExist",
+ " stat /d/e want errNotExist",
+ "create /a A want ok",
+ " stat /a want 1",
+ "create /d/e EEE want errNotExist",
+ "mk-dir /a want errExist",
+ "mk-dir /d/m want errNotExist",
+ "mk-dir /d want ok",
+ " stat /d want dir",
+ "create /d/e EEE want ok",
+ " stat /d/e want 3",
+ " find / /a /d /d/e",
+ "create /d/f FFFF want ok",
+ "create /d/g GGGGGGG want ok",
+ "mk-dir /d/m want ok",
+ "mk-dir /d/m want errExist",
+ "create /d/m/p PPPPP want ok",
+ " stat /d/e want 3",
+ " stat /d/f want 4",
+ " stat /d/g want 7",
+ " stat /d/h want errNotExist",
+ " stat /d/m want dir",
+ " stat /d/m/p want 5",
+ " find / /a /d /d/e /d/f /d/g /d/m /d/m/p",
+ "rm-all /d want ok",
+ " stat /a want 1",
+ " stat /d want errNotExist",
+ " stat /d/e want errNotExist",
+ " stat /d/f want errNotExist",
+ " stat /d/g want errNotExist",
+ " stat /d/m want errNotExist",
+ " stat /d/m/p want errNotExist",
+ " find / /a",
+ "mk-dir /d/m want errNotExist",
+ "mk-dir /d want ok",
+ "create /d/f FFFF want ok",
+ "rm-all /d/f want ok",
+ "mk-dir /d/m want ok",
+ "rm-all /z want ok",
+ "rm-all / want err",
+ "create /b BB want ok",
+ " stat / want dir",
+ " stat /a want 1",
+ " stat /b want 2",
+ " stat /c want errNotExist",
+ " stat /d want dir",
+ " stat /d/m want dir",
+ " find / /a /b /d /d/m",
+ "move__ o=F /b /c want ok",
+ " stat /b want errNotExist",
+ " stat /c want 2",
+ " stat /d/m want dir",
+ " stat /d/n want errNotExist",
+ " find / /a /c /d /d/m",
+ "move__ o=F /d/m /d/n want ok",
+ "create /d/n/q QQQQ want ok",
+ " stat /d/m want errNotExist",
+ " stat /d/n want dir",
+ " stat /d/n/q want 4",
+ "move__ o=F /d /d/n/z want err",
+ "move__ o=T /c /d/n/q want ok",
+ " stat /c want errNotExist",
+ " stat /d/n/q want 2",
+ " find / /a /d /d/n /d/n/q",
+ "create /d/n/r RRRRR want ok",
+ "mk-dir /u want ok",
+ "mk-dir /u/v want ok",
+ "move__ o=F /d/n /u want errExist",
+ "create /t TTTTTT want ok",
+ "move__ o=F /d/n /t want errExist",
+ "rm-all /t want ok",
+ "move__ o=F /d/n /t want ok",
+ " stat /d want dir",
+ " stat /d/n want errNotExist",
+ " stat /d/n/r want errNotExist",
+ " stat /t want dir",
+ " stat /t/q want 2",
+ " stat /t/r want 5",
+ " find / /a /d /t /t/q /t/r /u /u/v",
+ "move__ o=F /t / want errExist",
+ "move__ o=T /t /u/v want ok",
+ " stat /u/v/r want 5",
+ "move__ o=F / /z want err",
+ " find / /a /d /u /u/v /u/v/q /u/v/r",
+ " stat /a want 1",
+ " stat /b want errNotExist",
+ " stat /c want errNotExist",
+ " stat /u/v/r want 5",
+ "copy__ o=F d=0 /a /b want ok",
+ "copy__ o=T d=0 /a /c want ok",
+ " stat /a want 1",
+ " stat /b want 1",
+ " stat /c want 1",
+ " stat /u/v/r want 5",
+ "copy__ o=F d=0 /u/v/r /b want errExist",
+ " stat /b want 1",
+ "copy__ o=T d=0 /u/v/r /b want ok",
+ " stat /a want 1",
+ " stat /b want 5",
+ " stat /u/v/r want 5",
+ "rm-all /a want ok",
+ "rm-all /b want ok",
+ "mk-dir /u/v/w want ok",
+ "create /u/v/w/s SSSSSSSS want ok",
+ " stat /d want dir",
+ " stat /d/x want errNotExist",
+ " stat /d/y want errNotExist",
+ " stat /u/v/r want 5",
+ " stat /u/v/w/s want 8",
+ " find / /c /d /u /u/v /u/v/q /u/v/r /u/v/w /u/v/w/s",
+ "copy__ o=T d=0 /u/v /d/x want ok",
+ "copy__ o=T d=∞ /u/v /d/y want ok",
+ "rm-all /u want ok",
+ " stat /d/x want dir",
+ " stat /d/x/q want errNotExist",
+ " stat /d/x/r want errNotExist",
+ " stat /d/x/w want errNotExist",
+ " stat /d/x/w/s want errNotExist",
+ " stat /d/y want dir",
+ " stat /d/y/q want 2",
+ " stat /d/y/r want 5",
+ " stat /d/y/w want dir",
+ " stat /d/y/w/s want 8",
+ " stat /u want errNotExist",
+ " find / /c /d /d/x /d/y /d/y/q /d/y/r /d/y/w /d/y/w/s",
+ "copy__ o=F d=∞ /d/y /d/x want errExist",
+ }
+
+ for i, tc := range testCases {
+ tc = strings.TrimSpace(tc)
+ j := strings.IndexByte(tc, ' ')
+ if j < 0 {
+ t.Fatalf("test case #%d %q: invalid command", i, tc)
+ }
+ op, arg := tc[:j], tc[j+1:]
+
+ switch op {
+ default:
+ t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
+
+ case "create":
+ parts := strings.Split(arg, " ")
+ if len(parts) != 4 || parts[2] != "want" {
+ t.Fatalf("test case #%d %q: invalid write", i, tc)
+ }
+ f, opErr := fs.OpenFile(parts[0], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if got := errStr(opErr); got != parts[3] {
+ t.Fatalf("test case #%d %q: OpenFile: got %q (%v), want %q", i, tc, got, opErr, parts[3])
+ }
+ if f != nil {
+ if _, err := f.Write([]byte(parts[1])); err != nil {
+ t.Fatalf("test case #%d %q: Write: %v", i, tc, err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
+ }
+ }
+
+ case "find":
+ got, err := find(nil, fs, "/")
+ if err != nil {
+ t.Fatalf("test case #%d %q: find: %v", i, tc, err)
+ }
+ sort.Strings(got)
+ want := strings.Split(arg, " ")
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("test case #%d %q:\ngot %s\nwant %s", i, tc, got, want)
+ }
+
+ case "copy__", "mk-dir", "move__", "rm-all", "stat":
+ nParts := 3
+ switch op {
+ case "copy__":
+ nParts = 6
+ case "move__":
+ nParts = 5
+ }
+ parts := strings.Split(arg, " ")
+ if len(parts) != nParts {
+ t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
+ }
+
+ got, opErr := "", error(nil)
+ switch op {
+ case "copy__":
+ depth := 0
+ if parts[1] == "d=∞" {
+ depth = infiniteDepth
+ }
+ _, opErr = copyFiles(fs, parts[2], parts[3], parts[0] == "o=T", depth, 0)
+ case "mk-dir":
+ opErr = fs.Mkdir(parts[0], 0777)
+ case "move__":
+ _, opErr = moveFiles(fs, parts[1], parts[2], parts[0] == "o=T")
+ case "rm-all":
+ opErr = fs.RemoveAll(parts[0])
+ case "stat":
+ var stat os.FileInfo
+ fileName := parts[0]
+ if stat, opErr = fs.Stat(fileName); opErr == nil {
+ if stat.IsDir() {
+ got = "dir"
+ } else {
+ got = strconv.Itoa(int(stat.Size()))
+ }
+
+ if fileName == "/" {
+ // For a Dir FileSystem, the virtual file system root maps to a
+ // real file system name like "/tmp/webdav-test012345", which does
+ // not end with "/". We skip such cases.
+ } else if statName := stat.Name(); path.Base(fileName) != statName {
+ t.Fatalf("test case #%d %q: file name %q inconsistent with stat name %q",
+ i, tc, fileName, statName)
+ }
+ }
+ }
+ if got == "" {
+ got = errStr(opErr)
+ }
+
+ if parts[len(parts)-2] != "want" {
+ t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
+ }
+ if want := parts[len(parts)-1]; got != want {
+ t.Fatalf("test case #%d %q: got %q (%v), want %q", i, tc, got, opErr, want)
+ }
+ }
+ }
+}
+
+func TestDir(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl":
+ t.Skip("see golang.org/issue/12004")
+ case "plan9":
+ t.Skip("see golang.org/issue/11453")
+ }
+
+ td, err := ioutil.TempDir("", "webdav-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(td)
+ testFS(t, Dir(td))
+}
+
+func TestMemFS(t *testing.T) {
+ testFS(t, NewMemFS())
+}
+
+func TestMemFSRoot(t *testing.T) {
+ fs := NewMemFS()
+ for i := 0; i < 5; i++ {
+ stat, err := fs.Stat("/")
+ if err != nil {
+ t.Fatalf("i=%d: Stat: %v", i, err)
+ }
+ if !stat.IsDir() {
+ t.Fatalf("i=%d: Stat.IsDir is false, want true", i)
+ }
+
+ f, err := fs.OpenFile("/", os.O_RDONLY, 0)
+ if err != nil {
+ t.Fatalf("i=%d: OpenFile: %v", i, err)
+ }
+ defer f.Close()
+ children, err := f.Readdir(-1)
+ if err != nil {
+ t.Fatalf("i=%d: Readdir: %v", i, err)
+ }
+ if len(children) != i {
+ t.Fatalf("i=%d: got %d children, want %d", i, len(children), i)
+ }
+
+ if _, err := f.Write(make([]byte, 1)); err == nil {
+ t.Fatalf("i=%d: Write: got nil error, want non-nil", i)
+ }
+
+ if err := fs.Mkdir(fmt.Sprintf("/dir%d", i), 0777); err != nil {
+ t.Fatalf("i=%d: Mkdir: %v", i, err)
+ }
+ }
+}
+
+func TestMemFileReaddir(t *testing.T) {
+ fs := NewMemFS()
+ if err := fs.Mkdir("/foo", 0777); err != nil {
+ t.Fatalf("Mkdir: %v", err)
+ }
+ readdir := func(count int) ([]os.FileInfo, error) {
+ f, err := fs.OpenFile("/foo", os.O_RDONLY, 0)
+ if err != nil {
+ t.Fatalf("OpenFile: %v", err)
+ }
+ defer f.Close()
+ return f.Readdir(count)
+ }
+ if got, err := readdir(-1); len(got) != 0 || err != nil {
+ t.Fatalf("readdir(-1): got %d fileInfos with err=%v, want 0, <nil>", len(got), err)
+ }
+ if got, err := readdir(+1); len(got) != 0 || err != io.EOF {
+ t.Fatalf("readdir(+1): got %d fileInfos with err=%v, want 0, EOF", len(got), err)
+ }
+}
+
+func TestMemFile(t *testing.T) {
+ testCases := []string{
+ "wantData ",
+ "wantSize 0",
+ "write abc",
+ "wantData abc",
+ "write de",
+ "wantData abcde",
+ "wantSize 5",
+ "write 5*x",
+ "write 4*y+2*z",
+ "write 3*st",
+ "wantData abcdexxxxxyyyyzzststst",
+ "wantSize 22",
+ "seek set 4 want 4",
+ "write EFG",
+ "wantData abcdEFGxxxyyyyzzststst",
+ "wantSize 22",
+ "seek set 2 want 2",
+ "read cdEF",
+ "read Gx",
+ "seek cur 0 want 8",
+ "seek cur 2 want 10",
+ "seek cur -1 want 9",
+ "write J",
+ "wantData abcdEFGxxJyyyyzzststst",
+ "wantSize 22",
+ "seek cur -4 want 6",
+ "write ghijk",
+ "wantData abcdEFghijkyyyzzststst",
+ "wantSize 22",
+ "read yyyz",
+ "seek cur 0 want 15",
+ "write ",
+ "seek cur 0 want 15",
+ "read ",
+ "seek cur 0 want 15",
+ "seek end -3 want 19",
+ "write ZZ",
+ "wantData abcdEFghijkyyyzzstsZZt",
+ "wantSize 22",
+ "write 4*A",
+ "wantData abcdEFghijkyyyzzstsZZAAAA",
+ "wantSize 25",
+ "seek end 0 want 25",
+ "seek end -5 want 20",
+ "read Z+4*A",
+ "write 5*B",
+ "wantData abcdEFghijkyyyzzstsZZAAAABBBBB",
+ "wantSize 30",
+ "seek end 10 want 40",
+ "write C",
+ "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C",
+ "wantSize 41",
+ "write D",
+ "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD",
+ "wantSize 42",
+ "seek set 43 want 43",
+ "write E",
+ "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD.E",
+ "wantSize 44",
+ "seek set 0 want 0",
+ "write 5*123456789_",
+ "wantData 123456789_123456789_123456789_123456789_123456789_",
+ "wantSize 50",
+ "seek cur 0 want 50",
+ "seek cur -99 want err",
+ }
+
+ const filename = "/foo"
+ fs := NewMemFS()
+ f, err := fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ t.Fatalf("OpenFile: %v", err)
+ }
+ defer f.Close()
+
+ for i, tc := range testCases {
+ j := strings.IndexByte(tc, ' ')
+ if j < 0 {
+ t.Fatalf("test case #%d %q: invalid command", i, tc)
+ }
+ op, arg := tc[:j], tc[j+1:]
+
+ // Expand an arg like "3*a+2*b" to "aaabb".
+ parts := strings.Split(arg, "+")
+ for j, part := range parts {
+ if k := strings.IndexByte(part, '*'); k >= 0 {
+ repeatCount, repeatStr := part[:k], part[k+1:]
+ n, err := strconv.Atoi(repeatCount)
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid repeat count %q", i, tc, repeatCount)
+ }
+ parts[j] = strings.Repeat(repeatStr, n)
+ }
+ }
+ arg = strings.Join(parts, "")
+
+ switch op {
+ default:
+ t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
+
+ case "read":
+ buf := make([]byte, len(arg))
+ if _, err := io.ReadFull(f, buf); err != nil {
+ t.Fatalf("test case #%d %q: ReadFull: %v", i, tc, err)
+ }
+ if got := string(buf); got != arg {
+ t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
+ }
+
+ case "seek":
+ parts := strings.Split(arg, " ")
+ if len(parts) != 4 {
+ t.Fatalf("test case #%d %q: invalid seek", i, tc)
+ }
+
+ whence := 0
+ switch parts[0] {
+ default:
+ t.Fatalf("test case #%d %q: invalid seek whence", i, tc)
+ case "set":
+ whence = os.SEEK_SET
+ case "cur":
+ whence = os.SEEK_CUR
+ case "end":
+ whence = os.SEEK_END
+ }
+ offset, err := strconv.Atoi(parts[1])
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid offset %q", i, tc, parts[1])
+ }
+
+ if parts[2] != "want" {
+ t.Fatalf("test case #%d %q: invalid seek", i, tc)
+ }
+ if parts[3] == "err" {
+ _, err := f.Seek(int64(offset), whence)
+ if err == nil {
+ t.Fatalf("test case #%d %q: Seek returned nil error, want non-nil", i, tc)
+ }
+ } else {
+ got, err := f.Seek(int64(offset), whence)
+ if err != nil {
+ t.Fatalf("test case #%d %q: Seek: %v", i, tc, err)
+ }
+ want, err := strconv.Atoi(parts[3])
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid want %q", i, tc, parts[3])
+ }
+ if got != int64(want) {
+ t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
+ }
+ }
+
+ case "write":
+ n, err := f.Write([]byte(arg))
+ if err != nil {
+ t.Fatalf("test case #%d %q: write: %v", i, tc, err)
+ }
+ if n != len(arg) {
+ t.Fatalf("test case #%d %q: write returned %d bytes, want %d", i, tc, n, len(arg))
+ }
+
+ case "wantData":
+ g, err := fs.OpenFile(filename, os.O_RDONLY, 0666)
+ if err != nil {
+ t.Fatalf("test case #%d %q: OpenFile: %v", i, tc, err)
+ }
+ gotBytes, err := ioutil.ReadAll(g)
+ if err != nil {
+ t.Fatalf("test case #%d %q: ReadAll: %v", i, tc, err)
+ }
+ for i, c := range gotBytes {
+ if c == '\x00' {
+ gotBytes[i] = '.'
+ }
+ }
+ got := string(gotBytes)
+ if got != arg {
+ t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
+ }
+ if err := g.Close(); err != nil {
+ t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
+ }
+
+ case "wantSize":
+ n, err := strconv.Atoi(arg)
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid size %q", i, tc, arg)
+ }
+ fi, err := fs.Stat(filename)
+ if err != nil {
+ t.Fatalf("test case #%d %q: Stat: %v", i, tc, err)
+ }
+ if got, want := fi.Size(), int64(n); got != want {
+ t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
+ }
+ }
+ }
+}
+
+// TestMemFileWriteAllocs tests that writing N consecutive 1KiB chunks to a
+// memFile doesn't allocate a new buffer for each of those N times. Otherwise,
+// calling io.Copy(aMemFile, src) is likely to have quadratic complexity.
+func TestMemFileWriteAllocs(t *testing.T) {
+ fs := NewMemFS()
+ f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ t.Fatalf("OpenFile: %v", err)
+ }
+ defer f.Close()
+
+ xxx := make([]byte, 1024)
+ for i := range xxx {
+ xxx[i] = 'x'
+ }
+
+ a := testing.AllocsPerRun(100, func() {
+ f.Write(xxx)
+ })
+ // AllocsPerRun returns an integral value, so we compare the rounded-down
+ // number to zero.
+ if a > 0 {
+ t.Fatalf("%v allocs per run, want 0", a)
+ }
+}
+
+func BenchmarkMemFileWrite(b *testing.B) {
+ fs := NewMemFS()
+ xxx := make([]byte, 1024)
+ for i := range xxx {
+ xxx[i] = 'x'
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ b.Fatalf("OpenFile: %v", err)
+ }
+ for j := 0; j < 100; j++ {
+ f.Write(xxx)
+ }
+ if err := f.Close(); err != nil {
+ b.Fatalf("Close: %v", err)
+ }
+ if err := fs.RemoveAll("/xxx"); err != nil {
+ b.Fatalf("RemoveAll: %v", err)
+ }
+ }
+}
+
+func TestCopyMoveProps(t *testing.T) {
+ fs := NewMemFS()
+ create := func(name string) error {
+ f, err := fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ return err
+ }
+ _, wErr := f.Write([]byte("contents"))
+ cErr := f.Close()
+ if wErr != nil {
+ return wErr
+ }
+ return cErr
+ }
+ patch := func(name string, patches ...Proppatch) error {
+ f, err := fs.OpenFile(name, os.O_RDWR, 0666)
+ if err != nil {
+ return err
+ }
+ _, pErr := f.(DeadPropsHolder).Patch(patches)
+ cErr := f.Close()
+ if pErr != nil {
+ return pErr
+ }
+ return cErr
+ }
+ props := func(name string) (map[xml.Name]Property, error) {
+ f, err := fs.OpenFile(name, os.O_RDWR, 0666)
+ if err != nil {
+ return nil, err
+ }
+ m, pErr := f.(DeadPropsHolder).DeadProps()
+ cErr := f.Close()
+ if pErr != nil {
+ return nil, pErr
+ }
+ if cErr != nil {
+ return nil, cErr
+ }
+ return m, nil
+ }
+
+ p0 := Property{
+ XMLName: xml.Name{Space: "x:", Local: "boat"},
+ InnerXML: []byte("pea-green"),
+ }
+ p1 := Property{
+ XMLName: xml.Name{Space: "x:", Local: "ring"},
+ InnerXML: []byte("1 shilling"),
+ }
+ p2 := Property{
+ XMLName: xml.Name{Space: "x:", Local: "spoon"},
+ InnerXML: []byte("runcible"),
+ }
+ p3 := Property{
+ XMLName: xml.Name{Space: "x:", Local: "moon"},
+ InnerXML: []byte("light"),
+ }
+
+ if err := create("/src"); err != nil {
+ t.Fatalf("create /src: %v", err)
+ }
+ if err := patch("/src", Proppatch{Props: []Property{p0, p1}}); err != nil {
+ t.Fatalf("patch /src +p0 +p1: %v", err)
+ }
+ if _, err := copyFiles(fs, "/src", "/tmp", true, infiniteDepth, 0); err != nil {
+ t.Fatalf("copyFiles /src /tmp: %v", err)
+ }
+ if _, err := moveFiles(fs, "/tmp", "/dst", true); err != nil {
+ t.Fatalf("moveFiles /tmp /dst: %v", err)
+ }
+ if err := patch("/src", Proppatch{Props: []Property{p0}, Remove: true}); err != nil {
+ t.Fatalf("patch /src -p0: %v", err)
+ }
+ if err := patch("/src", Proppatch{Props: []Property{p2}}); err != nil {
+ t.Fatalf("patch /src +p2: %v", err)
+ }
+ if err := patch("/dst", Proppatch{Props: []Property{p1}, Remove: true}); err != nil {
+ t.Fatalf("patch /dst -p1: %v", err)
+ }
+ if err := patch("/dst", Proppatch{Props: []Property{p3}}); err != nil {
+ t.Fatalf("patch /dst +p3: %v", err)
+ }
+
+ gotSrc, err := props("/src")
+ if err != nil {
+ t.Fatalf("props /src: %v", err)
+ }
+ wantSrc := map[xml.Name]Property{
+ p1.XMLName: p1,
+ p2.XMLName: p2,
+ }
+ if !reflect.DeepEqual(gotSrc, wantSrc) {
+ t.Fatalf("props /src:\ngot %v\nwant %v", gotSrc, wantSrc)
+ }
+
+ gotDst, err := props("/dst")
+ if err != nil {
+ t.Fatalf("props /dst: %v", err)
+ }
+ wantDst := map[xml.Name]Property{
+ p0.XMLName: p0,
+ p3.XMLName: p3,
+ }
+ if !reflect.DeepEqual(gotDst, wantDst) {
+ t.Fatalf("props /dst:\ngot %v\nwant %v", gotDst, wantDst)
+ }
+}
+
+func TestWalkFS(t *testing.T) {
+ testCases := []struct {
+ desc string
+ buildfs []string
+ startAt string
+ depth int
+ walkFn filepath.WalkFunc
+ want []string
+ }{{
+ "just root",
+ []string{},
+ "/",
+ infiniteDepth,
+ nil,
+ []string{
+ "/",
+ },
+ }, {
+ "infinite walk from root",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/d",
+ "mkdir /e",
+ "touch /f",
+ },
+ "/",
+ infiniteDepth,
+ nil,
+ []string{
+ "/",
+ "/a",
+ "/a/b",
+ "/a/b/c",
+ "/a/d",
+ "/e",
+ "/f",
+ },
+ }, {
+ "infinite walk from subdir",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/d",
+ "mkdir /e",
+ "touch /f",
+ },
+ "/a",
+ infiniteDepth,
+ nil,
+ []string{
+ "/a",
+ "/a/b",
+ "/a/b/c",
+ "/a/d",
+ },
+ }, {
+ "depth 1 walk from root",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/d",
+ "mkdir /e",
+ "touch /f",
+ },
+ "/",
+ 1,
+ nil,
+ []string{
+ "/",
+ "/a",
+ "/e",
+ "/f",
+ },
+ }, {
+ "depth 1 walk from subdir",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/b/g",
+ "mkdir /a/b/g/h",
+ "touch /a/b/g/i",
+ "touch /a/b/g/h/j",
+ },
+ "/a/b",
+ 1,
+ nil,
+ []string{
+ "/a/b",
+ "/a/b/c",
+ "/a/b/g",
+ },
+ }, {
+ "depth 0 walk from subdir",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/b/g",
+ "mkdir /a/b/g/h",
+ "touch /a/b/g/i",
+ "touch /a/b/g/h/j",
+ },
+ "/a/b",
+ 0,
+ nil,
+ []string{
+ "/a/b",
+ },
+ }, {
+ "infinite walk from file",
+ []string{
+ "mkdir /a",
+ "touch /a/b",
+ "touch /a/c",
+ },
+ "/a/b",
+ 0,
+ nil,
+ []string{
+ "/a/b",
+ },
+ }, {
+ "infinite walk with skipped subdir",
+ []string{
+ "mkdir /a",
+ "mkdir /a/b",
+ "touch /a/b/c",
+ "mkdir /a/b/g",
+ "mkdir /a/b/g/h",
+ "touch /a/b/g/i",
+ "touch /a/b/g/h/j",
+ "touch /a/b/z",
+ },
+ "/",
+ infiniteDepth,
+ func(path string, info os.FileInfo, err error) error {
+ if path == "/a/b/g" {
+ return filepath.SkipDir
+ }
+ return nil
+ },
+ []string{
+ "/",
+ "/a",
+ "/a/b",
+ "/a/b/c",
+ "/a/b/z",
+ },
+ }}
+ for _, tc := range testCases {
+ fs, err := buildTestFS(tc.buildfs)
+ if err != nil {
+ t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
+ }
+ var got []string
+ traceFn := func(path string, info os.FileInfo, err error) error {
+ if tc.walkFn != nil {
+ err = tc.walkFn(path, info, err)
+ if err != nil {
+ return err
+ }
+ }
+ got = append(got, path)
+ return nil
+ }
+ fi, err := fs.Stat(tc.startAt)
+ if err != nil {
+ t.Fatalf("%s: cannot stat: %v", tc.desc, err)
+ }
+ err = walkFS(fs, tc.depth, tc.startAt, fi, traceFn)
+ if err != nil {
+ t.Errorf("%s:\ngot error %v, want nil", tc.desc, err)
+ continue
+ }
+ sort.Strings(got)
+ sort.Strings(tc.want)
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("%s:\ngot %q\nwant %q", tc.desc, got, tc.want)
+ continue
+ }
+ }
+}
+
+func buildTestFS(buildfs []string) (FileSystem, error) {
+ // TODO: Could this be merged with the build logic in TestFS?
+
+ fs := NewMemFS()
+ for _, b := range buildfs {
+ op := strings.Split(b, " ")
+ switch op[0] {
+ case "mkdir":
+ err := fs.Mkdir(op[1], os.ModeDir|0777)
+ if err != nil {
+ return nil, err
+ }
+ case "touch":
+ f, err := fs.OpenFile(op[1], os.O_RDWR|os.O_CREATE, 0666)
+ if err != nil {
+ return nil, err
+ }
+ f.Close()
+ case "write":
+ f, err := fs.OpenFile(op[1], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ return nil, err
+ }
+ _, err = f.Write([]byte(op[2]))
+ f.Close()
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, fmt.Errorf("unknown file operation %q", op[0])
+ }
+ }
+ return fs, nil
+}
diff --git a/webdav/if.go b/webdav/if.go
new file mode 100644
index 0000000..416e81c
--- /dev/null
+++ b/webdav/if.go
@@ -0,0 +1,173 @@
+// Copyright 2014 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.
+
+package webdav
+
+// The If header is covered by Section 10.4.
+// http://www.webdav.org/specs/rfc4918.html#HEADER_If
+
+import (
+ "strings"
+)
+
+// ifHeader is a disjunction (OR) of ifLists.
+type ifHeader struct {
+ lists []ifList
+}
+
+// ifList is a conjunction (AND) of Conditions, and an optional resource tag.
+type ifList struct {
+ resourceTag string
+ conditions []Condition
+}
+
+// parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string
+// should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is
+// returned by req.Header.Get("If") for a http.Request req.
+func parseIfHeader(httpHeader string) (h ifHeader, ok bool) {
+ s := strings.TrimSpace(httpHeader)
+ switch tokenType, _, _ := lex(s); tokenType {
+ case '(':
+ return parseNoTagLists(s)
+ case angleTokenType:
+ return parseTaggedLists(s)
+ default:
+ return ifHeader{}, false
+ }
+}
+
+func parseNoTagLists(s string) (h ifHeader, ok bool) {
+ for {
+ l, remaining, ok := parseList(s)
+ if !ok {
+ return ifHeader{}, false
+ }
+ h.lists = append(h.lists, l)
+ if remaining == "" {
+ return h, true
+ }
+ s = remaining
+ }
+}
+
+func parseTaggedLists(s string) (h ifHeader, ok bool) {
+ resourceTag, n := "", 0
+ for first := true; ; first = false {
+ tokenType, tokenStr, remaining := lex(s)
+ switch tokenType {
+ case angleTokenType:
+ if !first && n == 0 {
+ return ifHeader{}, false
+ }
+ resourceTag, n = tokenStr, 0
+ s = remaining
+ case '(':
+ n++
+ l, remaining, ok := parseList(s)
+ if !ok {
+ return ifHeader{}, false
+ }
+ l.resourceTag = resourceTag
+ h.lists = append(h.lists, l)
+ if remaining == "" {
+ return h, true
+ }
+ s = remaining
+ default:
+ return ifHeader{}, false
+ }
+ }
+}
+
+func parseList(s string) (l ifList, remaining string, ok bool) {
+ tokenType, _, s := lex(s)
+ if tokenType != '(' {
+ return ifList{}, "", false
+ }
+ for {
+ tokenType, _, remaining = lex(s)
+ if tokenType == ')' {
+ if len(l.conditions) == 0 {
+ return ifList{}, "", false
+ }
+ return l, remaining, true
+ }
+ c, remaining, ok := parseCondition(s)
+ if !ok {
+ return ifList{}, "", false
+ }
+ l.conditions = append(l.conditions, c)
+ s = remaining
+ }
+}
+
+func parseCondition(s string) (c Condition, remaining string, ok bool) {
+ tokenType, tokenStr, s := lex(s)
+ if tokenType == notTokenType {
+ c.Not = true
+ tokenType, tokenStr, s = lex(s)
+ }
+ switch tokenType {
+ case strTokenType, angleTokenType:
+ c.Token = tokenStr
+ case squareTokenType:
+ c.ETag = tokenStr
+ default:
+ return Condition{}, "", false
+ }
+ return c, s, true
+}
+
+// Single-rune tokens like '(' or ')' have a token type equal to their rune.
+// All other tokens have a negative token type.
+const (
+ errTokenType = rune(-1)
+ eofTokenType = rune(-2)
+ strTokenType = rune(-3)
+ notTokenType = rune(-4)
+ angleTokenType = rune(-5)
+ squareTokenType = rune(-6)
+)
+
+func lex(s string) (tokenType rune, tokenStr string, remaining string) {
+ // The net/textproto Reader that parses the HTTP header will collapse
+ // Linear White Space that spans multiple "\r\n" lines to a single " ",
+ // so we don't need to look for '\r' or '\n'.
+ for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') {
+ s = s[1:]
+ }
+ if len(s) == 0 {
+ return eofTokenType, "", ""
+ }
+ i := 0
+loop:
+ for ; i < len(s); i++ {
+ switch s[i] {
+ case '\t', ' ', '(', ')', '<', '>', '[', ']':
+ break loop
+ }
+ }
+
+ if i != 0 {
+ tokenStr, remaining = s[:i], s[i:]
+ if tokenStr == "Not" {
+ return notTokenType, "", remaining
+ }
+ return strTokenType, tokenStr, remaining
+ }
+
+ j := 0
+ switch s[0] {
+ case '<':
+ j, tokenType = strings.IndexByte(s, '>'), angleTokenType
+ case '[':
+ j, tokenType = strings.IndexByte(s, ']'), squareTokenType
+ default:
+ return rune(s[0]), "", s[1:]
+ }
+ if j < 0 {
+ return errTokenType, "", ""
+ }
+ return tokenType, s[1:j], s[j+1:]
+}
diff --git a/webdav/if_test.go b/webdav/if_test.go
new file mode 100644
index 0000000..aad61a4
--- /dev/null
+++ b/webdav/if_test.go
@@ -0,0 +1,322 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestParseIfHeader(t *testing.T) {
+ // The "section x.y.z" test cases come from section x.y.z of the spec at
+ // http://www.webdav.org/specs/rfc4918.html
+ testCases := []struct {
+ desc string
+ input string
+ want ifHeader
+ }{{
+ "bad: empty",
+ ``,
+ ifHeader{},
+ }, {
+ "bad: no parens",
+ `foobar`,
+ ifHeader{},
+ }, {
+ "bad: empty list #1",
+ `()`,
+ ifHeader{},
+ }, {
+ "bad: empty list #2",
+ `(a) (b c) () (d)`,
+ ifHeader{},
+ }, {
+ "bad: no list after resource #1",
+ `<foo>`,
+ ifHeader{},
+ }, {
+ "bad: no list after resource #2",
+ `<foo> <bar> (a)`,
+ ifHeader{},
+ }, {
+ "bad: no list after resource #3",
+ `<foo> (a) (b) <bar>`,
+ ifHeader{},
+ }, {
+ "bad: no-tag-list followed by tagged-list",
+ `(a) (b) <foo> (c)`,
+ ifHeader{},
+ }, {
+ "bad: unfinished list",
+ `(a`,
+ ifHeader{},
+ }, {
+ "bad: unfinished ETag",
+ `([b`,
+ ifHeader{},
+ }, {
+ "bad: unfinished Notted list",
+ `(Not a`,
+ ifHeader{},
+ }, {
+ "bad: double Not",
+ `(Not Not a)`,
+ ifHeader{},
+ }, {
+ "good: one list with a Token",
+ `(a)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `a`,
+ }},
+ }},
+ },
+ }, {
+ "good: one list with an ETag",
+ `([a])`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ ETag: `a`,
+ }},
+ }},
+ },
+ }, {
+ "good: one list with three Nots",
+ `(Not a Not b Not [d])`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Not: true,
+ Token: `a`,
+ }, {
+ Not: true,
+ Token: `b`,
+ }, {
+ Not: true,
+ ETag: `d`,
+ }},
+ }},
+ },
+ }, {
+ "good: two lists",
+ `(a) (b)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `a`,
+ }},
+ }, {
+ conditions: []Condition{{
+ Token: `b`,
+ }},
+ }},
+ },
+ }, {
+ "good: two Notted lists",
+ `(Not a) (Not b)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Not: true,
+ Token: `a`,
+ }},
+ }, {
+ conditions: []Condition{{
+ Not: true,
+ Token: `b`,
+ }},
+ }},
+ },
+ }, {
+ "section 7.5.1",
+ `<http://www.example.com/users/f/fielding/index.html>
+ (<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>)`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `http://www.example.com/users/f/fielding/index.html`,
+ conditions: []Condition{{
+ Token: `urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6`,
+ }},
+ }},
+ },
+ }, {
+ "section 7.5.2 #1",
+ `(<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`,
+ }},
+ }},
+ },
+ }, {
+ "section 7.5.2 #2",
+ `<http://example.com/locked/>
+ (<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>)`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `http://example.com/locked/`,
+ conditions: []Condition{{
+ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`,
+ }},
+ }},
+ },
+ }, {
+ "section 7.5.2 #3",
+ `<http://example.com/locked/member>
+ (<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>)`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `http://example.com/locked/member`,
+ conditions: []Condition{{
+ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`,
+ }},
+ }},
+ },
+ }, {
+ "section 9.9.6",
+ `(<urn:uuid:fe184f2e-6eec-41d0-c765-01adc56e6bb4>)
+ (<urn:uuid:e454f3f3-acdc-452a-56c7-00a5c91e4b77>)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `urn:uuid:fe184f2e-6eec-41d0-c765-01adc56e6bb4`,
+ }},
+ }, {
+ conditions: []Condition{{
+ Token: `urn:uuid:e454f3f3-acdc-452a-56c7-00a5c91e4b77`,
+ }},
+ }},
+ },
+ }, {
+ "section 9.10.8",
+ `(<urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4>)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.6",
+ `(<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>
+ ["I am an ETag"])
+ (["I am another ETag"])`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`,
+ }, {
+ ETag: `"I am an ETag"`,
+ }},
+ }, {
+ conditions: []Condition{{
+ ETag: `"I am another ETag"`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.7",
+ `(Not <urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>
+ <urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092>)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Not: true,
+ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`,
+ }, {
+ Token: `urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.8",
+ `(<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>)
+ (Not <DAV:no-lock>)`,
+ ifHeader{
+ lists: []ifList{{
+ conditions: []Condition{{
+ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`,
+ }},
+ }, {
+ conditions: []Condition{{
+ Not: true,
+ Token: `DAV:no-lock`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.9",
+ `</resource1>
+ (<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>
+ [W/"A weak ETag"]) (["strong ETag"])`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `/resource1`,
+ conditions: []Condition{{
+ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`,
+ }, {
+ ETag: `W/"A weak ETag"`,
+ }},
+ }, {
+ resourceTag: `/resource1`,
+ conditions: []Condition{{
+ ETag: `"strong ETag"`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.10",
+ `<http://www.example.com/specs/>
+ (<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>)`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `http://www.example.com/specs/`,
+ conditions: []Condition{{
+ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.11 #1",
+ `</specs/rfc2518.doc> (["4217"])`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `/specs/rfc2518.doc`,
+ conditions: []Condition{{
+ ETag: `"4217"`,
+ }},
+ }},
+ },
+ }, {
+ "section 10.4.11 #2",
+ `</specs/rfc2518.doc> (Not ["4217"])`,
+ ifHeader{
+ lists: []ifList{{
+ resourceTag: `/specs/rfc2518.doc`,
+ conditions: []Condition{{
+ Not: true,
+ ETag: `"4217"`,
+ }},
+ }},
+ },
+ }}
+
+ for _, tc := range testCases {
+ got, ok := parseIfHeader(strings.Replace(tc.input, "\n", "", -1))
+ if gotEmpty := reflect.DeepEqual(got, ifHeader{}); gotEmpty == ok {
+ t.Errorf("%s: should be different: empty header == %t, ok == %t", tc.desc, gotEmpty, ok)
+ continue
+ }
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("%s:\ngot %v\nwant %v", tc.desc, got, tc.want)
+ continue
+ }
+ }
+}
diff --git a/webdav/internal/xml/README b/webdav/internal/xml/README
new file mode 100644
index 0000000..89656f4
--- /dev/null
+++ b/webdav/internal/xml/README
@@ -0,0 +1,11 @@
+This is a fork of the encoding/xml package at ca1d6c4, the last commit before
+https://go.googlesource.com/go/+/c0d6d33 "encoding/xml: restore Go 1.4 name
+space behavior" made late in the lead-up to the Go 1.5 release.
+
+The list of encoding/xml changes is at
+https://go.googlesource.com/go/+log/master/src/encoding/xml
+
+This fork is temporary, and I (nigeltao) expect to revert it after Go 1.6 is
+released.
+
+See http://golang.org/issue/11841
diff --git a/webdav/internal/xml/atom_test.go b/webdav/internal/xml/atom_test.go
new file mode 100644
index 0000000..a712843
--- /dev/null
+++ b/webdav/internal/xml/atom_test.go
@@ -0,0 +1,56 @@
+// Copyright 2011 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.
+
+package xml
+
+import "time"
+
+var atomValue = &Feed{
+ XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
+ Title: "Example Feed",
+ Link: []Link{{Href: "http://example.org/"}},
+ Updated: ParseTime("2003-12-13T18:30:02Z"),
+ Author: Person{Name: "John Doe"},
+ Id: "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6",
+
+ Entry: []Entry{
+ {
+ Title: "Atom-Powered Robots Run Amok",
+ Link: []Link{{Href: "http://example.org/2003/12/13/atom03"}},
+ Id: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
+ Updated: ParseTime("2003-12-13T18:30:02Z"),
+ Summary: NewText("Some text."),
+ },
+ },
+}
+
+var atomXml = `` +
+ `<feed xmlns="http://www.w3.org/2005/Atom" updated="2003-12-13T18:30:02Z">` +
+ `<title>Example Feed</title>` +
+ `<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>` +
+ `<link href="http://example.org/"></link>` +
+ `<author><name>John Doe</name><uri></uri><email></email></author>` +
+ `<entry>` +
+ `<title>Atom-Powered Robots Run Amok</title>` +
+ `<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>` +
+ `<link href="http://example.org/2003/12/13/atom03"></link>` +
+ `<updated>2003-12-13T18:30:02Z</updated>` +
+ `<author><name></name><uri></uri><email></email></author>` +
+ `<summary>Some text.</summary>` +
+ `</entry>` +
+ `</feed>`
+
+func ParseTime(str string) time.Time {
+ t, err := time.Parse(time.RFC3339, str)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+func NewText(text string) Text {
+ return Text{
+ Body: text,
+ }
+}
diff --git a/webdav/internal/xml/example_test.go b/webdav/internal/xml/example_test.go
new file mode 100644
index 0000000..becedd5
--- /dev/null
+++ b/webdav/internal/xml/example_test.go
@@ -0,0 +1,151 @@
+// Copyright 2012 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.
+
+package xml_test
+
+import (
+ "encoding/xml"
+ "fmt"
+ "os"
+)
+
+func ExampleMarshalIndent() {
+ type Address struct {
+ City, State string
+ }
+ type Person struct {
+ XMLName xml.Name `xml:"person"`
+ Id int `xml:"id,attr"`
+ FirstName string `xml:"name>first"`
+ LastName string `xml:"name>last"`
+ Age int `xml:"age"`
+ Height float32 `xml:"height,omitempty"`
+ Married bool
+ Address
+ Comment string `xml:",comment"`
+ }
+
+ v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
+ v.Comment = " Need more details. "
+ v.Address = Address{"Hanga Roa", "Easter Island"}
+
+ output, err := xml.MarshalIndent(v, " ", " ")
+ if err != nil {
+ fmt.Printf("error: %v\n", err)
+ }
+
+ os.Stdout.Write(output)
+ // Output:
+ // <person id="13">
+ // <name>
+ // <first>John</first>
+ // <last>Doe</last>
+ // </name>
+ // <age>42</age>
+ // <Married>false</Married>
+ // <City>Hanga Roa</City>
+ // <State>Easter Island</State>
+ // <!-- Need more details. -->
+ // </person>
+}
+
+func ExampleEncoder() {
+ type Address struct {
+ City, State string
+ }
+ type Person struct {
+ XMLName xml.Name `xml:"person"`
+ Id int `xml:"id,attr"`
+ FirstName string `xml:"name>first"`
+ LastName string `xml:"name>last"`
+ Age int `xml:"age"`
+ Height float32 `xml:"height,omitempty"`
+ Married bool
+ Address
+ Comment string `xml:",comment"`
+ }
+
+ v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
+ v.Comment = " Need more details. "
+ v.Address = Address{"Hanga Roa", "Easter Island"}
+
+ enc := xml.NewEncoder(os.Stdout)
+ enc.Indent(" ", " ")
+ if err := enc.Encode(v); err != nil {
+ fmt.Printf("error: %v\n", err)
+ }
+
+ // Output:
+ // <person id="13">
+ // <name>
+ // <first>John</first>
+ // <last>Doe</last>
+ // </name>
+ // <age>42</age>
+ // <Married>false</Married>
+ // <City>Hanga Roa</City>
+ // <State>Easter Island</State>
+ // <!-- Need more details. -->
+ // </person>
+}
+
+// This example demonstrates unmarshaling an XML excerpt into a value with
+// some preset fields. Note that the Phone field isn't modified and that
+// the XML <Company> element is ignored. Also, the Groups field is assigned
+// considering the element path provided in its tag.
+func ExampleUnmarshal() {
+ type Email struct {
+ Where string `xml:"where,attr"`
+ Addr string
+ }
+ type Address struct {
+ City, State string
+ }
+ type Result struct {
+ XMLName xml.Name `xml:"Person"`
+ Name string `xml:"FullName"`
+ Phone string
+ Email []Email
+ Groups []string `xml:"Group>Value"`
+ Address
+ }
+ v := Result{Name: "none", Phone: "none"}
+
+ data := `
+ <Person>
+ <FullName>Grace R. Emlin</FullName>
+ <Company>Example Inc.</Company>
+ <Email where="home">
+ <Addr>gre@example.com</Addr>
+ </Email>
+ <Email where='work'>
+ <Addr>gre@work.com</Addr>
+ </Email>
+ <Group>
+ <Value>Friends</Value>
+ <Value>Squash</Value>
+ </Group>
+ <City>Hanga Roa</City>
+ <State>Easter Island</State>
+ </Person>
+ `
+ err := xml.Unmarshal([]byte(data), &v)
+ if err != nil {
+ fmt.Printf("error: %v", err)
+ return
+ }
+ fmt.Printf("XMLName: %#v\n", v.XMLName)
+ fmt.Printf("Name: %q\n", v.Name)
+ fmt.Printf("Phone: %q\n", v.Phone)
+ fmt.Printf("Email: %v\n", v.Email)
+ fmt.Printf("Groups: %v\n", v.Groups)
+ fmt.Printf("Address: %v\n", v.Address)
+ // Output:
+ // XMLName: xml.Name{Space:"", Local:"Person"}
+ // Name: "Grace R. Emlin"
+ // Phone: "none"
+ // Email: [{home gre@example.com} {work gre@work.com}]
+ // Groups: [Friends Squash]
+ // Address: {Hanga Roa Easter Island}
+}
diff --git a/webdav/internal/xml/marshal.go b/webdav/internal/xml/marshal.go
new file mode 100644
index 0000000..3c3b6ac
--- /dev/null
+++ b/webdav/internal/xml/marshal.go
@@ -0,0 +1,1223 @@
+// Copyright 2011 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.
+
+package xml
+
+import (
+ "bufio"
+ "bytes"
+ "encoding"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+const (
+ // A generic XML header suitable for use with the output of Marshal.
+ // This is not automatically added to any output of this package,
+ // it is provided as a convenience.
+ Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
+)
+
+// Marshal returns the XML encoding of v.
+//
+// Marshal handles an array or slice by marshalling each of the elements.
+// Marshal handles a pointer by marshalling the value it points at or, if the
+// pointer is nil, by writing nothing. Marshal handles an interface value by
+// marshalling the value it contains or, if the interface value is nil, by
+// writing nothing. Marshal handles all other data by writing one or more XML
+// elements containing the data.
+//
+// The name for the XML elements is taken from, in order of preference:
+// - the tag on the XMLName field, if the data is a struct
+// - the value of the XMLName field of type xml.Name
+// - the tag of the struct field used to obtain the data
+// - the name of the struct field used to obtain the data
+// - the name of the marshalled type
+//
+// The XML element for a struct contains marshalled elements for each of the
+// exported fields of the struct, with these exceptions:
+// - the XMLName field, described above, is omitted.
+// - a field with tag "-" is omitted.
+// - a field with tag "name,attr" becomes an attribute with
+// the given name in the XML element.
+// - a field with tag ",attr" becomes an attribute with the
+// field name in the XML element.
+// - a field with tag ",chardata" is written as character data,
+// not as an XML element.
+// - a field with tag ",innerxml" is written verbatim, not subject
+// to the usual marshalling procedure.
+// - a field with tag ",comment" is written as an XML comment, not
+// subject to the usual marshalling procedure. It must not contain
+// the "--" string within it.
+// - a field with a tag including the "omitempty" option is omitted
+// if the field value is empty. The empty values are false, 0, any
+// nil pointer or interface value, and any array, slice, map, or
+// string of length zero.
+// - an anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
+//
+// If a field uses a tag "a>b>c", then the element c will be nested inside
+// parent elements a and b. Fields that appear next to each other that name
+// the same parent will be enclosed in one XML element.
+//
+// See MarshalIndent for an example.
+//
+// Marshal will return an error if asked to marshal a channel, function, or map.
+func Marshal(v interface{}) ([]byte, error) {
+ var b bytes.Buffer
+ if err := NewEncoder(&b).Encode(v); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// Marshaler is the interface implemented by objects that can marshal
+// themselves into valid XML elements.
+//
+// MarshalXML encodes the receiver as zero or more XML elements.
+// By convention, arrays or slices are typically encoded as a sequence
+// of elements, one per entry.
+// Using start as the element tag is not required, but doing so
+// will enable Unmarshal to match the XML elements to the correct
+// struct field.
+// One common implementation strategy is to construct a separate
+// value with a layout corresponding to the desired XML and then
+// to encode it using e.EncodeElement.
+// Another common strategy is to use repeated calls to e.EncodeToken
+// to generate the XML output one token at a time.
+// The sequence of encoded tokens must make up zero or more valid
+// XML elements.
+type Marshaler interface {
+ MarshalXML(e *Encoder, start StartElement) error
+}
+
+// MarshalerAttr is the interface implemented by objects that can marshal
+// themselves into valid XML attributes.
+//
+// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver.
+// Using name as the attribute name is not required, but doing so
+// will enable Unmarshal to match the attribute to the correct
+// struct field.
+// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute
+// will be generated in the output.
+// MarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type MarshalerAttr interface {
+ MarshalXMLAttr(name Name) (Attr, error)
+}
+
+// MarshalIndent works like Marshal, but each XML element begins on a new
+// indented line that starts with prefix and is followed by one or more
+// copies of indent according to the nesting depth.
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
+ var b bytes.Buffer
+ enc := NewEncoder(&b)
+ enc.Indent(prefix, indent)
+ if err := enc.Encode(v); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// An Encoder writes XML data to an output stream.
+type Encoder struct {
+ p printer
+}
+
+// NewEncoder returns a new encoder that writes to w.
+func NewEncoder(w io.Writer) *Encoder {
+ e := &Encoder{printer{Writer: bufio.NewWriter(w)}}
+ e.p.encoder = e
+ return e
+}
+
+// Indent sets the encoder to generate XML in which each element
+// begins on a new indented line that starts with prefix and is followed by
+// one or more copies of indent according to the nesting depth.
+func (enc *Encoder) Indent(prefix, indent string) {
+ enc.p.prefix = prefix
+ enc.p.indent = indent
+}
+
+// Encode writes the XML encoding of v to the stream.
+//
+// See the documentation for Marshal for details about the conversion
+// of Go values to XML.
+//
+// Encode calls Flush before returning.
+func (enc *Encoder) Encode(v interface{}) error {
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil)
+ if err != nil {
+ return err
+ }
+ return enc.p.Flush()
+}
+
+// EncodeElement writes the XML encoding of v to the stream,
+// using start as the outermost tag in the encoding.
+//
+// See the documentation for Marshal for details about the conversion
+// of Go values to XML.
+//
+// EncodeElement calls Flush before returning.
+func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error {
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start)
+ if err != nil {
+ return err
+ }
+ return enc.p.Flush()
+}
+
+var (
+ begComment = []byte("<!--")
+ endComment = []byte("-->")
+ endProcInst = []byte("?>")
+ endDirective = []byte(">")
+)
+
+// EncodeToken writes the given XML token to the stream.
+// It returns an error if StartElement and EndElement tokens are not
+// properly matched.
+//
+// EncodeToken does not call Flush, because usually it is part of a
+// larger operation such as Encode or EncodeElement (or a custom
+// Marshaler's MarshalXML invoked during those), and those will call
+// Flush when finished. Callers that create an Encoder and then invoke
+// EncodeToken directly, without using Encode or EncodeElement, need to
+// call Flush when finished to ensure that the XML is written to the
+// underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only
+// as the first token in the stream.
+//
+// When encoding a StartElement holding an XML namespace prefix
+// declaration for a prefix that is not already declared, contained
+// elements (including the StartElement itself) will use the declared
+// prefix when encoding names with matching namespace URIs.
+func (enc *Encoder) EncodeToken(t Token) error {
+
+ p := &enc.p
+ switch t := t.(type) {
+ case StartElement:
+ if err := p.writeStart(&t); err != nil {
+ return err
+ }
+ case EndElement:
+ if err := p.writeEnd(t.Name); err != nil {
+ return err
+ }
+ case CharData:
+ escapeText(p, t, false)
+ case Comment:
+ if bytes.Contains(t, endComment) {
+ return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
+ }
+ p.WriteString("<!--")
+ p.Write(t)
+ p.WriteString("-->")
+ return p.cachedWriteError()
+ case ProcInst:
+ // First token to be encoded which is also a ProcInst with target of xml
+ // is the xml declaration. The only ProcInst where target of xml is allowed.
+ if t.Target == "xml" && p.Buffered() != 0 {
+ return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+ }
+ if !isNameString(t.Target) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
+ }
+ if bytes.Contains(t.Inst, endProcInst) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
+ }
+ p.WriteString("<?")
+ p.WriteString(t.Target)
+ if len(t.Inst) > 0 {
+ p.WriteByte(' ')
+ p.Write(t.Inst)
+ }
+ p.WriteString("?>")
+ case Directive:
+ if !isValidDirective(t) {
+ return fmt.Errorf("xml: EncodeToken of Directive containing wrong < or > markers")
+ }
+ p.WriteString("<!")
+ p.Write(t)
+ p.WriteString(">")
+ default:
+ return fmt.Errorf("xml: EncodeToken of invalid token type")
+
+ }
+ return p.cachedWriteError()
+}
+
+// isValidDirective reports whether dir is a valid directive text,
+// meaning angle brackets are matched, ignoring comments and strings.
+func isValidDirective(dir Directive) bool {
+ var (
+ depth int
+ inquote uint8
+ incomment bool
+ )
+ for i, c := range dir {
+ switch {
+ case incomment:
+ if c == '>' {
+ if n := 1 + i - len(endComment); n >= 0 && bytes.Equal(dir[n:i+1], endComment) {
+ incomment = false
+ }
+ }
+ // Just ignore anything in comment
+ case inquote != 0:
+ if c == inquote {
+ inquote = 0
+ }
+ // Just ignore anything within quotes
+ case c == '\'' || c == '"':
+ inquote = c
+ case c == '<':
+ if i+len(begComment) < len(dir) && bytes.Equal(dir[i:i+len(begComment)], begComment) {
+ incomment = true
+ } else {
+ depth++
+ }
+ case c == '>':
+ if depth == 0 {
+ return false
+ }
+ depth--
+ }
+ }
+ return depth == 0 && inquote == 0 && !incomment
+}
+
+// Flush flushes any buffered XML to the underlying writer.
+// See the EncodeToken documentation for details about when it is necessary.
+func (enc *Encoder) Flush() error {
+ return enc.p.Flush()
+}
+
+type printer struct {
+ *bufio.Writer
+ encoder *Encoder
+ seq int
+ indent string
+ prefix string
+ depth int
+ indentedIn bool
+ putNewline bool
+ defaultNS string
+ attrNS map[string]string // map prefix -> name space
+ attrPrefix map[string]string // map name space -> prefix
+ prefixes []printerPrefix
+ tags []Name
+}
+
+// printerPrefix holds a namespace undo record.
+// When an element is popped, the prefix record
+// is set back to the recorded URL. The empty
+// prefix records the URL for the default name space.
+//
+// The start of an element is recorded with an element
+// that has mark=true.
+type printerPrefix struct {
+ prefix string
+ url string
+ mark bool
+}
+
+func (p *printer) prefixForNS(url string, isAttr bool) string {
+ // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
+ // and must be referred to that way.
+ // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
+ // but users should not be trying to use that one directly - that's our job.)
+ if url == xmlURL {
+ return "xml"
+ }
+ if !isAttr && url == p.defaultNS {
+ // We can use the default name space.
+ return ""
+ }
+ return p.attrPrefix[url]
+}
+
+// defineNS pushes any namespace definition found in the given attribute.
+// If ignoreNonEmptyDefault is true, an xmlns="nonempty"
+// attribute will be ignored.
+func (p *printer) defineNS(attr Attr, ignoreNonEmptyDefault bool) error {
+ var prefix string
+ if attr.Name.Local == "xmlns" {
+ if attr.Name.Space != "" && attr.Name.Space != "xml" && attr.Name.Space != xmlURL {
+ return fmt.Errorf("xml: cannot redefine xmlns attribute prefix")
+ }
+ } else if attr.Name.Space == "xmlns" && attr.Name.Local != "" {
+ prefix = attr.Name.Local
+ if attr.Value == "" {
+ // Technically, an empty XML namespace is allowed for an attribute.
+ // From http://www.w3.org/TR/xml-names11/#scoping-defaulting:
+ //
+ // The attribute value in a namespace declaration for a prefix may be
+ // empty. This has the effect, within the scope of the declaration, of removing
+ // any association of the prefix with a namespace name.
+ //
+ // However our namespace prefixes here are used only as hints. There's
+ // no need to respect the removal of a namespace prefix, so we ignore it.
+ return nil
+ }
+ } else {
+ // Ignore: it's not a namespace definition
+ return nil
+ }
+ if prefix == "" {
+ if attr.Value == p.defaultNS {
+ // No need for redefinition.
+ return nil
+ }
+ if attr.Value != "" && ignoreNonEmptyDefault {
+ // We have an xmlns="..." value but
+ // it can't define a name space in this context,
+ // probably because the element has an empty
+ // name space. In this case, we just ignore
+ // the name space declaration.
+ return nil
+ }
+ } else if _, ok := p.attrPrefix[attr.Value]; ok {
+ // There's already a prefix for the given name space,
+ // so use that. This prevents us from
+ // having two prefixes for the same name space
+ // so attrNS and attrPrefix can remain bijective.
+ return nil
+ }
+ p.pushPrefix(prefix, attr.Value)
+ return nil
+}
+
+// createNSPrefix creates a name space prefix attribute
+// to use for the given name space, defining a new prefix
+// if necessary.
+// If isAttr is true, the prefix is to be created for an attribute
+// prefix, which means that the default name space cannot
+// be used.
+func (p *printer) createNSPrefix(url string, isAttr bool) {
+ if _, ok := p.attrPrefix[url]; ok {
+ // We already have a prefix for the given URL.
+ return
+ }
+ switch {
+ case !isAttr && url == p.defaultNS:
+ // We can use the default name space.
+ return
+ case url == "":
+ // The only way we can encode names in the empty
+ // name space is by using the default name space,
+ // so we must use that.
+ if p.defaultNS != "" {
+ // The default namespace is non-empty, so we
+ // need to set it to empty.
+ p.pushPrefix("", "")
+ }
+ return
+ case url == xmlURL:
+ return
+ }
+ // TODO If the URL is an existing prefix, we could
+ // use it as is. That would enable the
+ // marshaling of elements that had been unmarshaled
+ // and with a name space prefix that was not found.
+ // although technically it would be incorrect.
+
+ // Pick a name. We try to use the final element of the path
+ // but fall back to _.
+ prefix := strings.TrimRight(url, "/")
+ if i := strings.LastIndex(prefix, "/"); i >= 0 {
+ prefix = prefix[i+1:]
+ }
+ if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
+ prefix = "_"
+ }
+ if strings.HasPrefix(prefix, "xml") {
+ // xmlanything is reserved.
+ prefix = "_" + prefix
+ }
+ if p.attrNS[prefix] != "" {
+ // Name is taken. Find a better one.
+ for p.seq++; ; p.seq++ {
+ if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
+ prefix = id
+ break
+ }
+ }
+ }
+
+ p.pushPrefix(prefix, url)
+}
+
+// writeNamespaces writes xmlns attributes for all the
+// namespace prefixes that have been defined in
+// the current element.
+func (p *printer) writeNamespaces() {
+ for i := len(p.prefixes) - 1; i >= 0; i-- {
+ prefix := p.prefixes[i]
+ if prefix.mark {
+ return
+ }
+ p.WriteString(" ")
+ if prefix.prefix == "" {
+ // Default name space.
+ p.WriteString(`xmlns="`)
+ } else {
+ p.WriteString("xmlns:")
+ p.WriteString(prefix.prefix)
+ p.WriteString(`="`)
+ }
+ EscapeText(p, []byte(p.nsForPrefix(prefix.prefix)))
+ p.WriteString(`"`)
+ }
+}
+
+// pushPrefix pushes a new prefix on the prefix stack
+// without checking to see if it is already defined.
+func (p *printer) pushPrefix(prefix, url string) {
+ p.prefixes = append(p.prefixes, printerPrefix{
+ prefix: prefix,
+ url: p.nsForPrefix(prefix),
+ })
+ p.setAttrPrefix(prefix, url)
+}
+
+// nsForPrefix returns the name space for the given
+// prefix. Note that this is not valid for the
+// empty attribute prefix, which always has an empty
+// name space.
+func (p *printer) nsForPrefix(prefix string) string {
+ if prefix == "" {
+ return p.defaultNS
+ }
+ return p.attrNS[prefix]
+}
+
+// markPrefix marks the start of an element on the prefix
+// stack.
+func (p *printer) markPrefix() {
+ p.prefixes = append(p.prefixes, printerPrefix{
+ mark: true,
+ })
+}
+
+// popPrefix pops all defined prefixes for the current
+// element.
+func (p *printer) popPrefix() {
+ for len(p.prefixes) > 0 {
+ prefix := p.prefixes[len(p.prefixes)-1]
+ p.prefixes = p.prefixes[:len(p.prefixes)-1]
+ if prefix.mark {
+ break
+ }
+ p.setAttrPrefix(prefix.prefix, prefix.url)
+ }
+}
+
+// setAttrPrefix sets an attribute name space prefix.
+// If url is empty, the attribute is removed.
+// If prefix is empty, the default name space is set.
+func (p *printer) setAttrPrefix(prefix, url string) {
+ if prefix == "" {
+ p.defaultNS = url
+ return
+ }
+ if url == "" {
+ delete(p.attrPrefix, p.attrNS[prefix])
+ delete(p.attrNS, prefix)
+ return
+ }
+ if p.attrPrefix == nil {
+ // Need to define a new name space.
+ p.attrPrefix = make(map[string]string)
+ p.attrNS = make(map[string]string)
+ }
+ // Remove any old prefix value. This is OK because we maintain a
+ // strict one-to-one mapping between prefix and URL (see
+ // defineNS)
+ delete(p.attrPrefix, p.attrNS[prefix])
+ p.attrPrefix[url] = prefix
+ p.attrNS[prefix] = url
+}
+
+var (
+ marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem()
+ textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+)
+
+// marshalValue writes one or more XML elements representing val.
+// If val was obtained from a struct field, finfo must have its details.
+func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
+ if startTemplate != nil && startTemplate.Name.Local == "" {
+ return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
+ }
+
+ if !val.IsValid() {
+ return nil
+ }
+ if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
+ return nil
+ }
+
+ // Drill into interfaces and pointers.
+ // This can turn into an infinite loop given a cyclic chain,
+ // but it matches the Go 1 behavior.
+ for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ return nil
+ }
+ val = val.Elem()
+ }
+
+ kind := val.Kind()
+ typ := val.Type()
+
+ // Check for marshaler.
+ if val.CanInterface() && typ.Implements(marshalerType) {
+ return p.marshalInterface(val.Interface().(Marshaler), p.defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerType) {
+ return p.marshalInterface(pv.Interface().(Marshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
+ }
+ }
+
+ // Check for text marshaler.
+ if val.CanInterface() && typ.Implements(textMarshalerType) {
+ return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), p.defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
+ }
+ }
+
+ // Slices and arrays iterate over the elements. They do not have an enclosing tag.
+ if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
+ for i, n := 0, val.Len(); i < n; i++ {
+ if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ tinfo, err := getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Create start element.
+ // Precedence for the XML element name is:
+ // 0. startTemplate
+ // 1. XMLName field in underlying struct;
+ // 2. field name/tag in the struct field; and
+ // 3. type name
+ var start StartElement
+
+ // explicitNS records whether the element's name space has been
+ // explicitly set (for example an XMLName field).
+ explicitNS := false
+
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ explicitNS = true
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if tinfo.xmlname != nil {
+ xmlname := tinfo.xmlname
+ if xmlname.name != "" {
+ start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
+ } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
+ start.Name = v
+ }
+ explicitNS = true
+ }
+ if start.Name.Local == "" && finfo != nil {
+ start.Name.Local = finfo.name
+ if finfo.xmlns != "" {
+ start.Name.Space = finfo.xmlns
+ explicitNS = true
+ }
+ }
+ if start.Name.Local == "" {
+ name := typ.Name()
+ if name == "" {
+ return &UnsupportedTypeError{typ}
+ }
+ start.Name.Local = name
+ }
+
+ // defaultNS records the default name space as set by a xmlns="..."
+ // attribute. We don't set p.defaultNS because we want to let
+ // the attribute writing code (in p.defineNS) be solely responsible
+ // for maintaining that.
+ defaultNS := p.defaultNS
+
+ // Attributes
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fAttr == 0 {
+ continue
+ }
+ attr, err := p.fieldAttr(finfo, val)
+ if err != nil {
+ return err
+ }
+ if attr.Name.Local == "" {
+ continue
+ }
+ start.Attr = append(start.Attr, attr)
+ if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
+ defaultNS = attr.Value
+ }
+ }
+ if !explicitNS {
+ // Historic behavior: elements use the default name space
+ // they are contained in by default.
+ start.Name.Space = defaultNS
+ }
+ // Historic behaviour: an element that's in a namespace sets
+ // the default namespace for all elements contained within it.
+ start.setDefaultNamespace()
+
+ if err := p.writeStart(&start); err != nil {
+ return err
+ }
+
+ if val.Kind() == reflect.Struct {
+ err = p.marshalStruct(tinfo, val)
+ } else {
+ s, b, err1 := p.marshalSimple(typ, val)
+ if err1 != nil {
+ err = err1
+ } else if b != nil {
+ EscapeText(p, b)
+ } else {
+ p.EscapeString(s)
+ }
+ }
+ if err != nil {
+ return err
+ }
+
+ if err := p.writeEnd(start.Name); err != nil {
+ return err
+ }
+
+ return p.cachedWriteError()
+}
+
+// fieldAttr returns the attribute of the given field.
+// If the returned attribute has an empty Name.Local,
+// it should not be used.
+// The given value holds the value containing the field.
+func (p *printer) fieldAttr(finfo *fieldInfo, val reflect.Value) (Attr, error) {
+ fv := finfo.value(val)
+ name := Name{Space: finfo.xmlns, Local: finfo.name}
+ if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
+ return Attr{}, nil
+ }
+ if fv.Kind() == reflect.Interface && fv.IsNil() {
+ return Attr{}, nil
+ }
+ if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
+ attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, err
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
+ attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, err
+ }
+ }
+ if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
+ text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, err
+ }
+ return Attr{name, string(text)}, nil
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, err
+ }
+ return Attr{name, string(text)}, nil
+ }
+ }
+ // Dereference or skip nil pointer, interface values.
+ switch fv.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if fv.IsNil() {
+ return Attr{}, nil
+ }
+ fv = fv.Elem()
+ }
+ s, b, err := p.marshalSimple(fv.Type(), fv)
+ if err != nil {
+ return Attr{}, err
+ }
+ if b != nil {
+ s = string(b)
+ }
+ return Attr{name, s}, nil
+}
+
+// defaultStart returns the default start element to use,
+// given the reflect type, field info, and start template.
+func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
+ var start StartElement
+ // Precedence for the XML element name is as above,
+ // except that we do not look inside structs for the first field.
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if finfo != nil && finfo.name != "" {
+ start.Name.Local = finfo.name
+ start.Name.Space = finfo.xmlns
+ } else if typ.Name() != "" {
+ start.Name.Local = typ.Name()
+ } else {
+ // Must be a pointer to a named type,
+ // since it has the Marshaler methods.
+ start.Name.Local = typ.Elem().Name()
+ }
+ // Historic behaviour: elements use the name space of
+ // the element they are contained in by default.
+ if start.Name.Space == "" {
+ start.Name.Space = p.defaultNS
+ }
+ start.setDefaultNamespace()
+ return start
+}
+
+// marshalInterface marshals a Marshaler interface value.
+func (p *printer) marshalInterface(val Marshaler, start StartElement) error {
+ // Push a marker onto the tag stack so that MarshalXML
+ // cannot close the XML tags that it did not open.
+ p.tags = append(p.tags, Name{})
+ n := len(p.tags)
+
+ err := val.MarshalXML(p.encoder, start)
+ if err != nil {
+ return err
+ }
+
+ // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark.
+ if len(p.tags) > n {
+ return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local)
+ }
+ p.tags = p.tags[:n-1]
+ return nil
+}
+
+// marshalTextInterface marshals a TextMarshaler interface value.
+func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error {
+ if err := p.writeStart(&start); err != nil {
+ return err
+ }
+ text, err := val.MarshalText()
+ if err != nil {
+ return err
+ }
+ EscapeText(p, text)
+ return p.writeEnd(start.Name)
+}
+
+// writeStart writes the given start element.
+func (p *printer) writeStart(start *StartElement) error {
+ if start.Name.Local == "" {
+ return fmt.Errorf("xml: start tag with no name")
+ }
+
+ p.tags = append(p.tags, start.Name)
+ p.markPrefix()
+ // Define any name spaces explicitly declared in the attributes.
+ // We do this as a separate pass so that explicitly declared prefixes
+ // will take precedence over implicitly declared prefixes
+ // regardless of the order of the attributes.
+ ignoreNonEmptyDefault := start.Name.Space == ""
+ for _, attr := range start.Attr {
+ if err := p.defineNS(attr, ignoreNonEmptyDefault); err != nil {
+ return err
+ }
+ }
+ // Define any new name spaces implied by the attributes.
+ for _, attr := range start.Attr {
+ name := attr.Name
+ // From http://www.w3.org/TR/xml-names11/#defaulting
+ // "Default namespace declarations do not apply directly
+ // to attribute names; the interpretation of unprefixed
+ // attributes is determined by the element on which they
+ // appear."
+ // This means we don't need to create a new namespace
+ // when an attribute name space is empty.
+ if name.Space != "" && !name.isNamespace() {
+ p.createNSPrefix(name.Space, true)
+ }
+ }
+ p.createNSPrefix(start.Name.Space, false)
+
+ p.writeIndent(1)
+ p.WriteByte('<')
+ p.writeName(start.Name, false)
+ p.writeNamespaces()
+ for _, attr := range start.Attr {
+ name := attr.Name
+ if name.Local == "" || name.isNamespace() {
+ // Namespaces have already been written by writeNamespaces above.
+ continue
+ }
+ p.WriteByte(' ')
+ p.writeName(name, true)
+ p.WriteString(`="`)
+ p.EscapeString(attr.Value)
+ p.WriteByte('"')
+ }
+ p.WriteByte('>')
+ return nil
+}
+
+// writeName writes the given name. It assumes
+// that p.createNSPrefix(name) has already been called.
+func (p *printer) writeName(name Name, isAttr bool) {
+ if prefix := p.prefixForNS(name.Space, isAttr); prefix != "" {
+ p.WriteString(prefix)
+ p.WriteByte(':')
+ }
+ p.WriteString(name.Local)
+}
+
+func (p *printer) writeEnd(name Name) error {
+ if name.Local == "" {
+ return fmt.Errorf("xml: end tag with no name")
+ }
+ if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" {
+ return fmt.Errorf("xml: end tag </%s> without start tag", name.Local)
+ }
+ if top := p.tags[len(p.tags)-1]; top != name {
+ if top.Local != name.Local {
+ return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local)
+ }
+ return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space)
+ }
+ p.tags = p.tags[:len(p.tags)-1]
+
+ p.writeIndent(-1)
+ p.WriteByte('<')
+ p.WriteByte('/')
+ p.writeName(name, false)
+ p.WriteByte('>')
+ p.popPrefix()
+ return nil
+}
+
+func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) {
+ switch val.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.FormatInt(val.Int(), 10), nil, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return strconv.FormatUint(val.Uint(), 10), nil, nil
+ case reflect.Float32, reflect.Float64:
+ return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil
+ case reflect.String:
+ return val.String(), nil, nil
+ case reflect.Bool:
+ return strconv.FormatBool(val.Bool()), nil, nil
+ case reflect.Array:
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // [...]byte
+ var bytes []byte
+ if val.CanAddr() {
+ bytes = val.Slice(0, val.Len()).Bytes()
+ } else {
+ bytes = make([]byte, val.Len())
+ reflect.Copy(reflect.ValueOf(bytes), val)
+ }
+ return "", bytes, nil
+ case reflect.Slice:
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // []byte
+ return "", val.Bytes(), nil
+ }
+ return "", nil, &UnsupportedTypeError{typ}
+}
+
+var ddBytes = []byte("--")
+
+func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
+ s := parentStack{p: p}
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fAttr != 0 {
+ continue
+ }
+ vf := finfo.value(val)
+
+ // Dereference or skip nil pointer, interface values.
+ switch vf.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if !vf.IsNil() {
+ vf = vf.Elem()
+ }
+ }
+
+ switch finfo.flags & fMode {
+ case fCharData:
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ if vf.CanInterface() && vf.Type().Implements(textMarshalerType) {
+ data, err := vf.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ if vf.CanAddr() {
+ pv := vf.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ data, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ }
+ var scratch [64]byte
+ switch vf.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
+ case reflect.Float32, reflect.Float64:
+ Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
+ case reflect.Bool:
+ Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
+ case reflect.String:
+ if err := EscapeText(p, []byte(vf.String())); err != nil {
+ return err
+ }
+ case reflect.Slice:
+ if elem, ok := vf.Interface().([]byte); ok {
+ if err := EscapeText(p, elem); err != nil {
+ return err
+ }
+ }
+ }
+ continue
+
+ case fComment:
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ k := vf.Kind()
+ if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
+ return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
+ }
+ if vf.Len() == 0 {
+ continue
+ }
+ p.writeIndent(0)
+ p.WriteString("<!--")
+ dashDash := false
+ dashLast := false
+ switch k {
+ case reflect.String:
+ s := vf.String()
+ dashDash = strings.Index(s, "--") >= 0
+ dashLast = s[len(s)-1] == '-'
+ if !dashDash {
+ p.WriteString(s)
+ }
+ case reflect.Slice:
+ b := vf.Bytes()
+ dashDash = bytes.Index(b, ddBytes) >= 0
+ dashLast = b[len(b)-1] == '-'
+ if !dashDash {
+ p.Write(b)
+ }
+ default:
+ panic("can't happen")
+ }
+ if dashDash {
+ return fmt.Errorf(`xml: comments must not contain "--"`)
+ }
+ if dashLast {
+ // "--->" is invalid grammar. Make it "- -->"
+ p.WriteByte(' ')
+ }
+ p.WriteString("-->")
+ continue
+
+ case fInnerXml:
+ iface := vf.Interface()
+ switch raw := iface.(type) {
+ case []byte:
+ p.Write(raw)
+ continue
+ case string:
+ p.WriteString(raw)
+ continue
+ }
+
+ case fElement, fElement | fAny:
+ if err := s.setParents(finfo, vf); err != nil {
+ return err
+ }
+ }
+ if err := p.marshalValue(vf, finfo, nil); err != nil {
+ return err
+ }
+ }
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ return p.cachedWriteError()
+}
+
+var noField fieldInfo
+
+// return the bufio Writer's cached write error
+func (p *printer) cachedWriteError() error {
+ _, err := p.Write(nil)
+ return err
+}
+
+func (p *printer) writeIndent(depthDelta int) {
+ if len(p.prefix) == 0 && len(p.indent) == 0 {
+ return
+ }
+ if depthDelta < 0 {
+ p.depth--
+ if p.indentedIn {
+ p.indentedIn = false
+ return
+ }
+ p.indentedIn = false
+ }
+ if p.putNewline {
+ p.WriteByte('\n')
+ } else {
+ p.putNewline = true
+ }
+ if len(p.prefix) > 0 {
+ p.WriteString(p.prefix)
+ }
+ if len(p.indent) > 0 {
+ for i := 0; i < p.depth; i++ {
+ p.WriteString(p.indent)
+ }
+ }
+ if depthDelta > 0 {
+ p.depth++
+ p.indentedIn = true
+ }
+}
+
+type parentStack struct {
+ p *printer
+ xmlns string
+ parents []string
+}
+
+// setParents sets the stack of current parents to those found in finfo.
+// It only writes the start elements if vf holds a non-nil value.
+// If finfo is &noField, it pops all elements.
+func (s *parentStack) setParents(finfo *fieldInfo, vf reflect.Value) error {
+ xmlns := s.p.defaultNS
+ if finfo.xmlns != "" {
+ xmlns = finfo.xmlns
+ }
+ commonParents := 0
+ if xmlns == s.xmlns {
+ for ; commonParents < len(finfo.parents) && commonParents < len(s.parents); commonParents++ {
+ if finfo.parents[commonParents] != s.parents[commonParents] {
+ break
+ }
+ }
+ }
+ // Pop off any parents that aren't in common with the previous field.
+ for i := len(s.parents) - 1; i >= commonParents; i-- {
+ if err := s.p.writeEnd(Name{
+ Space: s.xmlns,
+ Local: s.parents[i],
+ }); err != nil {
+ return err
+ }
+ }
+ s.parents = finfo.parents
+ s.xmlns = xmlns
+ if commonParents >= len(s.parents) {
+ // No new elements to push.
+ return nil
+ }
+ if (vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) && vf.IsNil() {
+ // The element is nil, so no need for the start elements.
+ s.parents = s.parents[:commonParents]
+ return nil
+ }
+ // Push any new parents required.
+ for _, name := range s.parents[commonParents:] {
+ start := &StartElement{
+ Name: Name{
+ Space: s.xmlns,
+ Local: name,
+ },
+ }
+ // Set the default name space for parent elements
+ // to match what we do with other elements.
+ if s.xmlns != s.p.defaultNS {
+ start.setDefaultNamespace()
+ }
+ if err := s.p.writeStart(start); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// A MarshalXMLError is returned when Marshal encounters a type
+// that cannot be converted into XML.
+type UnsupportedTypeError struct {
+ Type reflect.Type
+}
+
+func (e *UnsupportedTypeError) Error() string {
+ return "xml: unsupported type: " + e.Type.String()
+}
+
+func isEmptyValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
diff --git a/webdav/internal/xml/marshal_test.go b/webdav/internal/xml/marshal_test.go
new file mode 100644
index 0000000..5dc78e7
--- /dev/null
+++ b/webdav/internal/xml/marshal_test.go
@@ -0,0 +1,1939 @@
+// Copyright 2011 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.
+
+package xml
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+type DriveType int
+
+const (
+ HyperDrive DriveType = iota
+ ImprobabilityDrive
+)
+
+type Passenger struct {
+ Name []string `xml:"name"`
+ Weight float32 `xml:"weight"`
+}
+
+type Ship struct {
+ XMLName struct{} `xml:"spaceship"`
+
+ Name string `xml:"name,attr"`
+ Pilot string `xml:"pilot,attr"`
+ Drive DriveType `xml:"drive"`
+ Age uint `xml:"age"`
+ Passenger []*Passenger `xml:"passenger"`
+ secret string
+}
+
+type NamedType string
+
+type Port struct {
+ XMLName struct{} `xml:"port"`
+ Type string `xml:"type,attr,omitempty"`
+ Comment string `xml:",comment"`
+ Number string `xml:",chardata"`
+}
+
+type Domain struct {
+ XMLName struct{} `xml:"domain"`
+ Country string `xml:",attr,omitempty"`
+ Name []byte `xml:",chardata"`
+ Comment []byte `xml:",comment"`
+}
+
+type Book struct {
+ XMLName struct{} `xml:"book"`
+ Title string `xml:",chardata"`
+}
+
+type Event struct {
+ XMLName struct{} `xml:"event"`
+ Year int `xml:",chardata"`
+}
+
+type Movie struct {
+ XMLName struct{} `xml:"movie"`
+ Length uint `xml:",chardata"`
+}
+
+type Pi struct {
+ XMLName struct{} `xml:"pi"`
+ Approximation float32 `xml:",chardata"`
+}
+
+type Universe struct {
+ XMLName struct{} `xml:"universe"`
+ Visible float64 `xml:",chardata"`
+}
+
+type Particle struct {
+ XMLName struct{} `xml:"particle"`
+ HasMass bool `xml:",chardata"`
+}
+
+type Departure struct {
+ XMLName struct{} `xml:"departure"`
+ When time.Time `xml:",chardata"`
+}
+
+type SecretAgent struct {
+ XMLName struct{} `xml:"agent"`
+ Handle string `xml:"handle,attr"`
+ Identity string
+ Obfuscate string `xml:",innerxml"`
+}
+
+type NestedItems struct {
+ XMLName struct{} `xml:"result"`
+ Items []string `xml:">item"`
+ Item1 []string `xml:"Items>item1"`
+}
+
+type NestedOrder struct {
+ XMLName struct{} `xml:"result"`
+ Field1 string `xml:"parent>c"`
+ Field2 string `xml:"parent>b"`
+ Field3 string `xml:"parent>a"`
+}
+
+type MixedNested struct {
+ XMLName struct{} `xml:"result"`
+ A string `xml:"parent1>a"`
+ B string `xml:"b"`
+ C string `xml:"parent1>parent2>c"`
+ D string `xml:"parent1>d"`
+}
+
+type NilTest struct {
+ A interface{} `xml:"parent1>parent2>a"`
+ B interface{} `xml:"parent1>b"`
+ C interface{} `xml:"parent1>parent2>c"`
+}
+
+type Service struct {
+ XMLName struct{} `xml:"service"`
+ Domain *Domain `xml:"host>domain"`
+ Port *Port `xml:"host>port"`
+ Extra1 interface{}
+ Extra2 interface{} `xml:"host>extra2"`
+}
+
+var nilStruct *Ship
+
+type EmbedA struct {
+ EmbedC
+ EmbedB EmbedB
+ FieldA string
+}
+
+type EmbedB struct {
+ FieldB string
+ *EmbedC
+}
+
+type EmbedC struct {
+ FieldA1 string `xml:"FieldA>A1"`
+ FieldA2 string `xml:"FieldA>A2"`
+ FieldB string
+ FieldC string
+}
+
+type NameCasing struct {
+ XMLName struct{} `xml:"casing"`
+ Xy string
+ XY string
+ XyA string `xml:"Xy,attr"`
+ XYA string `xml:"XY,attr"`
+}
+
+type NamePrecedence struct {
+ XMLName Name `xml:"Parent"`
+ FromTag XMLNameWithoutTag `xml:"InTag"`
+ FromNameVal XMLNameWithoutTag
+ FromNameTag XMLNameWithTag
+ InFieldName string
+}
+
+type XMLNameWithTag struct {
+ XMLName Name `xml:"InXMLNameTag"`
+ Value string `xml:",chardata"`
+}
+
+type XMLNameWithNSTag struct {
+ XMLName Name `xml:"ns InXMLNameWithNSTag"`
+ Value string `xml:",chardata"`
+}
+
+type XMLNameWithoutTag struct {
+ XMLName Name
+ Value string `xml:",chardata"`
+}
+
+type NameInField struct {
+ Foo Name `xml:"ns foo"`
+}
+
+type AttrTest struct {
+ Int int `xml:",attr"`
+ Named int `xml:"int,attr"`
+ Float float64 `xml:",attr"`
+ Uint8 uint8 `xml:",attr"`
+ Bool bool `xml:",attr"`
+ Str string `xml:",attr"`
+ Bytes []byte `xml:",attr"`
+}
+
+type OmitAttrTest struct {
+ Int int `xml:",attr,omitempty"`
+ Named int `xml:"int,attr,omitempty"`
+ Float float64 `xml:",attr,omitempty"`
+ Uint8 uint8 `xml:",attr,omitempty"`
+ Bool bool `xml:",attr,omitempty"`
+ Str string `xml:",attr,omitempty"`
+ Bytes []byte `xml:",attr,omitempty"`
+}
+
+type OmitFieldTest struct {
+ Int int `xml:",omitempty"`
+ Named int `xml:"int,omitempty"`
+ Float float64 `xml:",omitempty"`
+ Uint8 uint8 `xml:",omitempty"`
+ Bool bool `xml:",omitempty"`
+ Str string `xml:",omitempty"`
+ Bytes []byte `xml:",omitempty"`
+ Ptr *PresenceTest `xml:",omitempty"`
+}
+
+type AnyTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField AnyHolder `xml:",any"`
+}
+
+type AnyOmitTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField *AnyHolder `xml:",any,omitempty"`
+}
+
+type AnySliceTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField []AnyHolder `xml:",any"`
+}
+
+type AnyHolder struct {
+ XMLName Name
+ XML string `xml:",innerxml"`
+}
+
+type RecurseA struct {
+ A string
+ B *RecurseB
+}
+
+type RecurseB struct {
+ A *RecurseA
+ B string
+}
+
+type PresenceTest struct {
+ Exists *struct{}
+}
+
+type IgnoreTest struct {
+ PublicSecret string `xml:"-"`
+}
+
+type MyBytes []byte
+
+type Data struct {
+ Bytes []byte
+ Attr []byte `xml:",attr"`
+ Custom MyBytes
+}
+
+type Plain struct {
+ V interface{}
+}
+
+type MyInt int
+
+type EmbedInt struct {
+ MyInt
+}
+
+type Strings struct {
+ X []string `xml:"A>B,omitempty"`
+}
+
+type PointerFieldsTest struct {
+ XMLName Name `xml:"dummy"`
+ Name *string `xml:"name,attr"`
+ Age *uint `xml:"age,attr"`
+ Empty *string `xml:"empty,attr"`
+ Contents *string `xml:",chardata"`
+}
+
+type ChardataEmptyTest struct {
+ XMLName Name `xml:"test"`
+ Contents *string `xml:",chardata"`
+}
+
+type MyMarshalerTest struct {
+}
+
+var _ Marshaler = (*MyMarshalerTest)(nil)
+
+func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
+ e.EncodeToken(start)
+ e.EncodeToken(CharData([]byte("hello world")))
+ e.EncodeToken(EndElement{start.Name})
+ return nil
+}
+
+type MyMarshalerAttrTest struct{}
+
+var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
+
+func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
+ return Attr{name, "hello world"}, nil
+}
+
+type MyMarshalerValueAttrTest struct{}
+
+var _ MarshalerAttr = MyMarshalerValueAttrTest{}
+
+func (m MyMarshalerValueAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
+ return Attr{name, "hello world"}, nil
+}
+
+type MarshalerStruct struct {
+ Foo MyMarshalerAttrTest `xml:",attr"`
+}
+
+type MarshalerValueStruct struct {
+ Foo MyMarshalerValueAttrTest `xml:",attr"`
+}
+
+type InnerStruct struct {
+ XMLName Name `xml:"testns outer"`
+}
+
+type OuterStruct struct {
+ InnerStruct
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterNamedStruct struct {
+ InnerStruct
+ XMLName Name `xml:"outerns test"`
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterNamedOrderedStruct struct {
+ XMLName Name `xml:"outerns test"`
+ InnerStruct
+ IntAttr int `xml:"int,attr"`
+}
+
+type OuterOuterStruct struct {
+ OuterStruct
+}
+
+type NestedAndChardata struct {
+ AB []string `xml:"A>B"`
+ Chardata string `xml:",chardata"`
+}
+
+type NestedAndComment struct {
+ AB []string `xml:"A>B"`
+ Comment string `xml:",comment"`
+}
+
+type XMLNSFieldStruct struct {
+ Ns string `xml:"xmlns,attr"`
+ Body string
+}
+
+type NamedXMLNSFieldStruct struct {
+ XMLName struct{} `xml:"testns test"`
+ Ns string `xml:"xmlns,attr"`
+ Body string
+}
+
+type XMLNSFieldStructWithOmitEmpty struct {
+ Ns string `xml:"xmlns,attr,omitempty"`
+ Body string
+}
+
+type NamedXMLNSFieldStructWithEmptyNamespace struct {
+ XMLName struct{} `xml:"test"`
+ Ns string `xml:"xmlns,attr"`
+ Body string
+}
+
+type RecursiveXMLNSFieldStruct struct {
+ Ns string `xml:"xmlns,attr"`
+ Body *RecursiveXMLNSFieldStruct `xml:",omitempty"`
+ Text string `xml:",omitempty"`
+}
+
+func ifaceptr(x interface{}) interface{} {
+ return &x
+}
+
+var (
+ nameAttr = "Sarah"
+ ageAttr = uint(12)
+ contentsAttr = "lorem ipsum"
+)
+
+// Unless explicitly stated as such (or *Plain), all of the
+// tests below are two-way tests. When introducing new tests,
+// please try to make them two-way as well to ensure that
+// marshalling and unmarshalling are as symmetrical as feasible.
+var marshalTests = []struct {
+ Value interface{}
+ ExpectXML string
+ MarshalOnly bool
+ UnmarshalOnly bool
+}{
+ // Test nil marshals to nothing
+ {Value: nil, ExpectXML: ``, MarshalOnly: true},
+ {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
+
+ // Test value types
+ {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
+ {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
+ {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
+ {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
+ {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
+ {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
+ {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
+ {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
+ {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`},
+ {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`},
+ {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`},
+ {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
+ {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
+ {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
+ {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
+
+ // Test time.
+ {
+ Value: &Plain{time.Unix(1e9, 123456789).UTC()},
+ ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
+ },
+
+ // A pointer to struct{} may be used to test for an element's presence.
+ {
+ Value: &PresenceTest{new(struct{})},
+ ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
+ },
+ {
+ Value: &PresenceTest{},
+ ExpectXML: `<PresenceTest></PresenceTest>`,
+ },
+
+ // A pointer to struct{} may be used to test for an element's presence.
+ {
+ Value: &PresenceTest{new(struct{})},
+ ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
+ },
+ {
+ Value: &PresenceTest{},
+ ExpectXML: `<PresenceTest></PresenceTest>`,
+ },
+
+ // A []byte field is only nil if the element was not found.
+ {
+ Value: &Data{},
+ ExpectXML: `<Data></Data>`,
+ UnmarshalOnly: true,
+ },
+ {
+ Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
+ ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
+ UnmarshalOnly: true,
+ },
+
+ // Check that []byte works, including named []byte types.
+ {
+ Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
+ ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
+ },
+
+ // Test innerxml
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<redacted/>",
+ },
+ ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<Identity>James Bond</Identity><redacted/>",
+ },
+ ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ UnmarshalOnly: true,
+ },
+
+ // Test structs
+ {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
+ {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
+ {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`},
+ {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
+ {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
+ {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
+ {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
+ {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
+ {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
+ {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
+ {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
+ {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
+ {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
+ {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
+ {Value: atomValue, ExpectXML: atomXml},
+ {
+ Value: &Ship{
+ Name: "Heart of Gold",
+ Pilot: "Computer",
+ Age: 1,
+ Drive: ImprobabilityDrive,
+ Passenger: []*Passenger{
+ {
+ Name: []string{"Zaphod", "Beeblebrox"},
+ Weight: 7.25,
+ },
+ {
+ Name: []string{"Trisha", "McMillen"},
+ Weight: 5.5,
+ },
+ {
+ Name: []string{"Ford", "Prefect"},
+ Weight: 7,
+ },
+ {
+ Name: []string{"Arthur", "Dent"},
+ Weight: 6.75,
+ },
+ },
+ },
+ ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
+ `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
+ `<age>1</age>` +
+ `<passenger>` +
+ `<name>Zaphod</name>` +
+ `<name>Beeblebrox</name>` +
+ `<weight>7.25</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Trisha</name>` +
+ `<name>McMillen</name>` +
+ `<weight>5.5</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Ford</name>` +
+ `<name>Prefect</name>` +
+ `<weight>7</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Arthur</name>` +
+ `<name>Dent</name>` +
+ `<weight>6.75</weight>` +
+ `</passenger>` +
+ `</spaceship>`,
+ },
+
+ // Test a>b
+ {
+ Value: &NestedItems{Items: nil, Item1: nil},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `</Items>` +
+ `</result>`,
+ },
+ {
+ Value: &NestedItems{Items: []string{}, Item1: []string{}},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `</Items>` +
+ `</result>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &NestedItems{Items: nil, Item1: []string{"A"}},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `<item1>A</item1>` +
+ `</Items>` +
+ `</result>`,
+ },
+ {
+ Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `<item>A</item>` +
+ `<item>B</item>` +
+ `</Items>` +
+ `</result>`,
+ },
+ {
+ Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
+ ExpectXML: `<result>` +
+ `<Items>` +
+ `<item>A</item>` +
+ `<item>B</item>` +
+ `<item1>C</item1>` +
+ `</Items>` +
+ `</result>`,
+ },
+ {
+ Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
+ ExpectXML: `<result>` +
+ `<parent>` +
+ `<c>C</c>` +
+ `<b>B</b>` +
+ `<a>A</a>` +
+ `</parent>` +
+ `</result>`,
+ },
+ {
+ Value: &NilTest{A: "A", B: nil, C: "C"},
+ ExpectXML: `<NilTest>` +
+ `<parent1>` +
+ `<parent2><a>A</a></parent2>` +
+ `<parent2><c>C</c></parent2>` +
+ `</parent1>` +
+ `</NilTest>`,
+ MarshalOnly: true, // Uses interface{}
+ },
+ {
+ Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
+ ExpectXML: `<result>` +
+ `<parent1><a>A</a></parent1>` +
+ `<b>B</b>` +
+ `<parent1>` +
+ `<parent2><c>C</c></parent2>` +
+ `<d>D</d>` +
+ `</parent1>` +
+ `</result>`,
+ },
+ {
+ Value: &Service{Port: &Port{Number: "80"}},
+ ExpectXML: `<service><host><port>80</port></host></service>`,
+ },
+ {
+ Value: &Service{},
+ ExpectXML: `<service></service>`,
+ },
+ {
+ Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
+ ExpectXML: `<service>` +
+ `<host><port>80</port></host>` +
+ `<Extra1>A</Extra1>` +
+ `<host><extra2>B</extra2></host>` +
+ `</service>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
+ ExpectXML: `<service>` +
+ `<host><port>80</port></host>` +
+ `<host><extra2>example</extra2></host>` +
+ `</service>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &struct {
+ XMLName struct{} `xml:"space top"`
+ A string `xml:"x>a"`
+ B string `xml:"x>b"`
+ C string `xml:"space x>c"`
+ C1 string `xml:"space1 x>c"`
+ D1 string `xml:"space1 x>d"`
+ E1 string `xml:"x>e"`
+ }{
+ A: "a",
+ B: "b",
+ C: "c",
+ C1: "c1",
+ D1: "d1",
+ E1: "e1",
+ },
+ ExpectXML: `<top xmlns="space">` +
+ `<x><a>a</a><b>b</b><c>c</c></x>` +
+ `<x xmlns="space1">` +
+ `<c>c1</c>` +
+ `<d>d1</d>` +
+ `</x>` +
+ `<x>` +
+ `<e>e1</e>` +
+ `</x>` +
+ `</top>`,
+ },
+ {
+ Value: &struct {
+ XMLName Name
+ A string `xml:"x>a"`
+ B string `xml:"x>b"`
+ C string `xml:"space x>c"`
+ C1 string `xml:"space1 x>c"`
+ D1 string `xml:"space1 x>d"`
+ }{
+ XMLName: Name{
+ Space: "space0",
+ Local: "top",
+ },
+ A: "a",
+ B: "b",
+ C: "c",
+ C1: "c1",
+ D1: "d1",
+ },
+ ExpectXML: `<top xmlns="space0">` +
+ `<x><a>a</a><b>b</b></x>` +
+ `<x xmlns="space"><c>c</c></x>` +
+ `<x xmlns="space1">` +
+ `<c>c1</c>` +
+ `<d>d1</d>` +
+ `</x>` +
+ `</top>`,
+ },
+ {
+ Value: &struct {
+ XMLName struct{} `xml:"top"`
+ B string `xml:"space x>b"`
+ B1 string `xml:"space1 x>b"`
+ }{
+ B: "b",
+ B1: "b1",
+ },
+ ExpectXML: `<top>` +
+ `<x xmlns="space"><b>b</b></x>` +
+ `<x xmlns="space1"><b>b1</b></x>` +
+ `</top>`,
+ },
+
+ // Test struct embedding
+ {
+ Value: &EmbedA{
+ EmbedC: EmbedC{
+ FieldA1: "", // Shadowed by A.A
+ FieldA2: "", // Shadowed by A.A
+ FieldB: "A.C.B",
+ FieldC: "A.C.C",
+ },
+ EmbedB: EmbedB{
+ FieldB: "A.B.B",
+ EmbedC: &EmbedC{
+ FieldA1: "A.B.C.A1",
+ FieldA2: "A.B.C.A2",
+ FieldB: "", // Shadowed by A.B.B
+ FieldC: "A.B.C.C",
+ },
+ },
+ FieldA: "A.A",
+ },
+ ExpectXML: `<EmbedA>` +
+ `<FieldB>A.C.B</FieldB>` +
+ `<FieldC>A.C.C</FieldC>` +
+ `<EmbedB>` +
+ `<FieldB>A.B.B</FieldB>` +
+ `<FieldA>` +
+ `<A1>A.B.C.A1</A1>` +
+ `<A2>A.B.C.A2</A2>` +
+ `</FieldA>` +
+ `<FieldC>A.B.C.C</FieldC>` +
+ `</EmbedB>` +
+ `<FieldA>A.A</FieldA>` +
+ `</EmbedA>`,
+ },
+
+ // Test that name casing matters
+ {
+ Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
+ ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
+ },
+
+ // Test the order in which the XML element name is chosen
+ {
+ Value: &NamePrecedence{
+ FromTag: XMLNameWithoutTag{Value: "A"},
+ FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
+ FromNameTag: XMLNameWithTag{Value: "C"},
+ InFieldName: "D",
+ },
+ ExpectXML: `<Parent>` +
+ `<InTag>A</InTag>` +
+ `<InXMLName>B</InXMLName>` +
+ `<InXMLNameTag>C</InXMLNameTag>` +
+ `<InFieldName>D</InFieldName>` +
+ `</Parent>`,
+ MarshalOnly: true,
+ },
+ {
+ Value: &NamePrecedence{
+ XMLName: Name{Local: "Parent"},
+ FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
+ FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
+ FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
+ InFieldName: "D",
+ },
+ ExpectXML: `<Parent>` +
+ `<InTag>A</InTag>` +
+ `<FromNameVal>B</FromNameVal>` +
+ `<InXMLNameTag>C</InXMLNameTag>` +
+ `<InFieldName>D</InFieldName>` +
+ `</Parent>`,
+ UnmarshalOnly: true,
+ },
+
+ // xml.Name works in a plain field as well.
+ {
+ Value: &NameInField{Name{Space: "ns", Local: "foo"}},
+ ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
+ },
+ {
+ Value: &NameInField{Name{Space: "ns", Local: "foo"}},
+ ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
+ UnmarshalOnly: true,
+ },
+
+ // Marshaling zero xml.Name uses the tag or field name.
+ {
+ Value: &NameInField{},
+ ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
+ MarshalOnly: true,
+ },
+
+ // Test attributes
+ {
+ Value: &AttrTest{
+ Int: 8,
+ Named: 9,
+ Float: 23.5,
+ Uint8: 255,
+ Bool: true,
+ Str: "str",
+ Bytes: []byte("byt"),
+ },
+ ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
+ ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
+ },
+ {
+ Value: &AttrTest{Bytes: []byte{}},
+ ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
+ ` Bool="false" Str="" Bytes=""></AttrTest>`,
+ },
+ {
+ Value: &OmitAttrTest{
+ Int: 8,
+ Named: 9,
+ Float: 23.5,
+ Uint8: 255,
+ Bool: true,
+ Str: "str",
+ Bytes: []byte("byt"),
+ },
+ ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
+ ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
+ },
+ {
+ Value: &OmitAttrTest{},
+ ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
+ },
+
+ // pointer fields
+ {
+ Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
+ ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
+ MarshalOnly: true,
+ },
+
+ // empty chardata pointer field
+ {
+ Value: &ChardataEmptyTest{},
+ ExpectXML: `<test></test>`,
+ MarshalOnly: true,
+ },
+
+ // omitempty on fields
+ {
+ Value: &OmitFieldTest{
+ Int: 8,
+ Named: 9,
+ Float: 23.5,
+ Uint8: 255,
+ Bool: true,
+ Str: "str",
+ Bytes: []byte("byt"),
+ Ptr: &PresenceTest{},
+ },
+ ExpectXML: `<OmitFieldTest>` +
+ `<Int>8</Int>` +
+ `<int>9</int>` +
+ `<Float>23.5</Float>` +
+ `<Uint8>255</Uint8>` +
+ `<Bool>true</Bool>` +
+ `<Str>str</Str>` +
+ `<Bytes>byt</Bytes>` +
+ `<Ptr></Ptr>` +
+ `</OmitFieldTest>`,
+ },
+ {
+ Value: &OmitFieldTest{},
+ ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
+ },
+
+ // Test ",any"
+ {
+ ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
+ Value: &AnyTest{
+ Nested: "known",
+ AnyField: AnyHolder{
+ XMLName: Name{Local: "other"},
+ XML: "<sub>unknown</sub>",
+ },
+ },
+ },
+ {
+ Value: &AnyTest{Nested: "known",
+ AnyField: AnyHolder{
+ XML: "<unknown/>",
+ XMLName: Name{Local: "AnyField"},
+ },
+ },
+ ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested></a>`,
+ Value: &AnyOmitTest{
+ Nested: "b",
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
+ Value: &AnySliceTest{
+ Nested: "b",
+ AnyField: []AnyHolder{
+ {
+ XMLName: Name{Local: "c"},
+ XML: "<d>e</d>",
+ },
+ {
+ XMLName: Name{Space: "f", Local: "g"},
+ XML: "<h>i</h>",
+ },
+ },
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested></a>`,
+ Value: &AnySliceTest{
+ Nested: "b",
+ },
+ },
+
+ // Test recursive types.
+ {
+ Value: &RecurseA{
+ A: "a1",
+ B: &RecurseB{
+ A: &RecurseA{"a2", nil},
+ B: "b1",
+ },
+ },
+ ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
+ },
+
+ // Test ignoring fields via "-" tag
+ {
+ ExpectXML: `<IgnoreTest></IgnoreTest>`,
+ Value: &IgnoreTest{},
+ },
+ {
+ ExpectXML: `<IgnoreTest></IgnoreTest>`,
+ Value: &IgnoreTest{PublicSecret: "can't tell"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
+ Value: &IgnoreTest{},
+ UnmarshalOnly: true,
+ },
+
+ // Test escaping.
+ {
+ ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`,
+ Value: &AnyTest{
+ Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
+ AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`,
+ Value: &AnyTest{
+ Nested: "newline: \n; cr: \r; tab: \t;",
+ AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
+ },
+ },
+ {
+ ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
+ Value: &AnyTest{
+ Nested: "1\n2\n3\n\n4\n5",
+ },
+ UnmarshalOnly: true,
+ },
+ {
+ ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
+ Value: &EmbedInt{
+ MyInt: 42,
+ },
+ },
+ // Test omitempty with parent chain; see golang.org/issue/4168.
+ {
+ ExpectXML: `<Strings><A></A></Strings>`,
+ Value: &Strings{},
+ },
+ // Custom marshalers.
+ {
+ ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
+ Value: &MyMarshalerTest{},
+ },
+ {
+ ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
+ Value: &MarshalerStruct{},
+ },
+ {
+ ExpectXML: `<MarshalerValueStruct Foo="hello world"></MarshalerValueStruct>`,
+ Value: &MarshalerValueStruct{},
+ },
+ {
+ ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+ Value: &OuterStruct{IntAttr: 10},
+ },
+ {
+ ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+ Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+ },
+ {
+ ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+ Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+ },
+ {
+ ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+ Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
+ },
+ {
+ ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`,
+ Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"},
+ },
+ {
+ ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
+ Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
+ },
+ {
+ ExpectXML: `<XMLNSFieldStruct xmlns="http://example.com/ns"><Body>hello world</Body></XMLNSFieldStruct>`,
+ Value: &XMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
+ },
+ {
+ ExpectXML: `<testns:test xmlns:testns="testns" xmlns="http://example.com/ns"><Body>hello world</Body></testns:test>`,
+ Value: &NamedXMLNSFieldStruct{Ns: "http://example.com/ns", Body: "hello world"},
+ },
+ {
+ ExpectXML: `<testns:test xmlns:testns="testns"><Body>hello world</Body></testns:test>`,
+ Value: &NamedXMLNSFieldStruct{Ns: "", Body: "hello world"},
+ },
+ {
+ ExpectXML: `<XMLNSFieldStructWithOmitEmpty><Body>hello world</Body></XMLNSFieldStructWithOmitEmpty>`,
+ Value: &XMLNSFieldStructWithOmitEmpty{Body: "hello world"},
+ },
+ {
+ // The xmlns attribute must be ignored because the <test>
+ // element is in the empty namespace, so it's not possible
+ // to set the default namespace to something non-empty.
+ ExpectXML: `<test><Body>hello world</Body></test>`,
+ Value: &NamedXMLNSFieldStructWithEmptyNamespace{Ns: "foo", Body: "hello world"},
+ MarshalOnly: true,
+ },
+ {
+ ExpectXML: `<RecursiveXMLNSFieldStruct xmlns="foo"><Body xmlns=""><Text>hello world</Text></Body></RecursiveXMLNSFieldStruct>`,
+ Value: &RecursiveXMLNSFieldStruct{
+ Ns: "foo",
+ Body: &RecursiveXMLNSFieldStruct{
+ Text: "hello world",
+ },
+ },
+ },
+}
+
+func TestMarshal(t *testing.T) {
+ for idx, test := range marshalTests {
+ if test.UnmarshalOnly {
+ continue
+ }
+ data, err := Marshal(test.Value)
+ if err != nil {
+ t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
+ continue
+ }
+ if got, want := string(data), test.ExpectXML; got != want {
+ if strings.Contains(want, "\n") {
+ t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
+ } else {
+ t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
+ }
+ }
+ }
+}
+
+type AttrParent struct {
+ X string `xml:"X>Y,attr"`
+}
+
+type BadAttr struct {
+ Name []string `xml:"name,attr"`
+}
+
+var marshalErrorTests = []struct {
+ Value interface{}
+ Err string
+ Kind reflect.Kind
+}{
+ {
+ Value: make(chan bool),
+ Err: "xml: unsupported type: chan bool",
+ Kind: reflect.Chan,
+ },
+ {
+ Value: map[string]string{
+ "question": "What do you get when you multiply six by nine?",
+ "answer": "42",
+ },
+ Err: "xml: unsupported type: map[string]string",
+ Kind: reflect.Map,
+ },
+ {
+ Value: map[*Ship]bool{nil: false},
+ Err: "xml: unsupported type: map[*xml.Ship]bool",
+ Kind: reflect.Map,
+ },
+ {
+ Value: &Domain{Comment: []byte("f--bar")},
+ Err: `xml: comments must not contain "--"`,
+ },
+ // Reject parent chain with attr, never worked; see golang.org/issue/5033.
+ {
+ Value: &AttrParent{},
+ Err: `xml: X>Y chain not valid with attr flag`,
+ },
+ {
+ Value: BadAttr{[]string{"X", "Y"}},
+ Err: `xml: unsupported type: []string`,
+ },
+}
+
+var marshalIndentTests = []struct {
+ Value interface{}
+ Prefix string
+ Indent string
+ ExpectXML string
+}{
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<redacted/>",
+ },
+ Prefix: "",
+ Indent: "\t",
+ ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
+ },
+}
+
+func TestMarshalErrors(t *testing.T) {
+ for idx, test := range marshalErrorTests {
+ data, err := Marshal(test.Value)
+ if err == nil {
+ t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
+ continue
+ }
+ if err.Error() != test.Err {
+ t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
+ }
+ if test.Kind != reflect.Invalid {
+ if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
+ t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
+ }
+ }
+ }
+}
+
+// Do invertibility testing on the various structures that we test
+func TestUnmarshal(t *testing.T) {
+ for i, test := range marshalTests {
+ if test.MarshalOnly {
+ continue
+ }
+ if _, ok := test.Value.(*Plain); ok {
+ continue
+ }
+ vt := reflect.TypeOf(test.Value)
+ dest := reflect.New(vt.Elem()).Interface()
+ err := Unmarshal([]byte(test.ExpectXML), dest)
+
+ switch fix := dest.(type) {
+ case *Feed:
+ fix.Author.InnerXML = ""
+ for i := range fix.Entry {
+ fix.Entry[i].Author.InnerXML = ""
+ }
+ }
+
+ if err != nil {
+ t.Errorf("#%d: unexpected error: %#v", i, err)
+ } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
+ t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
+ }
+ }
+}
+
+func TestMarshalIndent(t *testing.T) {
+ for i, test := range marshalIndentTests {
+ data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
+ if err != nil {
+ t.Errorf("#%d: Error: %s", i, err)
+ continue
+ }
+ if got, want := string(data), test.ExpectXML; got != want {
+ t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
+ }
+ }
+}
+
+type limitedBytesWriter struct {
+ w io.Writer
+ remain int // until writes fail
+}
+
+func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
+ if lw.remain <= 0 {
+ println("error")
+ return 0, errors.New("write limit hit")
+ }
+ if len(p) > lw.remain {
+ p = p[:lw.remain]
+ n, _ = lw.w.Write(p)
+ lw.remain = 0
+ return n, errors.New("write limit hit")
+ }
+ n, err = lw.w.Write(p)
+ lw.remain -= n
+ return n, err
+}
+
+func TestMarshalWriteErrors(t *testing.T) {
+ var buf bytes.Buffer
+ const writeCap = 1024
+ w := &limitedBytesWriter{&buf, writeCap}
+ enc := NewEncoder(w)
+ var err error
+ var i int
+ const n = 4000
+ for i = 1; i <= n; i++ {
+ err = enc.Encode(&Passenger{
+ Name: []string{"Alice", "Bob"},
+ Weight: 5,
+ })
+ if err != nil {
+ break
+ }
+ }
+ if err == nil {
+ t.Error("expected an error")
+ }
+ if i == n {
+ t.Errorf("expected to fail before the end")
+ }
+ if buf.Len() != writeCap {
+ t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
+ }
+}
+
+func TestMarshalWriteIOErrors(t *testing.T) {
+ enc := NewEncoder(errWriter{})
+
+ expectErr := "unwritable"
+ err := enc.Encode(&Passenger{})
+ if err == nil || err.Error() != expectErr {
+ t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
+ }
+}
+
+func TestMarshalFlush(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.EncodeToken(CharData("hello world")); err != nil {
+ t.Fatalf("enc.EncodeToken: %v", err)
+ }
+ if buf.Len() > 0 {
+ t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
+ }
+ if err := enc.Flush(); err != nil {
+ t.Fatalf("enc.Flush: %v", err)
+ }
+ if buf.String() != "hello world" {
+ t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
+ }
+}
+
+var encodeElementTests = []struct {
+ desc string
+ value interface{}
+ start StartElement
+ expectXML string
+}{{
+ desc: "simple string",
+ value: "hello",
+ start: StartElement{
+ Name: Name{Local: "a"},
+ },
+ expectXML: `<a>hello</a>`,
+}, {
+ desc: "string with added attributes",
+ value: "hello",
+ start: StartElement{
+ Name: Name{Local: "a"},
+ Attr: []Attr{{
+ Name: Name{Local: "x"},
+ Value: "y",
+ }, {
+ Name: Name{Local: "foo"},
+ Value: "bar",
+ }},
+ },
+ expectXML: `<a x="y" foo="bar">hello</a>`,
+}, {
+ desc: "start element with default name space",
+ value: struct {
+ Foo XMLNameWithNSTag
+ }{
+ Foo: XMLNameWithNSTag{
+ Value: "hello",
+ },
+ },
+ start: StartElement{
+ Name: Name{Space: "ns", Local: "a"},
+ Attr: []Attr{{
+ Name: Name{Local: "xmlns"},
+ // "ns" is the name space defined in XMLNameWithNSTag
+ Value: "ns",
+ }},
+ },
+ expectXML: `<a xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></a>`,
+}, {
+ desc: "start element in name space with different default name space",
+ value: struct {
+ Foo XMLNameWithNSTag
+ }{
+ Foo: XMLNameWithNSTag{
+ Value: "hello",
+ },
+ },
+ start: StartElement{
+ Name: Name{Space: "ns2", Local: "a"},
+ Attr: []Attr{{
+ Name: Name{Local: "xmlns"},
+ // "ns" is the name space defined in XMLNameWithNSTag
+ Value: "ns",
+ }},
+ },
+ expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns"><InXMLNameWithNSTag>hello</InXMLNameWithNSTag></ns2:a>`,
+}, {
+ desc: "XMLMarshaler with start element with default name space",
+ value: &MyMarshalerTest{},
+ start: StartElement{
+ Name: Name{Space: "ns2", Local: "a"},
+ Attr: []Attr{{
+ Name: Name{Local: "xmlns"},
+ // "ns" is the name space defined in XMLNameWithNSTag
+ Value: "ns",
+ }},
+ },
+ expectXML: `<ns2:a xmlns:ns2="ns2" xmlns="ns">hello world</ns2:a>`,
+}}
+
+func TestEncodeElement(t *testing.T) {
+ for idx, test := range encodeElementTests {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ err := enc.EncodeElement(test.value, test.start)
+ if err != nil {
+ t.Fatalf("enc.EncodeElement: %v", err)
+ }
+ err = enc.Flush()
+ if err != nil {
+ t.Fatalf("enc.Flush: %v", err)
+ }
+ if got, want := buf.String(), test.expectXML; got != want {
+ t.Errorf("#%d(%s): EncodeElement(%#v, %#v):\nhave %#q\nwant %#q", idx, test.desc, test.value, test.start, got, want)
+ }
+ }
+}
+
+func BenchmarkMarshal(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ Marshal(atomValue)
+ }
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ b.ReportAllocs()
+ xml := []byte(atomXml)
+ for i := 0; i < b.N; i++ {
+ Unmarshal(xml, &Feed{})
+ }
+}
+
+// golang.org/issue/6556
+func TestStructPointerMarshal(t *testing.T) {
+ type A struct {
+ XMLName string `xml:"a"`
+ B []interface{}
+ }
+ type C struct {
+ XMLName Name
+ Value string `xml:"value"`
+ }
+
+ a := new(A)
+ a.B = append(a.B, &C{
+ XMLName: Name{Local: "c"},
+ Value: "x",
+ })
+
+ b, err := Marshal(a)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x := string(b); x != "<a><c><value>x</value></c></a>" {
+ t.Fatal(x)
+ }
+ var v A
+ err = Unmarshal(b, &v)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+var encodeTokenTests = []struct {
+ desc string
+ toks []Token
+ want string
+ err string
+}{{
+ desc: "start element with name space",
+ toks: []Token{
+ StartElement{Name{"space", "local"}, nil},
+ },
+ want: `<space:local xmlns:space="space">`,
+}, {
+ desc: "start element with no name",
+ toks: []Token{
+ StartElement{Name{"space", ""}, nil},
+ },
+ err: "xml: start tag with no name",
+}, {
+ desc: "end element with no name",
+ toks: []Token{
+ EndElement{Name{"space", ""}},
+ },
+ err: "xml: end tag with no name",
+}, {
+ desc: "char data",
+ toks: []Token{
+ CharData("foo"),
+ },
+ want: `foo`,
+}, {
+ desc: "char data with escaped chars",
+ toks: []Token{
+ CharData(" \t\n"),
+ },
+ want: " 	\n",
+}, {
+ desc: "comment",
+ toks: []Token{
+ Comment("foo"),
+ },
+ want: `<!--foo-->`,
+}, {
+ desc: "comment with invalid content",
+ toks: []Token{
+ Comment("foo-->"),
+ },
+ err: "xml: EncodeToken of Comment containing --> marker",
+}, {
+ desc: "proc instruction",
+ toks: []Token{
+ ProcInst{"Target", []byte("Instruction")},
+ },
+ want: `<?Target Instruction?>`,
+}, {
+ desc: "proc instruction with empty target",
+ toks: []Token{
+ ProcInst{"", []byte("Instruction")},
+ },
+ err: "xml: EncodeToken of ProcInst with invalid Target",
+}, {
+ desc: "proc instruction with bad content",
+ toks: []Token{
+ ProcInst{"", []byte("Instruction?>")},
+ },
+ err: "xml: EncodeToken of ProcInst with invalid Target",
+}, {
+ desc: "directive",
+ toks: []Token{
+ Directive("foo"),
+ },
+ want: `<!foo>`,
+}, {
+ desc: "more complex directive",
+ toks: []Token{
+ Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"),
+ },
+ want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`,
+}, {
+ desc: "directive instruction with bad name",
+ toks: []Token{
+ Directive("foo>"),
+ },
+ err: "xml: EncodeToken of Directive containing wrong < or > markers",
+}, {
+ desc: "end tag without start tag",
+ toks: []Token{
+ EndElement{Name{"foo", "bar"}},
+ },
+ err: "xml: end tag </bar> without start tag",
+}, {
+ desc: "mismatching end tag local name",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, nil},
+ EndElement{Name{"", "bar"}},
+ },
+ err: "xml: end tag </bar> does not match start tag <foo>",
+ want: `<foo>`,
+}, {
+ desc: "mismatching end tag namespace",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, nil},
+ EndElement{Name{"another", "foo"}},
+ },
+ err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
+ want: `<space:foo xmlns:space="space">`,
+}, {
+ desc: "start element with explicit namespace",
+ toks: []Token{
+ StartElement{Name{"space", "local"}, []Attr{
+ {Name{"xmlns", "x"}, "space"},
+ {Name{"space", "foo"}, "value"},
+ }},
+ },
+ want: `<x:local xmlns:x="space" x:foo="value">`,
+}, {
+ desc: "start element with explicit namespace and colliding prefix",
+ toks: []Token{
+ StartElement{Name{"space", "local"}, []Attr{
+ {Name{"xmlns", "x"}, "space"},
+ {Name{"space", "foo"}, "value"},
+ {Name{"x", "bar"}, "other"},
+ }},
+ },
+ want: `<x:local xmlns:x_1="x" xmlns:x="space" x:foo="value" x_1:bar="other">`,
+}, {
+ desc: "start element using previously defined namespace",
+ toks: []Token{
+ StartElement{Name{"", "local"}, []Attr{
+ {Name{"xmlns", "x"}, "space"},
+ }},
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"space", "x"}, "y"},
+ }},
+ },
+ want: `<local xmlns:x="space"><x:foo x:x="y">`,
+}, {
+ desc: "nested name space with same prefix",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"xmlns", "x"}, "space1"},
+ }},
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"xmlns", "x"}, "space2"},
+ }},
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"space1", "a"}, "space1 value"},
+ {Name{"space2", "b"}, "space2 value"},
+ }},
+ EndElement{Name{"", "foo"}},
+ EndElement{Name{"", "foo"}},
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"space1", "a"}, "space1 value"},
+ {Name{"space2", "b"}, "space2 value"},
+ }},
+ },
+ want: `<foo xmlns:x="space1"><foo xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" x:b="space2 value"></foo></foo><foo xmlns:space2="space2" x:a="space1 value" space2:b="space2 value">`,
+}, {
+ desc: "start element defining several prefixes for the same name space",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"xmlns", "a"}, "space"},
+ {Name{"xmlns", "b"}, "space"},
+ {Name{"space", "x"}, "value"},
+ }},
+ },
+ want: `<a:foo xmlns:a="space" a:x="value">`,
+}, {
+ desc: "nested element redefines name space",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"xmlns", "x"}, "space"},
+ }},
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"xmlns", "y"}, "space"},
+ {Name{"space", "a"}, "value"},
+ }},
+ },
+ want: `<foo xmlns:x="space"><x:foo x:a="value">`,
+}, {
+ desc: "nested element creates alias for default name space",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ }},
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"xmlns", "y"}, "space"},
+ {Name{"space", "a"}, "value"},
+ }},
+ },
+ want: `<foo xmlns="space"><foo xmlns:y="space" y:a="value">`,
+}, {
+ desc: "nested element defines default name space with existing prefix",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"xmlns", "x"}, "space"},
+ }},
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ {Name{"space", "a"}, "value"},
+ }},
+ },
+ want: `<foo xmlns:x="space"><foo xmlns="space" x:a="value">`,
+}, {
+ desc: "nested element uses empty attribute name space when default ns defined",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ }},
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "attr"}, "value"},
+ }},
+ },
+ want: `<foo xmlns="space"><foo attr="value">`,
+}, {
+ desc: "redefine xmlns",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"foo", "xmlns"}, "space"},
+ }},
+ },
+ err: `xml: cannot redefine xmlns attribute prefix`,
+}, {
+ desc: "xmlns with explicit name space #1",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"xml", "xmlns"}, "space"},
+ }},
+ },
+ want: `<foo xmlns="space">`,
+}, {
+ desc: "xmlns with explicit name space #2",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{xmlURL, "xmlns"}, "space"},
+ }},
+ },
+ want: `<foo xmlns="space">`,
+}, {
+ desc: "empty name space declaration is ignored",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"xmlns", "foo"}, ""},
+ }},
+ },
+ want: `<foo>`,
+}, {
+ desc: "attribute with no name is ignored",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"", ""}, "value"},
+ }},
+ },
+ want: `<foo>`,
+}, {
+ desc: "namespace URL with non-valid name",
+ toks: []Token{
+ StartElement{Name{"/34", "foo"}, []Attr{
+ {Name{"/34", "x"}, "value"},
+ }},
+ },
+ want: `<_:foo xmlns:_="/34" _:x="value">`,
+}, {
+ desc: "nested element resets default namespace to empty",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ }},
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"", "xmlns"}, ""},
+ {Name{"", "x"}, "value"},
+ {Name{"space", "x"}, "value"},
+ }},
+ },
+ want: `<foo xmlns="space"><foo xmlns:space="space" xmlns="" x="value" space:x="value">`,
+}, {
+ desc: "nested element requires empty default name space",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ }},
+ StartElement{Name{"", "foo"}, nil},
+ },
+ want: `<foo xmlns="space"><foo xmlns="">`,
+}, {
+ desc: "attribute uses name space from xmlns",
+ toks: []Token{
+ StartElement{Name{"some/space", "foo"}, []Attr{
+ {Name{"", "attr"}, "value"},
+ {Name{"some/space", "other"}, "other value"},
+ }},
+ },
+ want: `<space:foo xmlns:space="some/space" attr="value" space:other="other value">`,
+}, {
+ desc: "default name space should not be used by attributes",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ {Name{"xmlns", "bar"}, "space"},
+ {Name{"space", "baz"}, "foo"},
+ }},
+ StartElement{Name{"space", "baz"}, nil},
+ EndElement{Name{"space", "baz"}},
+ EndElement{Name{"space", "foo"}},
+ },
+ want: `<foo xmlns:bar="space" xmlns="space" bar:baz="foo"><baz></baz></foo>`,
+}, {
+ desc: "default name space not used by attributes, not explicitly defined",
+ toks: []Token{
+ StartElement{Name{"space", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ {Name{"space", "baz"}, "foo"},
+ }},
+ StartElement{Name{"space", "baz"}, nil},
+ EndElement{Name{"space", "baz"}},
+ EndElement{Name{"space", "foo"}},
+ },
+ want: `<foo xmlns:space="space" xmlns="space" space:baz="foo"><baz></baz></foo>`,
+}, {
+ desc: "impossible xmlns declaration",
+ toks: []Token{
+ StartElement{Name{"", "foo"}, []Attr{
+ {Name{"", "xmlns"}, "space"},
+ }},
+ StartElement{Name{"space", "bar"}, []Attr{
+ {Name{"space", "attr"}, "value"},
+ }},
+ },
+ want: `<foo><space:bar xmlns:space="space" space:attr="value">`,
+}}
+
+func TestEncodeToken(t *testing.T) {
+loop:
+ for i, tt := range encodeTokenTests {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ var err error
+ for j, tok := range tt.toks {
+ err = enc.EncodeToken(tok)
+ if err != nil && j < len(tt.toks)-1 {
+ t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
+ continue loop
+ }
+ }
+ errorf := func(f string, a ...interface{}) {
+ t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
+ }
+ switch {
+ case tt.err != "" && err == nil:
+ errorf(" expected error; got none")
+ continue
+ case tt.err == "" && err != nil:
+ errorf(" got error: %v", err)
+ continue
+ case tt.err != "" && err != nil && tt.err != err.Error():
+ errorf(" error mismatch; got %v, want %v", err, tt.err)
+ continue
+ }
+ if err := enc.Flush(); err != nil {
+ errorf(" %v", err)
+ continue
+ }
+ if got := buf.String(); got != tt.want {
+ errorf("\ngot %v\nwant %v", got, tt.want)
+ continue
+ }
+ }
+}
+
+func TestProcInstEncodeToken(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+
+ if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
+ t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
+ }
+
+ if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
+ t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
+ }
+
+ if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
+ t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
+ }
+}
+
+func TestDecodeEncode(t *testing.T) {
+ var in, out bytes.Buffer
+ in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
+<?Target Instruction?>
+<root>
+</root>
+`)
+ dec := NewDecoder(&in)
+ enc := NewEncoder(&out)
+ for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
+ err = enc.EncodeToken(tok)
+ if err != nil {
+ t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
+ }
+ }
+}
+
+// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
+func TestRace9796(t *testing.T) {
+ type A struct{}
+ type B struct {
+ C []A `xml:"X>Y"`
+ }
+ var wg sync.WaitGroup
+ for i := 0; i < 2; i++ {
+ wg.Add(1)
+ go func() {
+ Marshal(B{[]A{A{}}})
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func TestIsValidDirective(t *testing.T) {
+ testOK := []string{
+ "<>",
+ "< < > >",
+ "<!DOCTYPE '<' '>' '>' <!--nothing-->>",
+ "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>",
+ "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>",
+ "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >",
+ }
+ testKO := []string{
+ "<",
+ ">",
+ "<!--",
+ "-->",
+ "< > > < < >",
+ "<!dummy <!-- > -->",
+ "<!DOCTYPE doc '>",
+ "<!DOCTYPE doc '>'",
+ "<!DOCTYPE doc <!--comment>",
+ }
+ for _, s := range testOK {
+ if !isValidDirective(Directive(s)) {
+ t.Errorf("Directive %q is expected to be valid", s)
+ }
+ }
+ for _, s := range testKO {
+ if isValidDirective(Directive(s)) {
+ t.Errorf("Directive %q is expected to be invalid", s)
+ }
+ }
+}
+
+// Issue 11719. EncodeToken used to silently eat tokens with an invalid type.
+func TestSimpleUseOfEncodeToken(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil {
+ t.Errorf("enc.EncodeToken: pointer type should be rejected")
+ }
+ if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil {
+ t.Errorf("enc.EncodeToken: pointer type should be rejected")
+ }
+ if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil {
+ t.Errorf("enc.EncodeToken: StartElement %s", err)
+ }
+ if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil {
+ t.Errorf("enc.EncodeToken: EndElement %s", err)
+ }
+ if err := enc.EncodeToken(Universe{}); err == nil {
+ t.Errorf("enc.EncodeToken: invalid type not caught")
+ }
+ if err := enc.Flush(); err != nil {
+ t.Errorf("enc.Flush: %s", err)
+ }
+ if buf.Len() == 0 {
+ t.Errorf("enc.EncodeToken: empty buffer")
+ }
+ want := "<object2></object2>"
+ if buf.String() != want {
+ t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())
+ }
+}
diff --git a/webdav/internal/xml/read.go b/webdav/internal/xml/read.go
new file mode 100644
index 0000000..75b9f2b
--- /dev/null
+++ b/webdav/internal/xml/read.go
@@ -0,0 +1,692 @@
+// 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.
+
+package xml
+
+import (
+ "bytes"
+ "encoding"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
+// an XML element is an order-dependent collection of anonymous
+// values, while a data structure is an order-independent collection
+// of named values.
+// See package json for a textual representation more suitable
+// to data structures.
+
+// Unmarshal parses the XML-encoded data and stores the result in
+// the value pointed to by v, which must be an arbitrary struct,
+// slice, or string. Well-formed data that does not fit into v is
+// discarded.
+//
+// Because Unmarshal uses the reflect package, it can only assign
+// to exported (upper case) fields. Unmarshal uses a case-sensitive
+// comparison to match XML element names to tag values and struct
+// field names.
+//
+// Unmarshal maps an XML element to a struct using the following rules.
+// In the rules, the tag of a field refers to the value associated with the
+// key 'xml' in the struct field's tag (see the example above).
+//
+// * If the struct has a field of type []byte or string with tag
+// ",innerxml", Unmarshal accumulates the raw XML nested inside the
+// element in that field. The rest of the rules still apply.
+//
+// * If the struct has a field named XMLName of type xml.Name,
+// Unmarshal records the element name in that field.
+//
+// * If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
+// returns an error.
+//
+// * If the XML element has an attribute whose name matches a
+// struct field name with an associated tag containing ",attr" or
+// the explicit name in a struct field tag of the form "name,attr",
+// Unmarshal records the attribute value in that field.
+//
+// * If the XML element contains character data, that data is
+// accumulated in the first struct field that has tag ",chardata".
+// The struct field may have type []byte or string.
+// If there is no such field, the character data is discarded.
+//
+// * If the XML element contains comments, they are accumulated in
+// the first struct field that has tag ",comment". The struct
+// field may have type []byte or string. If there is no such
+// field, the comments are discarded.
+//
+// * If the XML element contains a sub-element whose name matches
+// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
+// will descend into the XML structure looking for elements with the
+// given names, and will map the innermost elements to that struct
+// field. A tag starting with ">" is equivalent to one starting
+// with the field name followed by ">".
+//
+// * If the XML element contains a sub-element whose name matches
+// a struct field's XMLName tag and the struct field has no
+// explicit name tag as per the previous rule, unmarshal maps
+// the sub-element to that struct field.
+//
+// * If the XML element contains a sub-element whose name matches a
+// field without any mode flags (",attr", ",chardata", etc), Unmarshal
+// maps the sub-element to that struct field.
+//
+// * If the XML element contains a sub-element that hasn't matched any
+// of the above rules and the struct has a field with tag ",any",
+// unmarshal maps the sub-element to that struct field.
+//
+// * An anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
+//
+// * A struct field with tag "-" is never unmarshalled into.
+//
+// Unmarshal maps an XML element to a string or []byte by saving the
+// concatenation of that element's character data in the string or
+// []byte. The saved []byte is never nil.
+//
+// Unmarshal maps an attribute value to a string or []byte by saving
+// the value in the string or slice.
+//
+// Unmarshal maps an XML element to a slice by extending the length of
+// the slice and mapping the element to the newly created value.
+//
+// Unmarshal maps an XML element or attribute value to a bool by
+// setting it to the boolean value represented by the string.
+//
+// Unmarshal maps an XML element or attribute value to an integer or
+// floating-point field by setting the field to the result of
+// interpreting the string value in decimal. There is no check for
+// overflow.
+//
+// Unmarshal maps an XML element to an xml.Name by recording the
+// element name.
+//
+// Unmarshal maps an XML element to a pointer by setting the pointer
+// to a freshly allocated value and then mapping the element to that value.
+//
+func Unmarshal(data []byte, v interface{}) error {
+ return NewDecoder(bytes.NewReader(data)).Decode(v)
+}
+
+// Decode works like xml.Unmarshal, except it reads the decoder
+// stream to find the start element.
+func (d *Decoder) Decode(v interface{}) error {
+ return d.DecodeElement(v, nil)
+}
+
+// DecodeElement works like xml.Unmarshal except that it takes
+// a pointer to the start XML element to decode into v.
+// It is useful when a client reads some raw XML tokens itself
+// but also wants to defer to Unmarshal for some elements.
+func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
+ val := reflect.ValueOf(v)
+ if val.Kind() != reflect.Ptr {
+ return errors.New("non-pointer passed to Unmarshal")
+ }
+ return d.unmarshal(val.Elem(), start)
+}
+
+// An UnmarshalError represents an error in the unmarshalling process.
+type UnmarshalError string
+
+func (e UnmarshalError) Error() string { return string(e) }
+
+// Unmarshaler is the interface implemented by objects that can unmarshal
+// an XML element description of themselves.
+//
+// UnmarshalXML decodes a single XML element
+// beginning with the given start element.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXML must consume exactly one XML element.
+// One common implementation strategy is to unmarshal into
+// a separate value with a layout matching the expected XML
+// using d.DecodeElement, and then to copy the data from
+// that value into the receiver.
+// Another common strategy is to use d.Token to process the
+// XML object one token at a time.
+// UnmarshalXML may not use d.RawToken.
+type Unmarshaler interface {
+ UnmarshalXML(d *Decoder, start StartElement) error
+}
+
+// UnmarshalerAttr is the interface implemented by objects that can unmarshal
+// an XML attribute description of themselves.
+//
+// UnmarshalXMLAttr decodes a single XML attribute.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type UnmarshalerAttr interface {
+ UnmarshalXMLAttr(attr Attr) error
+}
+
+// receiverType returns the receiver type to use in an expression like "%s.MethodName".
+func receiverType(val interface{}) string {
+ t := reflect.TypeOf(val)
+ if t.Name() != "" {
+ return t.String()
+ }
+ return "(" + t.String() + ")"
+}
+
+// unmarshalInterface unmarshals a single XML element into val.
+// start is the opening tag of the element.
+func (p *Decoder) unmarshalInterface(val Unmarshaler, start *StartElement) error {
+ // Record that decoder must stop at end tag corresponding to start.
+ p.pushEOF()
+
+ p.unmarshalDepth++
+ err := val.UnmarshalXML(p, *start)
+ p.unmarshalDepth--
+ if err != nil {
+ p.popEOF()
+ return err
+ }
+
+ if !p.popEOF() {
+ return fmt.Errorf("xml: %s.UnmarshalXML did not consume entire <%s> element", receiverType(val), start.Name.Local)
+ }
+
+ return nil
+}
+
+// unmarshalTextInterface unmarshals a single XML element into val.
+// The chardata contained in the element (but not its children)
+// is passed to the text unmarshaler.
+func (p *Decoder) unmarshalTextInterface(val encoding.TextUnmarshaler, start *StartElement) error {
+ var buf []byte
+ depth := 1
+ for depth > 0 {
+ t, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := t.(type) {
+ case CharData:
+ if depth == 1 {
+ buf = append(buf, t...)
+ }
+ case StartElement:
+ depth++
+ case EndElement:
+ depth--
+ }
+ }
+ return val.UnmarshalText(buf)
+}
+
+// unmarshalAttr unmarshals a single XML attribute into val.
+func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) {
+ return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ }
+
+ // Not an UnmarshalerAttr; try encoding.TextUnmarshaler.
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ }
+
+ copyValue(val, []byte(attr.Value))
+ return nil
+}
+
+var (
+ unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+ unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem()
+ textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
+// Unmarshal a single XML element into val.
+func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
+ // Find start element if we need it.
+ if start == nil {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ if t, ok := tok.(StartElement); ok {
+ start = &t
+ break
+ }
+ }
+ }
+
+ // Load value from interface, but only if the result will be
+ // usefully addressable.
+ if val.Kind() == reflect.Interface && !val.IsNil() {
+ e := val.Elem()
+ if e.Kind() == reflect.Ptr && !e.IsNil() {
+ val = e
+ }
+ }
+
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return p.unmarshalInterface(val.Interface().(Unmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerType) {
+ return p.unmarshalInterface(pv.Interface().(Unmarshaler), start)
+ }
+ }
+
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler), start)
+ }
+ }
+
+ var (
+ data []byte
+ saveData reflect.Value
+ comment []byte
+ saveComment reflect.Value
+ saveXML reflect.Value
+ saveXMLIndex int
+ saveXMLData []byte
+ saveAny reflect.Value
+ sv reflect.Value
+ tinfo *typeInfo
+ err error
+ )
+
+ switch v := val; v.Kind() {
+ default:
+ return errors.New("unknown type " + v.Type().String())
+
+ case reflect.Interface:
+ // TODO: For now, simply ignore the field. In the near
+ // future we may choose to unmarshal the start
+ // element on it, if not nil.
+ return p.Skip()
+
+ case reflect.Slice:
+ typ := v.Type()
+ if typ.Elem().Kind() == reflect.Uint8 {
+ // []byte
+ saveData = v
+ break
+ }
+
+ // Slice of element values.
+ // Grow slice.
+ n := v.Len()
+ if n >= v.Cap() {
+ ncap := 2 * n
+ if ncap < 4 {
+ ncap = 4
+ }
+ new := reflect.MakeSlice(typ, n, ncap)
+ reflect.Copy(new, v)
+ v.Set(new)
+ }
+ v.SetLen(n + 1)
+
+ // Recur to read element into slice.
+ if err := p.unmarshal(v.Index(n), start); err != nil {
+ v.SetLen(n)
+ return err
+ }
+ return nil
+
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String:
+ saveData = v
+
+ case reflect.Struct:
+ typ := v.Type()
+ if typ == nameType {
+ v.Set(reflect.ValueOf(start.Name))
+ break
+ }
+
+ sv = v
+ tinfo, err = getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Validate and assign element name.
+ if tinfo.xmlname != nil {
+ finfo := tinfo.xmlname
+ if finfo.name != "" && finfo.name != start.Name.Local {
+ return UnmarshalError("expected element type <" + finfo.name + "> but have <" + start.Name.Local + ">")
+ }
+ if finfo.xmlns != "" && finfo.xmlns != start.Name.Space {
+ e := "expected element <" + finfo.name + "> in name space " + finfo.xmlns + " but have "
+ if start.Name.Space == "" {
+ e += "no name space"
+ } else {
+ e += start.Name.Space
+ }
+ return UnmarshalError(e)
+ }
+ fv := finfo.value(sv)
+ if _, ok := fv.Interface().(Name); ok {
+ fv.Set(reflect.ValueOf(start.Name))
+ }
+ }
+
+ // Assign attributes.
+ // Also, determine whether we need to save character data or comments.
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ switch finfo.flags & fMode {
+ case fAttr:
+ strv := finfo.value(sv)
+ // Look for attribute.
+ for _, a := range start.Attr {
+ if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
+ if err := p.unmarshalAttr(strv, a); err != nil {
+ return err
+ }
+ break
+ }
+ }
+
+ case fCharData:
+ if !saveData.IsValid() {
+ saveData = finfo.value(sv)
+ }
+
+ case fComment:
+ if !saveComment.IsValid() {
+ saveComment = finfo.value(sv)
+ }
+
+ case fAny, fAny | fElement:
+ if !saveAny.IsValid() {
+ saveAny = finfo.value(sv)
+ }
+
+ case fInnerXml:
+ if !saveXML.IsValid() {
+ saveXML = finfo.value(sv)
+ if p.saved == nil {
+ saveXMLIndex = 0
+ p.saved = new(bytes.Buffer)
+ } else {
+ saveXMLIndex = p.savedOffset()
+ }
+ }
+ }
+ }
+ }
+
+ // Find end element.
+ // Process sub-elements along the way.
+Loop:
+ for {
+ var savedOffset int
+ if saveXML.IsValid() {
+ savedOffset = p.savedOffset()
+ }
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ consumed := false
+ if sv.IsValid() {
+ consumed, err = p.unmarshalPath(tinfo, sv, nil, &t)
+ if err != nil {
+ return err
+ }
+ if !consumed && saveAny.IsValid() {
+ consumed = true
+ if err := p.unmarshal(saveAny, &t); err != nil {
+ return err
+ }
+ }
+ }
+ if !consumed {
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ }
+
+ case EndElement:
+ if saveXML.IsValid() {
+ saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
+ if saveXMLIndex == 0 {
+ p.saved = nil
+ }
+ }
+ break Loop
+
+ case CharData:
+ if saveData.IsValid() {
+ data = append(data, t...)
+ }
+
+ case Comment:
+ if saveComment.IsValid() {
+ comment = append(comment, t...)
+ }
+ }
+ }
+
+ if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) {
+ if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+
+ if saveData.IsValid() && saveData.CanAddr() {
+ pv := saveData.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+ }
+
+ if err := copyValue(saveData, data); err != nil {
+ return err
+ }
+
+ switch t := saveComment; t.Kind() {
+ case reflect.String:
+ t.SetString(string(comment))
+ case reflect.Slice:
+ t.Set(reflect.ValueOf(comment))
+ }
+
+ switch t := saveXML; t.Kind() {
+ case reflect.String:
+ t.SetString(string(saveXMLData))
+ case reflect.Slice:
+ t.Set(reflect.ValueOf(saveXMLData))
+ }
+
+ return nil
+}
+
+func copyValue(dst reflect.Value, src []byte) (err error) {
+ dst0 := dst
+
+ if dst.Kind() == reflect.Ptr {
+ if dst.IsNil() {
+ dst.Set(reflect.New(dst.Type().Elem()))
+ }
+ dst = dst.Elem()
+ }
+
+ // Save accumulated data.
+ switch dst.Kind() {
+ case reflect.Invalid:
+ // Probably a comment.
+ default:
+ return errors.New("cannot unmarshal into " + dst0.Type().String())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetInt(itmp)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ utmp, err := strconv.ParseUint(string(src), 10, dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetUint(utmp)
+ case reflect.Float32, reflect.Float64:
+ ftmp, err := strconv.ParseFloat(string(src), dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetFloat(ftmp)
+ case reflect.Bool:
+ value, err := strconv.ParseBool(strings.TrimSpace(string(src)))
+ if err != nil {
+ return err
+ }
+ dst.SetBool(value)
+ case reflect.String:
+ dst.SetString(string(src))
+ case reflect.Slice:
+ if len(src) == 0 {
+ // non-nil to flag presence
+ src = []byte{}
+ }
+ dst.SetBytes(src)
+ }
+ return nil
+}
+
+// unmarshalPath walks down an XML structure looking for wanted
+// paths, and calls unmarshal on them.
+// The consumed result tells whether XML elements have been consumed
+// from the Decoder until start's matching end element, or if it's
+// still untouched because start is uninteresting for sv's fields.
+func (p *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) {
+ recurse := false
+Loop:
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fElement == 0 || len(finfo.parents) < len(parents) || finfo.xmlns != "" && finfo.xmlns != start.Name.Space {
+ continue
+ }
+ for j := range parents {
+ if parents[j] != finfo.parents[j] {
+ continue Loop
+ }
+ }
+ if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
+ // It's a perfect match, unmarshal the field.
+ return true, p.unmarshal(finfo.value(sv), start)
+ }
+ if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
+ // It's a prefix for the field. Break and recurse
+ // since it's not ok for one field path to be itself
+ // the prefix for another field path.
+ recurse = true
+
+ // We can reuse the same slice as long as we
+ // don't try to append to it.
+ parents = finfo.parents[:len(parents)+1]
+ break
+ }
+ }
+ if !recurse {
+ // We have no business with this element.
+ return false, nil
+ }
+ // The element is not a perfect match for any field, but one
+ // or more fields have the path to this element as a parent
+ // prefix. Recurse and attempt to match these.
+ for {
+ var tok Token
+ tok, err = p.Token()
+ if err != nil {
+ return true, err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ consumed2, err := p.unmarshalPath(tinfo, sv, parents, &t)
+ if err != nil {
+ return true, err
+ }
+ if !consumed2 {
+ if err := p.Skip(); err != nil {
+ return true, err
+ }
+ }
+ case EndElement:
+ return true, nil
+ }
+ }
+}
+
+// Skip reads tokens until it has consumed the end element
+// matching the most recent start element already consumed.
+// It recurs if it encounters a start element, so it can be used to
+// skip nested structures.
+// It returns nil if it finds an end element matching the start
+// element; otherwise it returns an error describing the problem.
+func (d *Decoder) Skip() error {
+ for {
+ tok, err := d.Token()
+ if err != nil {
+ return err
+ }
+ switch tok.(type) {
+ case StartElement:
+ if err := d.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+}
diff --git a/webdav/internal/xml/read_test.go b/webdav/internal/xml/read_test.go
new file mode 100644
index 0000000..02f1e10
--- /dev/null
+++ b/webdav/internal/xml/read_test.go
@@ -0,0 +1,744 @@
+// 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.
+
+package xml
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+// Stripped down Atom feed data structures.
+
+func TestUnmarshalFeed(t *testing.T) {
+ var f Feed
+ if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if !reflect.DeepEqual(f, atomFeed) {
+ t.Fatalf("have %#v\nwant %#v", f, atomFeed)
+ }
+}
+
+// hget http://codereview.appspot.com/rss/mine/rsc
+const atomFeedString = `
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
+</title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
+ An attempt at adding pubsubhubbub support to Rietveld.
+http://code.google.com/p/pubsubhubbub
+http://code.google.com/p/rietveld/issues/detail?id=155
+
+The server side of the protocol is trivial:
+ 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
+ feeds that will be pubsubhubbubbed.
+ 2. every time one of those feeds changes, tell the hub
+ with a simple POST request.
+
+I have tested this by adding debug prints to a local hub
+server and checking that the server got the right publish
+requests.
+
+I can&#39;t quite get the server to work, but I think the bug
+is not in my code. I think that the server expects to be
+able to grab the feed and see the feed&#39;s actual URL in
+the link rel=&quot;self&quot;, but the default value for that drops
+the :port from the URL, and I cannot for the life of me
+figure out how to get the Atom generator deep inside
+django not to do that, or even where it is doing that,
+or even what code is running to generate the Atom feed.
+(I thought I knew but I added some assert False statements
+and it kept running!)
+
+Ignoring that particular problem, I would appreciate
+feedback on the right way to get the two values at
+the top of feeds.py marked NOTE(rsc).
+
+
+</summary></entry><entry><title>rietveld: correct tab handling
+</title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
+ This fixes the buggy tab rendering that can be seen at
+http://codereview.appspot.com/116075/diff/1/2
+
+The fundamental problem was that the tab code was
+not being told what column the text began in, so it
+didn&#39;t know where to put the tab stops. Another problem
+was that some of the code assumed that string byte
+offsets were the same as column offsets, which is only
+true if there are no tabs.
+
+In the process of fixing this, I cleaned up the arguments
+to Fold and ExpandTabs and renamed them Break and
+_ExpandTabs so that I could be sure that I found all the
+call sites. I also wanted to verify that ExpandTabs was
+not being used from outside intra_region_diff.py.
+
+
+</summary></entry></feed> `
+
+type Feed struct {
+ XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
+ Title string `xml:"title"`
+ Id string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated time.Time `xml:"updated,attr"`
+ Author Person `xml:"author"`
+ Entry []Entry `xml:"entry"`
+}
+
+type Entry struct {
+ Title string `xml:"title"`
+ Id string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated time.Time `xml:"updated"`
+ Author Person `xml:"author"`
+ Summary Text `xml:"summary"`
+}
+
+type Link struct {
+ Rel string `xml:"rel,attr,omitempty"`
+ Href string `xml:"href,attr"`
+}
+
+type Person struct {
+ Name string `xml:"name"`
+ URI string `xml:"uri"`
+ Email string `xml:"email"`
+ InnerXML string `xml:",innerxml"`
+}
+
+type Text struct {
+ Type string `xml:"type,attr,omitempty"`
+ Body string `xml:",chardata"`
+}
+
+var atomFeed = Feed{
+ XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
+ Title: "Code Review - My issues",
+ Link: []Link{
+ {Rel: "alternate", Href: "http://codereview.appspot.com/"},
+ {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
+ },
+ Id: "http://codereview.appspot.com/",
+ Updated: ParseTime("2009-10-04T01:35:58+00:00"),
+ Author: Person{
+ Name: "rietveld<>",
+ InnerXML: "<name>rietveld<></name>",
+ },
+ Entry: []Entry{
+ {
+ Title: "rietveld: an attempt at pubsubhubbub\n",
+ Link: []Link{
+ {Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
+ },
+ Updated: ParseTime("2009-10-04T01:35:58+00:00"),
+ Author: Person{
+ Name: "email-address-removed",
+ InnerXML: "<name>email-address-removed</name>",
+ },
+ Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
+ Summary: Text{
+ Type: "html",
+ Body: `
+ An attempt at adding pubsubhubbub support to Rietveld.
+http://code.google.com/p/pubsubhubbub
+http://code.google.com/p/rietveld/issues/detail?id=155
+
+The server side of the protocol is trivial:
+ 1. add a <link rel="hub" href="hub-server"> tag to all
+ feeds that will be pubsubhubbubbed.
+ 2. every time one of those feeds changes, tell the hub
+ with a simple POST request.
+
+I have tested this by adding debug prints to a local hub
+server and checking that the server got the right publish
+requests.
+
+I can't quite get the server to work, but I think the bug
+is not in my code. I think that the server expects to be
+able to grab the feed and see the feed's actual URL in
+the link rel="self", but the default value for that drops
+the :port from the URL, and I cannot for the life of me
+figure out how to get the Atom generator deep inside
+django not to do that, or even where it is doing that,
+or even what code is running to generate the Atom feed.
+(I thought I knew but I added some assert False statements
+and it kept running!)
+
+Ignoring that particular problem, I would appreciate
+feedback on the right way to get the two values at
+the top of feeds.py marked NOTE(rsc).
+
+
+`,
+ },
+ },
+ {
+ Title: "rietveld: correct tab handling\n",
+ Link: []Link{
+ {Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
+ },
+ Updated: ParseTime("2009-10-03T23:02:17+00:00"),
+ Author: Person{
+ Name: "email-address-removed",
+ InnerXML: "<name>email-address-removed</name>",
+ },
+ Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
+ Summary: Text{
+ Type: "html",
+ Body: `
+ This fixes the buggy tab rendering that can be seen at
+http://codereview.appspot.com/116075/diff/1/2
+
+The fundamental problem was that the tab code was
+not being told what column the text began in, so it
+didn't know where to put the tab stops. Another problem
+was that some of the code assumed that string byte
+offsets were the same as column offsets, which is only
+true if there are no tabs.
+
+In the process of fixing this, I cleaned up the arguments
+to Fold and ExpandTabs and renamed them Break and
+_ExpandTabs so that I could be sure that I found all the
+call sites. I also wanted to verify that ExpandTabs was
+not being used from outside intra_region_diff.py.
+
+
+`,
+ },
+ },
+ },
+}
+
+const pathTestString = `
+<Result>
+ <Before>1</Before>
+ <Items>
+ <Item1>
+ <Value>A</Value>
+ </Item1>
+ <Item2>
+ <Value>B</Value>
+ </Item2>
+ <Item1>
+ <Value>C</Value>
+ <Value>D</Value>
+ </Item1>
+ <_>
+ <Value>E</Value>
+ </_>
+ </Items>
+ <After>2</After>
+</Result>
+`
+
+type PathTestItem struct {
+ Value string
+}
+
+type PathTestA struct {
+ Items []PathTestItem `xml:">Item1"`
+ Before, After string
+}
+
+type PathTestB struct {
+ Other []PathTestItem `xml:"Items>Item1"`
+ Before, After string
+}
+
+type PathTestC struct {
+ Values1 []string `xml:"Items>Item1>Value"`
+ Values2 []string `xml:"Items>Item2>Value"`
+ Before, After string
+}
+
+type PathTestSet struct {
+ Item1 []PathTestItem
+}
+
+type PathTestD struct {
+ Other PathTestSet `xml:"Items"`
+ Before, After string
+}
+
+type PathTestE struct {
+ Underline string `xml:"Items>_>Value"`
+ Before, After string
+}
+
+var pathTests = []interface{}{
+ &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
+ &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
+ &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
+ &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
+ &PathTestE{Underline: "E", Before: "1", After: "2"},
+}
+
+func TestUnmarshalPaths(t *testing.T) {
+ for _, pt := range pathTests {
+ v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
+ if err := Unmarshal([]byte(pathTestString), v); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if !reflect.DeepEqual(v, pt) {
+ t.Fatalf("have %#v\nwant %#v", v, pt)
+ }
+ }
+}
+
+type BadPathTestA struct {
+ First string `xml:"items>item1"`
+ Other string `xml:"items>item2"`
+ Second string `xml:"items"`
+}
+
+type BadPathTestB struct {
+ Other string `xml:"items>item2>value"`
+ First string `xml:"items>item1"`
+ Second string `xml:"items>item1>value"`
+}
+
+type BadPathTestC struct {
+ First string
+ Second string `xml:"First"`
+}
+
+type BadPathTestD struct {
+ BadPathEmbeddedA
+ BadPathEmbeddedB
+}
+
+type BadPathEmbeddedA struct {
+ First string
+}
+
+type BadPathEmbeddedB struct {
+ Second string `xml:"First"`
+}
+
+var badPathTests = []struct {
+ v, e interface{}
+}{
+ {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
+ {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
+ {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
+ {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
+}
+
+func TestUnmarshalBadPaths(t *testing.T) {
+ for _, tt := range badPathTests {
+ err := Unmarshal([]byte(pathTestString), tt.v)
+ if !reflect.DeepEqual(err, tt.e) {
+ t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
+ }
+ }
+}
+
+const OK = "OK"
+const withoutNameTypeData = `
+<?xml version="1.0" charset="utf-8"?>
+<Test3 Attr="OK" />`
+
+type TestThree struct {
+ XMLName Name `xml:"Test3"`
+ Attr string `xml:",attr"`
+}
+
+func TestUnmarshalWithoutNameType(t *testing.T) {
+ var x TestThree
+ if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if x.Attr != OK {
+ t.Fatalf("have %v\nwant %v", x.Attr, OK)
+ }
+}
+
+func TestUnmarshalAttr(t *testing.T) {
+ type ParamVal struct {
+ Int int `xml:"int,attr"`
+ }
+
+ type ParamPtr struct {
+ Int *int `xml:"int,attr"`
+ }
+
+ type ParamStringPtr struct {
+ Int *string `xml:"int,attr"`
+ }
+
+ x := []byte(`<Param int="1" />`)
+
+ p1 := &ParamPtr{}
+ if err := Unmarshal(x, p1); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p1.Int == nil {
+ t.Fatalf("Unmarshal failed in to *int field")
+ } else if *p1.Int != 1 {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
+ }
+
+ p2 := &ParamVal{}
+ if err := Unmarshal(x, p2); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p2.Int != 1 {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
+ }
+
+ p3 := &ParamStringPtr{}
+ if err := Unmarshal(x, p3); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p3.Int == nil {
+ t.Fatalf("Unmarshal failed in to *string field")
+ } else if *p3.Int != "1" {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
+ }
+}
+
+type Tables struct {
+ HTable string `xml:"http://www.w3.org/TR/html4/ table"`
+ FTable string `xml:"http://www.w3schools.com/furniture table"`
+}
+
+var tables = []struct {
+ xml string
+ tab Tables
+ ns string
+}{
+ {
+ xml: `<Tables>` +
+ `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
+ `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
+ `</Tables>`,
+ tab: Tables{"hello", "world"},
+ },
+ {
+ xml: `<Tables>` +
+ `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
+ `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
+ `</Tables>`,
+ tab: Tables{"hello", "world"},
+ },
+ {
+ xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
+ `<f:table>world</f:table>` +
+ `<h:table>hello</h:table>` +
+ `</Tables>`,
+ tab: Tables{"hello", "world"},
+ },
+ {
+ xml: `<Tables>` +
+ `<table>bogus</table>` +
+ `</Tables>`,
+ tab: Tables{},
+ },
+ {
+ xml: `<Tables>` +
+ `<table>only</table>` +
+ `</Tables>`,
+ tab: Tables{HTable: "only"},
+ ns: "http://www.w3.org/TR/html4/",
+ },
+ {
+ xml: `<Tables>` +
+ `<table>only</table>` +
+ `</Tables>`,
+ tab: Tables{FTable: "only"},
+ ns: "http://www.w3schools.com/furniture",
+ },
+ {
+ xml: `<Tables>` +
+ `<table>only</table>` +
+ `</Tables>`,
+ tab: Tables{},
+ ns: "something else entirely",
+ },
+}
+
+func TestUnmarshalNS(t *testing.T) {
+ for i, tt := range tables {
+ var dst Tables
+ var err error
+ if tt.ns != "" {
+ d := NewDecoder(strings.NewReader(tt.xml))
+ d.DefaultSpace = tt.ns
+ err = d.Decode(&dst)
+ } else {
+ err = Unmarshal([]byte(tt.xml), &dst)
+ }
+ if err != nil {
+ t.Errorf("#%d: Unmarshal: %v", i, err)
+ continue
+ }
+ want := tt.tab
+ if dst != want {
+ t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
+ }
+ }
+}
+
+func TestRoundTrip(t *testing.T) {
+ // From issue 7535
+ const s = `<ex:element xmlns:ex="http://example.com/schema"></ex:element>`
+ in := bytes.NewBufferString(s)
+ for i := 0; i < 10; i++ {
+ out := &bytes.Buffer{}
+ d := NewDecoder(in)
+ e := NewEncoder(out)
+
+ for {
+ t, err := d.Token()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ fmt.Println("failed:", err)
+ return
+ }
+ e.EncodeToken(t)
+ }
+ e.Flush()
+ in = out
+ }
+ if got := in.String(); got != s {
+ t.Errorf("have: %q\nwant: %q\n", got, s)
+ }
+}
+
+func TestMarshalNS(t *testing.T) {
+ dst := Tables{"hello", "world"}
+ data, err := Marshal(&dst)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
+ str := string(data)
+ if str != want {
+ t.Errorf("have: %q\nwant: %q\n", str, want)
+ }
+}
+
+type TableAttrs struct {
+ TAttr TAttr
+}
+
+type TAttr struct {
+ HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
+ FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
+ Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
+ Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
+ Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
+ Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
+ Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
+}
+
+var tableAttrs = []struct {
+ xml string
+ tab TableAttrs
+ ns string
+}{
+ {
+ xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
+ `h:table="hello" f:table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
+ },
+ {
+ xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
+ `h:table="hello" f:table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
+ },
+ {
+ xml: `<TableAttrs><TAttr ` +
+ `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
+ },
+ {
+ // Default space does not apply to attribute names.
+ xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
+ `h:table="hello" table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
+ },
+ {
+ // Default space does not apply to attribute names.
+ xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
+ `table="hello" f:table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
+ },
+ {
+ xml: `<TableAttrs><TAttr ` +
+ `table="bogus" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{},
+ },
+ {
+ // Default space does not apply to attribute names.
+ xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
+ `h:table="hello" table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
+ ns: "http://www.w3schools.com/furniture",
+ },
+ {
+ // Default space does not apply to attribute names.
+ xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
+ `table="hello" f:table="world" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
+ ns: "http://www.w3.org/TR/html4/",
+ },
+ {
+ xml: `<TableAttrs><TAttr ` +
+ `table="bogus" ` +
+ `/></TableAttrs>`,
+ tab: TableAttrs{},
+ ns: "something else entirely",
+ },
+}
+
+func TestUnmarshalNSAttr(t *testing.T) {
+ for i, tt := range tableAttrs {
+ var dst TableAttrs
+ var err error
+ if tt.ns != "" {
+ d := NewDecoder(strings.NewReader(tt.xml))
+ d.DefaultSpace = tt.ns
+ err = d.Decode(&dst)
+ } else {
+ err = Unmarshal([]byte(tt.xml), &dst)
+ }
+ if err != nil {
+ t.Errorf("#%d: Unmarshal: %v", i, err)
+ continue
+ }
+ want := tt.tab
+ if dst != want {
+ t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
+ }
+ }
+}
+
+func TestMarshalNSAttr(t *testing.T) {
+ src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
+ data, err := Marshal(&src)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ want := `<TableAttrs><TAttr xmlns:json_1="http://golang.org/2/json/" xmlns:json="http://golang.org/json/" xmlns:_xmlfoo="http://golang.org/xmlfoo/" xmlns:_xml="http://golang.org/xml/" xmlns:furniture="http://www.w3schools.com/furniture" xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" furniture:table="world" xml:lang="en_US" _xml:other="other1" _xmlfoo:other="other2" json:other="other3" json_1:other="other4"></TAttr></TableAttrs>`
+ str := string(data)
+ if str != want {
+ t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
+ }
+
+ var dst TableAttrs
+ if err := Unmarshal(data, &dst); err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+
+ if dst != src {
+ t.Errorf("Unmarshal = %q, want %q", dst, src)
+ }
+}
+
+type MyCharData struct {
+ body string
+}
+
+func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error {
+ for {
+ t, err := d.Token()
+ if err == io.EOF { // found end of element
+ break
+ }
+ if err != nil {
+ return err
+ }
+ if char, ok := t.(CharData); ok {
+ m.body += string(char)
+ }
+ }
+ return nil
+}
+
+var _ Unmarshaler = (*MyCharData)(nil)
+
+func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error {
+ panic("must not call")
+}
+
+type MyAttr struct {
+ attr string
+}
+
+func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
+ m.attr = attr.Value
+ return nil
+}
+
+var _ UnmarshalerAttr = (*MyAttr)(nil)
+
+type MyStruct struct {
+ Data *MyCharData
+ Attr *MyAttr `xml:",attr"`
+
+ Data2 MyCharData
+ Attr2 MyAttr `xml:",attr"`
+}
+
+func TestUnmarshaler(t *testing.T) {
+ xml := `<?xml version="1.0" encoding="utf-8"?>
+ <MyStruct Attr="attr1" Attr2="attr2">
+ <Data>hello <!-- comment -->world</Data>
+ <Data2>howdy <!-- comment -->world</Data2>
+ </MyStruct>
+ `
+
+ var m MyStruct
+ if err := Unmarshal([]byte(xml), &m); err != nil {
+ t.Fatal(err)
+ }
+
+ if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" {
+ t.Errorf("m=%#+v\n", m)
+ }
+}
+
+type Pea struct {
+ Cotelydon string
+}
+
+type Pod struct {
+ Pea interface{} `xml:"Pea"`
+}
+
+// https://golang.org/issue/6836
+func TestUnmarshalIntoInterface(t *testing.T) {
+ pod := new(Pod)
+ pod.Pea = new(Pea)
+ xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
+ err := Unmarshal([]byte(xml), pod)
+ if err != nil {
+ t.Fatalf("failed to unmarshal %q: %v", xml, err)
+ }
+ pea, ok := pod.Pea.(*Pea)
+ if !ok {
+ t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
+ }
+ have, want := pea.Cotelydon, "Green stuff"
+ if have != want {
+ t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
+ }
+}
diff --git a/webdav/internal/xml/typeinfo.go b/webdav/internal/xml/typeinfo.go
new file mode 100644
index 0000000..c9a6421
--- /dev/null
+++ b/webdav/internal/xml/typeinfo.go
@@ -0,0 +1,371 @@
+// Copyright 2011 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.
+
+package xml
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// typeInfo holds details for the xml representation of a type.
+type typeInfo struct {
+ xmlname *fieldInfo
+ fields []fieldInfo
+}
+
+// fieldInfo holds details for the xml representation of a single field.
+type fieldInfo struct {
+ idx []int
+ name string
+ xmlns string
+ flags fieldFlags
+ parents []string
+}
+
+type fieldFlags int
+
+const (
+ fElement fieldFlags = 1 << iota
+ fAttr
+ fCharData
+ fInnerXml
+ fComment
+ fAny
+
+ fOmitEmpty
+
+ fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
+)
+
+var tinfoMap = make(map[reflect.Type]*typeInfo)
+var tinfoLock sync.RWMutex
+
+var nameType = reflect.TypeOf(Name{})
+
+// getTypeInfo returns the typeInfo structure with details necessary
+// for marshalling and unmarshalling typ.
+func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
+ tinfoLock.RLock()
+ tinfo, ok := tinfoMap[typ]
+ tinfoLock.RUnlock()
+ if ok {
+ return tinfo, nil
+ }
+ tinfo = &typeInfo{}
+ if typ.Kind() == reflect.Struct && typ != nameType {
+ n := typ.NumField()
+ for i := 0; i < n; i++ {
+ f := typ.Field(i)
+ if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
+ continue // Private field
+ }
+
+ // For embedded structs, embed its fields.
+ if f.Anonymous {
+ t := f.Type
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() == reflect.Struct {
+ inner, err := getTypeInfo(t)
+ if err != nil {
+ return nil, err
+ }
+ if tinfo.xmlname == nil {
+ tinfo.xmlname = inner.xmlname
+ }
+ for _, finfo := range inner.fields {
+ finfo.idx = append([]int{i}, finfo.idx...)
+ if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
+ return nil, err
+ }
+ }
+ continue
+ }
+ }
+
+ finfo, err := structFieldInfo(typ, &f)
+ if err != nil {
+ return nil, err
+ }
+
+ if f.Name == "XMLName" {
+ tinfo.xmlname = finfo
+ continue
+ }
+
+ // Add the field if it doesn't conflict with other fields.
+ if err := addFieldInfo(typ, tinfo, finfo); err != nil {
+ return nil, err
+ }
+ }
+ }
+ tinfoLock.Lock()
+ tinfoMap[typ] = tinfo
+ tinfoLock.Unlock()
+ return tinfo, nil
+}
+
+// structFieldInfo builds and returns a fieldInfo for f.
+func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
+ finfo := &fieldInfo{idx: f.Index}
+
+ // Split the tag from the xml namespace if necessary.
+ tag := f.Tag.Get("xml")
+ if i := strings.Index(tag, " "); i >= 0 {
+ finfo.xmlns, tag = tag[:i], tag[i+1:]
+ }
+
+ // Parse flags.
+ tokens := strings.Split(tag, ",")
+ if len(tokens) == 1 {
+ finfo.flags = fElement
+ } else {
+ tag = tokens[0]
+ for _, flag := range tokens[1:] {
+ switch flag {
+ case "attr":
+ finfo.flags |= fAttr
+ case "chardata":
+ finfo.flags |= fCharData
+ case "innerxml":
+ finfo.flags |= fInnerXml
+ case "comment":
+ finfo.flags |= fComment
+ case "any":
+ finfo.flags |= fAny
+ case "omitempty":
+ finfo.flags |= fOmitEmpty
+ }
+ }
+
+ // Validate the flags used.
+ valid := true
+ switch mode := finfo.flags & fMode; mode {
+ case 0:
+ finfo.flags |= fElement
+ case fAttr, fCharData, fInnerXml, fComment, fAny:
+ if f.Name == "XMLName" || tag != "" && mode != fAttr {
+ valid = false
+ }
+ default:
+ // This will also catch multiple modes in a single field.
+ valid = false
+ }
+ if finfo.flags&fMode == fAny {
+ finfo.flags |= fElement
+ }
+ if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
+ valid = false
+ }
+ if !valid {
+ return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+ }
+
+ // Use of xmlns without a name is not allowed.
+ if finfo.xmlns != "" && tag == "" {
+ return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+
+ if f.Name == "XMLName" {
+ // The XMLName field records the XML element name. Don't
+ // process it as usual because its name should default to
+ // empty rather than to the field name.
+ finfo.name = tag
+ return finfo, nil
+ }
+
+ if tag == "" {
+ // If the name part of the tag is completely empty, get
+ // default from XMLName of underlying struct if feasible,
+ // or field name otherwise.
+ if xmlname := lookupXMLName(f.Type); xmlname != nil {
+ finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
+ } else {
+ finfo.name = f.Name
+ }
+ return finfo, nil
+ }
+
+ if finfo.xmlns == "" && finfo.flags&fAttr == 0 {
+ // If it's an element no namespace specified, get the default
+ // from the XMLName of enclosing struct if possible.
+ if xmlname := lookupXMLName(typ); xmlname != nil {
+ finfo.xmlns = xmlname.xmlns
+ }
+ }
+
+ // Prepare field name and parents.
+ parents := strings.Split(tag, ">")
+ if parents[0] == "" {
+ parents[0] = f.Name
+ }
+ if parents[len(parents)-1] == "" {
+ return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
+ }
+ finfo.name = parents[len(parents)-1]
+ if len(parents) > 1 {
+ if (finfo.flags & fElement) == 0 {
+ return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
+ }
+ finfo.parents = parents[:len(parents)-1]
+ }
+
+ // If the field type has an XMLName field, the names must match
+ // so that the behavior of both marshalling and unmarshalling
+ // is straightforward and unambiguous.
+ if finfo.flags&fElement != 0 {
+ ftyp := f.Type
+ xmlname := lookupXMLName(ftyp)
+ if xmlname != nil && xmlname.name != finfo.name {
+ return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
+ finfo.name, typ, f.Name, xmlname.name, ftyp)
+ }
+ }
+ return finfo, nil
+}
+
+// lookupXMLName returns the fieldInfo for typ's XMLName field
+// in case it exists and has a valid xml field tag, otherwise
+// it returns nil.
+func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
+ for typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
+ if typ.Kind() != reflect.Struct {
+ return nil
+ }
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ f := typ.Field(i)
+ if f.Name != "XMLName" {
+ continue
+ }
+ finfo, err := structFieldInfo(typ, &f)
+ if finfo.name != "" && err == nil {
+ return finfo
+ }
+ // Also consider errors as a non-existent field tag
+ // and let getTypeInfo itself report the error.
+ break
+ }
+ return nil
+}
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+// addFieldInfo adds finfo to tinfo.fields if there are no
+// conflicts, or if conflicts arise from previous fields that were
+// obtained from deeper embedded structures than finfo. In the latter
+// case, the conflicting entries are dropped.
+// A conflict occurs when the path (parent + name) to a field is
+// itself a prefix of another path, or when two paths match exactly.
+// It is okay for field paths to share a common, shorter prefix.
+func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
+ var conflicts []int
+Loop:
+ // First, figure all conflicts. Most working code will have none.
+ for i := range tinfo.fields {
+ oldf := &tinfo.fields[i]
+ if oldf.flags&fMode != newf.flags&fMode {
+ continue
+ }
+ if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
+ continue
+ }
+ minl := min(len(newf.parents), len(oldf.parents))
+ for p := 0; p < minl; p++ {
+ if oldf.parents[p] != newf.parents[p] {
+ continue Loop
+ }
+ }
+ if len(oldf.parents) > len(newf.parents) {
+ if oldf.parents[len(newf.parents)] == newf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else if len(oldf.parents) < len(newf.parents) {
+ if newf.parents[len(oldf.parents)] == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else {
+ if newf.name == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ }
+ }
+ // Without conflicts, add the new field and return.
+ if conflicts == nil {
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+ }
+
+ // If any conflict is shallower, ignore the new field.
+ // This matches the Go field resolution on embedding.
+ for _, i := range conflicts {
+ if len(tinfo.fields[i].idx) < len(newf.idx) {
+ return nil
+ }
+ }
+
+ // Otherwise, if any of them is at the same depth level, it's an error.
+ for _, i := range conflicts {
+ oldf := &tinfo.fields[i]
+ if len(oldf.idx) == len(newf.idx) {
+ f1 := typ.FieldByIndex(oldf.idx)
+ f2 := typ.FieldByIndex(newf.idx)
+ return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
+ }
+ }
+
+ // Otherwise, the new field is shallower, and thus takes precedence,
+ // so drop the conflicting fields from tinfo and append the new one.
+ for c := len(conflicts) - 1; c >= 0; c-- {
+ i := conflicts[c]
+ copy(tinfo.fields[i:], tinfo.fields[i+1:])
+ tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
+ }
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+}
+
+// A TagPathError represents an error in the unmarshalling process
+// caused by the use of field tags with conflicting paths.
+type TagPathError struct {
+ Struct reflect.Type
+ Field1, Tag1 string
+ Field2, Tag2 string
+}
+
+func (e *TagPathError) Error() string {
+ return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
+}
+
+// value returns v's field value corresponding to finfo.
+// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
+// and dereferences pointers as necessary.
+func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
+ for i, x := range finfo.idx {
+ if i > 0 {
+ t := v.Type()
+ if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
+}
diff --git a/webdav/internal/xml/xml.go b/webdav/internal/xml/xml.go
new file mode 100644
index 0000000..ffab4a7
--- /dev/null
+++ b/webdav/internal/xml/xml.go
@@ -0,0 +1,1998 @@
+// 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.
+
+// Package xml implements a simple XML 1.0 parser that
+// understands XML name spaces.
+package xml
+
+// References:
+// Annotated XML spec: http://www.xml.com/axml/testaxml.htm
+// XML name spaces: http://www.w3.org/TR/REC-xml-names/
+
+// TODO(rsc):
+// Test error handling.
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A SyntaxError represents a syntax error in the XML input stream.
+type SyntaxError struct {
+ Msg string
+ Line int
+}
+
+func (e *SyntaxError) Error() string {
+ return "XML syntax error on line " + strconv.Itoa(e.Line) + ": " + e.Msg
+}
+
+// A Name represents an XML name (Local) annotated with a name space
+// identifier (Space). In tokens returned by Decoder.Token, the Space
+// identifier is given as a canonical URL, not the short prefix used in
+// the document being parsed.
+//
+// As a special case, XML namespace declarations will use the literal
+// string "xmlns" for the Space field instead of the fully resolved URL.
+// See Encoder.EncodeToken for more information on namespace encoding
+// behaviour.
+type Name struct {
+ Space, Local string
+}
+
+// isNamespace reports whether the name is a namespace-defining name.
+func (name Name) isNamespace() bool {
+ return name.Local == "xmlns" || name.Space == "xmlns"
+}
+
+// An Attr represents an attribute in an XML element (Name=Value).
+type Attr struct {
+ Name Name
+ Value string
+}
+
+// A Token is an interface holding one of the token types:
+// StartElement, EndElement, CharData, Comment, ProcInst, or Directive.
+type Token interface{}
+
+// A StartElement represents an XML start element.
+type StartElement struct {
+ Name Name
+ Attr []Attr
+}
+
+func (e StartElement) Copy() StartElement {
+ attrs := make([]Attr, len(e.Attr))
+ copy(attrs, e.Attr)
+ e.Attr = attrs
+ return e
+}
+
+// End returns the corresponding XML end element.
+func (e StartElement) End() EndElement {
+ return EndElement{e.Name}
+}
+
+// setDefaultNamespace sets the namespace of the element
+// as the default for all elements contained within it.
+func (e *StartElement) setDefaultNamespace() {
+ if e.Name.Space == "" {
+ // If there's no namespace on the element, don't
+ // set the default. Strictly speaking this might be wrong, as
+ // we can't tell if the element had no namespace set
+ // or was just using the default namespace.
+ return
+ }
+ // Don't add a default name space if there's already one set.
+ for _, attr := range e.Attr {
+ if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
+ return
+ }
+ }
+ e.Attr = append(e.Attr, Attr{
+ Name: Name{
+ Local: "xmlns",
+ },
+ Value: e.Name.Space,
+ })
+}
+
+// An EndElement represents an XML end element.
+type EndElement struct {
+ Name Name
+}
+
+// A CharData represents XML character data (raw text),
+// in which XML escape sequences have been replaced by
+// the characters they represent.
+type CharData []byte
+
+func makeCopy(b []byte) []byte {
+ b1 := make([]byte, len(b))
+ copy(b1, b)
+ return b1
+}
+
+func (c CharData) Copy() CharData { return CharData(makeCopy(c)) }
+
+// A Comment represents an XML comment of the form <!--comment-->.
+// The bytes do not include the <!-- and --> comment markers.
+type Comment []byte
+
+func (c Comment) Copy() Comment { return Comment(makeCopy(c)) }
+
+// A ProcInst represents an XML processing instruction of the form <?target inst?>
+type ProcInst struct {
+ Target string
+ Inst []byte
+}
+
+func (p ProcInst) Copy() ProcInst {
+ p.Inst = makeCopy(p.Inst)
+ return p
+}
+
+// A Directive represents an XML directive of the form <!text>.
+// The bytes do not include the <! and > markers.
+type Directive []byte
+
+func (d Directive) Copy() Directive { return Directive(makeCopy(d)) }
+
+// CopyToken returns a copy of a Token.
+func CopyToken(t Token) Token {
+ switch v := t.(type) {
+ case CharData:
+ return v.Copy()
+ case Comment:
+ return v.Copy()
+ case Directive:
+ return v.Copy()
+ case ProcInst:
+ return v.Copy()
+ case StartElement:
+ return v.Copy()
+ }
+ return t
+}
+
+// A Decoder represents an XML parser reading a particular input stream.
+// The parser assumes that its input is encoded in UTF-8.
+type Decoder struct {
+ // Strict defaults to true, enforcing the requirements
+ // of the XML specification.
+ // If set to false, the parser allows input containing common
+ // mistakes:
+ // * If an element is missing an end tag, the parser invents
+ // end tags as necessary to keep the return values from Token
+ // properly balanced.
+ // * In attribute values and character data, unknown or malformed
+ // character entities (sequences beginning with &) are left alone.
+ //
+ // Setting:
+ //
+ // d.Strict = false;
+ // d.AutoClose = HTMLAutoClose;
+ // d.Entity = HTMLEntity
+ //
+ // creates a parser that can handle typical HTML.
+ //
+ // Strict mode does not enforce the requirements of the XML name spaces TR.
+ // In particular it does not reject name space tags using undefined prefixes.
+ // Such tags are recorded with the unknown prefix as the name space URL.
+ Strict bool
+
+ // When Strict == false, AutoClose indicates a set of elements to
+ // consider closed immediately after they are opened, regardless
+ // of whether an end element is present.
+ AutoClose []string
+
+ // Entity can be used to map non-standard entity names to string replacements.
+ // The parser behaves as if these standard mappings are present in the map,
+ // regardless of the actual map content:
+ //
+ // "lt": "<",
+ // "gt": ">",
+ // "amp": "&",
+ // "apos": "'",
+ // "quot": `"`,
+ Entity map[string]string
+
+ // CharsetReader, if non-nil, defines a function to generate
+ // charset-conversion readers, converting from the provided
+ // non-UTF-8 charset into UTF-8. If CharsetReader is nil or
+ // returns an error, parsing stops with an error. One of the
+ // the CharsetReader's result values must be non-nil.
+ CharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+ // DefaultSpace sets the default name space used for unadorned tags,
+ // as if the entire XML stream were wrapped in an element containing
+ // the attribute xmlns="DefaultSpace".
+ DefaultSpace string
+
+ r io.ByteReader
+ buf bytes.Buffer
+ saved *bytes.Buffer
+ stk *stack
+ free *stack
+ needClose bool
+ toClose Name
+ nextToken Token
+ nextByte int
+ ns map[string]string
+ err error
+ line int
+ offset int64
+ unmarshalDepth int
+}
+
+// NewDecoder creates a new XML parser reading from r.
+// If r does not implement io.ByteReader, NewDecoder will
+// do its own buffering.
+func NewDecoder(r io.Reader) *Decoder {
+ d := &Decoder{
+ ns: make(map[string]string),
+ nextByte: -1,
+ line: 1,
+ Strict: true,
+ }
+ d.switchToReader(r)
+ return d
+}
+
+// Token returns the next XML token in the input stream.
+// At the end of the input stream, Token returns nil, io.EOF.
+//
+// Slices of bytes in the returned token data refer to the
+// parser's internal buffer and remain valid only until the next
+// call to Token. To acquire a copy of the bytes, call CopyToken
+// or the token's Copy method.
+//
+// Token expands self-closing elements such as <br/>
+// into separate start and end elements returned by successive calls.
+//
+// Token guarantees that the StartElement and EndElement
+// tokens it returns are properly nested and matched:
+// if Token encounters an unexpected end element,
+// it will return an error.
+//
+// Token implements XML name spaces as described by
+// http://www.w3.org/TR/REC-xml-names/. Each of the
+// Name structures contained in the Token has the Space
+// set to the URL identifying its name space when known.
+// If Token encounters an unrecognized name space prefix,
+// it uses the prefix as the Space rather than report an error.
+func (d *Decoder) Token() (t Token, err error) {
+ if d.stk != nil && d.stk.kind == stkEOF {
+ err = io.EOF
+ return
+ }
+ if d.nextToken != nil {
+ t = d.nextToken
+ d.nextToken = nil
+ } else if t, err = d.rawToken(); err != nil {
+ return
+ }
+
+ if !d.Strict {
+ if t1, ok := d.autoClose(t); ok {
+ d.nextToken = t
+ t = t1
+ }
+ }
+ switch t1 := t.(type) {
+ case StartElement:
+ // In XML name spaces, the translations listed in the
+ // attributes apply to the element name and
+ // to the other attribute names, so process
+ // the translations first.
+ for _, a := range t1.Attr {
+ if a.Name.Space == "xmlns" {
+ v, ok := d.ns[a.Name.Local]
+ d.pushNs(a.Name.Local, v, ok)
+ d.ns[a.Name.Local] = a.Value
+ }
+ if a.Name.Space == "" && a.Name.Local == "xmlns" {
+ // Default space for untagged names
+ v, ok := d.ns[""]
+ d.pushNs("", v, ok)
+ d.ns[""] = a.Value
+ }
+ }
+
+ d.translate(&t1.Name, true)
+ for i := range t1.Attr {
+ d.translate(&t1.Attr[i].Name, false)
+ }
+ d.pushElement(t1.Name)
+ t = t1
+
+ case EndElement:
+ d.translate(&t1.Name, true)
+ if !d.popElement(&t1) {
+ return nil, d.err
+ }
+ t = t1
+ }
+ return
+}
+
+const xmlURL = "http://www.w3.org/XML/1998/namespace"
+
+// Apply name space translation to name n.
+// The default name space (for Space=="")
+// applies only to element names, not to attribute names.
+func (d *Decoder) translate(n *Name, isElementName bool) {
+ switch {
+ case n.Space == "xmlns":
+ return
+ case n.Space == "" && !isElementName:
+ return
+ case n.Space == "xml":
+ n.Space = xmlURL
+ case n.Space == "" && n.Local == "xmlns":
+ return
+ }
+ if v, ok := d.ns[n.Space]; ok {
+ n.Space = v
+ } else if n.Space == "" {
+ n.Space = d.DefaultSpace
+ }
+}
+
+func (d *Decoder) switchToReader(r io.Reader) {
+ // Get efficient byte at a time reader.
+ // Assume that if reader has its own
+ // ReadByte, it's efficient enough.
+ // Otherwise, use bufio.
+ if rb, ok := r.(io.ByteReader); ok {
+ d.r = rb
+ } else {
+ d.r = bufio.NewReader(r)
+ }
+}
+
+// Parsing state - stack holds old name space translations
+// and the current set of open elements. The translations to pop when
+// ending a given tag are *below* it on the stack, which is
+// more work but forced on us by XML.
+type stack struct {
+ next *stack
+ kind int
+ name Name
+ ok bool
+}
+
+const (
+ stkStart = iota
+ stkNs
+ stkEOF
+)
+
+func (d *Decoder) push(kind int) *stack {
+ s := d.free
+ if s != nil {
+ d.free = s.next
+ } else {
+ s = new(stack)
+ }
+ s.next = d.stk
+ s.kind = kind
+ d.stk = s
+ return s
+}
+
+func (d *Decoder) pop() *stack {
+ s := d.stk
+ if s != nil {
+ d.stk = s.next
+ s.next = d.free
+ d.free = s
+ }
+ return s
+}
+
+// Record that after the current element is finished
+// (that element is already pushed on the stack)
+// Token should return EOF until popEOF is called.
+func (d *Decoder) pushEOF() {
+ // Walk down stack to find Start.
+ // It might not be the top, because there might be stkNs
+ // entries above it.
+ start := d.stk
+ for start.kind != stkStart {
+ start = start.next
+ }
+ // The stkNs entries below a start are associated with that
+ // element too; skip over them.
+ for start.next != nil && start.next.kind == stkNs {
+ start = start.next
+ }
+ s := d.free
+ if s != nil {
+ d.free = s.next
+ } else {
+ s = new(stack)
+ }
+ s.kind = stkEOF
+ s.next = start.next
+ start.next = s
+}
+
+// Undo a pushEOF.
+// The element must have been finished, so the EOF should be at the top of the stack.
+func (d *Decoder) popEOF() bool {
+ if d.stk == nil || d.stk.kind != stkEOF {
+ return false
+ }
+ d.pop()
+ return true
+}
+
+// Record that we are starting an element with the given name.
+func (d *Decoder) pushElement(name Name) {
+ s := d.push(stkStart)
+ s.name = name
+}
+
+// Record that we are changing the value of ns[local].
+// The old value is url, ok.
+func (d *Decoder) pushNs(local string, url string, ok bool) {
+ s := d.push(stkNs)
+ s.name.Local = local
+ s.name.Space = url
+ s.ok = ok
+}
+
+// Creates a SyntaxError with the current line number.
+func (d *Decoder) syntaxError(msg string) error {
+ return &SyntaxError{Msg: msg, Line: d.line}
+}
+
+// Record that we are ending an element with the given name.
+// The name must match the record at the top of the stack,
+// which must be a pushElement record.
+// After popping the element, apply any undo records from
+// the stack to restore the name translations that existed
+// before we saw this element.
+func (d *Decoder) popElement(t *EndElement) bool {
+ s := d.pop()
+ name := t.Name
+ switch {
+ case s == nil || s.kind != stkStart:
+ d.err = d.syntaxError("unexpected end element </" + name.Local + ">")
+ return false
+ case s.name.Local != name.Local:
+ if !d.Strict {
+ d.needClose = true
+ d.toClose = t.Name
+ t.Name = s.name
+ return true
+ }
+ d.err = d.syntaxError("element <" + s.name.Local + "> closed by </" + name.Local + ">")
+ return false
+ case s.name.Space != name.Space:
+ d.err = d.syntaxError("element <" + s.name.Local + "> in space " + s.name.Space +
+ "closed by </" + name.Local + "> in space " + name.Space)
+ return false
+ }
+
+ // Pop stack until a Start or EOF is on the top, undoing the
+ // translations that were associated with the element we just closed.
+ for d.stk != nil && d.stk.kind != stkStart && d.stk.kind != stkEOF {
+ s := d.pop()
+ if s.ok {
+ d.ns[s.name.Local] = s.name.Space
+ } else {
+ delete(d.ns, s.name.Local)
+ }
+ }
+
+ return true
+}
+
+// If the top element on the stack is autoclosing and
+// t is not the end tag, invent the end tag.
+func (d *Decoder) autoClose(t Token) (Token, bool) {
+ if d.stk == nil || d.stk.kind != stkStart {
+ return nil, false
+ }
+ name := strings.ToLower(d.stk.name.Local)
+ for _, s := range d.AutoClose {
+ if strings.ToLower(s) == name {
+ // This one should be auto closed if t doesn't close it.
+ et, ok := t.(EndElement)
+ if !ok || et.Name.Local != name {
+ return EndElement{d.stk.name}, true
+ }
+ break
+ }
+ }
+ return nil, false
+}
+
+var errRawToken = errors.New("xml: cannot use RawToken from UnmarshalXML method")
+
+// RawToken is like Token but does not verify that
+// start and end elements match and does not translate
+// name space prefixes to their corresponding URLs.
+func (d *Decoder) RawToken() (Token, error) {
+ if d.unmarshalDepth > 0 {
+ return nil, errRawToken
+ }
+ return d.rawToken()
+}
+
+func (d *Decoder) rawToken() (Token, error) {
+ if d.err != nil {
+ return nil, d.err
+ }
+ if d.needClose {
+ // The last element we read was self-closing and
+ // we returned just the StartElement half.
+ // Return the EndElement half now.
+ d.needClose = false
+ return EndElement{d.toClose}, nil
+ }
+
+ b, ok := d.getc()
+ if !ok {
+ return nil, d.err
+ }
+
+ if b != '<' {
+ // Text section.
+ d.ungetc(b)
+ data := d.text(-1, false)
+ if data == nil {
+ return nil, d.err
+ }
+ return CharData(data), nil
+ }
+
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ switch b {
+ case '/':
+ // </: End element
+ var name Name
+ if name, ok = d.nsname(); !ok {
+ if d.err == nil {
+ d.err = d.syntaxError("expected element name after </")
+ }
+ return nil, d.err
+ }
+ d.space()
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != '>' {
+ d.err = d.syntaxError("invalid characters between </" + name.Local + " and >")
+ return nil, d.err
+ }
+ return EndElement{name}, nil
+
+ case '?':
+ // <?: Processing instruction.
+ var target string
+ if target, ok = d.name(); !ok {
+ if d.err == nil {
+ d.err = d.syntaxError("expected target name after <?")
+ }
+ return nil, d.err
+ }
+ d.space()
+ d.buf.Reset()
+ var b0 byte
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ d.buf.WriteByte(b)
+ if b0 == '?' && b == '>' {
+ break
+ }
+ b0 = b
+ }
+ data := d.buf.Bytes()
+ data = data[0 : len(data)-2] // chop ?>
+
+ if target == "xml" {
+ content := string(data)
+ ver := procInst("version", content)
+ if ver != "" && ver != "1.0" {
+ d.err = fmt.Errorf("xml: unsupported version %q; only version 1.0 is supported", ver)
+ return nil, d.err
+ }
+ enc := procInst("encoding", content)
+ if enc != "" && enc != "utf-8" && enc != "UTF-8" {
+ if d.CharsetReader == nil {
+ d.err = fmt.Errorf("xml: encoding %q declared but Decoder.CharsetReader is nil", enc)
+ return nil, d.err
+ }
+ newr, err := d.CharsetReader(enc, d.r.(io.Reader))
+ if err != nil {
+ d.err = fmt.Errorf("xml: opening charset %q: %v", enc, err)
+ return nil, d.err
+ }
+ if newr == nil {
+ panic("CharsetReader returned a nil Reader for charset " + enc)
+ }
+ d.switchToReader(newr)
+ }
+ }
+ return ProcInst{target, data}, nil
+
+ case '!':
+ // <!: Maybe comment, maybe CDATA.
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ switch b {
+ case '-': // <!-
+ // Probably <!-- for a comment.
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != '-' {
+ d.err = d.syntaxError("invalid sequence <!- not part of <!--")
+ return nil, d.err
+ }
+ // Look for terminator.
+ d.buf.Reset()
+ var b0, b1 byte
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ d.buf.WriteByte(b)
+ if b0 == '-' && b1 == '-' && b == '>' {
+ break
+ }
+ b0, b1 = b1, b
+ }
+ data := d.buf.Bytes()
+ data = data[0 : len(data)-3] // chop -->
+ return Comment(data), nil
+
+ case '[': // <![
+ // Probably <![CDATA[.
+ for i := 0; i < 6; i++ {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != "CDATA["[i] {
+ d.err = d.syntaxError("invalid <![ sequence")
+ return nil, d.err
+ }
+ }
+ // Have <![CDATA[. Read text until ]]>.
+ data := d.text(-1, true)
+ if data == nil {
+ return nil, d.err
+ }
+ return CharData(data), nil
+ }
+
+ // Probably a directive: <!DOCTYPE ...>, <!ENTITY ...>, etc.
+ // We don't care, but accumulate for caller. Quoted angle
+ // brackets do not count for nesting.
+ d.buf.Reset()
+ d.buf.WriteByte(b)
+ inquote := uint8(0)
+ depth := 0
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if inquote == 0 && b == '>' && depth == 0 {
+ break
+ }
+ HandleB:
+ d.buf.WriteByte(b)
+ switch {
+ case b == inquote:
+ inquote = 0
+
+ case inquote != 0:
+ // in quotes, no special action
+
+ case b == '\'' || b == '"':
+ inquote = b
+
+ case b == '>' && inquote == 0:
+ depth--
+
+ case b == '<' && inquote == 0:
+ // Look for <!-- to begin comment.
+ s := "!--"
+ for i := 0; i < len(s); i++ {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != s[i] {
+ for j := 0; j < i; j++ {
+ d.buf.WriteByte(s[j])
+ }
+ depth++
+ goto HandleB
+ }
+ }
+
+ // Remove < that was written above.
+ d.buf.Truncate(d.buf.Len() - 1)
+
+ // Look for terminator.
+ var b0, b1 byte
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b0 == '-' && b1 == '-' && b == '>' {
+ break
+ }
+ b0, b1 = b1, b
+ }
+ }
+ }
+ return Directive(d.buf.Bytes()), nil
+ }
+
+ // Must be an open element like <a href="foo">
+ d.ungetc(b)
+
+ var (
+ name Name
+ empty bool
+ attr []Attr
+ )
+ if name, ok = d.nsname(); !ok {
+ if d.err == nil {
+ d.err = d.syntaxError("expected element name after <")
+ }
+ return nil, d.err
+ }
+
+ attr = []Attr{}
+ for {
+ d.space()
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b == '/' {
+ empty = true
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != '>' {
+ d.err = d.syntaxError("expected /> in element")
+ return nil, d.err
+ }
+ break
+ }
+ if b == '>' {
+ break
+ }
+ d.ungetc(b)
+
+ n := len(attr)
+ if n >= cap(attr) {
+ nCap := 2 * cap(attr)
+ if nCap == 0 {
+ nCap = 4
+ }
+ nattr := make([]Attr, n, nCap)
+ copy(nattr, attr)
+ attr = nattr
+ }
+ attr = attr[0 : n+1]
+ a := &attr[n]
+ if a.Name, ok = d.nsname(); !ok {
+ if d.err == nil {
+ d.err = d.syntaxError("expected attribute name in element")
+ }
+ return nil, d.err
+ }
+ d.space()
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != '=' {
+ if d.Strict {
+ d.err = d.syntaxError("attribute name without = in element")
+ return nil, d.err
+ } else {
+ d.ungetc(b)
+ a.Value = a.Name.Local
+ }
+ } else {
+ d.space()
+ data := d.attrval()
+ if data == nil {
+ return nil, d.err
+ }
+ a.Value = string(data)
+ }
+ }
+ if empty {
+ d.needClose = true
+ d.toClose = name
+ }
+ return StartElement{name, attr}, nil
+}
+
+func (d *Decoder) attrval() []byte {
+ b, ok := d.mustgetc()
+ if !ok {
+ return nil
+ }
+ // Handle quoted attribute values
+ if b == '"' || b == '\'' {
+ return d.text(int(b), false)
+ }
+ // Handle unquoted attribute values for strict parsers
+ if d.Strict {
+ d.err = d.syntaxError("unquoted or missing attribute value in element")
+ return nil
+ }
+ // Handle unquoted attribute values for unstrict parsers
+ d.ungetc(b)
+ d.buf.Reset()
+ for {
+ b, ok = d.mustgetc()
+ if !ok {
+ return nil
+ }
+ // http://www.w3.org/TR/REC-html40/intro/sgmltut.html#h-3.2.2
+ if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' ||
+ '0' <= b && b <= '9' || b == '_' || b == ':' || b == '-' {
+ d.buf.WriteByte(b)
+ } else {
+ d.ungetc(b)
+ break
+ }
+ }
+ return d.buf.Bytes()
+}
+
+// Skip spaces if any
+func (d *Decoder) space() {
+ for {
+ b, ok := d.getc()
+ if !ok {
+ return
+ }
+ switch b {
+ case ' ', '\r', '\n', '\t':
+ default:
+ d.ungetc(b)
+ return
+ }
+ }
+}
+
+// Read a single byte.
+// If there is no byte to read, return ok==false
+// and leave the error in d.err.
+// Maintain line number.
+func (d *Decoder) getc() (b byte, ok bool) {
+ if d.err != nil {
+ return 0, false
+ }
+ if d.nextByte >= 0 {
+ b = byte(d.nextByte)
+ d.nextByte = -1
+ } else {
+ b, d.err = d.r.ReadByte()
+ if d.err != nil {
+ return 0, false
+ }
+ if d.saved != nil {
+ d.saved.WriteByte(b)
+ }
+ }
+ if b == '\n' {
+ d.line++
+ }
+ d.offset++
+ return b, true
+}
+
+// InputOffset returns the input stream byte offset of the current decoder position.
+// The offset gives the location of the end of the most recently returned token
+// and the beginning of the next token.
+func (d *Decoder) InputOffset() int64 {
+ return d.offset
+}
+
+// Return saved offset.
+// If we did ungetc (nextByte >= 0), have to back up one.
+func (d *Decoder) savedOffset() int {
+ n := d.saved.Len()
+ if d.nextByte >= 0 {
+ n--
+ }
+ return n
+}
+
+// Must read a single byte.
+// If there is no byte to read,
+// set d.err to SyntaxError("unexpected EOF")
+// and return ok==false
+func (d *Decoder) mustgetc() (b byte, ok bool) {
+ if b, ok = d.getc(); !ok {
+ if d.err == io.EOF {
+ d.err = d.syntaxError("unexpected EOF")
+ }
+ }
+ return
+}
+
+// Unread a single byte.
+func (d *Decoder) ungetc(b byte) {
+ if b == '\n' {
+ d.line--
+ }
+ d.nextByte = int(b)
+ d.offset--
+}
+
+var entity = map[string]int{
+ "lt": '<',
+ "gt": '>',
+ "amp": '&',
+ "apos": '\'',
+ "quot": '"',
+}
+
+// Read plain text section (XML calls it character data).
+// If quote >= 0, we are in a quoted string and need to find the matching quote.
+// If cdata == true, we are in a <![CDATA[ section and need to find ]]>.
+// On failure return nil and leave the error in d.err.
+func (d *Decoder) text(quote int, cdata bool) []byte {
+ var b0, b1 byte
+ var trunc int
+ d.buf.Reset()
+Input:
+ for {
+ b, ok := d.getc()
+ if !ok {
+ if cdata {
+ if d.err == io.EOF {
+ d.err = d.syntaxError("unexpected EOF in CDATA section")
+ }
+ return nil
+ }
+ break Input
+ }
+
+ // <![CDATA[ section ends with ]]>.
+ // It is an error for ]]> to appear in ordinary text.
+ if b0 == ']' && b1 == ']' && b == '>' {
+ if cdata {
+ trunc = 2
+ break Input
+ }
+ d.err = d.syntaxError("unescaped ]]> not in CDATA section")
+ return nil
+ }
+
+ // Stop reading text if we see a <.
+ if b == '<' && !cdata {
+ if quote >= 0 {
+ d.err = d.syntaxError("unescaped < inside quoted string")
+ return nil
+ }
+ d.ungetc('<')
+ break Input
+ }
+ if quote >= 0 && b == byte(quote) {
+ break Input
+ }
+ if b == '&' && !cdata {
+ // Read escaped character expression up to semicolon.
+ // XML in all its glory allows a document to define and use
+ // its own character names with <!ENTITY ...> directives.
+ // Parsers are required to recognize lt, gt, amp, apos, and quot
+ // even if they have not been declared.
+ before := d.buf.Len()
+ d.buf.WriteByte('&')
+ var ok bool
+ var text string
+ var haveText bool
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ if b == '#' {
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ base := 10
+ if b == 'x' {
+ base = 16
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ }
+ start := d.buf.Len()
+ for '0' <= b && b <= '9' ||
+ base == 16 && 'a' <= b && b <= 'f' ||
+ base == 16 && 'A' <= b && b <= 'F' {
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ }
+ if b != ';' {
+ d.ungetc(b)
+ } else {
+ s := string(d.buf.Bytes()[start:])
+ d.buf.WriteByte(';')
+ n, err := strconv.ParseUint(s, base, 64)
+ if err == nil && n <= unicode.MaxRune {
+ text = string(n)
+ haveText = true
+ }
+ }
+ } else {
+ d.ungetc(b)
+ if !d.readName() {
+ if d.err != nil {
+ return nil
+ }
+ ok = false
+ }
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ if b != ';' {
+ d.ungetc(b)
+ } else {
+ name := d.buf.Bytes()[before+1:]
+ d.buf.WriteByte(';')
+ if isName(name) {
+ s := string(name)
+ if r, ok := entity[s]; ok {
+ text = string(r)
+ haveText = true
+ } else if d.Entity != nil {
+ text, haveText = d.Entity[s]
+ }
+ }
+ }
+ }
+
+ if haveText {
+ d.buf.Truncate(before)
+ d.buf.Write([]byte(text))
+ b0, b1 = 0, 0
+ continue Input
+ }
+ if !d.Strict {
+ b0, b1 = 0, 0
+ continue Input
+ }
+ ent := string(d.buf.Bytes()[before:])
+ if ent[len(ent)-1] != ';' {
+ ent += " (no semicolon)"
+ }
+ d.err = d.syntaxError("invalid character entity " + ent)
+ return nil
+ }
+
+ // We must rewrite unescaped \r and \r\n into \n.
+ if b == '\r' {
+ d.buf.WriteByte('\n')
+ } else if b1 == '\r' && b == '\n' {
+ // Skip \r\n--we already wrote \n.
+ } else {
+ d.buf.WriteByte(b)
+ }
+
+ b0, b1 = b1, b
+ }
+ data := d.buf.Bytes()
+ data = data[0 : len(data)-trunc]
+
+ // Inspect each rune for being a disallowed character.
+ buf := data
+ for len(buf) > 0 {
+ r, size := utf8.DecodeRune(buf)
+ if r == utf8.RuneError && size == 1 {
+ d.err = d.syntaxError("invalid UTF-8")
+ return nil
+ }
+ buf = buf[size:]
+ if !isInCharacterRange(r) {
+ d.err = d.syntaxError(fmt.Sprintf("illegal character code %U", r))
+ return nil
+ }
+ }
+
+ return data
+}
+
+// Decide whether the given rune is in the XML Character Range, per
+// the Char production of http://www.xml.com/axml/testaxml.htm,
+// Section 2.2 Characters.
+func isInCharacterRange(r rune) (inrange bool) {
+ return r == 0x09 ||
+ r == 0x0A ||
+ r == 0x0D ||
+ r >= 0x20 && r <= 0xDF77 ||
+ r >= 0xE000 && r <= 0xFFFD ||
+ r >= 0x10000 && r <= 0x10FFFF
+}
+
+// Get name space name: name with a : stuck in the middle.
+// The part before the : is the name space identifier.
+func (d *Decoder) nsname() (name Name, ok bool) {
+ s, ok := d.name()
+ if !ok {
+ return
+ }
+ i := strings.Index(s, ":")
+ if i < 0 {
+ name.Local = s
+ } else {
+ name.Space = s[0:i]
+ name.Local = s[i+1:]
+ }
+ return name, true
+}
+
+// Get name: /first(first|second)*/
+// Do not set d.err if the name is missing (unless unexpected EOF is received):
+// let the caller provide better context.
+func (d *Decoder) name() (s string, ok bool) {
+ d.buf.Reset()
+ if !d.readName() {
+ return "", false
+ }
+
+ // Now we check the characters.
+ b := d.buf.Bytes()
+ if !isName(b) {
+ d.err = d.syntaxError("invalid XML name: " + string(b))
+ return "", false
+ }
+ return string(b), true
+}
+
+// Read a name and append its bytes to d.buf.
+// The name is delimited by any single-byte character not valid in names.
+// All multi-byte characters are accepted; the caller must check their validity.
+func (d *Decoder) readName() (ok bool) {
+ var b byte
+ if b, ok = d.mustgetc(); !ok {
+ return
+ }
+ if b < utf8.RuneSelf && !isNameByte(b) {
+ d.ungetc(b)
+ return false
+ }
+ d.buf.WriteByte(b)
+
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return
+ }
+ if b < utf8.RuneSelf && !isNameByte(b) {
+ d.ungetc(b)
+ break
+ }
+ d.buf.WriteByte(b)
+ }
+ return true
+}
+
+func isNameByte(c byte) bool {
+ return 'A' <= c && c <= 'Z' ||
+ 'a' <= c && c <= 'z' ||
+ '0' <= c && c <= '9' ||
+ c == '_' || c == ':' || c == '.' || c == '-'
+}
+
+func isName(s []byte) bool {
+ if len(s) == 0 {
+ return false
+ }
+ c, n := utf8.DecodeRune(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) {
+ return false
+ }
+ for n < len(s) {
+ s = s[n:]
+ c, n = utf8.DecodeRune(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) && !unicode.Is(second, c) {
+ return false
+ }
+ }
+ return true
+}
+
+func isNameString(s string) bool {
+ if len(s) == 0 {
+ return false
+ }
+ c, n := utf8.DecodeRuneInString(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) {
+ return false
+ }
+ for n < len(s) {
+ s = s[n:]
+ c, n = utf8.DecodeRuneInString(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) && !unicode.Is(second, c) {
+ return false
+ }
+ }
+ return true
+}
+
+// These tables were generated by cut and paste from Appendix B of
+// the XML spec at http://www.xml.com/axml/testaxml.htm
+// and then reformatting. First corresponds to (Letter | '_' | ':')
+// and second corresponds to NameChar.
+
+var first = &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {0x003A, 0x003A, 1},
+ {0x0041, 0x005A, 1},
+ {0x005F, 0x005F, 1},
+ {0x0061, 0x007A, 1},
+ {0x00C0, 0x00D6, 1},
+ {0x00D8, 0x00F6, 1},
+ {0x00F8, 0x00FF, 1},
+ {0x0100, 0x0131, 1},
+ {0x0134, 0x013E, 1},
+ {0x0141, 0x0148, 1},
+ {0x014A, 0x017E, 1},
+ {0x0180, 0x01C3, 1},
+ {0x01CD, 0x01F0, 1},
+ {0x01F4, 0x01F5, 1},
+ {0x01FA, 0x0217, 1},
+ {0x0250, 0x02A8, 1},
+ {0x02BB, 0x02C1, 1},
+ {0x0386, 0x0386, 1},
+ {0x0388, 0x038A, 1},
+ {0x038C, 0x038C, 1},
+ {0x038E, 0x03A1, 1},
+ {0x03A3, 0x03CE, 1},
+ {0x03D0, 0x03D6, 1},
+ {0x03DA, 0x03E0, 2},
+ {0x03E2, 0x03F3, 1},
+ {0x0401, 0x040C, 1},
+ {0x040E, 0x044F, 1},
+ {0x0451, 0x045C, 1},
+ {0x045E, 0x0481, 1},
+ {0x0490, 0x04C4, 1},
+ {0x04C7, 0x04C8, 1},
+ {0x04CB, 0x04CC, 1},
+ {0x04D0, 0x04EB, 1},
+ {0x04EE, 0x04F5, 1},
+ {0x04F8, 0x04F9, 1},
+ {0x0531, 0x0556, 1},
+ {0x0559, 0x0559, 1},
+ {0x0561, 0x0586, 1},
+ {0x05D0, 0x05EA, 1},
+ {0x05F0, 0x05F2, 1},
+ {0x0621, 0x063A, 1},
+ {0x0641, 0x064A, 1},
+ {0x0671, 0x06B7, 1},
+ {0x06BA, 0x06BE, 1},
+ {0x06C0, 0x06CE, 1},
+ {0x06D0, 0x06D3, 1},
+ {0x06D5, 0x06D5, 1},
+ {0x06E5, 0x06E6, 1},
+ {0x0905, 0x0939, 1},
+ {0x093D, 0x093D, 1},
+ {0x0958, 0x0961, 1},
+ {0x0985, 0x098C, 1},
+ {0x098F, 0x0990, 1},
+ {0x0993, 0x09A8, 1},
+ {0x09AA, 0x09B0, 1},
+ {0x09B2, 0x09B2, 1},
+ {0x09B6, 0x09B9, 1},
+ {0x09DC, 0x09DD, 1},
+ {0x09DF, 0x09E1, 1},
+ {0x09F0, 0x09F1, 1},
+ {0x0A05, 0x0A0A, 1},
+ {0x0A0F, 0x0A10, 1},
+ {0x0A13, 0x0A28, 1},
+ {0x0A2A, 0x0A30, 1},
+ {0x0A32, 0x0A33, 1},
+ {0x0A35, 0x0A36, 1},
+ {0x0A38, 0x0A39, 1},
+ {0x0A59, 0x0A5C, 1},
+ {0x0A5E, 0x0A5E, 1},
+ {0x0A72, 0x0A74, 1},
+ {0x0A85, 0x0A8B, 1},
+ {0x0A8D, 0x0A8D, 1},
+ {0x0A8F, 0x0A91, 1},
+ {0x0A93, 0x0AA8, 1},
+ {0x0AAA, 0x0AB0, 1},
+ {0x0AB2, 0x0AB3, 1},
+ {0x0AB5, 0x0AB9, 1},
+ {0x0ABD, 0x0AE0, 0x23},
+ {0x0B05, 0x0B0C, 1},
+ {0x0B0F, 0x0B10, 1},
+ {0x0B13, 0x0B28, 1},
+ {0x0B2A, 0x0B30, 1},
+ {0x0B32, 0x0B33, 1},
+ {0x0B36, 0x0B39, 1},
+ {0x0B3D, 0x0B3D, 1},
+ {0x0B5C, 0x0B5D, 1},
+ {0x0B5F, 0x0B61, 1},
+ {0x0B85, 0x0B8A, 1},
+ {0x0B8E, 0x0B90, 1},
+ {0x0B92, 0x0B95, 1},
+ {0x0B99, 0x0B9A, 1},
+ {0x0B9C, 0x0B9C, 1},
+ {0x0B9E, 0x0B9F, 1},
+ {0x0BA3, 0x0BA4, 1},
+ {0x0BA8, 0x0BAA, 1},
+ {0x0BAE, 0x0BB5, 1},
+ {0x0BB7, 0x0BB9, 1},
+ {0x0C05, 0x0C0C, 1},
+ {0x0C0E, 0x0C10, 1},
+ {0x0C12, 0x0C28, 1},
+ {0x0C2A, 0x0C33, 1},
+ {0x0C35, 0x0C39, 1},
+ {0x0C60, 0x0C61, 1},
+ {0x0C85, 0x0C8C, 1},
+ {0x0C8E, 0x0C90, 1},
+ {0x0C92, 0x0CA8, 1},
+ {0x0CAA, 0x0CB3, 1},
+ {0x0CB5, 0x0CB9, 1},
+ {0x0CDE, 0x0CDE, 1},
+ {0x0CE0, 0x0CE1, 1},
+ {0x0D05, 0x0D0C, 1},
+ {0x0D0E, 0x0D10, 1},
+ {0x0D12, 0x0D28, 1},
+ {0x0D2A, 0x0D39, 1},
+ {0x0D60, 0x0D61, 1},
+ {0x0E01, 0x0E2E, 1},
+ {0x0E30, 0x0E30, 1},
+ {0x0E32, 0x0E33, 1},
+ {0x0E40, 0x0E45, 1},
+ {0x0E81, 0x0E82, 1},
+ {0x0E84, 0x0E84, 1},
+ {0x0E87, 0x0E88, 1},
+ {0x0E8A, 0x0E8D, 3},
+ {0x0E94, 0x0E97, 1},
+ {0x0E99, 0x0E9F, 1},
+ {0x0EA1, 0x0EA3, 1},
+ {0x0EA5, 0x0EA7, 2},
+ {0x0EAA, 0x0EAB, 1},
+ {0x0EAD, 0x0EAE, 1},
+ {0x0EB0, 0x0EB0, 1},
+ {0x0EB2, 0x0EB3, 1},
+ {0x0EBD, 0x0EBD, 1},
+ {0x0EC0, 0x0EC4, 1},
+ {0x0F40, 0x0F47, 1},
+ {0x0F49, 0x0F69, 1},
+ {0x10A0, 0x10C5, 1},
+ {0x10D0, 0x10F6, 1},
+ {0x1100, 0x1100, 1},
+ {0x1102, 0x1103, 1},
+ {0x1105, 0x1107, 1},
+ {0x1109, 0x1109, 1},
+ {0x110B, 0x110C, 1},
+ {0x110E, 0x1112, 1},
+ {0x113C, 0x1140, 2},
+ {0x114C, 0x1150, 2},
+ {0x1154, 0x1155, 1},
+ {0x1159, 0x1159, 1},
+ {0x115F, 0x1161, 1},
+ {0x1163, 0x1169, 2},
+ {0x116D, 0x116E, 1},
+ {0x1172, 0x1173, 1},
+ {0x1175, 0x119E, 0x119E - 0x1175},
+ {0x11A8, 0x11AB, 0x11AB - 0x11A8},
+ {0x11AE, 0x11AF, 1},
+ {0x11B7, 0x11B8, 1},
+ {0x11BA, 0x11BA, 1},
+ {0x11BC, 0x11C2, 1},
+ {0x11EB, 0x11F0, 0x11F0 - 0x11EB},
+ {0x11F9, 0x11F9, 1},
+ {0x1E00, 0x1E9B, 1},
+ {0x1EA0, 0x1EF9, 1},
+ {0x1F00, 0x1F15, 1},
+ {0x1F18, 0x1F1D, 1},
+ {0x1F20, 0x1F45, 1},
+ {0x1F48, 0x1F4D, 1},
+ {0x1F50, 0x1F57, 1},
+ {0x1F59, 0x1F5B, 0x1F5B - 0x1F59},
+ {0x1F5D, 0x1F5D, 1},
+ {0x1F5F, 0x1F7D, 1},
+ {0x1F80, 0x1FB4, 1},
+ {0x1FB6, 0x1FBC, 1},
+ {0x1FBE, 0x1FBE, 1},
+ {0x1FC2, 0x1FC4, 1},
+ {0x1FC6, 0x1FCC, 1},
+ {0x1FD0, 0x1FD3, 1},
+ {0x1FD6, 0x1FDB, 1},
+ {0x1FE0, 0x1FEC, 1},
+ {0x1FF2, 0x1FF4, 1},
+ {0x1FF6, 0x1FFC, 1},
+ {0x2126, 0x2126, 1},
+ {0x212A, 0x212B, 1},
+ {0x212E, 0x212E, 1},
+ {0x2180, 0x2182, 1},
+ {0x3007, 0x3007, 1},
+ {0x3021, 0x3029, 1},
+ {0x3041, 0x3094, 1},
+ {0x30A1, 0x30FA, 1},
+ {0x3105, 0x312C, 1},
+ {0x4E00, 0x9FA5, 1},
+ {0xAC00, 0xD7A3, 1},
+ },
+}
+
+var second = &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {0x002D, 0x002E, 1},
+ {0x0030, 0x0039, 1},
+ {0x00B7, 0x00B7, 1},
+ {0x02D0, 0x02D1, 1},
+ {0x0300, 0x0345, 1},
+ {0x0360, 0x0361, 1},
+ {0x0387, 0x0387, 1},
+ {0x0483, 0x0486, 1},
+ {0x0591, 0x05A1, 1},
+ {0x05A3, 0x05B9, 1},
+ {0x05BB, 0x05BD, 1},
+ {0x05BF, 0x05BF, 1},
+ {0x05C1, 0x05C2, 1},
+ {0x05C4, 0x0640, 0x0640 - 0x05C4},
+ {0x064B, 0x0652, 1},
+ {0x0660, 0x0669, 1},
+ {0x0670, 0x0670, 1},
+ {0x06D6, 0x06DC, 1},
+ {0x06DD, 0x06DF, 1},
+ {0x06E0, 0x06E4, 1},
+ {0x06E7, 0x06E8, 1},
+ {0x06EA, 0x06ED, 1},
+ {0x06F0, 0x06F9, 1},
+ {0x0901, 0x0903, 1},
+ {0x093C, 0x093C, 1},
+ {0x093E, 0x094C, 1},
+ {0x094D, 0x094D, 1},
+ {0x0951, 0x0954, 1},
+ {0x0962, 0x0963, 1},
+ {0x0966, 0x096F, 1},
+ {0x0981, 0x0983, 1},
+ {0x09BC, 0x09BC, 1},
+ {0x09BE, 0x09BF, 1},
+ {0x09C0, 0x09C4, 1},
+ {0x09C7, 0x09C8, 1},
+ {0x09CB, 0x09CD, 1},
+ {0x09D7, 0x09D7, 1},
+ {0x09E2, 0x09E3, 1},
+ {0x09E6, 0x09EF, 1},
+ {0x0A02, 0x0A3C, 0x3A},
+ {0x0A3E, 0x0A3F, 1},
+ {0x0A40, 0x0A42, 1},
+ {0x0A47, 0x0A48, 1},
+ {0x0A4B, 0x0A4D, 1},
+ {0x0A66, 0x0A6F, 1},
+ {0x0A70, 0x0A71, 1},
+ {0x0A81, 0x0A83, 1},
+ {0x0ABC, 0x0ABC, 1},
+ {0x0ABE, 0x0AC5, 1},
+ {0x0AC7, 0x0AC9, 1},
+ {0x0ACB, 0x0ACD, 1},
+ {0x0AE6, 0x0AEF, 1},
+ {0x0B01, 0x0B03, 1},
+ {0x0B3C, 0x0B3C, 1},
+ {0x0B3E, 0x0B43, 1},
+ {0x0B47, 0x0B48, 1},
+ {0x0B4B, 0x0B4D, 1},
+ {0x0B56, 0x0B57, 1},
+ {0x0B66, 0x0B6F, 1},
+ {0x0B82, 0x0B83, 1},
+ {0x0BBE, 0x0BC2, 1},
+ {0x0BC6, 0x0BC8, 1},
+ {0x0BCA, 0x0BCD, 1},
+ {0x0BD7, 0x0BD7, 1},
+ {0x0BE7, 0x0BEF, 1},
+ {0x0C01, 0x0C03, 1},
+ {0x0C3E, 0x0C44, 1},
+ {0x0C46, 0x0C48, 1},
+ {0x0C4A, 0x0C4D, 1},
+ {0x0C55, 0x0C56, 1},
+ {0x0C66, 0x0C6F, 1},
+ {0x0C82, 0x0C83, 1},
+ {0x0CBE, 0x0CC4, 1},
+ {0x0CC6, 0x0CC8, 1},
+ {0x0CCA, 0x0CCD, 1},
+ {0x0CD5, 0x0CD6, 1},
+ {0x0CE6, 0x0CEF, 1},
+ {0x0D02, 0x0D03, 1},
+ {0x0D3E, 0x0D43, 1},
+ {0x0D46, 0x0D48, 1},
+ {0x0D4A, 0x0D4D, 1},
+ {0x0D57, 0x0D57, 1},
+ {0x0D66, 0x0D6F, 1},
+ {0x0E31, 0x0E31, 1},
+ {0x0E34, 0x0E3A, 1},
+ {0x0E46, 0x0E46, 1},
+ {0x0E47, 0x0E4E, 1},
+ {0x0E50, 0x0E59, 1},
+ {0x0EB1, 0x0EB1, 1},
+ {0x0EB4, 0x0EB9, 1},
+ {0x0EBB, 0x0EBC, 1},
+ {0x0EC6, 0x0EC6, 1},
+ {0x0EC8, 0x0ECD, 1},
+ {0x0ED0, 0x0ED9, 1},
+ {0x0F18, 0x0F19, 1},
+ {0x0F20, 0x0F29, 1},
+ {0x0F35, 0x0F39, 2},
+ {0x0F3E, 0x0F3F, 1},
+ {0x0F71, 0x0F84, 1},
+ {0x0F86, 0x0F8B, 1},
+ {0x0F90, 0x0F95, 1},
+ {0x0F97, 0x0F97, 1},
+ {0x0F99, 0x0FAD, 1},
+ {0x0FB1, 0x0FB7, 1},
+ {0x0FB9, 0x0FB9, 1},
+ {0x20D0, 0x20DC, 1},
+ {0x20E1, 0x3005, 0x3005 - 0x20E1},
+ {0x302A, 0x302F, 1},
+ {0x3031, 0x3035, 1},
+ {0x3099, 0x309A, 1},
+ {0x309D, 0x309E, 1},
+ {0x30FC, 0x30FE, 1},
+ },
+}
+
+// HTMLEntity is an entity map containing translations for the
+// standard HTML entity characters.
+var HTMLEntity = htmlEntity
+
+var htmlEntity = map[string]string{
+ /*
+ hget http://www.w3.org/TR/html4/sgml/entities.html |
+ ssam '
+ ,y /\>/ x/\<(.|\n)+/ s/\n/ /g
+ ,x v/^\<!ENTITY/d
+ ,s/\<!ENTITY ([^ ]+) .*U\+([0-9A-F][0-9A-F][0-9A-F][0-9A-F]) .+/ "\1": "\\u\2",/g
+ '
+ */
+ "nbsp": "\u00A0",
+ "iexcl": "\u00A1",
+ "cent": "\u00A2",
+ "pound": "\u00A3",
+ "curren": "\u00A4",
+ "yen": "\u00A5",
+ "brvbar": "\u00A6",
+ "sect": "\u00A7",
+ "uml": "\u00A8",
+ "copy": "\u00A9",
+ "ordf": "\u00AA",
+ "laquo": "\u00AB",
+ "not": "\u00AC",
+ "shy": "\u00AD",
+ "reg": "\u00AE",
+ "macr": "\u00AF",
+ "deg": "\u00B0",
+ "plusmn": "\u00B1",
+ "sup2": "\u00B2",
+ "sup3": "\u00B3",
+ "acute": "\u00B4",
+ "micro": "\u00B5",
+ "para": "\u00B6",
+ "middot": "\u00B7",
+ "cedil": "\u00B8",
+ "sup1": "\u00B9",
+ "ordm": "\u00BA",
+ "raquo": "\u00BB",
+ "frac14": "\u00BC",
+ "frac12": "\u00BD",
+ "frac34": "\u00BE",
+ "iquest": "\u00BF",
+ "Agrave": "\u00C0",
+ "Aacute": "\u00C1",
+ "Acirc": "\u00C2",
+ "Atilde": "\u00C3",
+ "Auml": "\u00C4",
+ "Aring": "\u00C5",
+ "AElig": "\u00C6",
+ "Ccedil": "\u00C7",
+ "Egrave": "\u00C8",
+ "Eacute": "\u00C9",
+ "Ecirc": "\u00CA",
+ "Euml": "\u00CB",
+ "Igrave": "\u00CC",
+ "Iacute": "\u00CD",
+ "Icirc": "\u00CE",
+ "Iuml": "\u00CF",
+ "ETH": "\u00D0",
+ "Ntilde": "\u00D1",
+ "Ograve": "\u00D2",
+ "Oacute": "\u00D3",
+ "Ocirc": "\u00D4",
+ "Otilde": "\u00D5",
+ "Ouml": "\u00D6",
+ "times": "\u00D7",
+ "Oslash": "\u00D8",
+ "Ugrave": "\u00D9",
+ "Uacute": "\u00DA",
+ "Ucirc": "\u00DB",
+ "Uuml": "\u00DC",
+ "Yacute": "\u00DD",
+ "THORN": "\u00DE",
+ "szlig": "\u00DF",
+ "agrave": "\u00E0",
+ "aacute": "\u00E1",
+ "acirc": "\u00E2",
+ "atilde": "\u00E3",
+ "auml": "\u00E4",
+ "aring": "\u00E5",
+ "aelig": "\u00E6",
+ "ccedil": "\u00E7",
+ "egrave": "\u00E8",
+ "eacute": "\u00E9",
+ "ecirc": "\u00EA",
+ "euml": "\u00EB",
+ "igrave": "\u00EC",
+ "iacute": "\u00ED",
+ "icirc": "\u00EE",
+ "iuml": "\u00EF",
+ "eth": "\u00F0",
+ "ntilde": "\u00F1",
+ "ograve": "\u00F2",
+ "oacute": "\u00F3",
+ "ocirc": "\u00F4",
+ "otilde": "\u00F5",
+ "ouml": "\u00F6",
+ "divide": "\u00F7",
+ "oslash": "\u00F8",
+ "ugrave": "\u00F9",
+ "uacute": "\u00FA",
+ "ucirc": "\u00FB",
+ "uuml": "\u00FC",
+ "yacute": "\u00FD",
+ "thorn": "\u00FE",
+ "yuml": "\u00FF",
+ "fnof": "\u0192",
+ "Alpha": "\u0391",
+ "Beta": "\u0392",
+ "Gamma": "\u0393",
+ "Delta": "\u0394",
+ "Epsilon": "\u0395",
+ "Zeta": "\u0396",
+ "Eta": "\u0397",
+ "Theta": "\u0398",
+ "Iota": "\u0399",
+ "Kappa": "\u039A",
+ "Lambda": "\u039B",
+ "Mu": "\u039C",
+ "Nu": "\u039D",
+ "Xi": "\u039E",
+ "Omicron": "\u039F",
+ "Pi": "\u03A0",
+ "Rho": "\u03A1",
+ "Sigma": "\u03A3",
+ "Tau": "\u03A4",
+ "Upsilon": "\u03A5",
+ "Phi": "\u03A6",
+ "Chi": "\u03A7",
+ "Psi": "\u03A8",
+ "Omega": "\u03A9",
+ "alpha": "\u03B1",
+ "beta": "\u03B2",
+ "gamma": "\u03B3",
+ "delta": "\u03B4",
+ "epsilon": "\u03B5",
+ "zeta": "\u03B6",
+ "eta": "\u03B7",
+ "theta": "\u03B8",
+ "iota": "\u03B9",
+ "kappa": "\u03BA",
+ "lambda": "\u03BB",
+ "mu": "\u03BC",
+ "nu": "\u03BD",
+ "xi": "\u03BE",
+ "omicron": "\u03BF",
+ "pi": "\u03C0",
+ "rho": "\u03C1",
+ "sigmaf": "\u03C2",
+ "sigma": "\u03C3",
+ "tau": "\u03C4",
+ "upsilon": "\u03C5",
+ "phi": "\u03C6",
+ "chi": "\u03C7",
+ "psi": "\u03C8",
+ "omega": "\u03C9",
+ "thetasym": "\u03D1",
+ "upsih": "\u03D2",
+ "piv": "\u03D6",
+ "bull": "\u2022",
+ "hellip": "\u2026",
+ "prime": "\u2032",
+ "Prime": "\u2033",
+ "oline": "\u203E",
+ "frasl": "\u2044",
+ "weierp": "\u2118",
+ "image": "\u2111",
+ "real": "\u211C",
+ "trade": "\u2122",
+ "alefsym": "\u2135",
+ "larr": "\u2190",
+ "uarr": "\u2191",
+ "rarr": "\u2192",
+ "darr": "\u2193",
+ "harr": "\u2194",
+ "crarr": "\u21B5",
+ "lArr": "\u21D0",
+ "uArr": "\u21D1",
+ "rArr": "\u21D2",
+ "dArr": "\u21D3",
+ "hArr": "\u21D4",
+ "forall": "\u2200",
+ "part": "\u2202",
+ "exist": "\u2203",
+ "empty": "\u2205",
+ "nabla": "\u2207",
+ "isin": "\u2208",
+ "notin": "\u2209",
+ "ni": "\u220B",
+ "prod": "\u220F",
+ "sum": "\u2211",
+ "minus": "\u2212",
+ "lowast": "\u2217",
+ "radic": "\u221A",
+ "prop": "\u221D",
+ "infin": "\u221E",
+ "ang": "\u2220",
+ "and": "\u2227",
+ "or": "\u2228",
+ "cap": "\u2229",
+ "cup": "\u222A",
+ "int": "\u222B",
+ "there4": "\u2234",
+ "sim": "\u223C",
+ "cong": "\u2245",
+ "asymp": "\u2248",
+ "ne": "\u2260",
+ "equiv": "\u2261",
+ "le": "\u2264",
+ "ge": "\u2265",
+ "sub": "\u2282",
+ "sup": "\u2283",
+ "nsub": "\u2284",
+ "sube": "\u2286",
+ "supe": "\u2287",
+ "oplus": "\u2295",
+ "otimes": "\u2297",
+ "perp": "\u22A5",
+ "sdot": "\u22C5",
+ "lceil": "\u2308",
+ "rceil": "\u2309",
+ "lfloor": "\u230A",
+ "rfloor": "\u230B",
+ "lang": "\u2329",
+ "rang": "\u232A",
+ "loz": "\u25CA",
+ "spades": "\u2660",
+ "clubs": "\u2663",
+ "hearts": "\u2665",
+ "diams": "\u2666",
+ "quot": "\u0022",
+ "amp": "\u0026",
+ "lt": "\u003C",
+ "gt": "\u003E",
+ "OElig": "\u0152",
+ "oelig": "\u0153",
+ "Scaron": "\u0160",
+ "scaron": "\u0161",
+ "Yuml": "\u0178",
+ "circ": "\u02C6",
+ "tilde": "\u02DC",
+ "ensp": "\u2002",
+ "emsp": "\u2003",
+ "thinsp": "\u2009",
+ "zwnj": "\u200C",
+ "zwj": "\u200D",
+ "lrm": "\u200E",
+ "rlm": "\u200F",
+ "ndash": "\u2013",
+ "mdash": "\u2014",
+ "lsquo": "\u2018",
+ "rsquo": "\u2019",
+ "sbquo": "\u201A",
+ "ldquo": "\u201C",
+ "rdquo": "\u201D",
+ "bdquo": "\u201E",
+ "dagger": "\u2020",
+ "Dagger": "\u2021",
+ "permil": "\u2030",
+ "lsaquo": "\u2039",
+ "rsaquo": "\u203A",
+ "euro": "\u20AC",
+}
+
+// HTMLAutoClose is the set of HTML elements that
+// should be considered to close automatically.
+var HTMLAutoClose = htmlAutoClose
+
+var htmlAutoClose = []string{
+ /*
+ hget http://www.w3.org/TR/html4/loose.dtd |
+ 9 sed -n 's/<!ELEMENT ([^ ]*) +- O EMPTY.+/ "\1",/p' | tr A-Z a-z
+ */
+ "basefont",
+ "br",
+ "area",
+ "link",
+ "img",
+ "param",
+ "hr",
+ "input",
+ "col",
+ "frame",
+ "isindex",
+ "base",
+ "meta",
+}
+
+var (
+ esc_quot = []byte(""") // shorter than """
+ esc_apos = []byte("'") // shorter than "'"
+ esc_amp = []byte("&")
+ esc_lt = []byte("<")
+ esc_gt = []byte(">")
+ esc_tab = []byte("	")
+ esc_nl = []byte("
")
+ esc_cr = []byte("
")
+ esc_fffd = []byte("\uFFFD") // Unicode replacement character
+)
+
+// EscapeText writes to w the properly escaped XML equivalent
+// of the plain text data s.
+func EscapeText(w io.Writer, s []byte) error {
+ return escapeText(w, s, true)
+}
+
+// escapeText writes to w the properly escaped XML equivalent
+// of the plain text data s. If escapeNewline is true, newline
+// characters will be escaped.
+func escapeText(w io.Writer, s []byte, escapeNewline bool) error {
+ var esc []byte
+ last := 0
+ for i := 0; i < len(s); {
+ r, width := utf8.DecodeRune(s[i:])
+ i += width
+ switch r {
+ case '"':
+ esc = esc_quot
+ case '\'':
+ esc = esc_apos
+ case '&':
+ esc = esc_amp
+ case '<':
+ esc = esc_lt
+ case '>':
+ esc = esc_gt
+ case '\t':
+ esc = esc_tab
+ case '\n':
+ if !escapeNewline {
+ continue
+ }
+ esc = esc_nl
+ case '\r':
+ esc = esc_cr
+ default:
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
+ esc = esc_fffd
+ break
+ }
+ continue
+ }
+ if _, err := w.Write(s[last : i-width]); err != nil {
+ return err
+ }
+ if _, err := w.Write(esc); err != nil {
+ return err
+ }
+ last = i
+ }
+ if _, err := w.Write(s[last:]); err != nil {
+ return err
+ }
+ return nil
+}
+
+// EscapeString writes to p the properly escaped XML equivalent
+// of the plain text data s.
+func (p *printer) EscapeString(s string) {
+ var esc []byte
+ last := 0
+ for i := 0; i < len(s); {
+ r, width := utf8.DecodeRuneInString(s[i:])
+ i += width
+ switch r {
+ case '"':
+ esc = esc_quot
+ case '\'':
+ esc = esc_apos
+ case '&':
+ esc = esc_amp
+ case '<':
+ esc = esc_lt
+ case '>':
+ esc = esc_gt
+ case '\t':
+ esc = esc_tab
+ case '\n':
+ esc = esc_nl
+ case '\r':
+ esc = esc_cr
+ default:
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
+ esc = esc_fffd
+ break
+ }
+ continue
+ }
+ p.WriteString(s[last : i-width])
+ p.Write(esc)
+ last = i
+ }
+ p.WriteString(s[last:])
+}
+
+// Escape is like EscapeText but omits the error return value.
+// It is provided for backwards compatibility with Go 1.0.
+// Code targeting Go 1.1 or later should use EscapeText.
+func Escape(w io.Writer, s []byte) {
+ EscapeText(w, s)
+}
+
+// procInst parses the `param="..."` or `param='...'`
+// value out of the provided string, returning "" if not found.
+func procInst(param, s string) string {
+ // TODO: this parsing is somewhat lame and not exact.
+ // It works for all actual cases, though.
+ param = param + "="
+ idx := strings.Index(s, param)
+ if idx == -1 {
+ return ""
+ }
+ v := s[idx+len(param):]
+ if v == "" {
+ return ""
+ }
+ if v[0] != '\'' && v[0] != '"' {
+ return ""
+ }
+ idx = strings.IndexRune(v[1:], rune(v[0]))
+ if idx == -1 {
+ return ""
+ }
+ return v[1 : idx+1]
+}
diff --git a/webdav/internal/xml/xml_test.go b/webdav/internal/xml/xml_test.go
new file mode 100644
index 0000000..312a7c9
--- /dev/null
+++ b/webdav/internal/xml/xml_test.go
@@ -0,0 +1,752 @@
+// 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.
+
+package xml
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "testing"
+ "unicode/utf8"
+)
+
+const testInput = `
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` +
+ "\r\n\t" + ` >
+ <hello lang="en">World <>'" 白鵬翔</hello>
+ <query>&何; &is-it;</query>
+ <goodbye />
+ <outer foo:attr="value" xmlns:tag="ns4">
+ <inner/>
+ </outer>
+ <tag:name>
+ <![CDATA[Some text here.]]>
+ </tag:name>
+</body><!-- missing final newline -->`
+
+var testEntity = map[string]string{"何": "What", "is-it": "is it?"}
+
+var rawTokens = []Token{
+ CharData("\n"),
+ ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
+ CharData("\n"),
+ Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
+ CharData("\n"),
+ StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
+ CharData("\n "),
+ StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
+ CharData("World <>'\" 白鵬翔"),
+ EndElement{Name{"", "hello"}},
+ CharData("\n "),
+ StartElement{Name{"", "query"}, []Attr{}},
+ CharData("What is it?"),
+ EndElement{Name{"", "query"}},
+ CharData("\n "),
+ StartElement{Name{"", "goodbye"}, []Attr{}},
+ EndElement{Name{"", "goodbye"}},
+ CharData("\n "),
+ StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
+ CharData("\n "),
+ StartElement{Name{"", "inner"}, []Attr{}},
+ EndElement{Name{"", "inner"}},
+ CharData("\n "),
+ EndElement{Name{"", "outer"}},
+ CharData("\n "),
+ StartElement{Name{"tag", "name"}, []Attr{}},
+ CharData("\n "),
+ CharData("Some text here."),
+ CharData("\n "),
+ EndElement{Name{"tag", "name"}},
+ CharData("\n"),
+ EndElement{Name{"", "body"}},
+ Comment(" missing final newline "),
+}
+
+var cookedTokens = []Token{
+ CharData("\n"),
+ ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
+ CharData("\n"),
+ Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
+ CharData("\n"),
+ StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
+ CharData("\n "),
+ StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
+ CharData("World <>'\" 白鵬翔"),
+ EndElement{Name{"ns2", "hello"}},
+ CharData("\n "),
+ StartElement{Name{"ns2", "query"}, []Attr{}},
+ CharData("What is it?"),
+ EndElement{Name{"ns2", "query"}},
+ CharData("\n "),
+ StartElement{Name{"ns2", "goodbye"}, []Attr{}},
+ EndElement{Name{"ns2", "goodbye"}},
+ CharData("\n "),
+ StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
+ CharData("\n "),
+ StartElement{Name{"ns2", "inner"}, []Attr{}},
+ EndElement{Name{"ns2", "inner"}},
+ CharData("\n "),
+ EndElement{Name{"ns2", "outer"}},
+ CharData("\n "),
+ StartElement{Name{"ns3", "name"}, []Attr{}},
+ CharData("\n "),
+ CharData("Some text here."),
+ CharData("\n "),
+ EndElement{Name{"ns3", "name"}},
+ CharData("\n"),
+ EndElement{Name{"ns2", "body"}},
+ Comment(" missing final newline "),
+}
+
+const testInputAltEncoding = `
+<?xml version="1.0" encoding="x-testing-uppercase"?>
+<TAG>VALUE</TAG>`
+
+var rawTokensAltEncoding = []Token{
+ CharData("\n"),
+ ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("value"),
+ EndElement{Name{"", "tag"}},
+}
+
+var xmlInput = []string{
+ // unexpected EOF cases
+ "<",
+ "<t",
+ "<t ",
+ "<t/",
+ "<!",
+ "<!-",
+ "<!--",
+ "<!--c-",
+ "<!--c--",
+ "<!d",
+ "<t></",
+ "<t></t",
+ "<?",
+ "<?p",
+ "<t a",
+ "<t a=",
+ "<t a='",
+ "<t a=''",
+ "<t/><![",
+ "<t/><![C",
+ "<t/><![CDATA[d",
+ "<t/><![CDATA[d]",
+ "<t/><![CDATA[d]]",
+
+ // other Syntax errors
+ "<>",
+ "<t/a",
+ "<0 />",
+ "<?0 >",
+ // "<!0 >", // let the Token() caller handle
+ "</0>",
+ "<t 0=''>",
+ "<t a='&'>",
+ "<t a='<'>",
+ "<t> c;</t>",
+ "<t a>",
+ "<t a=>",
+ "<t a=v>",
+ // "<![CDATA[d]]>", // let the Token() caller handle
+ "<t></e>",
+ "<t></>",
+ "<t></t!",
+ "<t>cdata]]></t>",
+}
+
+func TestRawToken(t *testing.T) {
+ d := NewDecoder(strings.NewReader(testInput))
+ d.Entity = testEntity
+ testRawToken(t, d, testInput, rawTokens)
+}
+
+const nonStrictInput = `
+<tag>non&entity</tag>
+<tag>&unknown;entity</tag>
+<tag>{</tag>
+<tag>&#zzz;</tag>
+<tag>&なまえ3;</tag>
+<tag><-gt;</tag>
+<tag>&;</tag>
+<tag>&0a;</tag>
+`
+
+var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"}
+
+var nonStrictTokens = []Token{
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("non&entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&unknown;entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("{"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&#zzz;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&なまえ3;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("<-gt;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&0a;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+}
+
+func TestNonStrictRawToken(t *testing.T) {
+ d := NewDecoder(strings.NewReader(nonStrictInput))
+ d.Strict = false
+ testRawToken(t, d, nonStrictInput, nonStrictTokens)
+}
+
+type downCaser struct {
+ t *testing.T
+ r io.ByteReader
+}
+
+func (d *downCaser) ReadByte() (c byte, err error) {
+ c, err = d.r.ReadByte()
+ if c >= 'A' && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+ return
+}
+
+func (d *downCaser) Read(p []byte) (int, error) {
+ d.t.Fatalf("unexpected Read call on downCaser reader")
+ panic("unreachable")
+}
+
+func TestRawTokenAltEncoding(t *testing.T) {
+ d := NewDecoder(strings.NewReader(testInputAltEncoding))
+ d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
+ if charset != "x-testing-uppercase" {
+ t.Fatalf("unexpected charset %q", charset)
+ }
+ return &downCaser{t, input.(io.ByteReader)}, nil
+ }
+ testRawToken(t, d, testInputAltEncoding, rawTokensAltEncoding)
+}
+
+func TestRawTokenAltEncodingNoConverter(t *testing.T) {
+ d := NewDecoder(strings.NewReader(testInputAltEncoding))
+ token, err := d.RawToken()
+ if token == nil {
+ t.Fatalf("expected a token on first RawToken call")
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ token, err = d.RawToken()
+ if token != nil {
+ t.Errorf("expected a nil token; got %#v", token)
+ }
+ if err == nil {
+ t.Fatalf("expected an error on second RawToken call")
+ }
+ const encoding = "x-testing-uppercase"
+ if !strings.Contains(err.Error(), encoding) {
+ t.Errorf("expected error to contain %q; got error: %v",
+ encoding, err)
+ }
+}
+
+func testRawToken(t *testing.T, d *Decoder, raw string, rawTokens []Token) {
+ lastEnd := int64(0)
+ for i, want := range rawTokens {
+ start := d.InputOffset()
+ have, err := d.RawToken()
+ end := d.InputOffset()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ var shave, swant string
+ if _, ok := have.(CharData); ok {
+ shave = fmt.Sprintf("CharData(%q)", have)
+ } else {
+ shave = fmt.Sprintf("%#v", have)
+ }
+ if _, ok := want.(CharData); ok {
+ swant = fmt.Sprintf("CharData(%q)", want)
+ } else {
+ swant = fmt.Sprintf("%#v", want)
+ }
+ t.Errorf("token %d = %s, want %s", i, shave, swant)
+ }
+
+ // Check that InputOffset returned actual token.
+ switch {
+ case start < lastEnd:
+ t.Errorf("token %d: position [%d,%d) for %T is before previous token", i, start, end, have)
+ case start >= end:
+ // Special case: EndElement can be synthesized.
+ if start == end && end == lastEnd {
+ break
+ }
+ t.Errorf("token %d: position [%d,%d) for %T is empty", i, start, end, have)
+ case end > int64(len(raw)):
+ t.Errorf("token %d: position [%d,%d) for %T extends beyond input", i, start, end, have)
+ default:
+ text := raw[start:end]
+ if strings.ContainsAny(text, "<>") && (!strings.HasPrefix(text, "<") || !strings.HasSuffix(text, ">")) {
+ t.Errorf("token %d: misaligned raw token %#q for %T", i, text, have)
+ }
+ }
+ lastEnd = end
+ }
+}
+
+// Ensure that directives (specifically !DOCTYPE) include the complete
+// text of any nested directives, noting that < and > do not change
+// nesting depth if they are in single or double quotes.
+
+var nestedDirectivesInput = `
+<!DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
+<!DOCTYPE [<!ENTITY xlt ">">]>
+<!DOCTYPE [<!ENTITY xlt "<">]>
+<!DOCTYPE [<!ENTITY xlt '>'>]>
+<!DOCTYPE [<!ENTITY xlt '<'>]>
+<!DOCTYPE [<!ENTITY xlt '">'>]>
+<!DOCTYPE [<!ENTITY xlt "'<">]>
+`
+
+var nestedDirectivesTokens = []Token{
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
+ CharData("\n"),
+}
+
+func TestNestedDirectives(t *testing.T) {
+ d := NewDecoder(strings.NewReader(nestedDirectivesInput))
+
+ for i, want := range nestedDirectivesTokens {
+ have, err := d.Token()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("token %d = %#v want %#v", i, have, want)
+ }
+ }
+}
+
+func TestToken(t *testing.T) {
+ d := NewDecoder(strings.NewReader(testInput))
+ d.Entity = testEntity
+
+ for i, want := range cookedTokens {
+ have, err := d.Token()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("token %d = %#v want %#v", i, have, want)
+ }
+ }
+}
+
+func TestSyntax(t *testing.T) {
+ for i := range xmlInput {
+ d := NewDecoder(strings.NewReader(xmlInput[i]))
+ var err error
+ for _, err = d.Token(); err == nil; _, err = d.Token() {
+ }
+ if _, ok := err.(*SyntaxError); !ok {
+ t.Fatalf(`xmlInput "%s": expected SyntaxError not received`, xmlInput[i])
+ }
+ }
+}
+
+type allScalars struct {
+ True1 bool
+ True2 bool
+ False1 bool
+ False2 bool
+ Int int
+ Int8 int8
+ Int16 int16
+ Int32 int32
+ Int64 int64
+ Uint int
+ Uint8 uint8
+ Uint16 uint16
+ Uint32 uint32
+ Uint64 uint64
+ Uintptr uintptr
+ Float32 float32
+ Float64 float64
+ String string
+ PtrString *string
+}
+
+var all = allScalars{
+ True1: true,
+ True2: true,
+ False1: false,
+ False2: false,
+ Int: 1,
+ Int8: -2,
+ Int16: 3,
+ Int32: -4,
+ Int64: 5,
+ Uint: 6,
+ Uint8: 7,
+ Uint16: 8,
+ Uint32: 9,
+ Uint64: 10,
+ Uintptr: 11,
+ Float32: 13.0,
+ Float64: 14.0,
+ String: "15",
+ PtrString: &sixteen,
+}
+
+var sixteen = "16"
+
+const testScalarsInput = `<allscalars>
+ <True1>true</True1>
+ <True2>1</True2>
+ <False1>false</False1>
+ <False2>0</False2>
+ <Int>1</Int>
+ <Int8>-2</Int8>
+ <Int16>3</Int16>
+ <Int32>-4</Int32>
+ <Int64>5</Int64>
+ <Uint>6</Uint>
+ <Uint8>7</Uint8>
+ <Uint16>8</Uint16>
+ <Uint32>9</Uint32>
+ <Uint64>10</Uint64>
+ <Uintptr>11</Uintptr>
+ <Float>12.0</Float>
+ <Float32>13.0</Float32>
+ <Float64>14.0</Float64>
+ <String>15</String>
+ <PtrString>16</PtrString>
+</allscalars>`
+
+func TestAllScalars(t *testing.T) {
+ var a allScalars
+ err := Unmarshal([]byte(testScalarsInput), &a)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(a, all) {
+ t.Errorf("have %+v want %+v", a, all)
+ }
+}
+
+type item struct {
+ Field_a string
+}
+
+func TestIssue569(t *testing.T) {
+ data := `<item><Field_a>abcd</Field_a></item>`
+ var i item
+ err := Unmarshal([]byte(data), &i)
+
+ if err != nil || i.Field_a != "abcd" {
+ t.Fatal("Expecting abcd")
+ }
+}
+
+func TestUnquotedAttrs(t *testing.T) {
+ data := "<tag attr=azAZ09:-_\t>"
+ d := NewDecoder(strings.NewReader(data))
+ d.Strict = false
+ token, err := d.Token()
+ if _, ok := err.(*SyntaxError); ok {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if token.(StartElement).Name.Local != "tag" {
+ t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
+ }
+ attr := token.(StartElement).Attr[0]
+ if attr.Value != "azAZ09:-_" {
+ t.Errorf("Unexpected attribute value: %v", attr.Value)
+ }
+ if attr.Name.Local != "attr" {
+ t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
+ }
+}
+
+func TestValuelessAttrs(t *testing.T) {
+ tests := [][3]string{
+ {"<p nowrap>", "p", "nowrap"},
+ {"<p nowrap >", "p", "nowrap"},
+ {"<input checked/>", "input", "checked"},
+ {"<input checked />", "input", "checked"},
+ }
+ for _, test := range tests {
+ d := NewDecoder(strings.NewReader(test[0]))
+ d.Strict = false
+ token, err := d.Token()
+ if _, ok := err.(*SyntaxError); ok {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if token.(StartElement).Name.Local != test[1] {
+ t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
+ }
+ attr := token.(StartElement).Attr[0]
+ if attr.Value != test[2] {
+ t.Errorf("Unexpected attribute value: %v", attr.Value)
+ }
+ if attr.Name.Local != test[2] {
+ t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
+ }
+ }
+}
+
+func TestCopyTokenCharData(t *testing.T) {
+ data := []byte("same data")
+ var tok1 Token = CharData(data)
+ tok2 := CopyToken(tok1)
+ if !reflect.DeepEqual(tok1, tok2) {
+ t.Error("CopyToken(CharData) != CharData")
+ }
+ data[1] = 'o'
+ if reflect.DeepEqual(tok1, tok2) {
+ t.Error("CopyToken(CharData) uses same buffer.")
+ }
+}
+
+func TestCopyTokenStartElement(t *testing.T) {
+ elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}
+ var tok1 Token = elt
+ tok2 := CopyToken(tok1)
+ if tok1.(StartElement).Attr[0].Value != "en" {
+ t.Error("CopyToken overwrote Attr[0]")
+ }
+ if !reflect.DeepEqual(tok1, tok2) {
+ t.Error("CopyToken(StartElement) != StartElement")
+ }
+ tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"}
+ if reflect.DeepEqual(tok1, tok2) {
+ t.Error("CopyToken(CharData) uses same buffer.")
+ }
+}
+
+func TestSyntaxErrorLineNum(t *testing.T) {
+ testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
+ d := NewDecoder(strings.NewReader(testInput))
+ var err error
+ for _, err = d.Token(); err == nil; _, err = d.Token() {
+ }
+ synerr, ok := err.(*SyntaxError)
+ if !ok {
+ t.Error("Expected SyntaxError.")
+ }
+ if synerr.Line != 3 {
+ t.Error("SyntaxError didn't have correct line number.")
+ }
+}
+
+func TestTrailingRawToken(t *testing.T) {
+ input := `<FOO></FOO> `
+ d := NewDecoder(strings.NewReader(input))
+ var err error
+ for _, err = d.RawToken(); err == nil; _, err = d.RawToken() {
+ }
+ if err != io.EOF {
+ t.Fatalf("d.RawToken() = _, %v, want _, io.EOF", err)
+ }
+}
+
+func TestTrailingToken(t *testing.T) {
+ input := `<FOO></FOO> `
+ d := NewDecoder(strings.NewReader(input))
+ var err error
+ for _, err = d.Token(); err == nil; _, err = d.Token() {
+ }
+ if err != io.EOF {
+ t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
+ }
+}
+
+func TestEntityInsideCDATA(t *testing.T) {
+ input := `<test><![CDATA[ &val=foo ]]></test>`
+ d := NewDecoder(strings.NewReader(input))
+ var err error
+ for _, err = d.Token(); err == nil; _, err = d.Token() {
+ }
+ if err != io.EOF {
+ t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
+ }
+}
+
+var characterTests = []struct {
+ in string
+ err string
+}{
+ {"\x12<doc/>", "illegal character code U+0012"},
+ {"<?xml version=\"1.0\"?>\x0b<doc/>", "illegal character code U+000B"},
+ {"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"},
+ {"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"},
+ {"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"},
+ {"<doc>&abc\x01;</doc>", "invalid character entity &abc (no semicolon)"},
+ {"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"},
+ {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity &\uFFFE;"},
+ {"<doc>&hello;</doc>", "invalid character entity &hello;"},
+}
+
+func TestDisallowedCharacters(t *testing.T) {
+
+ for i, tt := range characterTests {
+ d := NewDecoder(strings.NewReader(tt.in))
+ var err error
+
+ for err == nil {
+ _, err = d.Token()
+ }
+ synerr, ok := err.(*SyntaxError)
+ if !ok {
+ t.Fatalf("input %d d.Token() = _, %v, want _, *SyntaxError", i, err)
+ }
+ if synerr.Msg != tt.err {
+ t.Fatalf("input %d synerr.Msg wrong: want %q, got %q", i, tt.err, synerr.Msg)
+ }
+ }
+}
+
+type procInstEncodingTest struct {
+ expect, got string
+}
+
+var procInstTests = []struct {
+ input string
+ expect [2]string
+}{
+ {`version="1.0" encoding="utf-8"`, [2]string{"1.0", "utf-8"}},
+ {`version="1.0" encoding='utf-8'`, [2]string{"1.0", "utf-8"}},
+ {`version="1.0" encoding='utf-8' `, [2]string{"1.0", "utf-8"}},
+ {`version="1.0" encoding=utf-8`, [2]string{"1.0", ""}},
+ {`encoding="FOO" `, [2]string{"", "FOO"}},
+}
+
+func TestProcInstEncoding(t *testing.T) {
+ for _, test := range procInstTests {
+ if got := procInst("version", test.input); got != test.expect[0] {
+ t.Errorf("procInst(version, %q) = %q; want %q", test.input, got, test.expect[0])
+ }
+ if got := procInst("encoding", test.input); got != test.expect[1] {
+ t.Errorf("procInst(encoding, %q) = %q; want %q", test.input, got, test.expect[1])
+ }
+ }
+}
+
+// Ensure that directives with comments include the complete
+// text of any nested directives.
+
+var directivesWithCommentsInput = `
+<!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
+<!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]>
+<!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]>
+`
+
+var directivesWithCommentsTokens = []Token{
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE <!-> <!> [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+}
+
+func TestDirectivesWithComments(t *testing.T) {
+ d := NewDecoder(strings.NewReader(directivesWithCommentsInput))
+
+ for i, want := range directivesWithCommentsTokens {
+ have, err := d.Token()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("token %d = %#v want %#v", i, have, want)
+ }
+ }
+}
+
+// Writer whose Write method always returns an error.
+type errWriter struct{}
+
+func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") }
+
+func TestEscapeTextIOErrors(t *testing.T) {
+ expectErr := "unwritable"
+ err := EscapeText(errWriter{}, []byte{'A'})
+
+ if err == nil || err.Error() != expectErr {
+ t.Errorf("have %v, want %v", err, expectErr)
+ }
+}
+
+func TestEscapeTextInvalidChar(t *testing.T) {
+ input := []byte("A \x00 terminated string.")
+ expected := "A \uFFFD terminated string."
+
+ buff := new(bytes.Buffer)
+ if err := EscapeText(buff, input); err != nil {
+ t.Fatalf("have %v, want nil", err)
+ }
+ text := buff.String()
+
+ if text != expected {
+ t.Errorf("have %v, want %v", text, expected)
+ }
+}
+
+func TestIssue5880(t *testing.T) {
+ type T []byte
+ data, err := Marshal(T{192, 168, 0, 1})
+ if err != nil {
+ t.Errorf("Marshal error: %v", err)
+ }
+ if !utf8.Valid(data) {
+ t.Errorf("Marshal generated invalid UTF-8: %x", data)
+ }
+}
diff --git a/webdav/litmus_test_server.go b/webdav/litmus_test_server.go
new file mode 100644
index 0000000..514db5d
--- /dev/null
+++ b/webdav/litmus_test_server.go
@@ -0,0 +1,94 @@
+// Copyright 2015 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.
+
+// +build ignore
+
+/*
+This program is a server for the WebDAV 'litmus' compliance test at
+http://www.webdav.org/neon/litmus/
+To run the test:
+
+go run litmus_test_server.go
+
+and separately, from the downloaded litmus-xxx directory:
+
+make URL=http://localhost:9999/ check
+*/
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net/http"
+ "net/url"
+
+ "golang.org/x/net/webdav"
+)
+
+var port = flag.Int("port", 9999, "server port")
+
+func main() {
+ flag.Parse()
+ log.SetFlags(0)
+ h := &webdav.Handler{
+ FileSystem: webdav.NewMemFS(),
+ LockSystem: webdav.NewMemLS(),
+ Logger: func(r *http.Request, err error) {
+ litmus := r.Header.Get("X-Litmus")
+ if len(litmus) > 19 {
+ litmus = litmus[:16] + "..."
+ }
+
+ switch r.Method {
+ case "COPY", "MOVE":
+ dst := ""
+ if u, err := url.Parse(r.Header.Get("Destination")); err == nil {
+ dst = u.Path
+ }
+ o := r.Header.Get("Overwrite")
+ log.Printf("%-20s%-10s%-30s%-30so=%-2s%v", litmus, r.Method, r.URL.Path, dst, o, err)
+ default:
+ log.Printf("%-20s%-10s%-30s%v", litmus, r.Method, r.URL.Path, err)
+ }
+ },
+ }
+
+ // The next line would normally be:
+ // http.Handle("/", h)
+ // but we wrap that HTTP handler h to cater for a special case.
+ //
+ // The propfind_invalid2 litmus test case expects an empty namespace prefix
+ // declaration to be an error. The FAQ in the webdav litmus test says:
+ //
+ // "What does the "propfind_invalid2" test check for?...
+ //
+ // If a request was sent with an XML body which included an empty namespace
+ // prefix declaration (xmlns:ns1=""), then the server must reject that with
+ // a "400 Bad Request" response, as it is invalid according to the XML
+ // Namespace specification."
+ //
+ // On the other hand, the Go standard library's encoding/xml package
+ // accepts an empty xmlns namespace, as per the discussion at
+ // https://github.com/golang/go/issues/8068
+ //
+ // Empty namespaces seem disallowed in the second (2006) edition of the XML
+ // standard, but allowed in a later edition. The grammar differs between
+ // http://www.w3.org/TR/2006/REC-xml-names-20060816/#ns-decl and
+ // http://www.w3.org/TR/REC-xml-names/#dt-prefix
+ //
+ // Thus, we assume that the propfind_invalid2 test is obsolete, and
+ // hard-code the 400 Bad Request response that the test expects.
+ http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get("X-Litmus") == "props: 3 (propfind_invalid2)" {
+ http.Error(w, "400 Bad Request", http.StatusBadRequest)
+ return
+ }
+ h.ServeHTTP(w, r)
+ }))
+
+ addr := fmt.Sprintf(":%d", *port)
+ log.Printf("Serving %v", addr)
+ log.Fatal(http.ListenAndServe(addr, nil))
+}
diff --git a/webdav/lock.go b/webdav/lock.go
new file mode 100644
index 0000000..344ac5c
--- /dev/null
+++ b/webdav/lock.go
@@ -0,0 +1,445 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "container/heap"
+ "errors"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ // ErrConfirmationFailed is returned by a LockSystem's Confirm method.
+ ErrConfirmationFailed = errors.New("webdav: confirmation failed")
+ // ErrForbidden is returned by a LockSystem's Unlock method.
+ ErrForbidden = errors.New("webdav: forbidden")
+ // ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
+ ErrLocked = errors.New("webdav: locked")
+ // ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
+ ErrNoSuchLock = errors.New("webdav: no such lock")
+)
+
+// Condition can match a WebDAV resource, based on a token or ETag.
+// Exactly one of Token and ETag should be non-empty.
+type Condition struct {
+ Not bool
+ Token string
+ ETag string
+}
+
+// LockSystem manages access to a collection of named resources. The elements
+// in a lock name are separated by slash ('/', U+002F) characters, regardless
+// of host operating system convention.
+type LockSystem interface {
+ // Confirm confirms that the caller can claim all of the locks specified by
+ // the given conditions, and that holding the union of all of those locks
+ // gives exclusive access to all of the named resources. Up to two resources
+ // can be named. Empty names are ignored.
+ //
+ // Exactly one of release and err will be non-nil. If release is non-nil,
+ // all of the requested locks are held until release is called. Calling
+ // release does not unlock the lock, in the WebDAV UNLOCK sense, but once
+ // Confirm has confirmed that a lock claim is valid, that lock cannot be
+ // Confirmed again until it has been released.
+ //
+ // If Confirm returns ErrConfirmationFailed then the Handler will continue
+ // to try any other set of locks presented (a WebDAV HTTP request can
+ // present more than one set of locks). If it returns any other non-nil
+ // error, the Handler will write a "500 Internal Server Error" HTTP status.
+ Confirm(now time.Time, name0, name1 string, conditions ...Condition) (release func(), err error)
+
+ // Create creates a lock with the given depth, duration, owner and root
+ // (name). The depth will either be negative (meaning infinite) or zero.
+ //
+ // If Create returns ErrLocked then the Handler will write a "423 Locked"
+ // HTTP status. If it returns any other non-nil error, the Handler will
+ // write a "500 Internal Server Error" HTTP status.
+ //
+ // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
+ // when to use each error.
+ //
+ // The token returned identifies the created lock. It should be an absolute
+ // URI as defined by RFC 3986, Section 4.3. In particular, it should not
+ // contain whitespace.
+ Create(now time.Time, details LockDetails) (token string, err error)
+
+ // Refresh refreshes the lock with the given token.
+ //
+ // If Refresh returns ErrLocked then the Handler will write a "423 Locked"
+ // HTTP Status. If Refresh returns ErrNoSuchLock then the Handler will write
+ // a "412 Precondition Failed" HTTP Status. If it returns any other non-nil
+ // error, the Handler will write a "500 Internal Server Error" HTTP status.
+ //
+ // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
+ // when to use each error.
+ Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
+
+ // Unlock unlocks the lock with the given token.
+ //
+ // If Unlock returns ErrForbidden then the Handler will write a "403
+ // Forbidden" HTTP Status. If Unlock returns ErrLocked then the Handler
+ // will write a "423 Locked" HTTP status. If Unlock returns ErrNoSuchLock
+ // then the Handler will write a "409 Conflict" HTTP Status. If it returns
+ // any other non-nil error, the Handler will write a "500 Internal Server
+ // Error" HTTP status.
+ //
+ // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.11.1 for
+ // when to use each error.
+ Unlock(now time.Time, token string) error
+}
+
+// LockDetails are a lock's metadata.
+type LockDetails struct {
+ // Root is the root resource name being locked. For a zero-depth lock, the
+ // root is the only resource being locked.
+ Root string
+ // Duration is the lock timeout. A negative duration means infinite.
+ Duration time.Duration
+ // OwnerXML is the verbatim <owner> XML given in a LOCK HTTP request.
+ //
+ // TODO: does the "verbatim" nature play well with XML namespaces?
+ // Does the OwnerXML field need to have more structure? See
+ // https://codereview.appspot.com/175140043/#msg2
+ OwnerXML string
+ // ZeroDepth is whether the lock has zero depth. If it does not have zero
+ // depth, it has infinite depth.
+ ZeroDepth bool
+}
+
+// NewMemLS returns a new in-memory LockSystem.
+func NewMemLS() LockSystem {
+ return &memLS{
+ byName: make(map[string]*memLSNode),
+ byToken: make(map[string]*memLSNode),
+ gen: uint64(time.Now().Unix()),
+ }
+}
+
+type memLS struct {
+ mu sync.Mutex
+ byName map[string]*memLSNode
+ byToken map[string]*memLSNode
+ gen uint64
+ // byExpiry only contains those nodes whose LockDetails have a finite
+ // Duration and are yet to expire.
+ byExpiry byExpiry
+}
+
+func (m *memLS) nextToken() string {
+ m.gen++
+ return strconv.FormatUint(m.gen, 10)
+}
+
+func (m *memLS) collectExpiredNodes(now time.Time) {
+ for len(m.byExpiry) > 0 {
+ if now.Before(m.byExpiry[0].expiry) {
+ break
+ }
+ m.remove(m.byExpiry[0])
+ }
+}
+
+func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.collectExpiredNodes(now)
+
+ var n0, n1 *memLSNode
+ if name0 != "" {
+ if n0 = m.lookup(slashClean(name0), conditions...); n0 == nil {
+ return nil, ErrConfirmationFailed
+ }
+ }
+ if name1 != "" {
+ if n1 = m.lookup(slashClean(name1), conditions...); n1 == nil {
+ return nil, ErrConfirmationFailed
+ }
+ }
+
+ // Don't hold the same node twice.
+ if n1 == n0 {
+ n1 = nil
+ }
+
+ if n0 != nil {
+ m.hold(n0)
+ }
+ if n1 != nil {
+ m.hold(n1)
+ }
+ return func() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if n1 != nil {
+ m.unhold(n1)
+ }
+ if n0 != nil {
+ m.unhold(n0)
+ }
+ }, nil
+}
+
+// lookup returns the node n that locks the named resource, provided that n
+// matches at least one of the given conditions and that lock isn't held by
+// another party. Otherwise, it returns nil.
+//
+// n may be a parent of the named resource, if n is an infinite depth lock.
+func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) {
+ // TODO: support Condition.Not and Condition.ETag.
+ for _, c := range conditions {
+ n = m.byToken[c.Token]
+ if n == nil || n.held {
+ continue
+ }
+ if name == n.details.Root {
+ return n
+ }
+ if n.details.ZeroDepth {
+ continue
+ }
+ if n.details.Root == "/" || strings.HasPrefix(name, n.details.Root+"/") {
+ return n
+ }
+ }
+ return nil
+}
+
+func (m *memLS) hold(n *memLSNode) {
+ if n.held {
+ panic("webdav: memLS inconsistent held state")
+ }
+ n.held = true
+ if n.details.Duration >= 0 && n.byExpiryIndex >= 0 {
+ heap.Remove(&m.byExpiry, n.byExpiryIndex)
+ }
+}
+
+func (m *memLS) unhold(n *memLSNode) {
+ if !n.held {
+ panic("webdav: memLS inconsistent held state")
+ }
+ n.held = false
+ if n.details.Duration >= 0 {
+ heap.Push(&m.byExpiry, n)
+ }
+}
+
+func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.collectExpiredNodes(now)
+ details.Root = slashClean(details.Root)
+
+ if !m.canCreate(details.Root, details.ZeroDepth) {
+ return "", ErrLocked
+ }
+ n := m.create(details.Root)
+ n.token = m.nextToken()
+ m.byToken[n.token] = n
+ n.details = details
+ if n.details.Duration >= 0 {
+ n.expiry = now.Add(n.details.Duration)
+ heap.Push(&m.byExpiry, n)
+ }
+ return n.token, nil
+}
+
+func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.collectExpiredNodes(now)
+
+ n := m.byToken[token]
+ if n == nil {
+ return LockDetails{}, ErrNoSuchLock
+ }
+ if n.held {
+ return LockDetails{}, ErrLocked
+ }
+ if n.byExpiryIndex >= 0 {
+ heap.Remove(&m.byExpiry, n.byExpiryIndex)
+ }
+ n.details.Duration = duration
+ if n.details.Duration >= 0 {
+ n.expiry = now.Add(n.details.Duration)
+ heap.Push(&m.byExpiry, n)
+ }
+ return n.details, nil
+}
+
+func (m *memLS) Unlock(now time.Time, token string) error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.collectExpiredNodes(now)
+
+ n := m.byToken[token]
+ if n == nil {
+ return ErrNoSuchLock
+ }
+ if n.held {
+ return ErrLocked
+ }
+ m.remove(n)
+ return nil
+}
+
+func (m *memLS) canCreate(name string, zeroDepth bool) bool {
+ return walkToRoot(name, func(name0 string, first bool) bool {
+ n := m.byName[name0]
+ if n == nil {
+ return true
+ }
+ if first {
+ if n.token != "" {
+ // The target node is already locked.
+ return false
+ }
+ if !zeroDepth {
+ // The requested lock depth is infinite, and the fact that n exists
+ // (n != nil) means that a descendent of the target node is locked.
+ return false
+ }
+ } else if n.token != "" && !n.details.ZeroDepth {
+ // An ancestor of the target node is locked with infinite depth.
+ return false
+ }
+ return true
+ })
+}
+
+func (m *memLS) create(name string) (ret *memLSNode) {
+ walkToRoot(name, func(name0 string, first bool) bool {
+ n := m.byName[name0]
+ if n == nil {
+ n = &memLSNode{
+ details: LockDetails{
+ Root: name0,
+ },
+ byExpiryIndex: -1,
+ }
+ m.byName[name0] = n
+ }
+ n.refCount++
+ if first {
+ ret = n
+ }
+ return true
+ })
+ return ret
+}
+
+func (m *memLS) remove(n *memLSNode) {
+ delete(m.byToken, n.token)
+ n.token = ""
+ walkToRoot(n.details.Root, func(name0 string, first bool) bool {
+ x := m.byName[name0]
+ x.refCount--
+ if x.refCount == 0 {
+ delete(m.byName, name0)
+ }
+ return true
+ })
+ if n.byExpiryIndex >= 0 {
+ heap.Remove(&m.byExpiry, n.byExpiryIndex)
+ }
+}
+
+func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
+ for first := true; ; first = false {
+ if !f(name, first) {
+ return false
+ }
+ if name == "/" {
+ break
+ }
+ name = name[:strings.LastIndex(name, "/")]
+ if name == "" {
+ name = "/"
+ }
+ }
+ return true
+}
+
+type memLSNode struct {
+ // details are the lock metadata. Even if this node's name is not explicitly locked,
+ // details.Root will still equal the node's name.
+ details LockDetails
+ // token is the unique identifier for this node's lock. An empty token means that
+ // this node is not explicitly locked.
+ token string
+ // refCount is the number of self-or-descendent nodes that are explicitly locked.
+ refCount int
+ // expiry is when this node's lock expires.
+ expiry time.Time
+ // byExpiryIndex is the index of this node in memLS.byExpiry. It is -1
+ // if this node does not expire, or has expired.
+ byExpiryIndex int
+ // held is whether this node's lock is actively held by a Confirm call.
+ held bool
+}
+
+type byExpiry []*memLSNode
+
+func (b *byExpiry) Len() int {
+ return len(*b)
+}
+
+func (b *byExpiry) Less(i, j int) bool {
+ return (*b)[i].expiry.Before((*b)[j].expiry)
+}
+
+func (b *byExpiry) Swap(i, j int) {
+ (*b)[i], (*b)[j] = (*b)[j], (*b)[i]
+ (*b)[i].byExpiryIndex = i
+ (*b)[j].byExpiryIndex = j
+}
+
+func (b *byExpiry) Push(x interface{}) {
+ n := x.(*memLSNode)
+ n.byExpiryIndex = len(*b)
+ *b = append(*b, n)
+}
+
+func (b *byExpiry) Pop() interface{} {
+ i := len(*b) - 1
+ n := (*b)[i]
+ (*b)[i] = nil
+ n.byExpiryIndex = -1
+ *b = (*b)[:i]
+ return n
+}
+
+const infiniteTimeout = -1
+
+// parseTimeout parses the Timeout HTTP header, as per section 10.7. If s is
+// empty, an infiniteTimeout is returned.
+func parseTimeout(s string) (time.Duration, error) {
+ if s == "" {
+ return infiniteTimeout, nil
+ }
+ if i := strings.IndexByte(s, ','); i >= 0 {
+ s = s[:i]
+ }
+ s = strings.TrimSpace(s)
+ if s == "Infinite" {
+ return infiniteTimeout, nil
+ }
+ const pre = "Second-"
+ if !strings.HasPrefix(s, pre) {
+ return 0, errInvalidTimeout
+ }
+ s = s[len(pre):]
+ if s == "" || s[0] < '0' || '9' < s[0] {
+ return 0, errInvalidTimeout
+ }
+ n, err := strconv.ParseInt(s, 10, 64)
+ if err != nil || 1<<32-1 < n {
+ return 0, errInvalidTimeout
+ }
+ return time.Duration(n) * time.Second, nil
+}
diff --git a/webdav/lock_test.go b/webdav/lock_test.go
new file mode 100644
index 0000000..116d6c0
--- /dev/null
+++ b/webdav/lock_test.go
@@ -0,0 +1,731 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "fmt"
+ "math/rand"
+ "path"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestWalkToRoot(t *testing.T) {
+ testCases := []struct {
+ name string
+ want []string
+ }{{
+ "/a/b/c/d",
+ []string{
+ "/a/b/c/d",
+ "/a/b/c",
+ "/a/b",
+ "/a",
+ "/",
+ },
+ }, {
+ "/a",
+ []string{
+ "/a",
+ "/",
+ },
+ }, {
+ "/",
+ []string{
+ "/",
+ },
+ }}
+
+ for _, tc := range testCases {
+ var got []string
+ if !walkToRoot(tc.name, func(name0 string, first bool) bool {
+ if first != (len(got) == 0) {
+ t.Errorf("name=%q: first=%t but len(got)==%d", tc.name, first, len(got))
+ return false
+ }
+ got = append(got, name0)
+ return true
+ }) {
+ continue
+ }
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("name=%q:\ngot %q\nwant %q", tc.name, got, tc.want)
+ }
+ }
+}
+
+var lockTestDurations = []time.Duration{
+ infiniteTimeout, // infiniteTimeout means to never expire.
+ 0, // A zero duration means to expire immediately.
+ 100 * time.Hour, // A very large duration will not expire in these tests.
+}
+
+// lockTestNames are the names of a set of mutually compatible locks. For each
+// name fragment:
+// - _ means no explicit lock.
+// - i means a infinite-depth lock,
+// - z means a zero-depth lock,
+var lockTestNames = []string{
+ "/_/_/_/_/z",
+ "/_/_/i",
+ "/_/z",
+ "/_/z/i",
+ "/_/z/z",
+ "/_/z/_/i",
+ "/_/z/_/z",
+ "/i",
+ "/z",
+ "/z/_/i",
+ "/z/_/z",
+}
+
+func lockTestZeroDepth(name string) bool {
+ switch name[len(name)-1] {
+ case 'i':
+ return false
+ case 'z':
+ return true
+ }
+ panic(fmt.Sprintf("lock name %q did not end with 'i' or 'z'", name))
+}
+
+func TestMemLSCanCreate(t *testing.T) {
+ now := time.Unix(0, 0)
+ m := NewMemLS().(*memLS)
+
+ for _, name := range lockTestNames {
+ _, err := m.Create(now, LockDetails{
+ Root: name,
+ Duration: infiniteTimeout,
+ ZeroDepth: lockTestZeroDepth(name),
+ })
+ if err != nil {
+ t.Fatalf("creating lock for %q: %v", name, err)
+ }
+ }
+
+ wantCanCreate := func(name string, zeroDepth bool) bool {
+ for _, n := range lockTestNames {
+ switch {
+ case n == name:
+ // An existing lock has the same name as the proposed lock.
+ return false
+ case strings.HasPrefix(n, name):
+ // An existing lock would be a child of the proposed lock,
+ // which conflicts if the proposed lock has infinite depth.
+ if !zeroDepth {
+ return false
+ }
+ case strings.HasPrefix(name, n):
+ // An existing lock would be an ancestor of the proposed lock,
+ // which conflicts if the ancestor has infinite depth.
+ if n[len(n)-1] == 'i' {
+ return false
+ }
+ }
+ }
+ return true
+ }
+
+ var check func(int, string)
+ check = func(recursion int, name string) {
+ for _, zeroDepth := range []bool{false, true} {
+ got := m.canCreate(name, zeroDepth)
+ want := wantCanCreate(name, zeroDepth)
+ if got != want {
+ t.Errorf("canCreate name=%q zeroDepth=%t: got %t, want %t", name, zeroDepth, got, want)
+ }
+ }
+ if recursion == 6 {
+ return
+ }
+ if name != "/" {
+ name += "/"
+ }
+ for _, c := range "_iz" {
+ check(recursion+1, name+string(c))
+ }
+ }
+ check(0, "/")
+}
+
+func TestMemLSLookup(t *testing.T) {
+ now := time.Unix(0, 0)
+ m := NewMemLS().(*memLS)
+
+ badToken := m.nextToken()
+ t.Logf("badToken=%q", badToken)
+
+ for _, name := range lockTestNames {
+ token, err := m.Create(now, LockDetails{
+ Root: name,
+ Duration: infiniteTimeout,
+ ZeroDepth: lockTestZeroDepth(name),
+ })
+ if err != nil {
+ t.Fatalf("creating lock for %q: %v", name, err)
+ }
+ t.Logf("%-15q -> node=%p token=%q", name, m.byName[name], token)
+ }
+
+ baseNames := append([]string{"/a", "/b/c"}, lockTestNames...)
+ for _, baseName := range baseNames {
+ for _, suffix := range []string{"", "/0", "/1/2/3"} {
+ name := baseName + suffix
+
+ goodToken := ""
+ base := m.byName[baseName]
+ if base != nil && (suffix == "" || !lockTestZeroDepth(baseName)) {
+ goodToken = base.token
+ }
+
+ for _, token := range []string{badToken, goodToken} {
+ if token == "" {
+ continue
+ }
+
+ got := m.lookup(name, Condition{Token: token})
+ want := base
+ if token == badToken {
+ want = nil
+ }
+ if got != want {
+ t.Errorf("name=%-20qtoken=%q (bad=%t): got %p, want %p",
+ name, token, token == badToken, got, want)
+ }
+ }
+ }
+ }
+}
+
+func TestMemLSConfirm(t *testing.T) {
+ now := time.Unix(0, 0)
+ m := NewMemLS().(*memLS)
+ alice, err := m.Create(now, LockDetails{
+ Root: "/alice",
+ Duration: infiniteTimeout,
+ ZeroDepth: false,
+ })
+ tweedle, err := m.Create(now, LockDetails{
+ Root: "/tweedle",
+ Duration: infiniteTimeout,
+ ZeroDepth: false,
+ })
+ if err != nil {
+ t.Fatalf("Create: %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Create: inconsistent state: %v", err)
+ }
+
+ // Test a mismatch between name and condition.
+ _, err = m.Confirm(now, "/tweedle/dee", "", Condition{Token: alice})
+ if err != ErrConfirmationFailed {
+ t.Fatalf("Confirm (mismatch): got %v, want ErrConfirmationFailed", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Confirm (mismatch): inconsistent state: %v", err)
+ }
+
+ // Test two names (that fall under the same lock) in the one Confirm call.
+ release, err := m.Confirm(now, "/tweedle/dee", "/tweedle/dum", Condition{Token: tweedle})
+ if err != nil {
+ t.Fatalf("Confirm (twins): %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Confirm (twins): inconsistent state: %v", err)
+ }
+ release()
+ if err := m.consistent(); err != nil {
+ t.Fatalf("release (twins): inconsistent state: %v", err)
+ }
+
+ // Test the same two names in overlapping Confirm / release calls.
+ releaseDee, err := m.Confirm(now, "/tweedle/dee", "", Condition{Token: tweedle})
+ if err != nil {
+ t.Fatalf("Confirm (sequence #0): %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Confirm (sequence #0): inconsistent state: %v", err)
+ }
+
+ _, err = m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
+ if err != ErrConfirmationFailed {
+ t.Fatalf("Confirm (sequence #1): got %v, want ErrConfirmationFailed", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Confirm (sequence #1): inconsistent state: %v", err)
+ }
+
+ releaseDee()
+ if err := m.consistent(); err != nil {
+ t.Fatalf("release (sequence #2): inconsistent state: %v", err)
+ }
+
+ releaseDum, err := m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
+ if err != nil {
+ t.Fatalf("Confirm (sequence #3): %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Confirm (sequence #3): inconsistent state: %v", err)
+ }
+
+ // Test that you can't unlock a held lock.
+ err = m.Unlock(now, tweedle)
+ if err != ErrLocked {
+ t.Fatalf("Unlock (sequence #4): got %v, want ErrLocked", err)
+ }
+
+ releaseDum()
+ if err := m.consistent(); err != nil {
+ t.Fatalf("release (sequence #5): inconsistent state: %v", err)
+ }
+
+ err = m.Unlock(now, tweedle)
+ if err != nil {
+ t.Fatalf("Unlock (sequence #6): %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Unlock (sequence #6): inconsistent state: %v", err)
+ }
+}
+
+func TestMemLSNonCanonicalRoot(t *testing.T) {
+ now := time.Unix(0, 0)
+ m := NewMemLS().(*memLS)
+ token, err := m.Create(now, LockDetails{
+ Root: "/foo/./bar//",
+ Duration: 1 * time.Second,
+ })
+ if err != nil {
+ t.Fatalf("Create: %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Create: inconsistent state: %v", err)
+ }
+ if err := m.Unlock(now, token); err != nil {
+ t.Fatalf("Unlock: %v", err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("Unlock: inconsistent state: %v", err)
+ }
+}
+
+func TestMemLSExpiry(t *testing.T) {
+ m := NewMemLS().(*memLS)
+ testCases := []string{
+ "setNow 0",
+ "create /a.5",
+ "want /a.5",
+ "create /c.6",
+ "want /a.5 /c.6",
+ "create /a/b.7",
+ "want /a.5 /a/b.7 /c.6",
+ "setNow 4",
+ "want /a.5 /a/b.7 /c.6",
+ "setNow 5",
+ "want /a/b.7 /c.6",
+ "setNow 6",
+ "want /a/b.7",
+ "setNow 7",
+ "want ",
+ "setNow 8",
+ "want ",
+ "create /a.12",
+ "create /b.13",
+ "create /c.15",
+ "create /a/d.16",
+ "want /a.12 /a/d.16 /b.13 /c.15",
+ "refresh /a.14",
+ "want /a.14 /a/d.16 /b.13 /c.15",
+ "setNow 12",
+ "want /a.14 /a/d.16 /b.13 /c.15",
+ "setNow 13",
+ "want /a.14 /a/d.16 /c.15",
+ "setNow 14",
+ "want /a/d.16 /c.15",
+ "refresh /a/d.20",
+ "refresh /c.20",
+ "want /a/d.20 /c.20",
+ "setNow 20",
+ "want ",
+ }
+
+ tokens := map[string]string{}
+ zTime := time.Unix(0, 0)
+ now := zTime
+ for i, tc := range testCases {
+ j := strings.IndexByte(tc, ' ')
+ if j < 0 {
+ t.Fatalf("test case #%d %q: invalid command", i, tc)
+ }
+ op, arg := tc[:j], tc[j+1:]
+ switch op {
+ default:
+ t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
+
+ case "create", "refresh":
+ parts := strings.Split(arg, ".")
+ if len(parts) != 2 {
+ t.Fatalf("test case #%d %q: invalid create", i, tc)
+ }
+ root := parts[0]
+ d, err := strconv.Atoi(parts[1])
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid duration", i, tc)
+ }
+ dur := time.Unix(0, 0).Add(time.Duration(d) * time.Second).Sub(now)
+
+ switch op {
+ case "create":
+ token, err := m.Create(now, LockDetails{
+ Root: root,
+ Duration: dur,
+ ZeroDepth: true,
+ })
+ if err != nil {
+ t.Fatalf("test case #%d %q: Create: %v", i, tc, err)
+ }
+ tokens[root] = token
+
+ case "refresh":
+ token := tokens[root]
+ if token == "" {
+ t.Fatalf("test case #%d %q: no token for %q", i, tc, root)
+ }
+ got, err := m.Refresh(now, token, dur)
+ if err != nil {
+ t.Fatalf("test case #%d %q: Refresh: %v", i, tc, err)
+ }
+ want := LockDetails{
+ Root: root,
+ Duration: dur,
+ ZeroDepth: true,
+ }
+ if got != want {
+ t.Fatalf("test case #%d %q:\ngot %v\nwant %v", i, tc, got, want)
+ }
+ }
+
+ case "setNow":
+ d, err := strconv.Atoi(arg)
+ if err != nil {
+ t.Fatalf("test case #%d %q: invalid duration", i, tc)
+ }
+ now = time.Unix(0, 0).Add(time.Duration(d) * time.Second)
+
+ case "want":
+ m.mu.Lock()
+ m.collectExpiredNodes(now)
+ got := make([]string, 0, len(m.byToken))
+ for _, n := range m.byToken {
+ got = append(got, fmt.Sprintf("%s.%d",
+ n.details.Root, n.expiry.Sub(zTime)/time.Second))
+ }
+ m.mu.Unlock()
+ sort.Strings(got)
+ want := []string{}
+ if arg != "" {
+ want = strings.Split(arg, " ")
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, want)
+ }
+ }
+
+ if err := m.consistent(); err != nil {
+ t.Fatalf("test case #%d %q: inconsistent state: %v", i, tc, err)
+ }
+ }
+}
+
+func TestMemLS(t *testing.T) {
+ now := time.Unix(0, 0)
+ m := NewMemLS().(*memLS)
+ rng := rand.New(rand.NewSource(0))
+ tokens := map[string]string{}
+ nConfirm, nCreate, nRefresh, nUnlock := 0, 0, 0, 0
+ const N = 2000
+
+ for i := 0; i < N; i++ {
+ name := lockTestNames[rng.Intn(len(lockTestNames))]
+ duration := lockTestDurations[rng.Intn(len(lockTestDurations))]
+ confirmed, unlocked := false, false
+
+ // If the name was already locked, we randomly confirm/release, refresh
+ // or unlock it. Otherwise, we create a lock.
+ token := tokens[name]
+ if token != "" {
+ switch rng.Intn(3) {
+ case 0:
+ confirmed = true
+ nConfirm++
+ release, err := m.Confirm(now, name, "", Condition{Token: token})
+ if err != nil {
+ t.Fatalf("iteration #%d: Confirm %q: %v", i, name, err)
+ }
+ if err := m.consistent(); err != nil {
+ t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
+ }
+ release()
+
+ case 1:
+ nRefresh++
+ if _, err := m.Refresh(now, token, duration); err != nil {
+ t.Fatalf("iteration #%d: Refresh %q: %v", i, name, err)
+ }
+
+ case 2:
+ unlocked = true
+ nUnlock++
+ if err := m.Unlock(now, token); err != nil {
+ t.Fatalf("iteration #%d: Unlock %q: %v", i, name, err)
+ }
+ }
+
+ } else {
+ nCreate++
+ var err error
+ token, err = m.Create(now, LockDetails{
+ Root: name,
+ Duration: duration,
+ ZeroDepth: lockTestZeroDepth(name),
+ })
+ if err != nil {
+ t.Fatalf("iteration #%d: Create %q: %v", i, name, err)
+ }
+ }
+
+ if !confirmed {
+ if duration == 0 || unlocked {
+ // A zero-duration lock should expire immediately and is
+ // effectively equivalent to being unlocked.
+ tokens[name] = ""
+ } else {
+ tokens[name] = token
+ }
+ }
+
+ if err := m.consistent(); err != nil {
+ t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
+ }
+ }
+
+ if nConfirm < N/10 {
+ t.Fatalf("too few Confirm calls: got %d, want >= %d", nConfirm, N/10)
+ }
+ if nCreate < N/10 {
+ t.Fatalf("too few Create calls: got %d, want >= %d", nCreate, N/10)
+ }
+ if nRefresh < N/10 {
+ t.Fatalf("too few Refresh calls: got %d, want >= %d", nRefresh, N/10)
+ }
+ if nUnlock < N/10 {
+ t.Fatalf("too few Unlock calls: got %d, want >= %d", nUnlock, N/10)
+ }
+}
+
+func (m *memLS) consistent() error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ // If m.byName is non-empty, then it must contain an entry for the root "/",
+ // and its refCount should equal the number of locked nodes.
+ if len(m.byName) > 0 {
+ n := m.byName["/"]
+ if n == nil {
+ return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
+ }
+ if n.refCount != len(m.byToken) {
+ return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
+ }
+ }
+
+ for name, n := range m.byName {
+ // The map keys should be consistent with the node's copy of the key.
+ if n.details.Root != name {
+ return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
+ }
+
+ // A name must be clean, and start with a "/".
+ if len(name) == 0 || name[0] != '/' {
+ return fmt.Errorf(`node name %q does not start with "/"`, name)
+ }
+ if name != path.Clean(name) {
+ return fmt.Errorf(`node name %q is not clean`, name)
+ }
+
+ // A node's refCount should be positive.
+ if n.refCount <= 0 {
+ return fmt.Errorf("non-positive refCount for node at name %q", name)
+ }
+
+ // A node's refCount should be the number of self-or-descendents that
+ // are locked (i.e. have a non-empty token).
+ var list []string
+ for name0, n0 := range m.byName {
+ // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
+ // so strings.HasPrefix is equivalent to self-or-descendent name match.
+ // We don't have to worry about "/foo/bar" being a false positive match
+ // for "/foo/b".
+ if strings.HasPrefix(name0, name) && n0.token != "" {
+ list = append(list, name0)
+ }
+ }
+ if n.refCount != len(list) {
+ sort.Strings(list)
+ return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
+ name, n.refCount, list, len(list))
+ }
+
+ // A node n is in m.byToken if it has a non-empty token.
+ if n.token != "" {
+ if _, ok := m.byToken[n.token]; !ok {
+ return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
+ }
+ }
+
+ // A node n is in m.byExpiry if it has a non-negative byExpiryIndex.
+ if n.byExpiryIndex >= 0 {
+ if n.byExpiryIndex >= len(m.byExpiry) {
+ return fmt.Errorf("node at name %q has byExpiryIndex %d but m.byExpiry has length %d", name, n.byExpiryIndex, len(m.byExpiry))
+ }
+ if n != m.byExpiry[n.byExpiryIndex] {
+ return fmt.Errorf("node at name %q has byExpiryIndex %d but that indexes a different node", name, n.byExpiryIndex)
+ }
+ }
+ }
+
+ for token, n := range m.byToken {
+ // The map keys should be consistent with the node's copy of the key.
+ if n.token != token {
+ return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
+ }
+
+ // Every node in m.byToken is in m.byName.
+ if _, ok := m.byName[n.details.Root]; !ok {
+ return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
+ }
+ }
+
+ for i, n := range m.byExpiry {
+ // The slice indices should be consistent with the node's copy of the index.
+ if n.byExpiryIndex != i {
+ return fmt.Errorf("node byExpiryIndex %d != byExpiry slice index %d", n.byExpiryIndex, i)
+ }
+
+ // Every node in m.byExpiry is in m.byName.
+ if _, ok := m.byName[n.details.Root]; !ok {
+ return fmt.Errorf("node at name %q in m.byExpiry but not in m.byName", n.details.Root)
+ }
+
+ // No node in m.byExpiry should be held.
+ if n.held {
+ return fmt.Errorf("node at name %q in m.byExpiry is held", n.details.Root)
+ }
+ }
+ return nil
+}
+
+func TestParseTimeout(t *testing.T) {
+ testCases := []struct {
+ s string
+ want time.Duration
+ wantErr error
+ }{{
+ "",
+ infiniteTimeout,
+ nil,
+ }, {
+ "Infinite",
+ infiniteTimeout,
+ nil,
+ }, {
+ "Infinitesimal",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "infinite",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second-0",
+ 0 * time.Second,
+ nil,
+ }, {
+ "Second-123",
+ 123 * time.Second,
+ nil,
+ }, {
+ " Second-456 ",
+ 456 * time.Second,
+ nil,
+ }, {
+ "Second-4100000000",
+ 4100000000 * time.Second,
+ nil,
+ }, {
+ "junk",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second-",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second--1",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second--123",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second-+123",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second-0x123",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "second-123",
+ 0,
+ errInvalidTimeout,
+ }, {
+ "Second-4294967295",
+ 4294967295 * time.Second,
+ nil,
+ }, {
+ // Section 10.7 says that "The timeout value for TimeType "Second"
+ // must not be greater than 2^32-1."
+ "Second-4294967296",
+ 0,
+ errInvalidTimeout,
+ }, {
+ // This test case comes from section 9.10.9 of the spec. It says,
+ //
+ // "In this request, the client has specified that it desires an
+ // infinite-length lock, if available, otherwise a timeout of 4.1
+ // billion seconds, if available."
+ //
+ // The Go WebDAV package always supports infinite length locks,
+ // and ignores the fallback after the comma.
+ "Infinite, Second-4100000000",
+ infiniteTimeout,
+ nil,
+ }}
+
+ for _, tc := range testCases {
+ got, gotErr := parseTimeout(tc.s)
+ if got != tc.want || gotErr != tc.wantErr {
+ t.Errorf("parsing %q:\ngot %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr)
+ }
+ }
+}
diff --git a/webdav/prop.go b/webdav/prop.go
new file mode 100644
index 0000000..a0ba16c
--- /dev/null
+++ b/webdav/prop.go
@@ -0,0 +1,385 @@
+// Copyright 2015 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.
+
+package webdav
+
+import (
+ "fmt"
+ "io"
+ "mime"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+// Proppatch describes a property update instruction as defined in RFC 4918.
+// See http://www.webdav.org/specs/rfc4918.html#METHOD_PROPPATCH
+type Proppatch struct {
+ // Remove specifies whether this patch removes properties. If it does not
+ // remove them, it sets them.
+ Remove bool
+ // Props contains the properties to be set or removed.
+ Props []Property
+}
+
+// Propstat describes a XML propstat element as defined in RFC 4918.
+// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
+type Propstat struct {
+ // Props contains the properties for which Status applies.
+ Props []Property
+
+ // Status defines the HTTP status code of the properties in Prop.
+ // Allowed values include, but are not limited to the WebDAV status
+ // code extensions for HTTP/1.1.
+ // http://www.webdav.org/specs/rfc4918.html#status.code.extensions.to.http11
+ Status int
+
+ // XMLError contains the XML representation of the optional error element.
+ // XML content within this field must not rely on any predefined
+ // namespace declarations or prefixes. If empty, the XML error element
+ // is omitted.
+ XMLError string
+
+ // ResponseDescription contains the contents of the optional
+ // responsedescription field. If empty, the XML element is omitted.
+ ResponseDescription string
+}
+
+// makePropstats returns a slice containing those of x and y whose Props slice
+// is non-empty. If both are empty, it returns a slice containing an otherwise
+// zero Propstat whose HTTP status code is 200 OK.
+func makePropstats(x, y Propstat) []Propstat {
+ pstats := make([]Propstat, 0, 2)
+ if len(x.Props) != 0 {
+ pstats = append(pstats, x)
+ }
+ if len(y.Props) != 0 {
+ pstats = append(pstats, y)
+ }
+ if len(pstats) == 0 {
+ pstats = append(pstats, Propstat{
+ Status: http.StatusOK,
+ })
+ }
+ return pstats
+}
+
+// DeadPropsHolder holds the dead properties of a resource.
+//
+// Dead properties are those properties that are explicitly defined. In
+// comparison, live properties, such as DAV:getcontentlength, are implicitly
+// defined by the underlying resource, and cannot be explicitly overridden or
+// removed. See the Terminology section of
+// http://www.webdav.org/specs/rfc4918.html#rfc.section.3
+//
+// There is a whitelist of the names of live properties. This package handles
+// all live properties, and will only pass non-whitelisted names to the Patch
+// method of DeadPropsHolder implementations.
+type DeadPropsHolder interface {
+ // DeadProps returns a copy of the dead properties held.
+ DeadProps() (map[xml.Name]Property, error)
+
+ // Patch patches the dead properties held.
+ //
+ // Patching is atomic; either all or no patches succeed. It returns (nil,
+ // non-nil) if an internal server error occurred, otherwise the Propstats
+ // collectively contain one Property for each proposed patch Property. If
+ // all patches succeed, Patch returns a slice of length one and a Propstat
+ // element with a 200 OK HTTP status code. If none succeed, for reasons
+ // other than an internal server error, no Propstat has status 200 OK.
+ //
+ // For more details on when various HTTP status codes apply, see
+ // http://www.webdav.org/specs/rfc4918.html#PROPPATCH-status
+ Patch([]Proppatch) ([]Propstat, error)
+}
+
+// liveProps contains all supported, protected DAV: properties.
+var liveProps = map[xml.Name]struct {
+ // findFn implements the propfind function of this property. If nil,
+ // it indicates a hidden property.
+ findFn func(FileSystem, LockSystem, string, os.FileInfo) (string, error)
+ // dir is true if the property applies to directories.
+ dir bool
+}{
+ xml.Name{Space: "DAV:", Local: "resourcetype"}: {
+ findFn: findResourceType,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "displayname"}: {
+ findFn: findDisplayName,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "getcontentlength"}: {
+ findFn: findContentLength,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "getlastmodified"}: {
+ findFn: findLastModified,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "creationdate"}: {
+ findFn: nil,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "getcontentlanguage"}: {
+ findFn: nil,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "getcontenttype"}: {
+ findFn: findContentType,
+ dir: true,
+ },
+ xml.Name{Space: "DAV:", Local: "getetag"}: {
+ findFn: findETag,
+ // findETag implements ETag as the concatenated hex values of a file's
+ // modification time and size. This is not a reliable synchronization
+ // mechanism for directories, so we do not advertise getetag for DAV
+ // collections.
+ dir: false,
+ },
+
+ // TODO: The lockdiscovery property requires LockSystem to list the
+ // active locks on a resource.
+ xml.Name{Space: "DAV:", Local: "lockdiscovery"}: {},
+ xml.Name{Space: "DAV:", Local: "supportedlock"}: {
+ findFn: findSupportedLock,
+ dir: true,
+ },
+}
+
+// TODO(nigeltao) merge props and allprop?
+
+// Props returns the status of the properties named pnames for resource name.
+//
+// Each Propstat has a unique status and each property name will only be part
+// of one Propstat element.
+func props(fs FileSystem, ls LockSystem, name string, pnames []xml.Name) ([]Propstat, error) {
+ f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ fi, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ isDir := fi.IsDir()
+
+ var deadProps map[xml.Name]Property
+ if dph, ok := f.(DeadPropsHolder); ok {
+ deadProps, err = dph.DeadProps()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ pstatOK := Propstat{Status: http.StatusOK}
+ pstatNotFound := Propstat{Status: http.StatusNotFound}
+ for _, pn := range pnames {
+ // If this file has dead properties, check if they contain pn.
+ if dp, ok := deadProps[pn]; ok {
+ pstatOK.Props = append(pstatOK.Props, dp)
+ continue
+ }
+ // Otherwise, it must either be a live property or we don't know it.
+ if prop := liveProps[pn]; prop.findFn != nil && (prop.dir || !isDir) {
+ innerXML, err := prop.findFn(fs, ls, name, fi)
+ if err != nil {
+ return nil, err
+ }
+ pstatOK.Props = append(pstatOK.Props, Property{
+ XMLName: pn,
+ InnerXML: []byte(innerXML),
+ })
+ } else {
+ pstatNotFound.Props = append(pstatNotFound.Props, Property{
+ XMLName: pn,
+ })
+ }
+ }
+ return makePropstats(pstatOK, pstatNotFound), nil
+}
+
+// Propnames returns the property names defined for resource name.
+func propnames(fs FileSystem, ls LockSystem, name string) ([]xml.Name, error) {
+ f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ fi, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ isDir := fi.IsDir()
+
+ var deadProps map[xml.Name]Property
+ if dph, ok := f.(DeadPropsHolder); ok {
+ deadProps, err = dph.DeadProps()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ pnames := make([]xml.Name, 0, len(liveProps)+len(deadProps))
+ for pn, prop := range liveProps {
+ if prop.findFn != nil && (prop.dir || !isDir) {
+ pnames = append(pnames, pn)
+ }
+ }
+ for pn := range deadProps {
+ pnames = append(pnames, pn)
+ }
+ return pnames, nil
+}
+
+// Allprop returns the properties defined for resource name and the properties
+// named in include.
+//
+// Note that RFC 4918 defines 'allprop' to return the DAV: properties defined
+// within the RFC plus dead properties. Other live properties should only be
+// returned if they are named in 'include'.
+//
+// See http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
+func allprop(fs FileSystem, ls LockSystem, name string, include []xml.Name) ([]Propstat, error) {
+ pnames, err := propnames(fs, ls, name)
+ if err != nil {
+ return nil, err
+ }
+ // Add names from include if they are not already covered in pnames.
+ nameset := make(map[xml.Name]bool)
+ for _, pn := range pnames {
+ nameset[pn] = true
+ }
+ for _, pn := range include {
+ if !nameset[pn] {
+ pnames = append(pnames, pn)
+ }
+ }
+ return props(fs, ls, name, pnames)
+}
+
+// Patch patches the properties of resource name. The return values are
+// constrained in the same manner as DeadPropsHolder.Patch.
+func patch(fs FileSystem, ls LockSystem, name string, patches []Proppatch) ([]Propstat, error) {
+ conflict := false
+loop:
+ for _, patch := range patches {
+ for _, p := range patch.Props {
+ if _, ok := liveProps[p.XMLName]; ok {
+ conflict = true
+ break loop
+ }
+ }
+ }
+ if conflict {
+ pstatForbidden := Propstat{
+ Status: http.StatusForbidden,
+ XMLError: `<D:cannot-modify-protected-property xmlns:D="DAV:"/>`,
+ }
+ pstatFailedDep := Propstat{
+ Status: StatusFailedDependency,
+ }
+ for _, patch := range patches {
+ for _, p := range patch.Props {
+ if _, ok := liveProps[p.XMLName]; ok {
+ pstatForbidden.Props = append(pstatForbidden.Props, Property{XMLName: p.XMLName})
+ } else {
+ pstatFailedDep.Props = append(pstatFailedDep.Props, Property{XMLName: p.XMLName})
+ }
+ }
+ }
+ return makePropstats(pstatForbidden, pstatFailedDep), nil
+ }
+
+ f, err := fs.OpenFile(name, os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ if dph, ok := f.(DeadPropsHolder); ok {
+ ret, err := dph.Patch(patches)
+ if err != nil {
+ return nil, err
+ }
+ // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat says that
+ // "The contents of the prop XML element must only list the names of
+ // properties to which the result in the status element applies."
+ for _, pstat := range ret {
+ for i, p := range pstat.Props {
+ pstat.Props[i] = Property{XMLName: p.XMLName}
+ }
+ }
+ return ret, nil
+ }
+ // The file doesn't implement the optional DeadPropsHolder interface, so
+ // all patches are forbidden.
+ pstat := Propstat{Status: http.StatusForbidden}
+ for _, patch := range patches {
+ for _, p := range patch.Props {
+ pstat.Props = append(pstat.Props, Property{XMLName: p.XMLName})
+ }
+ }
+ return []Propstat{pstat}, nil
+}
+
+func findResourceType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ if fi.IsDir() {
+ return `<D:collection xmlns:D="DAV:"/>`, nil
+ }
+ return "", nil
+}
+
+func findDisplayName(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ if slashClean(name) == "/" {
+ // Hide the real name of a possibly prefixed root directory.
+ return "", nil
+ }
+ return fi.Name(), nil
+}
+
+func findContentLength(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ return strconv.FormatInt(fi.Size(), 10), nil
+}
+
+func findLastModified(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ return fi.ModTime().Format(http.TimeFormat), nil
+}
+
+func findContentType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+ // This implementation is based on serveContent's code in the standard net/http package.
+ ctype := mime.TypeByExtension(filepath.Ext(name))
+ if ctype == "" {
+ // Read a chunk to decide between utf-8 text and binary.
+ var buf [512]byte
+ n, _ := io.ReadFull(f, buf[:])
+ ctype = http.DetectContentType(buf[:n])
+ // Rewind file.
+ _, err = f.Seek(0, os.SEEK_SET)
+ }
+ return ctype, err
+}
+
+func findETag(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ // The Apache http 2.4 web server by default concatenates the
+ // modification time and size of a file. We replicate the heuristic
+ // with nanosecond granularity.
+ return fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size()), nil
+}
+
+func findSupportedLock(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ return `` +
+ `<D:lockentry xmlns:D="DAV:">` +
+ `<D:lockscope><D:exclusive/></D:lockscope>` +
+ `<D:locktype><D:write/></D:locktype>` +
+ `</D:lockentry>`, nil
+}
diff --git a/webdav/prop_test.go b/webdav/prop_test.go
new file mode 100644
index 0000000..3ad19f4
--- /dev/null
+++ b/webdav/prop_test.go
@@ -0,0 +1,619 @@
+// Copyright 2015 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.
+
+package webdav
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "reflect"
+ "sort"
+ "testing"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+func TestMemPS(t *testing.T) {
+ // calcProps calculates the getlastmodified and getetag DAV: property
+ // values in pstats for resource name in file-system fs.
+ calcProps := func(name string, fs FileSystem, ls LockSystem, pstats []Propstat) error {
+ fi, err := fs.Stat(name)
+ if err != nil {
+ return err
+ }
+ for _, pst := range pstats {
+ for i, p := range pst.Props {
+ switch p.XMLName {
+ case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
+ p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
+ pst.Props[i] = p
+ case xml.Name{Space: "DAV:", Local: "getetag"}:
+ if fi.IsDir() {
+ continue
+ }
+ etag, err := findETag(fs, ls, name, fi)
+ if err != nil {
+ return err
+ }
+ p.InnerXML = []byte(etag)
+ pst.Props[i] = p
+ }
+ }
+ }
+ return nil
+ }
+
+ const (
+ lockEntry = `` +
+ `<D:lockentry xmlns:D="DAV:">` +
+ `<D:lockscope><D:exclusive/></D:lockscope>` +
+ `<D:locktype><D:write/></D:locktype>` +
+ `</D:lockentry>`
+ statForbiddenError = `<D:cannot-modify-protected-property xmlns:D="DAV:"/>`
+ )
+
+ type propOp struct {
+ op string
+ name string
+ pnames []xml.Name
+ patches []Proppatch
+ wantPnames []xml.Name
+ wantPropstats []Propstat
+ }
+
+ testCases := []struct {
+ desc string
+ noDeadProps bool
+ buildfs []string
+ propOp []propOp
+ }{{
+ desc: "propname",
+ buildfs: []string{"mkdir /dir", "touch /file"},
+ propOp: []propOp{{
+ op: "propname",
+ name: "/dir",
+ wantPnames: []xml.Name{
+ xml.Name{Space: "DAV:", Local: "resourcetype"},
+ xml.Name{Space: "DAV:", Local: "displayname"},
+ xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ xml.Name{Space: "DAV:", Local: "supportedlock"},
+ },
+ }, {
+ op: "propname",
+ name: "/file",
+ wantPnames: []xml.Name{
+ xml.Name{Space: "DAV:", Local: "resourcetype"},
+ xml.Name{Space: "DAV:", Local: "displayname"},
+ xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ xml.Name{Space: "DAV:", Local: "getetag"},
+ xml.Name{Space: "DAV:", Local: "supportedlock"},
+ },
+ }},
+ }, {
+ desc: "allprop dir and file",
+ buildfs: []string{"mkdir /dir", "write /file foobarbaz"},
+ propOp: []propOp{{
+ op: "allprop",
+ name: "/dir",
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
+ InnerXML: []byte(`<D:collection xmlns:D="DAV:"/>`),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
+ InnerXML: []byte("dir"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ InnerXML: []byte("0"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ InnerXML: nil, // Calculated during test.
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ InnerXML: []byte("text/plain; charset=utf-8"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
+ InnerXML: []byte(lockEntry),
+ }},
+ }},
+ }, {
+ op: "allprop",
+ name: "/file",
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
+ InnerXML: []byte(""),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
+ InnerXML: []byte("file"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ InnerXML: []byte("9"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ InnerXML: nil, // Calculated during test.
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ InnerXML: []byte("text/plain; charset=utf-8"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ InnerXML: nil, // Calculated during test.
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
+ InnerXML: []byte(lockEntry),
+ }},
+ }},
+ }, {
+ op: "allprop",
+ name: "/file",
+ pnames: []xml.Name{
+ {"DAV:", "resourcetype"},
+ {"foo", "bar"},
+ },
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
+ InnerXML: []byte(""),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
+ InnerXML: []byte("file"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ InnerXML: []byte("9"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ InnerXML: nil, // Calculated during test.
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ InnerXML: []byte("text/plain; charset=utf-8"),
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ InnerXML: nil, // Calculated during test.
+ }, {
+ XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
+ InnerXML: []byte(lockEntry),
+ }}}, {
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }}},
+ },
+ }},
+ }, {
+ desc: "propfind DAV:resourcetype",
+ buildfs: []string{"mkdir /dir", "touch /file"},
+ propOp: []propOp{{
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{"DAV:", "resourcetype"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
+ InnerXML: []byte(`<D:collection xmlns:D="DAV:"/>`),
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/file",
+ pnames: []xml.Name{{"DAV:", "resourcetype"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
+ InnerXML: []byte(""),
+ }},
+ }},
+ }},
+ }, {
+ desc: "propfind unsupported DAV properties",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{"DAV:", "getcontentlanguage"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "getcontentlanguage"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{"DAV:", "creationdate"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "creationdate"},
+ }},
+ }},
+ }},
+ }, {
+ desc: "propfind getetag for files but not for directories",
+ buildfs: []string{"mkdir /dir", "touch /file"},
+ propOp: []propOp{{
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{"DAV:", "getetag"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/file",
+ pnames: []xml.Name{{"DAV:", "getetag"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ InnerXML: nil, // Calculated during test.
+ }},
+ }},
+ }},
+ }, {
+ desc: "proppatch property on no-dead-properties file system",
+ buildfs: []string{"mkdir /dir"},
+ noDeadProps: true,
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusForbidden,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }, {
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusForbidden,
+ XMLError: statForbiddenError,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
+ }},
+ }},
+ }},
+ }, {
+ desc: "proppatch dead property",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{Space: "foo", Local: "bar"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }},
+ }},
+ }},
+ }, {
+ desc: "proppatch dead property with failed dependency",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }},
+ }, {
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
+ InnerXML: []byte("xxx"),
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusForbidden,
+ XMLError: statForbiddenError,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
+ }},
+ }, {
+ Status: StatusFailedDependency,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{Space: "foo", Local: "bar"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }},
+ }, {
+ desc: "proppatch remove dead property",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }, {
+ XMLName: xml.Name{Space: "spam", Local: "ham"},
+ InnerXML: []byte("eggs"),
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }, {
+ XMLName: xml.Name{Space: "spam", Local: "ham"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{
+ {Space: "foo", Local: "bar"},
+ {Space: "spam", Local: "ham"},
+ },
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }, {
+ XMLName: xml.Name{Space: "spam", Local: "ham"},
+ InnerXML: []byte("eggs"),
+ }},
+ }},
+ }, {
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Remove: true,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }, {
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{
+ {Space: "foo", Local: "bar"},
+ {Space: "spam", Local: "ham"},
+ },
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }, {
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "spam", Local: "ham"},
+ InnerXML: []byte("eggs"),
+ }},
+ }},
+ }},
+ }, {
+ desc: "propname with dead property",
+ buildfs: []string{"touch /file"},
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/file",
+ patches: []Proppatch{{
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ InnerXML: []byte("baz"),
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }, {
+ op: "propname",
+ name: "/file",
+ wantPnames: []xml.Name{
+ xml.Name{Space: "DAV:", Local: "resourcetype"},
+ xml.Name{Space: "DAV:", Local: "displayname"},
+ xml.Name{Space: "DAV:", Local: "getcontentlength"},
+ xml.Name{Space: "DAV:", Local: "getlastmodified"},
+ xml.Name{Space: "DAV:", Local: "getcontenttype"},
+ xml.Name{Space: "DAV:", Local: "getetag"},
+ xml.Name{Space: "DAV:", Local: "supportedlock"},
+ xml.Name{Space: "foo", Local: "bar"},
+ },
+ }},
+ }, {
+ desc: "proppatch remove unknown dead property",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "proppatch",
+ name: "/dir",
+ patches: []Proppatch{{
+ Remove: true,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ wantPropstats: []Propstat{{
+ Status: http.StatusOK,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo", Local: "bar"},
+ }},
+ }},
+ }},
+ }, {
+ desc: "bad: propfind unknown property",
+ buildfs: []string{"mkdir /dir"},
+ propOp: []propOp{{
+ op: "propfind",
+ name: "/dir",
+ pnames: []xml.Name{{"foo:", "bar"}},
+ wantPropstats: []Propstat{{
+ Status: http.StatusNotFound,
+ Props: []Property{{
+ XMLName: xml.Name{Space: "foo:", Local: "bar"},
+ }},
+ }},
+ }},
+ }}
+
+ for _, tc := range testCases {
+ fs, err := buildTestFS(tc.buildfs)
+ if err != nil {
+ t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
+ }
+ if tc.noDeadProps {
+ fs = noDeadPropsFS{fs}
+ }
+ ls := NewMemLS()
+ for _, op := range tc.propOp {
+ desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name)
+ if err = calcProps(op.name, fs, ls, op.wantPropstats); err != nil {
+ t.Fatalf("%s: calcProps: %v", desc, err)
+ }
+
+ // Call property system.
+ var propstats []Propstat
+ switch op.op {
+ case "propname":
+ pnames, err := propnames(fs, ls, op.name)
+ if err != nil {
+ t.Errorf("%s: got error %v, want nil", desc, err)
+ continue
+ }
+ sort.Sort(byXMLName(pnames))
+ sort.Sort(byXMLName(op.wantPnames))
+ if !reflect.DeepEqual(pnames, op.wantPnames) {
+ t.Errorf("%s: pnames\ngot %q\nwant %q", desc, pnames, op.wantPnames)
+ }
+ continue
+ case "allprop":
+ propstats, err = allprop(fs, ls, op.name, op.pnames)
+ case "propfind":
+ propstats, err = props(fs, ls, op.name, op.pnames)
+ case "proppatch":
+ propstats, err = patch(fs, ls, op.name, op.patches)
+ default:
+ t.Fatalf("%s: %s not implemented", desc, op.op)
+ }
+ if err != nil {
+ t.Errorf("%s: got error %v, want nil", desc, err)
+ continue
+ }
+ // Compare return values from allprop, propfind or proppatch.
+ for _, pst := range propstats {
+ sort.Sort(byPropname(pst.Props))
+ }
+ for _, pst := range op.wantPropstats {
+ sort.Sort(byPropname(pst.Props))
+ }
+ sort.Sort(byStatus(propstats))
+ sort.Sort(byStatus(op.wantPropstats))
+ if !reflect.DeepEqual(propstats, op.wantPropstats) {
+ t.Errorf("%s: propstat\ngot %q\nwant %q", desc, propstats, op.wantPropstats)
+ }
+ }
+ }
+}
+
+func cmpXMLName(a, b xml.Name) bool {
+ if a.Space != b.Space {
+ return a.Space < b.Space
+ }
+ return a.Local < b.Local
+}
+
+type byXMLName []xml.Name
+
+func (b byXMLName) Len() int { return len(b) }
+func (b byXMLName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byXMLName) Less(i, j int) bool { return cmpXMLName(b[i], b[j]) }
+
+type byPropname []Property
+
+func (b byPropname) Len() int { return len(b) }
+func (b byPropname) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byPropname) Less(i, j int) bool { return cmpXMLName(b[i].XMLName, b[j].XMLName) }
+
+type byStatus []Propstat
+
+func (b byStatus) Len() int { return len(b) }
+func (b byStatus) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b byStatus) Less(i, j int) bool { return b[i].Status < b[j].Status }
+
+type noDeadPropsFS struct {
+ FileSystem
+}
+
+func (fs noDeadPropsFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ f, err := fs.FileSystem.OpenFile(name, flag, perm)
+ if err != nil {
+ return nil, err
+ }
+ return noDeadPropsFile{f}, nil
+}
+
+// noDeadPropsFile wraps a File but strips any optional DeadPropsHolder methods
+// provided by the underlying File implementation.
+type noDeadPropsFile struct {
+ f File
+}
+
+func (f noDeadPropsFile) Close() error { return f.f.Close() }
+func (f noDeadPropsFile) Read(p []byte) (int, error) { return f.f.Read(p) }
+func (f noDeadPropsFile) Readdir(count int) ([]os.FileInfo, error) { return f.f.Readdir(count) }
+func (f noDeadPropsFile) Seek(off int64, whence int) (int64, error) { return f.f.Seek(off, whence) }
+func (f noDeadPropsFile) Stat() (os.FileInfo, error) { return f.f.Stat() }
+func (f noDeadPropsFile) Write(p []byte) (int, error) { return f.f.Write(p) }
diff --git a/webdav/webdav.go b/webdav/webdav.go
new file mode 100644
index 0000000..9bc4543
--- /dev/null
+++ b/webdav/webdav.go
@@ -0,0 +1,674 @@
+// Copyright 2014 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.
+
+// Package webdav etc etc TODO.
+package webdav // import "golang.org/x/net/webdav"
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+)
+
+// Package webdav's XML output requires the standard library's encoding/xml
+// package version 1.5 or greater. Otherwise, it will produce malformed XML.
+//
+// As of May 2015, the Go stable release is version 1.4, so we print a message
+// to let users know that this golang.org/x/etc package won't work yet.
+//
+// This package also won't work with Go 1.3 and earlier, but making this
+// runtime version check catch all the earlier versions too, and not just
+// "1.4.x", isn't worth the complexity.
+//
+// TODO: delete this check at some point after Go 1.5 is released.
+var go1Dot4 = strings.HasPrefix(runtime.Version(), "go1.4.")
+
+func init() {
+ if go1Dot4 {
+ log.Println("package webdav requires Go version 1.5 or greater")
+ }
+}
+
+type Handler struct {
+ // FileSystem is the virtual file system.
+ FileSystem FileSystem
+ // LockSystem is the lock management system.
+ LockSystem LockSystem
+ // Logger is an optional error logger. If non-nil, it will be called
+ // for all HTTP requests.
+ Logger func(*http.Request, error)
+}
+
+func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ status, err := http.StatusBadRequest, errUnsupportedMethod
+ if h.FileSystem == nil {
+ status, err = http.StatusInternalServerError, errNoFileSystem
+ } else if h.LockSystem == nil {
+ status, err = http.StatusInternalServerError, errNoLockSystem
+ } else {
+ switch r.Method {
+ case "OPTIONS":
+ status, err = h.handleOptions(w, r)
+ case "GET", "HEAD", "POST":
+ status, err = h.handleGetHeadPost(w, r)
+ case "DELETE":
+ status, err = h.handleDelete(w, r)
+ case "PUT":
+ status, err = h.handlePut(w, r)
+ case "MKCOL":
+ status, err = h.handleMkcol(w, r)
+ case "COPY", "MOVE":
+ status, err = h.handleCopyMove(w, r)
+ case "LOCK":
+ status, err = h.handleLock(w, r)
+ case "UNLOCK":
+ status, err = h.handleUnlock(w, r)
+ case "PROPFIND":
+ status, err = h.handlePropfind(w, r)
+ case "PROPPATCH":
+ status, err = h.handleProppatch(w, r)
+ }
+ }
+
+ if status != 0 {
+ w.WriteHeader(status)
+ if status != http.StatusNoContent {
+ w.Write([]byte(StatusText(status)))
+ }
+ }
+ if h.Logger != nil {
+ h.Logger(r, err)
+ }
+}
+
+func (h *Handler) lock(now time.Time, root string) (token string, status int, err error) {
+ token, err = h.LockSystem.Create(now, LockDetails{
+ Root: root,
+ Duration: infiniteTimeout,
+ ZeroDepth: true,
+ })
+ if err != nil {
+ if err == ErrLocked {
+ return "", StatusLocked, err
+ }
+ return "", http.StatusInternalServerError, err
+ }
+ return token, 0, nil
+}
+
+func (h *Handler) confirmLocks(r *http.Request, src, dst string) (release func(), status int, err error) {
+ hdr := r.Header.Get("If")
+ if hdr == "" {
+ // An empty If header means that the client hasn't previously created locks.
+ // Even if this client doesn't care about locks, we still need to check that
+ // the resources aren't locked by another client, so we create temporary
+ // locks that would conflict with another client's locks. These temporary
+ // locks are unlocked at the end of the HTTP request.
+ now, srcToken, dstToken := time.Now(), "", ""
+ if src != "" {
+ srcToken, status, err = h.lock(now, src)
+ if err != nil {
+ return nil, status, err
+ }
+ }
+ if dst != "" {
+ dstToken, status, err = h.lock(now, dst)
+ if err != nil {
+ if srcToken != "" {
+ h.LockSystem.Unlock(now, srcToken)
+ }
+ return nil, status, err
+ }
+ }
+
+ return func() {
+ if dstToken != "" {
+ h.LockSystem.Unlock(now, dstToken)
+ }
+ if srcToken != "" {
+ h.LockSystem.Unlock(now, srcToken)
+ }
+ }, 0, nil
+ }
+
+ ih, ok := parseIfHeader(hdr)
+ if !ok {
+ return nil, http.StatusBadRequest, errInvalidIfHeader
+ }
+ // ih is a disjunction (OR) of ifLists, so any ifList will do.
+ for _, l := range ih.lists {
+ lsrc := l.resourceTag
+ if lsrc == "" {
+ lsrc = src
+ } else {
+ u, err := url.Parse(lsrc)
+ if err != nil {
+ continue
+ }
+ if u.Host != r.Host {
+ continue
+ }
+ lsrc = u.Path
+ }
+ release, err = h.LockSystem.Confirm(time.Now(), lsrc, dst, l.conditions...)
+ if err == ErrConfirmationFailed {
+ continue
+ }
+ if err != nil {
+ return nil, http.StatusInternalServerError, err
+ }
+ return release, 0, nil
+ }
+ // Section 10.4.1 says that "If this header is evaluated and all state lists
+ // fail, then the request must fail with a 412 (Precondition Failed) status."
+ // We follow the spec even though the cond_put_corrupt_token test case from
+ // the litmus test warns on seeing a 412 instead of a 423 (Locked).
+ return nil, http.StatusPreconditionFailed, ErrLocked
+}
+
+func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ allow := "OPTIONS, LOCK, PUT, MKCOL"
+ if fi, err := h.FileSystem.Stat(r.URL.Path); err == nil {
+ if fi.IsDir() {
+ allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND"
+ } else {
+ allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND, PUT"
+ }
+ }
+ w.Header().Set("Allow", allow)
+ // http://www.webdav.org/specs/rfc4918.html#dav.compliance.classes
+ w.Header().Set("DAV", "1, 2")
+ // http://msdn.microsoft.com/en-au/library/cc250217.aspx
+ w.Header().Set("MS-Author-Via", "DAV")
+ return 0, nil
+}
+
+func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ // TODO: check locks for read-only access??
+ f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDONLY, 0)
+ if err != nil {
+ return http.StatusNotFound, err
+ }
+ defer f.Close()
+ fi, err := f.Stat()
+ if err != nil {
+ return http.StatusNotFound, err
+ }
+ if !fi.IsDir() {
+ etag, err := findETag(h.FileSystem, h.LockSystem, r.URL.Path, fi)
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ w.Header().Set("ETag", etag)
+ }
+ // Let ServeContent determine the Content-Type header.
+ http.ServeContent(w, r, r.URL.Path, fi.ModTime(), f)
+ return 0, nil
+}
+
+func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ release, status, err := h.confirmLocks(r, r.URL.Path, "")
+ if err != nil {
+ return status, err
+ }
+ defer release()
+
+ // TODO: return MultiStatus where appropriate.
+
+ // "godoc os RemoveAll" says that "If the path does not exist, RemoveAll
+ // returns nil (no error)." WebDAV semantics are that it should return a
+ // "404 Not Found". We therefore have to Stat before we RemoveAll.
+ if _, err := h.FileSystem.Stat(r.URL.Path); err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusMethodNotAllowed, err
+ }
+ if err := h.FileSystem.RemoveAll(r.URL.Path); err != nil {
+ return http.StatusMethodNotAllowed, err
+ }
+ return http.StatusNoContent, nil
+}
+
+func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ release, status, err := h.confirmLocks(r, r.URL.Path, "")
+ if err != nil {
+ return status, err
+ }
+ defer release()
+ // TODO(rost): Support the If-Match, If-None-Match headers? See bradfitz'
+ // comments in http.checkEtag.
+
+ f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ return http.StatusNotFound, err
+ }
+ _, copyErr := io.Copy(f, r.Body)
+ fi, statErr := f.Stat()
+ closeErr := f.Close()
+ // TODO(rost): Returning 405 Method Not Allowed might not be appropriate.
+ if copyErr != nil {
+ return http.StatusMethodNotAllowed, copyErr
+ }
+ if statErr != nil {
+ return http.StatusMethodNotAllowed, statErr
+ }
+ if closeErr != nil {
+ return http.StatusMethodNotAllowed, closeErr
+ }
+ etag, err := findETag(h.FileSystem, h.LockSystem, r.URL.Path, fi)
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ w.Header().Set("ETag", etag)
+ return http.StatusCreated, nil
+}
+
+func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ release, status, err := h.confirmLocks(r, r.URL.Path, "")
+ if err != nil {
+ return status, err
+ }
+ defer release()
+
+ if r.ContentLength > 0 {
+ return http.StatusUnsupportedMediaType, nil
+ }
+ if err := h.FileSystem.Mkdir(r.URL.Path, 0777); err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusConflict, err
+ }
+ return http.StatusMethodNotAllowed, err
+ }
+ return http.StatusCreated, nil
+}
+
+func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ hdr := r.Header.Get("Destination")
+ if hdr == "" {
+ return http.StatusBadRequest, errInvalidDestination
+ }
+ u, err := url.Parse(hdr)
+ if err != nil {
+ return http.StatusBadRequest, errInvalidDestination
+ }
+ if u.Host != r.Host {
+ return http.StatusBadGateway, errInvalidDestination
+ }
+
+ dst, src := u.Path, r.URL.Path
+ if dst == "" {
+ return http.StatusBadGateway, errInvalidDestination
+ }
+ if dst == src {
+ return http.StatusForbidden, errDestinationEqualsSource
+ }
+
+ if r.Method == "COPY" {
+ // Section 7.5.1 says that a COPY only needs to lock the destination,
+ // not both destination and source. Strictly speaking, this is racy,
+ // even though a COPY doesn't modify the source, if a concurrent
+ // operation modifies the source. However, the litmus test explicitly
+ // checks that COPYing a locked-by-another source is OK.
+ release, status, err := h.confirmLocks(r, "", dst)
+ if err != nil {
+ return status, err
+ }
+ defer release()
+
+ // Section 9.8.3 says that "The COPY method on a collection without a Depth
+ // header must act as if a Depth header with value "infinity" was included".
+ depth := infiniteDepth
+ if hdr := r.Header.Get("Depth"); hdr != "" {
+ depth = parseDepth(hdr)
+ if depth != 0 && depth != infiniteDepth {
+ // Section 9.8.3 says that "A client may submit a Depth header on a
+ // COPY on a collection with a value of "0" or "infinity"."
+ return http.StatusBadRequest, errInvalidDepth
+ }
+ }
+ return copyFiles(h.FileSystem, src, dst, r.Header.Get("Overwrite") != "F", depth, 0)
+ }
+
+ release, status, err := h.confirmLocks(r, src, dst)
+ if err != nil {
+ return status, err
+ }
+ defer release()
+
+ // Section 9.9.2 says that "The MOVE method on a collection must act as if
+ // a "Depth: infinity" header was used on it. A client must not submit a
+ // Depth header on a MOVE on a collection with any value but "infinity"."
+ if hdr := r.Header.Get("Depth"); hdr != "" {
+ if parseDepth(hdr) != infiniteDepth {
+ return http.StatusBadRequest, errInvalidDepth
+ }
+ }
+ return moveFiles(h.FileSystem, src, dst, r.Header.Get("Overwrite") == "T")
+}
+
+func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus int, retErr error) {
+ duration, err := parseTimeout(r.Header.Get("Timeout"))
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+ li, status, err := readLockInfo(r.Body)
+ if err != nil {
+ return status, err
+ }
+
+ token, ld, now, created := "", LockDetails{}, time.Now(), false
+ if li == (lockInfo{}) {
+ // An empty lockInfo means to refresh the lock.
+ ih, ok := parseIfHeader(r.Header.Get("If"))
+ if !ok {
+ return http.StatusBadRequest, errInvalidIfHeader
+ }
+ if len(ih.lists) == 1 && len(ih.lists[0].conditions) == 1 {
+ token = ih.lists[0].conditions[0].Token
+ }
+ if token == "" {
+ return http.StatusBadRequest, errInvalidLockToken
+ }
+ ld, err = h.LockSystem.Refresh(now, token, duration)
+ if err != nil {
+ if err == ErrNoSuchLock {
+ return http.StatusPreconditionFailed, err
+ }
+ return http.StatusInternalServerError, err
+ }
+
+ } else {
+ // Section 9.10.3 says that "If no Depth header is submitted on a LOCK request,
+ // then the request MUST act as if a "Depth:infinity" had been submitted."
+ depth := infiniteDepth
+ if hdr := r.Header.Get("Depth"); hdr != "" {
+ depth = parseDepth(hdr)
+ if depth != 0 && depth != infiniteDepth {
+ // Section 9.10.3 says that "Values other than 0 or infinity must not be
+ // used with the Depth header on a LOCK method".
+ return http.StatusBadRequest, errInvalidDepth
+ }
+ }
+ ld = LockDetails{
+ Root: r.URL.Path,
+ Duration: duration,
+ OwnerXML: li.Owner.InnerXML,
+ ZeroDepth: depth == 0,
+ }
+ token, err = h.LockSystem.Create(now, ld)
+ if err != nil {
+ if err == ErrLocked {
+ return StatusLocked, err
+ }
+ return http.StatusInternalServerError, err
+ }
+ defer func() {
+ if retErr != nil {
+ h.LockSystem.Unlock(now, token)
+ }
+ }()
+
+ // Create the resource if it didn't previously exist.
+ if _, err := h.FileSystem.Stat(r.URL.Path); err != nil {
+ f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+ if err != nil {
+ // TODO: detect missing intermediate dirs and return http.StatusConflict?
+ return http.StatusInternalServerError, err
+ }
+ f.Close()
+ created = true
+ }
+
+ // http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
+ // Lock-Token value is a Coded-URL. We add angle brackets.
+ w.Header().Set("Lock-Token", "<"+token+">")
+ }
+
+ w.Header().Set("Content-Type", "application/xml; charset=utf-8")
+ if created {
+ // This is "w.WriteHeader(http.StatusCreated)" and not "return
+ // http.StatusCreated, nil" because we write our own (XML) response to w
+ // and Handler.ServeHTTP would otherwise write "Created".
+ w.WriteHeader(http.StatusCreated)
+ }
+ writeLockInfo(w, token, ld)
+ return 0, nil
+}
+
+func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ // http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
+ // Lock-Token value is a Coded-URL. We strip its angle brackets.
+ t := r.Header.Get("Lock-Token")
+ if len(t) < 2 || t[0] != '<' || t[len(t)-1] != '>' {
+ return http.StatusBadRequest, errInvalidLockToken
+ }
+ t = t[1 : len(t)-1]
+
+ switch err = h.LockSystem.Unlock(time.Now(), t); err {
+ case nil:
+ return http.StatusNoContent, err
+ case ErrForbidden:
+ return http.StatusForbidden, err
+ case ErrLocked:
+ return StatusLocked, err
+ case ErrNoSuchLock:
+ return http.StatusConflict, err
+ default:
+ return http.StatusInternalServerError, err
+ }
+}
+
+func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ fi, err := h.FileSystem.Stat(r.URL.Path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusMethodNotAllowed, err
+ }
+ depth := infiniteDepth
+ if hdr := r.Header.Get("Depth"); hdr != "" {
+ depth = parseDepth(hdr)
+ if depth == invalidDepth {
+ return http.StatusBadRequest, errInvalidDepth
+ }
+ }
+ pf, status, err := readPropfind(r.Body)
+ if err != nil {
+ return status, err
+ }
+
+ mw := multistatusWriter{w: w}
+
+ walkFn := func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ var pstats []Propstat
+ if pf.Propname != nil {
+ pnames, err := propnames(h.FileSystem, h.LockSystem, path)
+ if err != nil {
+ return err
+ }
+ pstat := Propstat{Status: http.StatusOK}
+ for _, xmlname := range pnames {
+ pstat.Props = append(pstat.Props, Property{XMLName: xmlname})
+ }
+ pstats = append(pstats, pstat)
+ } else if pf.Allprop != nil {
+ pstats, err = allprop(h.FileSystem, h.LockSystem, path, pf.Prop)
+ } else {
+ pstats, err = props(h.FileSystem, h.LockSystem, path, pf.Prop)
+ }
+ if err != nil {
+ return err
+ }
+ return mw.write(makePropstatResponse(path, pstats))
+ }
+
+ walkErr := walkFS(h.FileSystem, depth, r.URL.Path, fi, walkFn)
+ closeErr := mw.close()
+ if walkErr != nil {
+ return http.StatusInternalServerError, walkErr
+ }
+ if closeErr != nil {
+ return http.StatusInternalServerError, closeErr
+ }
+ return 0, nil
+}
+
+func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (status int, err error) {
+ release, status, err := h.confirmLocks(r, r.URL.Path, "")
+ if err != nil {
+ return status, err
+ }
+ defer release()
+
+ if _, err := h.FileSystem.Stat(r.URL.Path); err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusMethodNotAllowed, err
+ }
+ patches, status, err := readProppatch(r.Body)
+ if err != nil {
+ return status, err
+ }
+ pstats, err := patch(h.FileSystem, h.LockSystem, r.URL.Path, patches)
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ mw := multistatusWriter{w: w}
+ writeErr := mw.write(makePropstatResponse(r.URL.Path, pstats))
+ closeErr := mw.close()
+ if writeErr != nil {
+ return http.StatusInternalServerError, writeErr
+ }
+ if closeErr != nil {
+ return http.StatusInternalServerError, closeErr
+ }
+ return 0, nil
+}
+
+func makePropstatResponse(href string, pstats []Propstat) *response {
+ resp := response{
+ Href: []string{href},
+ Propstat: make([]propstat, 0, len(pstats)),
+ }
+ for _, p := range pstats {
+ var xmlErr *xmlError
+ if p.XMLError != "" {
+ xmlErr = &xmlError{InnerXML: []byte(p.XMLError)}
+ }
+ resp.Propstat = append(resp.Propstat, propstat{
+ Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, StatusText(p.Status)),
+ Prop: p.Props,
+ ResponseDescription: p.ResponseDescription,
+ Error: xmlErr,
+ })
+ }
+ return &resp
+}
+
+const (
+ infiniteDepth = -1
+ invalidDepth = -2
+)
+
+// parseDepth maps the strings "0", "1" and "infinity" to 0, 1 and
+// infiniteDepth. Parsing any other string returns invalidDepth.
+//
+// Different WebDAV methods have further constraints on valid depths:
+// - PROPFIND has no further restrictions, as per section 9.1.
+// - COPY accepts only "0" or "infinity", as per section 9.8.3.
+// - MOVE accepts only "infinity", as per section 9.9.2.
+// - LOCK accepts only "0" or "infinity", as per section 9.10.3.
+// These constraints are enforced by the handleXxx methods.
+func parseDepth(s string) int {
+ switch s {
+ case "0":
+ return 0
+ case "1":
+ return 1
+ case "infinity":
+ return infiniteDepth
+ }
+ return invalidDepth
+}
+
+// StripPrefix is like http.StripPrefix but it also strips the prefix from any
+// Destination headers, so that COPY and MOVE requests also see stripped paths.
+func StripPrefix(prefix string, h http.Handler) http.Handler {
+ if prefix == "" {
+ return h
+ }
+ h = http.StripPrefix(prefix, h)
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ dsts := r.Header["Destination"]
+ for i, dst := range dsts {
+ u, err := url.Parse(dst)
+ if err != nil {
+ continue
+ }
+ if p := strings.TrimPrefix(u.Path, prefix); len(p) < len(u.Path) {
+ u.Path = p
+ dsts[i] = u.String()
+ }
+ }
+ h.ServeHTTP(w, r)
+ })
+}
+
+// http://www.webdav.org/specs/rfc4918.html#status.code.extensions.to.http11
+const (
+ StatusMulti = 207
+ StatusUnprocessableEntity = 422
+ StatusLocked = 423
+ StatusFailedDependency = 424
+ StatusInsufficientStorage = 507
+)
+
+func StatusText(code int) string {
+ switch code {
+ case StatusMulti:
+ return "Multi-Status"
+ case StatusUnprocessableEntity:
+ return "Unprocessable Entity"
+ case StatusLocked:
+ return "Locked"
+ case StatusFailedDependency:
+ return "Failed Dependency"
+ case StatusInsufficientStorage:
+ return "Insufficient Storage"
+ }
+ return http.StatusText(code)
+}
+
+var (
+ errDestinationEqualsSource = errors.New("webdav: destination equals source")
+ errDirectoryNotEmpty = errors.New("webdav: directory not empty")
+ errInvalidDepth = errors.New("webdav: invalid depth")
+ errInvalidDestination = errors.New("webdav: invalid destination")
+ errInvalidIfHeader = errors.New("webdav: invalid If header")
+ errInvalidLockInfo = errors.New("webdav: invalid lock info")
+ errInvalidLockToken = errors.New("webdav: invalid lock token")
+ errInvalidPropfind = errors.New("webdav: invalid propfind")
+ errInvalidProppatch = errors.New("webdav: invalid proppatch")
+ errInvalidResponse = errors.New("webdav: invalid response")
+ errInvalidTimeout = errors.New("webdav: invalid timeout")
+ errNoFileSystem = errors.New("webdav: no file system")
+ errNoLockSystem = errors.New("webdav: no lock system")
+ errNotADirectory = errors.New("webdav: not a directory")
+ errRecursionTooDeep = errors.New("webdav: recursion too deep")
+ errUnsupportedLockInfo = errors.New("webdav: unsupported lock info")
+ errUnsupportedMethod = errors.New("webdav: unsupported method")
+)
diff --git a/webdav/webdav_test.go b/webdav/webdav_test.go
new file mode 100644
index 0000000..c422860
--- /dev/null
+++ b/webdav/webdav_test.go
@@ -0,0 +1,161 @@
+// Copyright 2015 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.
+
+package webdav
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+)
+
+// TestStripPrefix tests the StripPrefix function. We can't test the
+// StripPrefix function with the litmus test, even though all of the litmus
+// test paths start with "/litmus/", because one of the first things that the
+// litmus test does is "MKCOL /litmus/". That request succeeds without a
+// StripPrefix, but fails with a StripPrefix because you cannot MKCOL the root
+// directory of a FileSystem.
+func TestStripPrefix(t *testing.T) {
+ const dst, blah = "Destination", "blah blah blah"
+
+ do := func(method, urlStr string, body io.Reader, wantStatusCode int, headers ...string) error {
+ req, err := http.NewRequest(method, urlStr, body)
+ if err != nil {
+ return err
+ }
+ for len(headers) >= 2 {
+ req.Header.Add(headers[0], headers[1])
+ headers = headers[2:]
+ }
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+ if res.StatusCode != wantStatusCode {
+ return fmt.Errorf("got status code %d, want %d", res.StatusCode, wantStatusCode)
+ }
+ return nil
+ }
+
+ prefixes := []string{
+ "/",
+ "/a/",
+ "/a/b/",
+ "/a/b/c/",
+ }
+ for _, prefix := range prefixes {
+ fs := NewMemFS()
+ h := http.Handler(&Handler{
+ FileSystem: fs,
+ LockSystem: NewMemLS(),
+ })
+ mux := http.NewServeMux()
+ if prefix != "/" {
+ // Note that this is webdav.StripPrefix, not http.StripPrefix.
+ h = StripPrefix(prefix, h)
+ }
+ mux.Handle(prefix, h)
+ srv := httptest.NewServer(mux)
+ defer srv.Close()
+
+ // The script is:
+ // MKCOL /a
+ // MKCOL /a/b
+ // PUT /a/b/c
+ // COPY /a/b/c /a/b/d
+ // MKCOL /a/b/e
+ // MOVE /a/b/d /a/b/e/f
+ // which should yield the (possibly stripped) filenames /a/b/c and
+ // /a/b/e/f, plus their parent directories.
+
+ wantA := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusMovedPermanently,
+ "/a/b/": http.StatusNotFound,
+ "/a/b/c/": http.StatusNotFound,
+ }[prefix]
+ if err := do("MKCOL", srv.URL+"/a", nil, wantA); err != nil {
+ t.Errorf("prefix=%-9q MKCOL /a: %v", prefix, err)
+ continue
+ }
+
+ wantB := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusCreated,
+ "/a/b/": http.StatusMovedPermanently,
+ "/a/b/c/": http.StatusNotFound,
+ }[prefix]
+ if err := do("MKCOL", srv.URL+"/a/b", nil, wantB); err != nil {
+ t.Errorf("prefix=%-9q MKCOL /a/b: %v", prefix, err)
+ continue
+ }
+
+ wantC := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusCreated,
+ "/a/b/": http.StatusCreated,
+ "/a/b/c/": http.StatusMovedPermanently,
+ }[prefix]
+ if err := do("PUT", srv.URL+"/a/b/c", strings.NewReader(blah), wantC); err != nil {
+ t.Errorf("prefix=%-9q PUT /a/b/c: %v", prefix, err)
+ continue
+ }
+
+ wantD := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusCreated,
+ "/a/b/": http.StatusCreated,
+ "/a/b/c/": http.StatusMovedPermanently,
+ }[prefix]
+ if err := do("COPY", srv.URL+"/a/b/c", nil, wantD, dst, srv.URL+"/a/b/d"); err != nil {
+ t.Errorf("prefix=%-9q COPY /a/b/c /a/b/d: %v", prefix, err)
+ continue
+ }
+
+ wantE := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusCreated,
+ "/a/b/": http.StatusCreated,
+ "/a/b/c/": http.StatusNotFound,
+ }[prefix]
+ if err := do("MKCOL", srv.URL+"/a/b/e", nil, wantE); err != nil {
+ t.Errorf("prefix=%-9q MKCOL /a/b/e: %v", prefix, err)
+ continue
+ }
+
+ wantF := map[string]int{
+ "/": http.StatusCreated,
+ "/a/": http.StatusCreated,
+ "/a/b/": http.StatusCreated,
+ "/a/b/c/": http.StatusNotFound,
+ }[prefix]
+ if err := do("MOVE", srv.URL+"/a/b/d", nil, wantF, dst, srv.URL+"/a/b/e/f"); err != nil {
+ t.Errorf("prefix=%-9q MOVE /a/b/d /a/b/e/f: %v", prefix, err)
+ continue
+ }
+
+ got, err := find(nil, fs, "/")
+ if err != nil {
+ t.Errorf("prefix=%-9q find: %v", prefix, err)
+ continue
+ }
+ sort.Strings(got)
+ want := map[string][]string{
+ "/": []string{"/", "/a", "/a/b", "/a/b/c", "/a/b/e", "/a/b/e/f"},
+ "/a/": []string{"/", "/b", "/b/c", "/b/e", "/b/e/f"},
+ "/a/b/": []string{"/", "/c", "/e", "/e/f"},
+ "/a/b/c/": []string{"/"},
+ }[prefix]
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("prefix=%-9q find:\ngot %v\nwant %v", prefix, got, want)
+ continue
+ }
+ }
+}
diff --git a/webdav/xml.go b/webdav/xml.go
new file mode 100644
index 0000000..8705cda
--- /dev/null
+++ b/webdav/xml.go
@@ -0,0 +1,469 @@
+// Copyright 2014 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.
+
+package webdav
+
+// The XML encoding is covered by Section 14.
+// http://www.webdav.org/specs/rfc4918.html#xml.element.definitions
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "time"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
+type lockInfo struct {
+ XMLName xml.Name `xml:"lockinfo"`
+ Exclusive *struct{} `xml:"lockscope>exclusive"`
+ Shared *struct{} `xml:"lockscope>shared"`
+ Write *struct{} `xml:"locktype>write"`
+ Owner owner `xml:"owner"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
+type owner struct {
+ InnerXML string `xml:",innerxml"`
+}
+
+func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
+ c := &countingReader{r: r}
+ if err = xml.NewDecoder(c).Decode(&li); err != nil {
+ if err == io.EOF {
+ if c.n == 0 {
+ // An empty body means to refresh the lock.
+ // http://www.webdav.org/specs/rfc4918.html#refreshing-locks
+ return lockInfo{}, 0, nil
+ }
+ err = errInvalidLockInfo
+ }
+ return lockInfo{}, http.StatusBadRequest, err
+ }
+ // We only support exclusive (non-shared) write locks. In practice, these are
+ // the only types of locks that seem to matter.
+ if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
+ return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
+ }
+ return li, 0, nil
+}
+
+type countingReader struct {
+ n int
+ r io.Reader
+}
+
+func (c *countingReader) Read(p []byte) (int, error) {
+ n, err := c.r.Read(p)
+ c.n += n
+ return n, err
+}
+
+func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
+ depth := "infinity"
+ if ld.ZeroDepth {
+ depth = "0"
+ }
+ timeout := ld.Duration / time.Second
+ return fmt.Fprintf(w, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+
+ "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"+
+ " <D:locktype><D:write/></D:locktype>\n"+
+ " <D:lockscope><D:exclusive/></D:lockscope>\n"+
+ " <D:depth>%s</D:depth>\n"+
+ " <D:owner>%s</D:owner>\n"+
+ " <D:timeout>Second-%d</D:timeout>\n"+
+ " <D:locktoken><D:href>%s</D:href></D:locktoken>\n"+
+ " <D:lockroot><D:href>%s</D:href></D:lockroot>\n"+
+ "</D:activelock></D:lockdiscovery></D:prop>",
+ depth, ld.OwnerXML, timeout, escape(token), escape(ld.Root),
+ )
+}
+
+func escape(s string) string {
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '"', '&', '\'', '<', '>':
+ b := bytes.NewBuffer(nil)
+ xml.EscapeText(b, []byte(s))
+ return b.String()
+ }
+ }
+ return s
+}
+
+// Next returns the next token, if any, in the XML stream of d.
+// RFC 4918 requires to ignore comments, processing instructions
+// and directives.
+// http://www.webdav.org/specs/rfc4918.html#property_values
+// http://www.webdav.org/specs/rfc4918.html#xml-extensibility
+func next(d *xml.Decoder) (xml.Token, error) {
+ for {
+ t, err := d.Token()
+ if err != nil {
+ return t, err
+ }
+ switch t.(type) {
+ case xml.Comment, xml.Directive, xml.ProcInst:
+ continue
+ default:
+ return t, nil
+ }
+ }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
+type propfindProps []xml.Name
+
+// UnmarshalXML appends the property names enclosed within start to pn.
+//
+// It returns an error if start does not contain any properties or if
+// properties contain values. Character data between properties is ignored.
+func (pn *propfindProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ for {
+ t, err := next(d)
+ if err != nil {
+ return err
+ }
+ switch t.(type) {
+ case xml.EndElement:
+ if len(*pn) == 0 {
+ return fmt.Errorf("%s must not be empty", start.Name.Local)
+ }
+ return nil
+ case xml.StartElement:
+ name := t.(xml.StartElement).Name
+ t, err = next(d)
+ if err != nil {
+ return err
+ }
+ if _, ok := t.(xml.EndElement); !ok {
+ return fmt.Errorf("unexpected token %T", t)
+ }
+ *pn = append(*pn, name)
+ }
+ }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
+type propfind struct {
+ XMLName xml.Name `xml:"DAV: propfind"`
+ Allprop *struct{} `xml:"DAV: allprop"`
+ Propname *struct{} `xml:"DAV: propname"`
+ Prop propfindProps `xml:"DAV: prop"`
+ Include propfindProps `xml:"DAV: include"`
+}
+
+func readPropfind(r io.Reader) (pf propfind, status int, err error) {
+ c := countingReader{r: r}
+ if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
+ if err == io.EOF {
+ if c.n == 0 {
+ // An empty body means to propfind allprop.
+ // http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
+ return propfind{Allprop: new(struct{})}, 0, nil
+ }
+ err = errInvalidPropfind
+ }
+ return propfind{}, http.StatusBadRequest, err
+ }
+
+ if pf.Allprop == nil && pf.Include != nil {
+ return propfind{}, http.StatusBadRequest, errInvalidPropfind
+ }
+ if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
+ return propfind{}, http.StatusBadRequest, errInvalidPropfind
+ }
+ if pf.Prop != nil && pf.Propname != nil {
+ return propfind{}, http.StatusBadRequest, errInvalidPropfind
+ }
+ if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
+ return propfind{}, http.StatusBadRequest, errInvalidPropfind
+ }
+ return pf, 0, nil
+}
+
+// Property represents a single DAV resource property as defined in RFC 4918.
+// See http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
+type Property struct {
+ // XMLName is the fully qualified name that identifies this property.
+ XMLName xml.Name
+
+ // Lang is an optional xml:lang attribute.
+ Lang string `xml:"xml:lang,attr,omitempty"`
+
+ // InnerXML contains the XML representation of the property value.
+ // See http://www.webdav.org/specs/rfc4918.html#property_values
+ //
+ // Property values of complex type or mixed-content must have fully
+ // expanded XML namespaces or be self-contained with according
+ // XML namespace declarations. They must not rely on any XML
+ // namespace declarations within the scope of the XML document,
+ // even including the DAV: namespace.
+ InnerXML []byte `xml:",innerxml"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
+// See multistatusWriter for the "D:" namespace prefix.
+type xmlError struct {
+ XMLName xml.Name `xml:"D:error"`
+ InnerXML []byte `xml:",innerxml"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
+// See multistatusWriter for the "D:" namespace prefix.
+type propstat struct {
+ Prop []Property `xml:"D:prop>_ignored_"`
+ Status string `xml:"D:status"`
+ Error *xmlError `xml:"D:error"`
+ ResponseDescription string `xml:"D:responsedescription,omitempty"`
+}
+
+// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
+// before encoding. See multistatusWriter.
+func (ps propstat) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ for k, prop := range ps.Prop {
+ if prop.XMLName.Space == "DAV:" {
+ prop.XMLName = xml.Name{Space: "", Local: "D:" + prop.XMLName.Local}
+ ps.Prop[k] = prop
+ }
+ }
+ // Distinct type to avoid infinite recursion of MarshalXML.
+ type newpropstat propstat
+ return e.EncodeElement(newpropstat(ps), start)
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
+// See multistatusWriter for the "D:" namespace prefix.
+type response struct {
+ XMLName xml.Name `xml:"D:response"`
+ Href []string `xml:"D:href"`
+ Propstat []propstat `xml:"D:propstat"`
+ Status string `xml:"D:status,omitempty"`
+ Error *xmlError `xml:"D:error"`
+ ResponseDescription string `xml:"D:responsedescription,omitempty"`
+}
+
+// MultistatusWriter marshals one or more Responses into a XML
+// multistatus response.
+// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
+// TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
+// "DAV:" on this element, is prepended on the nested response, as well as on all
+// its nested elements. All property names in the DAV: namespace are prefixed as
+// well. This is because some versions of Mini-Redirector (on windows 7) ignore
+// elements with a default namespace (no prefixed namespace). A less intrusive fix
+// should be possible after golang.org/cl/11074. See https://golang.org/issue/11177
+type multistatusWriter struct {
+ // ResponseDescription contains the optional responsedescription
+ // of the multistatus XML element. Only the latest content before
+ // close will be emitted. Empty response descriptions are not
+ // written.
+ responseDescription string
+
+ w http.ResponseWriter
+ enc *xml.Encoder
+}
+
+// Write validates and emits a DAV response as part of a multistatus response
+// element.
+//
+// It sets the HTTP status code of its underlying http.ResponseWriter to 207
+// (Multi-Status) and populates the Content-Type header. If r is the
+// first, valid response to be written, Write prepends the XML representation
+// of r with a multistatus tag. Callers must call close after the last response
+// has been written.
+func (w *multistatusWriter) write(r *response) error {
+ switch len(r.Href) {
+ case 0:
+ return errInvalidResponse
+ case 1:
+ if len(r.Propstat) > 0 != (r.Status == "") {
+ return errInvalidResponse
+ }
+ default:
+ if len(r.Propstat) > 0 || r.Status == "" {
+ return errInvalidResponse
+ }
+ }
+ err := w.writeHeader()
+ if err != nil {
+ return err
+ }
+ return w.enc.Encode(r)
+}
+
+// writeHeader writes a XML multistatus start element on w's underlying
+// http.ResponseWriter and returns the result of the write operation.
+// After the first write attempt, writeHeader becomes a no-op.
+func (w *multistatusWriter) writeHeader() error {
+ if w.enc != nil {
+ return nil
+ }
+ w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
+ w.w.WriteHeader(StatusMulti)
+ _, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`)
+ if err != nil {
+ return err
+ }
+ w.enc = xml.NewEncoder(w.w)
+ return w.enc.EncodeToken(xml.StartElement{
+ Name: xml.Name{
+ Space: "DAV:",
+ Local: "multistatus",
+ },
+ Attr: []xml.Attr{{
+ Name: xml.Name{Space: "xmlns", Local: "D"},
+ Value: "DAV:",
+ }},
+ })
+}
+
+// Close completes the marshalling of the multistatus response. It returns
+// an error if the multistatus response could not be completed. If both the
+// return value and field enc of w are nil, then no multistatus response has
+// been written.
+func (w *multistatusWriter) close() error {
+ if w.enc == nil {
+ return nil
+ }
+ var end []xml.Token
+ if w.responseDescription != "" {
+ name := xml.Name{Space: "DAV:", Local: "responsedescription"}
+ end = append(end,
+ xml.StartElement{Name: name},
+ xml.CharData(w.responseDescription),
+ xml.EndElement{Name: name},
+ )
+ }
+ end = append(end, xml.EndElement{
+ Name: xml.Name{Space: "DAV:", Local: "multistatus"},
+ })
+ for _, t := range end {
+ err := w.enc.EncodeToken(t)
+ if err != nil {
+ return err
+ }
+ }
+ return w.enc.Flush()
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch)
+type proppatchProps []Property
+
+var xmlLangName = xml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
+
+func xmlLang(s xml.StartElement, d string) string {
+ for _, attr := range s.Attr {
+ if attr.Name == xmlLangName {
+ return attr.Value
+ }
+ }
+ return d
+}
+
+type xmlValue []byte
+
+func (v *xmlValue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ // The XML value of a property can be arbitrary, mixed-content XML.
+ // To make sure that the unmarshalled value contains all required
+ // namespaces, we encode all the property value XML tokens into a
+ // buffer. This forces the encoder to redeclare any used namespaces.
+ var b bytes.Buffer
+ e := xml.NewEncoder(&b)
+ for {
+ t, err := next(d)
+ if err != nil {
+ return err
+ }
+ if e, ok := t.(xml.EndElement); ok && e.Name == start.Name {
+ break
+ }
+ if err = e.EncodeToken(t); err != nil {
+ return err
+ }
+ }
+ err := e.Flush()
+ if err != nil {
+ return err
+ }
+ *v = b.Bytes()
+ return nil
+}
+
+// UnmarshalXML appends the property names and values enclosed within start
+// to ps.
+//
+// An xml:lang attribute that is defined either on the DAV:prop or property
+// name XML element is propagated to the property's Lang field.
+//
+// UnmarshalXML returns an error if start does not contain any properties or if
+// property values contain syntactically incorrect XML.
+func (ps *proppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ lang := xmlLang(start, "")
+ for {
+ t, err := next(d)
+ if err != nil {
+ return err
+ }
+ switch elem := t.(type) {
+ case xml.EndElement:
+ if len(*ps) == 0 {
+ return fmt.Errorf("%s must not be empty", start.Name.Local)
+ }
+ return nil
+ case xml.StartElement:
+ p := Property{
+ XMLName: t.(xml.StartElement).Name,
+ Lang: xmlLang(t.(xml.StartElement), lang),
+ }
+ err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
+ if err != nil {
+ return err
+ }
+ *ps = append(*ps, p)
+ }
+ }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
+type setRemove struct {
+ XMLName xml.Name
+ Lang string `xml:"xml:lang,attr,omitempty"`
+ Prop proppatchProps `xml:"DAV: prop"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
+type propertyupdate struct {
+ XMLName xml.Name `xml:"DAV: propertyupdate"`
+ Lang string `xml:"xml:lang,attr,omitempty"`
+ SetRemove []setRemove `xml:",any"`
+}
+
+func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
+ var pu propertyupdate
+ if err = xml.NewDecoder(r).Decode(&pu); err != nil {
+ return nil, http.StatusBadRequest, err
+ }
+ for _, op := range pu.SetRemove {
+ remove := false
+ switch op.XMLName {
+ case xml.Name{Space: "DAV:", Local: "set"}:
+ // No-op.
+ case xml.Name{Space: "DAV:", Local: "remove"}:
+ for _, p := range op.Prop {
+ if len(p.InnerXML) > 0 {
+ return nil, http.StatusBadRequest, errInvalidProppatch
+ }
+ }
+ remove = true
+ default:
+ return nil, http.StatusBadRequest, errInvalidProppatch
+ }
+ patches = append(patches, Proppatch{Remove: remove, Props: op.Prop})
+ }
+ return patches, 0, nil
+}
diff --git a/webdav/xml_test.go b/webdav/xml_test.go
new file mode 100644
index 0000000..bc5641f
--- /dev/null
+++ b/webdav/xml_test.go
@@ -0,0 +1,909 @@
+// Copyright 2014 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.
+
+package webdav
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+
+ "golang.org/x/net/webdav/internal/xml"
+)
+
+func TestReadLockInfo(t *testing.T) {
+ // The "section x.y.z" test cases come from section x.y.z of the spec at
+ // http://www.webdav.org/specs/rfc4918.html
+ testCases := []struct {
+ desc string
+ input string
+ wantLI lockInfo
+ wantStatus int
+ }{{
+ "bad: junk",
+ "xxx",
+ lockInfo{},
+ http.StatusBadRequest,
+ }, {
+ "bad: invalid owner XML",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n" +
+ " <D:owner>\n" +
+ " <D:href> no end tag \n" +
+ " </D:owner>\n" +
+ "</D:lockinfo>",
+ lockInfo{},
+ http.StatusBadRequest,
+ }, {
+ "bad: invalid UTF-8",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n" +
+ " <D:owner>\n" +
+ " <D:href> \xff </D:href>\n" +
+ " </D:owner>\n" +
+ "</D:lockinfo>",
+ lockInfo{},
+ http.StatusBadRequest,
+ }, {
+ "bad: unfinished XML #1",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n",
+ lockInfo{},
+ http.StatusBadRequest,
+ }, {
+ "bad: unfinished XML #2",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n" +
+ " <D:owner>\n",
+ lockInfo{},
+ http.StatusBadRequest,
+ }, {
+ "good: empty",
+ "",
+ lockInfo{},
+ 0,
+ }, {
+ "good: plain-text owner",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n" +
+ " <D:owner>gopher</D:owner>\n" +
+ "</D:lockinfo>",
+ lockInfo{
+ XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
+ Exclusive: new(struct{}),
+ Write: new(struct{}),
+ Owner: owner{
+ InnerXML: "gopher",
+ },
+ },
+ 0,
+ }, {
+ "section 9.10.7",
+ "" +
+ "<D:lockinfo xmlns:D='DAV:'>\n" +
+ " <D:lockscope><D:exclusive/></D:lockscope>\n" +
+ " <D:locktype><D:write/></D:locktype>\n" +
+ " <D:owner>\n" +
+ " <D:href>http://example.org/~ejw/contact.html</D:href>\n" +
+ " </D:owner>\n" +
+ "</D:lockinfo>",
+ lockInfo{
+ XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
+ Exclusive: new(struct{}),
+ Write: new(struct{}),
+ Owner: owner{
+ InnerXML: "\n <D:href>http://example.org/~ejw/contact.html</D:href>\n ",
+ },
+ },
+ 0,
+ }}
+
+ for _, tc := range testCases {
+ li, status, err := readLockInfo(strings.NewReader(tc.input))
+ if tc.wantStatus != 0 {
+ if err == nil {
+ t.Errorf("%s: got nil error, want non-nil", tc.desc)
+ continue
+ }
+ } else if err != nil {
+ t.Errorf("%s: %v", tc.desc, err)
+ continue
+ }
+ if !reflect.DeepEqual(li, tc.wantLI) || status != tc.wantStatus {
+ t.Errorf("%s:\ngot lockInfo=%v, status=%v\nwant lockInfo=%v, status=%v",
+ tc.desc, li, status, tc.wantLI, tc.wantStatus)
+ continue
+ }
+ }
+}
+
+func TestReadPropfind(t *testing.T) {
+ testCases := []struct {
+ desc string
+ input string
+ wantPF propfind
+ wantStatus int
+ }{{
+ desc: "propfind: propname",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:propname/>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Propname: new(struct{}),
+ },
+ }, {
+ desc: "propfind: empty body means allprop",
+ input: "",
+ wantPF: propfind{
+ Allprop: new(struct{}),
+ },
+ }, {
+ desc: "propfind: allprop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:allprop/>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Allprop: new(struct{}),
+ },
+ }, {
+ desc: "propfind: allprop followed by include",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:allprop/>\n" +
+ " <A:include><A:displayname/></A:include>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Allprop: new(struct{}),
+ Include: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: include followed by allprop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:include><A:displayname/></A:include>\n" +
+ " <A:allprop/>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Allprop: new(struct{}),
+ Include: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: propfind",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:displayname/></A:prop>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: prop with ignored comments",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop>\n" +
+ " <!-- ignore -->\n" +
+ " <A:displayname><!-- ignore --></A:displayname>\n" +
+ " </A:prop>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: propfind with ignored whitespace",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop> <A:displayname/></A:prop>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: propfind with ignored mixed-content",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop>foo<A:displayname/>bar</A:prop>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
+ },
+ }, {
+ desc: "propfind: propname with ignored element (section A.4)",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:propname/>\n" +
+ " <E:leave-out xmlns:E='E:'>*boss*</E:leave-out>\n" +
+ "</A:propfind>",
+ wantPF: propfind{
+ XMLName: xml.Name{Space: "DAV:", Local: "propfind"},
+ Propname: new(struct{}),
+ },
+ }, {
+ desc: "propfind: bad: junk",
+ input: "xxx",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: propname and allprop (section A.3)",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:propname/>" +
+ " <A:allprop/>" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: propname and prop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:displayname/></A:prop>\n" +
+ " <A:propname/>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: allprop and prop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:allprop/>\n" +
+ " <A:prop><A:foo/><A:/prop>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: empty propfind with ignored element (section A.4)",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <E:expired-props/>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: empty prop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop/>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: prop with just chardata",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop>foo</A:prop>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "bad: interrupted prop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:foo></A:prop>\n",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "bad: malformed end element prop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:foo/></A:bar></A:prop>\n",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: property with chardata value",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:foo>bar</A:foo></A:prop>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: property with whitespace value",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:prop><A:foo> </A:foo></A:prop>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "propfind: bad: include without allprop",
+ input: "" +
+ "<A:propfind xmlns:A='DAV:'>\n" +
+ " <A:include><A:foo/></A:include>\n" +
+ "</A:propfind>",
+ wantStatus: http.StatusBadRequest,
+ }}
+
+ for _, tc := range testCases {
+ pf, status, err := readPropfind(strings.NewReader(tc.input))
+ if tc.wantStatus != 0 {
+ if err == nil {
+ t.Errorf("%s: got nil error, want non-nil", tc.desc)
+ continue
+ }
+ } else if err != nil {
+ t.Errorf("%s: %v", tc.desc, err)
+ continue
+ }
+ if !reflect.DeepEqual(pf, tc.wantPF) || status != tc.wantStatus {
+ t.Errorf("%s:\ngot propfind=%v, status=%v\nwant propfind=%v, status=%v",
+ tc.desc, pf, status, tc.wantPF, tc.wantStatus)
+ continue
+ }
+ }
+}
+
+func TestMultistatusWriter(t *testing.T) {
+ if go1Dot4 {
+ t.Skip("TestMultistatusWriter requires Go version 1.5 or greater")
+ }
+
+ ///The "section x.y.z" test cases come from section x.y.z of the spec at
+ // http://www.webdav.org/specs/rfc4918.html
+ testCases := []struct {
+ desc string
+ responses []response
+ respdesc string
+ writeHeader bool
+ wantXML string
+ wantCode int
+ wantErr error
+ }{{
+ desc: "section 9.2.2 (failed dependency)",
+ responses: []response{{
+ Href: []string{"http://example.com/foo"},
+ Propstat: []propstat{{
+ Prop: []Property{{
+ XMLName: xml.Name{
+ Space: "http://ns.example.com/",
+ Local: "Authors",
+ },
+ }},
+ Status: "HTTP/1.1 424 Failed Dependency",
+ }, {
+ Prop: []Property{{
+ XMLName: xml.Name{
+ Space: "http://ns.example.com/",
+ Local: "Copyright-Owner",
+ },
+ }},
+ Status: "HTTP/1.1 409 Conflict",
+ }},
+ ResponseDescription: "Copyright Owner cannot be deleted or altered.",
+ }},
+ wantXML: `` +
+ `<?xml version="1.0" encoding="UTF-8"?>` +
+ `<multistatus xmlns="DAV:">` +
+ ` <response>` +
+ ` <href>http://example.com/foo</href>` +
+ ` <propstat>` +
+ ` <prop>` +
+ ` <Authors xmlns="http://ns.example.com/"></Authors>` +
+ ` </prop>` +
+ ` <status>HTTP/1.1 424 Failed Dependency</status>` +
+ ` </propstat>` +
+ ` <propstat xmlns="DAV:">` +
+ ` <prop>` +
+ ` <Copyright-Owner xmlns="http://ns.example.com/"></Copyright-Owner>` +
+ ` </prop>` +
+ ` <status>HTTP/1.1 409 Conflict</status>` +
+ ` </propstat>` +
+ ` <responsedescription>Copyright Owner cannot be deleted or altered.</responsedescription>` +
+ `</response>` +
+ `</multistatus>`,
+ wantCode: StatusMulti,
+ }, {
+ desc: "section 9.6.2 (lock-token-submitted)",
+ responses: []response{{
+ Href: []string{"http://example.com/foo"},
+ Status: "HTTP/1.1 423 Locked",
+ Error: &xmlError{
+ InnerXML: []byte(`<lock-token-submitted xmlns="DAV:"/>`),
+ },
+ }},
+ wantXML: `` +
+ `<?xml version="1.0" encoding="UTF-8"?>` +
+ `<multistatus xmlns="DAV:">` +
+ ` <response>` +
+ ` <href>http://example.com/foo</href>` +
+ ` <status>HTTP/1.1 423 Locked</status>` +
+ ` <error><lock-token-submitted xmlns="DAV:"/></error>` +
+ ` </response>` +
+ `</multistatus>`,
+ wantCode: StatusMulti,
+ }, {
+ desc: "section 9.1.3",
+ responses: []response{{
+ Href: []string{"http://example.com/foo"},
+ Propstat: []propstat{{
+ Prop: []Property{{
+ XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "bigbox"},
+ InnerXML: []byte(`` +
+ `<BoxType xmlns="http://ns.example.com/boxschema/">` +
+ `Box type A` +
+ `</BoxType>`),
+ }, {
+ XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "author"},
+ InnerXML: []byte(`` +
+ `<Name xmlns="http://ns.example.com/boxschema/">` +
+ `J.J. Johnson` +
+ `</Name>`),
+ }},
+ Status: "HTTP/1.1 200 OK",
+ }, {
+ Prop: []Property{{
+ XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "DingALing"},
+ }, {
+ XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "Random"},
+ }},
+ Status: "HTTP/1.1 403 Forbidden",
+ ResponseDescription: "The user does not have access to the DingALing property.",
+ }},
+ }},
+ respdesc: "There has been an access violation error.",
+ wantXML: `` +
+ `<?xml version="1.0" encoding="UTF-8"?>` +
+ `<multistatus xmlns="DAV:" xmlns:B="http://ns.example.com/boxschema/">` +
+ ` <response>` +
+ ` <href>http://example.com/foo</href>` +
+ ` <propstat>` +
+ ` <prop>` +
+ ` <B:bigbox><B:BoxType>Box type A</B:BoxType></B:bigbox>` +
+ ` <B:author><B:Name>J.J. Johnson</B:Name></B:author>` +
+ ` </prop>` +
+ ` <status>HTTP/1.1 200 OK</status>` +
+ ` </propstat>` +
+ ` <propstat>` +
+ ` <prop>` +
+ ` <B:DingALing/>` +
+ ` <B:Random/>` +
+ ` </prop>` +
+ ` <status>HTTP/1.1 403 Forbidden</status>` +
+ ` <responsedescription>The user does not have access to the DingALing property.</responsedescription>` +
+ ` </propstat>` +
+ ` </response>` +
+ ` <responsedescription>There has been an access violation error.</responsedescription>` +
+ `</multistatus>`,
+ wantCode: StatusMulti,
+ }, {
+ desc: "no response written",
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "no response written (with description)",
+ respdesc: "too bad",
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "empty multistatus with header",
+ writeHeader: true,
+ wantXML: `<multistatus xmlns="DAV:"></multistatus>`,
+ wantCode: StatusMulti,
+ }, {
+ desc: "bad: no href",
+ responses: []response{{
+ Propstat: []propstat{{
+ Prop: []Property{{
+ XMLName: xml.Name{
+ Space: "http://example.com/",
+ Local: "foo",
+ },
+ }},
+ Status: "HTTP/1.1 200 OK",
+ }},
+ }},
+ wantErr: errInvalidResponse,
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "bad: multiple hrefs and no status",
+ responses: []response{{
+ Href: []string{"http://example.com/foo", "http://example.com/bar"},
+ }},
+ wantErr: errInvalidResponse,
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "bad: one href and no propstat",
+ responses: []response{{
+ Href: []string{"http://example.com/foo"},
+ }},
+ wantErr: errInvalidResponse,
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "bad: status with one href and propstat",
+ responses: []response{{
+ Href: []string{"http://example.com/foo"},
+ Propstat: []propstat{{
+ Prop: []Property{{
+ XMLName: xml.Name{
+ Space: "http://example.com/",
+ Local: "foo",
+ },
+ }},
+ Status: "HTTP/1.1 200 OK",
+ }},
+ Status: "HTTP/1.1 200 OK",
+ }},
+ wantErr: errInvalidResponse,
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }, {
+ desc: "bad: multiple hrefs and propstat",
+ responses: []response{{
+ Href: []string{
+ "http://example.com/foo",
+ "http://example.com/bar",
+ },
+ Propstat: []propstat{{
+ Prop: []Property{{
+ XMLName: xml.Name{
+ Space: "http://example.com/",
+ Local: "foo",
+ },
+ }},
+ Status: "HTTP/1.1 200 OK",
+ }},
+ }},
+ wantErr: errInvalidResponse,
+ // default of http.responseWriter
+ wantCode: http.StatusOK,
+ }}
+
+ n := xmlNormalizer{omitWhitespace: true}
+loop:
+ for _, tc := range testCases {
+ rec := httptest.NewRecorder()
+ w := multistatusWriter{w: rec, responseDescription: tc.respdesc}
+ if tc.writeHeader {
+ if err := w.writeHeader(); err != nil {
+ t.Errorf("%s: got writeHeader error %v, want nil", tc.desc, err)
+ continue
+ }
+ }
+ for _, r := range tc.responses {
+ if err := w.write(&r); err != nil {
+ if err != tc.wantErr {
+ t.Errorf("%s: got write error %v, want %v",
+ tc.desc, err, tc.wantErr)
+ }
+ continue loop
+ }
+ }
+ if err := w.close(); err != tc.wantErr {
+ t.Errorf("%s: got close error %v, want %v",
+ tc.desc, err, tc.wantErr)
+ continue
+ }
+ if rec.Code != tc.wantCode {
+ t.Errorf("%s: got HTTP status code %d, want %d\n",
+ tc.desc, rec.Code, tc.wantCode)
+ continue
+ }
+ gotXML := rec.Body.String()
+ eq, err := n.equalXML(strings.NewReader(gotXML), strings.NewReader(tc.wantXML))
+ if err != nil {
+ t.Errorf("%s: equalXML: %v", tc.desc, err)
+ continue
+ }
+ if !eq {
+ t.Errorf("%s: XML body\ngot %s\nwant %s", tc.desc, gotXML, tc.wantXML)
+ }
+ }
+}
+
+func TestReadProppatch(t *testing.T) {
+ ppStr := func(pps []Proppatch) string {
+ var outer []string
+ for _, pp := range pps {
+ var inner []string
+ for _, p := range pp.Props {
+ inner = append(inner, fmt.Sprintf("{XMLName: %q, Lang: %q, InnerXML: %q}",
+ p.XMLName, p.Lang, p.InnerXML))
+ }
+ outer = append(outer, fmt.Sprintf("{Remove: %t, Props: [%s]}",
+ pp.Remove, strings.Join(inner, ", ")))
+ }
+ return "[" + strings.Join(outer, ", ") + "]"
+ }
+
+ testCases := []struct {
+ desc string
+ input string
+ wantPP []Proppatch
+ wantStatus int
+ }{{
+ desc: "proppatch: section 9.2 (with simple property value)",
+ input: `` +
+ `<?xml version="1.0" encoding="utf-8" ?>` +
+ `<D:propertyupdate xmlns:D="DAV:"` +
+ ` xmlns:Z="http://ns.example.com/z/">` +
+ ` <D:set>` +
+ ` <D:prop><Z:Authors>somevalue</Z:Authors></D:prop>` +
+ ` </D:set>` +
+ ` <D:remove>` +
+ ` <D:prop><Z:Copyright-Owner/></D:prop>` +
+ ` </D:remove>` +
+ `</D:propertyupdate>`,
+ wantPP: []Proppatch{{
+ Props: []Property{{
+ xml.Name{Space: "http://ns.example.com/z/", Local: "Authors"},
+ "",
+ []byte(`somevalue`),
+ }},
+ }, {
+ Remove: true,
+ Props: []Property{{
+ xml.Name{Space: "http://ns.example.com/z/", Local: "Copyright-Owner"},
+ "",
+ nil,
+ }},
+ }},
+ }, {
+ desc: "proppatch: lang attribute on prop",
+ input: `` +
+ `<?xml version="1.0" encoding="utf-8" ?>` +
+ `<D:propertyupdate xmlns:D="DAV:">` +
+ ` <D:set>` +
+ ` <D:prop xml:lang="en">` +
+ ` <foo xmlns="http://example.com/ns"/>` +
+ ` </D:prop>` +
+ ` </D:set>` +
+ `</D:propertyupdate>`,
+ wantPP: []Proppatch{{
+ Props: []Property{{
+ xml.Name{Space: "http://example.com/ns", Local: "foo"},
+ "en",
+ nil,
+ }},
+ }},
+ }, {
+ desc: "bad: remove with value",
+ input: `` +
+ `<?xml version="1.0" encoding="utf-8" ?>` +
+ `<D:propertyupdate xmlns:D="DAV:"` +
+ ` xmlns:Z="http://ns.example.com/z/">` +
+ ` <D:remove>` +
+ ` <D:prop>` +
+ ` <Z:Authors>` +
+ ` <Z:Author>Jim Whitehead</Z:Author>` +
+ ` </Z:Authors>` +
+ ` </D:prop>` +
+ ` </D:remove>` +
+ `</D:propertyupdate>`,
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "bad: empty propertyupdate",
+ input: `` +
+ `<?xml version="1.0" encoding="utf-8" ?>` +
+ `<D:propertyupdate xmlns:D="DAV:"` +
+ `</D:propertyupdate>`,
+ wantStatus: http.StatusBadRequest,
+ }, {
+ desc: "bad: empty prop",
+ input: `` +
+ `<?xml version="1.0" encoding="utf-8" ?>` +
+ `<D:propertyupdate xmlns:D="DAV:"` +
+ ` xmlns:Z="http://ns.example.com/z/">` +
+ ` <D:remove>` +
+ ` <D:prop/>` +
+ ` </D:remove>` +
+ `</D:propertyupdate>`,
+ wantStatus: http.StatusBadRequest,
+ }}
+
+ for _, tc := range testCases {
+ pp, status, err := readProppatch(strings.NewReader(tc.input))
+ if tc.wantStatus != 0 {
+ if err == nil {
+ t.Errorf("%s: got nil error, want non-nil", tc.desc)
+ continue
+ }
+ } else if err != nil {
+ t.Errorf("%s: %v", tc.desc, err)
+ continue
+ }
+ if status != tc.wantStatus {
+ t.Errorf("%s: got status %d, want %d", tc.desc, status, tc.wantStatus)
+ continue
+ }
+ if !reflect.DeepEqual(pp, tc.wantPP) || status != tc.wantStatus {
+ t.Errorf("%s: proppatch\ngot %v\nwant %v", tc.desc, ppStr(pp), ppStr(tc.wantPP))
+ }
+ }
+}
+
+func TestUnmarshalXMLValue(t *testing.T) {
+ testCases := []struct {
+ desc string
+ input string
+ wantVal string
+ }{{
+ desc: "simple char data",
+ input: "<root>foo</root>",
+ wantVal: "foo",
+ }, {
+ desc: "empty element",
+ input: "<root><foo/></root>",
+ wantVal: "<foo/>",
+ }, {
+ desc: "preserve namespace",
+ input: `<root><foo xmlns="bar"/></root>`,
+ wantVal: `<foo xmlns="bar"/>`,
+ }, {
+ desc: "preserve root element namespace",
+ input: `<root xmlns:bar="bar"><bar:foo/></root>`,
+ wantVal: `<foo xmlns="bar"/>`,
+ }, {
+ desc: "preserve whitespace",
+ input: "<root> \t </root>",
+ wantVal: " \t ",
+ }, {
+ desc: "preserve mixed content",
+ input: `<root xmlns="bar"> <foo>a<bam xmlns="baz"/> </foo> </root>`,
+ wantVal: ` <foo xmlns="bar">a<bam xmlns="baz"/> </foo> `,
+ }, {
+ desc: "section 9.2",
+ input: `` +
+ `<Z:Authors xmlns:Z="http://ns.example.com/z/">` +
+ ` <Z:Author>Jim Whitehead</Z:Author>` +
+ ` <Z:Author>Roy Fielding</Z:Author>` +
+ `</Z:Authors>`,
+ wantVal: `` +
+ ` <Author xmlns="http://ns.example.com/z/">Jim Whitehead</Author>` +
+ ` <Author xmlns="http://ns.example.com/z/">Roy Fielding</Author>`,
+ }, {
+ desc: "section 4.3.1 (mixed content)",
+ input: `` +
+ `<x:author ` +
+ ` xmlns:x='http://example.com/ns' ` +
+ ` xmlns:D="DAV:">` +
+ ` <x:name>Jane Doe</x:name>` +
+ ` <!-- Jane's contact info -->` +
+ ` <x:uri type='email'` +
+ ` added='2005-11-26'>mailto:jane.doe@example.com</x:uri>` +
+ ` <x:uri type='web'` +
+ ` added='2005-11-27'>http://www.example.com</x:uri>` +
+ ` <x:notes xmlns:h='http://www.w3.org/1999/xhtml'>` +
+ ` Jane has been working way <h:em>too</h:em> long on the` +
+ ` long-awaited revision of <![CDATA[<RFC2518>]]>.` +
+ ` </x:notes>` +
+ `</x:author>`,
+ wantVal: `` +
+ ` <name xmlns="http://example.com/ns">Jane Doe</name>` +
+ ` ` +
+ ` <uri type='email'` +
+ ` xmlns="http://example.com/ns" ` +
+ ` added='2005-11-26'>mailto:jane.doe@example.com</uri>` +
+ ` <uri added='2005-11-27'` +
+ ` type='web'` +
+ ` xmlns="http://example.com/ns">http://www.example.com</uri>` +
+ ` <notes xmlns="http://example.com/ns" ` +
+ ` xmlns:h="http://www.w3.org/1999/xhtml">` +
+ ` Jane has been working way <h:em>too</h:em> long on the` +
+ ` long-awaited revision of <RFC2518>.` +
+ ` </notes>`,
+ }}
+
+ var n xmlNormalizer
+ for _, tc := range testCases {
+ d := xml.NewDecoder(strings.NewReader(tc.input))
+ var v xmlValue
+ if err := d.Decode(&v); err != nil {
+ t.Errorf("%s: got error %v, want nil", tc.desc, err)
+ continue
+ }
+ eq, err := n.equalXML(bytes.NewReader(v), strings.NewReader(tc.wantVal))
+ if err != nil {
+ t.Errorf("%s: equalXML: %v", tc.desc, err)
+ continue
+ }
+ if !eq {
+ t.Errorf("%s:\ngot %s\nwant %s", tc.desc, string(v), tc.wantVal)
+ }
+ }
+}
+
+// xmlNormalizer normalizes XML.
+type xmlNormalizer struct {
+ // omitWhitespace instructs to ignore whitespace between element tags.
+ omitWhitespace bool
+ // omitComments instructs to ignore XML comments.
+ omitComments bool
+}
+
+// normalize writes the normalized XML content of r to w. It applies the
+// following rules
+//
+// * Rename namespace prefixes according to an internal heuristic.
+// * Remove unnecessary namespace declarations.
+// * Sort attributes in XML start elements in lexical order of their
+// fully qualified name.
+// * Remove XML directives and processing instructions.
+// * Remove CDATA between XML tags that only contains whitespace, if
+// instructed to do so.
+// * Remove comments, if instructed to do so.
+//
+func (n *xmlNormalizer) normalize(w io.Writer, r io.Reader) error {
+ d := xml.NewDecoder(r)
+ e := xml.NewEncoder(w)
+ for {
+ t, err := d.Token()
+ if err != nil {
+ if t == nil && err == io.EOF {
+ break
+ }
+ return err
+ }
+ switch val := t.(type) {
+ case xml.Directive, xml.ProcInst:
+ continue
+ case xml.Comment:
+ if n.omitComments {
+ continue
+ }
+ case xml.CharData:
+ if n.omitWhitespace && len(bytes.TrimSpace(val)) == 0 {
+ continue
+ }
+ case xml.StartElement:
+ start, _ := xml.CopyToken(val).(xml.StartElement)
+ attr := start.Attr[:0]
+ for _, a := range start.Attr {
+ if a.Name.Space == "xmlns" || a.Name.Local == "xmlns" {
+ continue
+ }
+ attr = append(attr, a)
+ }
+ sort.Sort(byName(attr))
+ start.Attr = attr
+ t = start
+ }
+ err = e.EncodeToken(t)
+ if err != nil {
+ return err
+ }
+ }
+ return e.Flush()
+}
+
+// equalXML tests for equality of the normalized XML contents of a and b.
+func (n *xmlNormalizer) equalXML(a, b io.Reader) (bool, error) {
+ var buf bytes.Buffer
+ if err := n.normalize(&buf, a); err != nil {
+ return false, err
+ }
+ normA := buf.String()
+ buf.Reset()
+ if err := n.normalize(&buf, b); err != nil {
+ return false, err
+ }
+ normB := buf.String()
+ return normA == normB, nil
+}
+
+type byName []xml.Attr
+
+func (a byName) Len() int { return len(a) }
+func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byName) Less(i, j int) bool {
+ if a[i].Name.Space != a[j].Name.Space {
+ return a[i].Name.Space < a[j].Name.Space
+ }
+ return a[i].Name.Local < a[j].Name.Local
+}
diff --git a/websocket/client.go b/websocket/client.go
new file mode 100644
index 0000000..20d1e1e
--- /dev/null
+++ b/websocket/client.go
@@ -0,0 +1,113 @@
+// 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.
+
+package websocket
+
+import (
+ "bufio"
+ "crypto/tls"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+)
+
+// DialError is an error that occurs while dialling a websocket server.
+type DialError struct {
+ *Config
+ Err error
+}
+
+func (e *DialError) Error() string {
+ return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
+}
+
+// NewConfig creates a new WebSocket config for client connection.
+func NewConfig(server, origin string) (config *Config, err error) {
+ config = new(Config)
+ config.Version = ProtocolVersionHybi13
+ config.Location, err = url.ParseRequestURI(server)
+ if err != nil {
+ return
+ }
+ config.Origin, err = url.ParseRequestURI(origin)
+ if err != nil {
+ return
+ }
+ config.Header = http.Header(make(map[string][]string))
+ return
+}
+
+// NewClient creates a new WebSocket client connection over rwc.
+func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
+ br := bufio.NewReader(rwc)
+ bw := bufio.NewWriter(rwc)
+ err = hybiClientHandshake(config, br, bw)
+ if err != nil {
+ return
+ }
+ buf := bufio.NewReadWriter(br, bw)
+ ws = newHybiClientConn(config, buf, rwc)
+ return
+}
+
+// Dial opens a new client connection to a WebSocket.
+func Dial(url_, protocol, origin string) (ws *Conn, err error) {
+ config, err := NewConfig(url_, origin)
+ if err != nil {
+ return nil, err
+ }
+ if protocol != "" {
+ config.Protocol = []string{protocol}
+ }
+ return DialConfig(config)
+}
+
+var portMap = map[string]string{
+ "ws": "80",
+ "wss": "443",
+}
+
+func parseAuthority(location *url.URL) string {
+ if _, ok := portMap[location.Scheme]; ok {
+ if _, _, err := net.SplitHostPort(location.Host); err != nil {
+ return net.JoinHostPort(location.Host, portMap[location.Scheme])
+ }
+ }
+ return location.Host
+}
+
+// DialConfig opens a new client connection to a WebSocket with a config.
+func DialConfig(config *Config) (ws *Conn, err error) {
+ var client net.Conn
+ if config.Location == nil {
+ return nil, &DialError{config, ErrBadWebSocketLocation}
+ }
+ if config.Origin == nil {
+ return nil, &DialError{config, ErrBadWebSocketOrigin}
+ }
+ switch config.Location.Scheme {
+ case "ws":
+ client, err = net.Dial("tcp", parseAuthority(config.Location))
+
+ case "wss":
+ client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
+
+ default:
+ err = ErrBadScheme
+ }
+ if err != nil {
+ goto Error
+ }
+
+ ws, err = NewClient(config, client)
+ if err != nil {
+ client.Close()
+ goto Error
+ }
+ return
+
+Error:
+ return nil, &DialError{config, err}
+}
diff --git a/websocket/exampledial_test.go b/websocket/exampledial_test.go
new file mode 100644
index 0000000..72bb9d4
--- /dev/null
+++ b/websocket/exampledial_test.go
@@ -0,0 +1,31 @@
+// Copyright 2012 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.
+
+package websocket_test
+
+import (
+ "fmt"
+ "log"
+
+ "golang.org/x/net/websocket"
+)
+
+// This example demonstrates a trivial client.
+func ExampleDial() {
+ origin := "http://localhost/"
+ url := "ws://localhost:12345/ws"
+ ws, err := websocket.Dial(url, "", origin)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
+ log.Fatal(err)
+ }
+ var msg = make([]byte, 512)
+ var n int
+ if n, err = ws.Read(msg); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Received: %s.\n", msg[:n])
+}
diff --git a/websocket/examplehandler_test.go b/websocket/examplehandler_test.go
new file mode 100644
index 0000000..f22a98f
--- /dev/null
+++ b/websocket/examplehandler_test.go
@@ -0,0 +1,26 @@
+// Copyright 2012 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.
+
+package websocket_test
+
+import (
+ "io"
+ "net/http"
+
+ "golang.org/x/net/websocket"
+)
+
+// Echo the data received on the WebSocket.
+func EchoServer(ws *websocket.Conn) {
+ io.Copy(ws, ws)
+}
+
+// This example demonstrates a trivial echo server.
+func ExampleHandler() {
+ http.Handle("/echo", websocket.Handler(EchoServer))
+ err := http.ListenAndServe(":12345", nil)
+ if err != nil {
+ panic("ListenAndServe: " + err.Error())
+ }
+}
diff --git a/websocket/hybi.go b/websocket/hybi.go
new file mode 100644
index 0000000..60bbc84
--- /dev/null
+++ b/websocket/hybi.go
@@ -0,0 +1,586 @@
+// Copyright 2011 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.
+
+package websocket
+
+// This file implements a protocol of hybi draft.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+const (
+ websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+ closeStatusNormal = 1000
+ closeStatusGoingAway = 1001
+ closeStatusProtocolError = 1002
+ closeStatusUnsupportedData = 1003
+ closeStatusFrameTooLarge = 1004
+ closeStatusNoStatusRcvd = 1005
+ closeStatusAbnormalClosure = 1006
+ closeStatusBadMessageData = 1007
+ closeStatusPolicyViolation = 1008
+ closeStatusTooBigData = 1009
+ closeStatusExtensionMismatch = 1010
+
+ maxControlFramePayloadLength = 125
+)
+
+var (
+ ErrBadMaskingKey = &ProtocolError{"bad masking key"}
+ ErrBadPongMessage = &ProtocolError{"bad pong message"}
+ ErrBadClosingStatus = &ProtocolError{"bad closing status"}
+ ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
+ ErrNotImplemented = &ProtocolError{"not implemented"}
+
+ handshakeHeader = map[string]bool{
+ "Host": true,
+ "Upgrade": true,
+ "Connection": true,
+ "Sec-Websocket-Key": true,
+ "Sec-Websocket-Origin": true,
+ "Sec-Websocket-Version": true,
+ "Sec-Websocket-Protocol": true,
+ "Sec-Websocket-Accept": true,
+ }
+)
+
+// A hybiFrameHeader is a frame header as defined in hybi draft.
+type hybiFrameHeader struct {
+ Fin bool
+ Rsv [3]bool
+ OpCode byte
+ Length int64
+ MaskingKey []byte
+
+ data *bytes.Buffer
+}
+
+// A hybiFrameReader is a reader for hybi frame.
+type hybiFrameReader struct {
+ reader io.Reader
+
+ header hybiFrameHeader
+ pos int64
+ length int
+}
+
+func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
+ n, err = frame.reader.Read(msg)
+ if err != nil {
+ return 0, err
+ }
+ if frame.header.MaskingKey != nil {
+ for i := 0; i < n; i++ {
+ msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
+ frame.pos++
+ }
+ }
+ return n, err
+}
+
+func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
+
+func (frame *hybiFrameReader) HeaderReader() io.Reader {
+ if frame.header.data == nil {
+ return nil
+ }
+ if frame.header.data.Len() == 0 {
+ return nil
+ }
+ return frame.header.data
+}
+
+func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
+
+func (frame *hybiFrameReader) Len() (n int) { return frame.length }
+
+// A hybiFrameReaderFactory creates new frame reader based on its frame type.
+type hybiFrameReaderFactory struct {
+ *bufio.Reader
+}
+
+// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
+// See Section 5.2 Base Framing protocol for detail.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
+func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
+ hybiFrame := new(hybiFrameReader)
+ frame = hybiFrame
+ var header []byte
+ var b byte
+ // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
+ b, err = buf.ReadByte()
+ if err != nil {
+ return
+ }
+ header = append(header, b)
+ hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
+ for i := 0; i < 3; i++ {
+ j := uint(6 - i)
+ hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
+ }
+ hybiFrame.header.OpCode = header[0] & 0x0f
+
+ // Second byte. Mask/Payload len(7bits)
+ b, err = buf.ReadByte()
+ if err != nil {
+ return
+ }
+ header = append(header, b)
+ mask := (b & 0x80) != 0
+ b &= 0x7f
+ lengthFields := 0
+ switch {
+ case b <= 125: // Payload length 7bits.
+ hybiFrame.header.Length = int64(b)
+ case b == 126: // Payload length 7+16bits
+ lengthFields = 2
+ case b == 127: // Payload length 7+64bits
+ lengthFields = 8
+ }
+ for i := 0; i < lengthFields; i++ {
+ b, err = buf.ReadByte()
+ if err != nil {
+ return
+ }
+ if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
+ b &= 0x7f
+ }
+ header = append(header, b)
+ hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
+ }
+ if mask {
+ // Masking key. 4 bytes.
+ for i := 0; i < 4; i++ {
+ b, err = buf.ReadByte()
+ if err != nil {
+ return
+ }
+ header = append(header, b)
+ hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
+ }
+ }
+ hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
+ hybiFrame.header.data = bytes.NewBuffer(header)
+ hybiFrame.length = len(header) + int(hybiFrame.header.Length)
+ return
+}
+
+// A HybiFrameWriter is a writer for hybi frame.
+type hybiFrameWriter struct {
+ writer *bufio.Writer
+
+ header *hybiFrameHeader
+}
+
+func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
+ var header []byte
+ var b byte
+ if frame.header.Fin {
+ b |= 0x80
+ }
+ for i := 0; i < 3; i++ {
+ if frame.header.Rsv[i] {
+ j := uint(6 - i)
+ b |= 1 << j
+ }
+ }
+ b |= frame.header.OpCode
+ header = append(header, b)
+ if frame.header.MaskingKey != nil {
+ b = 0x80
+ } else {
+ b = 0
+ }
+ lengthFields := 0
+ length := len(msg)
+ switch {
+ case length <= 125:
+ b |= byte(length)
+ case length < 65536:
+ b |= 126
+ lengthFields = 2
+ default:
+ b |= 127
+ lengthFields = 8
+ }
+ header = append(header, b)
+ for i := 0; i < lengthFields; i++ {
+ j := uint((lengthFields - i - 1) * 8)
+ b = byte((length >> j) & 0xff)
+ header = append(header, b)
+ }
+ if frame.header.MaskingKey != nil {
+ if len(frame.header.MaskingKey) != 4 {
+ return 0, ErrBadMaskingKey
+ }
+ header = append(header, frame.header.MaskingKey...)
+ frame.writer.Write(header)
+ data := make([]byte, length)
+ for i := range data {
+ data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
+ }
+ frame.writer.Write(data)
+ err = frame.writer.Flush()
+ return length, err
+ }
+ frame.writer.Write(header)
+ frame.writer.Write(msg)
+ err = frame.writer.Flush()
+ return length, err
+}
+
+func (frame *hybiFrameWriter) Close() error { return nil }
+
+type hybiFrameWriterFactory struct {
+ *bufio.Writer
+ needMaskingKey bool
+}
+
+func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
+ if buf.needMaskingKey {
+ frameHeader.MaskingKey, err = generateMaskingKey()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
+}
+
+type hybiFrameHandler struct {
+ conn *Conn
+ payloadType byte
+}
+
+func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
+ if handler.conn.IsServerConn() {
+ // The client MUST mask all frames sent to the server.
+ if frame.(*hybiFrameReader).header.MaskingKey == nil {
+ handler.WriteClose(closeStatusProtocolError)
+ return nil, io.EOF
+ }
+ } else {
+ // The server MUST NOT mask all frames.
+ if frame.(*hybiFrameReader).header.MaskingKey != nil {
+ handler.WriteClose(closeStatusProtocolError)
+ return nil, io.EOF
+ }
+ }
+ if header := frame.HeaderReader(); header != nil {
+ io.Copy(ioutil.Discard, header)
+ }
+ switch frame.PayloadType() {
+ case ContinuationFrame:
+ frame.(*hybiFrameReader).header.OpCode = handler.payloadType
+ case TextFrame, BinaryFrame:
+ handler.payloadType = frame.PayloadType()
+ case CloseFrame:
+ return nil, io.EOF
+ case PingFrame, PongFrame:
+ b := make([]byte, maxControlFramePayloadLength)
+ n, err := io.ReadFull(frame, b)
+ if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
+ return nil, err
+ }
+ io.Copy(ioutil.Discard, frame)
+ if frame.PayloadType() == PingFrame {
+ if _, err := handler.WritePong(b[:n]); err != nil {
+ return nil, err
+ }
+ }
+ return nil, nil
+ }
+ return frame, nil
+}
+
+func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
+ handler.conn.wio.Lock()
+ defer handler.conn.wio.Unlock()
+ w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
+ if err != nil {
+ return err
+ }
+ msg := make([]byte, 2)
+ binary.BigEndian.PutUint16(msg, uint16(status))
+ _, err = w.Write(msg)
+ w.Close()
+ return err
+}
+
+func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
+ handler.conn.wio.Lock()
+ defer handler.conn.wio.Unlock()
+ w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
+ if err != nil {
+ return 0, err
+ }
+ n, err = w.Write(msg)
+ w.Close()
+ return n, err
+}
+
+// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
+func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+ if buf == nil {
+ br := bufio.NewReader(rwc)
+ bw := bufio.NewWriter(rwc)
+ buf = bufio.NewReadWriter(br, bw)
+ }
+ ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
+ frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
+ frameWriterFactory: hybiFrameWriterFactory{
+ buf.Writer, request == nil},
+ PayloadType: TextFrame,
+ defaultCloseStatus: closeStatusNormal}
+ ws.frameHandler = &hybiFrameHandler{conn: ws}
+ return ws
+}
+
+// generateMaskingKey generates a masking key for a frame.
+func generateMaskingKey() (maskingKey []byte, err error) {
+ maskingKey = make([]byte, 4)
+ if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
+ return
+ }
+ return
+}
+
+// generateNonce generates a nonce consisting of a randomly selected 16-byte
+// value that has been base64-encoded.
+func generateNonce() (nonce []byte) {
+ key := make([]byte, 16)
+ if _, err := io.ReadFull(rand.Reader, key); err != nil {
+ panic(err)
+ }
+ nonce = make([]byte, 24)
+ base64.StdEncoding.Encode(nonce, key)
+ return
+}
+
+// removeZone removes IPv6 zone identifer from host.
+// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
+func removeZone(host string) string {
+ if !strings.HasPrefix(host, "[") {
+ return host
+ }
+ i := strings.LastIndex(host, "]")
+ if i < 0 {
+ return host
+ }
+ j := strings.LastIndex(host[:i], "%")
+ if j < 0 {
+ return host
+ }
+ return host[:j] + host[i:]
+}
+
+// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
+// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
+func getNonceAccept(nonce []byte) (expected []byte, err error) {
+ h := sha1.New()
+ if _, err = h.Write(nonce); err != nil {
+ return
+ }
+ if _, err = h.Write([]byte(websocketGUID)); err != nil {
+ return
+ }
+ expected = make([]byte, 28)
+ base64.StdEncoding.Encode(expected, h.Sum(nil))
+ return
+}
+
+// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
+func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
+ bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
+
+ // According to RFC 6874, an HTTP client, proxy, or other
+ // intermediary must remove any IPv6 zone identifier attached
+ // to an outgoing URI.
+ bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
+ bw.WriteString("Upgrade: websocket\r\n")
+ bw.WriteString("Connection: Upgrade\r\n")
+ nonce := generateNonce()
+ if config.handshakeData != nil {
+ nonce = []byte(config.handshakeData["key"])
+ }
+ bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
+ bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
+
+ if config.Version != ProtocolVersionHybi13 {
+ return ErrBadProtocolVersion
+ }
+
+ bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
+ if len(config.Protocol) > 0 {
+ bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
+ }
+ // TODO(ukai): send Sec-WebSocket-Extensions.
+ err = config.Header.WriteSubset(bw, handshakeHeader)
+ if err != nil {
+ return err
+ }
+
+ bw.WriteString("\r\n")
+ if err = bw.Flush(); err != nil {
+ return err
+ }
+
+ resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != 101 {
+ return ErrBadStatus
+ }
+ if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
+ strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
+ return ErrBadUpgrade
+ }
+ expectedAccept, err := getNonceAccept(nonce)
+ if err != nil {
+ return err
+ }
+ if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
+ return ErrChallengeResponse
+ }
+ if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
+ return ErrUnsupportedExtensions
+ }
+ offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
+ if offeredProtocol != "" {
+ protocolMatched := false
+ for i := 0; i < len(config.Protocol); i++ {
+ if config.Protocol[i] == offeredProtocol {
+ protocolMatched = true
+ break
+ }
+ }
+ if !protocolMatched {
+ return ErrBadWebSocketProtocol
+ }
+ config.Protocol = []string{offeredProtocol}
+ }
+
+ return nil
+}
+
+// newHybiClientConn creates a client WebSocket connection after handshake.
+func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
+ return newHybiConn(config, buf, rwc, nil)
+}
+
+// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
+type hybiServerHandshaker struct {
+ *Config
+ accept []byte
+}
+
+func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
+ c.Version = ProtocolVersionHybi13
+ if req.Method != "GET" {
+ return http.StatusMethodNotAllowed, ErrBadRequestMethod
+ }
+ // HTTP version can be safely ignored.
+
+ if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
+ !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
+ return http.StatusBadRequest, ErrNotWebSocket
+ }
+
+ key := req.Header.Get("Sec-Websocket-Key")
+ if key == "" {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+ version := req.Header.Get("Sec-Websocket-Version")
+ switch version {
+ case "13":
+ c.Version = ProtocolVersionHybi13
+ default:
+ return http.StatusBadRequest, ErrBadWebSocketVersion
+ }
+ var scheme string
+ if req.TLS != nil {
+ scheme = "wss"
+ } else {
+ scheme = "ws"
+ }
+ c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+ protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
+ if protocol != "" {
+ protocols := strings.Split(protocol, ",")
+ for i := 0; i < len(protocols); i++ {
+ c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
+ }
+ }
+ c.accept, err = getNonceAccept([]byte(key))
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ return http.StatusSwitchingProtocols, nil
+}
+
+// Origin parses the Origin header in req.
+// If the Origin header is not set, it returns nil and nil.
+func Origin(config *Config, req *http.Request) (*url.URL, error) {
+ var origin string
+ switch config.Version {
+ case ProtocolVersionHybi13:
+ origin = req.Header.Get("Origin")
+ }
+ if origin == "" {
+ return nil, nil
+ }
+ return url.ParseRequestURI(origin)
+}
+
+func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
+ if len(c.Protocol) > 0 {
+ if len(c.Protocol) != 1 {
+ // You need choose a Protocol in Handshake func in Server.
+ return ErrBadWebSocketProtocol
+ }
+ }
+ buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
+ buf.WriteString("Upgrade: websocket\r\n")
+ buf.WriteString("Connection: Upgrade\r\n")
+ buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
+ if len(c.Protocol) > 0 {
+ buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
+ }
+ // TODO(ukai): send Sec-WebSocket-Extensions.
+ if c.Header != nil {
+ err := c.Header.WriteSubset(buf, handshakeHeader)
+ if err != nil {
+ return err
+ }
+ }
+ buf.WriteString("\r\n")
+ return buf.Flush()
+}
+
+func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+ return newHybiServerConn(c.Config, buf, rwc, request)
+}
+
+// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
+func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+ return newHybiConn(config, buf, rwc, request)
+}
diff --git a/websocket/hybi_test.go b/websocket/hybi_test.go
new file mode 100644
index 0000000..9504aa2
--- /dev/null
+++ b/websocket/hybi_test.go
@@ -0,0 +1,608 @@
+// Copyright 2011 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.
+
+package websocket
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "testing"
+)
+
+// Test the getNonceAccept function with values in
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
+func TestSecWebSocketAccept(t *testing.T) {
+ nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==")
+ expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
+ accept, err := getNonceAccept(nonce)
+ if err != nil {
+ t.Errorf("getNonceAccept: returned error %v", err)
+ return
+ }
+ if !bytes.Equal(expected, accept) {
+ t.Errorf("getNonceAccept: expected %q got %q", expected, accept)
+ }
+}
+
+func TestHybiClientHandshake(t *testing.T) {
+ type test struct {
+ url, host string
+ }
+ tests := []test{
+ {"ws://server.example.com/chat", "server.example.com"},
+ {"ws://127.0.0.1/chat", "127.0.0.1"},
+ }
+ if _, err := url.ParseRequestURI("http://[fe80::1%25lo0]"); err == nil {
+ tests = append(tests, test{"ws://[fe80::1%25lo0]/chat", "[fe80::1]"})
+ }
+
+ for _, tt := range tests {
+ var b bytes.Buffer
+ bw := bufio.NewWriter(&b)
+ br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+Sec-WebSocket-Protocol: chat
+
+`))
+ var err error
+ var config Config
+ config.Location, err = url.ParseRequestURI(tt.url)
+ if err != nil {
+ t.Fatal("location url", err)
+ }
+ config.Origin, err = url.ParseRequestURI("http://example.com")
+ if err != nil {
+ t.Fatal("origin url", err)
+ }
+ config.Protocol = append(config.Protocol, "chat")
+ config.Protocol = append(config.Protocol, "superchat")
+ config.Version = ProtocolVersionHybi13
+ config.handshakeData = map[string]string{
+ "key": "dGhlIHNhbXBsZSBub25jZQ==",
+ }
+ if err := hybiClientHandshake(&config, br, bw); err != nil {
+ t.Fatal("handshake", err)
+ }
+ req, err := http.ReadRequest(bufio.NewReader(&b))
+ if err != nil {
+ t.Fatal("read request", err)
+ }
+ if req.Method != "GET" {
+ t.Errorf("request method expected GET, but got %s", req.Method)
+ }
+ if req.URL.Path != "/chat" {
+ t.Errorf("request path expected /chat, but got %s", req.URL.Path)
+ }
+ if req.Proto != "HTTP/1.1" {
+ t.Errorf("request proto expected HTTP/1.1, but got %s", req.Proto)
+ }
+ if req.Host != tt.host {
+ t.Errorf("request host expected %s, but got %s", tt.host, req.Host)
+ }
+ var expectedHeader = map[string]string{
+ "Connection": "Upgrade",
+ "Upgrade": "websocket",
+ "Sec-Websocket-Key": config.handshakeData["key"],
+ "Origin": config.Origin.String(),
+ "Sec-Websocket-Protocol": "chat, superchat",
+ "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13),
+ }
+ for k, v := range expectedHeader {
+ if req.Header.Get(k) != v {
+ t.Errorf("%s expected %s, but got %v", k, v, req.Header.Get(k))
+ }
+ }
+ }
+}
+
+func TestHybiClientHandshakeWithHeader(t *testing.T) {
+ b := bytes.NewBuffer([]byte{})
+ bw := bufio.NewWriter(b)
+ br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+Sec-WebSocket-Protocol: chat
+
+`))
+ var err error
+ config := new(Config)
+ config.Location, err = url.ParseRequestURI("ws://server.example.com/chat")
+ if err != nil {
+ t.Fatal("location url", err)
+ }
+ config.Origin, err = url.ParseRequestURI("http://example.com")
+ if err != nil {
+ t.Fatal("origin url", err)
+ }
+ config.Protocol = append(config.Protocol, "chat")
+ config.Protocol = append(config.Protocol, "superchat")
+ config.Version = ProtocolVersionHybi13
+ config.Header = http.Header(make(map[string][]string))
+ config.Header.Add("User-Agent", "test")
+
+ config.handshakeData = map[string]string{
+ "key": "dGhlIHNhbXBsZSBub25jZQ==",
+ }
+ err = hybiClientHandshake(config, br, bw)
+ if err != nil {
+ t.Errorf("handshake failed: %v", err)
+ }
+ req, err := http.ReadRequest(bufio.NewReader(b))
+ if err != nil {
+ t.Fatalf("read request: %v", err)
+ }
+ if req.Method != "GET" {
+ t.Errorf("request method expected GET, but got %q", req.Method)
+ }
+ if req.URL.Path != "/chat" {
+ t.Errorf("request path expected /chat, but got %q", req.URL.Path)
+ }
+ if req.Proto != "HTTP/1.1" {
+ t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
+ }
+ if req.Host != "server.example.com" {
+ t.Errorf("request Host expected server.example.com, but got %v", req.Host)
+ }
+ var expectedHeader = map[string]string{
+ "Connection": "Upgrade",
+ "Upgrade": "websocket",
+ "Sec-Websocket-Key": config.handshakeData["key"],
+ "Origin": config.Origin.String(),
+ "Sec-Websocket-Protocol": "chat, superchat",
+ "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13),
+ "User-Agent": "test",
+ }
+ for k, v := range expectedHeader {
+ if req.Header.Get(k) != v {
+ t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k)))
+ }
+ }
+}
+
+func TestHybiServerHandshake(t *testing.T) {
+ config := new(Config)
+ handshaker := &hybiServerHandshaker{Config: config}
+ br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Origin: http://example.com
+Sec-WebSocket-Protocol: chat, superchat
+Sec-WebSocket-Version: 13
+
+`))
+ req, err := http.ReadRequest(br)
+ if err != nil {
+ t.Fatal("request", err)
+ }
+ code, err := handshaker.ReadHandshake(br, req)
+ if err != nil {
+ t.Errorf("handshake failed: %v", err)
+ }
+ if code != http.StatusSwitchingProtocols {
+ t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
+ }
+ expectedProtocols := []string{"chat", "superchat"}
+ if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) {
+ t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol)
+ }
+ b := bytes.NewBuffer([]byte{})
+ bw := bufio.NewWriter(b)
+
+ config.Protocol = config.Protocol[:1]
+
+ err = handshaker.AcceptHandshake(bw)
+ if err != nil {
+ t.Errorf("handshake response failed: %v", err)
+ }
+ expectedResponse := strings.Join([]string{
+ "HTTP/1.1 101 Switching Protocols",
+ "Upgrade: websocket",
+ "Connection: Upgrade",
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
+ "Sec-WebSocket-Protocol: chat",
+ "", ""}, "\r\n")
+
+ if b.String() != expectedResponse {
+ t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
+ }
+}
+
+func TestHybiServerHandshakeNoSubProtocol(t *testing.T) {
+ config := new(Config)
+ handshaker := &hybiServerHandshaker{Config: config}
+ br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Origin: http://example.com
+Sec-WebSocket-Version: 13
+
+`))
+ req, err := http.ReadRequest(br)
+ if err != nil {
+ t.Fatal("request", err)
+ }
+ code, err := handshaker.ReadHandshake(br, req)
+ if err != nil {
+ t.Errorf("handshake failed: %v", err)
+ }
+ if code != http.StatusSwitchingProtocols {
+ t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
+ }
+ if len(config.Protocol) != 0 {
+ t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol))
+ }
+ b := bytes.NewBuffer([]byte{})
+ bw := bufio.NewWriter(b)
+
+ err = handshaker.AcceptHandshake(bw)
+ if err != nil {
+ t.Errorf("handshake response failed: %v", err)
+ }
+ expectedResponse := strings.Join([]string{
+ "HTTP/1.1 101 Switching Protocols",
+ "Upgrade: websocket",
+ "Connection: Upgrade",
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
+ "", ""}, "\r\n")
+
+ if b.String() != expectedResponse {
+ t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
+ }
+}
+
+func TestHybiServerHandshakeHybiBadVersion(t *testing.T) {
+ config := new(Config)
+ handshaker := &hybiServerHandshaker{Config: config}
+ br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Origin: http://example.com
+Sec-WebSocket-Protocol: chat, superchat
+Sec-WebSocket-Version: 9
+
+`))
+ req, err := http.ReadRequest(br)
+ if err != nil {
+ t.Fatal("request", err)
+ }
+ code, err := handshaker.ReadHandshake(br, req)
+ if err != ErrBadWebSocketVersion {
+ t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err)
+ }
+ if code != http.StatusBadRequest {
+ t.Errorf("status expected %q but got %q", http.StatusBadRequest, code)
+ }
+}
+
+func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) {
+ b := bytes.NewBuffer([]byte{})
+ frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false}
+ w, _ := frameWriterFactory.NewFrameWriter(TextFrame)
+ w.(*hybiFrameWriter).header = frameHeader
+ _, err := w.Write(testPayload)
+ w.Close()
+ if err != nil {
+ t.Errorf("Write error %q", err)
+ }
+ var expectedFrame []byte
+ expectedFrame = append(expectedFrame, testHeader...)
+ expectedFrame = append(expectedFrame, testMaskedPayload...)
+ if !bytes.Equal(expectedFrame, b.Bytes()) {
+ t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes())
+ }
+ frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)}
+ r, err := frameReaderFactory.NewFrameReader()
+ if err != nil {
+ t.Errorf("Read error %q", err)
+ }
+ if header := r.HeaderReader(); header == nil {
+ t.Errorf("no header")
+ } else {
+ actualHeader := make([]byte, r.Len())
+ n, err := header.Read(actualHeader)
+ if err != nil {
+ t.Errorf("Read header error %q", err)
+ } else {
+ if n < len(testHeader) {
+ t.Errorf("header too short %q got %q", testHeader, actualHeader[:n])
+ }
+ if !bytes.Equal(testHeader, actualHeader[:n]) {
+ t.Errorf("header expected %q got %q", testHeader, actualHeader[:n])
+ }
+ }
+ }
+ if trailer := r.TrailerReader(); trailer != nil {
+ t.Errorf("unexpected trailer %q", trailer)
+ }
+ frame := r.(*hybiFrameReader)
+ if frameHeader.Fin != frame.header.Fin ||
+ frameHeader.OpCode != frame.header.OpCode ||
+ len(testPayload) != int(frame.header.Length) {
+ t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame)
+ }
+ payload := make([]byte, len(testPayload))
+ _, err = r.Read(payload)
+ if err != nil && err != io.EOF {
+ t.Errorf("read %v", err)
+ }
+ if !bytes.Equal(testPayload, payload) {
+ t.Errorf("payload %q vs %q", testPayload, payload)
+ }
+}
+
+func TestHybiShortTextFrame(t *testing.T) {
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame}
+ payload := []byte("hello")
+ testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader)
+
+ payload = make([]byte, 125)
+ testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader)
+}
+
+func TestHybiShortMaskedTextFrame(t *testing.T) {
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame,
+ MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}}
+ payload := []byte("hello")
+ maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3}
+ header := []byte{0x81, 0x85}
+ header = append(header, frameHeader.MaskingKey...)
+ testHybiFrame(t, header, payload, maskedPayload, frameHeader)
+}
+
+func TestHybiShortBinaryFrame(t *testing.T) {
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame}
+ payload := []byte("hello")
+ testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader)
+
+ payload = make([]byte, 125)
+ testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader)
+}
+
+func TestHybiControlFrame(t *testing.T) {
+ payload := []byte("hello")
+
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame}
+ testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader)
+
+ frameHeader = &hybiFrameHeader{Fin: true, OpCode: PingFrame}
+ testHybiFrame(t, []byte{0x89, 0x00}, nil, nil, frameHeader)
+
+ frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame}
+ testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader)
+
+ frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame}
+ testHybiFrame(t, []byte{0x8A, 0x00}, nil, nil, frameHeader)
+
+ frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame}
+ payload = []byte{0x03, 0xe8} // 1000
+ testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader)
+}
+
+func TestHybiLongFrame(t *testing.T) {
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame}
+ payload := make([]byte, 126)
+ testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader)
+
+ payload = make([]byte, 65535)
+ testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader)
+
+ payload = make([]byte, 65536)
+ testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader)
+}
+
+func TestHybiClientRead(t *testing.T) {
+ wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
+ 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'}
+ br := bufio.NewReader(bytes.NewBuffer(wireData))
+ bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
+ conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
+
+ msg := make([]byte, 512)
+ n, err := conn.Read(msg)
+ if err != nil {
+ t.Errorf("read 1st frame, error %q", err)
+ }
+ if n != 5 {
+ t.Errorf("read 1st frame, expect 5, got %d", n)
+ }
+ if !bytes.Equal(wireData[2:7], msg[:n]) {
+ t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n])
+ }
+ n, err = conn.Read(msg)
+ if err != nil {
+ t.Errorf("read 2nd frame, error %q", err)
+ }
+ if n != 5 {
+ t.Errorf("read 2nd frame, expect 5, got %d", n)
+ }
+ if !bytes.Equal(wireData[16:21], msg[:n]) {
+ t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n])
+ }
+ n, err = conn.Read(msg)
+ if err == nil {
+ t.Errorf("read not EOF")
+ }
+ if n != 0 {
+ t.Errorf("expect read 0, got %d", n)
+ }
+}
+
+func TestHybiShortRead(t *testing.T) {
+ wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
+ 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'}
+ br := bufio.NewReader(bytes.NewBuffer(wireData))
+ bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
+ conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
+
+ step := 0
+ pos := 0
+ expectedPos := []int{2, 5, 16, 19}
+ expectedLen := []int{3, 2, 3, 2}
+ for {
+ msg := make([]byte, 3)
+ n, err := conn.Read(msg)
+ if step >= len(expectedPos) {
+ if err == nil {
+ t.Errorf("read not EOF")
+ }
+ if n != 0 {
+ t.Errorf("expect read 0, got %d", n)
+ }
+ return
+ }
+ pos = expectedPos[step]
+ endPos := pos + expectedLen[step]
+ if err != nil {
+ t.Errorf("read from %d, got error %q", pos, err)
+ return
+ }
+ if n != endPos-pos {
+ t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n)
+ }
+ if !bytes.Equal(wireData[pos:endPos], msg[:n]) {
+ t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n])
+ }
+ step++
+ }
+}
+
+func TestHybiServerRead(t *testing.T) {
+ wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20,
+ 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
+ 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20,
+ 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello
+ 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24,
+ 0x9a, 0xec, 0xc6, 0x48, 0x89, // world
+ }
+ br := bufio.NewReader(bytes.NewBuffer(wireData))
+ bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
+ conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request))
+
+ expected := [][]byte{[]byte("hello"), []byte("world")}
+
+ msg := make([]byte, 512)
+ n, err := conn.Read(msg)
+ if err != nil {
+ t.Errorf("read 1st frame, error %q", err)
+ }
+ if n != 5 {
+ t.Errorf("read 1st frame, expect 5, got %d", n)
+ }
+ if !bytes.Equal(expected[0], msg[:n]) {
+ t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n])
+ }
+
+ n, err = conn.Read(msg)
+ if err != nil {
+ t.Errorf("read 2nd frame, error %q", err)
+ }
+ if n != 5 {
+ t.Errorf("read 2nd frame, expect 5, got %d", n)
+ }
+ if !bytes.Equal(expected[1], msg[:n]) {
+ t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n])
+ }
+
+ n, err = conn.Read(msg)
+ if err == nil {
+ t.Errorf("read not EOF")
+ }
+ if n != 0 {
+ t.Errorf("expect read 0, got %d", n)
+ }
+}
+
+func TestHybiServerReadWithoutMasking(t *testing.T) {
+ wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'}
+ br := bufio.NewReader(bytes.NewBuffer(wireData))
+ bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
+ conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request))
+ // server MUST close the connection upon receiving a non-masked frame.
+ msg := make([]byte, 512)
+ _, err := conn.Read(msg)
+ if err != io.EOF {
+ t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err)
+ }
+}
+
+func TestHybiClientReadWithMasking(t *testing.T) {
+ wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20,
+ 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
+ }
+ br := bufio.NewReader(bytes.NewBuffer(wireData))
+ bw := bufio.NewWriter(bytes.NewBuffer([]byte{}))
+ conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil)
+
+ // client MUST close the connection upon receiving a masked frame.
+ msg := make([]byte, 512)
+ _, err := conn.Read(msg)
+ if err != io.EOF {
+ t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err)
+ }
+}
+
+// Test the hybiServerHandshaker supports firefox implementation and
+// checks Connection request header include (but it's not necessary
+// equal to) "upgrade"
+func TestHybiServerFirefoxHandshake(t *testing.T) {
+ config := new(Config)
+ handshaker := &hybiServerHandshaker{Config: config}
+ br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: keep-alive, upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Origin: http://example.com
+Sec-WebSocket-Protocol: chat, superchat
+Sec-WebSocket-Version: 13
+
+`))
+ req, err := http.ReadRequest(br)
+ if err != nil {
+ t.Fatal("request", err)
+ }
+ code, err := handshaker.ReadHandshake(br, req)
+ if err != nil {
+ t.Errorf("handshake failed: %v", err)
+ }
+ if code != http.StatusSwitchingProtocols {
+ t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code)
+ }
+ b := bytes.NewBuffer([]byte{})
+ bw := bufio.NewWriter(b)
+
+ config.Protocol = []string{"chat"}
+
+ err = handshaker.AcceptHandshake(bw)
+ if err != nil {
+ t.Errorf("handshake response failed: %v", err)
+ }
+ expectedResponse := strings.Join([]string{
+ "HTTP/1.1 101 Switching Protocols",
+ "Upgrade: websocket",
+ "Connection: Upgrade",
+ "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
+ "Sec-WebSocket-Protocol: chat",
+ "", ""}, "\r\n")
+
+ if b.String() != expectedResponse {
+ t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
+ }
+}
diff --git a/websocket/server.go b/websocket/server.go
new file mode 100644
index 0000000..0895dea
--- /dev/null
+++ b/websocket/server.go
@@ -0,0 +1,113 @@
+// 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.
+
+package websocket
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net/http"
+)
+
+func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
+ var hs serverHandshaker = &hybiServerHandshaker{Config: config}
+ code, err := hs.ReadHandshake(buf.Reader, req)
+ if err == ErrBadWebSocketVersion {
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
+ fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
+ buf.WriteString("\r\n")
+ buf.WriteString(err.Error())
+ buf.Flush()
+ return
+ }
+ if err != nil {
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
+ buf.WriteString("\r\n")
+ buf.WriteString(err.Error())
+ buf.Flush()
+ return
+ }
+ if handshake != nil {
+ err = handshake(config, req)
+ if err != nil {
+ code = http.StatusForbidden
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
+ buf.WriteString("\r\n")
+ buf.Flush()
+ return
+ }
+ }
+ err = hs.AcceptHandshake(buf.Writer)
+ if err != nil {
+ code = http.StatusBadRequest
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
+ buf.WriteString("\r\n")
+ buf.Flush()
+ return
+ }
+ conn = hs.NewServerConn(buf, rwc, req)
+ return
+}
+
+// Server represents a server of a WebSocket.
+type Server struct {
+ // Config is a WebSocket configuration for new WebSocket connection.
+ Config
+
+ // Handshake is an optional function in WebSocket handshake.
+ // For example, you can check, or don't check Origin header.
+ // Another example, you can select config.Protocol.
+ Handshake func(*Config, *http.Request) error
+
+ // Handler handles a WebSocket connection.
+ Handler
+}
+
+// ServeHTTP implements the http.Handler interface for a WebSocket
+func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ s.serveWebSocket(w, req)
+}
+
+func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
+ rwc, buf, err := w.(http.Hijacker).Hijack()
+ if err != nil {
+ panic("Hijack failed: " + err.Error())
+ }
+ // The server should abort the WebSocket connection if it finds
+ // the client did not send a handshake that matches with protocol
+ // specification.
+ defer rwc.Close()
+ conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
+ if err != nil {
+ return
+ }
+ if conn == nil {
+ panic("unexpected nil conn")
+ }
+ s.Handler(conn)
+}
+
+// Handler is a simple interface to a WebSocket browser client.
+// It checks if Origin header is valid URL by default.
+// You might want to verify websocket.Conn.Config().Origin in the func.
+// If you use Server instead of Handler, you could call websocket.Origin and
+// check the origin in your Handshake func. So, if you want to accept
+// non-browser clients, which do not send an Origin header, set a
+// Server.Handshake that does not check the origin.
+type Handler func(*Conn)
+
+func checkOrigin(config *Config, req *http.Request) (err error) {
+ config.Origin, err = Origin(config, req)
+ if err == nil && config.Origin == nil {
+ return fmt.Errorf("null origin")
+ }
+ return err
+}
+
+// ServeHTTP implements the http.Handler interface for a WebSocket
+func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ s := Server{Handler: h, Handshake: checkOrigin}
+ s.serveWebSocket(w, req)
+}
diff --git a/websocket/websocket.go b/websocket/websocket.go
new file mode 100644
index 0000000..6068400
--- /dev/null
+++ b/websocket/websocket.go
@@ -0,0 +1,412 @@
+// 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.
+
+// Package websocket implements a client and server for the WebSocket protocol
+// as specified in RFC 6455.
+package websocket // import "golang.org/x/net/websocket"
+
+import (
+ "bufio"
+ "crypto/tls"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "sync"
+ "time"
+)
+
+const (
+ ProtocolVersionHybi13 = 13
+ ProtocolVersionHybi = ProtocolVersionHybi13
+ SupportedProtocolVersion = "13"
+
+ ContinuationFrame = 0
+ TextFrame = 1
+ BinaryFrame = 2
+ CloseFrame = 8
+ PingFrame = 9
+ PongFrame = 10
+ UnknownFrame = 255
+)
+
+// ProtocolError represents WebSocket protocol errors.
+type ProtocolError struct {
+ ErrorString string
+}
+
+func (err *ProtocolError) Error() string { return err.ErrorString }
+
+var (
+ ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
+ ErrBadScheme = &ProtocolError{"bad scheme"}
+ ErrBadStatus = &ProtocolError{"bad status"}
+ ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
+ ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
+ ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
+ ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
+ ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
+ ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
+ ErrBadFrame = &ProtocolError{"bad frame"}
+ ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
+ ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
+ ErrBadRequestMethod = &ProtocolError{"bad method"}
+ ErrNotSupported = &ProtocolError{"not supported"}
+)
+
+// Addr is an implementation of net.Addr for WebSocket.
+type Addr struct {
+ *url.URL
+}
+
+// Network returns the network type for a WebSocket, "websocket".
+func (addr *Addr) Network() string { return "websocket" }
+
+// Config is a WebSocket configuration
+type Config struct {
+ // A WebSocket server address.
+ Location *url.URL
+
+ // A Websocket client origin.
+ Origin *url.URL
+
+ // WebSocket subprotocols.
+ Protocol []string
+
+ // WebSocket protocol version.
+ Version int
+
+ // TLS config for secure WebSocket (wss).
+ TlsConfig *tls.Config
+
+ // Additional header fields to be sent in WebSocket opening handshake.
+ Header http.Header
+
+ handshakeData map[string]string
+}
+
+// serverHandshaker is an interface to handle WebSocket server side handshake.
+type serverHandshaker interface {
+ // ReadHandshake reads handshake request message from client.
+ // Returns http response code and error if any.
+ ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
+
+ // AcceptHandshake accepts the client handshake request and sends
+ // handshake response back to client.
+ AcceptHandshake(buf *bufio.Writer) (err error)
+
+ // NewServerConn creates a new WebSocket connection.
+ NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
+}
+
+// frameReader is an interface to read a WebSocket frame.
+type frameReader interface {
+ // Reader is to read payload of the frame.
+ io.Reader
+
+ // PayloadType returns payload type.
+ PayloadType() byte
+
+ // HeaderReader returns a reader to read header of the frame.
+ HeaderReader() io.Reader
+
+ // TrailerReader returns a reader to read trailer of the frame.
+ // If it returns nil, there is no trailer in the frame.
+ TrailerReader() io.Reader
+
+ // Len returns total length of the frame, including header and trailer.
+ Len() int
+}
+
+// frameReaderFactory is an interface to creates new frame reader.
+type frameReaderFactory interface {
+ NewFrameReader() (r frameReader, err error)
+}
+
+// frameWriter is an interface to write a WebSocket frame.
+type frameWriter interface {
+ // Writer is to write payload of the frame.
+ io.WriteCloser
+}
+
+// frameWriterFactory is an interface to create new frame writer.
+type frameWriterFactory interface {
+ NewFrameWriter(payloadType byte) (w frameWriter, err error)
+}
+
+type frameHandler interface {
+ HandleFrame(frame frameReader) (r frameReader, err error)
+ WriteClose(status int) (err error)
+}
+
+// Conn represents a WebSocket connection.
+type Conn struct {
+ config *Config
+ request *http.Request
+
+ buf *bufio.ReadWriter
+ rwc io.ReadWriteCloser
+
+ rio sync.Mutex
+ frameReaderFactory
+ frameReader
+
+ wio sync.Mutex
+ frameWriterFactory
+
+ frameHandler
+ PayloadType byte
+ defaultCloseStatus int
+}
+
+// Read implements the io.Reader interface:
+// it reads data of a frame from the WebSocket connection.
+// if msg is not large enough for the frame data, it fills the msg and next Read
+// will read the rest of the frame data.
+// it reads Text frame or Binary frame.
+func (ws *Conn) Read(msg []byte) (n int, err error) {
+ ws.rio.Lock()
+ defer ws.rio.Unlock()
+again:
+ if ws.frameReader == nil {
+ frame, err := ws.frameReaderFactory.NewFrameReader()
+ if err != nil {
+ return 0, err
+ }
+ ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
+ if err != nil {
+ return 0, err
+ }
+ if ws.frameReader == nil {
+ goto again
+ }
+ }
+ n, err = ws.frameReader.Read(msg)
+ if err == io.EOF {
+ if trailer := ws.frameReader.TrailerReader(); trailer != nil {
+ io.Copy(ioutil.Discard, trailer)
+ }
+ ws.frameReader = nil
+ goto again
+ }
+ return n, err
+}
+
+// Write implements the io.Writer interface:
+// it writes data as a frame to the WebSocket connection.
+func (ws *Conn) Write(msg []byte) (n int, err error) {
+ ws.wio.Lock()
+ defer ws.wio.Unlock()
+ w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
+ if err != nil {
+ return 0, err
+ }
+ n, err = w.Write(msg)
+ w.Close()
+ if err != nil {
+ return n, err
+ }
+ return n, err
+}
+
+// Close implements the io.Closer interface.
+func (ws *Conn) Close() error {
+ err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
+ err1 := ws.rwc.Close()
+ if err != nil {
+ return err
+ }
+ return err1
+}
+
+func (ws *Conn) IsClientConn() bool { return ws.request == nil }
+func (ws *Conn) IsServerConn() bool { return ws.request != nil }
+
+// LocalAddr returns the WebSocket Origin for the connection for client, or
+// the WebSocket location for server.
+func (ws *Conn) LocalAddr() net.Addr {
+ if ws.IsClientConn() {
+ return &Addr{ws.config.Origin}
+ }
+ return &Addr{ws.config.Location}
+}
+
+// RemoteAddr returns the WebSocket location for the connection for client, or
+// the Websocket Origin for server.
+func (ws *Conn) RemoteAddr() net.Addr {
+ if ws.IsClientConn() {
+ return &Addr{ws.config.Location}
+ }
+ return &Addr{ws.config.Origin}
+}
+
+var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
+
+// SetDeadline sets the connection's network read & write deadlines.
+func (ws *Conn) SetDeadline(t time.Time) error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetDeadline(t)
+ }
+ return errSetDeadline
+}
+
+// SetReadDeadline sets the connection's network read deadline.
+func (ws *Conn) SetReadDeadline(t time.Time) error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetReadDeadline(t)
+ }
+ return errSetDeadline
+}
+
+// SetWriteDeadline sets the connection's network write deadline.
+func (ws *Conn) SetWriteDeadline(t time.Time) error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetWriteDeadline(t)
+ }
+ return errSetDeadline
+}
+
+// Config returns the WebSocket config.
+func (ws *Conn) Config() *Config { return ws.config }
+
+// Request returns the http request upgraded to the WebSocket.
+// It is nil for client side.
+func (ws *Conn) Request() *http.Request { return ws.request }
+
+// Codec represents a symmetric pair of functions that implement a codec.
+type Codec struct {
+ Marshal func(v interface{}) (data []byte, payloadType byte, err error)
+ Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
+}
+
+// Send sends v marshaled by cd.Marshal as single frame to ws.
+func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
+ data, payloadType, err := cd.Marshal(v)
+ if err != nil {
+ return err
+ }
+ ws.wio.Lock()
+ defer ws.wio.Unlock()
+ w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(data)
+ w.Close()
+ return err
+}
+
+// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
+func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
+ ws.rio.Lock()
+ defer ws.rio.Unlock()
+ if ws.frameReader != nil {
+ _, err = io.Copy(ioutil.Discard, ws.frameReader)
+ if err != nil {
+ return err
+ }
+ ws.frameReader = nil
+ }
+again:
+ frame, err := ws.frameReaderFactory.NewFrameReader()
+ if err != nil {
+ return err
+ }
+ frame, err = ws.frameHandler.HandleFrame(frame)
+ if err != nil {
+ return err
+ }
+ if frame == nil {
+ goto again
+ }
+ payloadType := frame.PayloadType()
+ data, err := ioutil.ReadAll(frame)
+ if err != nil {
+ return err
+ }
+ return cd.Unmarshal(data, payloadType, v)
+}
+
+func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
+ switch data := v.(type) {
+ case string:
+ return []byte(data), TextFrame, nil
+ case []byte:
+ return data, BinaryFrame, nil
+ }
+ return nil, UnknownFrame, ErrNotSupported
+}
+
+func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
+ switch data := v.(type) {
+ case *string:
+ *data = string(msg)
+ return nil
+ case *[]byte:
+ *data = msg
+ return nil
+ }
+ return ErrNotSupported
+}
+
+/*
+Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
+To send/receive text frame, use string type.
+To send/receive binary frame, use []byte type.
+
+Trivial usage:
+
+ import "websocket"
+
+ // receive text frame
+ var message string
+ websocket.Message.Receive(ws, &message)
+
+ // send text frame
+ message = "hello"
+ websocket.Message.Send(ws, message)
+
+ // receive binary frame
+ var data []byte
+ websocket.Message.Receive(ws, &data)
+
+ // send binary frame
+ data = []byte{0, 1, 2}
+ websocket.Message.Send(ws, data)
+
+*/
+var Message = Codec{marshal, unmarshal}
+
+func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
+ msg, err = json.Marshal(v)
+ return msg, TextFrame, err
+}
+
+func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
+ return json.Unmarshal(msg, v)
+}
+
+/*
+JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
+
+Trivial usage:
+
+ import "websocket"
+
+ type T struct {
+ Msg string
+ Count int
+ }
+
+ // receive JSON type T
+ var data T
+ websocket.JSON.Receive(ws, &data)
+
+ // send JSON type T
+ websocket.JSON.Send(ws, data)
+*/
+var JSON = Codec{jsonMarshal, jsonUnmarshal}
diff --git a/websocket/websocket_test.go b/websocket/websocket_test.go
new file mode 100644
index 0000000..05b7e53
--- /dev/null
+++ b/websocket/websocket_test.go
@@ -0,0 +1,587 @@
+// 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.
+
+package websocket
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "reflect"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+var serverAddr string
+var once sync.Once
+
+func echoServer(ws *Conn) {
+ defer ws.Close()
+ io.Copy(ws, ws)
+}
+
+type Count struct {
+ S string
+ N int
+}
+
+func countServer(ws *Conn) {
+ defer ws.Close()
+ for {
+ var count Count
+ err := JSON.Receive(ws, &count)
+ if err != nil {
+ return
+ }
+ count.N++
+ count.S = strings.Repeat(count.S, count.N)
+ err = JSON.Send(ws, count)
+ if err != nil {
+ return
+ }
+ }
+}
+
+type testCtrlAndDataHandler struct {
+ hybiFrameHandler
+}
+
+func (h *testCtrlAndDataHandler) WritePing(b []byte) (int, error) {
+ h.hybiFrameHandler.conn.wio.Lock()
+ defer h.hybiFrameHandler.conn.wio.Unlock()
+ w, err := h.hybiFrameHandler.conn.frameWriterFactory.NewFrameWriter(PingFrame)
+ if err != nil {
+ return 0, err
+ }
+ n, err := w.Write(b)
+ w.Close()
+ return n, err
+}
+
+func ctrlAndDataServer(ws *Conn) {
+ defer ws.Close()
+ h := &testCtrlAndDataHandler{hybiFrameHandler: hybiFrameHandler{conn: ws}}
+ ws.frameHandler = h
+
+ go func() {
+ for i := 0; ; i++ {
+ var b []byte
+ if i%2 != 0 { // with or without payload
+ b = []byte(fmt.Sprintf("#%d-CONTROL-FRAME-FROM-SERVER", i))
+ }
+ if _, err := h.WritePing(b); err != nil {
+ break
+ }
+ if _, err := h.WritePong(b); err != nil { // unsolicited pong
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ }()
+
+ b := make([]byte, 128)
+ for {
+ n, err := ws.Read(b)
+ if err != nil {
+ break
+ }
+ if _, err := ws.Write(b[:n]); err != nil {
+ break
+ }
+ }
+}
+
+func subProtocolHandshake(config *Config, req *http.Request) error {
+ for _, proto := range config.Protocol {
+ if proto == "chat" {
+ config.Protocol = []string{proto}
+ return nil
+ }
+ }
+ return ErrBadWebSocketProtocol
+}
+
+func subProtoServer(ws *Conn) {
+ for _, proto := range ws.Config().Protocol {
+ io.WriteString(ws, proto)
+ }
+}
+
+func startServer() {
+ http.Handle("/echo", Handler(echoServer))
+ http.Handle("/count", Handler(countServer))
+ http.Handle("/ctrldata", Handler(ctrlAndDataServer))
+ subproto := Server{
+ Handshake: subProtocolHandshake,
+ Handler: Handler(subProtoServer),
+ }
+ http.Handle("/subproto", subproto)
+ server := httptest.NewServer(nil)
+ serverAddr = server.Listener.Addr().String()
+ log.Print("Test WebSocket server listening on ", serverAddr)
+}
+
+func newConfig(t *testing.T, path string) *Config {
+ config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost")
+ return config
+}
+
+func TestEcho(t *testing.T) {
+ once.Do(startServer)
+
+ // websocket.Dial()
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+ conn, err := NewClient(newConfig(t, "/echo"), client)
+ if err != nil {
+ t.Errorf("WebSocket handshake error: %v", err)
+ return
+ }
+
+ msg := []byte("hello, world\n")
+ if _, err := conn.Write(msg); err != nil {
+ t.Errorf("Write: %v", err)
+ }
+ var actual_msg = make([]byte, 512)
+ n, err := conn.Read(actual_msg)
+ if err != nil {
+ t.Errorf("Read: %v", err)
+ }
+ actual_msg = actual_msg[0:n]
+ if !bytes.Equal(msg, actual_msg) {
+ t.Errorf("Echo: expected %q got %q", msg, actual_msg)
+ }
+ conn.Close()
+}
+
+func TestAddr(t *testing.T) {
+ once.Do(startServer)
+
+ // websocket.Dial()
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+ conn, err := NewClient(newConfig(t, "/echo"), client)
+ if err != nil {
+ t.Errorf("WebSocket handshake error: %v", err)
+ return
+ }
+
+ ra := conn.RemoteAddr().String()
+ if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") {
+ t.Errorf("Bad remote addr: %v", ra)
+ }
+ la := conn.LocalAddr().String()
+ if !strings.HasPrefix(la, "http://") {
+ t.Errorf("Bad local addr: %v", la)
+ }
+ conn.Close()
+}
+
+func TestCount(t *testing.T) {
+ once.Do(startServer)
+
+ // websocket.Dial()
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+ conn, err := NewClient(newConfig(t, "/count"), client)
+ if err != nil {
+ t.Errorf("WebSocket handshake error: %v", err)
+ return
+ }
+
+ var count Count
+ count.S = "hello"
+ if err := JSON.Send(conn, count); err != nil {
+ t.Errorf("Write: %v", err)
+ }
+ if err := JSON.Receive(conn, &count); err != nil {
+ t.Errorf("Read: %v", err)
+ }
+ if count.N != 1 {
+ t.Errorf("count: expected %d got %d", 1, count.N)
+ }
+ if count.S != "hello" {
+ t.Errorf("count: expected %q got %q", "hello", count.S)
+ }
+ if err := JSON.Send(conn, count); err != nil {
+ t.Errorf("Write: %v", err)
+ }
+ if err := JSON.Receive(conn, &count); err != nil {
+ t.Errorf("Read: %v", err)
+ }
+ if count.N != 2 {
+ t.Errorf("count: expected %d got %d", 2, count.N)
+ }
+ if count.S != "hellohello" {
+ t.Errorf("count: expected %q got %q", "hellohello", count.S)
+ }
+ conn.Close()
+}
+
+func TestWithQuery(t *testing.T) {
+ once.Do(startServer)
+
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+
+ config := newConfig(t, "/echo")
+ config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr))
+ if err != nil {
+ t.Fatal("location url", err)
+ }
+
+ ws, err := NewClient(config, client)
+ if err != nil {
+ t.Errorf("WebSocket handshake: %v", err)
+ return
+ }
+ ws.Close()
+}
+
+func testWithProtocol(t *testing.T, subproto []string) (string, error) {
+ once.Do(startServer)
+
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+
+ config := newConfig(t, "/subproto")
+ config.Protocol = subproto
+
+ ws, err := NewClient(config, client)
+ if err != nil {
+ return "", err
+ }
+ msg := make([]byte, 16)
+ n, err := ws.Read(msg)
+ if err != nil {
+ return "", err
+ }
+ ws.Close()
+ return string(msg[:n]), nil
+}
+
+func TestWithProtocol(t *testing.T) {
+ proto, err := testWithProtocol(t, []string{"chat"})
+ if err != nil {
+ t.Errorf("SubProto: unexpected error: %v", err)
+ }
+ if proto != "chat" {
+ t.Errorf("SubProto: expected %q, got %q", "chat", proto)
+ }
+}
+
+func TestWithTwoProtocol(t *testing.T) {
+ proto, err := testWithProtocol(t, []string{"test", "chat"})
+ if err != nil {
+ t.Errorf("SubProto: unexpected error: %v", err)
+ }
+ if proto != "chat" {
+ t.Errorf("SubProto: expected %q, got %q", "chat", proto)
+ }
+}
+
+func TestWithBadProtocol(t *testing.T) {
+ _, err := testWithProtocol(t, []string{"test"})
+ if err != ErrBadStatus {
+ t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err)
+ }
+}
+
+func TestHTTP(t *testing.T) {
+ once.Do(startServer)
+
+ // If the client did not send a handshake that matches the protocol
+ // specification, the server MUST return an HTTP response with an
+ // appropriate error code (such as 400 Bad Request)
+ resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr))
+ if err != nil {
+ t.Errorf("Get: error %#v", err)
+ return
+ }
+ if resp == nil {
+ t.Error("Get: resp is null")
+ return
+ }
+ if resp.StatusCode != http.StatusBadRequest {
+ t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode)
+ }
+}
+
+func TestTrailingSpaces(t *testing.T) {
+ // http://code.google.com/p/go/issues/detail?id=955
+ // The last runs of this create keys with trailing spaces that should not be
+ // generated by the client.
+ once.Do(startServer)
+ config := newConfig(t, "/echo")
+ for i := 0; i < 30; i++ {
+ // body
+ ws, err := DialConfig(config)
+ if err != nil {
+ t.Errorf("Dial #%d failed: %v", i, err)
+ break
+ }
+ ws.Close()
+ }
+}
+
+func TestDialConfigBadVersion(t *testing.T) {
+ once.Do(startServer)
+ config := newConfig(t, "/echo")
+ config.Version = 1234
+
+ _, err := DialConfig(config)
+
+ if dialerr, ok := err.(*DialError); ok {
+ if dialerr.Err != ErrBadProtocolVersion {
+ t.Errorf("dial expected err %q but got %q", ErrBadProtocolVersion, dialerr.Err)
+ }
+ }
+}
+
+func TestSmallBuffer(t *testing.T) {
+ // http://code.google.com/p/go/issues/detail?id=1145
+ // Read should be able to handle reading a fragment of a frame.
+ once.Do(startServer)
+
+ // websocket.Dial()
+ client, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+ conn, err := NewClient(newConfig(t, "/echo"), client)
+ if err != nil {
+ t.Errorf("WebSocket handshake error: %v", err)
+ return
+ }
+
+ msg := []byte("hello, world\n")
+ if _, err := conn.Write(msg); err != nil {
+ t.Errorf("Write: %v", err)
+ }
+ var small_msg = make([]byte, 8)
+ n, err := conn.Read(small_msg)
+ if err != nil {
+ t.Errorf("Read: %v", err)
+ }
+ if !bytes.Equal(msg[:len(small_msg)], small_msg) {
+ t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg)
+ }
+ var second_msg = make([]byte, len(msg))
+ n, err = conn.Read(second_msg)
+ if err != nil {
+ t.Errorf("Read: %v", err)
+ }
+ second_msg = second_msg[0:n]
+ if !bytes.Equal(msg[len(small_msg):], second_msg) {
+ t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg)
+ }
+ conn.Close()
+}
+
+var parseAuthorityTests = []struct {
+ in *url.URL
+ out string
+}{
+ {
+ &url.URL{
+ Scheme: "ws",
+ Host: "www.google.com",
+ },
+ "www.google.com:80",
+ },
+ {
+ &url.URL{
+ Scheme: "wss",
+ Host: "www.google.com",
+ },
+ "www.google.com:443",
+ },
+ {
+ &url.URL{
+ Scheme: "ws",
+ Host: "www.google.com:80",
+ },
+ "www.google.com:80",
+ },
+ {
+ &url.URL{
+ Scheme: "wss",
+ Host: "www.google.com:443",
+ },
+ "www.google.com:443",
+ },
+ // some invalid ones for parseAuthority. parseAuthority doesn't
+ // concern itself with the scheme unless it actually knows about it
+ {
+ &url.URL{
+ Scheme: "http",
+ Host: "www.google.com",
+ },
+ "www.google.com",
+ },
+ {
+ &url.URL{
+ Scheme: "http",
+ Host: "www.google.com:80",
+ },
+ "www.google.com:80",
+ },
+ {
+ &url.URL{
+ Scheme: "asdf",
+ Host: "127.0.0.1",
+ },
+ "127.0.0.1",
+ },
+ {
+ &url.URL{
+ Scheme: "asdf",
+ Host: "www.google.com",
+ },
+ "www.google.com",
+ },
+}
+
+func TestParseAuthority(t *testing.T) {
+ for _, tt := range parseAuthorityTests {
+ out := parseAuthority(tt.in)
+ if out != tt.out {
+ t.Errorf("got %v; want %v", out, tt.out)
+ }
+ }
+}
+
+type closerConn struct {
+ net.Conn
+ closed int // count of the number of times Close was called
+}
+
+func (c *closerConn) Close() error {
+ c.closed++
+ return c.Conn.Close()
+}
+
+func TestClose(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("see golang.org/issue/11454")
+ }
+
+ once.Do(startServer)
+
+ conn, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal("dialing", err)
+ }
+
+ cc := closerConn{Conn: conn}
+
+ client, err := NewClient(newConfig(t, "/echo"), &cc)
+ if err != nil {
+ t.Fatalf("WebSocket handshake: %v", err)
+ }
+
+ // set the deadline to ten minutes ago, which will have expired by the time
+ // client.Close sends the close status frame.
+ conn.SetDeadline(time.Now().Add(-10 * time.Minute))
+
+ if err := client.Close(); err == nil {
+ t.Errorf("ws.Close(): expected error, got %v", err)
+ }
+ if cc.closed < 1 {
+ t.Fatalf("ws.Close(): expected underlying ws.rwc.Close to be called > 0 times, got: %v", cc.closed)
+ }
+}
+
+var originTests = []struct {
+ req *http.Request
+ origin *url.URL
+}{
+ {
+ req: &http.Request{
+ Header: http.Header{
+ "Origin": []string{"http://www.example.com"},
+ },
+ },
+ origin: &url.URL{
+ Scheme: "http",
+ Host: "www.example.com",
+ },
+ },
+ {
+ req: &http.Request{},
+ },
+}
+
+func TestOrigin(t *testing.T) {
+ conf := newConfig(t, "/echo")
+ conf.Version = ProtocolVersionHybi13
+ for i, tt := range originTests {
+ origin, err := Origin(conf, tt.req)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if !reflect.DeepEqual(origin, tt.origin) {
+ t.Errorf("#%d: got origin %v; want %v", i, origin, tt.origin)
+ continue
+ }
+ }
+}
+
+func TestCtrlAndData(t *testing.T) {
+ once.Do(startServer)
+
+ c, err := net.Dial("tcp", serverAddr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ws, err := NewClient(newConfig(t, "/ctrldata"), c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ws.Close()
+
+ h := &testCtrlAndDataHandler{hybiFrameHandler: hybiFrameHandler{conn: ws}}
+ ws.frameHandler = h
+
+ b := make([]byte, 128)
+ for i := 0; i < 2; i++ {
+ data := []byte(fmt.Sprintf("#%d-DATA-FRAME-FROM-CLIENT", i))
+ if _, err := ws.Write(data); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ var ctrl []byte
+ if i%2 != 0 { // with or without payload
+ ctrl = []byte(fmt.Sprintf("#%d-CONTROL-FRAME-FROM-CLIENT", i))
+ }
+ if _, err := h.WritePing(ctrl); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ n, err := ws.Read(b)
+ if err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ if !bytes.Equal(b[:n], data) {
+ t.Fatalf("#%d: got %v; want %v", i, b[:n], data)
+ }
+ }
+}