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='ýäè'>&#xA0;</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.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00FD;&#x00E4;&#x00E8;</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 &#x00EF;&#x00BB;&#x00BF; 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 &amp; 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='ýäè'>&#xA0;</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.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00FD;&#x00E4;&#x00E8;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00FD;&#x00E4;&#x00E8;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00FD;&#x00E4;&#x00E8;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</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 &amp; 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='ýäè'>&#xA0;</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.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</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 &amp; 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 "&lt;") 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 "&lt;" 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&lt;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 = "&amp;"
+		case '\'':
+			// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
+			esc = "&#39;"
+		case '<':
+			esc = "&lt;"
+		case '>':
+			esc = "&gt;"
+		case '"':
+			// "&#34;" is shorter than "&quot;".
+			esc = "&#34;"
+		case '\r':
+			esc = "&#13;"
+		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 "&lt;". 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 "&lt;" to become "<". It unescapes a
+// larger range of entities than EscapeString escapes. For example, "&aacute;"
+// unescapes to "á", as does "&#225;" 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",
+		"&amp; &gt; &lt;",
+		"& > <",
+	},
+	// Handle hitting the end of the string.
+	{
+		"stringEnd",
+		"&amp &amp",
+		"& &",
+	},
+	// Handle entities with two codepoints.
+	{
+		"multiCodepoint",
+		"text &gesl; blah",
+		"text \u22db\ufe00 blah",
+	},
+	// Handle decimal numeric entities.
+	{
+		"decimalEntity",
+		"Delta = &#916; ",
+		"Delta = Δ ",
+	},
+	// Handle hexadecimal numeric entities.
+	{
+		"hexadecimalEntity",
+		"Lambda = &#x3bb; = &#X3Bb ",
+		"Lambda = λ = λ ",
+	},
+	// Handle numeric early termination.
+	{
+		"numericEnds",
+		"&# &#x &#128;43 &copy = &#169f = &#xa9",
+		"&# &#x €43 © = ©f = ©",
+	},
+	// Handle numeric ISO-8859-1 entity replacements.
+	{
+		"numericReplacements",
+		"Footnote&#x87;",
+		"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&amp;b`,
+		`a &amp b`,
+		`&quot;`,
+		`"`,
+		`"<&>"`,
+		`&quot;&lt;&amp;&gt;&quot;`,
+		`3&5==1 && 0<1, "0&lt;1", a+acute=&aacute;`,
+		`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&lt;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&lt;1"`,
+		4: `.	.	<p id="A" foo="abc&#34;def">`,
+		5: `.	.	.	"2"`,
+		6: `.	.	.	<b empty="">`,
+		7: `.	.	.	.	"3"`,
+		8: `.	.	.	<i backslash="\">`,
+		9: `.	.	.	.	"&amp;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&lt;1<p id="A" foo="abc&#34;def">` +
+		`2<b empty="">3</b><i backslash="\">&amp;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(&#34;hello &#34;)...)</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, &#34;world&#34;...)</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&lt;- int = c
+    var crecv &lt;-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{&#34;Feb&#34;, 14},
+        Date{&#34;Nov&#34;, 11},
+        Date{&#34;Dec&#34;, 25},
+    }
+    <span class="comment">// Struct values, type name elided; always legal.</span>
+    holiday2 := []Date{
+        {&#34;Feb&#34;, 14},
+        {&#34;Nov&#34;, 11},
+        {&#34;Dec&#34;, 25},
+    }
+    <span class="comment">// Pointers, fully qualified, always legal.</span>
+    holiday3 := []*Date{
+        &amp;Date{&#34;Feb&#34;, 14},
+        &amp;Date{&#34;Nov&#34;, 11},
+        &amp;Date{&#34;Dec&#34;, 25},
+    }
+    <span class="comment">// Pointers, type name elided; legal in Go 1.</span>
+    holiday4 := []*Date{
+        {&#34;Feb&#34;, 14},
+        {&#34;Nov&#34;, 11},
+        {&#34;Dec&#34;, 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 = &lt;-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 := &#39;δ&#39; <span class="comment">// delta has type rune.</span>
+    var DELTA rune
+    DELTA = unicode.ToUpper(delta)
+    epsilon := unicode.ToLower(DELTA + 1)
+    if epsilon != &#39;δ&#39;+1 {
+        log.Fatal(&#34;inconsistent casing for Greek&#34;)
+    }</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{&#34;Sunday&#34;: 0, &#34;Monday&#34;: 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 &lt; 5; i++ {
+            for j := 0; j &lt; 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{&#34;Christmas&#34;, &#34;XMas&#34;}
+    Thanksgiving := Day{&#34;Thanksgiving&#34;, &#34;Turkey&#34;}
+    holiday := map[Day]bool{
+        Christmas:    true,
+        Thanksgiving: true,
+    }
+    fmt.Printf(&#34;Christmas is a holiday: %t\n&#34;, 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>&lt;</code> <code>&lt;=</code>
+<code>&gt;</code> <code>&gt;=</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>&#8224;</sup></li>
+<li><code>go/types</code></li>
+</ul>
+
+<p>
+(<sup>&#8224;</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(&#34;%s:%d: %s&#34;, 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(&#34;syntax error&#34;)</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&#39;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(&#34;Sleeping for %.3fs\n&#34;, 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(&#34;timeout&#34;, 30*time.Second, &#34;how long to wait for completion&#34;)</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(&#34;%s already exists&#34;, 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 == &#34;pictures&#34; { <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(&#34;.&#34;, 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(&#34;%x&#34;, 23)
+    const expect = &#34;17&#34;
+    if expect != got {
+        b.Fatalf(&#34;expected %q; got %q&#34;, expect, got)
+    }
+    b.StartTimer()
+    for i := 0; i &lt; b.N; i++ {
+        fmt.Sprintf(&#34;%x&#34;, 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&gt;BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO>BAR"
+
+#data
+FOO&gtBAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO>BAR"
+
+#data
+FOO&gt BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO> BAR"
+
+#data
+FOO&gt;;;BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO>;;BAR"
+
+#data
+I'm &notit; I tell you
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "I'm ¬it; I tell you"
+
+#data
+I'm &notin; 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&&&&gt;BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO&&&>BAR"
+
+#data
+FOO&#41;BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO)BAR"
+
+#data
+FOO&#x41;BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOABAR"
+
+#data
+FOO&#X41;BAR
+#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&#xBAR
+#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&#41BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO)BAR"
+
+#data
+FOO&#x41BAR
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO䆺R"
+
+#data
+FOO&#x41ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOAZOO"
+
+#data
+FOO&#x0000;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#x0078;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOxZOO"
+
+#data
+FOO&#x0079;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOyZOO"
+
+#data
+FOO&#x0080;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO€ZOO"
+
+#data
+FOO&#x0081;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x0082;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO‚ZOO"
+
+#data
+FOO&#x0083;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOƒZOO"
+
+#data
+FOO&#x0084;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO„ZOO"
+
+#data
+FOO&#x0085;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO…ZOO"
+
+#data
+FOO&#x0086;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO†ZOO"
+
+#data
+FOO&#x0087;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO‡ZOO"
+
+#data
+FOO&#x0088;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOˆZOO"
+
+#data
+FOO&#x0089;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO‰ZOO"
+
+#data
+FOO&#x008A;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOŠZOO"
+
+#data
+FOO&#x008B;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO‹ZOO"
+
+#data
+FOO&#x008C;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOŒZOO"
+
+#data
+FOO&#x008D;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x008E;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOŽZOO"
+
+#data
+FOO&#x008F;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x0090;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x0091;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO‘ZOO"
+
+#data
+FOO&#x0092;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO’ZOO"
+
+#data
+FOO&#x0093;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO“ZOO"
+
+#data
+FOO&#x0094;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO”ZOO"
+
+#data
+FOO&#x0095;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO•ZOO"
+
+#data
+FOO&#x0096;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO–ZOO"
+
+#data
+FOO&#x0097;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO—ZOO"
+
+#data
+FOO&#x0098;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO˜ZOO"
+
+#data
+FOO&#x0099;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO™ZOO"
+
+#data
+FOO&#x009A;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOšZOO"
+
+#data
+FOO&#x009B;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO›ZOO"
+
+#data
+FOO&#x009C;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOœZOO"
+
+#data
+FOO&#x009D;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x009E;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOžZOO"
+
+#data
+FOO&#x009F;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOŸZOO"
+
+#data
+FOO&#x00A0;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO ZOO"
+
+#data
+FOO&#xD7FF;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO퟿ZOO"
+
+#data
+FOO&#xD800;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#xD801;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#xDFFE;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#xDFFF;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#xE000;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOOZOO"
+
+#data
+FOO&#x10FFFE;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO􏿾ZOO"
+
+#data
+FOO&#x1087D4;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO􈟔ZOO"
+
+#data
+FOO&#x10FFFF;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO􏿿ZOO"
+
+#data
+FOO&#x110000;ZOO
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "FOO�ZOO"
+
+#data
+FOO&#xFFFFFF;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&gt;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&gt=YY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&gt=YY"
+
+#data
+<div bar="ZZ&gt0YY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&gt0YY"
+
+#data
+<div bar="ZZ&gt9YY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&gt9YY"
+
+#data
+<div bar="ZZ&gtaYY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&gtaYY"
+
+#data
+<div bar="ZZ&gtZYY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&gtZYY"
+
+#data
+<div bar="ZZ&gt YY"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ> YY"
+
+#data
+<div bar="ZZ&gt"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ>"
+
+#data
+<div bar='ZZ&gt'></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ>"
+
+#data
+<div bar=ZZ&gt></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ>"
+
+#data
+<div bar="ZZ&pound_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&pound;_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∏_id=23"
+
+#data
+<div bar="ZZ&pound=23"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&pound=23"
+
+#data
+<div bar="ZZ&prod=23"></div>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       bar="ZZ&prod=23"
+
+#data
+<div>ZZ&pound_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&pound;_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∏_id=23"
+
+#data
+<div>ZZ&pound=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
+&lang;&rang;
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "⟨⟩"
+
+#data
+&apos;
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "'"
+
+#data
+&ImaginaryI;
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "ⅈ"
+
+#data
+&Kopf;
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     "𝕂"
+
+#data
+&notinva;
+#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>&lt;/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>&lt;/textarea></textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     <textarea>
+|       "</textarea>"
+
+#data
+<!doctype html><textarea>&lt;</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     <textarea>
+|       "<"
+
+#data
+<!doctype html><textarea>a&lt;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>&lt;/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>&lt;/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 &=&amp>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
+&#45
+#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
+&AMP
+#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>&NotEqualTilde;
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "≂̸"
+
+#data
+<!DOCTYPE html>&NotEqualTilde;A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "≂̸A"
+
+#data
+<!DOCTYPE html>&ThickSpace;
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "  "
+
+#data
+<!DOCTYPE html>&ThickSpace;A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "  A"
+
+#data
+<!DOCTYPE html>&NotSubset;
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "⊂⃒"
+
+#data
+<!DOCTYPE html>&NotSubset;A
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "⊂⃒A"
+
+#data
+<!DOCTYPE html>&Gopf;
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     "𝔾"
+
+#data
+<!DOCTYPE html>&Gopf;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>&#x0a;&#x0a;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 &#x0043;DATA inside a <style> element
+#errors
+#document-fragment
+style
+#document
+| "this is &#x0043;DATA 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>&amp;</title>
+#errors
+Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE.
+#document
+| <html>
+|   <head>
+|     <title>
+|       "&"
+|   <body>
+
+#data
+<title><!--&amp;--></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>&amp;</title>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|     <title>
+|       "&"
+|   <body>
+
+#data
+<!doctype html><title><!--&amp;--></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
+&lt;
+#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&lt;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&lt;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 &lt; two",
+		"one &lt; 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",
+		"<",
+		"&lt;",
+	},
+	{
+		"not a tag #1",
+		"</",
+		"&lt;/",
+	},
+	{
+		"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 &lt; b",
+	},
+	{
+		"not a tag #8",
+		"<.>",
+		"&lt;.&gt;",
+	},
+	{
+		"not a tag #9",
+		"a<<<b>>>c",
+		"a&lt;&lt;$<b>$&gt;&gt;c",
+	},
+	{
+		"not a tag #10",
+		"if x<0 and y < 0 then x*y>0",
+		"if x&lt;0 and y &lt; 0 then x*y&gt;0",
+	},
+	{
+		"not a tag #11",
+		"<<p>",
+		"&lt;$<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&lt;/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>$&lt;a&gt;&lt;/b&gt;$</script>",
+	},
+	{
+		"unfinished script end tag",
+		"<SCRIPT>a</SCR",
+		"<script>$a&lt;/SCR",
+	},
+	{
+		"broken script end tag",
+		"<SCRIPT>a</SCR ipt>",
+		"<script>$a&lt;/SCR ipt&gt;",
+	},
+	{
+		"EOF in script end tag",
+		"<SCRIPT>a</SCRipt",
+		"<script>$a&lt;/SCRipt",
+	},
+	{
+		"scriptx end tag",
+		"<SCRIPT>a</SCRiptx",
+		"<script>$a&lt;/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&lt;/SCRipt&lt;script&gt;",
+	},
+	{
+		"script end tag after unfinished",
+		"<SCRIPT>a</SCRipt</script>",
+		"<script>$a&lt;/SCRipt$</script>",
+	},
+	{
+		"script/style mismatched tags",
+		"<script>a</style>",
+		"<script>$a&lt;/style&gt;",
+	},
+	{
+		"style element with entity",
+		"<style>&apos;",
+		"<style>$&amp;apos;",
+	},
+	{
+		"textarea with tag",
+		"<textarea><div></textarea>",
+		"<textarea>$&lt;div&gt;$</textarea>",
+	},
+	{
+		"title with tag and entity",
+		"<title><b>K&amp;R C</b></title>",
+		"<title>$&lt;b&gt;K&amp;R C&lt;/b&gt;$</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&quot;B\"  foo=\"bar\"><EM>te&lt;&amp;;xt</em></p>",
+		`<p id="a&#34;B" foo="bar">$<em>$te&lt;&amp;;xt$</em>$</p>`,
+	},
+	// A nonexistent entity. Tokenizing and converting back to a string should
+	// escape the "&" to become "&amp;".
+	{
+		"noSuchEntity",
+		`<a b="c&noSuchEntity;d">&lt;&alsoDoesntExist;&`,
+		`<a b="c&amp;noSuchEntity;d">$&lt;&amp;alsoDoesntExist;&amp;`,
+	},
+	{
+		"entity without semicolon",
+		`&notit;&notin;<a b="q=z&amp=5&notice=hello&not;=world">`,
+		`¬it;∉$<a b="q=z&amp;amp=5&amp;notice=hello¬=world">`,
+	},
+	{
+		"entity with digits",
+		"&frac12;",
+		"½",
+	},
+	// 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&#39;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&#39;t">$<p id="won&#39;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 &lt; 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&nbsp;(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 (&micro;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>&lt;/&gt;</V></Plain>`},
+	{Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
+	{Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</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="&lt;unix&gt;"></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&amp;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 &amp; 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: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
+		Value: &AnyTest{
+			Nested:   `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
+			AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
+		},
+	},
+	{
+		ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</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: " &#x9;\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&lt;&gt;</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 &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;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&amp;#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&amp;#39;s actual URL in
+the link rel=&amp;quot;self&amp;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&amp;#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&lt;&gt;</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 &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).
+
+
+`,
+			},
+		},
+		{
+			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&#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.
+
+
+`,
+			},
+		},
+	},
+}
+
+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 /\&gt;/ x/\&lt;(.|\n)+/ s/\n/ /g
+			,x v/^\&lt;!ENTITY/d
+			,s/\&lt;!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("&#34;") // shorter than "&quot;"
+	esc_apos = []byte("&#39;") // shorter than "&apos;"
+	esc_amp  = []byte("&amp;")
+	esc_lt   = []byte("&lt;")
+	esc_gt   = []byte("&gt;")
+	esc_tab  = []byte("&#x9;")
+	esc_nl   = []byte("&#xA;")
+	esc_cr   = []byte("&#xD;")
+	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 &lt;&gt;&apos;&quot; &#x767d;&#40300;翔</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>&nbspc;</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>&#123</tag>
+<tag>&#zzz;</tag>
+<tag>&なまえ3;</tag>
+<tag>&lt-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("&#123"),
+	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("&lt-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 &lt;RFC2518&gt;.` +
+			`  </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)
+		}
+	}
+}