[SCM] Packaging for Google Go branch, weekly-upstream-sid, updated. upstream-weekly/2011.07.07-5-g04f99b3

Ondřej Surý ondrej at sury.org
Thu Oct 6 07:34:17 UTC 2011


The following commit has been merged in the weekly-upstream-sid branch:
commit 04f99b387021a8ce32a8795360cba9beaf986a81
Author: Ondřej Surý <ondrej at sury.org>
Date:   Tue Sep 13 12:00:31 2011 +0200

    Imported Upstream version 2011.09.07

diff --git a/AUTHORS b/AUTHORS
index d8bf4b4..e4a4aa2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -78,6 +78,7 @@ James Toy <nil at opensesame.st>
 James Whitehead <jnwhiteh at gmail.com>
 Jan H. Hosang <jan.hosang at gmail.com>
 Jan Mercl <befelemepeseveze at gmail.com>
+Jaroslavas Počepko <jp at webmaster.ms>
 Jeff Hodges <jeff at somethingsimilar.com>
 Jeff R. Allen <jra at nella.org>
 Jim McGrath <jimmc2 at gmail.com>
@@ -102,6 +103,7 @@ Lucio De Re <lucio.dere at gmail.com>
 Luit van Drongelen <luitvd at gmail.com>
 Markus Duft <markus.duft at salomon.at>
 Martin Neubauer <m.ne at gmx.net>
+Mateusz Czapliński <czapkofan at gmail.com>
 Mathieu Lonjaret <mathieu.lonjaret at gmail.com>
 Matthew Horsnell <matthew.horsnell at gmail.com>
 Micah Stetson <micah.stetson at gmail.com>
@@ -109,6 +111,7 @@ Michael Elkins <michael.elkins at gmail.com>
 Michael Hoisie <hoisie at gmail.com>
 Miek Gieben <miek at miek.nl>
 Mikael Tillenius <mikti42 at gmail.com>
+Mike Rosset <mike.rosset at gmail.com>
 Mikio Hara <mikioh.mikioh at gmail.com>
 Mikkel Krautz <mikkel at krautz.dk>
 Moriyoshi Koizumi <mozo at mozo.jp>
@@ -156,3 +159,4 @@ Yasuhiro Matsumoto <mattn.jp at gmail.com>
 Yongjian Xu <i3dmaster at gmail.com>
 Yuusei Kuwana <kuwana at kumama.org>
 Yuval Pavel Zholkover <paulzhol at gmail.com>
+Ziad Hatahet <hatahet at gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 293df0b..2cab074 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -124,6 +124,7 @@ James Whitehead <jnwhiteh at gmail.com>
 Jamie Gennis <jgennis at google.com>
 Jan H. Hosang <jan.hosang at gmail.com>
 Jan Mercl <befelemepeseveze at gmail.com>
+Jaroslavas Počepko <jp at webmaster.ms>
 Jeff Hodges <jeff at somethingsimilar.com>
 Jeff R. Allen <jra at nella.org> <jeff.allen at gmail.com>
 Jim McGrath <jimmc2 at gmail.com>
@@ -159,6 +160,7 @@ Marcel van Lohuizen <mpvl at golang.org>
 Mark Zavislak <zavislak at google.com>
 Markus Duft <markus.duft at salomon.at>
 Martin Neubauer <m.ne at gmx.net>
+Mateusz Czapliński <czapkofan at gmail.com>
 Mathieu Lonjaret <mathieu.lonjaret at gmail.com>
 Matt Jones <mrjones at google.com>
 Matthew Horsnell <matthew.horsnell at gmail.com>
@@ -169,6 +171,7 @@ Michael Hoisie <hoisie at gmail.com>
 Michael T. Jones <mtj at google.com> <michael.jones at gmail.com>
 Miek Gieben <miek at miek.nl> <remigius.gieben at gmail.com>
 Mikael Tillenius <mikti42 at gmail.com>
+Mike Rosset <mike.rosset at gmail.com>
 Mike Samuel <mikesamuel at gmail.com>
 Mike Solomon <msolo at gmail.com>
 Mikio Hara <mikioh.mikioh at gmail.com>
@@ -236,3 +239,4 @@ Yongjian Xu <i3dmaster at gmail.com>
 Yuusei Kuwana <kuwana at kumama.org>
 Yuval Pavel Zholkover <paulzhol at gmail.com>
 Yves Junqueira <yves.junqueira at gmail.com>
+Ziad Hatahet <hatahet at gmail.com>
diff --git a/VERSION b/VERSION
index bfdfdd5..60a0361 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-weekly.2011-08-17 9454
+weekly.2011-09-07 9685
diff --git a/doc/Makefile b/doc/Makefile
index e4e3f83..29d7e2c 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -8,4 +8,11 @@ TARG=tmpltohtml
 GOFILES=\
 	tmpltohtml.go\
 
+go_tutorial.html: go_tutorial.tmpl tmpltohtml
+	makehtml go_tutorial.tmpl
+
+effective_go.html: effective_go.tmpl tmpltohtml
+	makehtml effective_go.tmpl
+
+
 include ../src/Make.cmd
diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile
index 09c3291..32dc1a1 100644
--- a/doc/codelab/wiki/Makefile
+++ b/doc/codelab/wiki/Makefile
@@ -10,7 +10,7 @@ include ../../../src/Make.common
 
 CLEANFILES+=index.html srcextract.bin htmlify.bin get.bin
 
-index.html: srcextract.bin htmlify.bin
+index.html: wiki.html srcextract.bin htmlify.bin
 	PATH=.:$$PATH awk '/^!/{system(substr($$0,2)); next} {print}' < wiki.html | tr -d '\r' > index.html
 
 test: get.bin
diff --git a/doc/devel/weekly.html b/doc/devel/weekly.html
index 5a9e3d9..aa605a3 100644
--- a/doc/devel/weekly.html
+++ b/doc/devel/weekly.html
@@ -14,6 +14,194 @@ hg pull
 hg update weekly.<i>YYYY-MM-DD</i>
 </pre>
 
+<h2 id="2011-09-06">2011-09-06</h2>
+
+<pre>
+This weekly snapshot consists of improvements and bug fixes, including fixes
+for issues introduced by escape analysis changes in the gc compiler.
+
+* build: clear execute bit from Go files (thanks Mike Rosset),
+	error out if problem with sudo.bash /usr/local/bin (thanks Mike Rosset).
+* exp/norm: add Reader and Writer,
+	performance improvements of quickSpan.
+* exp/regexp: bug fixes and RE2 tests.
+* exp/template/html: string replacement refactoring,
+	tweaks to js{,_test}.go.
+* gc: add -p flag to catch import cycles earlier,
+	fix label recursion bugs,
+	fix zero-length struct eval,
+	zero stack-allocated slice backing arrays,
+* gc, ld: fix Windows file paths (thanks Hector Chu).
+* go/parser: accept corner cases of signature syntax.
+* gobuilder: ignore _test.go files when looking for docs, more logging.
+* godoc: minor tweaks for App Engine use.
+* gofix: do not convert url in field names (thanks Gustavo Niemeyer).
+* gofmt: indent multi-line signatures.
+* gopprof: regexp fixes (thanks Hector Chu).
+* image/png: check zlib checksum during Decode.
+* libmach: fix incorrect use of memset (thanks Dave Cheney).
+* misc/goplay: fix template output.
+* net: ParseCIDR returns IPNet instead of IPMask (thanks Mikio Hara),
+	sync CIDRMask code, doc.
+* os: use GetFileAttributesEx to implement Stat on windows (thanks Alex Brainman).
+* runtime: fix openbsd 386 raisesigpipe,
+	implement exception handling on windows/amd64 (thanks Hector Chu),
+	test for concurrent channel consumers (thanks Christopher Wedgwood).
+* sort: use heapsort to bail out quicksort (thanks Ziad Hatahet).
+* sync/atomic: add LoadUintptr, add Store functions.
+* syscall: update routing message attributes handling (thanks Mikio Hara).
+* template: fix deadlock,
+	indirect or dereference function arguments if necessary,
+	slightly simplify the test for assignability of arguments.
+* url: handle ; in ParseQuery.
+* websocket: fix incorrect prints found by govet (thanks Robert Hencke).
+</pre>
+
+<h2 id="2011-09-01">2011-09-01</h2>
+
+<pre>
+This weekly contains performance improvements and bug fixes.
+
+The gc compiler now does escape analysis, which improves program performance
+by placing variables on the call stack instead of the heap when it is safe to
+do so.
+
+The container/vector package is deprecated and will be removed at some point
+in the future.
+
+Other changes:
+* archive/tar: support symlinks. (thanks Mike Rosset)
+* big: fix nat.scan bug. (thanks Evan Shaw)
+* bufio: handle a "\r\n" that straddles the buffer.
+	add openbsd.
+	avoid redundant bss declarations.
+	fix unused parameters.
+	fix windows/amd64 build with newest mingw-w64. (thanks Hector Chu)
+* bytes: clarify that NewBuffer is not for beginners.
+* cgo: explain how to free something.
+	fix GoBytes. (thanks Gustavo Niemeyer)
+	fixes callback for windows amd64. (thanks Wei Guangjing)
+	note that CString result must be freed. (thanks Gustavo Niemeyer)
+* cov: remove tautological #defines. (thanks Lucio De Re)
+* dashboard: yet another utf-8 fix.
+* doc/codelab/wiki: fix Makefile.
+* doc/progs: fix windows/amd64. (thanks Jaroslavas Počepko)
+* doc/tmpltohtml: update to new template package.
+* doc: emphasize that environment variables are optional.
+* effective_go: convert to use tmpltohtml.
+* exp/norm: reduced the size of the byte buffer used by reorderBuffer by half by reusing space when combining.
+	a few minor fixes to support the implementation of norm.
+	added implementation for []byte versions of methods.
+* exp/template/html: add some tests for ">" attributes.
+	added handling for URL attributes.
+	differentiate URL-valued attributes (such as href).
+	reworked escapeText to recognize attr boundaries.
+* exp/wingui: made compatible with windows/amd64. (thanks Jaroslavas Počepko)
+* flag: add Parsed, restore Usage.
+* gc: add openbsd.
+	escape analysis.
+	fix build on Plan 9. (thanks Lucio De Re)
+	fix div bug.
+	fix pc/line table. (thanks Julian Phillips)
+	fix some spurious leaks.
+	make static initialization more static.
+	remove JCXZ; add JCXZW, JCXZL, and JCXZQ instructions. (thanks Jaroslavas Počepko)
+	shuffle #includes.
+	simplify escape analysis recursion.
+	tweak and enable escape analysis.
+* go/ast cleanup: base File/PackageExports on FilterFile/FilterPackage code.
+	adjustments to filter function.
+	fix ast.MergePackageFiles to collect infos about imports. (thanks Sebastien Binet)
+	generalize ast.FilterFile.
+* go/build: add test support & use in gotest.
+	separate test imports out when scanning. (thanks Gustavo Niemeyer)
+* go/parser: fix type switch scoping.
+	fix type switch scoping.
+* gob: explain that Debug isn't useful unless it's compiled in.
+* gobuilder: increase log limit.
+* godashboard: fix utf-8 in user names.
+* godoc: first step towards reducing index size.
+	add dummy playground.js to silence godoc warning at start-up.
+	added systematic throttling to indexing goroutine.
+	fix bug in zip.go.
+	support for reading/writing (splitted) index files.
+	use virtual file system when generating package synopses.
+* gofix: forgot to rename the URL type.
+	osopen: fixed=true when changing O_CREAT. (thanks Tarmigan Casebolt)
+* goinstall: error out with paths that end with '/'. (thanks Tarmigan Casebolt)
+	report lack of $GOPATH on errors. (thanks Gustavo Niemeyer)
+	select the tag that is closest to runtime.Version.
+* gotry: add missing $. (thanks Tarmigan Casebolt)
+* http: add MaxBytesReader to limit request body size.
+	add file protocol transport.
+	adjust test threshold for larger suse buffers.
+	delete error kludge.
+	on invalid request, send 400 response.
+	return 413 instead of 400 when the request body is too large. (thanks Dave Cheney)
+	support setting Transport's TLS client config.
+* image/tiff: add a decode benchmark. (thanks Benny Siegert)
+	decoder optimization. (thanks Benny Siegert)
+* image: add PalettedImage interface, and make image/png recognize it. (thanks Jaroslavas Počepko)
+* io: add TeeReader. (thanks Hector Chu)
+* json: add struct tag option to wrap literals in strings.
+	calculate Offset for Indent correctly. (thanks Jeff Hodges)
+	fix decode bug with struct tag names with ,opts being ignored.
+* ld: handle Plan 9 ar format. (thanks Lucio De Re)
+	remove duplicate bss definitions.
+* libmach: support reading symbols from Windows .exe for nm. (thanks Mateusz Czapliński)
+* math: fix Pow10 loop. (thanks Volker Dobler)
+* mime: ParseMediaType returns os.Error now, not a nil map.
+	media type formatter. (thanks Pascal S. de Kloe)
+	text charset defaults. (thanks Pascal S. de Kloe)
+* misc/dashboard: remove limit for json package list.
+* misc/emacs: refine label detection.
+* net: add ParseMAC function. (thanks Paul Borman)
+	change the internal form of IPMask for IPv4. (thanks Mikio Hara)
+	disable "tcp" test on openbsd.
+	fix windows build. (thanks Alex Brainman)
+	join and leave a IPv6 group address, on a specific interface. (thanks Mikio Hara)
+	make use of IPv4len, IPv6len. (thanks Mikio Hara)
+	move internal string manipulation routines to parse.go. (thanks Mikio Hara)
+* os: disable Hostname test on OpenBSD.
+	fix WNOHANG Waitmsg. (thanks Gustavo Niemeyer)
+* reflect: add Value.Bytes, Value.SetBytes methods.
+* rpc: add benchmark for async rpc calls.
+* runtime: add openbsd 386 defs.h.
+	add runtime support for openbsd 386.
+	add runtime· prefix to showframe.
+	ctrlhandler for windows amd64. (thanks Wei Guangjing)
+	fix stack cleanup on windows/amd64. (thanks Hector Chu)
+	fix void warnings.
+	go interface to cdecl calbacks. (thanks Jaroslavas Počepko)
+	handle string + char literals in goc2c.
+	make arm work on Ubuntu Natty qemu.
+	openbsd thread tweaks.
+	simplify stack traces.
+	speed up cgo calls. (thanks Alex Brainman)
+	use cgo runtime functions to call windows syscalls. (thanks Alex Brainman)
+	windows/amd64 callbacks fixed and syscall fixed to allow using it in callbacks. (thanks Jaroslavas Počepko)
+* strconv: put decimal on stack.
+* spec: update section on Implementation Differences.
+* syscall: SOMAXCONN should be 0x7fffffff at winsock2. (thanks Yasuhiro Matsumoto)
+	add openbsd 386.
+	handle RTM_NEWROUTE in ParseNetlinkRouteAttr on Linux. (thanks Albert Strasheim)
+	handle routing entry in ParseRoutingSockaddr on BSD variants. (thanks Mikio Hara)
+	openbsd amd64 syscall support.
+	use the vdso page on linux x86 for faster syscalls instead of int $0x80. (thanks Yuval Pavel Zholkover)
+* template/parse: give if, range, and with a common representation.
+* template: grammar fix for template documentation. (thanks Bill Neubauer)
+	range over channel.
+	remove else and end nodes from public view.
+* test: put GOROOT/bin before all others in run.
+* time: fix Plan 9 build. (thanks Fazlul Shahriar)
+	fix zone during windows test.
+* type switches: test for pathological case.
+* version.bash: update VERSION on -save if already present. (thanks Gustavo Niemeyer)
+* websocket: implements new version of WebSocket protocol. (thanks Fumitoshi Ukai)
+* windows/386: clean stack after syscall. (thanks Jaroslavas Počepko)
+* xml: marshal "parent>child" tags correctly. (thanks Ross Light)
+</pre>
+
 <h2 id="2011-08-17">2011-08-17</h2>
 
 <pre>
diff --git a/doc/effective_go.html b/doc/effective_go.html
index 37cb516..6adf7e5 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -793,7 +793,7 @@ func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
         var nr int
         nr, err = r.Read(buf)
         n += nr
-        buf = buf[nr:len(buf)]
+        buf = buf[nr:]
     }
     return
 }
@@ -954,8 +954,9 @@ In Go terminology, it returns a pointer to a newly allocated zero value of type
 </p>
 
 <p>
-Since the memory returned by <code>new</code> is zeroed, it's helpful to arrange that the
-zeroed object can be used without further initialization.  This means a user of
+Since the memory returned by <code>new</code> is zeroed, it's helpful to arrange
+when designing your data structures that the
+zero value of each type can be used without further initialization.  This means a user of
 the data structure can create one with <code>new</code> and get right to
 work.
 For example, the documentation for <code>bytes.Buffer</code> states that
@@ -1075,8 +1076,9 @@ m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
 Back to allocation.
 The built-in function <code>make(T, </code><i>args</i><code>)</code> serves
 a purpose different from <code>new(T)</code>.
-It creates slices, maps, and channels only, and it returns an initialized (not zero)
-value of type <code>T</code>, not <code>*T</code>.
+It creates slices, maps, and channels only, and it returns an <em>initialized</em>
+(not <em>zeroed</em>)
+value of type <code>T</code> (not <code>*T</code>).
 The reason for the distinction
 is that these three types are, under the covers, references to data structures that
 must be initialized before use.
@@ -1620,11 +1622,12 @@ enumerator.  Since <code>iota</code> can be part of an expression and
 expressions can be implicitly repeated, it is easy to build intricate
 sets of values.
 </p>
-<pre>
-type ByteSize float64
+<pre><!--{{code "progs/eff_bytesize.go" `/^type ByteSize/` `/^\)/`}}
+-->type ByteSize float64
+
 const (
-    _ = iota  // ignore first value by assigning to blank identifier
-    KB ByteSize = 1<<(10*iota)
+    _           = iota // ignore first value by assigning to blank identifier
+    KB ByteSize = 1 << (10 * iota)
     MB
     GB
     TB
@@ -1639,27 +1642,27 @@ The ability to attach a method such as <code>String</code> to a
 type makes it possible for such values to format themselves
 automatically for printing, even as part of a general type.
 </p>
-<pre>
-func (b ByteSize) String() string {
+<pre><!--{{code "progs/eff_bytesize.go" `/^func.*ByteSize.*String/` `/^}/`}}
+-->func (b ByteSize) String() string {
     switch {
     case b >= YB:
-        return fmt.Sprintf("%.2fYB", float64(b/YB))
+        return fmt.Sprintf("%.2fYB", float64(b/YB))
     case b >= ZB:
-        return fmt.Sprintf("%.2fZB", float64(b/ZB))
+        return fmt.Sprintf("%.2fZB", float64(b/ZB))
     case b >= EB:
-        return fmt.Sprintf("%.2fEB", float64(b/EB))
+        return fmt.Sprintf("%.2fEB", float64(b/EB))
     case b >= PB:
-        return fmt.Sprintf("%.2fPB", float64(b/PB))
+        return fmt.Sprintf("%.2fPB", float64(b/PB))
     case b >= TB:
-        return fmt.Sprintf("%.2fTB", float64(b/TB))
+        return fmt.Sprintf("%.2fTB", float64(b/TB))
     case b >= GB:
-        return fmt.Sprintf("%.2fGB", float64(b/GB))
+        return fmt.Sprintf("%.2fGB", float64(b/GB))
     case b >= MB:
-        return fmt.Sprintf("%.2fMB", float64(b/MB))
+        return fmt.Sprintf("%.2fMB", float64(b/MB))
     case b >= KB:
-        return fmt.Sprintf("%.2fKB", float64(b/KB))
+        return fmt.Sprintf("%.2fKB", float64(b/KB))
     }
-    return fmt.Sprintf("%.2fB", float64(b))
+    return fmt.Sprintf("%.2fB", float64(b))
 }
 </pre>
 <p>
@@ -1810,8 +1813,8 @@ by the routines in package <code>sort</code> if it implements
 and it could also have a custom formatter.
 In this contrived example <code>Sequence</code> satisfies both.
 </p>
-<pre>
-type Sequence []int
+<pre><!--{{code "progs/eff_sequence.go" `/^type/` "$"}}
+-->type Sequence []int
 
 // Methods required by sort.Interface.
 func (s Sequence) Len() int {
@@ -1827,14 +1830,14 @@ func (s Sequence) Swap(i, j int) {
 // Method for printing - sorts the elements before printing.
 func (s Sequence) String() string {
     sort.Sort(s)
-    str := "["
+    str := "["
     for i, elem := range s {
         if i > 0 {
-            str += " "
+            str += " "
         }
         str += fmt.Sprint(elem)
     }
-    return str + "]"
+    return str + "]"
 }
 </pre>
 
@@ -2917,66 +2920,54 @@ for instance, a URL, saving you typing the URL into the phone's tiny keyboard.
 Here's the complete program.
 An explanation follows.
 </p>
-
-<pre>
-package main
+<pre><!--{{code "progs/eff_qr.go"}}
+-->package main
 
 import (
-    "flag"
-    "http"
-    "io"
-    "log"
-    "old/template" // New template package coming soon...
+    "flag"
+    "http"
+    "log"
+    "template"
 )
 
-var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
-var fmap = template.FormatterMap{
-    "html": template.HTMLFormatter,
-    "url+html": UrlHtmlFormatter,
-}
-var templ = template.MustParse(templateStr, fmap)
+var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
+
+var templ = template.Must(template.New("qr").Parse(templateStr))
 
 func main() {
     flag.Parse()
-    http.Handle("/", http.HandlerFunc(QR))
+    http.Handle("/", http.HandlerFunc(QR))
     err := http.ListenAndServe(*addr, nil)
     if err != nil {
-        log.Fatal("ListenAndServe:", err)
+        log.Fatal("ListenAndServe:", err)
     }
 }
 
 func QR(w http.ResponseWriter, req *http.Request) {
-    templ.Execute(w, req.FormValue("s"))
-}
-
-func UrlHtmlFormatter(w io.Writer, fmt string, v ...interface{}) {
-    template.HTMLEscape(w, []byte(http.URLEscape(v[0].(string))))
+    templ.Execute(w, req.FormValue("s"))
 }
 
-
 const templateStr = `
 <html>
 <head>
 <title>QR Link Generator</title>
 </head>
 <body>
-{.section @}
-<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={@|url+html}"
-/>
+{{if .}}
+<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{urlquery .}}" />
 <br>
-{@|html}
+{{html .}}
 <br>
 <br>
-{.end}
-<form action="/" name=f method="GET"><input maxLength=1024 size=70
-name=s value="" title="Text to QR Encode"><input type=submit
-value="Show QR" name=qr>
+{{end}}
+<form action="/" name=f method="GET"><input maxLength=1024 size=70
+name=s value="" title="Text to QR Encode"><input type=submit
+value="Show QR" name=qr>
 </form>
 </body>
 </html>
 `
 </pre>
-
 <p>
 The pieces up to <code>main</code> should be easy to follow.
 The one flag sets a default HTTP port for our server.  The template
@@ -2995,25 +2986,21 @@ server; it blocks while the server runs.
 executes the template on the data in the form value named <code>s</code>.
 </p>
 <p>
-The template package, inspired by <a
-href="http://code.google.com/p/json-template">json-template</a>, is
-powerful;
+The template package is powerful;
 this program just touches on its capabilities.
 In essence, it rewrites a piece of text on the fly by substituting elements derived
 from data items passed to <code>templ.Execute</code>, in this case the
 form value.  
 Within the template text (<code>templateStr</code>),
-brace-delimited pieces denote template actions.
-The piece from the <code>{.section @}</code>
-to <code>{.end}</code> executes with the value of the data item <code>@</code>,
-which is a shorthand for “the current item”, which is the form value.
-(When the string is empty, this piece of the template is suppressed.)
+double-brace-delimited pieces denote template actions.
+The piece from <code>{{if .}}</code>
+to <code>{{end}}</code> executes only if the value of the current data item, called <code>.</code> (dot),
+is non-empty.
+That is, when the string is empty, this piece of the template is suppressed.
 </p>
 <p>
-The snippet <code>{@|url+html}</code> says to run the data through the formatter
-installed in the formatter map (<code>fmap</code>)
-under the name <code>"url+html"</code>.
-That is the function <code>UrlHtmlFormatter</code>, which sanitizes the string
+The snippet <code>{{urlquery .}}</code> says to process the data with the function
+<code>urlquery</code>, which sanitizes the query string
 for safe display on the web page.
 </p>
 <p>
diff --git a/doc/effective_go.tmpl b/doc/effective_go.tmpl
new file mode 100644
index 0000000..46d774a
--- /dev/null
+++ b/doc/effective_go.tmpl
@@ -0,0 +1,2919 @@
+<!-- Effective Go -->
+
+<h2 id="introduction">Introduction</h2>
+
+<p>
+Go is a new language.  Although it borrows ideas from
+existing languages,
+it has unusual properties that make effective Go programs
+different in character from programs written in its relatives.
+A straightforward translation of a C++ or Java program into Go
+is unlikely to produce a satisfactory result—Java programs
+are written in Java, not Go.
+On the other hand, thinking about the problem from a Go
+perspective could produce a successful but quite different
+program.
+In other words,
+to write Go well, it's important to understand its properties
+and idioms.
+It's also important to know the established conventions for
+programming in Go, such as naming, formatting, program
+construction, and so on, so that programs you write
+will be easy for other Go programmers to understand.
+</p>
+
+<p>
+This document gives tips for writing clear, idiomatic Go code.
+It augments the <a href="go_spec.html">language specification</a>
+and the <a href="go_tutorial.html">tutorial</a>, both of which you
+should read first.
+</p>
+
+<h3 id="examples">Examples</h3>
+
+<p>
+The <a href="/src/pkg/">Go package sources</a>
+are intended to serve not
+only as the core library but also as examples of how to
+use the language.
+If you have a question about how to approach a problem or how something
+might be implemented, they can provide answers, ideas and
+background.
+</p>
+
+
+<h2 id="formatting">Formatting</h2>
+
+<p>
+Formatting issues are the most contentious
+but the least consequential.
+People can adapt to different formatting styles
+but it's better if they don't have to, and
+less time is devoted to the topic
+if everyone adheres to the same style.
+The problem is how to approach this Utopia without a long
+prescriptive style guide.
+</p>
+
+<p>
+With Go we take an unusual
+approach and let the machine
+take care of most formatting issues.
+The <code>gofmt</code> tool reads a Go program
+and emits the source in a standard style of indentation
+and vertical alignment, retaining and if necessary
+reformatting comments.
+If you want to know how to handle some new layout
+situation, run <code>gofmt</code>; if the answer doesn't
+seem right, rearrange your program (or file a bug about <code>gofmt</code>),
+don't work around it.
+</p>
+
+<p>
+As an example, there's no need to spend time lining up
+the comments on the fields of a structure.
+<code>Gofmt</code> will do that for you.  Given the
+declaration
+</p>
+
+<pre>
+type T struct {
+    name string // name of the object
+    value int // its value
+}
+</pre>
+
+<p>
+<code>gofmt</code> will line up the columns:
+</p>
+
+<pre>
+type T struct {
+    name    string // name of the object
+    value   int    // its value
+}
+</pre>
+
+<p>
+All Go code in the standard packages has been formatted with <code>gofmt</code>.
+</p>
+
+
+<p>
+Some formatting details remain.  Very briefly,
+</p>
+
+<dl>
+    <dt>Indentation</dt>
+    <dd>We use tabs for indentation and <code>gofmt</code> emits them by default.
+    Use spaces only if you must.
+    </dd>
+    <dt>Line length</dt>
+    <dd>
+    Go has no line length limit.  Don't worry about overflowing a punched card.
+    If a line feels too long, wrap it and indent with an extra tab.
+    </dd>
+    <dt>Parentheses</dt>
+    <dd>
+    Go needs fewer parentheses: control structures (<code>if</code>,
+    <code>for</code>, <code>switch</code>) do not have parentheses in
+    their syntax.
+    Also, the operator precedence hierarchy is shorter and clearer, so
+<pre>
+x<<8 + y<<16
+</pre>
+    means what the spacing implies.
+    </dd>
+</dl>
+
+<h2 id="commentary">Commentary</h2>
+
+<p>
+Go provides C-style <code>/* */</code> block comments
+and C++-style <code>//</code> line comments.
+Line comments are the norm;
+block comments appear mostly as package comments and
+are also useful to disable large swaths of code.
+</p>
+
+<p>
+The program—and web server—<code>godoc</code> processes
+Go source files to extract documentation about the contents of the
+package.
+Comments that appear before top-level declarations, with no intervening newlines,
+are extracted along with the declaration to serve as explanatory text for the item.
+The nature and style of these comments determines the
+quality of the documentation <code>godoc</code> produces.
+</p>
+
+<p>
+Every package should have a <i>package comment</i>, a block
+comment preceding the package clause.
+For multi-file packages, the package comment only needs to be
+present in one file, and any one will do.
+The package comment should introduce the package and
+provide information relevant to the package as a whole.
+It will appear first on the <code>godoc</code> page and
+should set up the detailed documentation that follows.
+</p>
+
+<pre>
+/*
+    Package regexp implements a simple library for
+    regular expressions.
+
+    The syntax of the regular expressions accepted is:
+
+    regexp:
+        concatenation { '|' concatenation }
+    concatenation:
+        { closure }
+    closure:
+        term [ '*' | '+' | '?' ]
+    term:
+        '^'
+        '$'
+        '.'
+        character
+        '[' [ '^' ] character-ranges ']'
+        '(' regexp ')'
+*/
+package regexp
+</pre>
+
+<p>
+If the package is simple, the package comment can be brief.
+</p>
+
+<pre>
+// Package path implements utility routines for
+// manipulating slash-separated filename paths.
+</pre>
+
+<p>
+Comments do not need extra formatting such as banners of stars.
+The generated output may not even be presented in a fixed-width font, so don't depend
+on spacing for alignment—<code>godoc</code>, like <code>gofmt</code>,
+takes care of that.
+The comments are uninterpreted plain text, so HTML and other
+annotations such as <code>_this_</code> will reproduce <i>verbatim</i> and should
+not be used.
+Depending on the context, <code>godoc</code> might not even
+reformat comments, so make sure they look good straight up:
+use correct spelling, punctuation, and sentence structure,
+fold long lines, and so on.
+</p>
+
+<p>
+Inside a package, any comment immediately preceding a top-level declaration
+serves as a <i>doc comment</i> for that declaration.
+Every exported (capitalized) name in a program should
+have a doc comment.
+</p>
+
+<p>
+Doc comments work best as complete sentences, which allow
+a wide variety of automated presentations.
+The first sentence should be a one-sentence summary that
+starts with the name being declared.
+</p>
+
+<pre>
+// Compile parses a regular expression and returns, if successful, a Regexp
+// object that can be used to match against text.
+func Compile(str string) (regexp *Regexp, error os.Error) {
+</pre>
+
+<p>
+Go's declaration syntax allows grouping of declarations.
+A single doc comment can introduce a group of related constants or variables.
+Since the whole declaration is presented, such a comment can often be perfunctory.
+</p>
+
+<pre>
+// Error codes returned by failures to parse an expression.
+var (
+    ErrInternal      = os.NewError("regexp: internal error")
+    ErrUnmatchedLpar = os.NewError("regexp: unmatched '('")
+    ErrUnmatchedRpar = os.NewError("regexp: unmatched ')'")
+    ...
+)
+</pre>
+
+<p>
+Even for private names, grouping can also indicate relationships between items,
+such as the fact that a set of variables is protected by a mutex.
+</p>
+
+<pre>
+var (
+    countLock   sync.Mutex
+    inputCount  uint32
+    outputCount uint32
+    errorCount  uint32
+)
+</pre>
+
+<h2 id="names">Names</h2>
+
+<p>
+Names are as important in Go as in any other language.
+In some cases they even have semantic effect: for instance,
+the visibility of a name outside a package is determined by whether its
+first character is upper case.
+It's therefore worth spending a little time talking about naming conventions
+in Go programs.
+</p>
+
+
+<h3 id="package-names">Package names</h3>
+
+<p>
+When a package is imported, the package name becomes an accessor for the
+contents.  After
+</p>
+
+<pre>
+import "bytes"
+</pre>
+
+<p>
+the importing package can talk about <code>bytes.Buffer</code>.  It's
+helpful if everyone using the package can use the same name to refer to
+its contents, which implies that the package name should be good:
+short, concise, evocative.  By convention, packages are given
+lower case, single-word names; there should be no need for underscores
+or mixedCaps.
+Err on the side of brevity, since everyone using your
+package will be typing that name.
+And don't worry about collisions <i>a priori</i>.
+The package name is only the default name for imports; it need not be unique
+across all source code, and in the rare case of a collision the
+importing package can choose a different name to use locally.
+In any case, confusion is rare because the file name in the import
+determines just which package is being used.
+</p>
+
+<p>
+Another convention is that the package name is the base name of
+its source directory;
+the package in <code>src/pkg/encoding/base64</code>
+is imported as <code>"encoding/base64"</code> but has name <code>base64</code>,
+not <code>encoding_base64</code> and not <code>encodingBase64</code>.
+</p>
+
+<p>
+The importer of a package will use the name to refer to its contents
+(the <code>import .</code> notation is intended mostly for tests and other
+unusual situations and should be avoided unless necessary),
+so exported names in the package can use that fact
+to avoid stutter.
+For instance, the buffered reader type in the <code>bufio</code> package is called <code>Reader</code>,
+not <code>BufReader</code>, because users see it as <code>bufio.Reader</code>,
+which is a clear, concise name.
+Moreover,
+because imported entities are always addressed with their package name, <code>bufio.Reader</code>
+does not conflict with <code>io.Reader</code>.
+Similarly, the function to make new instances of <code>ring.Ring</code>—which
+is the definition of a <em>constructor</em> in Go—would
+normally be called <code>NewRing</code>, but since
+<code>Ring</code> is the only type exported by the package, and since the
+package is called <code>ring</code>, it's called just <code>New</code>,
+which clients of the package see as <code>ring.New</code>.
+Use the package structure to help you choose good names.
+</p>
+
+<p>
+Another short example is <code>once.Do</code>;
+<code>once.Do(setup)</code> reads well and would not be improved by
+writing <code>once.DoOrWaitUntilDone(setup)</code>.
+Long names don't automatically make things more readable.
+If the name represents something intricate or subtle, it's usually better
+to write a helpful doc comment than to attempt to put all the information
+into the name.
+</p>
+
+<h3 id="Getters">Getters</h3>
+
+<p>
+Go doesn't provide automatic support for getters and setters.
+There's nothing wrong with providing getters and setters yourself,
+and it's often appropriate to do so, but it's neither idiomatic nor necessary
+to put <code>Get</code> into the getter's name.  If you have a field called
+<code>owner</code> (lower case, unexported), the getter method should be
+called <code>Owner</code> (upper case, exported), not <code>GetOwner</code>.
+The use of upper-case names for export provides the hook to discriminate
+the field from the method.
+A setter function, if needed, will likely be called <code>SetOwner</code>.
+Both names read well in practice:
+</p>
+<pre>
+owner := obj.Owner()
+if owner != user {
+    obj.SetOwner(user)
+}
+</pre>
+
+<h3 id="interface-names">Interface names</h3>
+
+<p>
+By convention, one-method interfaces are named by
+the method name plus the -er suffix: <code>Reader</code>,
+<code>Writer</code>, <code>Formatter</code> etc.
+</p>
+
+<p>
+There are a number of such names and it's productive to honor them and the function
+names they capture.
+<code>Read</code>, <code>Write</code>, <code>Close</code>, <code>Flush</code>,
+<code>String</code> and so on have
+canonical signatures and meanings.  To avoid confusion,
+don't give your method one of those names unless it
+has the same signature and meaning.
+Conversely, if your type implements a method with the
+same meaning as a method on a well-known type,
+give it the same name and signature;
+call your string-converter method <code>String</code> not <code>ToString</code>.
+</p>
+
+<h3 id="mixed-caps">MixedCaps</h3>
+
+<p>
+Finally, the convention in Go is to use <code>MixedCaps</code>
+or <code>mixedCaps</code> rather than underscores to write
+multiword names.
+</p>
+
+<h2 id="semicolons">Semicolons</h2>
+
+<p>
+Like C, Go's formal grammar uses semicolons to terminate statements;
+unlike C, those semicolons do not appear in the source.
+Instead the lexer uses a simple rule to insert semicolons automatically
+as it scans, so the input text is mostly free of them.
+</p>
+
+<p>
+The rule is this. If the last token before a newline is an identifier
+(which includes words like <code>int</code> and <code>float64</code>),
+a basic literal such as a number or string constant, or one of the
+tokens
+</p>
+<pre>
+break continue fallthrough return ++ -- ) }
+</pre>
+<p>
+the lexer always inserts a semicolon after the token.
+This could be summarized as, “if the newline comes
+after a token that could end a statement, insert a semicolon”.
+</p>
+
+<p>
+A semicolon can also be omitted immediately before a closing brace,
+so a statement such as
+</p>
+<pre>
+    go func() { for { dst <- <-src } }()
+</pre>
+<p>
+needs no semicolons.
+Idiomatic Go programs have semicolons only in places such as
+<code>for</code> loop clauses, to separate the initializer, condition, and
+continuation elements.  They are also necessary to separate multiple
+statements on a line, should you write code that way.
+</p>
+
+<p>
+One caveat. You should never put the opening brace of a
+control structure (<code>if</code>, <code>for</code>, <code>switch</code>,
+or <code>select</code>) on the next line.  If you do, a semicolon
+will be inserted before the brace, which could cause unwanted
+effects.  Write them like this
+</p>
+
+<pre>
+if i < f() {
+    g()
+}
+</pre>
+<p>
+not like this
+</p>
+<pre>
+if i < f()  // wrong!
+{           // wrong!
+    g()
+}
+</pre>
+
+
+<h2 id="control-structures">Control structures</h2>
+
+<p>
+The control structures of Go are related to those of C but differ
+in important ways.
+There is no <code>do</code> or <code>while</code> loop, only a
+slightly generalized
+<code>for</code>;
+<code>switch</code> is more flexible;
+<code>if</code> and <code>switch</code> accept an optional
+initialization statement like that of <code>for</code>;
+and there are new control structures including a type switch and a
+multiway communications multiplexer, <code>select</code>.
+The syntax is also slightly different:
+there are no parentheses
+and the bodies must always be brace-delimited.
+</p>
+
+<h3 id="if">If</h3>
+
+<p>
+In Go a simple <code>if</code> looks like this:
+</p>
+<pre>
+if x > 0 {
+    return y
+}
+</pre>
+
+<p>
+Mandatory braces encourage writing simple <code>if</code> statements
+on multiple lines.  It's good style to do so anyway,
+especially when the body contains a control statement such as a
+<code>return</code> or <code>break</code>.
+</p>
+
+<p>
+Since <code>if</code> and <code>switch</code> accept an initialization
+statement, it's common to see one used to set up a local variable.
+</p>
+
+<pre>
+if err := file.Chmod(0664); err != nil {
+    log.Print(err)
+    return err
+}
+</pre>
+
+<p id="else">
+In the Go libraries, you'll find that
+when an <code>if</code> statement doesn't flow into the next statement—that is,
+the body ends in <code>break</code>, <code>continue</code>,
+<code>goto</code>, or <code>return</code>—the unnecessary
+<code>else</code> is omitted.
+</p>
+
+<pre>
+f, err := os.Open(name)
+if err != nil {
+    return err
+}
+codeUsing(f)
+</pre>
+
+<p>
+This is an example of a common situation where code must guard against a
+sequence of error conditions.  The code reads well if the
+successful flow of control runs down the page, eliminating error cases
+as they arise.  Since error cases tend to end in <code>return</code>
+statements, the resulting code needs no <code>else</code> statements.
+</p>
+
+<pre>
+f, err := os.Open(name)
+if err != nil {
+    return err
+}
+d, err := f.Stat()
+if err != nil {
+    return err
+}
+codeUsing(f, d)
+</pre>
+
+
+<h3 id="for">For</h3>
+
+<p>
+The Go <code>for</code> loop is similar to—but not the same as—C's.
+It unifies <code>for</code>
+and <code>while</code> and there is no <code>do-while</code>.
+There are three forms, only one of which has semicolons.
+</p>
+<pre>
+// Like a C for
+for init; condition; post { }
+
+// Like a C while
+for condition { }
+
+// Like a C for(;;)
+for { }
+</pre>
+
+<p>
+Short declarations make it easy to declare the index variable right in the loop.
+</p>
+<pre>
+sum := 0
+for i := 0; i < 10; i++ {
+    sum += i
+}
+</pre>
+
+<p>
+If you're looping over an array, slice, string, or map,
+or reading from a channel, a <code>range</code> clause can
+manage the loop.
+</p>
+<pre>
+var m map[string]int
+sum := 0
+for _, value := range m {  // key is unused
+    sum += value
+}
+</pre>
+
+<p>
+For strings, the <code>range</code> does more work for you, breaking out individual
+Unicode characters by parsing the UTF-8.
+Erroneous encodings consume one byte and produce the
+replacement rune U+FFFD. The loop
+</p>
+<pre>
+for pos, char := range "日本語" {
+    fmt.Printf("character %c starts at byte position %d\n", char, pos)
+}
+</pre>
+<p>
+prints
+</p>
+<pre>
+character 日 starts at byte position 0
+character 本 starts at byte position 3
+character 語 starts at byte position 6
+</pre>
+
+<p>
+Finally, Go has no comma operator and <code>++</code> and <code>--</code>
+are statements not expressions.
+Thus if you want to run multiple variables in a <code>for</code>
+you should use parallel assignment.
+</p>
+<pre>
+// Reverse a
+for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
+    a[i], a[j] = a[j], a[i]
+}
+</pre>
+
+<h3 id="switch">Switch</h3>
+
+<p>
+Go's <code>switch</code> is more general than C's.
+The expressions need not be constants or even integers,
+the cases are evaluated top to bottom until a match is found,
+and if the <code>switch</code> has no expression it switches on
+<code>true</code>.
+It's therefore possible—and idiomatic—to write an
+<code>if</code>-<code>else</code>-<code>if</code>-<code>else</code>
+chain as a <code>switch</code>.
+</p>
+
+<pre>
+func unhex(c byte) byte {
+    switch {
+    case '0' <= c && c <= '9':
+        return c - '0'
+    case 'a' <= c && c <= 'f':
+        return c - 'a' + 10
+    case 'A' <= c && c <= 'F':
+        return c - 'A' + 10
+    }
+    return 0
+}
+</pre>
+
+<p>
+There is no automatic fall through, but cases can be presented
+in comma-separated lists.
+<pre>
+func shouldEscape(c byte) bool {
+    switch c {
+    case ' ', '?', '&', '=', '#', '+', '%':
+        return true
+    }
+    return false
+}
+</pre>
+
+<p>
+Here's a comparison routine for byte arrays that uses two
+<code>switch</code> statements:
+<pre>
+// Compare returns an integer comparing the two byte arrays
+// lexicographically.
+// The result will be 0 if a == b, -1 if a < b, and +1 if a > b
+func Compare(a, b []byte) int {
+    for i := 0; i < len(a) && i < len(b); i++ {
+        switch {
+        case a[i] > b[i]:
+            return 1
+        case a[i] < b[i]:
+            return -1
+        }
+    }
+    switch {
+    case len(a) < len(b):
+        return -1
+    case len(a) > len(b):
+        return 1
+    }
+    return 0
+}
+</pre>
+
+<p>
+A switch can also be used to discover the dynamic type of an interface
+variable.  Such a <em>type switch</em> uses the syntax of a type
+assertion with the keyword <code>type</code> inside the parentheses.
+If the switch declares a variable in the expression, the variable will
+have the corresponding type in each clause.
+</p>
+<pre>
+switch t := interfaceValue.(type) {
+default:
+    fmt.Printf("unexpected type %T", t)  // %T prints type
+case bool:
+    fmt.Printf("boolean %t\n", t)
+case int:
+    fmt.Printf("integer %d\n", t)
+case *bool:
+    fmt.Printf("pointer to boolean %t\n", *t)
+case *int:
+    fmt.Printf("pointer to integer %d\n", *t)
+}
+</pre>
+
+<h2 id="functions">Functions</h2>
+
+<h3 id="multiple-returns">Multiple return values</h3>
+
+<p>
+One of Go's unusual features is that functions and methods
+can return multiple values.  This form can be used to
+improve on a couple of clumsy idioms in C programs: in-band
+error returns (such as <code>-1</code> for <code>EOF</code>)
+and modifying an argument.
+</p>
+
+<p>
+In C, a write error is signaled by a negative count with the
+error code secreted away in a volatile location.
+In Go, <code>Write</code>
+can return a count <i>and</i> an error: “Yes, you wrote some
+bytes but not all of them because you filled the device”.
+The signature of <code>*File.Write</code> in package <code>os</code> is:
+</p>
+
+<pre>
+func (file *File) Write(b []byte) (n int, err Error)
+</pre>
+
+<p>
+and as the documentation says, it returns the number of bytes
+written and a non-nil <code>Error</code> when <code>n</code>
+<code>!=</code> <code>len(b)</code>.
+This is a common style; see the section on error handling for more examples.
+</p>
+
+<p>
+A similar approach obviates the need to pass a pointer to a return
+value to simulate a reference parameter.
+Here's a simple-minded function to
+grab a number from a position in a byte array, returning the number
+and the next position.
+</p>
+
+<pre>
+func nextInt(b []byte, i int) (int, int) {
+    for ; i < len(b) && !isDigit(b[i]); i++ {
+    }
+    x := 0
+    for ; i < len(b) && isDigit(b[i]); i++ {
+        x = x*10 + int(b[i])-'0'
+    }
+    return x, i
+}
+</pre>
+
+<p>
+You could use it to scan the numbers in an input array <code>a</code> like this:
+</p>
+
+<pre>
+    for i := 0; i < len(a); {
+        x, i = nextInt(a, i)
+        fmt.Println(x)
+    }
+</pre>
+
+<h3 id="named-results">Named result parameters</h3>
+
+<p>
+The return or result "parameters" of a Go function can be given names and
+used as regular variables, just like the incoming parameters.
+When named, they are initialized to the zero values for their types when
+the function begins; if the function executes a <code>return</code> statement
+with no arguments, the current values of the result parameters are
+used as the returned values.
+</p>
+
+<p>
+The names are not mandatory but they can make code shorter and clearer:
+they're documentation.
+If we name the results of <code>nextInt</code> it becomes
+obvious which returned <code>int</code>
+is which.
+</p>
+
+<pre>
+func nextInt(b []byte, pos int) (value, nextPos int) {
+</pre>
+
+<p>
+Because named results are initialized and tied to an unadorned return, they can simplify
+as well as clarify.  Here's a version
+of <code>io.ReadFull</code> that uses them well:
+</p>
+
+<pre>
+func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
+    for len(buf) > 0 && err == nil {
+        var nr int
+        nr, err = r.Read(buf)
+        n += nr
+        buf = buf[nr:]
+    }
+    return
+}
+</pre>
+
+<h3 id="defer">Defer</h3>
+
+<p>
+Go's <code>defer</code> statement schedules a function call (the
+<i>deferred</i> function) to be run immediately before the function
+executing the <code>defer</code> returns.  It's an unusual but
+effective way to deal with situations such as resources that must be
+released regardless of which path a function takes to return.  The
+canonical examples are unlocking a mutex or closing a file.
+</p>
+
+<pre>
+// Contents returns the file's contents as a string.
+func Contents(filename string) (string, os.Error) {
+    f, err := os.Open(filename)
+    if err != nil {
+        return "", err
+    }
+    defer f.Close()  // f.Close will run when we're finished.
+
+    var result []byte
+    buf := make([]byte, 100)
+    for {
+        n, err := f.Read(buf[0:])
+        result = append(result, buf[0:n]...) // append is discussed later.
+        if err != nil {
+            if err == os.EOF {
+                break
+            }
+            return "", err  // f will be closed if we return here.
+        }
+    }
+    return string(result), nil // f will be closed if we return here.
+}
+</pre>
+
+<p>
+Deferring a call to a function such as <code>Close</code> has two advantages.  First, it
+guarantees that you will never forget to close the file, a mistake
+that's easy to make if you later edit the function to add a new return
+path.  Second, it means that the close sits near the open,
+which is much clearer than placing it at the end of the function.
+</p>
+
+<p>
+The arguments to the deferred function (which include the receiver if
+the function is a method) are evaluated when the <i>defer</i>
+executes, not when the <i>call</i> executes.  Besides avoiding worries
+about variables changing values as the function executes, this means
+that a single deferred call site can defer multiple function
+executions.  Here's a silly example.
+</p>
+
+<pre>
+for i := 0; i < 5; i++ {
+    defer fmt.Printf("%d ", i)
+}
+</pre>
+
+<p>
+Deferred functions are executed in LIFO order, so this code will cause
+<code>4 3 2 1 0</code> to be printed when the function returns.  A
+more plausible example is a simple way to trace function execution
+through the program.  We could write a couple of simple tracing
+routines like this:
+</p>
+
+<pre>
+func trace(s string)   { fmt.Println("entering:", s) }
+func untrace(s string) { fmt.Println("leaving:", s) }
+
+// Use them like this:
+func a() {
+    trace("a")
+    defer untrace("a")
+    // do something....
+}
+</pre>
+
+<p>
+We can do better by exploiting the fact that arguments to deferred
+functions are evaluated when the <code>defer</code> executes.  The
+tracing routine can set up the argument to the untracing routine.
+This example:
+</p>
+
+<pre>
+func trace(s string) string {
+    fmt.Println("entering:", s)
+    return s
+}
+
+func un(s string) {
+    fmt.Println("leaving:", s)
+}
+
+func a() {
+    defer un(trace("a"))
+    fmt.Println("in a")
+}
+
+func b() {
+    defer un(trace("b"))
+    fmt.Println("in b")
+    a()
+}
+
+func main() {
+    b()
+}
+</pre>
+
+<p>
+prints
+</p>
+
+<pre>
+entering: b
+in b
+entering: a
+in a
+leaving: a
+leaving: b
+</pre>
+
+<p>
+For programmers accustomed to block-level resource management from
+other languages, <code>defer</code> may seem peculiar, but its most
+interesting and powerful applications come precisely from the fact
+that it's not block-based but function-based.  In the section on
+<code>panic</code> and <code>recover</code> we'll see another
+example of its possibilities.
+</p>
+
+<h2 id="data">Data</h2>
+
+<h3 id="allocation_new">Allocation with <code>new</code></h3>
+
+<p>
+Go has two allocation primitives, the built-in functions
+<code>new</code> and <code>make</code>.
+They do different things and apply to different types, which can be confusing,
+but the rules are simple.
+Let's talk about <code>new</code> first.
+It's a built-in function that allocates memory, but unlike its namesakes
+in some other languages it does not <em>initialize</em> the memory,
+it only <em>zeroes</em> it.
+That is,
+<code>new(T)</code> allocates zeroed storage for a new item of type
+<code>T</code> and returns its address, a value of type <code>*T</code>.
+In Go terminology, it returns a pointer to a newly allocated zero value of type
+<code>T</code>.
+</p>
+
+<p>
+Since the memory returned by <code>new</code> is zeroed, it's helpful to arrange
+when designing your data structures that the
+zero value of each type can be used without further initialization.  This means a user of
+the data structure can create one with <code>new</code> and get right to
+work.
+For example, the documentation for <code>bytes.Buffer</code> states that
+"the zero value for <code>Buffer</code> is an empty buffer ready to use."
+Similarly, <code>sync.Mutex</code> does not
+have an explicit constructor or <code>Init</code> method.
+Instead, the zero value for a <code>sync.Mutex</code>
+is defined to be an unlocked mutex.
+</p>
+
+<p>
+The zero-value-is-useful property works transitively. Consider this type declaration.
+</p>
+
+<pre>
+type SyncedBuffer struct {
+    lock    sync.Mutex
+    buffer  bytes.Buffer
+}
+</pre>
+
+<p>
+Values of type <code>SyncedBuffer</code> are also ready to use immediately upon allocation
+or just declaration.  In the next snippet, both <code>p</code> and <code>v</code> will work
+correctly without further arrangement.
+</p>
+
+<pre>
+p := new(SyncedBuffer)  // type *SyncedBuffer
+var v SyncedBuffer      // type  SyncedBuffer
+</pre>
+
+<h3 id="composite_literals">Constructors and composite literals</h3>
+
+<p>
+Sometimes the zero value isn't good enough and an initializing
+constructor is necessary, as in this example derived from
+package <code>os</code>.
+</p>
+
+<pre>
+func NewFile(fd int, name string) *File {
+    if fd < 0 {
+        return nil
+    }
+    f := new(File)
+    f.fd = fd
+    f.name = name
+    f.dirinfo = nil
+    f.nepipe = 0
+    return f
+}
+</pre>
+
+<p>
+There's a lot of boiler plate in there.  We can simplify it
+using a <i>composite literal</i>, which is
+an expression that creates a
+new instance each time it is evaluated.
+</p>
+
+<pre>
+func NewFile(fd int, name string) *File {
+    if fd < 0 {
+        return nil
+    }
+    f := File{fd, name, nil, 0}
+    return &f
+}
+</pre>
+
+<p>
+Note that, unlike in C, it's perfectly OK to return the address of a local variable;
+the storage associated with the variable survives after the function
+returns.
+In fact, taking the address of a composite literal
+allocates a fresh instance each time it is evaluated,
+so we can combine these last two lines.
+</p>
+
+<pre>
+    return &File{fd, name, nil, 0}
+</pre>
+
+<p>
+The fields of a composite literal are laid out in order and must all be present.
+However, by labeling the elements explicitly as <i>field</i><code>:</code><i>value</i>
+pairs, the initializers can appear in any
+order, with the missing ones left as their respective zero values.  Thus we could say
+</p>
+
+<pre>
+    return &File{fd: fd, name: name}
+</pre>
+
+<p>
+As a limiting case, if a composite literal contains no fields at all, it creates
+a zero value for the type.  The expressions <code>new(File)</code> and <code>&File{}</code> are equivalent.
+</p>
+
+<p>
+Composite literals can also be created for arrays, slices, and maps,
+with the field labels being indices or map keys as appropriate.
+In these examples, the initializations work regardless of the values of <code>Enone</code>,
+<code>Eio</code>, and <code>Einval</code>, as long as they are distinct.
+</p>
+
+<pre>
+a := [...]string   {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
+s := []string      {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
+m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
+</pre>
+
+<h3 id="allocation_make">Allocation with <code>make</code></h3>
+
+<p>
+Back to allocation.
+The built-in function <code>make(T, </code><i>args</i><code>)</code> serves
+a purpose different from <code>new(T)</code>.
+It creates slices, maps, and channels only, and it returns an <em>initialized</em>
+(not <em>zeroed</em>)
+value of type <code>T</code> (not <code>*T</code>).
+The reason for the distinction
+is that these three types are, under the covers, references to data structures that
+must be initialized before use.
+A slice, for example, is a three-item descriptor
+containing a pointer to the data (inside an array), the length, and the
+capacity, and until those items are initialized, the slice is <code>nil</code>.
+For slices, maps, and channels,
+<code>make</code> initializes the internal data structure and prepares
+the value for use.
+For instance,
+</p>
+
+<pre>
+make([]int, 10, 100)
+</pre>
+
+<p>
+allocates an array of 100 ints and then creates a slice
+structure with length 10 and a capacity of 100 pointing at the first
+10 elements of the array.
+(When making a slice, the capacity can be omitted; see the section on slices
+for more information.)
+In contrast, <code>new([]int)</code> returns a pointer to a newly allocated, zeroed slice
+structure, that is, a pointer to a <code>nil</code> slice value.
+
+<p>
+These examples illustrate the difference between <code>new</code> and
+<code>make</code>.
+</p>
+
+<pre>
+var p *[]int = new([]int)       // allocates slice structure; *p == nil; rarely useful
+var v  []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
+
+// Unnecessarily complex:
+var p *[]int = new([]int)
+*p = make([]int, 100, 100)
+
+// Idiomatic:
+v := make([]int, 100)
+</pre>
+
+<p>
+Remember that <code>make</code> applies only to maps, slices and channels
+and does not return a pointer.
+To obtain an explicit pointer allocate with <code>new</code>.
+</p>
+
+<h3 id="arrays">Arrays</h3>
+
+<p>
+Arrays are useful when planning the detailed layout of memory and sometimes
+can help avoid allocation, but primarily
+they are a building block for slices, the subject of the next section.
+To lay the foundation for that topic, here are a few words about arrays.
+</p>
+
+<p>
+There are major differences between the ways arrays work in Go and C.
+In Go,
+</p>
+<ul>
+<li>
+Arrays are values. Assigning one array to another copies all the elements.
+</li>
+<li>
+In particular, if you pass an array to a function, it
+will receive a <i>copy</i> of the array, not a pointer to it.
+<li>
+The size of an array is part of its type.  The types <code>[10]int</code>
+and <code>[20]int</code> are distinct.
+</li>
+</ul>
+
+<p>
+The value property can be useful but also expensive; if you want C-like behavior and efficiency,
+you can pass a pointer to the array.
+</p>
+
+<pre>
+func Sum(a *[3]float64) (sum float64) {
+    for _, v := range *a {
+        sum += v
+    }
+    return
+}
+
+array := [...]float64{7.0, 8.5, 9.1}
+x := Sum(&array)  // Note the explicit address-of operator
+</pre>
+
+<p>
+But even this style isn't idiomatic Go.  Slices are.
+</p>
+
+<h3 id="slices">Slices</h3>
+
+<p>
+Slices wrap arrays to give a more general, powerful, and convenient
+interface to sequences of data.  Except for items with explicit
+dimension such as transformation matrices, most array programming in
+Go is done with slices rather than simple arrays.
+</p>
+<p>
+Slices are <i>reference types</i>, which means that if you assign one
+slice to another, both refer to the same underlying array.  For
+instance, if a function takes a slice argument, changes it makes to
+the elements of the slice will be visible to the caller, analogous to
+passing a pointer to the underlying array.  A <code>Read</code>
+function can therefore accept a slice argument rather than a pointer
+and a count; the length within the slice sets an upper
+limit of how much data to read.  Here is the signature of the
+<code>Read</code> method of the <code>File</code> type in package
+<code>os</code>:
+</p>
+<pre>
+func (file *File) Read(buf []byte) (n int, err os.Error)
+</pre>
+<p>
+The method returns the number of bytes read and an error value, if
+any.  To read into the first 32 bytes of a larger buffer
+<code>b</code>, <i>slice</i> (here used as a verb) the buffer.
+</p>
+<pre>
+    n, err := f.Read(buf[0:32])
+</pre>
+<p>
+Such slicing is common and efficient.  In fact, leaving efficiency aside for
+the moment, this snippet would also read the first 32 bytes of the buffer.
+</p>
+<pre>
+    var n int
+    var err os.Error
+    for i := 0; i < 32; i++ {
+        nbytes, e := f.Read(buf[i:i+1])  // Read one byte.
+        if nbytes == 0 || e != nil {
+            err = e
+            break
+        }
+        n += nbytes
+    }
+</pre>
+<p>
+The length of a slice may be changed as long as it still fits within
+the limits of the underlying array; just assign it to a slice of
+itself.  The <i>capacity</i> of a slice, accessible by the built-in
+function <code>cap</code>, reports the maximum length the slice may
+assume.  Here is a function to append data to a slice.  If the data
+exceeds the capacity, the slice is reallocated.  The
+resulting slice is returned.  The function uses the fact that
+<code>len</code> and <code>cap</code> are legal when applied to the
+<code>nil</code> slice, and return 0.
+</p>
+<pre>
+func Append(slice, data[]byte) []byte {
+    l := len(slice)
+    if l + len(data) > cap(slice) {  // reallocate
+        // Allocate double what's needed, for future growth.
+        newSlice := make([]byte, (l+len(data))*2)
+        // The copy function is predeclared and works for any slice type.
+        copy(newSlice, slice)
+        slice = newSlice
+    }
+    slice = slice[0:l+len(data)]
+    for i, c := range data {
+        slice[l+i] = c
+    }
+    return slice
+}
+</pre>
+<p>
+We must return the slice afterwards because, although <code>Append</code>
+can modify the elements of <code>slice</code>, the slice itself (the run-time data
+structure holding the pointer, length, and capacity) is passed by value.
+<p>
+The idea of appending to a slice is so useful it's captured by the
+<code>append</code> built-in function.  To understand that function's
+design, though, we need a little more information, so we'll return
+to it later.
+</p>
+
+
+<h3 id="maps">Maps</h3>
+
+<p>
+Maps are a convenient and powerful built-in data structure to associate
+values of different types.
+The key can be of any type for which the equality operator is defined,
+such as integers,
+floating point and complex numbers,
+strings, pointers, and interfaces (as long as the dynamic type
+supports equality).  Structs, arrays and slices cannot be used as map keys,
+because equality is not defined on those types.
+Like slices, maps are a reference type. If you pass a map to a function
+that changes the contents of the map, the changes will be visible
+in the caller.
+</p>
+<p>
+Maps can be constructed using the usual composite literal syntax
+with colon-separated key-value pairs,
+so it's easy to build them during initialization.
+</p>
+<pre>
+var timeZone = map[string] int {
+    "UTC":  0*60*60,
+    "EST": -5*60*60,
+    "CST": -6*60*60,
+    "MST": -7*60*60,
+    "PST": -8*60*60,
+}
+</pre>
+<p>
+Assigning and fetching map values looks syntactically just like
+doing the same for arrays except that the index doesn't need to
+be an integer.
+</p>
+<pre>
+offset := timeZone["EST"]
+</pre>
+<p>
+An attempt to fetch a map value with a key that
+is not present in the map will return the zero value for the type
+of the entries
+in the map.  For instance, if the map contains integers, looking
+up a non-existent key will return <code>0</code>.
+A set can be implemented as a map with value type <code>bool</code>.
+Set the map entry to <code>true</code> to put the value in the set, and then
+test it by simple indexing.
+</p>
+<pre>
+attended := map[string] bool {
+    "Ann": true,
+    "Joe": true,
+    ...
+}
+
+if attended[person] { // will be false if person is not in the map
+    fmt.Println(person, "was at the meeting")
+}
+</pre>
+<p>
+Sometimes you need to distinguish a missing entry from
+a zero value.  Is there an entry for <code>"UTC"</code>
+or is that zero value because it's not in the map at all?
+You can discriminate with a form of multiple assignment.
+</p>
+<pre>
+var seconds int
+var ok bool
+seconds, ok = timeZone[tz]
+</pre>
+<p>
+For obvious reasons this is called the “comma ok” idiom.
+In this example, if <code>tz</code> is present, <code>seconds</code>
+will be set appropriately and <code>ok</code> will be true; if not,
+<code>seconds</code> will be set to zero and <code>ok</code> will
+be false.
+Here's a function that puts it together with a nice error report:
+</p>
+<pre>
+func offset(tz string) int {
+    if seconds, ok := timeZone[tz]; ok {
+        return seconds
+    }
+    log.Println("unknown time zone:", tz)
+    return 0
+}
+</pre>
+<p>
+To test for presence in the map without worrying about the actual value,
+you can use the <em>blank identifier</em>, a simple underscore (<code>_</code>).
+The blank identifier can be assigned or declared with any value of any type, with the
+value discarded harmlessly.  For testing just presence in a map, use the blank
+identifier in place of the usual variable for the value.
+</p>
+<pre>
+_, present := timeZone[tz]
+</pre>
+<p>
+To delete a map entry, turn the multiple assignment around by placing
+an extra boolean on the right; if the boolean is false, the entry
+is deleted. It's safe to do this even if the key is already absent
+from the map.
+</p>
+<pre>
+timeZone["PDT"] = 0, false  // Now on Standard Time
+</pre>
+
+<h3 id="printing">Printing</h3>
+
+<p>
+Formatted printing in Go uses a style similar to C's <code>printf</code>
+family but is richer and more general. The functions live in the <code>fmt</code>
+package and have capitalized names: <code>fmt.Printf</code>, <code>fmt.Fprintf</code>,
+<code>fmt.Sprintf</code> and so on.  The string functions (<code>Sprintf</code> etc.)
+return a string rather than filling in a provided buffer.
+</p>
+<p>
+You don't need to provide a format string.  For each of <code>Printf</code>,
+<code>Fprintf</code> and <code>Sprintf</code> there is another pair
+of functions, for instance <code>Print</code> and <code>Println</code>.
+These functions do not take a format string but instead generate a default
+format for each argument. The <code>Println</code> versions also insert a blank
+between arguments and append a newline to the output while
+the <code>Print</code> versions add blanks only if the operand on neither side is a string.
+In this example each line produces the same output.
+</p>
+<pre>
+fmt.Printf("Hello %d\n", 23)
+fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
+fmt.Println("Hello", 23)
+fmt.Println(fmt.Sprint("Hello ", 23))
+</pre>
+<p>
+As mentioned in
+the <a href="go_tutorial.html">tutorial</a>, <code>fmt.Fprint</code>
+and friends take as a first argument any object
+that implements the <code>io.Writer</code> interface; the variables <code>os.Stdout</code>
+and <code>os.Stderr</code> are familiar instances.
+</p>
+<p>
+Here things start to diverge from C.  First, the numeric formats such as <code>%d</code>
+do not take flags for signedness or size; instead, the printing routines use the
+type of the argument to decide these properties.
+</p>
+<pre>
+var x uint64 = 1<<64 - 1
+fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))
+</pre>
+<p>
+prints
+</p>
+<pre>
+18446744073709551615 ffffffffffffffff; -1 -1
+</pre>
+<p>
+If you just want the default conversion, such as decimal for integers, you can use
+the catchall format <code>%v</code> (for “value”); the result is exactly
+what <code>Print</code> and <code>Println</code> would produce.
+Moreover, that format can print <em>any</em> value, even arrays, structs, and
+maps.  Here is a print statement for the time zone map defined in the previous section.
+</p>
+<pre>
+fmt.Printf("%v\n", timeZone)  // or just fmt.Println(timeZone)
+</pre>
+<p>
+which gives output
+</p>
+<pre>
+map[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200]
+</pre>
+<p>
+For maps the keys may be output in any order, of course.
+When printing a struct, the modified format <code>%+v</code> annotates the
+fields of the structure with their names, and for any value the alternate
+format <code>%#v</code> prints the value in full Go syntax.
+</p>
+<pre>
+type T struct {
+    a int
+    b float
+    c string
+}
+t := &T{ 7, -2.35, "abc\tdef" }
+fmt.Printf("%v\n", t)
+fmt.Printf("%+v\n", t)
+fmt.Printf("%#v\n", t)
+fmt.Printf("%#v\n", timeZone)
+</pre>
+<p>
+prints
+</p>
+<pre>
+&{7 -2.35 abc   def}
+&{a:7 b:-2.35 c:abc     def}
+&main.T{a:7, b:-2.35, c:"abc\tdef"}
+map[string] int{"CST":-21600, "PST":-28800, "EST":-18000, "UTC":0, "MST":-25200}
+</pre>
+<p>
+(Note the ampersands.)
+That quoted string format is also available through <code>%q</code> when
+applied to a value of type <code>string</code> or <code>[]byte</code>;
+the alternate format <code>%#q</code> will use backquotes instead if possible.
+Also, <code>%x</code> works on strings and arrays of bytes as well as on integers,
+generating a long hexadecimal string, and with
+a space in the format (<code>% x</code>) it puts spaces between the bytes.
+</p>
+<p>
+Another handy format is <code>%T</code>, which prints the <em>type</em> of a value.
+<pre>
+fmt.Printf("%T\n", timeZone)
+</pre>
+<p>
+prints
+</p>
+<pre>
+map[string] int
+</pre>
+<p>
+If you want to control the default format for a custom type, all that's required is to define
+a method with the signature <code>String() string</code> on the type.
+For our simple type <code>T</code>, that might look like this.
+</p>
+<pre>
+func (t *T) String() string {
+    return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
+}
+fmt.Printf("%v\n", t)
+</pre>
+<p>
+to print in the format
+</p>
+<pre>
+7/-2.35/"abc\tdef"
+</pre>
+<p>
+(If you need to print <em>values</em> of type <code>T</code> as well as pointers to <code>T</code>,
+the receiver for <code>String</code> must be of value type; this example used a pointer because
+that's more efficient and idiomatic for struct types.
+See the section below on <a href="#pointers_vs_values">pointers vs. value receivers</a> for more information.)
+</p>
+<p>
+Our <code>String</code> method is able to call <code>Sprintf</code> because the
+print routines are fully reentrant and can be used recursively.
+We can even go one step further and pass a print routine's arguments directly to another such routine.
+The signature of <code>Printf</code> uses the type <code>...interface{}</code>
+for its final argument to specify that an arbitrary number of parameters (of arbitrary type)
+can appear after the format.
+</p>
+<pre>
+func Printf(format string, v ...interface{}) (n int, errno os.Error) {
+</pre>
+<p>
+Within the function <code>Printf</code>, <code>v</code> acts like a variable of type
+<code>[]interface{}</code> but if it is passed to another variadic function, it acts like
+a regular list of arguments.
+Here is the implementation of the
+function <code>log.Println</code> we used above. It passes its arguments directly to
+<code>fmt.Sprintln</code> for the actual formatting.
+</p>
+<pre>
+// Println prints to the standard logger in the manner of fmt.Println.
+func Println(v ...interface{}) {
+    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)
+}
+</pre>
+<p>
+We write <code>...</code> after <code>v</code> in the nested call to <code>Sprintln</code> to tell the
+compiler to treat <code>v</code> as a list of arguments; otherwise it would just pass
+<code>v</code> as a single slice argument.
+<p>
+There's even more to printing than we've covered here.  See the <code>godoc</code> documentation
+for package <code>fmt</code> for the details.
+</p>
+<p>
+By the way, a <code>...</code> parameter can be of a specific type, for instance <code>...int</code>
+for a min function that chooses the least of a list of integers:
+</p>
+<pre>
+func Min(a ...int) int {
+    min := int(^uint(0) >> 1)  // largest int
+    for _, i := range a {
+        if i < min {
+            min = i
+        }
+    }
+    return min
+}
+</pre>
+
+<h3 id="append">Append</h3>
+<p>
+Now we have the missing piece we needed to explain the design of
+the <code>append</code> built-in function.  The signature of <code>append</code>
+is different from our custom <code>Append</code> function above.
+Schematically, it's like this:
+<pre>
+func append(slice []<i>T</i>, elements...T) []<i>T</i>
+</pre>
+where <i>T</i> is a placeholder for any given type.  You can't
+actually write a function in Go where the type <code>T</code>
+is determined by the caller.
+That's why <code>append</code> is built in: it needs support from the
+compiler.
+<p>
+What <code>append</code> does is append the elements to the end of
+the slice and return the result.  The result needs to be returned
+because, as with our hand-written <code>Append</code>, the underlying
+array may change.  This simple example
+<pre>
+x := []int{1,2,3}
+x = append(x, 4, 5, 6)
+fmt.Println(x)
+</pre>
+prints <code>[1 2 3 4 5 6]</code>.  So <code>append</code> works a
+little like <code>Printf</code>, collecting an arbitrary number of
+arguments.
+<p>
+But what if we wanted to do what our <code>Append</code> does and
+append a slice to a slice?  Easy: use <code>...</code> at the call
+site, just as we did in the call to <code>Output</code> above.  This
+snippet produces identical output to the one above.
+<pre>
+x := []int{1,2,3}
+y := []int{4,5,6}
+x = append(x, y...)
+fmt.Println(x)
+</pre>
+Without that <code>...</code>, it wouldn't compile because the types
+would be wrong; <code>y</code> is not of type <code>int</code>.
+
+<h2 id="initialization">Initialization</h2>
+
+<p>
+Although it doesn't look superficially very different from
+initialization in C or C++, initialization in Go is more powerful.
+Complex structures can be built during initialization and the ordering
+issues between initialized objects in different packages are handled
+correctly.
+</p>
+
+<h3 id="constants">Constants</h3>
+
+<p>
+Constants in Go are just that—constant.
+They are created at compile time, even when defined as
+locals in functions,
+and can only be numbers, strings or booleans.
+Because of the compile-time restriction, the expressions
+that define them must be constant expressions,
+evaluatable by the compiler.  For instance,
+<code>1<<3</code> is a constant expression, while
+<code>math.Sin(math.Pi/4)</code> is not because
+the function call to <code>math.Sin</code> needs
+to happen at run time.
+</p>
+
+<p>
+In Go, enumerated constants are created using the <code>iota</code>
+enumerator.  Since <code>iota</code> can be part of an expression and
+expressions can be implicitly repeated, it is easy to build intricate
+sets of values.
+</p>
+{{code "progs/eff_bytesize.go" `/^type ByteSize/` `/^\)/`}}
+<p>
+The ability to attach a method such as <code>String</code> to a
+type makes it possible for such values to format themselves
+automatically for printing, even as part of a general type.
+</p>
+{{code "progs/eff_bytesize.go" `/^func.*ByteSize.*String/` `/^}/`}}
+<p>
+(The <code>float64</code> conversions prevent <code>Sprintf</code> 
+from recurring back through the <code>String</code> method for 
+<code>ByteSize</code>.)
+The expression <code>YB</code> prints as <code>1.00YB</code>,
+while <code>ByteSize(1e13)</code> prints as <code>9.09TB</code>.
+</p>
+
+<h3 id="variables">Variables</h3>
+
+<p>
+Variables can be initialized just like constants but the
+initializer can be a general expression computed at run time.
+</p>
+<pre>
+var (
+    HOME = os.Getenv("HOME")
+    USER = os.Getenv("USER")
+    GOROOT = os.Getenv("GOROOT")
+)
+</pre>
+
+<h3 id="init">The init function</h3>
+
+<p>
+Finally, each source file can define its own niladic <code>init</code> function to
+set up whatever state is required.  (Actually each file can have multiple
+<code>init</code> functions.) The only restriction is that, although
+goroutines can be launched during initialization, they will not begin
+execution until it completes; initialization always runs as a single thread
+of execution.
+And finally means finally: <code>init</code> is called after all the
+variable declarations in the package have evaluated their initializers,
+and those are evaluated only after all the imported packages have been
+initialized.
+</p>
+<p>
+Besides initializations that cannot be expressed as declarations,
+a common use of <code>init</code> functions is to verify or repair
+correctness of the program state before real execution begins.
+</p>
+
+<pre>
+func init() {
+    if USER == "" {
+        log.Fatal("$USER not set")
+    }
+    if HOME == "" {
+        HOME = "/usr/" + USER
+    }
+    if GOROOT == "" {
+        GOROOT = HOME + "/go"
+    }
+    // GOROOT may be overridden by --goroot flag on command line.
+    flag.StringVar(&GOROOT, "goroot", GOROOT, "Go root directory")
+}
+</pre>
+
+<h2 id="methods">Methods</h2>
+
+<h3 id="pointers_vs_values">Pointers vs. Values</h3>
+<p>
+Methods can be defined for any named type that is not a pointer or an interface;
+the receiver does not have to be a struct.
+<p>
+In the discussion of slices above, we wrote an <code>Append</code>
+function.  We can define it as a method on slices instead.  To do
+this, we first declare a named type to which we can bind the method, and
+then make the receiver for the method a value of that type.
+</p>
+<pre>
+type ByteSlice []byte
+
+func (slice ByteSlice) Append(data []byte) []byte {
+    // Body exactly the same as above
+}
+</pre>
+<p>
+This still requires the method to return the updated slice.  We can
+eliminate that clumsiness by redefining the method to take a
+<i>pointer</i> to a <code>ByteSlice</code> as its receiver, so the
+method can overwrite the caller's slice.
+</p>
+<pre>
+func (p *ByteSlice) Append(data []byte) {
+    slice := *p
+    // Body as above, without the return.
+    *p = slice
+}
+</pre>
+<p>
+In fact, we can do even better.  If we modify our function so it looks
+like a standard <code>Write</code> method, like this,
+</p>
+<pre>
+func (p *ByteSlice) Write(data []byte) (n int, err os.Error) {
+    slice := *p
+    // Again as above.
+    *p = slice
+    return len(data), nil
+}
+</pre>
+<p>
+then the type <code>*ByteSlice</code> satisfies the standard interface
+<code>io.Writer</code>, which is handy.  For instance, we can
+print into one.
+</p>
+<pre>
+    var b ByteSlice
+    fmt.Fprintf(&b, "This hour has %d days\n", 7)
+</pre>
+<p>
+We pass the address of a <code>ByteSlice</code>
+because only <code>*ByteSlice</code> satisfies <code>io.Writer</code>.
+The rule about pointers vs. values for receivers is that value methods
+can be invoked on pointers and values, but pointer methods can only be
+invoked on pointers.  This is because pointer methods can modify the
+receiver; invoking them on a copy of the value would cause those
+modifications to be discarded.
+</p>
+<p>
+By the way, the idea of using <code>Write</code> on a slice of bytes
+is implemented by <code>bytes.Buffer</code>.
+</p>
+
+<h2 id="interfaces_and_types">Interfaces and other types</h2>
+
+<h3 id="interfaces">Interfaces</h3>
+<p>
+Interfaces in Go provide a way to specify the behavior of an
+object: if something can do <em>this</em>, then it can be used
+<em>here</em>.  We've seen a couple of simple examples already;
+custom printers can be implemented by a <code>String</code> method
+while <code>Fprintf</code> can generate output to anything
+with a <code>Write</code> method.
+Interfaces with only one or two methods are common in Go code, and are
+usually given a name derived from the method, such as <code>io.Writer</code>
+for something that implements <code>Write</code>.
+</p>
+<p>
+A type can implement multiple interfaces.
+For instance, a collection can be sorted
+by the routines in package <code>sort</code> if it implements
+<code>sort.Interface</code>, which contains <code>Len()</code>,
+<code>Less(i, j int) bool</code>, and <code>Swap(i, j int)</code>,
+and it could also have a custom formatter.
+In this contrived example <code>Sequence</code> satisfies both.
+</p>
+{{code "progs/eff_sequence.go" `/^type/` "$"}}
+
+<h3 id="conversions">Conversions</h3>
+
+<p>
+The <code>String</code> method of <code>Sequence</code> is recreating the
+work that <code>Sprint</code> already does for slices.  We can share the
+effort if we convert the <code>Sequence</code> to a plain
+<code>[]int</code> before calling <code>Sprint</code>.
+</p>
+<pre>
+func (s Sequence) String() string {
+    sort.Sort(s)
+    return fmt.Sprint([]int(s))
+}
+</pre>
+<p>
+The conversion causes <code>s</code> to be treated as an ordinary slice
+and therefore receive the default formatting.
+Without the conversion, <code>Sprint</code> would find the
+<code>String</code> method of <code>Sequence</code> and recur indefinitely.
+Because the two types (<code>Sequence</code> and <code>[]int</code>)
+are the same if we ignore the type name, it's legal to convert between them.
+The conversion doesn't create a new value, it just temporarily acts
+as though the existing value has a new type.
+(There are other legal conversions, such as from integer to floating point, that
+do create a new value.)
+</p>
+<p>
+It's an idiom in Go programs to convert the
+type of an expression to access a different
+set of methods. As an example, we could use the existing
+type <code>sort.IntArray</code> to reduce the entire example
+to this:
+</p>
+<pre>
+type Sequence []int
+
+// Method for printing - sorts the elements before printing
+func (s Sequence) String() string {
+    sort.IntArray(s).Sort()
+    return fmt.Sprint([]int(s))
+}
+</pre>
+<p>
+Now, instead of having <code>Sequence</code> implement multiple
+interfaces (sorting and printing), we're using the ability of a data item to be
+converted to multiple types (<code>Sequence</code>, <code>sort.IntArray</code>
+and <code>[]int</code>), each of which does some part of the job.
+That's more unusual in practice but can be effective.
+</p>
+
+<h3 id="generality">Generality</h3>
+<p>
+If a type exists only to implement an interface
+and has no exported methods beyond that interface,
+there is no need to export the type itself.
+Exporting just the interface makes it clear that
+it's the behavior that matters, not the implementation,
+and that other implementations with different properties
+can mirror the behavior of the original type.
+It also avoids the need to repeat the documentation
+on every instance of a common method.
+</p>
+<p>
+In such cases, the constructor should return an interface value
+rather than the implementing type.
+As an example, in the hash libraries
+both <code>crc32.NewIEEE</code> and <code>adler32.New</code>
+return the interface type <code>hash.Hash32</code>.
+Substituting the CRC-32 algorithm for Adler-32 in a Go program
+requires only changing the constructor call;
+the rest of the code is unaffected by the change of algorithm.
+</p>
+<p>
+A similar approach allows the streaming cipher algorithms
+in the <code>crypto/block</code> package to be
+separated from the block ciphers they chain together.
+By analogy with the <code>bufio</code> package,
+they wrap a <code>Cipher</code> interface
+and return <code>hash.Hash</code>,
+<code>io.Reader</code>, or <code>io.Writer</code>
+interface values, not specific implementations.
+</p>
+<p>
+The interface to <code>crypto/block</code> includes:
+</p>
+<pre>
+type Cipher interface {
+    BlockSize() int
+    Encrypt(src, dst []byte)
+    Decrypt(src, dst []byte)
+}
+
+// NewECBDecrypter returns a reader that reads data
+// from r and decrypts it using c in electronic codebook (ECB) mode.
+func NewECBDecrypter(c Cipher, r io.Reader) io.Reader
+
+// NewCBCDecrypter returns a reader that reads data
+// from r and decrypts it using c in cipher block chaining (CBC) mode
+// with the initialization vector iv.
+func NewCBCDecrypter(c Cipher, iv []byte, r io.Reader) io.Reader
+</pre>
+<p>
+<code>NewECBDecrypter</code> and <code>NewCBCReader</code> apply not
+just to one specific encryption algorithm and data source but to any
+implementation of the <code>Cipher</code> interface and any
+<code>io.Reader</code>.  Because they return <code>io.Reader</code>
+interface values, replacing ECB
+encryption with CBC encryption is a localized change.  The constructor
+calls must be edited, but because the surrounding code must treat the result only
+as an <code>io.Reader</code>, it won't notice the difference.
+</p>
+
+<h3 id="interface_methods">Interfaces and methods</h3>
+<p>
+Since almost anything can have methods attached, almost anything can
+satisfy an interface.  One illustrative example is in the <code>http</code>
+package, which defines the <code>Handler</code> interface.  Any object
+that implements <code>Handler</code> can serve HTTP requests.
+</p>
+<pre>
+type Handler interface {
+    ServeHTTP(ResponseWriter, *Request)
+}
+</pre>
+<p>
+<code>ResponseWriter</code> is itself an interface that provides access
+to the methods needed to return the response to the client.
+Those methods include the standard <code>Write</code> method, so an
+<code>http.ResponseWriter</code> can be used wherever an <code>io.Writer</code>
+can be used.
+<code>Request</code> is a struct containing a parsed representation
+of the request from the client.
+<p>
+For brevity, let's ignore POSTs and assume HTTP requests are always
+GETs; that simplification does not affect the way the handlers are
+set up.  Here's a trivial but complete implementation of a handler to
+count the number of times the
+page is visited.
+</p>
+<pre>
+// Simple counter server.
+type Counter struct {
+    n int
+}
+
+func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+    ctr.n++
+    fmt.Fprintf(w, "counter = %d\n", ctr.n)
+}
+</pre>
+<p>
+(Keeping with our theme, note how <code>Fprintf</code> can print to an
+<code>http.ResponseWriter</code>.)
+For reference, here's how to attach such a server to a node on the URL tree.
+<pre>
+import "http"
+...
+ctr := new(Counter)
+http.Handle("/counter", ctr)
+</pre>
+<p>
+But why make <code>Counter</code> a struct?  An integer is all that's needed.
+(The receiver needs to be a pointer so the increment is visible to the caller.)
+</p>
+<pre>
+// Simpler counter server.
+type Counter int
+
+func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+    *ctr++
+    fmt.Fprintf(w, "counter = %d\n", *ctr)
+}
+</pre>
+<p>
+What if your program has some internal state that needs to be notified that a page
+has been visited?  Tie a channel to the web page.
+</p>
+<pre>
+// A channel that sends a notification on each visit.
+// (Probably want the channel to be buffered.)
+type Chan chan *http.Request
+
+func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+    ch <- req
+    fmt.Fprint(w, "notification sent")
+}
+</pre>
+<p>
+Finally, let's say we wanted to present on <code>/args</code> the arguments
+used when invoking the server binary.
+It's easy to write a function to print the arguments.
+</p>
+<pre>
+func ArgServer() {
+    for i, s := range os.Args {
+        fmt.Println(s)
+    }
+}
+</pre>
+<p>
+How do we turn that into an HTTP server?  We could make <code>ArgServer</code>
+a method of some type whose value we ignore, but there's a cleaner way.
+Since we can define a method for any type except pointers and interfaces,
+we can write a method for a function.
+The <code>http</code> package contains this code:
+</p>
+<pre>
+// The HandlerFunc type is an adapter to allow the use of
+// ordinary functions as HTTP handlers.  If f is a function
+// with the appropriate signature, HandlerFunc(f) is a
+// Handler object that calls f.
+type HandlerFunc func(ResponseWriter, *Request)
+
+// ServeHTTP calls f(c, req).
+func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) {
+    f(w, req)
+}
+</pre>
+<p>
+<code>HandlerFunc</code> is a type with a method, <code>ServeHTTP</code>,
+so values of that type can serve HTTP requests.  Look at the implementation
+of the method: the receiver is a function, <code>f</code>, and the method
+calls <code>f</code>.  That may seem odd but it's not that different from, say,
+the receiver being a channel and the method sending on the channel.
+</p>
+<p>
+To make <code>ArgServer</code> into an HTTP server, we first modify it
+to have the right signature.
+</p>
+<pre>
+// Argument server.
+func ArgServer(w http.ResponseWriter, req *http.Request) {
+    for i, s := range os.Args {
+        fmt.Fprintln(w, s)
+    }
+}
+</pre>
+<p>
+<code>ArgServer</code> now has same signature as <code>HandlerFunc</code>,
+so it can be converted to that type to access its methods,
+just as we converted <code>Sequence</code> to <code>IntArray</code>
+to access <code>IntArray.Sort</code>.
+The code to set it up is concise:
+</p>
+<pre>
+http.Handle("/args", http.HandlerFunc(ArgServer))
+</pre>
+<p>
+When someone visits the page <code>/args</code>,
+the handler installed at that page has value <code>ArgServer</code>
+and type <code>HandlerFunc</code>.
+The HTTP server will invoke the method <code>ServeHTTP</code>
+of that type, with <code>ArgServer</code> as the receiver, which will in turn call
+<code>ArgServer</code> (via the invocation <code>f(c, req)</code>
+inside <code>HandlerFunc.ServeHTTP</code>).
+The arguments will then be displayed.
+</p>
+<p>
+In this section we have made an HTTP server from a struct, an integer,
+a channel, and a function, all because interfaces are just sets of
+methods, which can be defined for (almost) any type.
+</p>
+
+<h2 id="embedding">Embedding</h2>
+
+<p>
+Go does not provide the typical, type-driven notion of subclassing,
+but it does have the ability to “borrow” pieces of an
+implementation by <em>embedding</em> types within a struct or
+interface.
+</p>
+<p>
+Interface embedding is very simple.
+We've mentioned the <code>io.Reader</code> and <code>io.Writer</code> interfaces before;
+here are their definitions.
+</p>
+<pre>
+type Reader interface {
+    Read(p []byte) (n int, err os.Error)
+}
+
+type Writer interface {
+    Write(p []byte) (n int, err os.Error)
+}
+</pre>
+<p>
+The <code>io</code> package also exports several other interfaces
+that specify objects that can implement several such methods.
+For instance, there is <code>io.ReadWriter</code>, an interface
+containing both <code>Read</code> and <code>Write</code>.
+We could specify <code>io.ReadWriter</code> by listing the
+two methods explicitly, but it's easier and more evocative
+to embed the two interfaces to form the new one, like this:
+</p>
+<pre>
+// ReadWriter is the interface that combines the Reader and Writer interfaces.
+type ReadWriter interface {
+    Reader
+    Writer
+}
+</pre>
+<p>
+This says just what it looks like: A <code>ReadWriter</code> can do
+what a <code>Reader</code> does <em>and</em> what a <code>Writer</code>
+does; it is a union of the embedded interfaces (which must be disjoint
+sets of methods).
+Only interfaces can be embedded within interfaces.
+<p>
+The same basic idea applies to structs, but with more far-reaching
+implications.  The <code>bufio</code> package has two struct types,
+<code>bufio.Reader</code> and <code>bufio.Writer</code>, each of
+which of course implements the analogous interfaces from package
+<code>io</code>.
+And <code>bufio</code> also implements a buffered reader/writer,
+which it does by combining a reader and a writer into one struct
+using embedding: it lists the types within the struct
+but does not give them field names.
+</p>
+<pre>
+// ReadWriter stores pointers to a Reader and a Writer.
+// It implements io.ReadWriter.
+type ReadWriter struct {
+    *Reader  // *bufio.Reader
+    *Writer  // *bufio.Writer
+}
+</pre>
+<p>
+The embedded elements are pointers to structs and of course
+must be initialized to point to valid structs before they
+can be used.
+The <code>ReadWriter</code> struct could be written as
+</p>
+<pre>
+type ReadWriter struct {
+    reader *Reader
+    writer *Writer
+}
+</pre>
+<p>
+but then to promote the methods of the fields and to
+satisfy the <code>io</code> interfaces, we would also need
+to provide forwarding methods, like this:
+</p>
+<pre>
+func (rw *ReadWriter) Read(p []byte) (n int, err os.Error) {
+    return rw.reader.Read(p)
+}
+</pre>
+<p>
+By embedding the structs directly, we avoid this bookkeeping.
+The methods of embedded types come along for free, which means that <code>bufio.ReadWriter</code>
+not only has the methods of <code>bufio.Reader</code> and <code>bufio.Writer</code>,
+it also satisfies all three interfaces:
+<code>io.Reader</code>,
+<code>io.Writer</code>, and
+<code>io.ReadWriter</code>.
+</p>
+<p>
+There's an important way in which embedding differs from subclassing.  When we embed a type,
+the methods of that type become methods of the outer type,
+but when they are invoked the receiver of the method is the inner type, not the outer one.
+In our example, when the <code>Read</code> method of a <code>bufio.ReadWriter</code> is
+invoked, it has exactly the same effect as the forwarding method written out above;
+the receiver is the <code>reader</code> field of the <code>ReadWriter</code>, not the
+<code>ReadWriter</code> itself.
+</p>
+<p>
+Embedding can also be a simple convenience.
+This example shows an embedded field alongside a regular, named field.
+</p>
+<pre>
+type Job struct {
+    Command string
+    *log.Logger
+}
+</pre>
+<p>
+The <code>Job</code> type now has the <code>Log</code>, <code>Logf</code>
+and other
+methods of <code>*log.Logger</code>.  We could have given the <code>Logger</code>
+a field name, of course, but it's not necessary to do so.  And now, once
+initialized, we can
+log to the <code>Job</code>:
+</p>
+<pre>
+job.Log("starting now...")
+</pre>
+<p>
+The <code>Logger</code> is a regular field of the struct and we can initialize
+it in the usual way with a constructor,
+</p>
+<pre>
+func NewJob(command string, logger *log.Logger) *Job {
+    return &Job{command, logger}
+}
+</pre>
+<p>
+or with a composite literal,
+</p>
+<pre>
+job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}
+</pre>
+<p>
+If we need to refer to an embedded field directly, the type name of the field,
+ignoring the package qualifier, serves as a field name.  If we needed to access the
+<code>*log.Logger</code> of a <code>Job</code> variable <code>job</code>,
+we would write <code>job.Logger</code>.
+This would be useful if we wanted to refine the methods of <code>Logger</code>.
+</p>
+<pre>
+func (job *Job) Logf(format string, args ...interface{}) {
+    job.Logger.Logf("%q: %s", job.Command, fmt.Sprintf(format, args))
+}
+</pre>
+<p>
+Embedding types introduces the problem of name conflicts but the rules to resolve
+them are simple.
+First, a field or method <code>X</code> hides any other item <code>X</code> in a more deeply
+nested part of the type.
+If <code>log.Logger</code> contained a field or method called <code>Command</code>, the <code>Command</code> field
+of <code>Job</code> would dominate it.
+</p>
+<p>
+Second, if the same name appears at the same nesting level, it is usually an error;
+it would be erroneous to embed <code>log.Logger</code> if the <code>Job</code> struct
+contained another field or method called <code>Logger</code>.
+However, if the duplicate name is never mentioned in the program outside the type definition, it is OK.
+This qualification provides some protection against changes made to types embedded from outside; there
+is no problem if a field is added that conflicts with another field in another subtype if neither field
+is ever used.
+</p>
+
+
+<h2 id="concurrency">Concurrency</h2>
+
+<h3 id="sharing">Share by communicating</h3>
+
+<p>
+Concurrent programming is a large topic and there is space only for some
+Go-specific highlights here.
+</p>
+<p>
+Concurrent programming in many environments is made difficult by the
+subtleties required to implement correct access to shared variables.  Go encourages
+a different approach in which shared values are passed around on channels
+and, in fact, never actively shared by separate threads of execution.
+Only one goroutine has access to the value at any given time.
+Data races cannot occur, by design.
+To encourage this way of thinking we have reduced it to a slogan:
+</p>
+<blockquote>
+Do not communicate by sharing memory;
+instead, share memory by communicating.
+</blockquote>
+<p>
+This approach can be taken too far.  Reference counts may be best done
+by putting a mutex around an integer variable, for instance.  But as a
+high-level approach, using channels to control access makes it easier
+to write clear, correct programs.
+</p>
+<p>
+One way to think about this model is to consider a typical single-threaded
+program running on one CPU. It has no need for synchronization primitives.
+Now run another such instance; it too needs no synchronization.  Now let those
+two communicate; if the communication is the synchronizer, there's still no need
+for other synchronization.  Unix pipelines, for example, fit this model
+perfectly.  Although Go's approach to concurrency originates in Hoare's
+Communicating Sequential Processes (CSP),
+it can also be seen as a type-safe generalization of Unix pipes.
+</p>
+
+<h3 id="goroutines">Goroutines</h3>
+
+<p>
+They're called <em>goroutines</em> because the existing
+terms—threads, coroutines, processes, and so on—convey
+inaccurate connotations.  A goroutine has a simple model: it is a
+function executing in parallel with other goroutines in the same
+address space.  It is lightweight, costing little more than the
+allocation of stack space.
+And the stacks start small, so they are cheap, and grow
+by allocating (and freeing) heap storage as required.
+</p>
+<p>
+Goroutines are multiplexed onto multiple OS threads so if one should
+block, such as while waiting for I/O, others continue to run.  Their
+design hides many of the complexities of thread creation and
+management.
+</p>
+<p>
+Prefix a function or method call with the <code>go</code>
+keyword to run the call in a new goroutine.
+When the call completes, the goroutine
+exits, silently.  (The effect is similar to the Unix shell's
+<code>&</code> notation for running a command in the
+background.)
+</p>
+<pre>
+go list.Sort()  // run list.Sort in parallel; don't wait for it. 
+</pre>
+<p>
+A function literal can be handy in a goroutine invocation.
+<pre>
+func Announce(message string, delay int64) {
+    go func() {
+        time.Sleep(delay)
+        fmt.Println(message)
+    }()  // Note the parentheses - must call the function.
+}
+</pre>
+<p>
+In Go, function literals are closures: the implementation makes
+sure the variables referred to by the function survive as long as they are active.
+<p>
+These examples aren't too practical because the functions have no way of signaling
+completion.  For that, we need channels.
+</p>
+
+<h3 id="channels">Channels</h3>
+
+<p>
+Like maps, channels are a reference type and are allocated with <code>make</code>.
+If an optional integer parameter is provided, it sets the buffer size for the channel.
+The default is zero, for an unbuffered or synchronous channel.
+</p>
+<pre>
+ci := make(chan int)            // unbuffered channel of integers
+cj := make(chan int, 0)         // unbuffered channel of integers
+cs := make(chan *os.File, 100)  // buffered channel of pointers to Files
+</pre>
+<p>
+Channels combine communication—the exchange of a value—with
+synchronization—guaranteeing that two calculations (goroutines) are in
+a known state.
+</p>
+<p>
+There are lots of nice idioms using channels.  Here's one to get us started.
+In the previous section we launched a sort in the background. A channel
+can allow the launching goroutine to wait for the sort to complete.
+</p>
+<pre>
+c := make(chan int)  // Allocate a channel.
+// Start the sort in a goroutine; when it completes, signal on the channel.
+go func() {
+    list.Sort()
+    c <- 1  // Send a signal; value does not matter. 
+}()
+doSomethingForAWhile()
+<-c   // Wait for sort to finish; discard sent value.
+</pre>
+<p>
+Receivers always block until there is data to receive.
+If the channel is unbuffered, the sender blocks until the receiver has
+received the value.
+If the channel has a buffer, the sender blocks only until the
+value has been copied to the buffer; if the buffer is full, this
+means waiting until some receiver has retrieved a value.
+</p>
+<p>
+A buffered channel can be used like a semaphore, for instance to
+limit throughput.  In this example, incoming requests are passed
+to <code>handle</code>, which sends a value into the channel, processes
+the request, and then receives a value from the channel.
+The capacity of the channel buffer limits the number of
+simultaneous calls to <code>process</code>.
+</p>
+<pre>
+var sem = make(chan int, MaxOutstanding)
+
+func handle(r *Request) {
+    sem <- 1    // Wait for active queue to drain.
+    process(r)  // May take a long time.
+    <-sem       // Done; enable next request to run.
+}
+
+func Serve(queue chan *Request) {
+    for {
+        req := <-queue
+        go handle(req)  // Don't wait for handle to finish.
+    }
+}
+</pre>
+<p>
+Here's the same idea implemented by starting a fixed
+number of <code>handle</code> goroutines all reading from the request
+channel.
+The number of goroutines limits the number of simultaneous
+calls to <code>process</code>.
+This <code>Serve</code> function also accepts a channel on which
+it will be told to exit; after launching the goroutines it blocks
+receiving from that channel.
+</p>
+<pre>
+func handle(queue chan *Request) {
+    for r := range queue {
+        process(r)
+    }
+}
+
+func Serve(clientRequests chan *clientRequests, quit chan bool) {
+    // Start handlers
+    for i := 0; i < MaxOutstanding; i++ {
+        go handle(clientRequests)
+    }
+    <-quit  // Wait to be told to exit.
+}
+</pre>
+
+<h3 id="chan_of_chan">Channels of channels</h3>
+<p>
+One of the most important properties of Go is that
+a channel is a first-class value that can be allocated and passed
+around like any other.  A common use of this property is
+to implement safe, parallel demultiplexing.
+<p>
+In the example in the previous section, <code>handle</code> was
+an idealized handler for a request but we didn't define the
+type it was handling.  If that type includes a channel on which
+to reply, each client can provide its own path for the answer.
+Here's a schematic definition of type <code>Request</code>.
+</p>
+<pre>
+type Request struct {
+    args        []int
+    f           func([]int) int
+    resultChan  chan int
+}
+</pre>
+<p>
+The client provides a function and its arguments, as well as
+a channel inside the request object on which to receive the answer.
+</p>
+<pre>
+func sum(a []int) (s int) {
+    for _, v := range a {
+        s += v
+    }
+    return
+}
+
+request := &Request{[]int{3, 4, 5}, sum, make(chan int)}
+// Send request
+clientRequests <- request
+// Wait for response.
+fmt.Printf("answer: %d\n", <-request.resultChan)
+</pre>
+<p>
+On the server side, the handler function is the only thing that changes.
+</p>
+<pre>
+func handle(queue chan *Request) {
+    for req := range queue {
+        req.resultChan <- req.f(req.args)
+    }
+}
+</pre>
+<p>
+There's clearly a lot more to do to make it realistic, but this
+code is a framework for a rate-limited, parallel, non-blocking RPC
+system, and there's not a mutex in sight.
+</p>
+
+<h3 id="parallel">Parallelization</h3>
+<p>
+Another application of these ideas is to parallelize a calculation
+across multiple CPU cores.  If the calculation can be broken into
+separate pieces, it can be parallelized, with a channel to signal
+when each piece completes.
+</p>
+<p>
+Let's say we have an expensive operation to perform on a vector of items,
+and that the value of the operation on each item is independent,
+as in this idealized example.
+</p>
+<pre>
+type Vector []float64
+
+// Apply the operation to v[i], v[i+1] ... up to v[n-1].
+func (v Vector) DoSome(i, n int, u Vector, c chan int) {
+    for ; i < n; i++ {
+        v[i] += u.Op(v[i])
+    }
+    c <- 1    // signal that this piece is done
+}
+</pre>
+<p>
+We launch the pieces independently in a loop, one per CPU.
+They can complete in any order but it doesn't matter; we just
+count the completion signals by draining the channel after
+launching all the goroutines.
+</p>
+<pre>
+const NCPU = 4  // number of CPU cores
+
+func (v Vector) DoAll(u Vector) {
+    c := make(chan int, NCPU)  // Buffering optional but sensible.
+    for i := 0; i < NCPU; i++ {
+        go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c)
+    }
+    // Drain the channel.
+    for i := 0; i < NCPU; i++ {
+        <-c    // wait for one task to complete
+    }
+    // All done.
+}
+
+</pre>
+
+<p>
+The current implementation of <code>gc</code> (<code>6g</code>, etc.)
+will not parallelize this code by default.
+It dedicates only a single core to user-level processing.  An
+arbitrary number of goroutines can be blocked in system calls, but
+by default only one can be executing user-level code at any time.
+It should be smarter and one day it will be smarter, but until it
+is if you want CPU parallelism you must tell the run-time
+how many goroutines you want executing code simultaneously.  There
+are two related ways to do this.  Either run your job with environment
+variable <code>GOMAXPROCS</code> set to the number of cores to use
+(default 1); or import the <code>runtime</code> package and call
+<code>runtime.GOMAXPROCS(NCPU)</code>.
+Again, this requirement is expected to be retired as the scheduling and run-time improve.
+</p>
+
+<h3 id="leaky_buffer">A leaky buffer</h3>
+
+<p>
+The tools of concurrent programming can even make non-concurrent
+ideas easier to express.  Here's an example abstracted from an RPC
+package.  The client goroutine loops receiving data from some source,
+perhaps a network.  To avoid allocating and freeing buffers, it keeps
+a free list, and uses a buffered channel to represent it.  If the
+channel is empty, a new buffer gets allocated.
+Once the message buffer is ready, it's sent to the server on
+<code>serverChan</code>.
+</p>
+<pre>
+var freeList = make(chan *Buffer, 100)
+var serverChan = make(chan *Buffer)
+
+func client() {
+    for {
+        var b *Buffer
+        // Grab a buffer if available; allocate if not.
+        select {
+        case b = <-freeList:
+            // Got one; nothing more to do.
+        default:
+            // None free, so allocate a new one.
+            b = new(Buffer)
+        }
+        load(b)              // Read next message from the net.
+        serverChan <- b      // Send to server.
+    }
+}
+</pre>
+<p>
+The server loop receives each message from the client, processes it,
+and returns the buffer to the free list.
+</p>
+<pre>
+func server() {
+    for {
+        b := <-serverChan    // Wait for work.
+        process(b)
+        // Reuse buffer if there's room.
+        select {
+        case freeList <- b:
+            // Buffer on free list; nothing more to do.
+        default:
+            // Free list full, just carry on.
+        }
+    }
+}
+</pre>
+<p>
+The client attempts to retrieve a buffer from <code>freeList</code>;
+if none is available, it allocates a fresh one.
+The server's send to <code>freeList</code> puts <code>b</code> back
+on the free list unless the list is full, in which case the
+buffer is dropped on the floor to be reclaimed by
+the garbage collector.
+(The <code>default</code> clauses in the <code>select</code>
+statements execute when no other case is ready,
+meaning that the <code>selects</code> never block.)
+This implementation builds a leaky bucket free list
+in just a few lines, relying on the buffered channel and
+the garbage collector for bookkeeping.
+</p>
+
+<h2 id="errors">Errors</h2>
+
+<p>
+Library routines must often return some sort of error indication to
+the caller.  As mentioned earlier, Go's multivalue return makes it
+easy to return a detailed error description alongside the normal
+return value.  By convention, errors have type <code>os.Error</code>,
+a simple interface.
+</p>
+<pre>
+type Error interface {
+    String() string
+}
+</pre>
+<p>
+A library writer is free to implement this interface with a
+richer model under the covers, making it possible not only
+to see the error but also to provide some context.
+For example, <code>os.Open</code> returns an <code>os.PathError</code>.
+</p>
+<pre>
+// PathError records an error and the operation and
+// file path that caused it.
+type PathError struct {
+    Op string    // "open", "unlink", etc.
+    Path string  // The associated file.
+    Error Error  // Returned by the system call.
+}
+
+func (e *PathError) String() string {
+    return e.Op + " " + e.Path + ": " + e.Error.String()
+}
+</pre>
+<p>
+<code>PathError</code>'s <code>String</code> generates
+a string like this:
+</p>
+<pre>
+open /etc/passwx: no such file or directory
+</pre>
+<p>
+Such an error, which includes the problematic file name, the
+operation, and the operating system error it triggered, is useful even
+if printed far from the call that caused it;
+it is much more informative than the plain
+"no such file or directory".
+</p>
+
+<p>
+When feasible, error strings should identify their origin, such as by having
+a prefix naming the package that generated the error.  For example, in package
+image, the string representation for a decoding error due to an unknown format
+is "image: unknown format".
+</p>
+
+<p>
+Callers that care about the precise error details can
+use a type switch or a type assertion to look for specific
+errors and extract details.  For <code>PathErrors</code>
+this might include examining the internal <code>Error</code>
+field for recoverable failures.
+</p>
+
+<pre>
+for try := 0; try < 2; try++ {
+    file, err = os.Open(filename)
+    if err == nil {
+        return
+    }
+    if e, ok := err.(*os.PathError); ok && e.Error == os.ENOSPC {
+        deleteTempFiles()  // Recover some space.
+        continue
+    }
+    return
+}
+</pre>
+
+<h3 id="panic">Panic</h3>
+
+<p>
+The usual way to report an error to a caller is to return an
+<code>os.Error</code> as an extra return value.  The canonical
+<code>Read</code> method is a well-known instance; it returns a byte
+count and an <code>os.Error</code>.  But what if the error is
+unrecoverable?  Sometimes the program simply cannot continue.
+</p>
+
+<p>
+For this purpose, there is a built-in function <code>panic</code>
+that in effect creates a run-time error that will stop the program
+(but see the next section).  The function takes a single argument
+of arbitrary type—often a string—to be printed as the
+program dies.  It's also a way to indicate that something impossible has
+happened, such as exiting an infinite loop.  In fact, the compiler
+recognizes a <code>panic</code> at the end of a function and
+suppresses the usual check for a <code>return</code> statement.
+</p>
+
+
+<pre>
+// A toy implementation of cube root using Newton's method.
+func CubeRoot(x float64) float64 {
+    z := x/3   // Arbitrary initial value
+    for i := 0; i < 1e6; i++ {
+        prevz := z
+        z -= (z*z*z-x) / (3*z*z)
+        if veryClose(z, prevz) {
+            return z
+        }
+    }
+    // A million iterations has not converged; something is wrong.
+    panic(fmt.Sprintf("CubeRoot(%g) did not converge", x))
+}
+</pre>
+
+<p>
+This is only an example but real library functions should
+avoid <code>panic</code>.  If the problem can be masked or worked
+around, it's always better to let things continue to run rather
+than taking down the whole program.  One possible counterexample
+is during initialization: if the library truly cannot set itself up,
+it might be reasonable to panic, so to speak.
+</p>
+
+<pre>
+var user = os.Getenv("USER")
+
+func init() {
+    if user == "" {
+        panic("no value for $USER")
+    }
+}
+</pre>
+
+<h3 id="recover">Recover</h3>
+
+<p>
+When <code>panic</code> is called, including implicitly for run-time
+errors such as indexing an array out of bounds or failing a type
+assertion, it immediately stops execution of the current function
+and begins unwinding the stack of the goroutine, running any deferred
+functions along the way.  If that unwinding reaches the top of the
+goroutine's stack, the program dies.  However, it is possible to
+use the built-in function <code>recover</code> to regain control
+of the goroutine and resume normal execution.
+</p>
+
+<p>
+A call to <code>recover</code> stops the unwinding and returns the
+argument passed to <code>panic</code>.  Because the only code that
+runs while unwinding is inside deferred functions, <code>recover</code>
+is only useful inside deferred functions.
+</p>
+
+<p>
+One application of <code>recover</code> is to shut down a failing goroutine
+inside a server without killing the other executing goroutines.
+</p>
+
+<pre>
+func server(workChan <-chan *Work) {
+    for work := range workChan {
+        go safelyDo(work)
+    }
+}
+
+func safelyDo(work *Work) {
+    defer func() {
+        if err := recover(); err != nil {
+            log.Println("work failed:", err)
+        }
+    }()
+    do(work)
+}
+</pre>
+
+<p>
+In this example, if <code>do(work)</code> panics, the result will be
+logged and the goroutine will exit cleanly without disturbing the
+others.  There's no need to do anything else in the deferred closure;
+calling <code>recover</code> handles the condition completely.
+</p>
+
+<p>
+Because <code>recover</code> always returns <code>nil</code> unless called directly
+from a deferred function, deferred code can call library routines that themselves
+use <code>panic</code> and <code>recover</code> without failing.  As an example,
+the deferred function in <code>safelyDo</code> might call a logging function before
+calling <code>recover</code>, and that logging code would run unaffected
+by the panicking state.
+</p>
+
+<p>
+With our recovery pattern in place, the <code>do</code>
+function (and anything it calls) can get out of any bad situation
+cleanly by calling <code>panic</code>.  We can use that idea to
+simplify error handling in complex software.  Let's look at an
+idealized excerpt from the <code>regexp</code> package, which reports
+parsing errors by calling <code>panic</code> with a local
+<code>Error</code> type.  Here's the definition of <code>Error</code>,
+an <code>error</code> method, and the <code>Compile</code> function.
+</p>
+
+<pre>
+// Error is the type of a parse error; it satisfies os.Error.
+type Error string
+func (e Error) String() string {
+    return string(e)
+}
+
+// error is a method of *Regexp that reports parsing errors by
+// panicking with an Error.
+func (regexp *Regexp) error(err string) {
+    panic(Error(err))
+}
+
+// Compile returns a parsed representation of the regular expression.
+func Compile(str string) (regexp *Regexp, err os.Error) {
+    regexp = new(Regexp)
+    // doParse will panic if there is a parse error.
+    defer func() {
+        if e := recover(); e != nil {
+            regexp = nil    // Clear return value.
+            err = e.(Error) // Will re-panic if not a parse error.
+        }
+    }()
+    return regexp.doParse(str), nil
+}
+</pre>
+
+<p>
+If <code>doParse</code> panics, the recovery block will set the
+return value to <code>nil</code>—deferred functions can modify
+named return values.  It then will then check, in the assignment
+to <code>err</code>, that the problem was a parse error by asserting
+that it has type <code>Error</code>.
+If it does not, the type assertion will fail, causing a run-time error
+that continues the stack unwinding as though nothing had interrupted
+it.  This check means that if something unexpected happens, such
+as an array index out of bounds, the code will fail even though we
+are using <code>panic</code> and <code>recover</code> to handle
+user-triggered errors.
+</p>
+
+<p>
+With error handling in place, the <code>error</code> method
+makes it easy to report parse errors without worrying about unwinding
+the parse stack by hand.
+</p>
+
+<p>
+Useful though this pattern is, it should be used only within a package.
+<code>Parse</code> turns its internal <code>panic</code> calls into
+<code>os.Error</code> values; it does not expose <code>panics</code>
+to its client.  That is a good rule to follow.
+</p>
+
+<p>
+By the way, this re-panic idiom changes the panic value if an actual
+error occurs.  However, both the original and new failures will be
+presented in the crash report, so the root cause of the problem will
+still be visible.  Thus this simple re-panic approach is usually
+sufficient—it's a crash after all—but if you want to
+display only the original value, you can write a little more code to
+filter unexpected problems and re-panic with the original error.
+That's left as an exercise for the reader.
+</p>
+
+
+<h2 id="web_server">A web server</h2>
+
+<p>
+Let's finish with a complete Go program, a web server.
+This one is actually a kind of web re-server.
+Google provides a service at
+<a href="http://chart.apis.google.com">http://chart.apis.google.com</a>
+that does automatic formatting of data into charts and graphs.
+It's hard to use interactively, though,
+because you need to put the data into the URL as a query.
+The program here provides a nicer interface to one form of data: given a short piece of text,
+it calls on the chart server to produce a QR code, a matrix of boxes that encode the
+text.
+That image can be grabbed with your cell phone's camera and interpreted as,
+for instance, a URL, saving you typing the URL into the phone's tiny keyboard.
+</p>
+<p>
+Here's the complete program.
+An explanation follows.
+</p>
+{{code "progs/eff_qr.go"}}
+<p>
+The pieces up to <code>main</code> should be easy to follow.
+The one flag sets a default HTTP port for our server.  The template
+variable <code>templ</code> is where the fun happens. It builds an HTML template
+that will be executed by the server to display the page; more about
+that in a moment.
+</p>
+<p>
+The <code>main</code> function parses the flags and, using the mechanism
+we talked about above, binds the function <code>QR</code> to the root path
+for the server.  Then <code>http.ListenAndServe</code> is called to start the
+server; it blocks while the server runs.
+</p>
+<p>
+<code>QR</code> just receives the request, which contains form data, and
+executes the template on the data in the form value named <code>s</code>.
+</p>
+<p>
+The template package is powerful;
+this program just touches on its capabilities.
+In essence, it rewrites a piece of text on the fly by substituting elements derived
+from data items passed to <code>templ.Execute</code>, in this case the
+form value.  
+Within the template text (<code>templateStr</code>),
+double-brace-delimited pieces denote template actions.
+The piece from <code>{{html "{{if .}}"}}</code>
+to <code>{{html "{{end}}"}}</code> executes only if the value of the current data item, called <code>.</code> (dot),
+is non-empty.
+That is, when the string is empty, this piece of the template is suppressed.
+</p>
+<p>
+The snippet <code>{{html "{{urlquery .}}"}}</code> says to process the data with the function
+<code>urlquery</code>, which sanitizes the query string
+for safe display on the web page.
+</p>
+<p>
+The rest of the template string is just the HTML to show when the page loads.
+If this is too quick an explanation, see the <a href="/pkg/template/">documentation</a>
+for the template package for a more thorough discussion.
+</p>
+<p>
+And there you have it: a useful webserver in a few lines of code plus some
+data-driven HTML text.
+Go is powerful enough to make a lot happen in a few lines.
+</p>
+
+<!--
+TODO
+<pre>
+verifying implementation
+type Color uint32
+    
+// Check that Color implements image.Color and image.Image
+var _ image.Color = Black
+var _ image.Image = Black
+</pre>
+-->
+
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 82c7ed4..f46e2f8 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,5 +1,5 @@
 <!-- title The Go Programming Language Specification -->
-<!-- subtitle Version of July 14, 2011 -->
+<!-- subtitle Version of August 31, 2011 -->
 
 <!--
 TODO
@@ -5265,8 +5265,7 @@ The following minimal alignment properties are guaranteed:
 <h2 id="Implementation_differences">Implementation differences - TODO</h2>
 <ul>
 	<li><code>len(a)</code> is only a constant if <code>a</code> is a (qualified) identifier denoting an array or pointer to an array.</li>
-	<li><code>nil</code> maps are not treated like empty maps.</li>
-	<li>Trying to send/receive from a <code>nil</code> channel causes a run-time panic.</li>
-	<li><code>unsafe.Alignof</code>, <code>unsafe.Offsetof</code>, and <code>unsafe.Sizeof</code> return an <code>int</code>.</li>
+	<li>In gccgo, <code>nil</code> maps are not treated like empty maps.</li>
+	<li>In gccgo, trying to send/receive from a <code>nil</code> channel causes a run-time panic.</li>
 </ul>
 </span>
diff --git a/doc/install.html b/doc/install.html
index a1bc899..21a6636 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -331,11 +331,11 @@ there is another mailing list, <a href="http://groups.google.com/group/golang-ch
 that receives a message summarizing each checkin to the Go repository.
 </p>
 
-<h2 id="environment">Environment variables</h2>
+<h2 id="environment">Optional environment variables</h2>
 
 <p>
 The Go compilation environment can be customized by environment variables.
-None are required by the build, but you may wish to set them
+<i>None are required by the build</i>, but you may wish to set them
 to override the defaults.
 </p>
 
@@ -442,10 +442,9 @@ to override the defaults.
 </dt>
 <dd>
 	The ARM architecture version the run-time libraries should target.
-	ARMv6 cores have more efficient synchronization primitives. Setting
-	<code>$GOARM</code> to 5 will compile the run-time libraries using
-	just SWP instructions that work on older architectures as well.
-	Running v6 code on an older core will cause an illegal instruction trap.
+	Setting <code>$GOARM</code> to 5 causes the linker to emit calls
+	to a software floating point implementation instead of using
+	hardware floating point support.
 </dd>
 </dl>
 
@@ -469,6 +468,6 @@ something like this:
 
 <pre>
 export GOROOT=$HOME/go
-export GOARCH=386
+export GOARCH=amd64
 export GOOS=linux
 </pre>
diff --git a/doc/makehtml b/doc/makehtml
index 2418c68..69e8e2b 100755
--- a/doc/makehtml
+++ b/doc/makehtml
@@ -14,4 +14,4 @@ then
 	exit 1
 fi
 
-make && ./tmpltohtml $TMPL > $HTML
+make tmpltohtml && ./tmpltohtml $TMPL > $HTML
diff --git a/doc/play/playground.js b/doc/play/playground.js
new file mode 100644
index 0000000..ce9aa27
--- /dev/null
+++ b/doc/play/playground.js
@@ -0,0 +1,6 @@
+// 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.
+
+// A dummy playground.js file to silence godoc errors
+// when accessing doc/root.html.
diff --git a/doc/progs/eff_bytesize.go b/doc/progs/eff_bytesize.go
new file mode 100644
index 0000000..bcfde1a
--- /dev/null
+++ b/doc/progs/eff_bytesize.go
@@ -0,0 +1,47 @@
+// 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 main
+
+import "fmt"
+
+type ByteSize float64
+
+const (
+	_           = iota // ignore first value by assigning to blank identifier
+	KB ByteSize = 1 << (10 * iota)
+	MB
+	GB
+	TB
+	PB
+	EB
+	ZB
+	YB
+)
+
+func (b ByteSize) String() string {
+	switch {
+	case b >= YB:
+		return fmt.Sprintf("%.2fYB", float64(b/YB))
+	case b >= ZB:
+		return fmt.Sprintf("%.2fZB", float64(b/ZB))
+	case b >= EB:
+		return fmt.Sprintf("%.2fEB", float64(b/EB))
+	case b >= PB:
+		return fmt.Sprintf("%.2fPB", float64(b/PB))
+	case b >= TB:
+		return fmt.Sprintf("%.2fTB", float64(b/TB))
+	case b >= GB:
+		return fmt.Sprintf("%.2fGB", float64(b/GB))
+	case b >= MB:
+		return fmt.Sprintf("%.2fMB", float64(b/MB))
+	case b >= KB:
+		return fmt.Sprintf("%.2fKB", float64(b/KB))
+	}
+	return fmt.Sprintf("%.2fB", float64(b))
+}
+
+func main() {
+	fmt.Println(YB, ByteSize(1e13))
+}
diff --git a/doc/progs/eff_qr.go b/doc/progs/eff_qr.go
new file mode 100644
index 0000000..5d1fd38
--- /dev/null
+++ b/doc/progs/eff_qr.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+	"flag"
+	"http"
+	"log"
+	"template"
+)
+
+var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
+
+var templ = template.Must(template.New("qr").Parse(templateStr))
+
+func main() {
+	flag.Parse()
+	http.Handle("/", http.HandlerFunc(QR))
+	err := http.ListenAndServe(*addr, nil)
+	if err != nil {
+		log.Fatal("ListenAndServe:", err)
+	}
+}
+
+func QR(w http.ResponseWriter, req *http.Request) {
+	templ.Execute(w, req.FormValue("s"))
+}
+
+const templateStr = `
+<html>
+<head>
+<title>QR Link Generator</title>
+</head>
+<body>
+{{if .}}
+<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{urlquery .}}" />
+<br>
+{{html .}}
+<br>
+<br>
+{{end}}
+<form action="/" name=f method="GET"><input maxLength=1024 size=70
+name=s value="" title="Text to QR Encode"><input type=submit
+value="Show QR" name=qr>
+</form>
+</body>
+</html>
+`
diff --git a/doc/progs/eff_sequence.go b/doc/progs/eff_sequence.go
new file mode 100644
index 0000000..11c885a
--- /dev/null
+++ b/doc/progs/eff_sequence.go
@@ -0,0 +1,42 @@
+// 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 main
+
+import (
+	"fmt"
+	"sort"
+)
+
+func main() {
+	seq := Sequence{6, 2, -1, 44, 16}
+	sort.Sort(seq)
+	fmt.Println(seq)
+}
+
+type Sequence []int
+
+// Methods required by sort.Interface.
+func (s Sequence) Len() int {
+	return len(s)
+}
+func (s Sequence) Less(i, j int) bool {
+	return s[i] < s[j]
+}
+func (s Sequence) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+// Method for printing - sorts the elements before printing.
+func (s Sequence) String() string {
+	sort.Sort(s)
+	str := "["
+	for i, elem := range s {
+		if i > 0 {
+			str += " "
+		}
+		str += fmt.Sprint(elem)
+	}
+	return str + "]"
+}
diff --git a/doc/progs/file_windows.go b/doc/progs/file_windows.go
index 03003a3..bfbac75 100644
--- a/doc/progs/file_windows.go
+++ b/doc/progs/file_windows.go
@@ -15,7 +15,7 @@ type File struct {
 }
 
 func newFile(fd syscall.Handle, name string) *File {
-	if fd < 0 {
+	if fd == ^syscall.Handle(0) {
 		return nil
 	}
 	return &File{fd, name}
diff --git a/doc/progs/run b/doc/progs/run
index 81781c9..9d56049 100755
--- a/doc/progs/run
+++ b/doc/progs/run
@@ -15,7 +15,7 @@ fi
 rm -f *.$O
 
 if [ "$GOOS" = "windows" ];then
-	$GC -o file.8 file_windows.go
+	$GC -o file.$O file_windows.go
 else
 	$GC file.go
 fi
@@ -35,6 +35,9 @@ for i in \
 	sieve1.go \
 	server1.go \
 	strings.go \
+	eff_bytesize.go\
+	eff_qr.go \
+	eff_sequence.go\
 ; do
 	$GC $i
 done
@@ -82,4 +85,7 @@ testitpipe sieve "sed 10q" "2 3 5 7 11 13 17 19 23 29"
 $GC server.go
 testit server1 "" ""
 
+testit eff_bytesize "" "1.00YB 9.09TB"
+testit eff_sequence "" "[-1 2 6 16 44]"
+
 rm -f $O.out *.$O
diff --git a/doc/tmpltohtml.go b/doc/tmpltohtml.go
index f4d2e2c..4a8d026 100644
--- a/doc/tmpltohtml.go
+++ b/doc/tmpltohtml.go
@@ -21,7 +21,6 @@
 package main
 
 import (
-	"exp/template"
 	"flag"
 	"fmt"
 	"io/ioutil"
@@ -29,6 +28,7 @@ import (
 	"os"
 	"regexp"
 	"strings"
+	"template"
 )
 
 func Usage() {
diff --git a/include/libc.h b/include/libc.h
index 03e247f..f9ad963 100644
--- a/include/libc.h
+++ b/include/libc.h
@@ -112,6 +112,7 @@ extern	void	sysfatal(char*, ...);
 #define notejmp		p9notejmp
 #define jmp_buf		p9jmp_buf
 #define pow10		p9pow10
+#undef  strtod
 #define strtod		fmtstrtod
 #define charstod	fmtcharstod
 #endif
@@ -306,6 +307,9 @@ extern int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
 extern int fork(void);
 extern int pread(int fd, void *buf, int n, int off);
 extern int pwrite(int fd, void *buf, int n, int off);
+#undef  getwd
+#define getwd(s, ns) getcwd(s, ns)
+#undef  lseek
 #define lseek(fd, n, base) _lseeki64(fd, n, base)
 #define mkdir(path, perm) mkdir(path)
 #define pipe(fd) _pipe(fd, 512, O_BINARY)
diff --git a/include/mach.h b/include/mach.h
index 5b1ce7b..cf7151c 100644
--- a/include/mach.h
+++ b/include/mach.h
@@ -142,6 +142,7 @@ enum
 	FAMD64B,		/* 6.out bootable */
 	FPOWER64,		/* 9.out */
 	FPOWER64B,		/* 9.out bootable */
+	FWINPE,			/* windows PE executable */
 
 	ANONE = 0,		/* dissembler types */
 	AMIPS,
diff --git a/lib/godoc/search.html b/lib/godoc/search.html
index 36c34f5..9fdd6dd 100644
--- a/lib/godoc/search.html
+++ b/lib/godoc/search.html
@@ -26,7 +26,7 @@
 			{{range .Files}}
 				{{$src_html := srcLink .File.Path | html}}
 				{{range .Groups}}
-					{{range .Infos}}
+					{{range .}}
 						<a href="/{{$src_html}}?h={{$query_url}}#L{{infoLine .}}">{{$src_html}}:{{infoLine .}}</a>
 						{{infoSnippet_html .}}
 					{{end}}
@@ -46,10 +46,10 @@
 				{{range .Groups}}
 					<tr>
 					<td width="25"></td>
-					<th align="left" valign="top">{{infoKind_html .Kind}}</th>
+					<th align="left" valign="top">{{index . 0 | infoKind_html}}</th>
 					<td align="left" width="4"></td>
 					<td>
-					{{range .Infos}}
+					{{range .}}
 						<a href="/{{$src_html}}?h={{$query_url}}#L{{infoLine .}}">{{infoLine .}}</a>
 					{{end}}
 					</td>
diff --git a/misc/cgo/life/Makefile b/misc/cgo/life/Makefile
index 5a10380..39ec13b 100644
--- a/misc/cgo/life/Makefile
+++ b/misc/cgo/life/Makefile
@@ -11,6 +11,20 @@ CGOFILES=\
 
 CGO_OFILES=\
 	c-life.o\
+	
+ifeq ($(GOOS),windows)
+ifeq ($(GOARCH),amd64)
+CGO_OFILES+=\
+	lib64_libmingwex_a-wassert.o\
+	lib64_libmingw32_a-mingw_helpers.o\
+
+lib64_libmingwex_a-wassert.o:
+	ar -x /mingw/x86_64-w64-mingw32/lib/libmingwex.a lib64_libmingwex_a-wassert.o
+
+lib64_libmingw32_a-mingw_helpers.o:
+	ar -x /mingw/x86_64-w64-mingw32/lib/libmingw32.a  lib64_libmingw32_a-mingw_helpers.o
+endif
+endif
 
 CLEANFILES+=life
 
diff --git a/misc/cgo/test/Makefile b/misc/cgo/test/Makefile
index d4309be..5617e78 100644
--- a/misc/cgo/test/Makefile
+++ b/misc/cgo/test/Makefile
@@ -12,6 +12,7 @@ CGOFILES=\
 	callback.go\
 	env.go\
 	exports.go\
+	helpers.go\
 	issue1222.go\
 	issue1328.go\
 	issue1560.go\
diff --git a/misc/cgo/test/basic.go b/misc/cgo/test/basic.go
index b9d0953..626e0e9 100644
--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -48,6 +48,10 @@ struct ibv_async_event {
 struct ibv_context {
 	xxpthread_mutex_t mutex;
 };
+
+int add(int x, int y) {
+	return x+y;
+};
 */
 import "C"
 import (
@@ -132,3 +136,11 @@ var (
 type Context struct {
 	ctx *C.struct_ibv_context
 }
+
+func benchCgoCall(b *testing.B) {
+	const x = C.int(2)
+	const y = C.int(3)
+	for i := 0; i < b.N; i++ {
+		C.add(x, y)
+	}
+}
diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go
index 3edee97..d20790e 100644
--- a/misc/cgo/test/callback.go
+++ b/misc/cgo/test/callback.go
@@ -65,7 +65,7 @@ func testCallbackPanic(t *testing.T) {
 func testCallbackPanicLoop(t *testing.T) {
 	// Make sure we don't blow out m->g0 stack.
 	for i := 0; i < 100000; i++ {
-		TestCallbackPanic(t)
+		testCallbackPanic(t)
 	}
 }
 
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index 94fba15..34beee6 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -26,3 +26,6 @@ func TestBlocking(t *testing.T)            { testBlocking(t) }
 func Test1328(t *testing.T)                { test1328(t) }
 func TestParallelSleep(t *testing.T)       { testParallelSleep(t) }
 func TestSetEnv(t *testing.T)              { testSetEnv(t) }
+func TestHelpers(t *testing.T)             { testHelpers(t) }
+
+func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
diff --git a/misc/cgo/test/helpers.go b/misc/cgo/test/helpers.go
new file mode 100644
index 0000000..de14d19
--- /dev/null
+++ b/misc/cgo/test/helpers.go
@@ -0,0 +1,35 @@
+// 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 cgotest
+
+// const char *greeting = "hello, world";
+import "C"
+
+import (
+	"reflect"
+	"testing"
+	"unsafe"
+)
+
+const greeting = "hello, world"
+
+type testPair struct {
+	Name      string
+	Got, Want interface{}
+}
+
+var testPairs = []testPair{
+	{"GoString", C.GoString(C.greeting), greeting},
+	{"GoStringN", C.GoStringN(C.greeting, 5), greeting[:5]},
+	{"GoBytes", C.GoBytes(unsafe.Pointer(C.greeting), 5), []byte(greeting[:5])},
+}
+
+func testHelpers(t *testing.T) {
+	for _, pair := range testPairs {
+		if !reflect.DeepEqual(pair.Got, pair.Want) {
+			t.Errorf("%s: got %#v, want %#v", pair.Got, pair.Want)
+		}
+	}
+}
diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go
index 989965b..a547984 100644
--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -479,7 +479,7 @@ func commitPoll(key string) {
 		return
 	}
 
-	const N = 20 // how many revisions to grab
+	const N = 50 // how many revisions to grab
 
 	data, _, err := runLog(nil, "", goroot, "hg", "log",
 		"--encoding=utf-8",
@@ -507,12 +507,12 @@ func commitPoll(key string) {
 	// Non-empty parent has form 1234:hashhashhash; we want full hash.
 	for i := range logs {
 		l := &logs[i]
-		log.Printf("hg log: %s < %s\n", l.Hash, l.Parent)
 		if l.Parent == "" && i+1 < len(logs) {
 			l.Parent = logs[i+1].Hash
 		} else if l.Parent != "" {
 			l.Parent, _ = fullHash(l.Parent)
 		}
+		log.Printf("hg log: %s < %s\n", l.Hash, l.Parent)
 		if l.Parent == "" {
 			// Can't create node without parent.
 			continue
diff --git a/misc/dashboard/builder/package.go b/misc/dashboard/builder/package.go
index b2a83fa..ebf4dd3 100644
--- a/misc/dashboard/builder/package.go
+++ b/misc/dashboard/builder/package.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"fmt"
 	"go/doc"
 	"go/parser"
 	"go/token"
@@ -17,6 +18,10 @@ import (
 const MaxCommentLength = 500 // App Engine won't store more in a StringProperty.
 
 func (b *Builder) buildPackages(workpath string, hash string) os.Error {
+	logdir := filepath.Join(*buildroot, "log")
+	if err := os.Mkdir(logdir, 0755); err != nil {
+		return err
+	}
 	pkgs, err := packages()
 	if err != nil {
 		return err
@@ -44,9 +49,13 @@ func (b *Builder) buildPackages(workpath string, hash string) os.Error {
 		}
 
 		// get doc comment from package source
-		info, err := packageComment(p, filepath.Join(goroot, "src", "pkg", p))
-		if err != nil {
-			log.Printf("packageComment %v: %v", p, err)
+		var info string
+		pkgPath := filepath.Join(goroot, "src", "pkg", p)
+		if _, err := os.Stat(pkgPath); err == nil {
+			info, err = packageComment(p, pkgPath)
+			if err != nil {
+				log.Printf("packageComment %v: %v", p, err)
+			}
 		}
 
 		// update dashboard with build state + info
@@ -54,6 +63,19 @@ func (b *Builder) buildPackages(workpath string, hash string) os.Error {
 		if err != nil {
 			log.Printf("updatePackage %v: %v", p, err)
 		}
+
+		if code == 0 {
+			log.Println("Build succeeded:", p)
+		} else {
+			log.Println("Build failed:", p)
+			fn := filepath.Join(logdir, strings.Replace(p, "/", "_", -1))
+			if f, err := os.Create(fn); err != nil {
+				log.Printf("creating %s: %v", fn, err)
+			} else {
+				fmt.Fprint(f, buildLog)
+				f.Close()
+			}
+		}
 	}
 	return nil
 }
@@ -61,6 +83,7 @@ func (b *Builder) buildPackages(workpath string, hash string) os.Error {
 func isGoFile(fi *os.FileInfo) bool {
 	return fi.IsRegular() && // exclude directories
 		!strings.HasPrefix(fi.Name, ".") && // ignore .files
+		!strings.HasSuffix(fi.Name, "_test.go") && // ignore tests
 		filepath.Ext(fi.Name) == ".go"
 }
 
@@ -74,10 +97,13 @@ func packageComment(pkg, pkgpath string) (info string, err os.Error) {
 		if name == "main" {
 			continue
 		}
+		pdoc := doc.NewPackageDoc(pkgs[name], pkg)
+		if pdoc.Doc == "" {
+			continue
+		}
 		if info != "" {
-			return "", os.NewError("multiple non-main package docs")
+			return "", os.NewError("multiple packages with docs")
 		}
-		pdoc := doc.NewPackageDoc(pkgs[name], pkg)
 		info = pdoc.Doc
 	}
 	// grab only first paragraph
diff --git a/misc/dashboard/godashboard/app.yaml b/misc/dashboard/godashboard/app.yaml
index 7b77a85..215c163 100644
--- a/misc/dashboard/godashboard/app.yaml
+++ b/misc/dashboard/godashboard/app.yaml
@@ -1,5 +1,5 @@
 application: godashboard
-version: 7
+version: 8
 runtime: python
 api_version: 1
 
diff --git a/misc/dashboard/godashboard/gobuild.py b/misc/dashboard/godashboard/gobuild.py
index 685dc83..ae8d99b 100644
--- a/misc/dashboard/godashboard/gobuild.py
+++ b/misc/dashboard/godashboard/gobuild.py
@@ -32,6 +32,9 @@ import const
 # numbers in an hg repo. When inserting a new commit, the parent commit must be
 # given and this is used to generate the new commit number. In order to create
 # the first Commit object, a special command (/init) is used.
+#
+# N.B. user is a StringProperty, so it must be type 'unicode'.
+# desc is a BlobProperty, so it must be type 'string'.  [sic]
 class Commit(db.Model):
     num = db.IntegerProperty() # internal, monotonic counter.
     node = db.StringProperty() # Hg hash
@@ -199,7 +202,7 @@ class Init(DashboardHandler):
         commit.num = 0
         commit.node = node
         commit.parentnode = ''
-        commit.user = self.request.get('user').encode('utf8')
+        commit.user = self.request.get('user')
         commit.date = date
         commit.desc = self.request.get('desc').encode('utf8')
 
@@ -233,7 +236,7 @@ class CommitHandler(DashboardHandler):
 
         node = self.request.get('node')
         date = parseDate(self.request.get('date'))
-        user = self.request.get('user').encode('utf8')
+        user = self.request.get('user')
         desc = self.request.get('desc').encode('utf8')
         parenthash = self.request.get('parent')
 
@@ -294,7 +297,7 @@ class Build(webapp.RequestHandler):
             return
 
         builder = self.request.get('builder')
-        log = self.request.get('log').encode('utf-8')
+        log = self.request.get('log').encode('utf8')
 
         loghash = ''
         if len(log) > 0:
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py
index 5cc2d24..7feac82 100644
--- a/misc/dashboard/godashboard/package.py
+++ b/misc/dashboard/godashboard/package.py
@@ -211,7 +211,7 @@ class PackagePage(webapp.RequestHandler):
             q = Package.all()
             s = '{"packages": ['
             sep = ''
-            for r in q.fetch(1000):
+            for r in q:
                 s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count)
                 sep = ','
             s += '\n]}\n'
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index ba7f723..1c90af8 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -7,7 +7,6 @@
 ;;; To do:
 
 ;; * Indentation is *almost* identical to gofmt
-;; ** We think struct literal keys are labels and outdent them
 ;; ** We disagree on the indentation of function literals in arguments
 ;; ** There are bugs with the close brace of struct literals
 ;; * Highlight identifiers according to their syntactic context: type,
@@ -401,7 +400,8 @@ indented one level."
                 (setq first nil))))
 
           ;; case, default, and labels are outdented 1 level
-          (when (looking-at "\\<case\\>\\|\\<default\\>\\|\\w+\\s *:\\(\\S.\\|$\\)")
+          ;; assume that labels are alone on the line
+          (when (looking-at "\\<case\\>\\|\\<default\\>\\|\\w+\\s *:\\s *$")
             (decf indent tab-width))
 
           ;; Continuation lines are indented 1 level
diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go
index bbc388b..a1eb617 100644
--- a/misc/goplay/goplay.go
+++ b/misc/goplay/goplay.go
@@ -145,7 +145,7 @@ func run(cmd ...string) ([]byte, os.Error) {
 var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template
 var output = template.Must(template.New("output").Parse(outputText))          // HTML template
 
-var outputText = `<pre>{{html .}}</pre>`
+var outputText = `<pre>{{printf "%s" . |html}}</pre>`
 
 var frontPageText = `<!doctype html>
 <html>
@@ -256,7 +256,7 @@ function compileUpdate() {
 </head>
 <body>
 <table width="100%"><tr><td width="60%" valign="top">
-<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">{{html .}}</textarea>
+<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">{{printf "%s" . |html}}</textarea>
 <div class="hints">
 (Shift-Enter to compile and run.)    
 <input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke
diff --git a/src/Make.inc b/src/Make.inc
index 8f549f6..c770b76 100644
--- a/src/Make.inc
+++ b/src/Make.inc
@@ -33,6 +33,7 @@ GOOS_LIST=\
 	darwin\
 	freebsd\
 	linux\
+	openbsd\
 	plan9\
 	windows\
 
@@ -117,7 +118,10 @@ HOST_AR?=ar
 HOST_EXTRA_CFLAGS?=-ggdb -O2
 HOST_EXTRA_LDFLAGS?=
 
-HOST_CFLAGS=-I"$(GOROOT)/include" $(HOST_EXTRA_CFLAGS)
+# The -fno-common here is not necessary, but some compilers
+# on OS X seem to set it by default.  Setting it here keeps the build
+# working in that non-standard context.
+HOST_CFLAGS=-fno-common -I"$(GOROOT)/include" $(HOST_EXTRA_CFLAGS)
 HOST_LDFLAGS=$(HOST_EXTRA_LDFLAGS)
 PWD=$(shell pwd)
 
diff --git a/src/Make.pkg b/src/Make.pkg
index fc80cf6..ad7d10b 100644
--- a/src/Make.pkg
+++ b/src/Make.pkg
@@ -83,10 +83,10 @@ $(TARGDIR)/$(TARG).a: _obj/$(TARG).a
 	cp _obj/$(TARG).a "$@"
 
 _go_.$O: $(GOFILES) $(PREREQ)
-	$(GC) $(GCIMPORTS) -o $@ $(GOFILES)
+	$(GC) $(GCIMPORTS) -p $(TARG) -o $@ $(GOFILES)
 
 _gotest_.$O: $(GOFILES) $(GOTESTFILES) $(PREREQ)
-	$(GC) $(GCIMPORTS) -o $@ $(GOFILES) $(GOTESTFILES)
+	$(GC) $(GCIMPORTS) -p $(TARG) -o $@ $(GOFILES) $(GOTESTFILES)
 
 _obj/$(TARG).a: _go_.$O $(OFILES)
 	@mkdir -p _obj/$(dir)
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c
index ad7ed05..4bef021 100644
--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -44,7 +44,11 @@ enum
 int
 systemtype(int sys)
 {
+#ifdef _WIN32
+	return sys&Windows;
+#else
 	return sys&Plan9;
+#endif
 }
 
 void
@@ -643,17 +647,13 @@ outhist(void)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h
index ff6d519..5349114 100644
--- a/src/cmd/5c/gc.h
+++ b/src/cmd/5c/gc.h
@@ -181,7 +181,7 @@ EXTERN	int32	maxargsafe;
 EXTERN	int	mnstring;
 EXTERN	Multab	multab[20];
 EXTERN	int	retok;
-EXTERN	int	hintabsize;
+extern	int	hintabsize;
 EXTERN	Node*	nodrat;
 EXTERN	Node*	nodret;
 EXTERN	Node*	nodsafe;
diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c
index 7cbaadb..3203253 100644
--- a/src/cmd/5c/swt.c
+++ b/src/cmd/5c/swt.c
@@ -460,17 +460,13 @@ outhist(Biobuf *b)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
index 6e2fbe2..0ea8695 100644
--- a/src/cmd/5g/cgen.c
+++ b/src/cmd/5g/cgen.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 /*
@@ -850,6 +852,7 @@ bgen(Node *n, int true, Prog *to)
 	Node n1, n2, n3, n4, tmp;
 	Prog *p1, *p2;
 
+	USED(n4);			// in unreachable code below
 	if(debug['g']) {
 		dump("\nbgen", n);
 	}
@@ -860,9 +863,6 @@ bgen(Node *n, int true, Prog *to)
 	if(n->ninit != nil)
 		genlist(n->ninit);
 
-	nl = n->left;
-	nr = n->right;
-
 	if(n->type == T) {
 		convlit(&n, types[TBOOL]);
 		if(n->type == T)
@@ -875,7 +875,6 @@ bgen(Node *n, int true, Prog *to)
 		patch(gins(AEND, N, N), to);
 		goto ret;
 	}
-	nl = N;
 	nr = N;
 
 	switch(n->op) {
@@ -984,6 +983,7 @@ bgen(Node *n, int true, Prog *to)
 			regfree(&n1);
 			break;
 
+#ifdef	NOTDEF
 			a = optoas(a, types[tptr]);
 			regalloc(&n1, types[tptr], N);
 			regalloc(&n3, types[tptr], N);
@@ -1001,6 +1001,7 @@ bgen(Node *n, int true, Prog *to)
 			regfree(&n3);
 			regfree(&n1);
 			break;
+#endif
 		}
 
 		if(isinter(nl->type)) {
@@ -1019,6 +1020,7 @@ bgen(Node *n, int true, Prog *to)
 			regfree(&n1);
 			break;
 
+#ifdef	NOTDEF
 			a = optoas(a, types[tptr]);
 			regalloc(&n1, types[tptr], N);
 			regalloc(&n3, types[tptr], N);
@@ -1036,6 +1038,7 @@ bgen(Node *n, int true, Prog *to)
 			regfree(&n3);
 			regfree(&n4);
 			break;
+#endif
 		}
 
 		if(iscomplex[nl->type->etype]) {
@@ -1198,8 +1201,6 @@ sgen(Node *n, Node *res, int32 w)
 		dump("r", n);
 		dump("res", res);
 	}
-	if(w == 0)
-		return;
 	if(w < 0)
 		fatal("sgen copy %d", w);
 	if(n->ullman >= UINF && res->ullman >= UINF)
@@ -1207,12 +1208,20 @@ sgen(Node *n, Node *res, int32 w)
 	if(n->type == T)
 		fatal("sgen: missing type");
 
+	if(w == 0) {
+		// evaluate side effects only.
+		regalloc(&dst, types[tptr], N);
+		agen(res, &dst);
+		agen(n, &dst);
+		regfree(&dst);
+		return;
+	}
+
 	// determine alignment.
 	// want to avoid unaligned access, so have to use
 	// smaller operations for less aligned types.
 	// for example moving [4]byte must use 4 MOVB not 1 MOVW.
 	align = n->type->align;
-	op = 0;
 	switch(align) {
 	default:
 		fatal("sgen: invalid alignment %d for %T", align, n->type);
@@ -1313,7 +1322,6 @@ sgen(Node *n, Node *res, int32 w)
 			p->from.type = D_OREG;
 			p->from.offset = dir;
 			p->scond |= C_PBIT;
-			ploop = p;
 	
 			p = gins(op, &tmp, &dst);
 			p->to.type = D_OREG;
diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c
index b56df76..1235d1a 100644
--- a/src/cmd/5g/cgen64.c
+++ b/src/cmd/5g/cgen64.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 /*
@@ -240,7 +242,7 @@ cgen64(Node *n, Node *res)
 			// shift is >= 1<<32
 			split64(r, &cl, &ch);
 			gmove(&ch, &s);
-			p1 = gins(ATST, &s, N);
+			gins(ATST, &s, N);
 			p6 = gbranch(ABNE, T);
 			gmove(&cl, &s);
 			splitclean();
@@ -248,7 +250,7 @@ cgen64(Node *n, Node *res)
 			gmove(r, &s);
 			p6 = P;
 		}
-		p1 = gins(ATST, &s, N);
+		gins(ATST, &s, N);
 
 		// shift == 0
 		p1 = gins(AMOVW, &bl, &al);
@@ -411,7 +413,7 @@ olsh_break:
 			gmove(r, &s);
 			p6 = P;
 		}
-		p1 = gins(ATST, &s, N);
+		gins(ATST, &s, N);
 
 		// shift == 0
 		p1 = gins(AMOVW, &bl, &al);
@@ -453,9 +455,9 @@ olsh_break:
 		p1 = gins(AMOVW, &bh, &al);
 		p1->scond = C_SCOND_EQ;
 		if(bh.type->etype == TINT32)
-			p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+			gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
 		else
-			p1 = gins(AEOR, &ah, &ah);
+			gins(AEOR, &ah, &ah);
 		p4 = gbranch(ABEQ, T);
 
 		// check if shift is < 64
diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c
index 1276610..0708042 100644
--- a/src/cmd/5g/galign.c
+++ b/src/cmd/5g/galign.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 int	thechar	= '5';
diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h
index ce4558e..c826d26 100644
--- a/src/cmd/5g/gg.h
+++ b/src/cmd/5g/gg.h
@@ -2,16 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include <u.h>
-#include <libc.h>
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
 
 #include "../gc/go.h"
 #include "../5l/5.out.h"
 
-#ifndef	EXTERN
-#define EXTERN	extern
-#endif
-
 typedef	struct	Addr	Addr;
 
 struct	Addr
@@ -51,22 +48,19 @@ struct	Prog
 #define REGALLOC_F0 (REGALLOC_RMAX+1)
 #define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)
 
-EXTERN	Biobuf*	bout;
 EXTERN	int32	dynloc;
 EXTERN	uchar	reg[REGALLOC_FMAX+1];
 EXTERN	int32	pcloc;		// instruction counter
 EXTERN	Strlit	emptystring;
 extern	char*	anames[];
-EXTERN	Hist*	hist;
 EXTERN	Prog	zprog;
-EXTERN	Node*	curfn;
 EXTERN	Node*	newproc;
 EXTERN	Node*	deferproc;
 EXTERN	Node*	deferreturn;
 EXTERN	Node*	panicindex;
 EXTERN	Node*	panicslice;
 EXTERN	Node*	throwreturn;
-EXTERN	long	unmappedzero;
+extern	long	unmappedzero;
 EXTERN	int	maxstksize;
 
 /*
@@ -169,3 +163,6 @@ int	Yconv(Fmt*);
 void	listinit(void);
 
 void	zaddr(Biobuf*, Addr*, int);
+
+#pragma	varargck	type	"D"	Addr*
+#pragma	varargck	type	"M"	Addr*
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index 3f5f47e..3f38318 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -4,6 +4,8 @@
 
 #undef	EXTERN
 #define	EXTERN
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -544,7 +546,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res)
 	}
 
 	// test for shift being 0
-	p1 = gins(ATST, &n1, N);
+	gins(ATST, &n1, N);
 	p3 = gbranch(ABEQ, T);
 
 	// test and fix up large shifts
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
index 27c8be6..9f728de 100644
--- a/src/cmd/5g/gobj.c
+++ b/src/cmd/5g/gobj.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 void
@@ -266,54 +268,6 @@ dumpfuncs(void)
 	}
 }
 
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
-	gflag = debug['g'];
-	debug['g'] = 0;
-
-	if(estrdat == nil) {
-		strdat = mal(sizeof(*pc));
-		clearp(strdat);
-		estrdat = strdat;
-	}
-	if(savepc)
-		fatal("data phase error");
-	savepc = pc;
-	pc = estrdat;
-}
-
-void
-text(void)
-{
-	if(!savepc)
-		fatal("text phase error");
-	debug['g'] = gflag;
-	estrdat = pc;
-	pc = savepc;
-	savepc = nil;
-}
-
-void
-dumpdata(void)
-{
-	Prog *p;
-
-	if(estrdat == nil)
-		return;
-	*pc = *strdat;
-	if(gflag)
-		for(p=pc; p!=estrdat; p=p->link)
-			print("%P\n", p);
-	pc = estrdat;
-}
-
 int
 dsname(Sym *sym, int off, char *t, int n)
 {
@@ -379,6 +333,17 @@ gdata(Node *nam, Node *nr, int wid)
 	Prog *p;
 	vlong v;
 
+	if(nr->op == OLITERAL) {
+		switch(nr->val.ctype) {
+		case CTCPLX:
+			gdatacomplex(nam, nr->val.u.cval);
+			return;
+		case CTSTR:
+			gdatastring(nam, nr->val.u.sval);
+			return;
+		}
+	}
+
 	if(wid == 8 && is64(nr->type)) {
 		v = mpgetfix(nr->val.u.xval);
 		p = gins(ADATA, nam, nodintconst(v));
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index ddaf52a..f8920df 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 // TODO(kaib): Can make this bigger if we move
@@ -50,6 +52,10 @@ clearp(Prog *p)
 	pcloc++;
 }
 
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
 /*
  * generate and return proc with p->as = as,
  * linked into program.  pc is next instruction.
@@ -59,10 +65,22 @@ prog(int as)
 {
 	Prog *p;
 
-	p = pc;
-	pc = mal(sizeof(*pc));
-
-	clearp(pc);
+	if(as == ADATA || as == AGLOBL) {
+		if(ddumped)
+			fatal("already dumped data");
+		if(dpc == nil) {
+			dpc = mal(sizeof(*dpc));
+			dfirst = dpc;
+		}
+		p = dpc;
+		dpc = mal(sizeof(*dpc));
+		p->link = dpc;
+	} else {
+		p = pc;
+		pc = mal(sizeof(*pc));
+		clearp(pc);
+		p->link = pc;
+	}
 
 	if(lineno == 0) {
 		if(debug['K'])
@@ -71,10 +89,21 @@ prog(int as)
 
 	p->as = as;
 	p->lineno = lineno;
-	p->link = pc;
 	return p;
 }
 
+void
+dumpdata(void)
+{
+	ddumped = 1;
+	if(dfirst == nil)
+		return;
+	newplist();
+	*pc = *dfirst;
+	pc = dpc;
+	clearp(pc);
+}
+
 /*
  * generate a branch.
  * t is ignored.
@@ -84,6 +113,8 @@ gbranch(int as, Type *t)
 {
 	Prog *p;
 
+	USED(t);
+
 	p = prog(as);
 	p->to.type = D_BRANCH;
 	p->to.branch = P;
@@ -1242,7 +1273,6 @@ naddr(Node *n, Addr *a, int canemitcode)
 			a->etype = simtype[n->type->etype];
 			a->width = n->type->width;
 		}
-		a->pun = n->pun;
 		a->offset = n->xoffset;
 		a->sym = n->sym;
 		if(a->sym == S)
diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c
index 0c6dbbf..fef9c85 100644
--- a/src/cmd/5g/list.c
+++ b/src/cmd/5g/list.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 // TODO(kaib): make 5g/list.c congruent with 5l/list.c
diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c
index 6cc93db..e87f5d6 100644
--- a/src/cmd/5g/peep.c
+++ b/src/cmd/5g/peep.c
@@ -29,6 +29,8 @@
 // THE SOFTWARE.
 
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -45,6 +47,9 @@ peep(void)
 	Reg *r, *r1, *r2;
 	Prog *p, *p1;
 	int t;
+
+	p1 = nil;
+	USED(p1);		// ... in unreachable code...
 /*
  * complete R structure
  */
@@ -115,12 +120,14 @@ loop1:
 			}
 			break;
 
+#ifdef	NOTDEF
 			if(p->scond == C_SCOND_NONE)
 			if(regtyp(&p->to))
 			if(isdconst(&p->from)) {
 				constprop(&p->from, &p->to, r->s1);
 			}
 			break;
+#endif
 		}
 	}
 	if(t)
@@ -128,6 +135,7 @@ loop1:
 
 return;
 
+#ifdef	NOTDEF
 	for(r=firstr; r!=R; r=r->link) {
 		p = r->prog;
 		switch(p->as) {
@@ -255,6 +263,7 @@ return;
 //	}
 
 	predicate();
+#endif
 }
 
 Reg*
@@ -1159,7 +1168,6 @@ copyu(Prog *p, Adr *v, Adr *s)
 				return 3;
 		return 0;
 	}
-	return 0;
 }
 
 /*
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index 2d2a6d0..9dd3f07 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -29,6 +29,8 @@
 // THE SOFTWARE.
 
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -112,19 +114,19 @@ setaddrs(Bits bit)
 {
 	int i, n;
 	Var *v;
-	Sym *s;
+	Node *node;
 
 	while(bany(&bit)) {
 		// convert each bit to a variable
 		i = bnum(bit);
-		s = var[i].sym;
+		node = var[i].node;
 		n = var[i].name;
 		bit.b[i/32] &= ~(1L<<(i%32));
 
 		// disable all pieces of that variable
 		for(i=0; i<nvar; i++) {
 			v = var+i;
-			if(v->sym == s && v->name == n)
+			if(v->node == node && v->name == n)
 				v->addr = 2;
 		}
 	}
@@ -202,7 +204,7 @@ regopt(Prog *firstp)
 	nvar = NREGVAR;
 	memset(var, 0, NREGVAR*sizeof var[0]);
 	for(i=0; i<NREGVAR; i++)
-		var[i].sym = lookup(regname[i]);
+		var[i].node = newname(lookup(regname[i]));
 
 	regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC);
 	for(z=0; z<BITS; z++) {
@@ -750,9 +752,9 @@ addmove(Reg *r, int bn, int rn, int f)
 	v = var + bn;
 
 	a = &p1->to;
-	a->sym = v->sym;
 	a->name = v->name;
 	a->node = v->node;
+	a->sym = v->node->sym;
 	a->offset = v->offset;
 	a->etype = v->etype;
 	a->type = D_OREG;
@@ -838,11 +840,10 @@ mkvar(Reg *r, Adr *a)
 	int i, t, n, et, z, w, flag;
 	int32 o;
 	Bits bit;
-	Sym *s;
+	Node *node;
 
 	// mark registers used
 	t = a->type;
-	n = D_NONE;
 
 	flag = 0;
 	switch(t) {
@@ -909,10 +910,11 @@ mkvar(Reg *r, Adr *a)
 		break;
 	}
 
-	s = a->sym;
-	if(s == S)
+	node = a->node;
+	if(node == N || node->op != ONAME || node->orig != N)
 		goto none;
-	if(s->name[0] == '.')
+	node = node->orig;
+	if(node->sym->name[0] == '.')
 		goto none;
 	et = a->etype;
 	o = a->offset;
@@ -920,7 +922,7 @@ mkvar(Reg *r, Adr *a)
 
 	for(i=0; i<nvar; i++) {
 		v = var+i;
-		if(v->sym == s && v->name == n) {
+		if(v->node == node && v->name == n) {
 			if(v->offset == o)
 			if(v->etype == et)
 			if(v->width == w)
@@ -944,7 +946,7 @@ mkvar(Reg *r, Adr *a)
 	}
 
 	if(nvar >= NVAR) {
-		if(debug['w'] > 1 && s)
+		if(debug['w'] > 1 && node)
 			fatal("variable not optimized: %D", a);
 		goto none;
 	}
@@ -953,17 +955,16 @@ mkvar(Reg *r, Adr *a)
 	nvar++;
 //print("var %d %E %D %S\n", i, et, a, s);
 	v = var+i;
-	v->sym = s;
 	v->offset = o;
 	v->name = n;
 //	v->gotype = a->gotype;
 	v->etype = et;
 	v->width = w;
 	v->addr = flag;		// funny punning
-	v->node = a->node;
+	v->node = node;
 	
 	if(debug['R'])
-		print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr);
+		print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
 
 	bit = blsh(i);
 	if(n == D_EXTERN || n == D_STATIC)
diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c
index 4f799d1..4011071 100644
--- a/src/cmd/5l/softfloat.c
+++ b/src/cmd/5l/softfloat.c
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#define	EXTERN
 #include	"l.h"
 #include	"../ld/lib.h"
 
diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c
index 42f4b1d..1a8e5ad 100644
--- a/src/cmd/6a/lex.c
+++ b/src/cmd/6a/lex.c
@@ -44,7 +44,11 @@ enum
 int
 systemtype(int sys)
 {
+#ifdef _WIN32
+	return sys&Windows;
+#else
 	return sys&Plan9;
+#endif
 }
 
 int
@@ -456,8 +460,8 @@ struct
 	"JGT",		LTYPER,	AJGT,
 	"JG",		LTYPER,	AJGT,	/* alternate */
 	"JNLE",		LTYPER,	AJGT,	/* alternate */
-
-	"JCXZ",		LTYPER,	AJCXZ,
+	"JCXZL",	LTYPER,	AJCXZL,
+	"JCXZQ",	LTYPER,	AJCXZQ,
 	"JMP",		LTYPEC,	AJMP,
 	"LAHF",		LTYPE0,	ALAHF,
 	"LARL",		LTYPE3,	ALARL,
@@ -1251,17 +1255,13 @@ outhist(void)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c
index d7a9170..3de8630 100644
--- a/src/cmd/6c/swt.c
+++ b/src/cmd/6c/swt.c
@@ -331,17 +331,13 @@ outhist(Biobuf *b)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c
index 24f88a4..43bec00 100644
--- a/src/cmd/6g/cgen.c
+++ b/src/cmd/6g/cgen.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 /*
@@ -727,9 +729,6 @@ bgen(Node *n, int true, Prog *to)
 	if(n->ninit != nil)
 		genlist(n->ninit);
 
-	nl = n->left;
-	nr = n->right;
-
 	if(n->type == T) {
 		convlit(&n, types[TBOOL]);
 		if(n->type == T)
@@ -742,7 +741,6 @@ bgen(Node *n, int true, Prog *to)
 		patch(gins(AEND, N, N), to);
 		goto ret;
 	}
-	nl = N;
 	nr = N;
 
 	switch(n->op) {
@@ -1031,11 +1029,9 @@ sgen(Node *n, Node *ns, int32 w)
 		dump("r", n);
 		dump("res", ns);
 	}
-	if(w == 0)
-		return;
-	if(n->ullman >= UINF && ns->ullman >= UINF) {
+
+	if(n->ullman >= UINF && ns->ullman >= UINF)
 		fatal("sgen UINF");
-	}
 
 	if(w < 0)
 		fatal("sgen copy %d", w);
@@ -1043,6 +1039,15 @@ sgen(Node *n, Node *ns, int32 w)
 	if(w == 16)
 		if(componentgen(n, ns))
 			return;
+	
+	if(w == 0) {
+		// evaluate side effects only
+		regalloc(&nodr, types[tptr], N);
+		agen(ns, &nodr);
+		agen(n, &nodr);
+		regfree(&nodr);
+		return;
+	}
 
 	// offset on the stack
 	osrc = stkof(n);
diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c
index e366362..b03ac1e 100644
--- a/src/cmd/6g/galign.c
+++ b/src/cmd/6g/galign.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 int	thechar	= '6';
diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h
index 2493771..8a80ee9 100644
--- a/src/cmd/6g/gg.h
+++ b/src/cmd/6g/gg.h
@@ -2,16 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include <u.h>
-#include <libc.h>
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
 
 #include "../gc/go.h"
 #include "../6l/6.out.h"
 
-#ifndef	EXTERN
-#define EXTERN	extern
-#endif
-
 typedef	struct	Addr	Addr;
 
 struct	Addr
@@ -44,22 +41,19 @@ struct	Prog
 	void*	reg;		// pointer to containing Reg struct
 };
 
-EXTERN	Biobuf*	bout;
 EXTERN	int32	dynloc;
 EXTERN	uchar	reg[D_NONE];
 EXTERN	int32	pcloc;		// instruction counter
 EXTERN	Strlit	emptystring;
 extern	char*	anames[];
-EXTERN	Hist*	hist;
 EXTERN	Prog	zprog;
-EXTERN	Node*	curfn;
 EXTERN	Node*	newproc;
 EXTERN	Node*	deferproc;
 EXTERN	Node*	deferreturn;
 EXTERN	Node*	panicindex;
 EXTERN	Node*	panicslice;
 EXTERN	Node*	throwreturn;
-EXTERN	vlong	unmappedzero;
+extern	vlong	unmappedzero;
 
 /*
  * gen.c
@@ -159,3 +153,5 @@ void	listinit(void);
 
 void	zaddr(Biobuf*, Addr*, int, int);
 
+#pragma	varargck	type	"D"	Addr*
+#pragma	varargck	type	"lD"	Addr*
diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c
index a5f2783..2f68885 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -4,6 +4,8 @@
 
 #undef	EXTERN
 #define	EXTERN
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -446,8 +448,8 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
 {
 	int a, check;
 	Node n3, n4, n5;
-	Type *t;
-	Node ax, dx, oldax, olddx;
+	Type *t, *t0;
+	Node ax, dx, ax1, n31, oldax, olddx;
 	Prog *p1, *p2, *p3;
 
 	// Have to be careful about handling
@@ -459,6 +461,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
 	// For int32 and int64, use explicit test.
 	// Could use int64 hw for int32.
 	t = nl->type;
+	t0 = t;
 	check = 0;
 	if(issigned[t->etype]) {
 		check = 1;
@@ -476,18 +479,28 @@ dodiv(int op, Node *nl, Node *nr, Node *res)
 	}
 	a = optoas(op, t);
 
-	regalloc(&n3, t, N);
+	regalloc(&n3, t0, N);
 	if(nl->ullman >= nr->ullman) {
-		savex(D_AX, &ax, &oldax, res, t);
+		savex(D_AX, &ax, &oldax, res, t0);
 		cgen(nl, &ax);
-		regalloc(&ax, t, &ax);	// mark ax live during cgen
+		regalloc(&ax, t0, &ax);	// mark ax live during cgen
 		cgen(nr, &n3);
 		regfree(&ax);
 	} else {
 		cgen(nr, &n3);
-		savex(D_AX, &ax, &oldax, res, t);
+		savex(D_AX, &ax, &oldax, res, t0);
 		cgen(nl, &ax);
 	}
+	if(t != t0) {
+		// Convert
+		ax1 = ax;
+		n31 = n3;
+		ax.type = t;
+		n3.type = t;
+		gmove(&ax1, &ax);
+		gmove(&n31, &n3);
+	}
+
 	p3 = P;
 	if(check) {
 		nodconst(&n4, t, -1);
diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c
index ba8a487..4dcce39 100644
--- a/src/cmd/6g/gobj.c
+++ b/src/cmd/6g/gobj.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 void
@@ -278,54 +280,6 @@ dumpfuncs(void)
 	}
 }
 
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
-	gflag = debug['g'];
-	debug['g'] = 0;
-
-	if(estrdat == nil) {
-		strdat = mal(sizeof(*pc));
-		clearp(strdat);
-		estrdat = strdat;
-	}
-	if(savepc)
-		fatal("data phase error");
-	savepc = pc;
-	pc = estrdat;
-}
-
-void
-text(void)
-{
-	if(!savepc)
-		fatal("text phase error");
-	debug['g'] = gflag;
-	estrdat = pc;
-	pc = savepc;
-	savepc = nil;
-}
-
-void
-dumpdata(void)
-{
-	Prog *p;
-
-	if(estrdat == nil)
-		return;
-	*pc = *strdat;
-	if(gflag)
-		for(p=pc; p!=estrdat; p=p->link)
-			print("%P\n", p);
-	pc = estrdat;
-}
-
 int
 dsname(Sym *s, int off, char *t, int n)
 {
@@ -381,6 +335,16 @@ gdata(Node *nam, Node *nr, int wid)
 {
 	Prog *p;
 
+	if(nr->op == OLITERAL) {
+		switch(nr->val.ctype) {
+		case CTCPLX:
+			gdatacomplex(nam, nr->val.u.cval);
+			return;
+		case CTSTR:
+			gdatastring(nam, nr->val.u.sval);
+			return;
+		}
+	}
 	p = gins(ADATA, nam, nr);
 	p->from.scale = wid;
 }
@@ -537,6 +501,8 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
 	int c, d, o, mov, add, loaded;
 	Prog *p;
 	Type *f;
+	
+	USED(iface);
 
 	if(debug['r'])
 		print("genembedtramp %T %T %S\n", rcvr, method, newnam);
@@ -619,7 +585,6 @@ out:
 		// but 6l has a bug, and it can't handle
 		// JMP instructions too close to the top of
 		// a new function.
-		p = pc;
 		gins(ANOP, N, N);
 	}
 
diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c
index d0d6d0c..92b15ef 100644
--- a/src/cmd/6g/gsubr.c
+++ b/src/cmd/6g/gsubr.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 // TODO(rsc): Can make this bigger if we move
@@ -46,6 +48,10 @@ clearp(Prog *p)
 	pcloc++;
 }
 
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
 /*
  * generate and return proc with p->as = as,
  * linked into program. pc is next instruction.
@@ -55,10 +61,22 @@ prog(int as)
 {
 	Prog *p;
 
-	p = pc;
-	pc = mal(sizeof(*pc));
-
-	clearp(pc);
+	if(as == ADATA || as == AGLOBL) {
+		if(ddumped)
+			fatal("already dumped data");
+		if(dpc == nil) {
+			dpc = mal(sizeof(*dpc));
+			dfirst = dpc;
+		}
+		p = dpc;
+		dpc = mal(sizeof(*dpc));
+		p->link = dpc;
+	} else {
+		p = pc;
+		pc = mal(sizeof(*pc));
+		clearp(pc);
+		p->link = pc;
+	}
 
 	if(lineno == 0) {
 		if(debug['K'])
@@ -67,10 +85,21 @@ prog(int as)
 
 	p->as = as;
 	p->lineno = lineno;
-	p->link = pc;
 	return p;
 }
 
+void
+dumpdata(void)
+{
+	ddumped = 1;
+	if(dfirst == nil)
+		return;
+	newplist();
+	*pc = *dfirst;
+	pc = dpc;
+	clearp(pc);
+}
+
 /*
  * generate a branch.
  * t is ignored.
@@ -79,6 +108,8 @@ Prog*
 gbranch(int as, Type *t)
 {
 	Prog *p;
+	
+	USED(t);
 
 	p = prog(as);
 	p->to.type = D_BRANCH;
@@ -1098,7 +1129,6 @@ naddr(Node *n, Addr *a, int canemitcode)
 			a->width = n->type->width;
 			a->gotype = ngotype(n);
 		}
-		a->pun = n->pun;
 		a->offset = n->xoffset;
 		a->sym = n->sym;
 		if(a->sym == S)
diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c
index c8077c9..ad63f7d 100644
--- a/src/cmd/6g/list.c
+++ b/src/cmd/6g/list.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 static	int	sconsize;
@@ -131,7 +133,7 @@ Dconv(Fmt *fp)
 		if(fp->flags & FmtLong) {
 			d1 = a->offset & 0xffffffffLL;
 			d2 = (a->offset>>32) & 0xffffffffLL;
-			snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2);
+			snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2);
 			break;
 		}
 		snprint(str, sizeof(str), "$%lld", a->offset);
diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c
index 4432203..63ef3f7 100644
--- a/src/cmd/6g/peep.c
+++ b/src/cmd/6g/peep.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -991,7 +993,6 @@ loop:
 		if(p->from.dval == p0->from.dval)
 		if(p->from.index == p0->from.index) {
 			excise(r);
-			t++;
 			goto loop;
 		}
 		break;
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index 4d42630..f380ced 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -28,9 +28,9 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
-#undef	EXTERN
-#define	EXTERN
 #include "opt.h"
 
 #define	NREGVAR	32	/* 16 general + 16 floating */
@@ -98,19 +98,19 @@ setaddrs(Bits bit)
 {
 	int i, n;
 	Var *v;
-	Sym *s;
+	Node *node;
 
 	while(bany(&bit)) {
 		// convert each bit to a variable
 		i = bnum(bit);
-		s = var[i].sym;
+		node = var[i].node;
 		n = var[i].name;
 		bit.b[i/32] &= ~(1L<<(i%32));
 
 		// disable all pieces of that variable
 		for(i=0; i<nvar; i++) {
 			v = var+i;
-			if(v->sym == s && v->name == n)
+			if(v->node == node && v->name == n)
 				v->addr = 2;
 		}
 	}
@@ -179,7 +179,6 @@ regopt(Prog *firstp)
 	r1 = R;
 	firstr = R;
 	lastr = R;
-	nvar = 0;
 
 	/*
 	 * control flow is more complicated in generated go code
@@ -189,7 +188,7 @@ regopt(Prog *firstp)
 	nvar = NREGVAR;
 	memset(var, 0, NREGVAR*sizeof var[0]);
 	for(i=0; i<NREGVAR; i++)
-		var[i].sym = lookup(regname[i]);
+		var[i].node = newname(lookup(regname[i]));
 
 	regbits = RtoB(D_SP);
 	for(z=0; z<BITS; z++) {
@@ -832,12 +831,12 @@ addmove(Reg *r, int bn, int rn, int f)
 	v = var + bn;
 
 	a = &p1->to;
-	a->sym = v->sym;
 	a->offset = v->offset;
 	a->etype = v->etype;
 	a->type = v->name;
 	a->gotype = v->gotype;
 	a->node = v->node;
+	a->sym = v->node->sym;
 
 	// need to clean this up with wptr and
 	// some of the defaults
@@ -933,7 +932,7 @@ mkvar(Reg *r, Adr *a)
 	uint32 regu;
 	int32 o;
 	Bits bit;
-	Sym *s;
+	Node *node;
 
 	/*
 	 * mark registers used
@@ -969,10 +968,11 @@ mkvar(Reg *r, Adr *a)
 		n = t;
 		break;
 	}
-	s = a->sym;
-	if(s == S)
+	node = a->node;
+	if(node == N || node->op != ONAME || node->orig != N)
 		goto none;
-	if(s->name[0] == '.')
+	node = node->orig;
+	if(node->sym->name[0] == '.')
 		goto none;
 	et = a->etype;
 	o = a->offset;
@@ -981,7 +981,7 @@ mkvar(Reg *r, Adr *a)
 	flag = 0;
 	for(i=0; i<nvar; i++) {
 		v = var+i;
-		if(v->sym == s && v->name == n) {
+		if(v->node == node && v->name == n) {
 			if(v->offset == o)
 			if(v->etype == et)
 			if(v->width == w)
@@ -995,11 +995,6 @@ mkvar(Reg *r, Adr *a)
 			}
 		}
 	}
-	if(a->pun) {
-//		print("disable pun %s\n", s->name);
-		flag = 1;
-
-	}
 	switch(et) {
 	case 0:
 	case TFUNC:
@@ -1007,25 +1002,24 @@ mkvar(Reg *r, Adr *a)
 	}
 
 	if(nvar >= NVAR) {
-		if(debug['w'] > 1 && s)
-			fatal("variable not optimized: %D", a);
+		if(debug['w'] > 1 && node != N)
+			fatal("variable not optimized: %#N", node);
 		goto none;
 	}
 
 	i = nvar;
 	nvar++;
 	v = var+i;
-	v->sym = s;
 	v->offset = o;
 	v->name = n;
 	v->gotype = a->gotype;
 	v->etype = et;
 	v->width = w;
 	v->addr = flag;		// funny punning
-	v->node = a->node;
+	v->node = node;
 
 	if(debug['R'])
-		print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a);
+		print("bit=%2d et=%2d w=%d %#N %D\n", i, et, w, node, a);
 	ostats.nvar++;
 
 	bit = blsh(i);
diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h
index 262da02..9a69c8a 100644
--- a/src/cmd/6l/6.out.h
+++ b/src/cmd/6l/6.out.h
@@ -121,7 +121,7 @@ enum	as
 	AIRETW,
 	AJCC,
 	AJCS,
-	AJCXZ,
+	AJCXZL,
 	AJEQ,
 	AJGE,
 	AJGT,
@@ -487,6 +487,7 @@ enum	as
 	AIDIVQ,
 	AIMULQ,
 	AIRETQ,
+	AJCXZQ,
 	ALEAQ,
 	ALEAVEQ,
 	ALODSQ,
diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c
index 36806ec..0a4c0eb 100644
--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -788,7 +788,8 @@ Optab optab[] =
 	{ AIRETW,	ynone,	Pe, 0xcf },
 	{ AJCC,		yjcond,	Px, 0x73,0x83,(00) },
 	{ AJCS,		yjcond,	Px, 0x72,0x82 },
-	{ AJCXZ,	yloop,	Px, 0xe3 },
+	{ AJCXZL,	yloop,	Px, 0xe3 },
+	{ AJCXZQ,	yloop,	Px, 0xe3 },
 	{ AJEQ,		yjcond,	Px, 0x74,0x84 },
 	{ AJGE,		yjcond,	Px, 0x7d,0x8d },
 	{ AJGT,		yjcond,	Px, 0x7f,0x8f },
diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c
index 5d13ad4..9b869a4 100644
--- a/src/cmd/6l/span.c
+++ b/src/cmd/6l/span.c
@@ -88,7 +88,10 @@ span1(Sym *s)
 						loop++;
 						q->back ^= 2;
 					}
-					s->p[q->pc+1] = v;
+					if(q->as == AJCXZL)
+						s->p[q->pc+2] = v;
+					else
+						s->p[q->pc+1] = v;
 				} else {
 					bp = s->p + q->pc + q->mark - 4;
 					*bp++ = v;
@@ -1439,10 +1442,11 @@ found:
 
 	case Zbr:
 	case Zjmp:
+	case Zloop:
 		// TODO: jump across functions needs reloc
 		q = p->pcond;
 		if(q == nil) {
-			diag("jmp/branch without target");
+			diag("jmp/branch/loop without target");
 			errorexit();
 		}
 		if(q->as == ATEXT) {
@@ -1466,8 +1470,12 @@ found:
 		if(p->back & 1) {
 			v = q->pc - (p->pc + 2);
 			if(v >= -128) {
+				if(p->as == AJCXZL)
+					*andptr++ = 0x67;
 				*andptr++ = op;
 				*andptr++ = v;
+			} else if(t[2] == Zloop) {
+				diag("loop too far: %P", p);
 			} else {
 				v -= 5-2;
 				if(t[2] == Zbr) {
@@ -1487,8 +1495,12 @@ found:
 		p->forwd = q->comefrom;
 		q->comefrom = p;
 		if(p->back & 2)	{ // short
+			if(p->as == AJCXZL)
+				*andptr++ = 0x67;
 			*andptr++ = op;
 			*andptr++ = 0;
+		} else if(t[2] == Zloop) {
+			diag("loop too far: %P", p);
 		} else {
 			if(t[2] == Zbr)
 				*andptr++ = 0x0f;
@@ -1520,19 +1532,6 @@ found:
 */
 		break;
 
-	case Zloop:
-		q = p->pcond;
-		if(q == nil) {
-			diag("loop without target");
-			errorexit();
-		}
-		v = q->pc - p->pc - 2;
-		if(v < -128 && v > 127)
-			diag("loop too far: %P", p);
-		*andptr++ = op;
-		*andptr++ = v;
-		break;
-
 	case Zbyte:
 		v = vaddr(&p->from, &rel);
 		if(rel.siz != 0) {
diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y
index a8ac773..9697608 100644
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -210,6 +210,13 @@ spec3:	/* JMP/CALL */
 		$$.from = nullgen;
 		$$.to = $1;
 	}
+|	'*' nam
+	{
+		$$.from = nullgen;
+		$$.to = $2;
+		$$.to.index = $2.type;
+		$$.to.type = D_INDIR+D_ADDR;
+	}
 
 spec4:	/* NOP */
 	nonnon
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index e56460e..ca2e2c1 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -44,7 +44,11 @@ enum
 int
 systemtype(int sys)
 {
+#ifdef _WIN32
+	return sys&Windows;
+#else
 	return sys&Plan9;
+#endif
 }
 
 int
@@ -371,7 +375,8 @@ struct
 	"JG",		LTYPER,	AJGT,	/* alternate */
 	"JNLE",		LTYPER,	AJGT,	/* alternate */
 
-	"JCXZ",		LTYPER,	AJCXZ,
+	"JCXZL",	LTYPER,	AJCXZL,
+	"JCXZW",	LTYPER,	AJCXZW,
 	"JMP",		LTYPEC,	AJMP,
 	"LAHF",		LTYPE0,	ALAHF,
 	"LARL",		LTYPE3,	ALARL,
@@ -911,17 +916,13 @@ outhist(void)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c
index 769ef2c..006bfdf 100644
--- a/src/cmd/8c/swt.c
+++ b/src/cmd/8c/swt.c
@@ -330,17 +330,13 @@ outhist(Biobuf *b)
 	for(h = hist; h != H; h = h->link) {
 		p = h->name;
 		op = 0;
-		/* on windows skip drive specifier in pathname */
 		if(systemtype(Windows) && p && p[1] == ':'){
-			p += 2;
-			c = *p;
-		}
-		if(p && p[0] != c && h->offset == 0 && pathname){
-			/* on windows skip drive specifier in pathname */
+			c = p[2];
+		} else if(p && p[0] != c && h->offset == 0 && pathname){
 			if(systemtype(Windows) && pathname[1] == ':') {
 				op = p;
-				p = pathname+2;
-				c = *p;
+				p = pathname;
+				c = p[2];
 			} else if(pathname[0] == c){
 				op = p;
 				p = pathname;
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c
index b316e6e..21b7815 100644
--- a/src/cmd/8g/cgen.c
+++ b/src/cmd/8g/cgen.c
@@ -5,6 +5,8 @@
 // TODO(rsc):
 //	assume CLD?
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 void
@@ -680,7 +682,6 @@ agen(Node *n, Node *res)
 		break;
 
 	case ODOT:
-		t = nl->type;
 		agen(nl, res);
 		if(n->xoffset != 0) {
 			nodconst(&n1, types[tptr], n->xoffset);
@@ -798,9 +799,6 @@ bgen(Node *n, int true, Prog *to)
 	if(n->ninit != nil)
 		genlist(n->ninit);
 
-	nl = n->left;
-	nr = n->right;
-
 	if(n->type == T) {
 		convlit(&n, types[TBOOL]);
 		if(n->type == T)
@@ -813,7 +811,6 @@ bgen(Node *n, int true, Prog *to)
 		patch(gins(AEND, N, N), to);
 		return;
 	}
-	nl = N;
 	nr = N;
 
 	switch(n->op) {
@@ -1139,15 +1136,20 @@ sgen(Node *n, Node *res, int32 w)
 		dump("r", n);
 		dump("res", res);
 	}
-	if(w == 0)
-		return;
-	if(n->ullman >= UINF && res->ullman >= UINF) {
+	if(n->ullman >= UINF && res->ullman >= UINF)
 		fatal("sgen UINF");
-	}
 
 	if(w < 0)
 		fatal("sgen copy %d", w);
 
+	if(w == 0) {
+		// evaluate side effects only.
+		tempname(&tdst, types[tptr]);
+		agen(res, &tdst);
+		agen(n, &tdst);
+		return;
+	}
+
 	// offset on the stack
 	osrc = stkof(n);
 	odst = stkof(res);
diff --git a/src/cmd/8g/cgen64.c b/src/cmd/8g/cgen64.c
index ba99cec..8e568a0 100644
--- a/src/cmd/8g/cgen64.c
+++ b/src/cmd/8g/cgen64.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 /*
diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c
index 7734603..4526a2e 100644
--- a/src/cmd/8g/galign.c
+++ b/src/cmd/8g/galign.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 int	thechar	= '8';
diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h
index 9f7a66a..e23ee9e 100644
--- a/src/cmd/8g/gg.h
+++ b/src/cmd/8g/gg.h
@@ -2,16 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include <u.h>
-#include <libc.h>
-
-#include "../gc/go.h"
-#include "../8l/8.out.h"
-
 #ifndef	EXTERN
 #define	EXTERN	extern
 #endif
 
+#include "../gc/go.h"
+#include "../8l/8.out.h"
+
 typedef	struct	Addr	Addr;
 
 struct	Addr
@@ -54,15 +51,12 @@ enum
 	Fpop2 = 1<<2,
 };
 
-EXTERN	Biobuf*	bout;
 EXTERN	int32	dynloc;
 EXTERN	uchar	reg[D_NONE];
 EXTERN	int32	pcloc;		// instruction counter
 EXTERN	Strlit	emptystring;
 extern	char*	anames[];
-EXTERN	Hist*	hist;
 EXTERN	Prog	zprog;
-EXTERN	Node*	curfn;
 EXTERN	Node*	newproc;
 EXTERN	Node*	deferproc;
 EXTERN	Node*	deferreturn;
@@ -168,12 +162,6 @@ void	complexgen(Node*, Node*);
 void	complexbool(int, Node*, Node*, int, Prog*);
 
 /*
- * gobj.c
- */
-void	data(void);
-void	text(void);
-
-/*
  * list.c
  */
 int	Aconv(Fmt*);
@@ -185,3 +173,5 @@ void	listinit(void);
 
 void	zaddr(Biobuf*, Addr*, int, int);
 
+#pragma	varargck	type	"D"	Addr*
+#pragma	varargck	type	"lD"	Addr*
diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c
index 108c493..c4f2823 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -4,6 +4,8 @@
 
 #undef	EXTERN
 #define	EXTERN
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -429,7 +431,6 @@ hard:
 	if(nr->ullman >= nl->ullman || nl->addable) {
 		mgen(nr, &n2, N);
 		nr = &n2;
-		nr = &n2;
 	} else {
 		tempname(&n2, nr->type);
 		cgen(nr, &n2);
@@ -483,8 +484,8 @@ void
 dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
 {
 	int check;
-	Node n1, t1, t2, n4, nz;
-	Type *t;
+	Node n1, t1, t2, t3, t4, n4, nz;
+	Type *t, *t0;
 	Prog *p1, *p2, *p3;
 
 	// Have to be careful about handling
@@ -496,6 +497,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
 	// For int32 and int64, use explicit test.
 	// Could use int64 hw for int32.
 	t = nl->type;
+	t0 = t;
 	check = 0;
 	if(issigned[t->etype]) {
 		check = 1;
@@ -514,8 +516,18 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx)
 
 	tempname(&t1, t);
 	tempname(&t2, t);
-	cgen(nl, &t1);
-	cgen(nr, &t2);
+	if(t0 != t) {
+		tempname(&t3, t0);
+		tempname(&t4, t0);
+		cgen(nl, &t3);
+		cgen(nr, &t4);
+		// Convert.
+		gmove(&t3, &t1);
+		gmove(&t4, &t2);
+	} else {
+		cgen(nl, &t1);
+		cgen(nr, &t2);
+	}
 
 	if(!samereg(ax, res) && !samereg(dx, res))
 		regalloc(&n1, t, res);
diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c
index 31c42a3..7025a53 100644
--- a/src/cmd/8g/gobj.c
+++ b/src/cmd/8g/gobj.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 void
@@ -276,54 +278,6 @@ dumpfuncs(void)
 	}
 }
 
-/* deferred DATA output */
-static Prog *strdat;
-static Prog *estrdat;
-static int gflag;
-static Prog *savepc;
-
-void
-data(void)
-{
-	gflag = debug['g'];
-	debug['g'] = 0;
-
-	if(estrdat == nil) {
-		strdat = mal(sizeof(*pc));
-		clearp(strdat);
-		estrdat = strdat;
-	}
-	if(savepc)
-		fatal("data phase error");
-	savepc = pc;
-	pc = estrdat;
-}
-
-void
-text(void)
-{
-	if(!savepc)
-		fatal("text phase error");
-	debug['g'] = gflag;
-	estrdat = pc;
-	pc = savepc;
-	savepc = nil;
-}
-
-void
-dumpdata(void)
-{
-	Prog *p;
-
-	if(estrdat == nil)
-		return;
-	*pc = *strdat;
-	if(gflag)
-		for(p=pc; p!=estrdat; p=p->link)
-			print("%P\n", p);
-	pc = estrdat;
-}
-
 int
 dsname(Sym *s, int off, char *t, int n)
 {
@@ -380,6 +334,17 @@ gdata(Node *nam, Node *nr, int wid)
 	Prog *p;
 	vlong v;
 
+	if(nr->op == OLITERAL) {
+		switch(nr->val.ctype) {
+		case CTCPLX:
+			gdatacomplex(nam, nr->val.u.cval);
+			return;
+		case CTSTR:
+			gdatastring(nam, nr->val.u.sval);
+			return;
+		}
+	}
+
 	if(wid == 8 && is64(nr->type)) {
 		v = mpgetfix(nr->val.u.xval);
 		p = gins(ADATA, nam, nodintconst(v));
@@ -547,6 +512,8 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
 	Prog *p;
 	Type *f;
 
+	USED(iface);
+
 	e = method->sym;
 	for(d=0; d<nelem(dotlist); d++) {
 		c = adddot1(e, rcvr, d, nil, 0);
@@ -626,7 +593,6 @@ out:
 		// but 6l has a bug, and it can't handle
 		// JMP instructions too close to the top of
 		// a new function.
-		p = pc;
 		gins(ANOP, N, N);
 	}
 
diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c
index a35c81e..1aae34e 100644
--- a/src/cmd/8g/gsubr.c
+++ b/src/cmd/8g/gsubr.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 // TODO(rsc): Can make this bigger if we move
@@ -48,6 +50,10 @@ clearp(Prog *p)
 	pcloc++;
 }
 
+static int ddumped;
+static Prog *dfirst;
+static Prog *dpc;
+
 /*
  * generate and return proc with p->as = as,
  * linked into program.  pc is next instruction.
@@ -57,10 +63,22 @@ prog(int as)
 {
 	Prog *p;
 
-	p = pc;
-	pc = mal(sizeof(*pc));
-
-	clearp(pc);
+	if(as == ADATA || as == AGLOBL) {
+		if(ddumped)
+			fatal("already dumped data");
+		if(dpc == nil) {
+			dpc = mal(sizeof(*dpc));
+			dfirst = dpc;
+		}
+		p = dpc;
+		dpc = mal(sizeof(*dpc));
+		p->link = dpc;
+	} else {
+		p = pc;
+		pc = mal(sizeof(*pc));
+		clearp(pc);
+		p->link = pc;
+	}
 
 	if(lineno == 0) {
 		if(debug['K'])
@@ -69,10 +87,21 @@ prog(int as)
 
 	p->as = as;
 	p->lineno = lineno;
-	p->link = pc;
 	return p;
 }
 
+void
+dumpdata(void)
+{
+	ddumped = 1;
+	if(dfirst == nil)
+		return;
+	newplist();
+	*pc = *dfirst;
+	pc = dpc;
+	clearp(pc);
+}
+
 /*
  * generate a branch.
  * t is ignored.
@@ -82,6 +111,7 @@ gbranch(int as, Type *t)
 {
 	Prog *p;
 
+	USED(t);
 	p = prog(as);
 	p->to.type = D_BRANCH;
 	p->to.branch = P;
@@ -822,7 +852,7 @@ regalloc(Node *n, Type *t, Node *o)
 
 		fprint(2, "registers allocated at\n");
 		for(i=D_AX; i<=D_DI; i++)
-			fprint(2, "\t%R\t%#ux\n", i, regpc[i]);
+			fprint(2, "\t%R\t%#lux\n", i, regpc[i]);
 		yyerror("out of fixed registers");
 		goto err;
 
@@ -832,7 +862,6 @@ regalloc(Node *n, Type *t, Node *o)
 		goto out;
 	}
 	yyerror("regalloc: unknown type %T", t);
-	i = 0;
 
 err:
 	nodreg(n, t, 0);
@@ -842,7 +871,7 @@ out:
 	if (i == D_SP)
 		print("alloc SP\n");
 	if(reg[i] == 0) {
-		regpc[i] = (ulong)__builtin_return_address(0);
+		regpc[i] = (ulong)getcallerpc(&n);
 		if(i == D_AX || i == D_CX || i == D_DX || i == D_SP) {
 			dump("regalloc-o", o);
 			fatal("regalloc %R", i);
@@ -1809,7 +1838,6 @@ naddr(Node *n, Addr *a, int canemitcode)
 			a->width = n->type->width;
 			a->gotype = ngotype(n);
 		}
-		a->pun = n->pun;
 		a->offset = n->xoffset;
 		a->sym = n->sym;
 		if(a->sym == S)
@@ -1955,5 +1983,9 @@ sudoclean(void)
 int
 sudoaddable(int as, Node *n, Addr *a)
 {
+	USED(as);
+	USED(n);
+	USED(a);
+
 	return 0;
 }
diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c
index edb1ece..88d3d5f 100644
--- a/src/cmd/8g/list.c
+++ b/src/cmd/8g/list.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 static	int	sconsize;
@@ -128,7 +130,7 @@ Dconv(Fmt *fp)
 		if(fp->flags & FmtLong) {
 			d1 = a->offset;
 			d2 = a->offset2;
-			snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2);
+			snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2);
 			break;
 		}
 		snprint(str, sizeof(str), "$%d", a->offset);
diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h
index 8f31dec..ed6eb15 100644
--- a/src/cmd/8g/opt.h
+++ b/src/cmd/8g/opt.h
@@ -162,3 +162,5 @@ int32	RtoB(int);
 int32	FtoB(int);
 int	BtoR(int32);
 int	BtoF(int32);
+
+#pragma	varargck	type	"D"	Adr*
diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c
index 5ad29e1..e0e44a5 100644
--- a/src/cmd/8g/peep.c
+++ b/src/cmd/8g/peep.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 #include "opt.h"
 
@@ -882,7 +884,6 @@ loop:
 		if(p->from.dval == p0->from.dval)
 		if(p->from.index == p0->from.index) {
 			excise(r);
-			t++;
 			goto loop;
 		}
 		break;
diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c
index 2b878f6..de5fd87 100644
--- a/src/cmd/8g/reg.c
+++ b/src/cmd/8g/reg.c
@@ -28,9 +28,9 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
-#undef	EXTERN
-#define	EXTERN
 #include "opt.h"
 
 #define	NREGVAR	8
@@ -98,19 +98,19 @@ setaddrs(Bits bit)
 {
 	int i, n;
 	Var *v;
-	Sym *s;
+	Node *node;
 
 	while(bany(&bit)) {
 		// convert each bit to a variable
 		i = bnum(bit);
-		s = var[i].sym;
+		node = var[i].node;
 		n = var[i].name;
 		bit.b[i/32] &= ~(1L<<(i%32));
 
 		// disable all pieces of that variable
 		for(i=0; i<nvar; i++) {
 			v = var+i;
-			if(v->sym == s && v->name == n)
+			if(v->node == node && v->name == n)
 				v->addr = 2;
 		}
 	}
@@ -155,7 +155,7 @@ regopt(Prog *firstp)
 	nvar = NREGVAR;
 	memset(var, 0, NREGVAR*sizeof var[0]);
 	for(i=0; i<NREGVAR; i++)
-		var[i].sym = lookup(regname[i]);
+		var[i].node = newname(lookup(regname[i]));
 
 	regbits = RtoB(D_SP);
 	for(z=0; z<BITS; z++) {
@@ -725,12 +725,12 @@ addmove(Reg *r, int bn, int rn, int f)
 	v = var + bn;
 
 	a = &p1->to;
-	a->sym = v->sym;
 	a->offset = v->offset;
 	a->etype = v->etype;
 	a->type = v->name;
 	a->gotype = v->gotype;
 	a->node = v->node;
+	a->sym = v->node->sym;
 
 	// need to clean this up with wptr and
 	// some of the defaults
@@ -810,7 +810,7 @@ mkvar(Reg *r, Adr *a)
 	int i, t, n, et, z, w, flag, regu;
 	int32 o;
 	Bits bit;
-	Sym *s;
+	Node *node;
 
 	/*
 	 * mark registers used
@@ -847,10 +847,11 @@ mkvar(Reg *r, Adr *a)
 		break;
 	}
 
-	s = a->sym;
-	if(s == S)
+	node = a->node;
+	if(node == N || node->op != ONAME || node->orig != N)
 		goto none;
-	if(s->name[0] == '.')
+	node = node->orig;
+	if(node->sym->name[0] == '.')
 		goto none;
 	et = a->etype;
 	o = a->offset;
@@ -859,7 +860,7 @@ mkvar(Reg *r, Adr *a)
 	flag = 0;
 	for(i=0; i<nvar; i++) {
 		v = var+i;
-		if(v->sym == s && v->name == n) {
+		if(v->node == node && v->name == n) {
 			if(v->offset == o)
 			if(v->etype == et)
 			if(v->width == w)
@@ -868,7 +869,7 @@ mkvar(Reg *r, Adr *a)
 			// if they overlap, disable both
 			if(overlap(v->offset, v->width, o, w)) {
 				if(debug['R'])
-					print("disable %s\n", v->sym->name);
+					print("disable %s\n", node->sym->name);
 				v->addr = 1;
 				flag = 1;
 			}
@@ -882,7 +883,7 @@ mkvar(Reg *r, Adr *a)
 	}
 
 	if(nvar >= NVAR) {
-		if(debug['w'] > 1 && s)
+		if(debug['w'] > 1 && node != N)
 			fatal("variable not optimized: %D", a);
 		goto none;
 	}
@@ -890,17 +891,16 @@ mkvar(Reg *r, Adr *a)
 	i = nvar;
 	nvar++;
 	v = var+i;
-	v->sym = s;
 	v->offset = o;
 	v->name = n;
 	v->gotype = a->gotype;
 	v->etype = et;
 	v->width = w;
 	v->addr = flag;		// funny punning
-	v->node = a->node;
+	v->node = node;
 
 	if(debug['R'])
-		print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr);
+		print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr);
 	ostats.nvar++;
 
 	bit = blsh(i);
diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h
index 9a8483a..5e4b73a 100644
--- a/src/cmd/8l/8.out.h
+++ b/src/cmd/8l/8.out.h
@@ -115,7 +115,8 @@ enum	as
 	AIRETW,
 	AJCC,
 	AJCS,
-	AJCXZ,
+	AJCXZL,
+	AJCXZW,
 	AJEQ,
 	AJGE,
 	AJGT,
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 22abd80..518951f 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -41,6 +41,7 @@
 
 char linuxdynld[] = "/lib/ld-linux.so.2";
 char freebsddynld[] = "/usr/libexec/ld-elf.so.1";
+char openbsddynld[] = "/usr/libexec/ld.so";
 
 int32
 entryvalue(void)
@@ -956,6 +957,9 @@ asmb(void)
 				case Hfreebsd:
 					interpreter = freebsddynld;
 					break;
+				case Hopenbsd:
+					interpreter = openbsddynld;
+					break;
 				}
 			}
 			elfinterp(sh, startva, interpreter);
@@ -1122,7 +1126,10 @@ asmb(void)
 		eh->ident[EI_VERSION] = EV_CURRENT;
 		switch(HEADTYPE) {
 		case Hfreebsd:
-			eh->ident[EI_OSABI] = 9;
+			eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
+			break;
+		case Hopenbsd:
+			eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
 			break;
 		}
 
diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go
index b708889..de877bb 100644
--- a/src/cmd/8l/doc.go
+++ b/src/cmd/8l/doc.go
@@ -33,6 +33,8 @@ Options new in this version:
 	Write Linux ELF binaries (default when $GOOS is linux)
 -Hfreebsd
 	Write FreeBSD ELF binaries (default when $GOOS is freebsd)
+-Hopenbsd
+	Write OpenBSD ELF binaries (default when $GOOS is openbsd)
 -Hwindows
 	Write Windows PE32 binaries (default when $GOOS is windows)
 -I interpreter
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index 4ee0db9..a721f38 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -209,6 +209,7 @@ enum
 	Zbr,
 	Zcall,
 	Zcallcon,
+	Zcallind,
 	Zib_,
 	Zib_rp,
 	Zibo_m,
diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c
index a8e1c34..297b5be 100644
--- a/src/cmd/8l/obj.c
+++ b/src/cmd/8l/obj.c
@@ -55,6 +55,7 @@ Header headers[] = {
    "darwin", Hdarwin,
    "linux", Hlinux,
    "freebsd", Hfreebsd,
+   "openbsd", Hopenbsd,
    "windows", Hwindows,
    "windowsgui", Hwindows,
    0, 0
@@ -69,6 +70,7 @@ Header headers[] = {
  *	-Hdarwin -Tx -Rx			is Apple Mach-O
  *	-Hlinux -Tx -Rx				is Linux ELF32
  *	-Hfreebsd -Tx -Rx			is FreeBSD ELF32
+ *	-Hopenbsd -Tx -Rx			is OpenBSD ELF32
  *	-Hwindows -Tx -Rx			is MS Windows PE32
  */
 
@@ -223,6 +225,7 @@ main(int argc, char *argv[])
 		break;
 	case Hlinux:	/* elf32 executable */
 	case Hfreebsd:
+	case Hopenbsd:
 		/*
 		 * ELF uses TLS offsets negative from %gs.
 		 * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS).
diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c
index f5c195d..69602d7 100644
--- a/src/cmd/8l/optab.c
+++ b/src/cmd/8l/optab.c
@@ -260,6 +260,7 @@ uchar	yloop[] =
 uchar	ycall[] =
 {
 	Ynone,	Yml,	Zo_m,	2,
+	Ynone,	Ycol,	Zcallind,	2,
 	Ynone,	Ybr,	Zcall,	0,
 	Ynone,	Yi32,	Zcallcon,	1,
 	0
@@ -383,7 +384,7 @@ Optab optab[] =
 	{ ABTSL,	yml_rl,	Pm, 0xab },
 	{ ABTSW,	yml_rl,	Pq, 0xab },
 	{ ABYTE,	ybyte,	Px, 1 },
-	{ ACALL,	ycall,	Px, 0xff,(02),0xe8 },
+	{ ACALL,	ycall,	Px, 0xff,(02),0xff,(0x15),0xe8 },
 	{ ACLC,		ynone,	Px, 0xf8 },
 	{ ACLD,		ynone,	Px, 0xfc },
 	{ ACLI,		ynone,	Px, 0xfa },
@@ -430,7 +431,8 @@ Optab optab[] =
 	{ AIRETW,	ynone,	Pe, 0xcf },
 	{ AJCC,		yjcond,	Px, 0x73,0x83,(00) },
 	{ AJCS,		yjcond,	Px, 0x72,0x82 },
-	{ AJCXZ,	yloop,	Px, 0xe3 },
+	{ AJCXZL,	yloop,	Px, 0xe3 },
+	{ AJCXZW,	yloop,	Px, 0xe3 },
 	{ AJEQ,		yjcond,	Px, 0x74,0x84 },
 	{ AJGE,		yjcond,	Px, 0x7d,0x8d },
 	{ AJGT,		yjcond,	Px, 0x7f,0x8f },
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index 2e0990c..54ea965 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -307,9 +307,12 @@ patch(void)
 					p->from.offset = 0;
 				}
 			}
-			if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) {
+			if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH)) {
 				s = p->to.sym;
-				if(s) {
+				if(p->to.type == D_INDIR+D_ADDR) {
+					 /* skip check if this is an indirect call (CALL *symbol(SB)) */
+					 continue;
+				} else if(s) {
 					if(debug['c'])
 						Bprint(&bso, "%s calls %s\n", TNAME, s->name);
 					if((s->type&~SSUB) != STEXT) {
diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c
index a4cba12..81c1d37 100644
--- a/src/cmd/8l/span.c
+++ b/src/cmd/8l/span.c
@@ -83,7 +83,10 @@ span1(Sym *s)
 						loop++;
 						q->back ^= 2;
 					}
-					s->p[q->pc+1] = v;
+					if(q->as == AJCXZW)
+						s->p[q->pc+2] = v;
+					else
+						s->p[q->pc+1] = v;
 				} else {
 					bp = s->p + q->pc + q->mark - 4;
 					*bp++ = v;
@@ -282,6 +285,8 @@ oclass(Adr *a)
 				}
 				return Yxxx;
 			}
+			//if(a->type == D_INDIR+D_ADDR)
+			//	print("*Ycol\n");
 			return Ycol;
 		}
 		return Ym;
@@ -1056,9 +1061,10 @@ found:
 
 	case Zbr:
 	case Zjmp:
+	case Zloop:
 		q = p->pcond;
 		if(q == nil) {
-			diag("jmp/branch without target");
+			diag("jmp/branch/loop without target");
 			errorexit();
 		}
 		if(q->as == ATEXT) {
@@ -1084,8 +1090,12 @@ found:
 		if(p->back & 1) {
 			v = q->pc - (p->pc + 2);
 			if(v >= -128) {
+				if(p->as == AJCXZW)
+					*andptr++ = 0x67;
 				*andptr++ = op;
 				*andptr++ = v;
+			} else if(t[2] == Zloop) {
+				diag("loop too far: %P", p);
 			} else {
 				v -= 5-2;
 				if(t[2] == Zbr) {
@@ -1105,8 +1115,12 @@ found:
 		p->forwd = q->comefrom;
 		q->comefrom = p;
 		if(p->back & 2)	{ // short
+			if(p->as == AJCXZW)
+				*andptr++ = 0x67;
 			*andptr++ = op;
 			*andptr++ = 0;
+		} else if(t[2] == Zloop) {
+			diag("loop too far: %P", p);
 		} else {
 			if(t[2] == Zbr)
 				*andptr++ = 0x0f;
@@ -1131,18 +1145,17 @@ found:
 		r->add = p->to.offset;
 		put4(0);
 		break;
-
-	case Zloop:
-		q = p->pcond;
-		if(q == nil) {
-			diag("loop without target");
-			errorexit();
-		}
-		v = q->pc - p->pc - 2;
-		if(v < -128 && v > 127)
-			diag("loop too far: %P", p);
+	
+	case Zcallind:
 		*andptr++ = op;
-		*andptr++ = v;
+		*andptr++ = o->op[z+1];
+		r = addrel(cursym);
+		r->off = p->pc + andptr - and;
+		r->type = D_ADDR;
+		r->siz = 4;
+		r->add = p->to.offset;
+		r->sym = p->to.sym;
+		put4(0);
 		break;
 
 	case Zbyte:
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index 6341382..7faece8 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -73,6 +73,9 @@ A few special functions convert between Go and C types
 by making copies of the data.  In pseudo-Go definitions:
 
 	// Go string to C string
+	// The C string is allocated in the C heap using malloc.
+	// It is the caller's responsibility to arrange for it to be
+	// freed, such as by calling C.free.
 	func C.CString(string) *C.char
 
 	// C string to Go string
diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile
index 95dba9c..62836fc 100644
--- a/src/cmd/cov/Makefile
+++ b/src/cmd/cov/Makefile
@@ -29,6 +29,7 @@ endif
 install: install-$(NAME)
 install-linux: install-default
 install-freebsd: install-default
+install-openbsd: install-default
 install-windows: install-default
 
 # on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash
diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c
index 5ff22c0..ecbabf3 100644
--- a/src/cmd/cov/main.c
+++ b/src/cmd/cov/main.c
@@ -337,6 +337,9 @@ cover(void)
 uvlong
 rgetzero(Map *map, char *reg)
 {
+	USED(map);
+	USED(reg);
+
 	return 0;
 }
 
diff --git a/src/cmd/cov/tree.c b/src/cmd/cov/tree.c
index 116772e..905bb7d 100644
--- a/src/cmd/cov/tree.c
+++ b/src/cmd/cov/tree.c
@@ -2,7 +2,7 @@
 
 /*
 Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
-                        Massachusetts Institute of Technology
+	Massachusetts Institute of Technology
 Portions Copyright (c) 2009 The Go Authors. All rights reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining
@@ -35,9 +35,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <libc.h>
 #include "tree.h"
 
-#define TreeNode TreeNode
-#define Tree Tree
-
 enum
 {
 	Red = 0,
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
index 0af7659..f7e3051 100644
--- a/src/cmd/gc/Makefile
+++ b/src/cmd/gc/Makefile
@@ -22,6 +22,7 @@ OFILES=\
 	closure.$O\
 	const.$O\
 	dcl.$O\
+	esc.$O\
 	export.$O\
 	gen.$O\
 	init.$O\
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
index 14c1c4a..f316c19 100644
--- a/src/cmd/gc/align.c
+++ b/src/cmd/gc/align.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c
index 7188ac4..f3b031c 100644
--- a/src/cmd/gc/bits.c
+++ b/src/cmd/gc/bits.c
@@ -28,6 +28,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
@@ -148,12 +150,12 @@ Qconv(Fmt *fp)
 			first = 0;
 		else
 			fmtprint(fp, " ");
-		if(var[i].sym == S)
+		if(var[i].node == N || var[i].node->sym == S)
 			fmtprint(fp, "$%lld", var[i].offset);
 		else {
-			fmtprint(fp, var[i].sym->name);
+			fmtprint(fp, var[i].node->sym->name);
 			if(var[i].offset != 0)
-				fmtprint(fp, "%+d", var[i].offset);
+				fmtprint(fp, "%+lld", (vlong)var[i].offset);
 		}
 		bits.b[i/32] &= ~(1L << (i%32));
 	}
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index 1261eef..d29e8cb 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -6,6 +6,8 @@
  * function literals aka closures
  */
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 void
@@ -57,7 +59,6 @@ closurebody(NodeList *body)
 		body = list1(nod(OEMPTY, N, N));
 
 	func = curfn;
-	l = func->dcl;
 	func->nbody = body;
 	funcbody(func);
 
@@ -129,6 +130,8 @@ makeclosure(Node *func, NodeList **init, int nowrap)
 	static int closgen;
 	char *p;
 
+	USED(init);
+
 	/*
 	 * wrap body in external function
 	 * with extra closure parameters.
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
index 36a64cb..135a810 100644
--- a/src/cmd/gc/const.c
+++ b/src/cmd/gc/const.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 #define	TUP(x,y)	(((x)<<16)|(y))
 
@@ -1099,7 +1101,7 @@ cmpslit(Node *l, Node *r)
 int
 smallintconst(Node *n)
 {
-	if(n->op == OLITERAL && n->type != T)
+	if(n->op == OLITERAL && n->val.ctype == CTINT && n->type != T)
 	switch(simtype[n->type->etype]) {
 	case TINT8:
 	case TUINT8:
@@ -1110,6 +1112,7 @@ smallintconst(Node *n)
 	case TBOOL:
 	case TPTR32:
 		return 1;
+	case TIDEAL:
 	case TINT64:
 	case TUINT64:
 		if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
index 890cf7f..5bc8d64 100644
--- a/src/cmd/gc/cplx.c
+++ b/src/cmd/gc/cplx.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "gg.h"
 
 static	void	subnode(Node *nr, Node *ni, Node *nc);
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
index 5bfeeb9..d8b89b4 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 #include	"y.tab.h"
 
@@ -115,6 +117,8 @@ dumpdcl(char *st)
 	Sym *s, *d;
 	int i;
 
+	USED(st);
+
 	i = 0;
 	for(d=dclstack; d!=S; d=d->link) {
 		i++;
@@ -188,7 +192,7 @@ declare(Node *n, int ctxt)
 		n->curfn = curfn;
 	}
 	if(ctxt == PAUTO)
-		n->xoffset = BADWIDTH;
+		n->xoffset = 0;
 
 	if(s->block == block)
 		redeclare(s, "in this block");
@@ -726,7 +730,6 @@ stotype(NodeList *l, int et, Type **t, int funarg)
 	for(; l; l=l->next) {
 		n = l->n;
 		lineno = n->lineno;
-		note = nil;
 
 		if(n->op != ODCLFIELD)
 			fatal("stotype: oops %N\n", n);
@@ -820,6 +823,10 @@ stotype(NodeList *l, int et, Type **t, int funarg)
 		f->width = BADWIDTH;
 		f->isddd = n->isddd;
 
+		// esc.c needs to find f given a PPARAM to add the tag.
+		if(funarg && n->left && n->left->class == PPARAM)
+			n->left->paramfld = f;
+
 		if(left != N && left->op == ONAME) {
 			f->nname = left;
 			f->embedded = n->embedded;
@@ -1133,8 +1140,6 @@ addmethod(Sym *sf, Type *t, int local)
 	Type *f, *d, *pa;
 	Node *n;
 
-	pa = nil;
-
 	// get field sym
 	if(sf == S)
 		fatal("no method symbol");
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
index 3fe7faf..5bb5e0e 100644
--- a/src/cmd/gc/doc.go
+++ b/src/cmd/gc/doc.go
@@ -35,12 +35,13 @@ Flags:
 		output file, default file.6 for 6g, etc.
 	-e
 		normally the compiler quits after 10 errors; -e prints all errors
+	-p path
+		assume that path is the eventual import path for this code,
+		and diagnose any attempt to import a package that depends on it.
 	-L
 		show entire file path when printing line numbers in errors
 	-I dir1 -I dir2
 		add dir1 and dir2 to the list of paths to check for imported packages
-	-N
-		disable optimization
 	-S
 		write assembly language text to standard output
 	-u
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
new file mode 100644
index 0000000..cd1f977
--- /dev/null
+++ b/src/cmd/gc/esc.c
@@ -0,0 +1,723 @@
+// 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.
+//
+// Escape analysis.
+//
+// First escfunc, esc and escassign recurse over the ast of each
+// function to dig out flow(dst,src) edges between any
+// pointer-containing nodes and store them in dst->escflowsrc.  For
+// variables assigned to a variable in an outer scope or used as a
+// return value, they store a flow(theSink, src) edge to a fake node
+// 'the Sink'.  For variables referenced in closures, an edge
+// flow(closure, &var) is recorded and the flow of a closure itself to
+// an outer scope is tracked the same way as other variables.
+//
+// Then escflood walks the graph starting at theSink and tags all
+// variables of it can reach an & node as escaping and all function
+// parameters it can reach as leaking.
+//
+// If a value's address is taken but the address does not escape,
+// then the value can stay on the stack.  If the value new(T) does
+// not escape, then new(T) can be rewritten into a stack allocation.
+// The same is true of slice literals.
+//
+// If escape analysis is disabled (-s), this code is not used.
+// Instead, the compiler assumes that any value whose address
+// is taken without being immediately dereferenced
+// needs to be moved to the heap, and new(T) and slice
+// literals are always real allocations.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static void escfunc(Node *func);
+static void esclist(NodeList *l);
+static void esc(Node *n);
+static void escassign(Node *dst, Node *src);
+static void esccall(Node*);
+static void escflows(Node *dst, Node *src);
+static void escflood(Node *dst);
+static void escwalk(int level, Node *dst, Node *src);
+static void esctag(Node *func);
+
+// Fake node that all
+//   - return values and output variables
+//   - parameters on imported functions not marked 'safe'
+//   - assignments to global variables
+// flow to.
+static Node	theSink;
+
+static NodeList*	dsts;		// all dst nodes
+static int	loopdepth;	// for detecting nested loop scopes
+static int	pdepth;		// for debug printing in recursions.
+static Strlit*	safetag;	// gets slapped on safe parameters' field types for export
+static int	dstcount, edgecount;	// diagnostic
+static NodeList*	noesc;	// list of possible non-escaping nodes, for printing
+
+void
+escapes(void)
+{
+	NodeList *l;
+
+	theSink.op = ONAME;
+	theSink.class = PEXTERN;
+	theSink.sym = lookup(".sink");
+	theSink.escloopdepth = -1;
+
+	safetag = strlit("noescape");
+
+	// flow-analyze top level functions
+	for(l=xtop; l; l=l->next)
+		if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
+			escfunc(l->n);
+
+	// print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
+
+	// visit the updstream of each dst, mark address nodes with
+	// addrescapes, mark parameters unsafe
+	for(l = dsts; l; l=l->next)
+		escflood(l->n);
+
+	// for all top level functions, tag the typenodes corresponding to the param nodes
+	for(l=xtop; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			esctag(l->n);
+
+	if(debug['m']) {
+		for(l=noesc; l; l=l->next)
+			if(l->n->esc == EscNone)
+				warnl(l->n->lineno, "%S %#N does not escape",
+					(l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
+					l->n);
+	}
+}
+
+
+static void
+escfunc(Node *func)
+{
+	Node *savefn, *n;
+	NodeList *ll;
+	int saveld;
+
+	saveld = loopdepth;
+	loopdepth = 1;
+	savefn = curfn;
+	curfn = func;
+
+	for(ll=curfn->dcl; ll; ll=ll->next) {
+		if(ll->n->op != ONAME)
+			continue;
+		switch (ll->n->class) {
+		case PPARAMOUT:
+			// output parameters flow to the sink
+			escflows(&theSink, ll->n);
+			ll->n->escloopdepth = loopdepth;
+			break;
+		case PPARAM:
+			if(ll->n->type && !haspointers(ll->n->type))
+				break;
+			ll->n->esc = EscNone;	// prime for escflood later
+			noesc = list(noesc, ll->n);
+			ll->n->escloopdepth = loopdepth;
+			break;
+		}
+	}
+
+	// walk will take the address of cvar->closure later and assign it to cvar.
+	// handle that here by linking a fake oaddr node directly to the closure.
+	for(ll=curfn->cvars; ll; ll=ll->next) {
+		if(ll->n->op == OXXX)  // see dcl.c:398
+			continue;
+
+		n = nod(OADDR, ll->n->closure, N);
+		n->lineno = ll->n->lineno;
+		typecheck(&n, Erv);
+		escassign(curfn, n);
+	}
+
+	esclist(curfn->nbody);
+	curfn = savefn;
+	loopdepth = saveld;
+}
+
+static void
+esclist(NodeList *l)
+{
+	for(; l; l=l->next)
+		esc(l->n);
+}
+
+static void
+esc(Node *n)
+{
+	int lno;
+	NodeList *ll, *lr;
+
+	if(n == N)
+		return;
+
+	lno = setlineno(n);
+
+	if(n->op == OFOR || n->op == ORANGE)
+		loopdepth++;
+
+	esc(n->left);
+	esc(n->right);
+	esc(n->ntest);
+	esc(n->nincr);
+	esclist(n->ninit);
+	esclist(n->nbody);
+	esclist(n->nelse);
+	esclist(n->list);
+	esclist(n->rlist);
+
+	if(n->op == OFOR || n->op == ORANGE)
+		loopdepth--;
+
+	if(debug['m'] > 1)
+		print("%L:[%d] %#S esc: %#N\n", lineno, loopdepth,
+		      (curfn && curfn->nname) ? curfn->nname->sym : S, n);
+
+	switch(n->op) {
+	case ODCL:
+		// Record loop depth at declaration.
+		if(n->left)
+			n->left->escloopdepth = loopdepth;
+		break;
+
+	case OLABEL:  // TODO: new loop/scope only if there are backjumps to it.
+		loopdepth++;
+		break;
+
+	case ORANGE:
+		// Everything but fixed array is a dereference.
+		if(isfixedarray(n->type) && n->list->next)
+			escassign(n->list->next->n, n->right);
+		break;
+
+	case OSWITCH:
+		if(n->ntest && n->ntest->op == OTYPESW) {
+			for(ll=n->list; ll; ll=ll->next) {  // cases
+				// ntest->right is the argument of the .(type),
+				// ll->n->nname is the variable per case
+				escassign(ll->n->nname, n->ntest->right);
+			}
+		}
+		break;
+
+	case OAS:
+	case OASOP:
+		escassign(n->left, n->right);
+		break;
+
+	case OAS2:	// x,y = a,b
+		if(count(n->list) == count(n->rlist))
+			for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+				escassign(ll->n, lr->n);
+		break;
+
+	case OAS2RECV:		// v, ok = <-ch
+	case OAS2MAPR:		// v, ok = m[k]
+	case OAS2DOTTYPE:	// v, ok = x.(type)
+	case OAS2MAPW:		// m[k] = x, ok
+		escassign(n->list->n, n->rlist->n);
+		break;
+
+	case OSEND:		// ch <- x
+		escassign(&theSink, n->right);
+		break;
+
+	case ODEFER:
+		if(loopdepth == 1)  // top level
+			break;
+		// arguments leak out of scope
+		// TODO: leak to a dummy node instead
+		// fallthrough
+	case OPROC:
+		// go f(x) - f and x escape
+		escassign(&theSink, n->left->left);
+		for(ll=n->left->list; ll; ll=ll->next)
+			escassign(&theSink, ll->n);
+		break;
+
+	case ORETURN:
+		for(ll=n->list; ll; ll=ll->next)
+			escassign(&theSink, ll->n);
+		break;
+
+	case OPANIC:
+		// Argument could leak through recover.
+		escassign(&theSink, n->left);
+		break;
+
+	case OAPPEND:
+		if(!n->isddd)
+			for(ll=n->list->next; ll; ll=ll->next)
+				escassign(&theSink, ll->n);  // lose track of assign to dereference
+		break;
+
+	case OCALLMETH:
+	case OCALLFUNC:
+	case OCALLINTER:
+		esccall(n);
+		break;
+
+	case OCONV:
+	case OCONVNOP:
+	case OCONVIFACE:
+		escassign(n, n->left);
+		break;
+
+	case OARRAYLIT:
+		if(isslice(n->type)) {
+			n->esc = EscNone;  // until proven otherwise
+			noesc = list(noesc, n);
+			n->escloopdepth = loopdepth;
+			// Values make it to memory, lose track.
+			for(ll=n->list; ll; ll=ll->next)
+				escassign(&theSink, ll->n->right);
+		} else {
+			// Link values to array.
+			for(ll=n->list; ll; ll=ll->next)
+				escassign(n, ll->n->right);
+		}
+		break;
+
+	case OSTRUCTLIT:
+		// Link values to struct.
+		for(ll=n->list; ll; ll=ll->next)
+			escassign(n, ll->n->right);
+		break;
+
+	case OMAPLIT:
+		n->esc = EscNone;  // until proven otherwise
+		noesc = list(noesc, n);
+		n->escloopdepth = loopdepth;
+		// Keys and values make it to memory, lose track.
+		for(ll=n->list; ll; ll=ll->next) {
+			escassign(&theSink, ll->n->left);
+			escassign(&theSink, ll->n->right);
+		}
+		break;
+	
+	case OADDR:
+	case OCLOSURE:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case ONEW:
+		n->escloopdepth = loopdepth;
+		n->esc = EscNone;  // until proven otherwise
+		noesc = list(noesc, n);
+		break;
+	}
+
+	lineno = lno;
+}
+
+// Assert that expr somehow gets assigned to dst, if non nil.  for
+// dst==nil, any name node expr still must be marked as being
+// evaluated in curfn.	For expr==nil, dst must still be examined for
+// evaluations inside it (e.g *f(x) = y)
+static void
+escassign(Node *dst, Node *src)
+{
+	int lno;
+
+	if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
+		return;
+
+	if(debug['m'] > 1)
+		print("%L:[%d] %#S escassign: %hN = %hN\n", lineno, loopdepth,
+		      (curfn && curfn->nname) ? curfn->nname->sym : S, dst, src);
+
+	setlineno(dst);
+	
+	// Analyze lhs of assignment.
+	// Replace dst with theSink if we can't track it.
+	switch(dst->op) {
+	default:
+		dump("dst", dst);
+		fatal("escassign: unexpected dst");
+
+	case OARRAYLIT:
+	case OCLOSURE:
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+		break;
+
+	case ONAME:
+		if(dst->class == PEXTERN)
+			dst = &theSink;
+		break;
+	case ODOT:	      // treat "dst.x  = src" as "dst = src"
+		escassign(dst->left, src);
+		return;
+	case OINDEX:
+		if(isfixedarray(dst->left->type)) {
+			escassign(dst->left, src);
+			return;
+		}
+		dst = &theSink;  // lose track of dereference
+		break;
+	case OIND:
+	case ODOTPTR:
+		dst = &theSink;  // lose track of dereference
+		break;
+	case OINDEXMAP:
+		// lose track of key and value
+		escassign(&theSink, dst->right);
+		dst = &theSink;
+		break;
+	}
+
+	lno = setlineno(src);
+	pdepth++;
+
+	switch(src->op) {
+	case OADDR:	// dst = &x
+	case OIND:	// dst = *x
+	case ODOTPTR:	// dst = (*x).f
+	case ONAME:
+	case OPARAM:
+	case ODDDARG:
+	case OARRAYLIT:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+		// loopdepth was set in the defining statement or function header
+		escflows(dst, src);
+		break;
+
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case ODOT:
+	case ODOTMETH:	// treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
+			// iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
+	case ODOTTYPE:
+	case ODOTTYPE2:
+	case OSLICE:
+	case OSLICEARR:
+		// Conversions, field access, slice all preserve the input value.
+		escassign(dst, src->left);
+		break;
+
+	case OAPPEND:
+		// Append returns first argument.
+		escassign(dst, src->list->n);
+		break;
+	
+	case OINDEX:
+		// Index of array preserves input value.
+		if(isfixedarray(src->left->type))
+			escassign(dst, src->left);
+		break;
+
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case ONEW:
+		escflows(dst, src);
+		break;
+
+	case OCLOSURE:
+		escflows(dst, src);
+		escfunc(src);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OMUL:
+	case ODIV:
+	case OMOD:
+	case OLSH:
+	case ORSH:
+	case OAND:
+	case OANDNOT:
+	case OPLUS:
+	case OMINUS:
+	case OCOM:
+		// Might be pointer arithmetic, in which case
+		// the operands flow into the result.
+		// TODO(rsc): Decide what the story is here.  This is unsettling.
+		escassign(dst, src->left);
+		escassign(dst, src->right);
+		break;
+
+	}
+
+	pdepth--;
+	lineno = lno;
+}
+
+
+// This is a bit messier than fortunate, pulled out of escassign's big
+// switch for clarity.	We either have the paramnodes, which may be
+// connected to other things throug flows or we have the parameter type
+// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
+// different for methods vs plain functions and for imported vs
+// this-package
+static void
+esccall(Node *n)
+{
+	NodeList *ll, *lr;
+	Node *a, *fn, *src;
+	Type *t, *fntype;
+
+	fn = N;
+	switch(n->op) {
+	default:
+		fatal("esccall");
+
+	case OCALLFUNC:
+		fn = n->left;
+		fntype = fn->type;
+		break;
+
+	case OCALLMETH:
+		fn = n->left->right->sym->def;
+		if(fn)
+			fntype = fn->type;
+		else
+			fntype = n->left->type;
+		break;
+
+	case OCALLINTER:
+		fntype = n->left->type;
+		break;
+	}
+
+	ll = n->list;
+	if(n->list != nil && n->list->next == nil) {
+		a = n->list->n;
+		if(a->type->etype == TSTRUCT && a->type->funarg) {
+			// f(g()).
+			// Since f's arguments are g's results and
+			// all function results escape, we're done.
+			ll = nil;
+		}
+	}
+			
+	if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype) {
+		// Local function.  Incorporate into flow graph.
+
+		// Receiver.
+		if(n->op != OCALLFUNC)
+			escassign(fn->ntype->left->left, n->left->left);
+
+		for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
+			src = ll->n;
+			if(lr->n->isddd && !n->isddd) {
+				// Introduce ODDDARG node to represent ... allocation.
+				src = nod(ODDDARG, N, N);
+				src->escloopdepth = loopdepth;
+				src->lineno = n->lineno;
+				src->esc = EscNone;  // until we find otherwise
+				noesc = list(noesc, src);
+				n->right = src;
+			}
+			if(lr->n->left != N)
+				escassign(lr->n->left, src);
+			if(src != ll->n)
+				break;
+		}
+		// "..." arguments are untracked
+		for(; ll; ll=ll->next)
+			escassign(&theSink, ll->n);
+		return;
+	}
+
+	// Imported function.  Use the escape tags.
+	if(n->op != OCALLFUNC) {
+		t = getthisx(fntype)->type;
+		if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+			escassign(&theSink, n->left->left);
+	}
+	for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+		src = ll->n;
+		if(t->isddd && !n->isddd) {
+			// Introduce ODDDARG node to represent ... allocation.
+			src = nod(ODDDARG, N, N);
+			src->escloopdepth = loopdepth;
+			src->lineno = n->lineno;
+			src->esc = EscNone;  // until we find otherwise
+			noesc = list(noesc, src);
+			n->right = src;
+		}
+		if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+			escassign(&theSink, src);
+		if(src != ll->n)
+			break;
+		t = t->down;
+	}
+	// "..." arguments are untracked
+	for(; ll; ll=ll->next)
+		escassign(&theSink, ll->n);
+}
+
+// Store the link src->dst in dst, throwing out some quick wins.
+static void
+escflows(Node *dst, Node *src)
+{
+	if(dst == nil || src == nil || dst == src)
+		return;
+
+	// Don't bother building a graph for scalars.
+	if(src->type && !haspointers(src->type))
+		return;
+
+	if(debug['m']>2)
+		print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
+
+	if(dst->escflowsrc == nil) {
+		dsts = list(dsts, dst);
+		dstcount++;
+	}
+	edgecount++;
+
+	dst->escflowsrc = list(dst->escflowsrc, src);
+}
+
+// Whenever we hit a reference node, the level goes up by one, and whenever
+// we hit an OADDR, the level goes down by one. as long as we're on a level > 0
+// finding an OADDR just means we're following the upstream of a dereference,
+// so this address doesn't leak (yet).
+// If level == 0, it means the /value/ of this node can reach the root of this flood.
+// so if this node is an OADDR, it's argument should be marked as escaping iff
+// it's currfn/loopdepth are different from the flood's root.
+// Once an object has been moved to the heap, all of it's upstream should be considered
+// escaping to the global scope.
+static void
+escflood(Node *dst)
+{
+	NodeList *l;
+
+	switch(dst->op) {
+	case ONAME:
+	case OCLOSURE:
+		break;
+	default:
+		return;
+	}
+
+	if(debug['m']>1)
+		print("\nescflood:%d: dst %hN scope:%#S[%d]\n", walkgen, dst,
+		      (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
+		      dst->escloopdepth);
+
+	for(l = dst->escflowsrc; l; l=l->next) {
+		walkgen++;
+		escwalk(0, dst, l->n);
+	}
+}
+
+static void
+escwalk(int level, Node *dst, Node *src)
+{
+	NodeList *ll;
+	int leaks;
+
+	if(src->walkgen == walkgen)
+		return;
+	src->walkgen = walkgen;
+
+	if(debug['m']>1)
+		print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
+		      level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
+		      (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
+
+	pdepth++;
+
+	leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
+
+	switch(src->op) {
+	case ONAME:
+		if(src->class == PPARAM && leaks && src->esc == EscNone) {
+			src->esc = EscScope;
+			if(debug['m'])
+				warnl(src->lineno, "leaking param: %hN", src);
+		}
+		break;
+
+	case OADDR:
+		if(leaks) {
+			src->esc = EscHeap;
+			addrescapes(src->left);
+			if(debug['m'])
+				warnl(src->lineno, "%#N escapes to heap", src);
+		}
+		escwalk(level-1, dst, src->left);
+		break;
+
+	case OARRAYLIT:
+		if(isfixedarray(src->type))
+			break;
+		// fall through
+	case ODDDARG:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case OMAPLIT:
+	case ONEW:
+	case OCLOSURE:
+		if(leaks) {
+			src->esc = EscHeap;
+			if(debug['m'])
+				warnl(src->lineno, "%#N escapes to heap", src);
+		}
+		break;
+
+	case OINDEX:
+		if(isfixedarray(src->type))
+			break;
+		// fall through
+	case OSLICE:
+	case ODOTPTR:
+	case OINDEXMAP:
+	case OIND:
+		escwalk(level+1, dst, src->left);
+	}
+
+	for(ll=src->escflowsrc; ll; ll=ll->next)
+		escwalk(level, dst, ll->n);
+
+	pdepth--;
+}
+
+static void
+esctag(Node *func)
+{
+	Node *savefn;
+	NodeList *ll;
+	
+	// External functions must be assumed unsafe.
+	if(func->nbody == nil)
+		return;
+
+	savefn = curfn;
+	curfn = func;
+
+	for(ll=curfn->dcl; ll; ll=ll->next) {
+		if(ll->n->op != ONAME || ll->n->class != PPARAM)
+			continue;
+
+		switch (ll->n->esc) {
+		case EscNone:	// not touched by escflood
+			if(haspointers(ll->n->type)) // don't bother tagging for scalars
+				ll->n->paramfld->note = safetag;
+		case EscHeap:	// touched by escflood, moved to heap
+		case EscScope:	// touched by escflood, value leaves scope
+			break;
+		}
+	}
+
+	curfn = savefn;
+}
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
index 014f0c5..421afda 100644
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 #include	"y.tab.h"
 
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
index cb66921..a818dbc 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -7,11 +7,13 @@
  * mainly statements and control flow.
  */
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 static void	cgen_dcl(Node *n);
 static void	cgen_proc(Node *n, int proc);
-static void checkgoto(Node*, Node*);
+static void	checkgoto(Node*, Node*);
 
 static Label *labellist;
 static Label *lastlabel;
@@ -26,50 +28,93 @@ sysfunc(char *name)
 	return n;
 }
 
+/*
+ * the address of n has been taken and might be used after
+ * the current function returns.  mark any local vars
+ * as needing to move to the heap.
+ */
 void
-allocparams(void)
+addrescapes(Node *n)
 {
-	NodeList *l;
-	Node *n;
-	uint32 w;
-	Sym *s;
-	int lno;
+	char buf[100];
+	Node *oldfn;
+
+	switch(n->op) {
+	default:
+		// probably a type error already.
+		// dump("addrescapes", n);
+		break;
+
+	case ONAME:
+		if(n == nodfp)
+			break;
+
+		// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
+		// on PPARAM it means something different.
+		if(n->class == PAUTO && n->esc == EscNever)
+			break;
+
+		if(debug['s'] && n->esc != EscUnknown)
+			fatal("without escape analysis, only PAUTO's should have esc: %N", n);
 
-	if(stksize < 0)
-		fatal("allocparams not during code generation");
-
-	/*
-	 * allocate (set xoffset) the stack
-	 * slots for all automatics.
-	 * allocated starting at -w down.
-	 */
-	lno = lineno;
-	for(l=curfn->dcl; l; l=l->next) {
-		n = l->n;
-		if(n->op == ONAME && n->class == PHEAP-1) {
-			// heap address variable; finish the job
-			// started in addrescapes.
-			s = n->sym;
-			tempname(n, n->type);
-			n->sym = s;
+		switch(n->class) {
+		case PPARAMREF:
+			addrescapes(n->defn);
+			break;
+		case PPARAM:
+		case PPARAMOUT:
+			// if func param, need separate temporary
+			// to hold heap pointer.
+			// the function type has already been checked
+			// (we're in the function body)
+			// so the param already has a valid xoffset.
+
+			// expression to refer to stack copy
+			n->stackparam = nod(OPARAM, n, N);
+			n->stackparam->type = n->type;
+			n->stackparam->addable = 1;
+			if(n->xoffset == BADWIDTH)
+				fatal("addrescapes before param assignment");
+			n->stackparam->xoffset = n->xoffset;
+			// fallthrough
+
+		case PAUTO:
+			n->class |= PHEAP;
+			n->addable = 0;
+			n->ullman = 2;
+			n->xoffset = 0;
+
+			// create stack variable to hold pointer to heap
+			oldfn = curfn;
+			curfn = n->curfn;
+			n->heapaddr = temp(ptrto(n->type));
+			snprint(buf, sizeof buf, "&%S", n->sym);
+			n->heapaddr->sym = lookup(buf);
+			n->heapaddr->orig->sym = n->heapaddr->sym;
+			if(!debug['s'])
+				n->esc = EscHeap;
+			if(debug['m'])
+				print("%L: moved to heap: %hN\n", n->lineno, n);
+			curfn = oldfn;
+			break;
 		}
-		if(n->op != ONAME || n->class != PAUTO)
-			continue;
-		if (n->xoffset != BADWIDTH)
-			continue;
-		if(n->type == T)
-			continue;
-		dowidth(n->type);
-		w = n->type->width;
-		if(w >= MAXWIDTH)
-			fatal("bad width");
-		stksize += w;
-		stksize = rnd(stksize, n->type->align);
-		if(thechar == '5')
-			stksize = rnd(stksize, widthptr);
-		n->xoffset = -stksize;
+		break;
+
+	case OIND:
+	case ODOTPTR:
+		break;
+
+	case ODOT:
+	case OINDEX:
+		// ODOTPTR has already been introduced,
+		// so these are the non-pointer ODOT and OINDEX.
+		// In &x[0], if x is a slice, then x does not
+		// escape--the pointer inside x does, but that
+		// is always a heap pointer anyway.
+		if(!isslice(n->left->type))
+			addrescapes(n->left);
+		break;
 	}
-	lineno = lno;
 }
 
 void
@@ -197,7 +242,7 @@ stmtlabel(Node *n)
 	if(n->sym != S)
 	if((lab = n->sym->label) != L)
 	if(lab->def != N)
-	if(lab->def->right == n)
+	if(lab->def->defn == n)
 		return lab;
 	return L;
 }
@@ -227,7 +272,6 @@ gen(Node *n)
 	if(n == N)
 		goto ret;
 
-	p3 = pc;	// save pc for loop labels
 	if(n->ninit)
 		genlist(n->ninit);
 
@@ -266,13 +310,13 @@ gen(Node *n)
 		if(lab->labelpc == P)
 			lab->labelpc = pc;
 
-		if(n->right) {
-			switch(n->right->op) {
+		if(n->defn) {
+			switch(n->defn->op) {
 			case OFOR:
 			case OSWITCH:
 			case OSELECT:
 				// so stmtlabel can find the label
-				n->right->sym = lab->sym;
+				n->defn->sym = lab->sym;
 			}
 		}
 		break;
@@ -486,7 +530,7 @@ cgen_callmeth(Node *n, int proc)
 /*
  * generate code to start new proc running call n.
  */
-void
+static void
 cgen_proc(Node *n, int proc)
 {
 	switch(n->left->op) {
@@ -598,15 +642,12 @@ cgen_as(Node *nl, Node *nr)
 	Type *tl;
 	int iszer;
 
-	if(nl == N)
-		return;
-
 	if(debug['g']) {
 		dump("cgen_as", nl);
 		dump("cgen_as = ", nr);
 	}
 
-	if(isblank(nl)) {
+	if(nl == N || isblank(nl)) {
 		cgen_discard(nr);
 		return;
 	}
@@ -748,12 +789,8 @@ tempname(Node *nn, Type *t)
 {
 	Node *n;
 	Sym *s;
-	uint32 w;
 
-	if(stksize < 0)
-		fatal("tempname not during code generation");
-
-	if (curfn == N)
+	if(curfn == N)
 		fatal("no curfn for tempname");
 
 	if(t == T) {
@@ -772,19 +809,21 @@ tempname(Node *nn, Type *t)
 	n->class = PAUTO;
 	n->addable = 1;
 	n->ullman = 1;
-	n->noescape = 1;
+	n->esc = EscNever;
 	n->curfn = curfn;
 	curfn->dcl = list(curfn->dcl, n);
 
 	dowidth(t);
-	w = t->width;
-	stksize += w;
-	stksize = rnd(stksize, t->align);
-	if(thechar == '5')
-		stksize = rnd(stksize, widthptr);
-	n->xoffset = -stksize;
-
-	//	print("\ttmpname (%d): %N\n", stksize, n);
-
+	n->xoffset = 0;
 	*nn = *n;
 }
+
+Node*
+temp(Type *t)
+{
+	Node *n;
+	
+	n = nod(OXXX, N, N);
+	tempname(n, t);
+	return n;
+}
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index da0fb51..f727994 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include	<u.h>
-#include	<libc.h>
 #include	<bio.h>
 
 #undef OAPPEND
@@ -155,7 +153,6 @@ struct	Type
 {
 	uchar	etype;
 	uchar	chan;
-	uchar	recur;		// to detect loops
 	uchar	trecur;		// to detect loops
 	uchar	printed;
 	uchar	embedded;	// TFIELD embedded type
@@ -203,8 +200,50 @@ struct	Type
 };
 #define	T	((Type*)0)
 
+typedef struct InitEntry InitEntry;
+typedef struct InitPlan InitPlan;
+
+struct InitEntry
+{
+	vlong xoffset;  // struct, array only
+	Node *key;  // map only
+	Node *expr;
+};
+
+struct InitPlan
+{
+	vlong lit;  // bytes of initialized non-zero literals
+	vlong zero;  // bytes of zeros
+	vlong expr;  // bytes of run-time computed expressions
+
+	InitEntry *e;
+	int len;
+	int cap;
+};
+
+enum
+{
+	EscUnknown,
+	EscHeap,
+	EscScope,
+	EscNone,
+	EscNever,
+};
+
 struct	Node
 {
+	// Tree structure.
+	// Generic recursive walks should follow these fields.
+	Node*	left;
+	Node*	right;
+	Node*	ntest;
+	Node*	nincr;
+	NodeList*	ninit;
+	NodeList*	nbody;
+	NodeList*	nelse;
+	NodeList*	list;
+	NodeList*	rlist;
+
 	uchar	op;
 	uchar	ullman;		// sethi/ullman number
 	uchar	addable;	// type of addressability - 0 is not addressable
@@ -215,41 +254,24 @@ struct	Node
 	uchar	embedded;	// ODCLFIELD embedded type
 	uchar	colas;		// OAS resulting from :=
 	uchar	diag;		// already printed error about this
-	uchar	noescape;	// ONAME never move to heap
+	uchar	esc;		// EscXXX
 	uchar	funcdepth;
 	uchar	builtin;	// built-in name, like len or close
 	uchar	walkdef;
 	uchar	typecheck;
 	uchar	local;
+	uchar	dodata;
 	uchar	initorder;
-	uchar	dodata;		// compile literal assignment as data statement
 	uchar	used;
 	uchar	isddd;
-	uchar	pun;		// don't registerize variable ONAME
 	uchar	readonly;
 	uchar	implicit;	// don't show in printout
 
 	// most nodes
-	Node*	left;
-	Node*	right;
 	Type*	type;
 	Type*	realtype;	// as determined by typecheck
-	NodeList*	list;
-	NodeList*	rlist;
 	Node*	orig;		// original form, for printing, and tracking copies of ONAMEs
 
-	// for-body
-	NodeList*	ninit;
-	Node*	ntest;
-	Node*	nincr;
-	NodeList*	nbody;
-
-	// if-body
-	NodeList*	nelse;
-
-	// cases
-	Node*	ncase;
-
 	// func
 	Node*	nname;
 	Node*	shortname;
@@ -263,9 +285,10 @@ struct	Node
 
 	// ONAME
 	Node*	ntype;
-	Node*	defn;
+	Node*	defn;	// ONAME: initializing assignment; OLABEL: labeled statement
 	Node*	pack;	// real package for import . names
 	Node*	curfn;	// function for local variables
+	Type*	paramfld; // TFIELD for this PPARAM
 
 	// ONAME func param with PHEAP
 	Node*	heapaddr;	// temp holding heap address of param
@@ -278,6 +301,13 @@ struct	Node
 
 	// OPACK
 	Pkg*	pkg;
+	
+	// OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+	InitPlan*	initplan;
+
+	// Escape analysis.
+	NodeList* escflowsrc;	// flow(this, src)
+	int	escloopdepth;	// -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
 
 	Sym*	sym;		// various
 	int32	vargen;		// unique name for OTYPE/ONAME
@@ -287,9 +317,25 @@ struct	Node
 	int32	stkdelta;	// offset added by stack frame compaction phase.
 	int32	ostk;
 	int32	iota;
+	uint32	walkgen;
 };
 #define	N	((Node*)0)
-EXTERN	int32	walkgen;
+
+/*
+ * Every node has a walkgen field.
+ * If you want to do a traversal of a node graph that
+ * might contain duplicates and want to avoid
+ * visiting the same nodes twice, increment walkgen
+ * before starting.  Then before processing a node, do
+ *
+ *	if(n->walkgen == walkgen)
+ *		return;
+ *	n->walkgen = walkgen;
+ *
+ * Such a walk cannot call another such walk recursively,
+ * because of the use of the global walkgen.
+ */
+EXTERN	uint32	walkgen;
 
 struct	NodeList
 {
@@ -374,7 +420,6 @@ enum
 	OADDR,
 	OANDAND,
 	OAPPEND,
-	OARRAY,
 	OARRAYBYTESTR, OARRAYRUNESTR,
 	OSTRARRAYBYTE, OSTRARRAYRUNE,
 	OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
@@ -444,6 +489,7 @@ enum
 
 	// misc
 	ODDD,
+	ODDDARG,
 
 	// for back ends
 	OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
@@ -561,7 +607,6 @@ typedef	struct	Var	Var;
 struct	Var
 {
 	vlong	offset;
-	Sym*	sym;
 	Sym*	gotype;
 	Node*	node;
 	int	width;
@@ -644,6 +689,7 @@ struct	Magic
 };
 
 typedef struct	Prog Prog;
+#pragma incomplete Prog
 
 struct	Label
 {
@@ -727,6 +773,7 @@ EXTERN	Pkg*	phash[128];
 EXTERN	int	tptr;		// either TPTR32 or TPTR64
 extern	char*	runtimeimport;
 extern	char*	unsafeimport;
+EXTERN	char*	myimportpath;
 EXTERN	Idir*	idirs;
 
 EXTERN	Type*	types[NTYPE];
@@ -911,6 +958,11 @@ Node*	typenod(Type *t);
 NodeList*	variter(NodeList *vl, Node *t, NodeList *el);
 
 /*
+ *	esc.c
+ */
+void	escapes(void);
+
+/*
  *	export.c
  */
 void	autoexport(Node *n, int ctxt);
@@ -927,7 +979,7 @@ Type*	pkgtype(Sym *s);
 /*
  *	gen.c
  */
-void	allocparams(void);
+void	addrescapes(Node *n);
 void	cgen_as(Node *nl, Node *nr);
 void	cgen_callmeth(Node *n, int proc);
 void	clearlabels(void);
@@ -937,6 +989,7 @@ void	gen(Node *n);
 void	genlist(NodeList *l);
 Node*	sysfunc(char *name);
 void	tempname(Node *n, Type *t);
+Node*	temp(Type*);
 
 /*
  *	init.c
@@ -1050,6 +1103,7 @@ void	dumptypestructs(void);
 Type*	methodfunc(Type *f, Type*);
 Node*	typename(Type *t);
 Sym*	typesym(Type *t);
+int	haspointers(Type *t);
 
 /*
  *	select.c
@@ -1170,6 +1224,7 @@ uint32	typehash(Type *t);
 void	ullmancalc(Node *n);
 void	umagic(Magic *m);
 void	warn(char *fmt, ...);
+void	warnl(int line, char *fmt, ...);
 void	yyerror(char *fmt, ...);
 void	yyerrorl(int line, char *fmt, ...);
 
@@ -1274,6 +1329,24 @@ Prog*	unpatch(Prog*);
 void	zfile(Biobuf *b, char *p, int n);
 void	zhist(Biobuf *b, int line, vlong offset);
 void	zname(Biobuf *b, Sym *s, int t);
-void	data(void);
-void	text(void);
 
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Mpint*
+#pragma	varargck	type	"D"	Addr*
+#pragma	varargck	type	"lD"	Addr*
+#pragma	varargck	type	"E"	int
+#pragma	varargck	type	"F"	Mpflt*
+#pragma	varargck	type	"J"	Node*
+#pragma	varargck	type	"L"	int
+#pragma	varargck	type	"L"	uint
+#pragma	varargck	type	"N"	Node*
+#pragma	varargck	type	"O"	uint
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"Q"	Bits
+#pragma	varargck	type	"R"	int
+#pragma	varargck	type	"S"	Sym*
+#pragma	varargck	type	"lS"	Sym*
+#pragma	varargck	type	"T"	Type*
+#pragma	varargck	type	"lT"	Type*
+#pragma	varargck	type	"Y"	char*
+#pragma	varargck	type	"Z"	Strlit*
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index 4c7fe60..a5e92bd 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -18,7 +18,9 @@
  */
 
 %{
+#include <u.h>
 #include <stdio.h>	/* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
 #include "go.h"
 
 static void fixlbrace(int);
@@ -241,11 +243,11 @@ import_package:
 			importpkg->name = $2->name;
 			pkglookup($2->name, nil)->npkg++;
 		} else if(strcmp(importpkg->name, $2->name) != 0)
-			yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path);
+			yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, $2->name, importpkg->path);
 		importpkg->direct = 1;
 		
 		if(safemode && !curio.importsafe)
-			yyerror("cannot import unsafe package %Z", importpkg->path);
+			yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
 	}
 
 import_safety:
@@ -1494,7 +1496,7 @@ non_dcl_stmt:
 	{
 		NodeList *l;
 
-		$1->right = $4;
+		$1->defn = $4;
 		l = list1($1);
 		if($4)
 			l = list(l, $4);
@@ -1684,7 +1686,11 @@ hidden_import:
 			p->name = $2->name;
 			pkglookup($2->name, nil)->npkg++;
 		} else if(strcmp(p->name, $2->name) != 0)
-			yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path);
+			yyerror("conflicting names %s and %s for package \"%Z\"", p->name, $2->name, p->path);
+		if(!incannedimport && myimportpath != nil && strcmp($3.u.sval->s, myimportpath) == 0) {
+			yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, $3.u.sval);
+			errorexit();
+		}
 	}
 |	LVAR hidden_pkg_importsym hidden_type ';'
 	{
diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c
index 8818db0..da69e41 100644
--- a/src/cmd/gc/init.c
+++ b/src/cmd/gc/init.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index 29b6d27..0290fb1 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#define		EXTERN
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 #include	"y.tab.h"
 #include	<ar.h>
@@ -64,7 +65,7 @@ yy_isalnum(int c)
 #define isalpha use_yy_isalpha_instead_of_isalpha
 #define isalnum use_yy_isalnum_instead_of_isalnum
 
-#define	DBG	if(!debug['x']);else print
+#define	DBG	if(!debug['x']){}else print
 enum
 {
 	EOF		= -1,
@@ -77,22 +78,29 @@ usage(void)
 	print("flags:\n");
 	// -A is allow use of "any" type, for bootstrapping
 	print("  -I DIR search for packages in DIR\n");
+	print("  -L show full path in file:line prints\n");
+	print("  -N disable optimizer\n");
+	print("  -S print the assembly language\n");
+	print("  -V print the compiler version\n");
 	print("  -d print declarations\n");
 	print("  -e no limit on number of errors printed\n");
 	print("  -f print stack frame structure\n");
 	print("  -h panic on an error\n");
+	print("  -m print about moves to heap\n");
 	print("  -o file specify output file\n");
-	print("  -S print the assembly language\n");
-	print("  -V print the compiler version\n");
+	print("  -p assumed import path for this code\n");
+	print("  -s disable escape analysis\n");
 	print("  -u disable package unsafe\n");
 	print("  -w print the parse tree after typing\n");
 	print("  -x print lex tokens\n");
-	exit(0);
+	exits(0);
 }
 
 void
 fault(int s)
 {
+	USED(s);
+
 	// If we've already complained about things
 	// in the program, don't bother complaining
 	// about the seg fault too; let the user clean up
@@ -108,9 +116,11 @@ main(int argc, char *argv[])
 	int i, c;
 	NodeList *l;
 	char *p;
-	
+
+#ifdef	SIGBUS	
 	signal(SIGBUS, fault);
 	signal(SIGSEGV, fault);
+#endif
 
 	localpkg = mkpkg(strlit(""));
 	localpkg->prefix = "\"\"";
@@ -145,6 +155,10 @@ main(int argc, char *argv[])
 	case 'o':
 		outfile = EARGF(usage());
 		break;
+	
+	case 'p':
+		myimportpath = EARGF(usage());
+		break;
 
 	case 'I':
 		addidir(EARGF(usage()));
@@ -156,7 +170,7 @@ main(int argc, char *argv[])
 
 	case 'V':
 		print("%cg version %s\n", thechar, getgoversion());
-		exit(0);
+		exits(0);
 	} ARGEND
 
 	if(argc < 1)
@@ -236,24 +250,24 @@ main(int argc, char *argv[])
 	if(debug['f'])
 		frame(1);
 
-	// Process top-level declarations in four phases.
+	// Process top-level declarations in phases.
 	// Phase 1: const, type, and names and types of funcs.
 	//   This will gather all the information about types
 	//   and methods but doesn't depend on any of it.
-	// Phase 2: Variable assignments.
-	//   To check interface assignments, depends on phase 1.
-	// Phase 3: Type check function bodies.
-	// Phase 4: Compile function bodies.
 	defercheckwidth();
 	for(l=xtop; l; l=l->next)
 		if(l->n->op != ODCL && l->n->op != OAS)
 			typecheck(&l->n, Etop);
+
+	// Phase 2: Variable assignments.
+	//   To check interface assignments, depends on phase 1.
 	for(l=xtop; l; l=l->next)
 		if(l->n->op == ODCL || l->n->op == OAS)
 			typecheck(&l->n, Etop);
 	resumetypecopy();
 	resumecheckwidth();
 
+	// Phase 3: Type check function bodies.
 	for(l=xtop; l; l=l->next) {
 		if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
 			curfn = l->n;
@@ -269,6 +283,11 @@ main(int argc, char *argv[])
 	if(nsavederrors+nerrors)
 		errorexit();
 
+	// Phase 3b: escape analysis.
+	if(!debug['s'])
+		escapes();
+
+	// Phase 4: Compile function bodies.
 	for(l=xtop; l; l=l->next)
 		if(l->n->op == ODCLFUNC)
 			funccompile(l->n, 0);
@@ -276,6 +295,7 @@ main(int argc, char *argv[])
 	if(nsavederrors+nerrors == 0)
 		fninit(xtop);
 
+	// Phase 4b: Compile all closures.
 	while(closures) {
 		l = closures;
 		closures = nil;
@@ -284,6 +304,7 @@ main(int argc, char *argv[])
 		}
 	}
 
+	// Phase 5: check external declarations.
 	for(l=externdcl; l; l=l->next)
 		if(l->n->op == ONAME)
 			typecheck(&l->n, Erv);
@@ -297,7 +318,7 @@ main(int argc, char *argv[])
 		errorexit();
 
 	flusherrors();
-	exit(0);
+	exits(0);
 	return 0;
 }
 
@@ -400,8 +421,8 @@ findpkg(Strlit *name)
 	}
 
 	// local imports should be canonicalized already.
-	// don't want to see "container/../container/vector"
-	// as different from "container/vector".
+	// don't want to see "encoding/../encoding/base64"
+	// as different from "encoding/base64".
 	q = mal(name->len+1);
 	memmove(q, name->s, name->len);
 	q[name->len] = '\0';
@@ -440,6 +461,8 @@ importfile(Val *f, int line)
 	Strlit *path;
 	char *cleanbuf;
 
+	USED(line);
+
 	// TODO(rsc): don't bother reloading imports more than once?
 
 	if(f->ctype != CTSTR) {
@@ -461,6 +484,11 @@ importfile(Val *f, int line)
 		errorexit();
 	}
 
+	if(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) {
+		yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval);
+		errorexit();
+	}
+
 	if(strcmp(f->u.sval->s, "unsafe") == 0) {
 		if(safemode) {
 			yyerror("cannot import package unsafe");
@@ -482,14 +510,14 @@ importfile(Val *f, int line)
 	}
 
 	if(!findpkg(path)) {
-		yyerror("can't find import: %Z", f->u.sval);
+		yyerror("can't find import: \"%Z\"", f->u.sval);
 		errorexit();
 	}
 	importpkg = mkpkg(path);
 
 	imp = Bopen(namebuf, OREAD);
 	if(imp == nil) {
-		yyerror("can't open import: %Z: %r", f->u.sval);
+		yyerror("can't open import: \"%Z\": %r", f->u.sval);
 		errorexit();
 	}
 	file = strdup(namebuf);
@@ -546,7 +574,7 @@ importfile(Val *f, int line)
 			continue;
 		return;
 	}
-	yyerror("no import in: %Z", f->u.sval);
+	yyerror("no import in \"%Z\"", f->u.sval);
 	unimportfile();
 }
 
@@ -665,7 +693,6 @@ l0:
 			ep = lexbuf+sizeof lexbuf;
 			*cp++ = c;
 			c = c1;
-			c1 = 0;
 			goto casedot;
 		}
 		if(c1 == '.') {
@@ -1056,7 +1083,6 @@ talph:
 	return s->lexical;
 
 tnum:
-	c1 = 0;
 	cp = lexbuf;
 	ep = lexbuf+sizeof lexbuf;
 	if(c != '0') {
@@ -1740,7 +1766,6 @@ lexfini(void)
 	}
 	
 	nodfp = nod(ONAME, N, N);
-	nodfp->noescape = 1;
 	nodfp->type = types[TINT32];
 	nodfp->xoffset = 0;
 	nodfp->class = PPARAM;
@@ -1923,7 +1948,7 @@ mkpackage(char* pkgname)
 					// errors if a conflicting top-level name is
 					// introduced by a different file.
 					if(!s->def->used && !nsyntaxerrors)
-						yyerrorl(s->def->lineno, "imported and not used: %Z", s->def->pkg->path);
+						yyerrorl(s->def->lineno, "imported and not used: \"%Z\"", s->def->pkg->path);
 					s->def = N;
 					continue;
 				}
@@ -1931,7 +1956,7 @@ mkpackage(char* pkgname)
 					// throw away top-level name left over
 					// from previous import . "x"
 					if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) {
-						yyerrorl(s->def->pack->lineno, "imported and not used: %Z", s->def->pack->pkg->path);
+						yyerrorl(s->def->pack->lineno, "imported and not used: \"%Z\"", s->def->pack->pkg->path);
 						s->def->pack->used = 1;
 					}
 					s->def = N;
diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c
index 7cea1a6..5856aab 100644
--- a/src/cmd/gc/md5.c
+++ b/src/cmd/gc/md5.c
@@ -5,6 +5,8 @@
 // 64-bit MD5 (does full MD5 but returns 64 bits only).
 // Translation of ../../pkg/crypto/md5/md5*.go.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 #include "md5.h"
 
diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c
index 6cd4e25..2b7307e 100644
--- a/src/cmd/gc/mparith1.c
+++ b/src/cmd/gc/mparith1.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
 /// uses arithmetic
diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c
index 4032550..71cc29c 100644
--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
 //
@@ -349,6 +351,7 @@ mporfixfix(Mpint *a, Mpint *b)
 	int i;
 	long x, *a1, *b1;
 
+	x = 0;
 	if(a->ovf || b->ovf) {
 		yyerror("ovf in mporfixfix");
 		mpmovecfix(a, 0);
@@ -383,6 +386,7 @@ mpandfixfix(Mpint *a, Mpint *b)
 	int i;
 	long x, *a1, *b1;
 
+	x = 0;
 	if(a->ovf || b->ovf) {
 		yyerror("ovf in mpandfixfix");
 		mpmovecfix(a, 0);
@@ -417,6 +421,7 @@ mpandnotfixfix(Mpint *a, Mpint *b)
 	int i;
 	long x, *a1, *b1;
 
+	x = 0;
 	if(a->ovf || b->ovf) {
 		yyerror("ovf in mpandnotfixfix");
 		mpmovecfix(a, 0);
@@ -451,6 +456,7 @@ mpxorfixfix(Mpint *a, Mpint *b)
 	int i;
 	long x, *a1, *b1;
 
+	x = 0;
 	if(a->ovf || b->ovf) {
 		yyerror("ovf in mporfixfix");
 		mpmovecfix(a, 0);
diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c
index b11a4f5..0c6c5a0 100644
--- a/src/cmd/gc/mparith3.c
+++ b/src/cmd/gc/mparith3.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
 /*
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
index f34fc76..d6fe6f6 100644
--- a/src/cmd/gc/obj.c
+++ b/src/cmd/gc/obj.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
@@ -29,10 +31,6 @@ dumpobj(void)
 
 	outhist(bout);
 
-	// add nil plist w AEND to catch
-	// auto-generated trampolines, data
-	newplist();
-
 	dumpglobls();
 	dumptypestructs();
 	dumpdata();
@@ -127,6 +125,7 @@ static void
 outhist(Biobuf *b)
 {
 	Hist *h;
+	int i, depth = 0;
 	char *p, ds[] = {'c', ':', '/', 0};
 
 	for(h = hist; h != H; h = h->link) {
@@ -156,13 +155,21 @@ outhist(Biobuf *b)
 					outzfile(b, p+1);
 				} else {
 					// relative name, like dir/file.go
-					if(h->offset == 0 && pathname && pathname[0] == '/') {
+					if(h->offset >= 0 && pathname && pathname[0] == '/') {
 						zfile(b, "/", 1);	// leading "/"
 						outzfile(b, pathname+1);
 					}
 					outzfile(b, p);
 				}
 			}
+			if(h->offset > 0) {
+				//line directive
+				depth++;
+			}
+		} else if(depth > 0) {
+			for(i = 0; i < depth; i++)
+				zhist(b, h->line, h->offset);
+			depth = 0;
 		}
 		zhist(b, h->line, h->offset);
 	}
@@ -259,7 +266,7 @@ stringsym(char *s, int len)
 		tmp.lit.len = len;
 		memmove(tmp.lit.s, s, len);
 		tmp.lit.s[len] = '\0';
-		snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp);
+		snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit);
 		pkg = gostringpkg;
 	}
 	sym = pkglookup(namebuf, pkg);
@@ -268,8 +275,7 @@ stringsym(char *s, int len)
 	if(sym->flags & SymUniq)
 		return sym;
 	sym->flags |= SymUniq;
-	
-	data();
+
 	off = 0;
 	
 	// string header
@@ -286,7 +292,6 @@ stringsym(char *s, int len)
 	off = duint8(sym, off, 0);  // terminating NUL for runtime
 	off = (off+widthptr-1)&~(widthptr-1);  // round to pointer alignment
 	ggloblsym(sym, off, 1);
-	text();
-	
+
 	return sym;	
 }
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
index abe8ea8..d16481b 100644
--- a/src/cmd/gc/pgen.c
+++ b/src/cmd/gc/pgen.c
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"gg.h"
 #include	"opt.h"
 
-static void compactframe(Prog* p);
+static void allocauto(Prog* p);
 
 void
 compile(Node *fn)
@@ -58,8 +60,6 @@ compile(Node *fn)
 	if(nerrors != 0)
 		goto ret;
 
-	allocparams();
-
 	continpc = P;
 	breakpc = P;
 
@@ -113,9 +113,9 @@ compile(Node *fn)
 	}
 
 	oldstksize = stksize;
-	compactframe(ptxt);
+	allocauto(ptxt);
 	if(0)
-		print("compactframe: %ld to %ld\n", oldstksize, stksize);
+		print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
 
 	defframe(ptxt);
 
@@ -145,13 +145,13 @@ cmpstackvar(Node *a, Node *b)
 
 // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
 static void
-compactframe(Prog* ptxt)
+allocauto(Prog* ptxt)
 {
 	NodeList *ll;
 	Node* n;
 	vlong w;
 
-	if (stksize == 0)
+	if(curfn->dcl == nil)
 		return;
 
 	// Mark the PAUTO's unused.
@@ -188,6 +188,7 @@ compactframe(Prog* ptxt)
 		if (n->class != PAUTO || n->op != ONAME)
 			continue;
 
+		dowidth(n->type);
 		w = n->type->width;
 		if(w >= MAXWIDTH || w < 0)
 			fatal("bad width");
diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c
index 5913e84..37e3e7a 100644
--- a/src/cmd/gc/print.c
+++ b/src/cmd/gc/print.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 enum
@@ -139,6 +141,10 @@ exprfmt(Fmt *f, Node *n, int prec)
 		fmtprint(f, "(%#N)", n->left);
 		break;
 
+	case ODDDARG:
+		fmtprint(f, "... argument");
+		break;
+
 	case OREGISTER:
 		fmtprint(f, "%R", n->val.u.reg);
 		break;
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
index 5ce693a..5cbafd8 100644
--- a/src/cmd/gc/range.c
+++ b/src/cmd/gc/range.c
@@ -6,6 +6,8 @@
  * range
  */
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 void
@@ -111,8 +113,6 @@ walkrange(Node *n)
 	}
 
 	v1 = n->list->n;
-	hv1 = N;
-
 	v2 = N;
 	if(n->list->next)
 		v2 = n->list->next->n;
@@ -123,8 +123,7 @@ walkrange(Node *n)
 		// no need to make a potentially expensive copy.
 		ha = a;
 	} else {
-		ha = nod(OXXX, N, N);
-		tempname(ha, a->type);
+		ha = temp(a->type);
 		init = list(init, nod(OAS, ha, a));
 	}
 
@@ -133,17 +132,14 @@ walkrange(Node *n)
 		fatal("walkrange");
 
 	case TARRAY:
-		hv1 = nod(OXXX, N, n);
-		tempname(hv1, types[TINT]);
-		hn = nod(OXXX, N, N);
-		tempname(hn, types[TINT]);
+		hv1 = temp(types[TINT]);
+		hn = temp(types[TINT]);
 		hp = nil;
 
 		init = list(init, nod(OAS, hv1, N));
 		init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
 		if(v2) {
-			hp = nod(OXXX, N, N);
-			tempname(hp, ptrto(n->type->type));
+			hp = temp(ptrto(n->type->type));
 			tmp = nod(OINDEX, ha, nodintconst(0));
 			tmp->etype = 1;	// no bounds check
 			init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
@@ -168,8 +164,7 @@ walkrange(Node *n)
 		th = typ(TARRAY);
 		th->type = ptrto(types[TUINT8]);
 		th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr;
-		hit = nod(OXXX, N, N);
-		tempname(hit, th);
+		hit = temp(th);
 
 		fn = syslook("mapiterinit", 1);
 		argtype(fn, t->down);
@@ -200,10 +195,8 @@ walkrange(Node *n)
 		break;
 
 	case TCHAN:
-		hv1 = nod(OXXX, N, n);
-		tempname(hv1, t->type);
-		hb = nod(OXXX, N, N);
-		tempname(hb, types[TBOOL]);
+		hv1 = temp(t->type);
+		hb = temp(types[TBOOL]);
 
 		n->ntest = nod(ONE, hb, nodbool(0));
 		a = nod(OAS2RECV, N, N);
@@ -215,18 +208,15 @@ walkrange(Node *n)
 		break;
 
 	case TSTRING:
-		ohv1 = nod(OXXX, N, N);
-		tempname(ohv1, types[TINT]);
+		ohv1 = temp(types[TINT]);
 
-		hv1 = nod(OXXX, N, N);
-		tempname(hv1, types[TINT]);
+		hv1 = temp(types[TINT]);
 		init = list(init, nod(OAS, hv1, N));
 
 		if(v2 == N)
 			a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
 		else {
-			hv2 = nod(OXXX, N, N);
-			tempname(hv2, types[TINT]);
+			hv2 = temp(types[TINT]);
 			a = nod(OAS2, N, N);
 			a->list = list(list1(hv1), hv2);
 			fn = syslook("stringiter2", 0);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 810787d..ca7d08e 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
@@ -140,7 +142,6 @@ methods(Type *t)
 	Type *f, *mt, *it, *this;
 	Sig *a, *b;
 	Sym *method;
-	Prog *oldlist;
 
 	// named method type
 	mt = methtype(t);
@@ -156,7 +157,6 @@ methods(Type *t)
 	// make list of methods for t,
 	// generating code if necessary.
 	a = nil;
-	oldlist = nil;
 	for(f=mt->xmethod; f; f=f->down) {
 		if(f->type->etype != TFUNC)
 			continue;
@@ -195,8 +195,6 @@ methods(Type *t)
 		if(!(a->isym->flags & SymSiggen)) {
 			a->isym->flags |= SymSiggen;
 			if(!eqtype(this, it) || this->width < types[tptr]->width) {
-				if(oldlist == nil)
-					oldlist = pc;
 				// Is okay to call genwrapper here always,
 				// but we can generate more efficient code
 				// using genembedtramp if all that is necessary
@@ -212,8 +210,6 @@ methods(Type *t)
 		if(!(a->tsym->flags & SymSiggen)) {
 			a->tsym->flags |= SymSiggen;
 			if(!eqtype(this, t)) {
-				if(oldlist == nil)
-					oldlist = pc;
 				if(isptr[t->etype] && isptr[this->etype]
 				&& f->embedded && !isifacemethod(f->type))
 					genembedtramp(t, f, a->tsym, 0);
@@ -223,16 +219,6 @@ methods(Type *t)
 		}
 	}
 
-	// restore data output
-	if(oldlist) {
-		// old list ended with AEND; change to ANOP
-		// so that the trampolines that follow can be found.
-		nopout(oldlist);
-
-		// start new data list
-		newplist();
-	}
-
 	return lsort(a, sigcmp);
 }
 
@@ -245,11 +231,9 @@ imethods(Type *t)
 	Sig *a, *all, *last;
 	Type *f;
 	Sym *method, *isym;
-	Prog *oldlist;
 
 	all = nil;
 	last = nil;
-	oldlist = nil;
 	for(f=t->type; f; f=f->down) {
 		if(f->etype != TFIELD)
 			fatal("imethods: not field");
@@ -287,21 +271,9 @@ imethods(Type *t)
 		isym = methodsym(method, t, 0);
 		if(!(isym->flags & SymSiggen)) {
 			isym->flags |= SymSiggen;
-			if(oldlist == nil)
-				oldlist = pc;
 			genwrapper(t, f, isym, 0);
 		}
 	}
-
-	if(oldlist) {
-		// old list ended with AEND; change to ANOP
-		// so that the trampolines that follow can be found.
-		nopout(oldlist);
-
-		// start new data list
-		newplist();
-	}
-
 	return all;
 }
 
@@ -528,7 +500,7 @@ typestruct(Type *t)
 	return pkglookup(name, typepkg);
 }
 
-static int
+int
 haspointers(Type *t)
 {
 	Type *t1;
@@ -584,7 +556,6 @@ dcommontype(Sym *s, int ot, Type *t)
 
 	dowidth(t);
 	
-	sptr = nil;
 	if(t->sym != nil && !isptr[t->etype])
 		sptr = dtypesym(ptrto(t));
 	else
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
index 909ad3a..8ace1d4 100644
--- a/src/cmd/gc/select.c
+++ b/src/cmd/gc/select.c
@@ -6,6 +6,8 @@
  * select
  */
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 void
@@ -59,7 +61,7 @@ typecheckselect(Node *sel)
 				break;
 
 			case OAS2RECV:
-				// convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
+				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
 				if(n->right->op != ORECV) {
 					yyerror("select assignment must have receive on right hand side");
 					break;
@@ -73,6 +75,7 @@ typecheckselect(Node *sel)
 			case ORECV:
 				// convert <-c into OSELRECV(N, <-c)
 				n = nod(OSELRECV, N, n);
+				n->typecheck = 1;
 				ncase->left = n;
 				break;
 
@@ -191,8 +194,7 @@ walkselect(Node *sel)
 					n->ntest->etype = 1;  // pointer does not escape
 					typecheck(&n->ntest, Erv);
 				} else {
-					tmp = nod(OXXX, N, N);
-					tempname(tmp, types[TBOOL]);
+					tmp = temp(types[TBOOL]);
 					a = nod(OADDR, tmp, N);
 					a->etype = 1;  // pointer does not escape
 					typecheck(&a, Erv);
@@ -212,8 +214,7 @@ walkselect(Node *sel)
 				n->left->etype = 1;  // pointer does not escape
 				typecheck(&n->left, Erv);
 			} else {
-				tmp = nod(OXXX, N, N);
-				tempname(tmp, ch->type->type);
+				tmp = temp(ch->type->type);
 				a = nod(OADDR, tmp, N);
 				a->etype = 1;  // pointer does not escape
 				typecheck(&a, Erv);
@@ -284,8 +285,7 @@ walkselect(Node *sel)
 
 	// generate sel-struct
 	setlineno(sel);
-	var = nod(OXXX, N, N);
-	tempname(var, ptrto(types[TUINT8]));
+	var = temp(ptrto(types[TUINT8]));
 	r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
 	typecheck(&r, Etop);
 	init = list(init, r);
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
index 917e2ae..4550577 100644
--- a/src/cmd/gc/sinit.c
+++ b/src/cmd/gc/sinit.c
@@ -6,11 +6,24 @@
  * static initialization
  */
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
+enum
+{
+	InitNotStarted = 0,
+	InitDone = 1,
+	InitPending = 2,
+};
+
+static int iszero(Node*);
+static void initplan(Node*);
 static NodeList *initlist;
 static void init2(Node*, NodeList**);
 static void init2list(NodeList*, NodeList**);
+static int staticinit(Node*, NodeList**);
+static Node *staticname(Type*, int);
 
 static void
 init1(Node *n, NodeList **out)
@@ -31,16 +44,16 @@ init1(Node *n, NodeList **out)
 	case PFUNC:
 		break;
 	default:
-		if(isblank(n) && n->defn != N && !n->defn->initorder) {
-			n->defn->initorder = 1;
+		if(isblank(n) && n->defn != N && n->defn->initorder == InitNotStarted) {
+			n->defn->initorder = InitDone;
 			*out = list(*out, n->defn);
 		}
 		return;
 	}
 
-	if(n->initorder == 1)
+	if(n->initorder == InitDone)
 		return;
-	if(n->initorder == 2) {
+	if(n->initorder == InitPending) {
 		if(n->class == PFUNC)
 			return;
 		
@@ -63,7 +76,7 @@ init1(Node *n, NodeList **out)
 		print("\t%L %S\n", n->lineno, n->sym);
 		errorexit();
 	}
-	n->initorder = 2;
+	n->initorder = InitPending;
 	l = malloc(sizeof *l);
 	l->next = initlist;
 	l->n = n;
@@ -84,20 +97,38 @@ init1(Node *n, NodeList **out)
 		case OAS:
 			if(n->defn->left != n)
 				goto bad;
+		/*
 			n->defn->dodata = 1;
 			init1(n->defn->right, out);
 			if(debug['j'])
 				print("%S\n", n->sym);
 			*out = list(*out, n->defn);
 			break;
+		*/
+			if(1) {
+				init1(n->defn->right, out);
+				if(debug['j'])
+					print("%S\n", n->sym);
+				if(!staticinit(n, out)) {
+if(debug['%']) dump("nonstatic", n->defn);
+					*out = list(*out, n->defn);
+				}
+			} else if(0) {
+				n->defn->dodata = 1;
+				init1(n->defn->right, out);
+				if(debug['j'])
+					print("%S\n", n->sym);
+				*out = list(*out, n->defn);
+			}
+			break;
 		
 		case OAS2FUNC:
 		case OAS2MAPR:
 		case OAS2DOTTYPE:
 		case OAS2RECV:
-			if(n->defn->initorder)
+			if(n->defn->initorder != InitNotStarted)
 				break;
-			n->defn->initorder = 1;
+			n->defn->initorder = InitDone;
 			for(l=n->defn->rlist; l; l=l->next)
 				init1(l->n, out);
 			*out = list(*out, n->defn);
@@ -109,7 +140,7 @@ init1(Node *n, NodeList **out)
 	if(l->n != n)
 		fatal("bad initlist");
 	free(l);
-	n->initorder = 1;
+	n->initorder = InitDone;
 	return;
 
 bad:
@@ -121,7 +152,7 @@ bad:
 static void
 init2(Node *n, NodeList **out)
 {
-	if(n == N || n->initorder == 1)
+	if(n == N || n->initorder == InitDone)
 		return;
 	init1(n, out);
 	init2(n->left, out);
@@ -141,7 +172,6 @@ init2list(NodeList *l, NodeList **out)
 		init2(l->n, out);
 }
 
-
 static void
 initreorder(NodeList *l, NodeList **out)
 {
@@ -165,13 +195,221 @@ NodeList*
 initfix(NodeList *l)
 {
 	NodeList *lout;
+	int lno;
 
 	lout = nil;
+	lno = lineno;
 	initreorder(l, &lout);
+	lineno = lno;
 	return lout;
 }
 
 /*
+ * compilation of top-level (static) assignments
+ * into DATA statements if at all possible.
+ */
+
+static int staticassign(Node*, Node*, NodeList**);
+
+static int
+staticinit(Node *n, NodeList **out)
+{
+	Node *l, *r;
+
+	if(n->op != ONAME || n->class != PEXTERN || n->defn == N || n->defn->op != OAS)
+		fatal("staticinit");
+
+	lineno = n->lineno;
+	l = n->defn->left;
+	r = n->defn->right;
+	return staticassign(l, r, out);
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+static int
+staticcopy(Node *l, Node *r, NodeList **out)
+{
+	int i;
+	InitEntry *e;
+	InitPlan *p;
+	Node *a, *ll, *rr, *orig, n1;
+
+	if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg)
+		return 0;
+	if(r->defn == N)	// zeroed
+		return 1;
+	if(r->defn->op != OAS)
+		return 0;
+	orig = r;
+	r = r->defn->right;
+	
+	switch(r->op) {
+	case ONAME:
+		if(staticcopy(l, r, out))
+			return 1;
+		*out = list(*out, nod(OAS, l, r));
+		return 1;
+	
+	case OLITERAL:
+		if(iszero(r))
+			return 1;
+		gdata(l, r, l->type->width);
+		return 1;
+
+	case OADDR:
+		switch(r->left->op) {
+		case ONAME:
+			gdata(l, r, l->type->width);
+			return 1;
+		case OARRAYLIT:
+		case OSTRUCTLIT:
+		case OMAPLIT:
+			// copy pointer
+			gdata(l, nod(OADDR, r->nname, N), l->type->width);
+			return 1;
+		}
+		break;
+
+	case OARRAYLIT:
+		if(isslice(r->type)) {
+			// copy slice
+			a = r->nname;
+			n1 = *l;
+			n1.xoffset = l->xoffset + Array_array;
+			gdata(&n1, nod(OADDR, a, N), widthptr);
+			n1.xoffset = l->xoffset + Array_nel;
+			gdata(&n1, r->right, 4);
+			n1.xoffset = l->xoffset + Array_cap;
+			gdata(&n1, r->right, 4);
+			return 1;
+		}
+		// fall through
+	case OSTRUCTLIT:
+		p = r->initplan;
+		n1 = *l;
+		for(i=0; i<p->len; i++) {
+			e = &p->e[i];
+			n1.xoffset = l->xoffset + e->xoffset;
+			n1.type = e->expr->type;
+			if(e->expr->op == OLITERAL)
+				gdata(&n1, e->expr, n1.type->width);
+			else if(staticassign(&n1, e->expr, out)) {
+				// Done
+			} else {
+				// Requires computation, but we're
+				// copying someone else's computation.
+				ll = nod(OXXX, N, N);
+				*ll = n1;
+				rr = nod(OXXX, N, N);
+				*rr = *orig;
+				rr->type = ll->type;
+				rr->xoffset += e->xoffset;
+				*out = list(*out, nod(OAS, ll, rr));
+			}
+		}
+		return 1;
+	}
+	return 0;
+}
+
+static int
+staticassign(Node *l, Node *r, NodeList **out)
+{
+	Node *a, n1;
+	Type *ta;
+	InitPlan *p;
+	InitEntry *e;
+	int i;
+	
+	switch(r->op) {
+	default:
+		//dump("not static", r);
+		break;
+	
+	case ONAME:
+		if(r->class == PEXTERN && r->sym->pkg == localpkg)
+			return staticcopy(l, r, out);
+		break;
+
+	case OLITERAL:
+		if(iszero(r))
+			return 1;
+		gdata(l, r, l->type->width);
+		return 1;
+
+	case OADDR:
+		switch(r->left->op) {
+		default:
+			//dump("not static addr", r);
+			break;
+
+		case ONAME:
+			gdata(l, r, l->type->width);
+			return 1;
+		
+		case OARRAYLIT:
+		case OMAPLIT:
+		case OSTRUCTLIT:
+			// Init pointer.
+			a = staticname(r->left->type, 1);
+			r->nname = a;
+			gdata(l, nod(OADDR, a, N), l->type->width);
+			// Init underlying literal.
+			if(!staticassign(a, r->left, out))
+				*out = list(*out, nod(OAS, a, r->left));
+			return 1;
+		}
+		break;
+
+	case OARRAYLIT:
+		initplan(r);
+		if(isslice(r->type)) {
+			// Init slice.
+			ta = typ(TARRAY);
+			ta->type = r->type->type;
+			ta->bound = mpgetfix(r->right->val.u.xval);
+			a = staticname(ta, 1);
+			r->nname = a;
+			n1 = *l;
+			n1.xoffset = l->xoffset + Array_array;
+			gdata(&n1, nod(OADDR, a, N), widthptr);
+			n1.xoffset = l->xoffset + Array_nel;
+			gdata(&n1, r->right, 4);
+			n1.xoffset = l->xoffset + Array_cap;
+			gdata(&n1, r->right, 4);
+			// Fall through to init underlying array.
+			l = a;
+		}
+		// fall through
+	case OSTRUCTLIT:
+		initplan(r);
+		p = r->initplan;
+		n1 = *l;
+		for(i=0; i<p->len; i++) {
+			e = &p->e[i];
+			n1.xoffset = l->xoffset + e->xoffset;
+			n1.type = e->expr->type;
+			if(e->expr->op == OLITERAL)
+				gdata(&n1, e->expr, n1.type->width);
+			else if(staticassign(&n1, e->expr, out)) {
+				// done
+			} else {
+				a = nod(OXXX, N, N);
+				*a = n1;
+				*out = list(*out, nod(OAS, a, e->expr));
+			}
+		}
+		return 1;
+
+	case OMAPLIT:
+		// TODO: Table-driven map insert.
+		break;
+	}
+	return 0;
+}
+
+/*
  * from here down is the walk analysis
  * of composite literals.
  * most of the work is to generate
@@ -408,7 +646,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
 	dowidth(t);
 
 	if(ctxt != 0) {
-
 		// put everything into static array
 		vstat = staticname(t, ctxt);
 		arraylit(ctxt, 1, n, vstat, init);
@@ -452,12 +689,17 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
 	}
 
 	// make new auto *array (3 declare)
-	vauto = nod(OXXX, N, N);
-	tempname(vauto, ptrto(t));
-
-	// set auto to point at new heap (3 assign)
-	a = nod(ONEW, N, N);
-	a->list = list1(typenod(t));
+	vauto = temp(ptrto(t));
+
+	// set auto to point at new temp or heap (3 assign)
+	if(n->esc == EscNone) {
+		a = temp(t);
+		*init = list(*init, nod(OAS, a, N));  // zero new temp
+		a = nod(OADDR, a, N);
+	} else {
+		a = nod(ONEW, N, N);
+		a->list = list1(typenod(t));
+	}
 	a = nod(OAS, vauto, a);
 	typecheck(&a, Etop);
 	walkexpr(&a, init);
@@ -522,6 +764,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init)
 	Node *vstat, *index, *value;
 	Sym *syma, *symb;
 
+USED(ctxt);
 ctxt = 0;
 
 	// make the map var
@@ -545,7 +788,6 @@ ctxt = 0;
 			b++;
 	}
 
-	t = T;
 	if(b != 0) {
 		// build type [count]struct { a Tindex, b Tvalue }
 		t = n->type;
@@ -615,8 +857,7 @@ ctxt = 0;
 		// for i = 0; i < len(vstat); i++ {
 		//	map[vstat[i].a] = vstat[i].b
 		// }
-		index = nod(OXXX, N, N);
-		tempname(index, types[TINT]);
+		index = temp(types[TINT]);
 
 		a = nod(OINDEX, vstat, index);
 		a->etype = 1;	// no bounds checking
@@ -917,18 +1158,15 @@ gen_as_init(Node *n)
 	case TPTR64:
 	case TFLOAT32:
 	case TFLOAT64:
-		gused(N); // in case the data is the dest of a goto
 		gdata(&nam, nr, nr->type->width);
 		break;
 
 	case TCOMPLEX64:
 	case TCOMPLEX128:
-		gused(N); // in case the data is the dest of a goto
 		gdatacomplex(&nam, nr->val.u.cval);
 		break;
 
 	case TSTRING:
-		gused(N); // in case the data is the dest of a goto
 		gdatastring(&nam, nr->val.u.sval);
 		break;
 	}
@@ -969,3 +1207,151 @@ no:
 	return 0;
 }
 
+static int iszero(Node*);
+static int isvaluelit(Node*);
+static InitEntry* entry(InitPlan*);
+static void addvalue(InitPlan*, vlong, Node*, Node*);
+
+static void
+initplan(Node *n)
+{
+	InitPlan *p;
+	Node *a;
+	NodeList *l;
+
+	if(n->initplan != nil)
+		return;
+	p = mal(sizeof *p);
+	n->initplan = p;
+	switch(n->op) {
+	default:
+		fatal("initplan");
+	case OARRAYLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY || !smallintconst(a->left))
+				fatal("initplan arraylit");
+			addvalue(p, n->type->type->width*mpgetfix(a->left->val.u.xval), N, a->right);
+		}
+		break;
+	case OSTRUCTLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY || a->left->type == T)
+				fatal("initplan structlit");
+			addvalue(p, a->left->type->width, N, a->right);
+		}
+		break;
+	case OMAPLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY)
+				fatal("initplan maplit");
+			addvalue(p, -1, a->left, a->right);
+		}
+		break;
+	}
+}
+
+static void
+addvalue(InitPlan *p, vlong xoffset, Node *key, Node *n)
+{
+	int i;
+	InitPlan *q;
+	InitEntry *e;
+
+	USED(key);
+
+	// special case: zero can be dropped entirely
+	if(iszero(n)) {
+		p->zero += n->type->width;
+		return;
+	}
+	
+	// special case: inline struct and array (not slice) literals
+	if(isvaluelit(n)) {
+		initplan(n);
+		q = n->initplan;
+		for(i=0; i<q->len; i++) {
+			e = entry(p);
+			*e = q->e[i];
+			e->xoffset += xoffset;
+		}
+		return;
+	}
+	
+	// add to plan
+	if(n->op == OLITERAL)
+		p->lit += n->type->width;
+	else
+		p->expr += n->type->width;
+
+	e = entry(p);
+	e->xoffset = xoffset;
+	e->expr = n;
+}
+
+static int
+iszero(Node *n)
+{
+	NodeList *l;
+
+	switch(n->op) {
+	case OLITERAL:
+		switch(n->val.ctype) {
+		default:
+			dump("unexpected literal", n);
+			fatal("iszero");
+	
+		case CTNIL:
+			return 1;
+		
+		case CTSTR:
+			return n->val.u.sval == nil || n->val.u.sval->len == 0;
+	
+		case CTBOOL:
+			return n->val.u.bval == 0;
+			
+		case CTINT:
+			return mpcmpfixc(n->val.u.xval, 0) == 0;
+	
+		case CTFLT:
+			return mpcmpfltc(n->val.u.fval, 0) == 0;
+	
+		case CTCPLX:
+			return mpcmpfltc(&n->val.u.cval->real, 0) == 0 && mpcmpfltc(&n->val.u.cval->imag, 0) == 0;
+		}
+		break;
+	case OARRAYLIT:
+		if(isslice(n->type))
+			break;
+		// fall through
+	case OSTRUCTLIT:
+		for(l=n->list; l; l=l->next)
+			if(!iszero(l->n->right))
+				return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+isvaluelit(Node *n)
+{
+	return (n->op == OARRAYLIT && isfixedarray(n->type)) || n->op == OSTRUCTLIT;
+}
+
+static InitEntry*
+entry(InitPlan *p)
+{
+	if(p->len >= p->cap) {
+		if(p->cap == 0)
+			p->cap = 4;
+		else
+			p->cap *= 2;
+		p->e = realloc(p->e, p->cap*sizeof p->e[0]);
+		if(p->e == nil)
+			fatal("out of memory");
+	}
+	return &p->e[p->len++];
+}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 1a05d43..9448c3f 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 #include	"md5.h"
 #include	"y.tab.h"
@@ -27,7 +29,7 @@ errorexit(void)
 	flusherrors();
 	if(outfile)
 		remove(outfile);
-	exit(1);
+	exits("error");
 }
 
 extern int yychar;
@@ -106,7 +108,7 @@ hcrash(void)
 	if(debug['h']) {
 		flusherrors();
 		if(outfile)
-			unlink(outfile);
+			remove(outfile);
 		*(volatile int*)0 = 0;
 	}
 }
@@ -210,6 +212,16 @@ warn(char *fmt, ...)
 }
 
 void
+warnl(int line, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	adderr(line, fmt, arg);
+	va_end(arg);
+}
+
+void
 fatal(char *fmt, ...)
 {
 	va_list arg;
@@ -393,7 +405,7 @@ importdot(Pkg *opkg, Node *pack)
 	}
 	if(n == 0) {
 		// can't possibly be used - there were no symbols
-		yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
+		yyerrorl(pack->lineno, "imported and not used: \"%Z\"", opkg->path);
 	}
 }
 
@@ -483,6 +495,7 @@ nod(int op, Node *nleft, Node *nright)
 	n->lineno = parserline();
 	n->xoffset = BADWIDTH;
 	n->orig = n;
+	n->curfn = curfn;
 	return n;
 }
 
@@ -1094,8 +1107,8 @@ Jconv(Fmt *fp)
 
 	if(n->class != 0) {
 		s = "";
-		if (n->class & PHEAP) s = ",heap";
-		if ((n->class & ~PHEAP) < nelem(classnames))
+		if(n->class & PHEAP) s = ",heap";
+		if((n->class & ~PHEAP) < nelem(classnames))
 			fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
 		else
 			fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
@@ -1107,8 +1120,29 @@ Jconv(Fmt *fp)
 	if(n->funcdepth != 0)
 		fmtprint(fp, " f(%d)", n->funcdepth);
 
-	if(n->noescape != 0)
-		fmtprint(fp, " ne(%d)", n->noescape);
+	switch(n->esc) {
+	case EscUnknown:
+		break;
+	case EscHeap:
+		fmtprint(fp, " esc(h)");
+		break;
+	case EscScope:
+		fmtprint(fp, " esc(s)");
+		break;
+	case EscNone:
+		fmtprint(fp, " esc(no)");
+		break;
+	case EscNever:
+		if(!c)
+			fmtprint(fp, " esc(N)");
+		break;
+	default:
+		fmtprint(fp, " esc(%d)", n->esc);
+		break;
+	}
+
+	if(n->escloopdepth)
+		fmtprint(fp, " ld(%d)", n->escloopdepth);
 
 	if(!c && n->typecheck != 0)
 		fmtprint(fp, " tc(%d)", n->typecheck);
@@ -1122,9 +1156,6 @@ Jconv(Fmt *fp)
 	if(n->implicit != 0)
 		fmtprint(fp, " implicit(%d)", n->implicit);
 
-	if(!c && n->pun != 0)
-		fmtprint(fp, " pun(%d)", n->pun);
-
 	if(!c && n->used != 0)
 		fmtprint(fp, " used(%d)", n->used);
 	return 0;
@@ -1523,7 +1554,7 @@ Nconv(Fmt *fp)
 
 	switch(n->op) {
 	default:
-		if (fp->flags & FmtShort)
+		if(fp->flags & FmtShort)
 			fmtprint(fp, "%O%hJ", n->op, n);
 		else
 			fmtprint(fp, "%O%J", n->op, n);
@@ -1532,13 +1563,13 @@ Nconv(Fmt *fp)
 	case ONAME:
 	case ONONAME:
 		if(n->sym == S) {
-			if (fp->flags & FmtShort)
+			if(fp->flags & FmtShort)
 				fmtprint(fp, "%O%hJ", n->op, n);
 			else
 				fmtprint(fp, "%O%J", n->op, n);
 			break;
 		}
-		if (fp->flags & FmtShort)
+		if(fp->flags & FmtShort)
 			fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
 		else
 			fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
@@ -2754,8 +2785,7 @@ copyexpr(Node *n, Type *t, NodeList **init)
 {
 	Node *a, *l;
 	
-	l = nod(OXXX, N, N);
-	tempname(l, t);
+	l = temp(t);
 	a = nod(OAS, l, n);
 	typecheck(&a, Etop);
 	walkexpr(&a, init);
@@ -2806,10 +2836,12 @@ setmaxarg(Type *t)
 		maxarg = w;
 }
 
-/* unicode-aware case-insensitive strcmp */
+/*
+ * unicode-aware case-insensitive strcmp
+ */
 
 static int
-cistrcmp(char *p, char *q)
+ucistrcmp(char *p, char *q)
 {
 	Rune rp, rq;
 
@@ -2851,7 +2883,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
 	c = 0;
 	if(u->etype == TSTRUCT || u->etype == TINTER) {
 		for(f=u->type; f!=T; f=f->down)
-			if(f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0)) {
+			if(f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0)) {
 				if(save)
 					*save = f;
 				c++;
@@ -2860,7 +2892,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
 	u = methtype(t);
 	if(u != T) {
 		for(f=u->method; f!=T; f=f->down)
-			if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0))) {
+			if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
 				if(save)
 					*save = f;
 				c++;
@@ -3176,7 +3208,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
 	int isddd;
 	Val v;
 
-	if(debug['r'])
+	if(0 && debug['r'])
 		print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
 			rcvr, method, newnam);
 
@@ -3410,8 +3442,13 @@ list1(Node *n)
 
 	if(n == nil)
 		return nil;
-	if(n->op == OBLOCK && n->ninit == nil)
-		return n->list;
+	if(n->op == OBLOCK && n->ninit == nil) {
+		// Flatten list and steal storage.
+		// Poison pointer to catch errant uses.
+		l = n->list;
+		n->list = (NodeList*)1;
+		return l;
+	}
 	l = mal(sizeof *l);
 	l->n = n;
 	l->end = l;
@@ -3453,7 +3490,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
 	listsort(&l1, f);
 	listsort(&l2, f);
 
-	if ((*f)(l1->n, l2->n) < 0) {
+	if((*f)(l1->n, l2->n) < 0) {
 		*l = l1;
 	} else {
 		*l = l2;
@@ -3469,7 +3506,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
 		
 		// l1 is last one from l1 that is < l2
 		le = l1->next;		// le is the rest of l1, first one that is >= l2
-		if (le != nil)
+		if(le != nil)
 			le->end = (*l)->end;
 
 		(*l)->end = l1;		// cut *l at l1
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index c2968c4..0381132 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
 enum
@@ -513,8 +515,7 @@ exprswitch(Node *sw)
 	exprname = N;
 	cas = nil;
 	if(arg != Strue && arg != Sfalse) {
-		exprname = nod(OXXX, N, N);
-		tempname(exprname, sw->ntest->type);
+		exprname = temp(sw->ntest->type);
 		cas = list1(nod(OAS, exprname, sw->ntest));
 		typechecklist(cas, Etop);
 	}
@@ -671,20 +672,17 @@ typeswitch(Node *sw)
 	 * predeclare temporary variables
 	 * and the boolean var
 	 */
-	facename = nod(OXXX, N, N);
-	tempname(facename, sw->ntest->right->type);
+	facename = temp(sw->ntest->right->type);
 	a = nod(OAS, facename, sw->ntest->right);
 	typecheck(&a, Etop);
 	cas = list(cas, a);
 
 	casebody(sw, facename);
 
-	boolname = nod(OXXX, N, N);
-	tempname(boolname, types[TBOOL]);
+	boolname = temp(types[TBOOL]);
 	typecheck(&boolname, Erv);
 
-	hashname = nod(OXXX, N, N);
-	tempname(hashname, types[TUINT32]);
+	hashname = temp(types[TUINT32]);
 	typecheck(&hashname, Erv);
 
 	t = sw->ntest->right->type;
@@ -865,9 +863,9 @@ typecheckswitch(Node *n)
 						yyerror("case %+N in %T switch", ll->n, t);
 					break;
 				case Etype:	// type switch
-					if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL))
+					if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) {
 						;
-					else if(ll->n->op != OTYPE && ll->n->type != T) {
+					} else if(ll->n->op != OTYPE && ll->n->type != T) {
 						yyerror("%#N is not a type", ll->n);
 						// reset to original type
 						ll->n = n->ntest->right;
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 78cdb5b..b9c302c 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -10,6 +10,8 @@
  * rewrites n->op to be more specific in some cases.
  */
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 static void	implicitstar(Node**);
@@ -21,7 +23,6 @@ static void	typecheckaste(int, Node*, int, Type*, NodeList*, char*);
 static Type*	lookdot1(Sym *s, Type *t, Type *f, int);
 static int	nokeys(NodeList*);
 static void	typecheckcomplit(Node**);
-static void	addrescapes(Node*);
 static void	typecheckas2(Node*);
 static void	typecheckas(Node*);
 static void	typecheckfunc(Node*);
@@ -37,7 +38,7 @@ static	NodeList*	typecheckdefstack;
 /*
  * resolve ONONAME to definition, if any.
  */
-Node*
+static Node*
 resolve(Node *n)
 {
 	Node *r;
@@ -152,7 +153,7 @@ typecheck(Node **np, int top)
 	}
 
 	if(n->typecheck == 2) {
-		yyerror("typechecking loop");
+		yyerror("typechecking loop involving %#N", n);
 		lineno = lno;
 		return n;
 	}
@@ -337,7 +338,7 @@ reswitch:
 	 */
 	case OIND:
 		ntop = Erv | Etype;
-		if(!(top & Eaddr))
+		if(!(top & Eaddr))  		// The *x in &*x is not an indirect.
 			ntop |= Eindir;
 		l = typecheck(&n->left, ntop);
 		if((t = l->type) == T)
@@ -535,7 +536,9 @@ reswitch:
 		l = n->left;
 		if((t = l->type) == T)
 			goto error;
-		if(!(top & Eindir) && !n->etype)
+		// top&Eindir means this is &x in *&x.  (or the arg to built-in print)
+		// n->etype means code generator flagged it as non-escaping.
+		if(debug['s'] && !(top & Eindir) && !n->etype)
 			addrescapes(n->left);
 		n->type = ptrto(t);
 		goto ret;
@@ -721,7 +724,7 @@ reswitch:
 		}
 		defaultlit(&n->right, t->type);
 		r = n->right;
-		if((t = r->type) == T)
+		if(r->type == T)
 			goto error;
 		r = assignconv(r, l->type->type, "send");
 		// TODO: more aggressive
@@ -1028,6 +1031,7 @@ reswitch:
 		}
 		n->left = args->n;
 		n->right = args->next->n;
+		n->list = nil;
 		n->type = types[TINT];
 		typecheck(&n->left, Erv);
 		typecheck(&n->right, Erv);
@@ -1038,7 +1042,7 @@ reswitch:
 		
 		// copy([]byte, string)
 		if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
-			if (n->left->type->type == types[TUINT8])
+			if(n->left->type->type == types[TUINT8])
 				goto ret;
 			yyerror("arguments to copy have different element types: %lT and string", n->left->type);
 			goto error;
@@ -1068,7 +1072,7 @@ reswitch:
 			goto error;
 		if((n->op = convertop(t, n->type, &why)) == 0) {
 			yyerror("cannot convert %+N to type %T%s", n->left, n->type, why);
-			op = OCONV;
+			n->op = OCONV;
 		}
 		switch(n->op) {
 		case OCONVNOP:
@@ -1602,7 +1606,8 @@ lookdot(Node *n, Type *t, int dostrcmp)
 		if(!eqtype(rcvr, tt)) {
 			if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
 				checklvalue(n->left, "call pointer method on");
-				addrescapes(n->left);
+				if(debug['s'])
+					addrescapes(n->left);
 				n->left = nod(OADDR, n->left, N);
 				n->left->implicit = 1;
 				typecheck(&n->left, Etype|Erv);
@@ -2097,6 +2102,7 @@ typecheckcomplit(Node **np)
 					yyerror("implicit assignment of unexported field '%s' in %T literal", s->name, t);
 				ll->n = assignconv(ll->n, f->type, "field value");
 				ll->n = nod(OKEY, newname(f->sym), ll->n);
+				ll->n->left->type = f;
 				ll->n->left->typecheck = 1;
 				f = f->down;
 			}
@@ -2126,14 +2132,15 @@ typecheckcomplit(Node **np)
 				// before we do the lookup.
 				if(s->pkg != localpkg)
 					s = lookup(s->name);
-				l->left = newname(s);
-				l->left->typecheck = 1;
 				f = lookdot1(s, t, t->type, 0);
 				typecheck(&l->right, Erv);
 				if(f == nil) {
 					yyerror("unknown %T field '%s' in struct literal", t, s->name);
 					continue;
 				}
+				l->left = newname(s);
+				l->left->typecheck = 1;
+				l->left->type = f;
 				s = f->sym;
 				fielddup(newname(s), hash, nhash);
 				l->right = assignconv(l->right, f->type, "field value");
@@ -2157,82 +2164,6 @@ error:
 }
 
 /*
- * the address of n has been taken and might be used after
- * the current function returns.  mark any local vars
- * as needing to move to the heap.
- */
-static void
-addrescapes(Node *n)
-{
-	char buf[100];
-	switch(n->op) {
-	default:
-		// probably a type error already.
-		// dump("addrescapes", n);
-		break;
-
-	case ONAME:
-		if(n->noescape)
-			break;
-		switch(n->class) {
-		case PPARAMREF:
-			addrescapes(n->defn);
-			break;
-		case PPARAM:
-		case PPARAMOUT:
-			// if func param, need separate temporary
-			// to hold heap pointer.
-			// the function type has already been checked
-			// (we're in the function body)
-			// so the param already has a valid xoffset.
-
-			// expression to refer to stack copy
-			n->stackparam = nod(OPARAM, n, N);
-			n->stackparam->type = n->type;
-			n->stackparam->addable = 1;
-			if(n->xoffset == BADWIDTH)
-				fatal("addrescapes before param assignment");
-			n->stackparam->xoffset = n->xoffset;
-			n->xoffset = 0;
-			// fallthrough
-		case PAUTO:
-
-			n->class |= PHEAP;
-			n->addable = 0;
-			n->ullman = 2;
-			n->xoffset = 0;
-
-			// create stack variable to hold pointer to heap
-			n->heapaddr = nod(ONAME, N, N);
-			n->heapaddr->type = ptrto(n->type);
-			snprint(buf, sizeof buf, "&%S", n->sym);
-			n->heapaddr->sym = lookup(buf);
-			n->heapaddr->class = PHEAP-1;	// defer tempname to allocparams
-			n->heapaddr->ullman = 1;
-			n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
-
-			break;
-		}
-		break;
-
-	case OIND:
-	case ODOTPTR:
-		break;
-
-	case ODOT:
-	case OINDEX:
-		// ODOTPTR has already been introduced,
-		// so these are the non-pointer ODOT and OINDEX.
-		// In &x[0], if x is a slice, then x does not
-		// escape--the pointer inside x does, but that
-		// is always a heap pointer anyway.
-		if(!isslice(n->left->type))
-			addrescapes(n->left);
-		break;
-	}
-}
-
-/*
  * lvalue etc
  */
 int
@@ -2462,7 +2393,6 @@ typecheckfunc(Node *n)
 {
 	Type *t, *rcvr;
 
-//dump("nname", n->nname);
 	typecheck(&n->nname, Erv | Easgn);
 	if((t = n->nname->type) == T)
 		return;
@@ -2514,7 +2444,7 @@ getforwtype(Node *n)
 {
 	Node *f1, *f2;
 
-	for(f1=f2=n; ; n=n->ntype) {
+	for(f2=n; ; n=n->ntype) {
 		if((n = resolve(n)) == N || n->op != OTYPE)
 			return T;
 
@@ -2772,6 +2702,7 @@ typecheckdef(Node *n)
 		if(n->ntype != N) {
 			typecheck(&n->ntype, Etype);
 			n->type = n->ntype->type;
+
 			if(n->type == T) {
 				n->diag = 1;
 				goto ret;
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
index d304077..6435492 100644
--- a/src/cmd/gc/unsafe.c
+++ b/src/cmd/gc/unsafe.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include <u.h>
+#include <libc.h>
 #include "go.h"
 
 /*
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 9cd4ee9..8a84956 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include	<u.h>
+#include	<libc.h>
 #include	"go.h"
 
 static	Node*	walkprint(Node*, NodeList**, int);
@@ -11,7 +13,7 @@ static	Node*	makenewvar(Type*, NodeList**, Node**);
 static	Node*	ascompatee1(int, Node*, Node*, NodeList**);
 static	NodeList*	ascompatee(int, NodeList*, NodeList*, NodeList**);
 static	NodeList*	ascompatet(int, NodeList*, Type**, int, NodeList**);
-static	NodeList*	ascompatte(int, int, Type**, NodeList*, int, NodeList**);
+static	NodeList*	ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
 static	Node*	convas(Node*, NodeList**);
 static	void	heapmoves(void);
 static	NodeList*	paramstoheap(Type **argin, int out);
@@ -287,7 +289,7 @@ walkstmt(Node **np)
 			n->list = reorder3(ll);
 			break;
 		}
-		ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+		ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
 		n->list = ll;
 		break;
 
@@ -378,14 +380,11 @@ walkexpr(Node **np, NodeList **init)
 		fatal("missed typecheck");
 	}
 
-	t = T;
-	et = Txxx;
-
 	switch(n->op) {
 	default:
 		dump("walk", n);
 		fatal("walkexpr: switch 1 unknown op %N", n);
-		goto ret;
+		break;
 
 	case OTYPE:
 	case ONONAME:
@@ -482,7 +481,7 @@ walkexpr(Node **np, NodeList **init)
 			goto ret;
 		walkexpr(&n->left, init);
 		walkexprlist(n->list, init);
-		ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+		ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
 		n->list = reorder1(ll);
 		goto ret;
 
@@ -499,7 +498,7 @@ walkexpr(Node **np, NodeList **init)
 		walkexpr(&n->left, init);
 		walkexprlist(n->list, init);
 
-		ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+		ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
 		n->list = reorder1(ll);
 		goto ret;
 
@@ -509,8 +508,8 @@ walkexpr(Node **np, NodeList **init)
 			goto ret;
 		walkexpr(&n->left, init);
 		walkexprlist(n->list, init);
-		ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
-		lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+		ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
+		lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
 		ll = concat(ll, lr);
 		n->left->left = N;
 		ullmancalc(n->left);
@@ -563,8 +562,7 @@ walkexpr(Node **np, NodeList **init)
 		// and map index has an implicit one.
 		lpost = nil;
 		if(l->op == OINDEXMAP) {
-			var = nod(OXXX, N, N);
-			tempname(var, l->type);
+			var = temp(l->type);
 			n->list->n = var;
 			a = nod(OAS, l, var);
 			typecheck(&a, Etop);
@@ -572,8 +570,7 @@ walkexpr(Node **np, NodeList **init)
 		}
 		l = n->list->next->n;
 		if(l->op == OINDEXMAP) {
-			var = nod(OXXX, N, N);
-			tempname(var, l->type);
+			var = temp(l->type);
 			n->list->next->n = var;
 			a = nod(OAS, l, var);
 			typecheck(&a, Etop);
@@ -975,7 +972,15 @@ walkexpr(Node **np, NodeList **init)
 		goto ret;
 
 	case ONEW:
-		n = callnew(n->type->type);
+		if(n->esc == EscNone && n->type->type->width < (1<<16)) {
+			r = temp(n->type->type);
+			*init = list(*init, nod(OAS, r, N));  // zero temp
+			r = nod(OADDR, r, N);
+			typecheck(&r, Erv);
+			n = r;
+		} else {
+			n = callnew(n->type->type);
+		}
 		goto ret;
 
 	case OCMPSTR:
@@ -1156,8 +1161,7 @@ walkexpr(Node **np, NodeList **init)
 	case OARRAYLIT:
 	case OMAPLIT:
 	case OSTRUCTLIT:
-		nvar = nod(OXXX, N, N);
-		tempname(nvar, n->type);
+		nvar = temp(n->type);
 		anylit(0, n, nvar, init);
 		n = nvar;
 		goto ret;
@@ -1186,8 +1190,7 @@ makenewvar(Type *t, NodeList **init, Node **nstar)
 {
 	Node *nvar, *nas;
 
-	nvar = nod(OXXX, N, N);
-	tempname(nvar, t);
+	nvar = temp(t);
 	nas = nod(OAS, nvar, callnew(t->type));
 	typecheck(&nas, Etop);
 	walkexpr(&nas, init);
@@ -1201,6 +1204,8 @@ makenewvar(Type *t, NodeList **init, Node **nstar)
 static Node*
 ascompatee1(int op, Node *l, Node *r, NodeList **init)
 {
+	USED(op);
+
 	return convas(nod(OAS, l, r), init);
 }
 
@@ -1257,6 +1262,8 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
 	int ucount;
 	NodeList *nn, *mm;
 
+	USED(op);
+
 	/*
 	 * check assign type list to
 	 * a expression list. called in
@@ -1279,8 +1286,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
 		// deferred until all the return arguments
 		// have been pulled from the output arguments
 		if(fncall(l, r->type)) {
-			tmp = nod(OXXX, N, N);
-			tempname(tmp, r->type);
+			tmp = temp(r->type);
 			typecheck(&tmp, Erv);
 			a = nod(OAS, l, tmp);
 			a = convas(a, init);
@@ -1309,21 +1315,27 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
  * package all the arguments that match a ... T parameter into a []T.
  */
 static NodeList*
-mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc)
 {
 	Node *a, *n;
 	Type *tslice;
-
+	
 	tslice = typ(TARRAY);
 	tslice->type = l->type->type;
 	tslice->bound = -1;
 
-	n = nod(OCOMPLIT, N, typenod(tslice));
-	n->list = lr0;
-	typecheck(&n, Erv);
-	if(n->type == T)
-		fatal("mkdotargslice: typecheck failed");
-	walkexpr(&n, init);
+	if(count(lr0) == 0) {
+		n = nodnil();
+		n->type = tslice;
+	} else {
+		n = nod(OCOMPLIT, N, typenod(tslice));
+		n->list = lr0;
+		n->esc = esc;
+		typecheck(&n, Erv);
+		if(n->type == T)
+			fatal("mkdotargslice: typecheck failed");
+		walkexpr(&n, init);
+	}
 
 	a = nod(OAS, nodarg(l, fp), n);
 	nn = list(nn, convas(a, init));
@@ -1343,7 +1355,6 @@ dumptypes(Type **nl, char *what)
 
 	fmtstrinit(&fmt);
 	fmtprint(&fmt, "\t");
-	l = structfirst(&savel, nl);
 	first = 1;
 	for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
 		if(first)
@@ -1387,8 +1398,9 @@ dumpnodetypes(NodeList *l, char *what)
  *	func(expr-list)
  */
 static NodeList*
-ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
 {
+	int esc;
 	Type *l, *ll;
 	Node *r, *a;
 	NodeList *nn, *lr0, *alist;
@@ -1416,8 +1428,7 @@ ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
 		// copy into temporaries.
 		alist = nil;
 		for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
-			a = nod(OXXX, N, N);
-			tempname(a, l->type);
+			a = temp(l->type);
 			alist = list(alist, a);
 		}
 		a = nod(OAS2, N, N);
@@ -1452,7 +1463,10 @@ loop:
 		// normal case -- make a slice of all
 		// remaining arguments and pass it to
 		// the ddd parameter.
-		nn = mkdotargslice(lr, nn, l, fp, init);
+		esc = EscUnknown;
+		if(call->right)
+			esc = call->right->esc;
+		nn = mkdotargslice(lr, nn, l, fp, init, esc);
 		goto ret;
 	}
 
@@ -1720,7 +1734,7 @@ out:
  * then it is done first. otherwise must
  * make temp variables
  */
-NodeList*
+static NodeList*
 reorder1(NodeList *all)
 {
 	Node *f, *a, *n;
@@ -1757,8 +1771,7 @@ reorder1(NodeList *all)
 		}
 
 		// make assignment of fncall to tempname
-		a = nod(OXXX, N, N);
-		tempname(a, n->right->type);
+		a = temp(n->right->type);
 		a = nod(OAS, a, n->right);
 		g = list(g, a);
 
@@ -1846,7 +1859,7 @@ vmatch1(Node *l, Node *r)
 	return 0;
 }
 
-NodeList*
+static NodeList*
 reorder3(NodeList *all)
 {
 	Node *n1, *n2, *q;
@@ -1861,8 +1874,7 @@ reorder3(NodeList *all)
 			if(c2 > c1) {
 				if(vmatch1(n1->left, n2->right)) {
 					// delay assignment to n1->left
-					q = nod(OXXX, N, N);
-					tempname(q, n1->right->type);
+					q = temp(n1->right->type);
 					q = nod(OAS, n1->left, q);
 					n1->left = q->right;
 					r = list(r, q);
@@ -2125,8 +2137,7 @@ append(Node *n, NodeList **init)
 
 	l = nil;
 
-	ns = nod(OXXX, N, N);             // var s
-	tempname(ns, nsrc->type);
+	ns = temp(nsrc->type);
 	l = list(l, nod(OAS, ns, nsrc));  // s = src
 
 	na = nodintconst(argc);         // const argc
@@ -2143,8 +2154,7 @@ append(Node *n, NodeList **init)
 					       conv(na, types[TINT64]))));
 	l = list(l, nx);
 
-	nn = nod(OXXX, N, N);                            // var n
-	tempname(nn, types[TINT]);
+	nn = temp(types[TINT]);
 	l = list(l, nod(OAS, nn, nod(OLEN, ns, N)));     // n = len(s)
 
 	nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na)));   // ...s[:n+argc]
diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c
index 6a86301..38b2962 100644
--- a/src/cmd/godefs/main.c
+++ b/src/cmd/godefs/main.c
@@ -98,7 +98,10 @@ waitfor(int pid)
 int
 spawn(char *prog, char **argv)
 {
-	int pid = fork();
+	int pid;
+	
+	USED(prog);
+	pid = fork();
 	if(pid < 0)
 		sysfatal("fork: %r");
 	if(pid == 0) {
diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile
index f40d717..a8cf5d6 100644
--- a/src/cmd/godoc/Makefile
+++ b/src/cmd/godoc/Makefile
@@ -18,6 +18,7 @@ GOFILES=\
 	parser.go\
 	snippet.go\
 	spec.go\
+	throttle.go\
 	utils.go\
 	zip.go\
 
diff --git a/src/cmd/godoc/appconfig.go b/src/cmd/godoc/appconfig.go
index 9cbe7a4..052a9eb 100644
--- a/src/cmd/godoc/appconfig.go
+++ b/src/cmd/godoc/appconfig.go
@@ -11,9 +11,20 @@ package main
 const (
 	// zipFilename is the name of the .zip file
 	// containing the file system served by godoc.
-	zipFilename = "go.zip"
+	zipFilename = "godoc.zip"
 
 	// zipGoroot is the path of the goroot directory
 	// in the .zip file.
-	zipGoroot = "/home/username/go"
+	zipGoroot = "/home/user/go"
+
+	// If indexFilenames != "", the search index is
+	// initialized with the index stored in these
+	// files (otherwise it will be built at run-time,
+	// eventually). indexFilenames is a glob pattern;
+	// the specified files are concatenated in sorted
+	// order (by filename).
+	// app-engine limit: file sizes must be <= 10MB;
+	// use "split -b8m indexfile index.split." to get
+	// smaller files.
+	indexFilenames = "index.split.*"
 )
diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go
index 9b89872..baba53f 100644
--- a/src/cmd/godoc/appinit.go
+++ b/src/cmd/godoc/appinit.go
@@ -23,11 +23,12 @@
 //		strings		// never version of the strings package
 //		...		//
 //	app.yaml		// app engine control file
-//	go.zip			// zip file containing the file system to serve
+//	godoc.zip		// .zip file containing the file system to serve
 //	godoc			// contains godoc sources
 //		appinit.go	// this file instead of godoc/main.go
 //		appconfig.go	// godoc for app engine configuration
 //		...		//
+//	index.split.*		// index file(s) containing the search index to serve
 //
 // To run app the engine emulator locally:
 //
@@ -39,10 +40,11 @@
 package main
 
 import (
-	"alt/archive/zip"
+	"archive/zip"
 	"http"
 	"log"
 	"os"
+	"path"
 )
 
 func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) {
@@ -53,7 +55,16 @@ func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.E
 
 func init() {
 	log.Println("initializing godoc ...")
+	log.Printf(".zip file   = %s", zipFilename)
+	log.Printf(".zip GOROOT = %s", zipGoroot)
+	log.Printf("index files = %s", indexFilenames)
+
+	// initialize flags for app engine
 	*goroot = path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
+	*indexEnabled = true
+	*indexFiles = indexFilenames
+	*maxResults = 0      // save space for now
+	*indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run)
 
 	// read .zip file and set up file systems
 	const zipfile = zipFilename
@@ -65,8 +76,8 @@ func init() {
 	fsHttp = NewHttpZipFS(rc, *goroot)
 
 	// initialize http handlers
-	initHandlers()
 	readTemplates()
+	initHandlers()
 	registerPublicHandlers(http.DefaultServeMux)
 
 	// initialize default directory tree with corresponding timestamp.
@@ -75,12 +86,10 @@ func init() {
 	// initialize directory trees for user-defined file systems (-path flag).
 	initDirTrees()
 
-	// create search index
-	// TODO(gri) Disabled for now as it takes too long. Find a solution for this.
-	/*
-		*indexEnabled = true
+	// initialize search index
+	if *indexEnabled {
 		go indexer()
-	*/
+	}
 
 	log.Println("godoc initialization complete")
 }
diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go
index e2643e4..602aa43 100644
--- a/src/cmd/godoc/codewalk.go
+++ b/src/cmd/godoc/codewalk.go
@@ -13,7 +13,6 @@
 package main
 
 import (
-	"container/vector"
 	"fmt"
 	"http"
 	"io"
@@ -183,17 +182,17 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string
 		serveError(w, r, relpath, err)
 		return
 	}
-	var v vector.Vector
+	var v []interface{}
 	for _, fi := range dir {
 		name := fi.Name()
 		if fi.IsDirectory() {
-			v.Push(&elem{name + "/", ""})
+			v = append(v, &elem{name + "/", ""})
 		} else if strings.HasSuffix(name, ".xml") {
 			cw, err := loadCodewalk(abspath + "/" + name)
 			if err != nil {
 				continue
 			}
-			v.Push(&elem{name[0 : len(name)-len(".xml")], cw.Title})
+			v = append(v, &elem{name[0 : len(name)-len(".xml")], cw.Title})
 		}
 	}
 
diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go
index aa590b3..7595ef9 100644
--- a/src/cmd/godoc/dirtrees.go
+++ b/src/cmd/godoc/dirtrees.go
@@ -117,7 +117,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i
 			// though the directory doesn't contain any real package files - was bug)
 			if synopses[0] == "" {
 				// no "optimal" package synopsis yet; continue to collect synopses
-				file, err := parser.ParseFile(fset, filepath.Join(path, d.Name()), nil,
+				file, err := parseFile(fset, filepath.Join(path, d.Name()),
 					parser.ParseComments|parser.PackageClauseOnly)
 				if err == nil {
 					hasPkgFiles = true
diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go
index dc98b0e..813527d 100644
--- a/src/cmd/godoc/doc.go
+++ b/src/cmd/godoc/doc.go
@@ -50,6 +50,17 @@ The flags are:
 	-index
 		enable identifier and full text search index
 		(no search box is shown if -index is not set)
+	-index_files=""
+		glob pattern specifying index files; if not empty,
+		the index is read from these files in sorted order
+	-index_throttle=0.75
+		index throttle value; a value of 0 means no time is allocated
+		to the indexer (the indexer will never finish), a value of 1.0
+		means that index creation is running at full throttle (other
+		goroutines may get no time while the index is built)
+	-write_index=false
+		write index to a file; the file name must be specified with
+		-index_files
 	-maxresults=10000
 		maximum number of full text search results shown
 		(no full text index is built if maxresults <= 0)
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index b8a8394..6b646a1 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -64,7 +64,10 @@ var (
 
 	// search index
 	indexEnabled = flag.Bool("index", false, "enable search index")
-	maxResults   = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
+	indexFiles   = flag.String("index_files", "", "glob pattern specifying index files;"+
+		"if not empty, the index is read from these files in sorted order")
+	maxResults    = flag.Int("maxresults", 10000, "maximum number of full text search results shown")
+	indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle")
 
 	// file system mapping
 	fs         FileSystem      // the underlying file system for godoc
@@ -403,8 +406,8 @@ var infoKinds = [nKinds]string{
 	Use:           "use",
 }
 
-func infoKind_htmlFunc(kind SpotKind) string {
-	return infoKinds[kind] // infoKind entries are html-escaped
+func infoKind_htmlFunc(info SpotInfo) string {
+	return infoKinds[info.Kind()] // infoKind entries are html-escaped
 }
 
 func infoLineFunc(info SpotInfo) int {
@@ -540,7 +543,19 @@ func readTemplate(name string) *template.Template {
 			path = defaultpath
 		}
 	}
-	return template.Must(template.New(name).Funcs(fmap).ParseFile(path))
+
+	// use underlying file system fs to read the template file
+	// (cannot use template ParseFile functions directly)
+	data, err := fs.ReadFile(path)
+	if err != nil {
+		log.Fatal("readTemplate: ", err)
+	}
+	// be explicit with errors (for app engine use)
+	t, err := template.New(name).Funcs(fmap).Parse(string(data))
+	if err != nil {
+		log.Fatal("readTemplate: ", err)
+	}
+	return t
 }
 
 var (
@@ -1049,9 +1064,7 @@ func lookup(query string) (result SearchResult) {
 	// is the result accurate?
 	if *indexEnabled {
 		if _, ts := fsModified.get(); timestamp < ts {
-			// The index is older than the latest file system change
-			// under godoc's observation. Indexing may be in progress
-			// or start shortly (see indexer()).
+			// The index is older than the latest file system change under godoc's observation.
 			result.Alert = "Indexing in progress: result may be inaccurate"
 		}
 	} else {
@@ -1128,26 +1141,61 @@ func fsDirnames() <-chan string {
 	return c
 }
 
+func readIndex(filenames string) os.Error {
+	matches, err := filepath.Glob(filenames)
+	if err != nil {
+		return err
+	}
+	sort.Strings(matches) // make sure files are in the right order
+	files := make([]io.Reader, 0, len(matches))
+	for _, filename := range matches {
+		f, err := os.Open(filename)
+		if err != nil {
+			return err
+		}
+		defer f.Close()
+		files = append(files, f)
+	}
+	x := new(Index)
+	if err := x.Read(io.MultiReader(files...)); err != nil {
+		return err
+	}
+	searchIndex.set(x)
+	return nil
+}
+
+func updateIndex() {
+	if *verbose {
+		log.Printf("updating index...")
+	}
+	start := time.Nanoseconds()
+	index := NewIndex(fsDirnames(), *maxResults > 0, *indexThrottle)
+	stop := time.Nanoseconds()
+	searchIndex.set(index)
+	if *verbose {
+		secs := float64((stop-start)/1e6) / 1e3
+		stats := index.Stats()
+		log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)",
+			secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots)
+	}
+	log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
+	runtime.GC()
+	log.Printf("after  GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
+}
+
 func indexer() {
+	// initialize the index from disk if possible
+	if *indexFiles != "" {
+		if err := readIndex(*indexFiles); err != nil {
+			log.Printf("error reading index: %s", err)
+		}
+	}
+
+	// repeatedly update the index when it goes out of date
 	for {
 		if !indexUpToDate() {
 			// index possibly out of date - make a new one
-			if *verbose {
-				log.Printf("updating index...")
-			}
-			start := time.Nanoseconds()
-			index := NewIndex(fsDirnames(), *maxResults > 0)
-			stop := time.Nanoseconds()
-			searchIndex.set(index)
-			if *verbose {
-				secs := float64((stop-start)/1e6) / 1e3
-				stats := index.Stats()
-				log.Printf("index updated (%gs, %d bytes of source, %d files, %d lines, %d unique words, %d spots)",
-					secs, stats.Bytes, stats.Files, stats.Lines, stats.Words, stats.Spots)
-			}
-			log.Printf("before GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
-			runtime.GC()
-			log.Printf("after  GC: bytes = %d footprint = %d", runtime.MemStats.HeapAlloc, runtime.MemStats.Sys)
+			updateIndex()
 		}
 		var delay int64 = 60 * 1e9 // by default, try every 60s
 		if *testDir != "" {
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
index 9b4f315..b993634 100644
--- a/src/cmd/godoc/index.go
+++ b/src/cmd/godoc/index.go
@@ -7,7 +7,7 @@
 //
 // Algorithm for identifier index:
 // - traverse all .go files of the file tree specified by root
-// - for each word (identifier) encountered, collect all occurrences (spots)
+// - for each identifier (word) encountered, collect all occurrences (spots)
 //   into a list; this produces a list of spots for each word
 // - reduce the lists: from a list of spots to a list of FileRuns,
 //   and from a list of FileRuns into a list of PakRuns
@@ -39,12 +39,13 @@ package main
 
 import (
 	"bytes"
-	"container/vector"
 	"go/ast"
 	"go/parser"
 	"go/token"
 	"go/scanner"
+	"gob"
 	"index/suffixarray"
+	"io"
 	"os"
 	"path/filepath"
 	"regexp"
@@ -53,47 +54,61 @@ import (
 )
 
 // ----------------------------------------------------------------------------
+// InterfaceSlice is a helper type for sorting interface
+// slices according to some slice-specific sort criteria.
+
+type Comparer func(x, y interface{}) bool
+
+type InterfaceSlice struct {
+	slice []interface{}
+	less  Comparer
+}
+
+func (p *InterfaceSlice) Len() int           { return len(p.slice) }
+func (p *InterfaceSlice) Less(i, j int) bool { return p.less(p.slice[i], p.slice[j]) }
+func (p *InterfaceSlice) Swap(i, j int)      { p.slice[i], p.slice[j] = p.slice[j], p.slice[i] }
+
+// ----------------------------------------------------------------------------
 // RunList
 
-// A RunList is a vector of entries that can be sorted according to some
+// A RunList is a list of entries that can be sorted according to some
 // criteria. A RunList may be compressed by grouping "runs" of entries
 // which are equal (according to the sort critera) into a new RunList of
 // runs. For instance, a RunList containing pairs (x, y) may be compressed
 // into a RunList containing pair runs (x, {y}) where each run consists of
 // a list of y's with the same x.
-type RunList struct {
-	vector.Vector
-	less func(x, y interface{}) bool
-}
-
-func (h *RunList) Less(i, j int) bool { return h.less(h.At(i), h.At(j)) }
+type RunList []interface{}
 
-func (h *RunList) sort(less func(x, y interface{}) bool) {
-	h.less = less
-	sort.Sort(h)
+func (h RunList) sort(less Comparer) {
+	sort.Sort(&InterfaceSlice{h, less})
 }
 
 // Compress entries which are the same according to a sort criteria
 // (specified by less) into "runs".
-func (h *RunList) reduce(less func(x, y interface{}) bool, newRun func(h *RunList, i, j int) interface{}) *RunList {
+func (h RunList) reduce(less Comparer, newRun func(h RunList) interface{}) RunList {
+	if len(h) == 0 {
+		return nil
+	}
+	// len(h) > 0
+
 	// create runs of entries with equal values
 	h.sort(less)
 
 	// for each run, make a new run object and collect them in a new RunList
 	var hh RunList
-	i := 0
-	for j := 0; j < h.Len(); j++ {
-		if less(h.At(i), h.At(j)) {
-			hh.Push(newRun(h, i, j))
-			i = j // start a new run
+	i, x := 0, h[0]
+	for j, y := range h {
+		if less(x, y) {
+			hh = append(hh, newRun(h[i:j]))
+			i, x = j, h[j] // start a new run
 		}
 	}
 	// add final run, if any
-	if i < h.Len() {
-		hh.Push(newRun(h, i, h.Len()))
+	if i < len(h) {
+		hh = append(hh, newRun(h[i:]))
 	}
 
-	return &hh
+	return hh
 }
 
 // ----------------------------------------------------------------------------
@@ -164,30 +179,25 @@ func (x SpotInfo) IsIndex() bool  { return x&1 != 0 }
 const removeDuplicates = true
 
 // A KindRun is a run of SpotInfos of the same kind in a given file.
-type KindRun struct {
-	Kind  SpotKind
-	Infos []SpotInfo
-}
+// The kind (3 bits) is stored in each SpotInfo element; to find the
+// kind of a KindRun, look at any of it's elements.
+type KindRun []SpotInfo
 
 // KindRuns are sorted by line number or index. Since the isIndex bit
 // is always the same for all infos in one list we can compare lori's.
-func (f *KindRun) Len() int           { return len(f.Infos) }
-func (f *KindRun) Less(i, j int) bool { return f.Infos[i].Lori() < f.Infos[j].Lori() }
-func (f *KindRun) Swap(i, j int)      { f.Infos[i], f.Infos[j] = f.Infos[j], f.Infos[i] }
+func (k KindRun) Len() int           { return len(k) }
+func (k KindRun) Less(i, j int) bool { return k[i].Lori() < k[j].Lori() }
+func (k KindRun) Swap(i, j int)      { k[i], k[j] = k[j], k[i] }
 
 // FileRun contents are sorted by Kind for the reduction into KindRuns.
 func lessKind(x, y interface{}) bool { return x.(SpotInfo).Kind() < y.(SpotInfo).Kind() }
 
-// newKindRun allocates a new KindRun from the SpotInfo run [i, j) in h.
-func newKindRun(h *RunList, i, j int) interface{} {
-	kind := h.At(i).(SpotInfo).Kind()
-	infos := make([]SpotInfo, j-i)
-	k := 0
-	for ; i < j; i++ {
-		infos[k] = h.At(i).(SpotInfo)
-		k++
+// newKindRun allocates a new KindRun from the SpotInfo run h.
+func newKindRun(h RunList) interface{} {
+	run := make(KindRun, len(h))
+	for i, x := range h {
+		run[i] = x.(SpotInfo)
 	}
-	run := &KindRun{kind, infos}
 
 	// Spots were sorted by file and kind to create this run.
 	// Within this run, sort them by line number or index.
@@ -199,15 +209,15 @@ func newKindRun(h *RunList, i, j int) interface{} {
 		// bit is always the same for all infos in one
 		// list we can simply compare the entire info.
 		k := 0
-		var prev SpotInfo
-		for i, x := range infos {
-			if x != prev || i == 0 {
-				infos[k] = x
+		prev := SpotInfo(1<<32 - 1) // an unlikely value
+		for _, x := range run {
+			if x != prev {
+				run[k] = x
 				k++
 				prev = x
 			}
 		}
-		run.Infos = infos[0:k]
+		run = run[0:k]
 	}
 
 	return run
@@ -229,8 +239,13 @@ func (p *Pak) less(q *Pak) bool {
 
 // A File describes a Go file.
 type File struct {
-	Path string // complete file name
-	Pak  Pak    // the package to which the file belongs
+	Name string // directory-local file name
+	Pak  *Pak   // the package to which the file belongs
+}
+
+// Path returns the file path of f.
+func (f *File) Path() string {
+	return filepath.Join(f.Pak.Path, f.Name)
 }
 
 // A Spot describes a single occurrence of a word.
@@ -242,30 +257,34 @@ type Spot struct {
 // A FileRun is a list of KindRuns belonging to the same file.
 type FileRun struct {
 	File   *File
-	Groups []*KindRun
+	Groups []KindRun
 }
 
-// Spots are sorted by path for the reduction into FileRuns.
-func lessSpot(x, y interface{}) bool { return x.(Spot).File.Path < y.(Spot).File.Path }
+// Spots are sorted by file path for the reduction into FileRuns.
+func lessSpot(x, y interface{}) bool {
+	fx := x.(Spot).File
+	fy := y.(Spot).File
+	// same as "return fx.Path() < fy.Path()" but w/o computing the file path first
+	px := fx.Pak.Path
+	py := fy.Pak.Path
+	return px < py || px == py && fx.Name < fy.Name
+}
 
-// newFileRun allocates a new FileRun from the Spot run [i, j) in h.
-func newFileRun(h0 *RunList, i, j int) interface{} {
-	file := h0.At(i).(Spot).File
+// newFileRun allocates a new FileRun from the Spot run h.
+func newFileRun(h RunList) interface{} {
+	file := h[0].(Spot).File
 
 	// reduce the list of Spots into a list of KindRuns
-	var h1 RunList
-	h1.Vector.Resize(j-i, 0)
-	k := 0
-	for ; i < j; i++ {
-		h1.Set(k, h0.At(i).(Spot).Info)
-		k++
+	h1 := make(RunList, len(h))
+	for i, x := range h {
+		h1[i] = x.(Spot).Info
 	}
 	h2 := h1.reduce(lessKind, newKindRun)
 
 	// create the FileRun
-	groups := make([]*KindRun, h2.Len())
-	for i := 0; i < h2.Len(); i++ {
-		groups[i] = h2.At(i).(*KindRun)
+	groups := make([]KindRun, len(h2))
+	for i, x := range h2 {
+		groups[i] = x.(KindRun)
 	}
 	return &FileRun{file, groups}
 }
@@ -275,28 +294,26 @@ func newFileRun(h0 *RunList, i, j int) interface{} {
 
 // A PakRun describes a run of *FileRuns of a package.
 type PakRun struct {
-	Pak   Pak
+	Pak   *Pak
 	Files []*FileRun
 }
 
 // Sorting support for files within a PakRun.
 func (p *PakRun) Len() int           { return len(p.Files) }
-func (p *PakRun) Less(i, j int) bool { return p.Files[i].File.Path < p.Files[j].File.Path }
+func (p *PakRun) Less(i, j int) bool { return p.Files[i].File.Name < p.Files[j].File.Name }
 func (p *PakRun) Swap(i, j int)      { p.Files[i], p.Files[j] = p.Files[j], p.Files[i] }
 
 // FileRuns are sorted by package for the reduction into PakRuns.
 func lessFileRun(x, y interface{}) bool {
-	return x.(*FileRun).File.Pak.less(&y.(*FileRun).File.Pak)
+	return x.(*FileRun).File.Pak.less(y.(*FileRun).File.Pak)
 }
 
-// newPakRun allocates a new PakRun from the *FileRun run [i, j) in h.
-func newPakRun(h *RunList, i, j int) interface{} {
-	pak := h.At(i).(*FileRun).File.Pak
-	files := make([]*FileRun, j-i)
-	k := 0
-	for ; i < j; i++ {
-		files[k] = h.At(i).(*FileRun)
-		k++
+// newPakRun allocates a new PakRun from the *FileRun run h.
+func newPakRun(h RunList) interface{} {
+	pak := h[0].(*FileRun).File.Pak
+	files := make([]*FileRun, len(h))
+	for i, x := range h {
+		files[i] = x.(*FileRun)
 	}
 	run := &PakRun{pak, files}
 	sort.Sort(run) // files were sorted by package; sort them by file now
@@ -310,9 +327,9 @@ func newPakRun(h *RunList, i, j int) interface{} {
 type HitList []*PakRun
 
 // PakRuns are sorted by package.
-func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(&y.(*PakRun).Pak) }
+func lessPakRun(x, y interface{}) bool { return x.(*PakRun).Pak.less(y.(*PakRun).Pak) }
 
-func reduce(h0 *RunList) HitList {
+func reduce(h0 RunList) HitList {
 	// reduce a list of Spots into a list of FileRuns
 	h1 := h0.reduce(lessSpot, newFileRun)
 	// reduce a list of FileRuns into a list of PakRuns
@@ -320,28 +337,18 @@ func reduce(h0 *RunList) HitList {
 	// sort the list of PakRuns by package
 	h2.sort(lessPakRun)
 	// create a HitList
-	h := make(HitList, h2.Len())
-	for i := 0; i < h2.Len(); i++ {
-		h[i] = h2.At(i).(*PakRun)
+	h := make(HitList, len(h2))
+	for i, p := range h2 {
+		h[i] = p.(*PakRun)
 	}
 	return h
 }
 
 func (h HitList) filter(pakname string) HitList {
-	// determine number of matching packages (most of the time just one)
-	n := 0
+	var hh HitList
 	for _, p := range h {
 		if p.Pak.Name == pakname {
-			n++
-		}
-	}
-	// create filtered HitList
-	hh := make(HitList, n)
-	i := 0
-	for _, p := range h {
-		if p.Pak.Name == pakname {
-			hh[i] = p
-			i++
+			hh = append(hh, p)
 		}
 	}
 	return hh
@@ -365,34 +372,27 @@ type AltWords struct {
 // wordPairs are sorted by their canonical spelling.
 func lessWordPair(x, y interface{}) bool { return x.(*wordPair).canon < y.(*wordPair).canon }
 
-// newAltWords allocates a new AltWords from the *wordPair run [i, j) in h.
-func newAltWords(h *RunList, i, j int) interface{} {
-	canon := h.At(i).(*wordPair).canon
-	alts := make([]string, j-i)
-	k := 0
-	for ; i < j; i++ {
-		alts[k] = h.At(i).(*wordPair).alt
-		k++
+// newAltWords allocates a new AltWords from the *wordPair run h.
+func newAltWords(h RunList) interface{} {
+	canon := h[0].(*wordPair).canon
+	alts := make([]string, len(h))
+	for i, x := range h {
+		alts[i] = x.(*wordPair).alt
 	}
 	return &AltWords{canon, alts}
 }
 
 func (a *AltWords) filter(s string) *AltWords {
-	if len(a.Alts) == 1 && a.Alts[0] == s {
-		// there are no different alternatives
-		return nil
-	}
-
-	// make a new AltWords with the current spelling removed
-	alts := make([]string, len(a.Alts))
-	i := 0
+	var alts []string
 	for _, w := range a.Alts {
 		if w != s {
-			alts[i] = w
-			i++
+			alts = append(alts, w)
 		}
 	}
-	return &AltWords{a.Canon, alts[0:i]}
+	if len(alts) > 0 {
+		return &AltWords{a.Canon, alts}
+	}
+	return nil
 }
 
 // ----------------------------------------------------------------------------
@@ -423,17 +423,32 @@ type Statistics struct {
 type Indexer struct {
 	fset     *token.FileSet          // file set for all indexed files
 	sources  bytes.Buffer            // concatenated sources
+	packages map[string]*Pak         // map of canonicalized *Paks
 	words    map[string]*IndexResult // RunLists of Spots
-	snippets vector.Vector           // vector of *Snippets, indexed by snippet indices
+	snippets []*Snippet              // indices are stored in SpotInfos
 	current  *token.File             // last file added to file set
 	file     *File                   // AST for current file
 	decl     ast.Decl                // AST for current decl
 	stats    Statistics
 }
 
+func (x *Indexer) lookupPackage(path, name string) *Pak {
+	// In the source directory tree, more than one package may
+	// live in the same directory. For the packages map, construct
+	// a key that includes both the directory path and the package
+	// name.
+	key := path + ":" + name
+	pak := x.packages[key]
+	if pak == nil {
+		pak = &Pak{path, name}
+		x.packages[key] = pak
+	}
+	return pak
+}
+
 func (x *Indexer) addSnippet(s *Snippet) int {
-	index := x.snippets.Len()
-	x.snippets.Push(s)
+	index := len(x.snippets)
+	x.snippets = append(x.snippets, s)
 	return index
 }
 
@@ -454,12 +469,12 @@ func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) {
 		if kind == Use || x.decl == nil {
 			// not a declaration or no snippet required
 			info := makeSpotInfo(kind, x.current.Line(id.Pos()), false)
-			lists.Others.Push(Spot{x.file, info})
+			lists.Others = append(lists.Others, Spot{x.file, info})
 		} else {
 			// a declaration with snippet
 			index := x.addSnippet(NewSnippet(x.fset, x.decl, id))
 			info := makeSpotInfo(kind, index, true)
-			lists.Decls.Push(Spot{x.file, info})
+			lists.Decls = append(lists.Decls, Spot{x.file, info})
 		}
 
 		x.stats.Spots++
@@ -713,9 +728,8 @@ func (x *Indexer) visitFile(dirname string, f FileInfo, fulltextIndex bool) {
 	if fast != nil {
 		// we've got a Go file to index
 		x.current = file
-		dir, _ := filepath.Split(filename)
-		pak := Pak{dir, fast.Name.Name}
-		x.file = &File{filename, pak}
+		pak := x.lookupPackage(dirname, fast.Name.Name)
+		x.file = &File{f.Name(), pak}
 		ast.Walk(x, fast)
 	}
 
@@ -747,12 +761,15 @@ func canonical(w string) string { return strings.ToLower(w) }
 // NewIndex creates a new index for the .go files
 // in the directories given by dirnames.
 //
-func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
+func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Index {
 	var x Indexer
+	th := NewThrottle(throttle, 0.1e9) // run at least 0.1s at a time
 
 	// initialize Indexer
+	// (use some reasonably sized maps to start)
 	x.fset = token.NewFileSet()
-	x.words = make(map[string]*IndexResult)
+	x.packages = make(map[string]*Pak, 256)
+	x.words = make(map[string]*IndexResult, 8192)
 
 	// index all files in the directories given by dirnames
 	for dirname := range dirnames {
@@ -764,6 +781,7 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
 			if !f.IsDirectory() {
 				x.visitFile(dirname, f, fulltextIndex)
 			}
+			th.Throttle()
 		}
 	}
 
@@ -782,13 +800,14 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
 	words := make(map[string]*LookupResult)
 	var wlist RunList
 	for w, h := range x.words {
-		decls := reduce(&h.Decls)
-		others := reduce(&h.Others)
+		decls := reduce(h.Decls)
+		others := reduce(h.Others)
 		words[w] = &LookupResult{
 			Decls:  decls,
 			Others: others,
 		}
-		wlist.Push(&wordPair{canonical(w), w})
+		wlist = append(wlist, &wordPair{canonical(w), w})
+		th.Throttle()
 	}
 	x.stats.Words = len(words)
 
@@ -798,24 +817,49 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index {
 
 	// convert alist into a map of alternative spellings
 	alts := make(map[string]*AltWords)
-	for i := 0; i < alist.Len(); i++ {
-		a := alist.At(i).(*AltWords)
+	for i := 0; i < len(alist); i++ {
+		a := alist[i].(*AltWords)
 		alts[a.Canon] = a
 	}
 
-	// convert snippet vector into a list
-	snippets := make([]*Snippet, x.snippets.Len())
-	for i := 0; i < x.snippets.Len(); i++ {
-		snippets[i] = x.snippets.At(i).(*Snippet)
-	}
-
 	// create text index
 	var suffixes *suffixarray.Index
 	if fulltextIndex {
 		suffixes = suffixarray.New(x.sources.Bytes())
 	}
 
-	return &Index{x.fset, suffixes, words, alts, snippets, x.stats}
+	return &Index{x.fset, suffixes, words, alts, x.snippets, x.stats}
+}
+
+type FileIndex struct {
+	Words    map[string]*LookupResult
+	Alts     map[string]*AltWords
+	Snippets []*Snippet
+}
+
+// Write writes the index x to w.
+func (x *Index) Write(w io.Writer) os.Error {
+	if x.suffixes != nil {
+		panic("no support for writing full text index yet")
+	}
+	fx := FileIndex{
+		x.words,
+		x.alts,
+		x.snippets,
+	}
+	return gob.NewEncoder(w).Encode(fx)
+}
+
+// Read reads the index from r into x; x must not be nil.
+func (x *Index) Read(r io.Reader) os.Error {
+	var fx FileIndex
+	if err := gob.NewDecoder(r).Decode(&fx); err != nil {
+		return err
+	}
+	x.words = fx.Words
+	x.alts = fx.Alts
+	x.snippets = fx.Snippets
+	return nil
 }
 
 // Stats() returns index statistics.
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 89b12b9..74d3111 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -54,6 +54,9 @@ var (
 	// (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico)
 	zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty")
 
+	// file-based index
+	writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files")
+
 	// periodic sync
 	syncCmd   = flag.String("sync", "", "sync command; disabled if empty")
 	syncMin   = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0")
@@ -221,8 +224,8 @@ func main() {
 	flag.Usage = usage
 	flag.Parse()
 
-	// Check usage: either server and no args, or command line and args
-	if (*httpAddr != "") != (flag.NArg() == 0) {
+	// Check usage: either server and no args, command line and args, or index creation mode
+	if (*httpAddr != "") != (flag.NArg() == 0) && !*writeIndex {
 		usage()
 	}
 
@@ -245,13 +248,48 @@ func main() {
 		if err != nil {
 			log.Fatalf("%s: %s\n", *zipfile, err)
 		}
+		defer rc.Close()                  // be nice (e.g., -writeIndex mode)
 		*goroot = path.Join("/", *goroot) // fsHttp paths are relative to '/'
 		fs = NewZipFS(rc)
 		fsHttp = NewHttpZipFS(rc, *goroot)
 	}
 
-	initHandlers()
 	readTemplates()
+	initHandlers()
+
+	if (*indexEnabled || *writeIndex) && *indexFiles != "" && *maxResults > 0 {
+		log.Println("warning: no support for full-text index yet (setting -maxresults to 0)")
+		*maxResults = 0
+	}
+
+	if *writeIndex {
+		// Write search index and exit.
+		if *indexFiles == "" {
+			log.Fatal("no index file specified")
+		}
+
+		log.Println("initialize file systems")
+		*verbose = true // want to see what happens
+		initFSTree()
+		initDirTrees()
+
+		*indexThrottle = 1
+		updateIndex()
+
+		log.Println("writing index file", *indexFiles)
+		f, err := os.Create(*indexFiles)
+		if err != nil {
+			log.Fatal(err)
+		}
+		index, _ := searchIndex.get()
+		err = index.(*Index).Write(f)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		log.Println("done")
+		return
+	}
 
 	if *httpAddr != "" {
 		// HTTP server mode.
@@ -304,7 +342,7 @@ func main() {
 			}()
 		}
 
-		// Start indexing goroutine.
+		// Initialize search index.
 		if *indexEnabled {
 			go indexer()
 		}
diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go
index da4b385..cc1780a 100644
--- a/src/cmd/godoc/parser.go
+++ b/src/cmd/godoc/parser.go
@@ -17,18 +17,18 @@ import (
 	"path/filepath"
 )
 
+func parseFile(fset *token.FileSet, filename string, mode uint) (*ast.File, os.Error) {
+	src, err := fs.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+	return parser.ParseFile(fset, filename, src, mode)
+}
+
 func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first os.Error) {
 	pkgs = make(map[string]*ast.Package)
 	for _, filename := range filenames {
-		src, err := fs.ReadFile(filename)
-		if err != nil {
-			if first == nil {
-				first = err
-			}
-			continue
-		}
-
-		file, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
+		file, err := parseFile(fset, filename, parser.ParseComments)
 		if err != nil {
 			if first == nil {
 				first = err
diff --git a/src/cmd/godoc/snippet.go b/src/cmd/godoc/snippet.go
old mode 100755
new mode 100644
diff --git a/src/cmd/godoc/throttle.go b/src/cmd/godoc/throttle.go
new file mode 100644
index 0000000..1934928
--- /dev/null
+++ b/src/cmd/godoc/throttle.go
@@ -0,0 +1,88 @@
+// 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 main
+
+import "time"
+
+// A Throttle permits throttling of a goroutine by
+// calling the Throttle method repeatedly.
+//
+type Throttle struct {
+	f  float64 // f = (1-r)/r for 0 < r < 1
+	tm int64   // minimum run time slice; >= 0
+	tr int64   // accumulated time running
+	ts int64   // accumulated time stopped
+	tt int64   // earliest throttle time (= time Throttle returned + tm)
+}
+
+// NewThrottle creates a new Throttle with a throttle value r and
+// a minimum allocated run time slice of tm nanoseconds:
+//
+//	r == 0: "empty" throttle; the goroutine is always sleeping
+//	r == 1: full throttle; the goroutine is never sleeping
+//
+// A value of r == 0.6 throttles a goroutine such that it runs
+// approx. 60% of the time, and sleeps approx. 40% of the time.
+// Values of r < 0 or r > 1 are clamped down to values between 0 and 1.
+// Values of tm < 0 are set to 0.
+//
+func NewThrottle(r float64, tm int64) *Throttle {
+	var f float64
+	switch {
+	case r <= 0:
+		f = -1 // indicates always sleep
+	case r >= 1:
+		f = 0 // assume r == 1 (never sleep)
+	default:
+		// 0 < r < 1
+		f = (1 - r) / r
+	}
+	if tm < 0 {
+		tm = 0
+	}
+	return &Throttle{f: f, tm: tm, tt: time.Nanoseconds() + tm}
+}
+
+// Throttle calls time.Sleep such that over time the ratio tr/ts between
+// accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r)
+// where r is the throttle value. Throttle returns immediately (w/o sleeping)
+// if less than tm ns have passed since the last call to Throttle.
+//
+func (p *Throttle) Throttle() {
+	if p.f < 0 {
+		select {} // always sleep
+	}
+
+	t0 := time.Nanoseconds()
+	if t0 < p.tt {
+		return // keep running (minimum time slice not exhausted yet)
+	}
+
+	// accumulate running time
+	p.tr += t0 - (p.tt - p.tm)
+
+	// compute sleep time
+	// Over time we want:
+	//
+	//	tr/ts = r/(1-r)
+	//
+	// Thus:
+	//
+	//	ts = tr*f with f = (1-r)/r
+	//
+	// After some incremental run time δr added to the total run time
+	// tr, the incremental sleep-time δs to get to the same ratio again
+	// after waking up from time.Sleep is:
+	if δs := int64(float64(p.tr)*p.f) - p.ts; δs > 0 {
+		time.Sleep(δs)
+	}
+
+	// accumulate (actual) sleep time
+	t1 := time.Nanoseconds()
+	p.ts += t1 - t0
+
+	// set earliest next throttle time
+	p.tt = t1 + p.tm
+}
diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go
index 27dc142..46d7112 100644
--- a/src/cmd/godoc/zip.go
+++ b/src/cmd/godoc/zip.go
@@ -183,9 +183,10 @@ func (z zipList) lookup(name string) (index int, exact bool) {
 	i := sort.Search(len(z), func(i int) bool {
 		return name <= z[i].Name
 	})
-	if i < 0 {
+	if i >= len(z) {
 		return -1, false
 	}
+	// 0 <= i < len(z)
 	if z[i].Name == name {
 		return i, true
 	}
@@ -196,9 +197,10 @@ func (z zipList) lookup(name string) (index int, exact bool) {
 	j := sort.Search(len(z), func(i int) bool {
 		return name <= z[i].Name
 	})
-	if j < 0 {
+	if j >= len(z) {
 		return -1, false
 	}
+	// 0 <= j < len(z)
 	if strings.HasPrefix(z[j].Name, name) {
 		return i + j, false
 	}
diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile
index 22033d7..d1f3ac6 100644
--- a/src/cmd/gofix/Makefile
+++ b/src/cmd/gofix/Makefile
@@ -14,6 +14,7 @@ GOFILES=\
 	httpserver.go\
 	main.go\
 	netdial.go\
+	netudpgroup.go\
 	oserrorstring.go\
 	osopen.go\
 	procattr.go\
diff --git a/src/cmd/gofix/netudpgroup.go b/src/cmd/gofix/netudpgroup.go
new file mode 100644
index 0000000..347452d
--- /dev/null
+++ b/src/cmd/gofix/netudpgroup.go
@@ -0,0 +1,57 @@
+// 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 main
+
+import (
+	"go/ast"
+)
+
+var netudpgroupFix = fix{
+	"netudpgroup",
+	netudpgroup,
+	`Adapt 1-argument calls of net.(*UDPConn).JoinGroup, LeaveGroup to use 2-argument form.
+
+http://codereview.appspot.com/4815074
+`,
+}
+
+func init() {
+	register(netudpgroupFix)
+}
+
+func netudpgroup(f *ast.File) bool {
+	if !imports(f, "net") {
+		return false
+	}
+
+	fixed := false
+	for _, d := range f.Decls {
+		fd, ok := d.(*ast.FuncDecl)
+		if !ok {
+			continue
+		}
+		walk(fd.Body, func(n interface{}) {
+			ce, ok := n.(*ast.CallExpr)
+			if !ok {
+				return
+			}
+			se, ok := ce.Fun.(*ast.SelectorExpr)
+			if !ok || len(ce.Args) != 1 {
+				return
+			}
+			switch se.Sel.String() {
+			case "JoinGroup", "LeaveGroup":
+				// c.JoinGroup(a) -> c.JoinGroup(nil, a)
+				// c.LeaveGroup(a) -> c.LeaveGroup(nil, a)
+				arg := ce.Args[0]
+				ce.Args = make([]ast.Expr, 2)
+				ce.Args[0] = ast.NewIdent("nil")
+				ce.Args[1] = arg
+				fixed = true
+			}
+		})
+	}
+	return fixed
+}
diff --git a/src/cmd/gofix/netudpgroup_test.go b/src/cmd/gofix/netudpgroup_test.go
new file mode 100644
index 0000000..b3b5816
--- /dev/null
+++ b/src/cmd/gofix/netudpgroup_test.go
@@ -0,0 +1,33 @@
+// 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 main
+
+func init() {
+	addTestCases(netudpgroupTests)
+}
+
+var netudpgroupTests = []testCase{
+	{
+		Name: "netudpgroup.0",
+		In: `package main
+
+import "net"
+
+func f() {
+	err := x.JoinGroup(gaddr)
+	err = y.LeaveGroup(gaddr)
+}
+`,
+		Out: `package main
+
+import "net"
+
+func f() {
+	err := x.JoinGroup(nil, gaddr)
+	err = y.LeaveGroup(nil, gaddr)
+}
+`,
+	},
+}
diff --git a/src/cmd/gofix/osopen.go b/src/cmd/gofix/osopen.go
index 56147c3..19c19b5 100644
--- a/src/cmd/gofix/osopen.go
+++ b/src/cmd/gofix/osopen.go
@@ -31,6 +31,7 @@ func osopen(f *ast.File) bool {
 		// Rename O_CREAT to O_CREATE.
 		if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") {
 			expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE"
+			fixed = true
 			return
 		}
 
diff --git a/src/cmd/gofix/osopen_test.go b/src/cmd/gofix/osopen_test.go
index 43ddd1a..a33bcd4 100644
--- a/src/cmd/gofix/osopen_test.go
+++ b/src/cmd/gofix/osopen_test.go
@@ -56,4 +56,27 @@ func f() {
 }
 `,
 	},
+	{
+		Name: "osopen.1",
+		In: `package main
+
+import (
+	"os"
+)
+
+func f() {
+	_ = os.O_CREAT
+}
+`,
+		Out: `package main
+
+import (
+	"os"
+)
+
+func f() {
+	_ = os.O_CREATE
+}
+`,
+	},
 }
diff --git a/src/cmd/gofix/url.go b/src/cmd/gofix/url.go
index c1e47bd..7135d8e 100644
--- a/src/cmd/gofix/url.go
+++ b/src/cmd/gofix/url.go
@@ -27,6 +27,7 @@ func init() {
 }
 
 var urlRenames = []struct{ in, out string }{
+	{"URL", "URL"},
 	{"ParseURL", "Parse"},
 	{"ParseURLReference", "ParseWithReference"},
 	{"ParseQuery", "ParseQuery"},
@@ -45,7 +46,12 @@ func url(f *ast.File) bool {
 	fixed := false
 
 	// Update URL code.
+	var skip interface{}
 	urlWalk := func(n interface{}) {
+		if n == skip {
+			skip = nil
+			return
+		}
 		// Is it an identifier?
 		if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" {
 			ident.Name = "url_"
@@ -56,6 +62,12 @@ func url(f *ast.File) bool {
 			fixed = urlDoFields(fn.Params) || fixed
 			fixed = urlDoFields(fn.Results) || fixed
 		}
+		// U{url: ...} is likely a struct field.
+		if kv, ok := n.(*ast.KeyValueExpr); ok {
+			if ident, ok := kv.Key.(*ast.Ident); ok && ident.Name == "url" {
+				skip = ident
+			}
+		}
 	}
 
 	// Fix up URL code and add import, at most once.
@@ -63,7 +75,7 @@ func url(f *ast.File) bool {
 		if fixed {
 			return
 		}
-		walk(f, urlWalk)
+		walkBeforeAfter(f, urlWalk, nop)
 		addImport(f, "url")
 		fixed = true
 	}
diff --git a/src/cmd/gofix/url_test.go b/src/cmd/gofix/url_test.go
index 1a7095a..8d9542c 100644
--- a/src/cmd/gofix/url_test.go
+++ b/src/cmd/gofix/url_test.go
@@ -18,6 +18,7 @@ import (
 )
 
 func f() {
+	var _ http.URL
 	http.ParseURL(a)
 	http.ParseURLReference(a)
 	http.ParseQuery(a)
@@ -33,6 +34,7 @@ func f() {
 import "url"
 
 func f() {
+	var _ url.URL
 	url.Parse(a)
 	url.ParseWithReference(a)
 	url.ParseQuery(a)
@@ -78,10 +80,15 @@ import (
 	"http"
 )
 
+type U struct{ url int }
+type M map[int]int
+
 func f() {
 	http.ParseURL(a)
 	var url = 23
 	url, x := 45, y
+	_ = U{url: url}
+	_ = M{url + 1: url}
 }
 
 func g(url string) string {
@@ -96,10 +103,15 @@ func h() (url string) {
 
 import "url"
 
+type U struct{ url int }
+type M map[int]int
+
 func f() {
 	url.Parse(a)
 	var url_ = 23
 	url_, x := 45, y
+	_ = U{url: url_}
+	_ = M{url_ + 1: url_}
 }
 
 func g(url_ string) string {
diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile
index f61354f..b906469 100644
--- a/src/cmd/goinstall/Makefile
+++ b/src/cmd/goinstall/Makefile
@@ -11,3 +11,9 @@ GOFILES=\
 	make.go\
 
 include ../../Make.cmd
+
+test:
+	gotest
+
+testshort:
+	gotest -test.short
diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go
index 8260cb4..47c6153 100644
--- a/src/cmd/goinstall/doc.go
+++ b/src/cmd/goinstall/doc.go
@@ -94,8 +94,11 @@ attempt to fetch updates.  The -u flag changes this behavior,
 causing goinstall to update all remote packages encountered during
 the installation.
 
-When downloading or updating, goinstall first looks for a tag or branch
-named "release".  If there is one, it uses that version of the code.
+When downloading or updating, goinstall looks for a tag with the "go." prefix
+that corresponds to the local Go version. For Go "release.r58" it looks for a
+tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
+If the specific "go.X" tag is not found, it chooses the closest earlier version.
+If an appropriate tag is found, goinstall uses that version of the code.
 Otherwise it uses the default version selected by the version control
 system, typically HEAD for git, tip for Mercurial.
 
diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go
index 3e9927c..cc87315 100644
--- a/src/cmd/goinstall/download.go
+++ b/src/cmd/goinstall/download.go
@@ -7,12 +7,15 @@
 package main
 
 import (
+	"bytes"
 	"exec"
 	"fmt"
 	"http"
 	"os"
 	"path/filepath"
 	"regexp"
+	"runtime"
+	"strconv"
 	"strings"
 )
 
@@ -36,22 +39,21 @@ func maybeReportToDashboard(path string) {
 // a vcs represents a version control system
 // like Mercurial, Git, or Subversion.
 type vcs struct {
-	name              string
-	cmd               string
-	metadir           string
-	checkout          string
-	clone             string
-	update            string
-	updateReleaseFlag string
-	pull              string
-	pullForceFlag     string
-	log               string
-	logLimitFlag      string
-	logReleaseFlag    string
-	check             string
-	protocols         []string
-	suffix            string
-	defaultHosts      []host
+	name          string
+	cmd           string
+	metadir       string
+	checkout      string
+	clone         string
+	update        string
+	updateRevFlag string
+	pull          string
+	pullForceFlag string
+	tagList       string
+	tagListRe     *regexp.Regexp
+	check         string
+	protocols     []string
+	suffix        string
+	defaultHosts  []host
 }
 
 type host struct {
@@ -61,20 +63,18 @@ type host struct {
 }
 
 var hg = vcs{
-	name:              "Mercurial",
-	cmd:               "hg",
-	metadir:           ".hg",
-	checkout:          "checkout",
-	clone:             "clone",
-	update:            "update",
-	updateReleaseFlag: "release",
-	pull:              "pull",
-	log:               "log",
-	logLimitFlag:      "-l1",
-	logReleaseFlag:    "-rrelease",
-	check:             "identify",
-	protocols:         []string{"https", "http"},
-	suffix:            ".hg",
+	name:      "Mercurial",
+	cmd:       "hg",
+	metadir:   ".hg",
+	checkout:  "checkout",
+	clone:     "clone",
+	update:    "update",
+	pull:      "pull",
+	tagList:   "tags",
+	tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
+	check:     "identify",
+	protocols: []string{"https", "http"},
+	suffix:    ".hg",
 	defaultHosts: []host{
 		{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
 		{regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
@@ -82,20 +82,18 @@ var hg = vcs{
 }
 
 var git = vcs{
-	name:              "Git",
-	cmd:               "git",
-	metadir:           ".git",
-	checkout:          "checkout",
-	clone:             "clone",
-	update:            "pull",
-	updateReleaseFlag: "release",
-	pull:              "fetch",
-	log:               "show-ref",
-	logLimitFlag:      "",
-	logReleaseFlag:    "release",
-	check:             "ls-remote",
-	protocols:         []string{"git", "https", "http"},
-	suffix:            ".git",
+	name:      "Git",
+	cmd:       "git",
+	metadir:   ".git",
+	checkout:  "checkout",
+	clone:     "clone",
+	update:    "pull",
+	pull:      "fetch",
+	tagList:   "tag",
+	tagListRe: regexp.MustCompile("([^\n]+)\n"),
+	check:     "ls-remote",
+	protocols: []string{"git", "https", "http"},
+	suffix:    ".git",
 	defaultHosts: []host{
 		{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
 		{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
@@ -103,40 +101,35 @@ var git = vcs{
 }
 
 var svn = vcs{
-	name:              "Subversion",
-	cmd:               "svn",
-	metadir:           ".svn",
-	checkout:          "checkout",
-	clone:             "checkout",
-	update:            "update",
-	updateReleaseFlag: "release",
-	log:               "log",
-	logLimitFlag:      "-l1",
-	logReleaseFlag:    "release",
-	check:             "info",
-	protocols:         []string{"https", "http", "svn"},
-	suffix:            ".svn",
+	name:      "Subversion",
+	cmd:       "svn",
+	metadir:   ".svn",
+	checkout:  "checkout",
+	clone:     "checkout",
+	update:    "update",
+	check:     "info",
+	protocols: []string{"https", "http", "svn"},
+	suffix:    ".svn",
 	defaultHosts: []host{
 		{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
 	},
 }
 
 var bzr = vcs{
-	name:              "Bazaar",
-	cmd:               "bzr",
-	metadir:           ".bzr",
-	checkout:          "update",
-	clone:             "branch",
-	update:            "update",
-	updateReleaseFlag: "-rrelease",
-	pull:              "pull",
-	pullForceFlag:     "--overwrite",
-	log:               "log",
-	logLimitFlag:      "-l1",
-	logReleaseFlag:    "-rrelease",
-	check:             "info",
-	protocols:         []string{"https", "http", "bzr"},
-	suffix:            ".bzr",
+	name:          "Bazaar",
+	cmd:           "bzr",
+	metadir:       ".bzr",
+	checkout:      "update",
+	clone:         "branch",
+	update:        "update",
+	updateRevFlag: "-r",
+	pull:          "pull",
+	pullForceFlag: "--overwrite",
+	tagList:       "tags",
+	tagListRe:     regexp.MustCompile("([^ ]+)[^\n]+\n"),
+	check:         "info",
+	protocols:     []string{"https", "http", "bzr"},
+	suffix:        ".bzr",
 	defaultHosts: []host{
 		{regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
 	},
@@ -240,20 +233,84 @@ func download(pkg, srcDir string) (public bool, err os.Error) {
 	return
 }
 
-// Try to detect if a "release" tag exists.  If it does, update
-// to the tagged version, otherwise just update the current branch.
-// NOTE(_nil): svn will always fail because it is trying to get
-// the revision history of a file named "release" instead of
-// looking for a commit with a release tag
+// updateRepo gets a list of tags in the repository and
+// checks out the tag closest to the current runtime.Version.
+// If no matching tag is found, it just updates to tip.
 func (v *vcs) updateRepo(dst string) os.Error {
-	if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
-		if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
-			return err
-		}
-	} else if err := run(dst, nil, v.cmd, v.update); err != nil {
+	if v.tagList == "" || v.tagListRe == nil {
+		// TODO(adg): fix for svn
+		return run(dst, nil, v.cmd, v.update)
+	}
+
+	// Get tag list.
+	stderr := new(bytes.Buffer)
+	cmd := exec.Command(v.cmd, v.tagList)
+	cmd.Dir = dst
+	cmd.Stderr = stderr
+	b, err := cmd.Output()
+	if err != nil {
+		errorf("%s %s: %s\n", v.cmd, v.tagList, stderr)
 		return err
 	}
-	return nil
+	var tags []string
+	for _, m := range v.tagListRe.FindAllStringSubmatch(string(b), -1) {
+		tags = append(tags, m[1])
+	}
+
+	// Only use the tag component of runtime.Version.
+	ver := strings.Split(runtime.Version(), " ")[0]
+
+	// Select tag.
+	if tag := selectTag(ver, tags); tag != "" {
+		printf("selecting revision %q\n", tag)
+		return run(dst, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
+	}
+
+	// No matching tag found, make default selection.
+	printf("selecting tip\n")
+	return run(dst, nil, v.cmd, v.update)
+}
+
+// selectTag returns the closest matching tag for a given version.
+// Closest means the latest one that is not after the current release.
+// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
+// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
+func selectTag(goVersion string, tags []string) (match string) {
+	const rPrefix = "release.r"
+	if strings.HasPrefix(goVersion, rPrefix) {
+		p := "go.r"
+		v, err := strconv.Atof64(goVersion[len(rPrefix):])
+		if err != nil {
+			return ""
+		}
+		var matchf float64
+		for _, t := range tags {
+			if !strings.HasPrefix(t, p) {
+				continue
+			}
+			tf, err := strconv.Atof64(t[len(p):])
+			if err != nil {
+				continue
+			}
+			if matchf < tf && tf <= v {
+				match, matchf = t, tf
+			}
+		}
+	}
+	const wPrefix = "weekly."
+	if strings.HasPrefix(goVersion, wPrefix) {
+		p := "go.weekly."
+		v := goVersion[len(wPrefix):]
+		for _, t := range tags {
+			if !strings.HasPrefix(t, p) {
+				continue
+			}
+			if match < t && t[len(p):] <= v {
+				match = t
+			}
+		}
+	}
+	return match
 }
 
 // checkoutRepo checks out repo into dst using vcs.
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
index 910ab70..23b26e3 100644
--- a/src/cmd/goinstall/main.go
+++ b/src/cmd/goinstall/main.go
@@ -20,8 +20,8 @@ import (
 )
 
 func usage() {
-	fmt.Fprint(os.Stderr, "usage: goinstall importpath...\n")
-	fmt.Fprintf(os.Stderr, "\tgoinstall -a\n")
+	fmt.Fprintln(os.Stderr, "usage: goinstall [flags] importpath...")
+	fmt.Fprintln(os.Stderr, "       goinstall [flags] -a")
 	flag.PrintDefaults()
 	os.Exit(2)
 }
@@ -71,6 +71,13 @@ func errorf(format string, args ...interface{}) {
 	logf(format, args...)
 }
 
+func terrorf(tree *build.Tree, format string, args ...interface{}) {
+	if tree != nil && tree.Goroot && os.Getenv("GOPATH") == "" {
+		format = strings.TrimRight(format, "\n") + " ($GOPATH not set)\n"
+	}
+	errorf(format, args...)
+}
+
 func main() {
 	flag.Usage = usage
 	flag.Parse()
@@ -156,7 +163,7 @@ func logPackage(pkg string, tree *build.Tree) (logged bool) {
 	name := filepath.Join(tree.Path, logfile)
 	fout, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
 	if err != nil {
-		logf("%s\n", err)
+		terrorf(tree, "package log: %s\n", err)
 		return false
 	}
 	fmt.Fprintf(fout, "%s\n", pkg)
@@ -182,6 +189,12 @@ func install(pkg, parent string) {
 		visit[pkg] = done
 	}()
 
+	// Don't allow trailing '/'
+	if _, f := filepath.Split(pkg); f == "" {
+		errorf("%s should not have trailing '/'\n", pkg)
+		return
+	}
+
 	// Check whether package is local or remote.
 	// If remote, download or update it.
 	tree, pkg, err := build.FindTree(pkg)
@@ -209,7 +222,7 @@ func install(pkg, parent string) {
 		}
 	}
 	if err != nil {
-		errorf("%s: %v\n", pkg, err)
+		terrorf(tree, "%s: %v\n", pkg, err)
 		return
 	}
 	dir := filepath.Join(tree.SrcDir(), pkg)
@@ -217,11 +230,11 @@ func install(pkg, parent string) {
 	// Install prerequisites.
 	dirInfo, err := build.ScanDir(dir, parent == "")
 	if err != nil {
-		errorf("%s: %v\n", pkg, err)
+		terrorf(tree, "%s: %v\n", pkg, err)
 		return
 	}
 	if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 {
-		errorf("%s: package has no files\n", pkg)
+		terrorf(tree, "%s: package has no files\n", pkg)
 		return
 	}
 	for _, p := range dirInfo.Imports {
@@ -237,13 +250,13 @@ func install(pkg, parent string) {
 	if *useMake {
 		err := domake(dir, pkg, tree, dirInfo.IsCommand())
 		if err != nil {
-			errorf("%s: install: %v\n", pkg, err)
+			terrorf(tree, "%s: install: %v\n", pkg, err)
 			return
 		}
 	} else {
 		script, err := build.Build(tree, pkg, dirInfo)
 		if err != nil {
-			errorf("%s: install: %v\n", pkg, err)
+			terrorf(tree, "%s: install: %v\n", pkg, err)
 			return
 		}
 		if *nuke {
@@ -257,7 +270,7 @@ func install(pkg, parent string) {
 			if script.Stale() {
 				printf("%s: install\n", pkg)
 				if err := script.Run(); err != nil {
-					errorf("%s: install: %v\n", pkg, err)
+					terrorf(tree, "%s: install: %v\n", pkg, err)
 					return
 				}
 			} else {
diff --git a/src/cmd/goinstall/tag_test.go b/src/cmd/goinstall/tag_test.go
new file mode 100644
index 0000000..a23a7ea
--- /dev/null
+++ b/src/cmd/goinstall/tag_test.go
@@ -0,0 +1,73 @@
+// 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 main
+
+import "testing"
+
+var selectTagTestTags = []string{
+	"go.r58",
+	"go.r58.1",
+	"go.r59",
+	"go.r59.1",
+	"go.r61",
+	"go.r61.1",
+	"go.weekly.2010-01-02",
+	"go.weekly.2011-10-12",
+	"go.weekly.2011-10-12.1",
+	"go.weekly.2011-10-14",
+	"go.weekly.2011-11-01",
+	// these should be ignored:
+	"release.r59",
+	"release.r59.1",
+	"release",
+	"weekly.2011-10-12",
+	"weekly.2011-10-12.1",
+	"weekly",
+	"foo",
+	"bar",
+	"go.f00",
+	"go!r60",
+	"go.1999-01-01",
+}
+
+var selectTagTests = []struct {
+	version  string
+	selected string
+}{
+	{"release.r57", ""},
+	{"release.r58.2", "go.r58.1"},
+	{"release.r59", "go.r59"},
+	{"release.r59.1", "go.r59.1"},
+	{"release.r60", "go.r59.1"},
+	{"release.r60.1", "go.r59.1"},
+	{"release.r61", "go.r61"},
+	{"release.r66", "go.r61.1"},
+	{"weekly.2010-01-01", ""},
+	{"weekly.2010-01-02", "go.weekly.2010-01-02"},
+	{"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
+	{"weekly.2010-01-03", "go.weekly.2010-01-02"},
+	{"weekly.2011-10-12", "go.weekly.2011-10-12"},
+	{"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
+	{"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
+	{"weekly.2011-10-14", "go.weekly.2011-10-14"},
+	{"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
+	{"weekly.2011-11-01", "go.weekly.2011-11-01"},
+	{"weekly.2014-01-01", "go.weekly.2011-11-01"},
+	{"weekly.3000-01-01", "go.weekly.2011-11-01"},
+	// faulty versions:
+	{"release.f00", ""},
+	{"weekly.1999-01-01", ""},
+	{"junk", ""},
+	{"", ""},
+}
+
+func TestSelectTag(t *testing.T) {
+	for _, c := range selectTagTests {
+		selected := selectTag(c.version, selectTagTestTags)
+		if selected != c.selected {
+			t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected)
+		}
+	}
+}
diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c
index 0b5e608..96f3660 100644
--- a/src/cmd/gopack/ar.c
+++ b/src/cmd/gopack/ar.c
@@ -1532,6 +1532,8 @@ arwrite(int fd, Armember *bp)
 int
 page(Arfile *ap)
 {
+	USED(ap);
+
 	sysfatal("page");
 	return 1;
 }
diff --git a/src/cmd/gotest/gotest.go b/src/cmd/gotest/gotest.go
index 4cb3da2..8e3a422 100644
--- a/src/cmd/gotest/gotest.go
+++ b/src/cmd/gotest/gotest.go
@@ -9,12 +9,13 @@ import (
 	"exec"
 	"fmt"
 	"go/ast"
+	"go/build"
 	"go/parser"
 	"go/token"
 	"io/ioutil"
 	"os"
-	"path/filepath"
 	"runtime"
+	"sort"
 	"strings"
 	"time"
 	"unicode"
@@ -159,17 +160,19 @@ func setEnvironment() {
 }
 
 // getTestFileNames gets the set of files we're looking at.
-// If gotest has no arguments, it scans for file names matching "[^.]*_test.go".
+// If gotest has no arguments, it scans the current directory
+// for test files.
 func getTestFileNames() {
 	names := fileNames
 	if len(names) == 0 {
-		var err os.Error
-		names, err = filepath.Glob("[^.]*_test.go")
+		info, err := build.ScanDir(".", true)
 		if err != nil {
-			Fatalf("Glob pattern error: %s", err)
+			Fatalf("scanning directory: %v", err)
 		}
+		names = append(info.TestGoFiles, info.XTestGoFiles...)
+		sort.Strings(names)
 		if len(names) == 0 {
-			Fatalf(`no test files found: no match for "[^.]*_test.go"`)
+			Fatalf("no test files found in current directory")
 		}
 	}
 	for _, n := range names {
diff --git a/src/cmd/gotry/gotry b/src/cmd/gotry/gotry
index 3cc7a98..c81b6c7 100755
--- a/src/cmd/gotry/gotry
+++ b/src/cmd/gotry/gotry
@@ -112,7 +112,7 @@ functions=$(getFunctions)
 
 # Write file to compile
 file="/tmp/$USER.try"
-rm -f "file.go"
+rm -f "$file.go"
 (
 cat <<'!'
 package main
diff --git a/src/cmd/gotype/testdata/test1.go b/src/cmd/gotype/testdata/test1.go
index 0bd4656..a3298e6 100644
--- a/src/cmd/gotype/testdata/test1.go
+++ b/src/cmd/gotype/testdata/test1.go
@@ -4,3 +4,20 @@ func _() {
 	// the scope of a local type declaration starts immediately after the type name
 	type T struct{ _ *T }
 }
+
+func _(x interface{}) {
+	// the variable defined by a TypeSwitchGuard is declared in each TypeCaseClause
+	switch t := x.(type) {
+	case int:
+		_ = t
+	case float32:
+		_ = t
+	default:
+		_ = t
+	}
+
+	// the variable defined by a TypeSwitchGuard must not conflict with other
+	// variables declared in the initial simple statement
+	switch t := 0; t := x.(type) {
+	}
+}
diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go
index 9e338ab..d4169ae 100644
--- a/src/cmd/hgpatch/main.go
+++ b/src/cmd/hgpatch/main.go
@@ -6,7 +6,6 @@ package main
 
 import (
 	"bytes"
-	"container/vector"
 	"exec"
 	"flag"
 	"fmt"
@@ -242,15 +241,17 @@ func chk(err os.Error) {
 // Undo log
 type undo func() os.Error
 
-var undoLog vector.Vector // vector of undo
+var undoLog []undo
 
-func undoRevert(name string) { undoLog.Push(undo(func() os.Error { return hgRevert(name) })) }
+func undoRevert(name string) {
+	undoLog = append(undoLog, undo(func() os.Error { return hgRevert(name) }))
+}
 
-func undoRm(name string) { undoLog.Push(undo(func() os.Error { return os.Remove(name) })) }
+func undoRm(name string) { undoLog = append(undoLog, undo(func() os.Error { return os.Remove(name) })) }
 
 func runUndo() {
-	for i := undoLog.Len() - 1; i >= 0; i-- {
-		if err := undoLog.At(i).(undo)(); err != nil {
+	for i := len(undoLog) - 1; i >= 0; i-- {
+		if err := undoLog[i](); err != nil {
 			fmt.Fprintf(os.Stderr, "%s\n", err)
 		}
 	}
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index e726916..a7f61c9 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -182,7 +182,11 @@ relocsym(Sym *s)
 			o = symaddr(r->sym) + r->add;
 			break;
 		case D_PCREL:
-			o = symaddr(r->sym) + r->add - (s->value + r->off + r->siz);
+			// r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call.
+			o = 0;
+			if(r->sym)
+				o += symaddr(r->sym);
+			o += r->add - (s->value + r->off + r->siz);
 			break;
 		case D_SIZE:
 			o = r->sym->size + r->add;
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index f9f9ef6..00cfc8c 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -77,6 +77,19 @@ elf64phdr(ElfPhdr *e)
 void
 elf32phdr(ElfPhdr *e)
 {
+	int frag;
+	
+	if(e->type == PT_LOAD) {
+		// Correct ELF loaders will do this implicitly,
+		// but buggy ELF loaders like the one in some
+		// versions of QEMU won't.
+		frag = e->vaddr&(e->align-1);
+		e->off -= frag;
+		e->vaddr -= frag;
+		e->paddr -= frag;
+		e->filesz += frag;
+		e->memsz += frag;
+	}
 	LPUT(e->type);
 	LPUT(e->off);
 	LPUT(e->vaddr);
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 5d1e6d6..37379e1 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -295,19 +295,33 @@ nextar(Biobuf *bp, int off, struct ar_hdr *a)
 {
 	int r;
 	int32 arsize;
+	char *buf;
 
 	if (off&01)
 		off++;
 	Bseek(bp, off, 0);
-	r = Bread(bp, a, SAR_HDR);
-	if(r != SAR_HDR)
-		return 0;
-	if(strncmp(a->fmag, ARFMAG, sizeof(a->fmag)))
+	buf = Brdline(bp, '\n');
+	r = Blinelen(bp);
+	if(buf == nil) {
+		if(r == 0)
+			return 0;
+		return -1;
+	}
+	if(r == SAR_HDR) {
+		memmove(a, buf, SAR_HDR);
+	} else if (r == SAR_HDR-SARNAME+16) {	// old Plan 9
+		memset(a->name, ' ', sizeof a->name);
+		memmove(a, buf, 16);
+		memmove((char*)a+SARNAME, buf+16, SAR_HDR-SARNAME);
+	} else {	// unexpected
+		return -1;
+	}
+	if(strncmp(a->fmag, ARFMAG, sizeof a->fmag))
 		return -1;
 	arsize = strtol(a->size, 0, 0);
 	if (arsize&1)
 		arsize++;
-	return arsize + SAR_HDR;
+	return arsize + r;
 }
 
 void
@@ -1332,7 +1346,7 @@ Yconv(Fmt *fp)
 		fmtprint(fp, "<nil>");
 	} else {
 		fmtstrinit(&fmt);
-		fmtprint(&fmt, "%s @0x%08x [%d]", s->name, s->value, s->size);
+		fmtprint(&fmt, "%s @0x%08llx [%lld]", s->name, (vlong)s->value, (vlong)s->size);
 		for (i = 0; i < s->size; i++) {
 			if (!(i%8)) fmtprint(&fmt,  "\n\t0x%04x ", i);
 			fmtprint(&fmt, "%02x ", s->p[i]);
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
index 334c995..df6c959 100644
--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -32,6 +32,11 @@ static char dosstub[] =
 	0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
+// Note: currently only up to 8 chars plus \0.
+static char *symlabels[] = {
+	"symtab", "esymtab", "pclntab", "epclntab"
+};
+
 static Sym *rsrcsym;
 
 static char symnames[256]; 
@@ -44,6 +49,7 @@ static int pe64;
 static int nsect;
 static int nextsectoff;
 static int nextfileoff;
+static int textsect;
 
 static IMAGE_FILE_HEADER fh;
 static IMAGE_OPTIONAL_HEADER oh;
@@ -449,20 +455,29 @@ addsymtable(void)
 {
 	IMAGE_SECTION_HEADER *h;
 	int i, size;
+	Sym *s;
 	
-	if(nextsymoff == 0)
-		return;
-	
-	size  = nextsymoff + 4 + 18;
+	fh.NumberOfSymbols = sizeof(symlabels)/sizeof(symlabels[0]);
+	size = nextsymoff + 4 + 18*fh.NumberOfSymbols;
 	h = addpesection(".symtab", size, size);
 	h->Characteristics = IMAGE_SCN_MEM_READ|
 		IMAGE_SCN_MEM_DISCARDABLE;
 	chksectoff(h, cpos());
 	fh.PointerToSymbolTable = cpos();
-	fh.NumberOfSymbols = 1;
-	strnput("", 18); // one empty symbol
-	// put symbol string table
-	lputl(size);
+	
+	// put COFF symbol table
+	for (i=0; i<fh.NumberOfSymbols; i++) {
+		s = rlookup(symlabels[i], 0);
+		strnput(s->name, 8);
+		lputl(datoff(s->value));
+		wputl(textsect);
+		wputl(0x0308);  // "array of structs"
+		cput(2);        // storage class: external
+		cput(0);        // no aux entries
+	}
+
+	// put COFF string table
+	lputl(nextsymoff + 4);
 	for (i=0; i<nextsymoff; i++)
 		cput(symnames[i]);
 	strnput("", h->SizeOfRawData - size);
@@ -510,6 +525,48 @@ addpersrc(void)
 	dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize;
 }
 
+static void
+addexcept(IMAGE_SECTION_HEADER *text)
+{
+	IMAGE_SECTION_HEADER *pdata, *xdata;
+	vlong startoff;
+	uvlong n;
+	Sym *sym;
+
+	if(thechar != '6')
+		return;
+
+	// write unwind info
+	sym = lookup("runtime.sigtramp", 0);
+	startoff = cpos();
+	lputl(9);	// version=1, flags=UNW_FLAG_EHANDLER, rest 0
+	lputl(sym->value - PEBASE);
+	lputl(0);
+
+	n = cpos() - startoff;
+	xdata = addpesection(".xdata", n, n);
+	xdata->Characteristics = IMAGE_SCN_MEM_READ|
+		IMAGE_SCN_CNT_INITIALIZED_DATA;
+	chksectoff(xdata, startoff);
+	strnput("", xdata->SizeOfRawData - n);
+
+	// write a function table entry for the whole text segment
+	startoff = cpos();
+	lputl(text->VirtualAddress);
+	lputl(text->VirtualAddress + text->VirtualSize);
+	lputl(xdata->VirtualAddress);
+
+	n = cpos() - startoff;
+	pdata = addpesection(".pdata", n, n);
+	pdata->Characteristics = IMAGE_SCN_MEM_READ|
+		IMAGE_SCN_CNT_INITIALIZED_DATA;
+	chksectoff(pdata, startoff);
+	strnput("", pdata->SizeOfRawData - n);
+
+	dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress;
+	dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize;
+}
+
 void
 asmbpe(void)
 {
@@ -532,6 +589,7 @@ asmbpe(void)
 		IMAGE_SCN_CNT_INITIALIZED_DATA|
 		IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ;
 	chksectseg(t, &segtext);
+	textsect = nsect;
 
 	d = addpesection(".data", segdata.len, segdata.filelen);
 	d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA|
@@ -546,7 +604,8 @@ asmbpe(void)
 	addexports();
 	addsymtable();
 	addpersrc();
-	
+	addexcept(t);
+
 	fh.NumberOfSections = nsect;
 	fh.TimeDateStamp = time(0);
 	fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED|
@@ -583,8 +642,16 @@ asmbpe(void)
 		set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI);
 	else
 		set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI);
-	set(SizeOfStackReserve, 0x0040000);
-	set(SizeOfStackCommit, 0x00001000);
+
+	// Disable stack growth as we don't want Windows to
+	// fiddle with the thread stack limits, which we set
+	// ourselves to circumvent the stack checks in the
+	// Windows exception dispatcher.
+	// Commit size must be strictly less than reserve
+	// size otherwise reserve will be rounded up to a
+	// larger size, as verified with VMMap.
+	set(SizeOfStackReserve, 0x00010000);
+	set(SizeOfStackCommit, 0x0000ffff);
 	set(SizeOfHeapReserve, 0x00100000);
 	set(SizeOfHeapCommit, 0x00001000);
 	set(NumberOfRvaAndSizes, 16);
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 60e146b..00413af 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -34,10 +34,7 @@
 #include	"../ld/lib.h"
 #include	"../ld/elf.h"
 
-char *elfstrdat;
-int elfstrsize;
-int maxelfstr;
-int elftextsh;
+static int maxelfstr;
 
 int
 putelfstr(char *s)
diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile
index 8a1a2f3..6cefceb 100644
--- a/src/cmd/prof/Makefile
+++ b/src/cmd/prof/Makefile
@@ -25,6 +25,7 @@ endif
 install: install-$(NAME) install-pprof
 install-linux: install-default
 install-freebsd: install-default
+install-openbsd: install-default
 install-windows: install-default
 
 # on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash
diff --git a/src/cmd/prof/gopprof b/src/cmd/prof/gopprof
index be5f84e..83438b7 100755
--- a/src/cmd/prof/gopprof
+++ b/src/cmd/prof/gopprof
@@ -1241,7 +1241,7 @@ sub Disassemble {
   while (<OBJDUMP>) {
     s/\r//g;         # turn windows-looking lines into unix-looking lines
     chop;
-    if (m|\s*([^:\s]+):(\d+)\s*$|) {
+    if (m|\s*(.+):(\d+)\s*$|) {
       # Location line of the form:
       #   <filename>:<linenumber>
       $filename = $1;
@@ -4485,7 +4485,7 @@ sub MapSymbolsWithNM {
 
 sub ShortFunctionName {
   my $function = shift;
-  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
+  while ($function =~ s/(?<!\.)\([^()]*\)(\s*const)?//g) { }   # Argument types
   while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
   $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
   return $function;
@@ -4494,7 +4494,7 @@ sub ShortFunctionName {
 # Trim overly long symbols found in disassembler output
 sub CleanDisassembly {
   my $d = shift;
-  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
+  while ($d =~ s/(?<!\.)\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
   while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
   return $d;
 }
diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c
index f36759c..f0acaf1 100644
--- a/src/cmd/prof/main.c
+++ b/src/cmd/prof/main.c
@@ -386,6 +386,8 @@ addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
 {
 	int h;
 	PC *x;
+	
+	USED(sp);
 
 	h = (pc + callerpc*101) % Ncounters;
 	for(x = counters[h]; x != NULL; x = x->next) {
@@ -437,6 +439,8 @@ uvlong nextpc;
 void
 xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
 {
+	USED(map);
+
 	char buf[1024];
 	if(sym == nil){
 		fprint(2, "syms\n");
diff --git a/src/lib9/exitcode.c b/src/lib9/exitcode.c
index 234492a..a952b2d 100644
--- a/src/lib9/exitcode.c
+++ b/src/lib9/exitcode.c
@@ -29,6 +29,7 @@ THE SOFTWARE.
 int
 exitcode(char *s)
 {
+	USED(s);
 	return 1;
 }
 
diff --git a/src/libmach/executable.c b/src/libmach/executable.c
index e903344..9d45323 100644
--- a/src/libmach/executable.c
+++ b/src/libmach/executable.c
@@ -66,6 +66,7 @@ static	int	adotout(int, Fhdr*, ExecHdr*);
 static	int	elfdotout(int, Fhdr*, ExecHdr*);
 static	int	machdotout(int, Fhdr*, ExecHdr*);
 static	int	armdotout(int, Fhdr*, ExecHdr*);
+static	int	pedotout(int, Fhdr*, ExecHdr*);
 static	void	setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32);
 static	void	setdata(Fhdr*, uvlong, int32, vlong, int32);
 static	void	settext(Fhdr*, uvlong, uvlong, int32, vlong);
@@ -312,6 +313,15 @@ ExecTable exectab[] =
 		sizeof(Exec),
 		beswal,
 		common },
+	{ 0x4d5a9000,    /* see dosstub[] in pe.c */
+		"windows PE executable",
+		nil,
+		FWINPE,
+		0,
+		&mi386,
+		sizeof(Exec), /* TODO */
+		nil,
+		pedotout },
 	{ 0 },
 };
 
@@ -502,6 +512,8 @@ commonllp64(int unused, Fhdr *fp, ExecHdr *hp)
 	int32 pgsize;
 	uvlong entry;
 
+	USED(unused);
+
 	hswal(&hp->e, sizeof(Exec)/sizeof(int32), beswal);
 	if(!(hp->e.exechdr.magic & HDR_MAGIC))
 		return 0;
@@ -542,6 +554,10 @@ commonllp64(int unused, Fhdr *fp, ExecHdr *hp)
 static int
 mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
 {
+	USED(fd);
+	USED(fp);
+	USED(hp);
+
 abort();
 #ifdef unused
 	USED(fd);
@@ -573,6 +589,10 @@ abort();
 static int
 mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
 {
+	USED(fd);
+	USED(fp);
+	USED(hp);
+
 abort();
 #ifdef unused
 	USED(fd);
@@ -604,6 +624,10 @@ abort();
 static int
 sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
 {
+	USED(fd);
+	USED(fp);
+	USED(hp);
+
 abort();
 #ifdef unused
 	USED(fd);
@@ -624,6 +648,10 @@ abort();
 static int
 nextboot(int fd, Fhdr *fp, ExecHdr *hp)
 {
+	USED(fd);
+	USED(fp);
+	USED(hp);
+
 abort();
 #ifdef unused
 	USED(fd);
@@ -645,7 +673,6 @@ abort();
 static int
 elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
 {
-
 	uvlong (*swav)(uvlong);
 	uint32 (*swal)(uint32);
 	ushort (*swab)(ushort);
@@ -784,7 +811,7 @@ elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
 		buf = malloc(sh[ep->shstrndx].size);
 		if (buf == 0)
 			goto done;
-		memset(buf, 0, sizeof buf);
+		memset(buf, 0, sh[ep->shstrndx].size);
 		seek(fd, sh[ep->shstrndx].offset, 0);
 		i = read(fd, buf, sh[ep->shstrndx].size);
 		USED(i);	// shut up ubuntu gcc
@@ -962,7 +989,7 @@ elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
 		buf = malloc(sh[ep->shstrndx].size);
 		if (buf == 0)
 			goto done;
-		memset(buf, 0, sizeof buf);
+		memset(buf, 0, sh[ep->shstrndx].size);
 		seek(fd, sh[ep->shstrndx].offset, 0);
 		i = read(fd, buf, sh[ep->shstrndx].size);
 		USED(i);	// shut up ubuntu gcc
@@ -1232,6 +1259,164 @@ armdotout(int fd, Fhdr *fp, ExecHdr *hp)
 	return 1;
 }
 
+/*
+ * Structures needed to parse PE image.
+ */
+typedef struct {
+	uint16 Machine;
+	uint16 NumberOfSections;
+	uint32 TimeDateStamp;
+	uint32 PointerToSymbolTable;
+	uint32 NumberOfSymbols;
+	uint16 SizeOfOptionalHeader;
+	uint16 Characteristics;
+} IMAGE_FILE_HEADER;
+
+typedef struct {
+	uint8  Name[8];
+	uint32 VirtualSize;
+	uint32 VirtualAddress;
+	uint32 SizeOfRawData;
+	uint32 PointerToRawData;
+	uint32 PointerToRelocations;
+	uint32 PointerToLineNumbers;
+	uint16 NumberOfRelocations;
+	uint16 NumberOfLineNumbers;
+	uint32 Characteristics;
+} IMAGE_SECTION_HEADER;
+
+typedef struct {
+	uint32 VirtualAddress;
+	uint32 Size;
+} IMAGE_DATA_DIRECTORY;
+
+typedef struct {
+	uint16 Magic;
+	uint8  MajorLinkerVersion;
+	uint8  MinorLinkerVersion;
+	uint32 SizeOfCode;
+	uint32 SizeOfInitializedData;
+	uint32 SizeOfUninitializedData;
+	uint32 AddressOfEntryPoint;
+	uint32 BaseOfCode;
+	uint32 BaseOfData;
+	uint32 ImageBase;
+	uint32 SectionAlignment;
+	uint32 FileAlignment;
+	uint16 MajorOperatingSystemVersion;
+	uint16 MinorOperatingSystemVersion;
+	uint16 MajorImageVersion;
+	uint16 MinorImageVersion;
+	uint16 MajorSubsystemVersion;
+	uint16 MinorSubsystemVersion;
+	uint32 Win32VersionValue;
+	uint32 SizeOfImage;
+	uint32 SizeOfHeaders;
+	uint32 CheckSum;
+	uint16 Subsystem;
+	uint16 DllCharacteristics;
+	uint32 SizeOfStackReserve;
+	uint32 SizeOfStackCommit;
+	uint32 SizeOfHeapReserve;
+	uint32 SizeOfHeapCommit;
+	uint32 LoaderFlags;
+	uint32 NumberOfRvaAndSizes;
+	IMAGE_DATA_DIRECTORY DataDirectory[16];
+} IMAGE_OPTIONAL_HEADER;
+
+static int
+match8(void *buf, char *cmp)
+{
+	return strncmp((char*)buf, cmp, 8) == 0;
+}
+
+/* TODO(czaplinski): 64b windows? */
+/*
+ * Read from Windows PE/COFF .exe file image.
+ */
+static int
+pedotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	uint32 start, magic;
+	uint32 symtab, esymtab;
+	IMAGE_FILE_HEADER fh;
+	IMAGE_SECTION_HEADER sh;
+	IMAGE_OPTIONAL_HEADER oh;
+	uint8 sym[18];
+	uint32 *valp;
+	int i;
+
+	USED(hp);
+	seek(fd, 0x3c, 0);
+	if (readn(fd, &start, sizeof(start)) != sizeof(start)) {
+		werrstr("crippled PE MSDOS header");
+		return 0;
+	}
+	start = leswal(start);
+
+	seek(fd, start, 0);
+	if (readn(fd, &magic, sizeof(magic)) != sizeof(magic)) {
+		werrstr("no PE magic number found");
+		return 0;
+	}
+	if (beswal(magic) != 0x50450000) {  /* "PE\0\0" */
+		werrstr("incorrect PE magic number");
+		return 0;
+	}
+
+	if (readn(fd, &fh, sizeof(fh)) != sizeof(fh)) {
+		werrstr("crippled PE File Header");
+		return 0;
+	}
+	if (fh.PointerToSymbolTable == 0) {
+		werrstr("zero pointer to COFF symbol table");
+		return 0;
+	}
+
+	if (readn(fd, &oh, sizeof(oh)) != sizeof(oh)) {
+		werrstr("crippled PE Optional Header");
+		return 0;
+	}
+
+	seek(fd, start+sizeof(magic)+sizeof(fh)+leswab(fh.SizeOfOptionalHeader), 0);
+	fp->txtaddr = fp->dataddr = 0;
+	for (i=0; i<leswab(fh.NumberOfSections); i++) {
+		if (readn(fd, &sh, sizeof(sh)) != sizeof(sh)) {
+			werrstr("could not read Section Header %d", i+1);
+			return 0;
+		}
+		if (match8(sh.Name, ".text"))
+			settext(fp, leswal(sh.VirtualAddress), leswal(oh.AddressOfEntryPoint), leswal(sh.VirtualSize), leswal(sh.PointerToRawData));
+		if (match8(sh.Name, ".data"))
+			setdata(fp, leswal(sh.VirtualAddress), leswal(sh.SizeOfRawData), leswal(sh.PointerToRawData), leswal(sh.VirtualSize)-leswal(sh.SizeOfRawData));
+	}
+	if (fp->txtaddr==0 || fp->dataddr==0) {
+		werrstr("no .text or .data");
+		return 0;
+	}
+
+	seek(fd, leswal(fh.PointerToSymbolTable), 0);
+	symtab = esymtab = 0;
+	for (i=0; i<leswal(fh.NumberOfSymbols); i++) {
+		if (readn(fd, &sym, sizeof(sym)) != sizeof(sym)) {
+			werrstr("crippled COFF symbol %d", i);
+			return 0;
+		}
+		valp = (uint32 *)&sym[8];
+		if (match8(sym, "symtab"))
+			symtab = leswal(*valp);
+		if (match8(sym, "esymtab"))
+			esymtab = leswal(*valp);
+	}
+	if (symtab==0 || esymtab==0) {
+		werrstr("no symtab or esymtab in COFF symbol table");
+		return 0;
+	}
+	setsym(fp, symtab, esymtab-symtab, 0, 0, 0, 0);
+
+	return 1;
+}
+
 static void
 settext(Fhdr *fp, uvlong e, uvlong a, int32 s, vlong off)
 {
diff --git a/src/libmach/fakeobj.c b/src/libmach/fakeobj.c
index ea7ef01..a4a897c 100644
--- a/src/libmach/fakeobj.c
+++ b/src/libmach/fakeobj.c
@@ -13,17 +13,17 @@
 #include <mach.h>
 #include "obj.h"
 
-int _is2(char* x) { return 0; }
-int _is7(char* x) { return 0; }
-int _is9(char* x) { return 0; }
-int _isk(char* x) { return 0; }
-int _isq(char* x) { return 0; }
-int _isv(char* x) { return 0; }
-int _isu(char* x) { return 0; }
-int _read2(Biobuf* b, Prog* p) { return 0; }
-int _read7(Biobuf* b, Prog* p) { return 0; }
-int _read9(Biobuf* b, Prog* p) { return 0; }
-int _readk(Biobuf* b, Prog* p) { return 0; }
-int _readq(Biobuf* b, Prog* p) { return 0; }
-int _readv(Biobuf* b, Prog* p) { return 0; }
-int _readu(Biobuf* b, Prog* p) { return 0; }
+int _is2(char* x) { USED(x); return 0; }
+int _is7(char* x) { USED(x); return 0; }
+int _is9(char* x) { USED(x); return 0; }
+int _isk(char* x) { USED(x); return 0; }
+int _isq(char* x) { USED(x); return 0; }
+int _isv(char* x) { USED(x); return 0; }
+int _isu(char* x) { USED(x); return 0; }
+int _read2(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _read7(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _read9(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _readk(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _readq(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _readv(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
+int _readu(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
diff --git a/src/libmach/linux.c b/src/libmach/linux.c
index 6ce1895..2c14326 100644
--- a/src/libmach/linux.c
+++ b/src/libmach/linux.c
@@ -807,6 +807,8 @@ ptraceerr:
 static int
 ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
 {
+	USED(seg);
+
 	return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
 		isr, map->pid, addr, v, n);
 }
@@ -937,6 +939,8 @@ ptraceregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
 {
 	int laddr;
 	uvlong u;
+	
+	USED(seg);
 
 	if((laddr = go2linux(addr)) < 0){
 		if(isr){
diff --git a/src/libmach/map.c b/src/libmach/map.c
index ebfe037..cd5ef09 100644
--- a/src/libmach/map.c
+++ b/src/libmach/map.c
@@ -137,6 +137,8 @@ int
 fdrw(Map *map, Seg *s, uvlong addr, void *v, uint n, int isread)
 {
 	int tot, m;
+	
+	USED(map);
 
 	for(tot=0; tot<n; tot+=m){
 		if(isread)
diff --git a/src/pkg/Makefile b/src/pkg/Makefile
index ac36aaf..c7e65c0 100644
--- a/src/pkg/Makefile
+++ b/src/pkg/Makefile
@@ -81,6 +81,7 @@ DIRS=\
 	exp/gui\
 	exp/gui/x11\
 	exp/norm\
+	exp/regexp\
 	exp/regexp/syntax\
 	exp/template/html\
 	expvar\
@@ -189,29 +190,7 @@ endif
 
 ifeq ($(GOOS),plan9)
 NOPLAN9BUILD=\
-	crypto/tls\
-	exp/gui/x11\
-	expvar\
-	http\
-	http/cgi\
-	http/fcgi\
-	http/httptest\
-	http/pprof\
-	http/spdy\
-	mail\
-	mime/multipart\
-	net\
-	net/dict\
-	net/textproto\
-	netchan\
 	os/signal\
-	rpc\
-	rpc/jsonrpc\
-	smtp\
-	syslog\
-	websocket\
-	../cmd/godoc\
-	../cmd/goinstall\
 
 DIRS:=$(filter-out $(NOPLAN9BUILD),$(DIRS))
 endif
@@ -240,7 +219,6 @@ NOTEST+=\
 	../cmd/cgo\
 	../cmd/ebnflint\
 	../cmd/godoc\
-	../cmd/goinstall\
 	../cmd/gotest\
 	../cmd/goyacc\
 	../cmd/hgpatch\
@@ -252,7 +230,6 @@ NOBENCH+=\
 ifeq ($(GOOS),windows)
 NOTEST+=os/signal    # no signals
 NOTEST+=syslog       # no network
-NOTEST+=time         # no syscall.Kill, syscall.SIGCHLD for sleep tests
 endif
 
 TEST=\
diff --git a/src/pkg/archive/tar/testdata/writer.tar b/src/pkg/archive/tar/testdata/writer.tar
index 0358f91..e6d816a 100644
Binary files a/src/pkg/archive/tar/testdata/writer.tar and b/src/pkg/archive/tar/testdata/writer.tar differ
diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go
index 8673bad..c6ce224 100644
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -134,7 +134,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error {
 	tw.numeric(s.next(12), hdr.Mtime)      // 136:148
 	s.next(8)                              // chksum (148:156)
 	s.next(1)[0] = hdr.Typeflag            // 156:157
-	s.next(100)                            // linkname (157:257)
+	tw.cString(s.next(100), hdr.Linkname)  // linkname (157:257)
 	copy(s.next(8), []byte("ustar\x0000")) // 257:265
 	tw.cString(s.next(32), hdr.Uname)      // 265:297
 	tw.cString(s.next(32), hdr.Gname)      // 297:329
diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go
index 838cb7e..6cc9386 100644
--- a/src/pkg/archive/tar/writer_test.go
+++ b/src/pkg/archive/tar/writer_test.go
@@ -24,6 +24,10 @@ type writerTest struct {
 }
 
 var writerTests = []*writerTest{
+	// The writer test file was produced with this command:
+	// tar (GNU tar) 1.26
+	//   ln -s small.txt link.txt
+	//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
 	&writerTest{
 		file: "testdata/writer.tar",
 		entries: []*writerTestEntry{
@@ -55,6 +59,21 @@ var writerTests = []*writerTest{
 				},
 				contents: "Google.com\n",
 			},
+			&writerTestEntry{
+				header: &Header{
+					Name:     "link.txt",
+					Mode:     0777,
+					Uid:      1000,
+					Gid:      1000,
+					Size:     0,
+					Mtime:    1314603082,
+					Typeflag: '2',
+					Linkname: "small.txt",
+					Uname:    "strings",
+					Gname:    "strings",
+				},
+				// no contents
+			},
 		},
 	},
 	// The truncated test file was produced using these commands:
diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go
old mode 100755
new mode 100644
diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go
old mode 100755
new mode 100644
index 03446d6..b2e1692
--- a/src/pkg/big/int_test.go
+++ b/src/pkg/big/int_test.go
@@ -301,6 +301,9 @@ func TestGetString(t *testing.T) {
 func TestSetString(t *testing.T) {
 	tmp := new(Int)
 	for i, test := range stringTests {
+		// initialize to a non-zero value so that issues with parsing
+		// 0 are detected
+		tmp.SetInt64(1234567890)
 		n1, ok1 := new(Int).SetString(test.in, test.base)
 		n2, ok2 := tmp.SetString(test.in, test.base)
 		expected := NewInt(test.val)
diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go
old mode 100755
new mode 100644
index be3aff2..33d6bb1
--- a/src/pkg/big/nat.go
+++ b/src/pkg/big/nat.go
@@ -646,7 +646,7 @@ func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) {
 					}
 				}
 			case os.EOF:
-				return z, 10, nil
+				return z.make(0), 10, nil
 			default:
 				return z, 10, err
 			}
diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go
old mode 100755
new mode 100644
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index 727ebfd..2ea7af3 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -54,11 +54,11 @@ type Reader struct {
 }
 
 // NewReaderSize creates a new Reader whose buffer has the specified size,
-// which must be greater than zero.  If the argument io.Reader is already a
+// which must be greater than one.  If the argument io.Reader is already a
 // Reader with large enough size, it returns the underlying Reader.
 // It returns the Reader and any error.
 func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) {
-	if size <= 0 {
+	if size <= 1 {
 		return nil, BufSizeError(size)
 	}
 	// Is it already a Reader?
@@ -298,6 +298,17 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
 func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
 	line, err = b.ReadSlice('\n')
 	if err == ErrBufferFull {
+		// Handle the case where "\r\n" straddles the buffer.
+		if len(line) > 0 && line[len(line)-1] == '\r' {
+			// Put the '\r' back on buf and drop it from line.
+			// Let the next call to ReadLine check for "\r\n".
+			if b.r == 0 {
+				// should be unreachable
+				panic("bufio: tried to rewind past start of buffer")
+			}
+			b.r--
+			line = line[:len(line)-1]
+		}
 		return line, true, nil
 	}
 
@@ -307,10 +318,11 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
 	err = nil
 
 	if line[len(line)-1] == '\n' {
-		line = line[:len(line)-1]
-	}
-	if len(line) > 0 && line[len(line)-1] == '\r' {
-		line = line[:len(line)-1]
+		drop := 1
+		if len(line) > 1 && line[len(line)-2] == '\r' {
+			drop = 2
+		}
+		line = line[:len(line)-drop]
 	}
 	return
 }
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 82c73d3..38213ff 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -137,7 +137,7 @@ var bufreaders = []bufReader{
 }
 
 var bufsizes = []int{
-	1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+	2, 3, 4, 5, 6, 7, 8, 9, 10,
 	23, 32, 46, 64, 93, 128, 1024, 4096,
 }
 
@@ -697,3 +697,71 @@ func TestLinesAfterRead(t *testing.T) {
 		t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
 	}
 }
+
+type readLineResult struct {
+	line     []byte
+	isPrefix bool
+	err      os.Error
+}
+
+var readLineNewlinesTests = []struct {
+	input   string
+	bufSize int
+	expect  []readLineResult
+}{
+	{"h\r\nb\r\n", 2, []readLineResult{
+		{[]byte("h"), true, nil},
+		{nil, false, nil},
+		{[]byte("b"), true, nil},
+		{nil, false, nil},
+		{nil, false, os.EOF},
+	}},
+	{"hello\r\nworld\r\n", 6, []readLineResult{
+		{[]byte("hello"), true, nil},
+		{nil, false, nil},
+		{[]byte("world"), true, nil},
+		{nil, false, nil},
+		{nil, false, os.EOF},
+	}},
+	{"hello\rworld\r", 6, []readLineResult{
+		{[]byte("hello"), true, nil},
+		{[]byte("\rworld"), true, nil},
+		{[]byte("\r"), false, nil},
+		{nil, false, os.EOF},
+	}},
+	{"h\ri\r\n\r", 2, []readLineResult{
+		{[]byte("h"), true, nil},
+		{[]byte("\ri"), true, nil},
+		{nil, false, nil},
+		{[]byte("\r"), false, nil},
+		{nil, false, os.EOF},
+	}},
+}
+
+func TestReadLineNewlines(t *testing.T) {
+	for _, e := range readLineNewlinesTests {
+		testReadLineNewlines(t, e.input, e.bufSize, e.expect)
+	}
+}
+
+func testReadLineNewlines(t *testing.T, input string, bufSize int, expect []readLineResult) {
+	b, err := NewReaderSize(strings.NewReader(input), bufSize)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for i, e := range expect {
+		line, isPrefix, err := b.ReadLine()
+		if bytes.Compare(line, e.line) != 0 {
+			t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line)
+			return
+		}
+		if isPrefix != e.isPrefix {
+			t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix)
+			return
+		}
+		if err != e.err {
+			t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err)
+			return
+		}
+	}
+}
diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go
index 5de8610..975031b 100644
--- a/src/pkg/bytes/buffer.go
+++ b/src/pkg/bytes/buffer.go
@@ -336,13 +336,18 @@ func (b *Buffer) ReadString(delim byte) (line string, err os.Error) {
 
 // NewBuffer creates and initializes a new Buffer using buf as its initial
 // contents.  It is intended to prepare a Buffer to read existing data.  It
-// can also be used to size the internal buffer for writing.  To do that,
+// can also be used to size the internal buffer for writing. To do that,
 // buf should have the desired capacity but a length of zero.
+//
+// In most cases, new(Buffer) (or just declaring a Buffer variable) is
+// preferable to NewBuffer.  In particular, passing a non-empty buf to
+// NewBuffer and then writing to the Buffer will overwrite buf, not append to
+// it.
 func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }
 
 // NewBufferString creates and initializes a new Buffer using string s as its
 // initial contents.  It is intended to prepare a buffer to read an existing
-// string.
+// string.  See the warnings about NewBuffer; similar issues apply here.
 func NewBufferString(s string) *Buffer {
 	return &Buffer{buf: []byte(s)}
 }
diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go
index c5c1f76..6625e3a 100644
--- a/src/pkg/container/heap/heap_test.go
+++ b/src/pkg/container/heap/heap_test.go
@@ -6,32 +6,46 @@ package heap_test
 
 import (
 	"testing"
-	"container/vector"
 	. "container/heap"
 )
 
-type myHeap struct {
-	// A vector.Vector implements sort.Interface except for Less,
-	// and it implements Push and Pop as required for heap.Interface.
-	vector.Vector
+type myHeap []int
+
+func (h *myHeap) Less(i, j int) bool {
+	return (*h)[i] < (*h)[j]
+}
+
+func (h *myHeap) Swap(i, j int) {
+	(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
+}
+
+func (h *myHeap) Len() int {
+	return len(*h)
+}
+
+func (h *myHeap) Pop() (v interface{}) {
+	*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
+	return
 }
 
-func (h *myHeap) Less(i, j int) bool { return h.At(i).(int) < h.At(j).(int) }
+func (h *myHeap) Push(v interface{}) {
+	*h = append(*h, v.(int))
+}
 
-func (h *myHeap) verify(t *testing.T, i int) {
+func (h myHeap) verify(t *testing.T, i int) {
 	n := h.Len()
 	j1 := 2*i + 1
 	j2 := 2*i + 2
 	if j1 < n {
 		if h.Less(j1, i) {
-			t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j1))
+			t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j1])
 			return
 		}
 		h.verify(t, j1)
 	}
 	if j2 < n {
 		if h.Less(j2, i) {
-			t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j2))
+			t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j2])
 			return
 		}
 		h.verify(t, j2)
diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go
old mode 100755
new mode 100644
diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go
index 111f60e..ecff7ff 100644
--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -31,7 +31,7 @@ var verifyTests = []verifyTest{
 		dnsName:       "www.google.com",
 
 		expectedChains: [][]string{
-			[]string{"Google", "Thawte", "VeriSign"},
+			{"Google", "Thawte", "VeriSign"},
 		},
 	},
 	{
@@ -68,7 +68,7 @@ var verifyTests = []verifyTest{
 		dnsName:       "www.google.com",
 
 		expectedChains: [][]string{
-			[]string{"Google", "Thawte", "VeriSign"},
+			{"Google", "Thawte", "VeriSign"},
 		},
 	},
 	{
@@ -78,7 +78,7 @@ var verifyTests = []verifyTest{
 		currentTime:   1302726541,
 
 		expectedChains: [][]string{
-			[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
+			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
 		},
 	},
 	{
@@ -88,8 +88,8 @@ var verifyTests = []verifyTest{
 		currentTime:   1302726541,
 
 		expectedChains: [][]string{
-			[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
-			[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
+			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
+			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
 		},
 	},
 }
diff --git a/src/pkg/exp/norm/Makefile b/src/pkg/exp/norm/Makefile
index a4dfb43..16239a7 100644
--- a/src/pkg/exp/norm/Makefile
+++ b/src/pkg/exp/norm/Makefile
@@ -9,6 +9,7 @@ GOFILES=\
 	composition.go\
 	forminfo.go\
 	normalize.go\
+	readwriter.go\
 	tables.go\
 	trie.go\
 
diff --git a/src/pkg/exp/norm/composition.go b/src/pkg/exp/norm/composition.go
index b2d2aba..ea59c81 100644
--- a/src/pkg/exp/norm/composition.go
+++ b/src/pkg/exp/norm/composition.go
@@ -7,26 +7,25 @@ package norm
 import "utf8"
 
 const (
-	maxCombiningChars = 30 + 2 // +2 to hold CGJ and Hangul overflow.
+	maxCombiningChars = 30
+	maxBufferSize     = maxCombiningChars + 2 // +1 to hold starter +1 to hold CGJ
 	maxBackRunes      = maxCombiningChars - 1
 	maxNFCExpansion   = 3  // NFC(0x1D160)
 	maxNFKCExpansion  = 18 // NFKC(0xFDFA)
 
-	maxRuneSizeInDecomp = 4
-	// Need to multiply by 2 as we don't reuse byte buffer space for recombining.
-	maxByteBufferSize = 2 * maxRuneSizeInDecomp * maxCombiningChars // 256
+	maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
 )
 
 // reorderBuffer is used to normalize a single segment.  Characters inserted with
-// insert() are decomposed and reordered based on CCC. The compose() method can
+// insert are decomposed and reordered based on CCC. The compose method can
 // be used to recombine characters.  Note that the byte buffer does not hold
 // the UTF-8 characters in order.  Only the rune array is maintained in sorted
-// order. flush() writes the resulting segment to a byte array.
+// order. flush writes the resulting segment to a byte array.
 type reorderBuffer struct {
-	rune  [maxCombiningChars]runeInfo // Per character info.
-	byte  [maxByteBufferSize]byte     // UTF-8 buffer. Referenced by runeInfo.pos.
-	nrune int                         // Number of runeInfos.
-	nbyte uint8                       // Number or bytes.
+	rune  [maxBufferSize]runeInfo // Per character info.
+	byte  [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos.
+	nrune int                     // Number of runeInfos.
+	nbyte uint8                   // Number or bytes.
 	f     formInfo
 }
 
@@ -49,10 +48,10 @@ func (rb *reorderBuffer) flush(out []byte) []byte {
 
 // insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
 // It returns false if the buffer is not large enough to hold the rune.
-// It is used internally by insert.
+// It is used internally by insert and insertString only.
 func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
 	n := rb.nrune
-	if n >= maxCombiningChars {
+	if n >= maxCombiningChars+1 {
 		return false
 	}
 	b := rb.rune[:]
@@ -68,7 +67,7 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
 	}
 	rb.nrune += 1
 	pos := uint8(rb.nbyte)
-	rb.nbyte += info.size
+	rb.nbyte += utf8.UTFMax
 	info.pos = pos
 	b[n] = info
 	return true
@@ -81,17 +80,21 @@ func (rb *reorderBuffer) insert(src []byte, info runeInfo) bool {
 		rune, _ := utf8.DecodeRune(src)
 		return rb.decomposeHangul(uint32(rune))
 	}
-	pos := rb.nbyte
 	if info.flags.hasDecomposition() {
 		dcomp := rb.f.decompose(src)
-		for i := 0; i < len(dcomp); i += int(info.size) {
+		for i := 0; i < len(dcomp); {
 			info = rb.f.info(dcomp[i:])
+			pos := rb.nbyte
 			if !rb.insertOrdered(info) {
 				return false
 			}
+			end := i + int(info.size)
+			copy(rb.byte[pos:], dcomp[i:end])
+			i = end
 		}
-		copy(rb.byte[pos:], dcomp)
 	} else {
+		// insertOrder changes nbyte
+		pos := rb.nbyte
 		if !rb.insertOrdered(info) {
 			return false
 		}
@@ -107,18 +110,21 @@ func (rb *reorderBuffer) insertString(src string, info runeInfo) bool {
 		rune, _ := utf8.DecodeRuneInString(src)
 		return rb.decomposeHangul(uint32(rune))
 	}
-	pos := rb.nbyte
-	dcomp := rb.f.decomposeString(src)
-	dn := len(dcomp)
-	if dn != 0 {
-		for i := 0; i < dn; i += int(info.size) {
+	if info.flags.hasDecomposition() {
+		dcomp := rb.f.decomposeString(src)
+		for i := 0; i < len(dcomp); {
 			info = rb.f.info(dcomp[i:])
+			pos := rb.nbyte
 			if !rb.insertOrdered(info) {
 				return false
 			}
+			end := i + int(info.size)
+			copy(rb.byte[pos:], dcomp[i:end])
+			i = end
 		}
-		copy(rb.byte[pos:], dcomp)
 	} else {
+		// insertOrder changes nbyte
+		pos := rb.nbyte
 		if !rb.insertOrdered(info) {
 			return false
 		}
@@ -131,17 +137,16 @@ func (rb *reorderBuffer) insertString(src string, info runeInfo) bool {
 func (rb *reorderBuffer) appendRune(rune uint32) {
 	bn := rb.nbyte
 	sz := utf8.EncodeRune(rb.byte[bn:], int(rune))
-	rb.nbyte += uint8(sz)
+	rb.nbyte += utf8.UTFMax
 	rb.rune[rb.nrune] = runeInfo{bn, uint8(sz), 0, 0}
 	rb.nrune++
 }
 
 // assignRune sets a rune at position pos. It is used for Hangul and recomposition.
 func (rb *reorderBuffer) assignRune(pos int, rune uint32) {
-	bn := rb.nbyte
+	bn := rb.rune[pos].pos
 	sz := utf8.EncodeRune(rb.byte[bn:], int(rune))
 	rb.rune[pos] = runeInfo{bn, uint8(sz), 0, 0}
-	rb.nbyte += uint8(sz)
 }
 
 // runeAt returns the rune at position n. It is used for Hangul and recomposition.
@@ -259,11 +264,10 @@ func (rb *reorderBuffer) decomposeHangul(rune uint32) bool {
 
 // combineHangul algorithmically combines Jamo character components into Hangul.
 // See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
-func (rb *reorderBuffer) combineHangul() {
-	k := 1
+func (rb *reorderBuffer) combineHangul(s, i, k int) {
 	b := rb.rune[:]
 	bn := rb.nrune
-	for s, i := 0, 1; i < bn; i++ {
+	for ; i < bn; i++ {
 		cccB := b[k-1].ccc
 		cccC := b[i].ccc
 		if cccB == 0 {
@@ -305,14 +309,17 @@ func (rb *reorderBuffer) compose() {
 	//  blocked from S if and only if there is some character B between S
 	//  and C, and either B is a starter or it has the same or higher
 	//  combining class as C."
+	bn := rb.nrune
+	if bn == 0 {
+		return
+	}
 	k := 1
 	b := rb.rune[:]
-	bn := rb.nrune
 	for s, i := 0, 1; i < bn; i++ {
 		if isJamoVT(rb.bytesAt(i)) {
 			// Redo from start in Hangul mode. Necessary to support
 			// U+320E..U+321E in NFKC mode.
-			rb.combineHangul()
+			rb.combineHangul(s, i, k)
 			return
 		}
 		ii := b[i]
diff --git a/src/pkg/exp/norm/forminfo.go b/src/pkg/exp/norm/forminfo.go
index ee3edb8..5e01e89 100644
--- a/src/pkg/exp/norm/forminfo.go
+++ b/src/pkg/exp/norm/forminfo.go
@@ -77,7 +77,7 @@ func decompBoundary(f *formInfo, info runeInfo) bool {
 }
 
 func compBoundaryBefore(f *formInfo, info runeInfo) bool {
-	if info.ccc == 0 && info.flags.isYesC() {
+	if info.ccc == 0 && !info.flags.combinesBackward() {
 		return true
 	}
 	// We assume that the CCC of the first character in a decomposition
@@ -89,9 +89,7 @@ func compBoundaryBefore(f *formInfo, info runeInfo) bool {
 func compBoundaryAfter(f *formInfo, info runeInfo) bool {
 	// This misses values where the last char in a decomposition is a
 	// boundary such as Hangul with JamoT.
-	// TODO(mpvl): verify this does not lead to segments that do
-	// not fit in the reorderBuffer.
-	return info.flags.isInert()
+	return info.isInert()
 }
 
 // We pack quick check data in 4 bits:
@@ -110,12 +108,15 @@ func (i qcInfo) isNoC() bool   { return i&0x6 == 0x2 }
 func (i qcInfo) isMaybe() bool { return i&0x4 != 0 }
 func (i qcInfo) isYesD() bool  { return i&0x1 == 0 }
 func (i qcInfo) isNoD() bool   { return i&0x1 != 0 }
-func (i qcInfo) isInert() bool { return i&0xf == 0 }
 
 func (i qcInfo) combinesForward() bool  { return i&0x8 != 0 }
 func (i qcInfo) combinesBackward() bool { return i&0x4 != 0 } // == isMaybe
 func (i qcInfo) hasDecomposition() bool { return i&0x1 != 0 } // == isNoD
 
+func (r runeInfo) isInert() bool {
+	return r.flags&0xf == 0 && r.ccc == 0
+}
+
 // Wrappers for tables.go
 
 // The 16-bit value of the decompostion tries is an index into a byte
diff --git a/src/pkg/exp/norm/maketables.go b/src/pkg/exp/norm/maketables.go
index e3e5700..dbe5606 100644
--- a/src/pkg/exp/norm/maketables.go
+++ b/src/pkg/exp/norm/maketables.go
@@ -515,9 +515,13 @@ func completeCharFields(form int) {
 			f.quickCheck[MComposed] = QCNo
 		case (i & 0xffff00) == JamoLBase:
 			f.quickCheck[MComposed] = QCYes
-			if JamoVBase <= i && i < JamoVEnd {
+			if JamoLBase <= i && i < JamoLEnd {
+				f.combinesForward = true
+			}
+			if JamoVBase <= i && i < JamoTEnd {
 				f.quickCheck[MComposed] = QCMaybe
 				f.combinesBackward = true
+				f.combinesForward = true
 			}
 			if JamoTBase <= i && i < JamoTEnd {
 				f.quickCheck[MComposed] = QCMaybe
@@ -562,7 +566,7 @@ func makeEntry(f *FormInfo) uint16 {
 	case QCMaybe:
 		e |= 0x6
 	default:
-		log.Fatalf("Illegal quickcheck value %d.", f.quickCheck[MComposed])
+		log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed])
 	}
 	return e
 }
diff --git a/src/pkg/exp/norm/normalize.go b/src/pkg/exp/norm/normalize.go
index e9d18dd..0bbf254 100644
--- a/src/pkg/exp/norm/normalize.go
+++ b/src/pkg/exp/norm/normalize.go
@@ -5,6 +5,8 @@
 // Package norm contains types and functions for normalizing Unicode strings.
 package norm
 
+import "utf8"
+
 // A Form denotes a canonical representation of Unicode code points.
 // The Unicode-defined normalization and equivalence forms are:
 //
@@ -32,17 +34,57 @@ const (
 
 // Bytes returns f(b). May return b if f(b) = b.
 func (f Form) Bytes(b []byte) []byte {
-	panic("not implemented")
+	n := f.QuickSpan(b)
+	if n == len(b) {
+		return b
+	}
+	out := make([]byte, n, len(b))
+	copy(out, b[0:n])
+	return f.Append(out, b[n:]...)
 }
 
 // String returns f(s).
 func (f Form) String(s string) string {
-	panic("not implemented")
+	n := f.QuickSpanString(s)
+	if n == len(s) {
+		return s
+	}
+	out := make([]byte, 0, len(s))
+	copy(out, s[0:n])
+	return string(f.AppendString(out, s[n:]))
 }
 
 // IsNormal returns true if b == f(b).
 func (f Form) IsNormal(b []byte) bool {
-	panic("not implemented")
+	fd := formTable[f]
+	bp := quickSpan(fd, b)
+	if bp == len(b) {
+		return true
+	}
+	rb := reorderBuffer{f: *fd}
+	for bp < len(b) {
+		decomposeSegment(&rb, b[bp:])
+		if fd.composing {
+			rb.compose()
+		}
+		for i := 0; i < rb.nrune; i++ {
+			info := rb.rune[i]
+			if bp+int(info.size) > len(b) {
+				return false
+			}
+			p := info.pos
+			pe := p + info.size
+			for ; p < pe; p++ {
+				if b[bp] != rb.byte[p] {
+					return false
+				}
+				bp++
+			}
+		}
+		rb.reset()
+		bp += quickSpan(fd, b[bp:])
+	}
+	return true
 }
 
 // IsNormalString returns true if s == f(s).
@@ -50,14 +92,99 @@ func (f Form) IsNormalString(s string) bool {
 	panic("not implemented")
 }
 
+// patchTail fixes a case where a rune may be incorrectly normalized
+// if it is followed by illegal continuation bytes. It returns the
+// patched buffer and the number of trailing continuation bytes that
+// have been dropped.
+func patchTail(rb *reorderBuffer, buf []byte) ([]byte, int) {
+	info, p := lastRuneStart(&rb.f, buf)
+	if p == -1 || info.size == 0 {
+		return buf, 0
+	}
+	end := p + int(info.size)
+	extra := len(buf) - end
+	if extra > 0 {
+		buf = decomposeToLastBoundary(rb, buf[:end])
+		if rb.f.composing {
+			rb.compose()
+		}
+		return rb.flush(buf), extra
+	}
+	return buf, 0
+}
+
+func appendQuick(f *formInfo, dst, src []byte) ([]byte, int) {
+	if len(src) == 0 {
+		return dst, 0
+	}
+	end := quickSpan(f, src)
+	return append(dst, src[:end]...), end
+}
+
 // Append returns f(append(out, b...)).
-// The buffer out must be empty or equal to f(out).
-func (f Form) Append(out, b []byte) []byte {
-	panic("not implemented")
+// The buffer out must be nil, empty, or equal to f(out).
+func (f Form) Append(out []byte, src ...byte) []byte {
+	if len(src) == 0 {
+		return out
+	}
+	fd := formTable[f]
+	rb := &reorderBuffer{f: *fd}
+	return doAppend(rb, out, src)
+}
+
+func doAppend(rb *reorderBuffer, out, src []byte) []byte {
+	doMerge := len(out) > 0
+	p := 0
+	if !utf8.RuneStart(src[0]) {
+		// Move leading non-starters to destination.
+		for p++; p < len(src) && !utf8.RuneStart(src[p]); p++ {
+		}
+		out = append(out, src[:p]...)
+		buf, ndropped := patchTail(rb, out)
+		if ndropped > 0 {
+			out = append(buf, src[p-ndropped:p]...)
+			doMerge = false // no need to merge, ends with illegal UTF-8
+		} else {
+			out = decomposeToLastBoundary(rb, buf) // force decomposition
+		}
+	}
+	fd := &rb.f
+	if doMerge {
+		var info runeInfo
+		if p < len(src) {
+			info = fd.info(src[p:])
+			if p == 0 && !fd.boundaryBefore(fd, info) {
+				out = decomposeToLastBoundary(rb, out)
+			}
+		}
+		if info.size == 0 || fd.boundaryBefore(fd, info) {
+			if fd.composing {
+				rb.compose()
+			}
+			out = rb.flush(out)
+			if info.size == 0 {
+				// Append incomplete UTF-8 encoding.
+				return append(out, src[p:]...)
+			}
+		}
+	}
+	if rb.nrune == 0 {
+		src = src[p:]
+		out, p = appendQuick(fd, out, src)
+	}
+	for n := 0; p < len(src); p += n {
+		p += decomposeSegment(rb, src[p:])
+		if fd.composing {
+			rb.compose()
+		}
+		out = rb.flush(out)
+		out, n = appendQuick(fd, out, src[p:])
+	}
+	return out
 }
 
 // AppendString returns f(append(out, []byte(s))).
-// The buffer out must be empty or equal to f(out).
+// The buffer out must be nil, empty, or equal to f(out).
 func (f Form) AppendString(out []byte, s string) []byte {
 	panic("not implemented")
 }
@@ -65,7 +192,64 @@ func (f Form) AppendString(out []byte, s string) []byte {
 // QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]).
 // It is not guaranteed to return the largest such n.
 func (f Form) QuickSpan(b []byte) int {
-	panic("not implemented")
+	return quickSpan(formTable[f], b)
+}
+
+func quickSpan(fd *formInfo, b []byte) int {
+	var lastCC uint8
+	var lastSegStart int
+	var i, nc int
+	for i < len(b) {
+		if b[i] < utf8.RuneSelf {
+			// Keep the loop tight for ASCII processing, as this is where
+			// most of the time is spent for this case.
+			for i++; i < len(b) && b[i] < utf8.RuneSelf; i++ {
+			}
+			lastSegStart = i - 1
+			lastCC = 0
+			nc = 0
+			continue
+		}
+		info := fd.info(b[i:])
+		if info.size == 0 {
+			// include incomplete runes
+			return len(b)
+		}
+		cc := info.ccc
+		if fd.composing {
+			if !info.flags.isYesC() {
+				break
+			}
+		} else {
+			if !info.flags.isYesD() {
+				break
+			}
+		}
+		if cc == 0 {
+			lastSegStart = i
+			nc = 0
+		} else {
+			if nc >= maxCombiningChars {
+				lastSegStart = i
+				lastCC = cc
+				nc = 1
+			} else {
+				if lastCC > cc {
+					return lastSegStart
+				}
+				nc++
+			}
+		}
+		lastCC = cc
+		i += int(info.size)
+	}
+	if i == len(b) {
+		return len(b)
+	}
+	if fd.composing {
+		return lastSegStart
+	}
+	return i
 }
 
 // QuickSpanString returns a boundary n such that b[0:n] == f(s[0:n]).
@@ -74,26 +258,174 @@ func (f Form) QuickSpanString(s string) int {
 	panic("not implemented")
 }
 
-// FirstBoundary returns the position i of the first boundary in b.
-// It returns len(b), false if b contains no boundaries.
-func (f Form) FirstBoundary(b []byte) (i int, ok bool) {
-	panic("not implemented")
+// FirstBoundary returns the position i of the first boundary in b
+// or -1 if b contains no boundary.
+func (f Form) FirstBoundary(b []byte) int {
+	i := 0
+	for ; i < len(b) && !utf8.RuneStart(b[i]); i++ {
+	}
+	if i >= len(b) {
+		return -1
+	}
+	fd := formTable[f]
+	info := fd.info(b[i:])
+	for n := 0; info.size != 0 && !fd.boundaryBefore(fd, info); {
+		i += int(info.size)
+		if n++; n >= maxCombiningChars {
+			return i
+		}
+		if i >= len(b) {
+			if !fd.boundaryAfter(fd, info) {
+				return -1
+			}
+			return len(b)
+		}
+		info = fd.info(b[i:])
+	}
+	if info.size == 0 {
+		return -1
+	}
+	return i
 }
 
-// FirstBoundaryInString return the position i of the first boundary in s.
-// It returns len(s), false if s contains no boundaries.
+// FirstBoundaryInString returns the position i of the first boundary in s
+// or -1 if s contains no boundary.
 func (f Form) FirstBoundaryInString(s string) (i int, ok bool) {
 	panic("not implemented")
 }
 
-// LastBoundaryIn returns the position i of the last boundary in b.
-// It returns 0, false if b contains no boundary.
-func (f Form) LastBoundary(b []byte) (i int, ok bool) {
-	panic("not implemented")
+// LastBoundary returns the position i of the last boundary in b
+// or -1 if b contains no boundary.
+func (f Form) LastBoundary(b []byte) int {
+	return lastBoundary(formTable[f], b)
+}
+
+func lastBoundary(fd *formInfo, b []byte) int {
+	i := len(b)
+	info, p := lastRuneStart(fd, b)
+	if p == -1 {
+		return -1
+	}
+	if info.size == 0 { // ends with incomplete rune
+		if p == 0 { // starts wtih incomplete rune
+			return -1
+		}
+		i = p
+		info, p = lastRuneStart(fd, b[:i])
+		if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter
+			return i
+		}
+	}
+	if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8
+		return i
+	}
+	if fd.boundaryAfter(fd, info) {
+		return i
+	}
+	i = p
+	for n := 0; i >= 0 && !fd.boundaryBefore(fd, info); {
+		info, p = lastRuneStart(fd, b[:i])
+		if n++; n >= maxCombiningChars {
+			return len(b)
+		}
+		if p+int(info.size) != i {
+			if p == -1 { // no boundary found
+				return -1
+			}
+			return i // boundary after an illegal UTF-8 encoding
+		}
+		i = p
+	}
+	return i
 }
 
-// LastBoundaryInString returns the position i of the last boundary in s.
-// It returns 0, false if s contains no boundary.
-func (f Form) LastBoundaryInString(s string) (i int, ok bool) {
+// LastBoundaryInString returns the position i of the last boundary in s
+// or -1 if s contains no boundary.
+func (f Form) LastBoundaryInString(s string) int {
 	panic("not implemented")
 }
+
+// decomposeSegment scans the first segment in src into rb.
+// It returns the number of bytes consumed from src.
+// TODO(mpvl): consider inserting U+034f (Combining Grapheme Joiner)
+// when we detect a sequence of 30+ non-starter chars.
+func decomposeSegment(rb *reorderBuffer, src []byte) int {
+	// Force one character to be consumed.
+	info := rb.f.info(src)
+	if info.size == 0 {
+		return 0
+	}
+	sp := 0
+	for rb.insert(src[sp:], info) {
+		sp += int(info.size)
+		if sp >= len(src) {
+			break
+		}
+		info = rb.f.info(src[sp:])
+		bound := rb.f.boundaryBefore(&rb.f, info)
+		if bound || info.size == 0 {
+			break
+		}
+	}
+	return sp
+}
+
+// lastRuneStart returns the runeInfo and position of the last
+// rune in buf or the zero runeInfo and -1 if no rune was found.
+func lastRuneStart(fd *formInfo, buf []byte) (runeInfo, int) {
+	p := len(buf) - 1
+	for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- {
+	}
+	if p < 0 {
+		return runeInfo{0, 0, 0, 0}, -1
+	}
+	return fd.info(buf[p:]), p
+}
+
+// decomposeToLastBoundary finds an open segment at the end of the buffer
+// and scans it into rb. Returns the buffer minus the last segment.
+func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte {
+	fd := &rb.f
+	info, i := lastRuneStart(fd, buf)
+	if int(info.size) != len(buf)-i {
+		// illegal trailing continuation bytes
+		return buf
+	}
+	if rb.f.boundaryAfter(fd, info) {
+		return buf
+	}
+	var add [maxBackRunes]runeInfo // stores runeInfo in reverse order
+	add[0] = info
+	padd := 1
+	n := 1
+	p := len(buf) - int(info.size)
+	for ; p >= 0 && !rb.f.boundaryBefore(fd, info); p -= int(info.size) {
+		info, i = lastRuneStart(fd, buf[:p])
+		if int(info.size) != p-i {
+			break
+		}
+		// Check that decomposition doesn't result in overflow.
+		if info.flags.hasDecomposition() {
+			dcomp := rb.f.decompose(buf[p-int(info.size):])
+			for i := 0; i < len(dcomp); {
+				inf := rb.f.info(dcomp[i:])
+				i += int(inf.size)
+				n++
+			}
+		} else {
+			n++
+		}
+		if n > maxBackRunes {
+			break
+		}
+		add[padd] = info
+		padd++
+	}
+	pp := p
+	for padd--; padd >= 0; padd-- {
+		info = add[padd]
+		rb.insert(buf[pp:], info)
+		pp += int(info.size)
+	}
+	return buf[:p]
+}
diff --git a/src/pkg/exp/norm/normalize_test.go b/src/pkg/exp/norm/normalize_test.go
new file mode 100644
index 0000000..6e8650d
--- /dev/null
+++ b/src/pkg/exp/norm/normalize_test.go
@@ -0,0 +1,624 @@
+// 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 norm
+
+import (
+	"strings"
+	"testing"
+)
+
+type PositionTest struct {
+	input  string
+	pos    int
+	buffer string // expected contents of reorderBuffer, if applicable
+}
+
+type positionFunc func(rb *reorderBuffer, s string) int
+
+func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []PositionTest) {
+	rb := reorderBuffer{f: *formTable[f]}
+	for i, test := range tests {
+		rb.reset()
+		pos := fn(&rb, test.input)
+		if pos != test.pos {
+			t.Errorf("%s:%d: position is %d; want %d", name, i, pos, test.pos)
+		}
+		runes := []int(test.buffer)
+		if rb.nrune != len(runes) {
+			t.Errorf("%s:%d: reorder buffer lenght is %d; want %d", name, i, rb.nrune, len(runes))
+			continue
+		}
+		for j, want := range runes {
+			found := int(rb.runeAt(j))
+			if found != want {
+				t.Errorf("%s:%d: rune at %d is %U; want %U", name, i, j, found, want)
+			}
+		}
+	}
+}
+
+var decomposeSegmentTests = []PositionTest{
+	// illegal runes
+	{"\xC0", 0, ""},
+	{"\u00E0\x80", 2, "\u0061\u0300"},
+	// starter
+	{"a", 1, "a"},
+	{"ab", 1, "a"},
+	// starter + composing
+	{"a\u0300", 3, "a\u0300"},
+	{"a\u0300b", 3, "a\u0300"},
+	// with decomposition
+	{"\u00C0", 2, "A\u0300"},
+	{"\u00C0b", 2, "A\u0300"},
+	// long
+	{strings.Repeat("\u0300", 31), 62, strings.Repeat("\u0300", 31)},
+	// ends with incomplete UTF-8 encoding
+	{"\xCC", 0, ""},
+	{"\u0300\xCC", 2, "\u0300"},
+}
+
+func decomposeSegmentF(rb *reorderBuffer, s string) int {
+	return decomposeSegment(rb, []byte(s))
+}
+
+func TestDecomposeSegment(t *testing.T) {
+	runPosTests(t, "TestDecomposeSegment", NFC, decomposeSegmentF, decomposeSegmentTests)
+}
+
+var firstBoundaryTests = []PositionTest{
+	// no boundary
+	{"", -1, ""},
+	{"\u0300", -1, ""},
+	{"\x80\x80", -1, ""},
+	// illegal runes
+	{"\xff", 0, ""},
+	{"\u0300\xff", 2, ""},
+	{"\u0300\xc0\x80\x80", 2, ""},
+	// boundaries
+	{"a", 0, ""},
+	{"\u0300a", 2, ""},
+	// Hangul
+	{"\u1103\u1161", 0, ""},
+	{"\u110B\u1173\u11B7", 0, ""},
+	{"\u1161\u110B\u1173\u11B7", 3, ""},
+	{"\u1173\u11B7\u1103\u1161", 6, ""},
+	// too many combining characters.
+	{strings.Repeat("\u0300", maxCombiningChars-1), -1, ""},
+	{strings.Repeat("\u0300", maxCombiningChars), 60, ""},
+	{strings.Repeat("\u0300", maxCombiningChars+1), 60, ""},
+}
+
+func firstBoundary(rb *reorderBuffer, s string) int {
+	return rb.f.form.FirstBoundary([]byte(s))
+}
+
+func TestFirstBoundary(t *testing.T) {
+	runPosTests(t, "TestFirstBoundary", NFC, firstBoundary, firstBoundaryTests)
+}
+
+var decomposeToLastTests = []PositionTest{
+	// ends with inert character
+	{"Hello!", 6, ""},
+	{"\u0632", 2, ""},
+	{"a\u0301\u0635", 5, ""},
+	// ends with non-inert starter
+	{"a", 0, "a"},
+	{"a\u0301a", 3, "a"},
+	{"a\u0301\u03B9", 3, "\u03B9"},
+	{"a\u0327", 0, "a\u0327"},
+	// illegal runes
+	{"\xFF", 1, ""},
+	{"aa\xFF", 3, ""},
+	{"\xC0\x80\x80", 3, ""},
+	{"\xCC\x80\x80", 3, ""},
+	// ends with incomplete UTF-8 encoding
+	{"a\xCC", 2, ""},
+	// ends with combining characters
+	{"\u0300\u0301", 0, "\u0300\u0301"},
+	{"a\u0300\u0301", 0, "a\u0300\u0301"},
+	{"a\u0301\u0308", 0, "a\u0301\u0308"},
+	{"a\u0308\u0301", 0, "a\u0308\u0301"},
+	{"aaaa\u0300\u0301", 3, "a\u0300\u0301"},
+	{"\u0300a\u0300\u0301", 2, "a\u0300\u0301"},
+	{"\u00C0", 0, "A\u0300"},
+	{"a\u00C0", 1, "A\u0300"},
+	// decomposing
+	{"a\u0300\uFDC0", 3, "\u0645\u062C\u064A"},
+	{"\uFDC0" + strings.Repeat("\u0300", 26), 0, "\u0645\u062C\u064A" + strings.Repeat("\u0300", 26)},
+	// Hangul
+	{"a\u1103", 1, "\u1103"},
+	{"a\u110B", 1, "\u110B"},
+	{"a\u110B\u1173", 1, "\u110B\u1173"},
+	// See comment in composition.go:compBoundaryAfter.
+	{"a\u110B\u1173\u11B7", 1, "\u110B\u1173\u11B7"},
+	{"a\uC73C", 1, "\u110B\u1173"},
+	{"다음", 3, "\u110B\u1173\u11B7"},
+	{"다", 0, "\u1103\u1161"},
+	{"\u1103\u1161\u110B\u1173\u11B7", 6, "\u110B\u1173\u11B7"},
+	{"\u110B\u1173\u11B7\u1103\u1161", 9, "\u1103\u1161"},
+	{"다음음", 6, "\u110B\u1173\u11B7"},
+	{"음다다", 6, "\u1103\u1161"},
+	// buffer overflow
+	{"a" + strings.Repeat("\u0300", 30), 3, strings.Repeat("\u0300", 29)},
+	{"\uFDFA" + strings.Repeat("\u0300", 14), 3, strings.Repeat("\u0300", 14)},
+	// weird UTF-8
+	{"a\u0300\u11B7", 0, "a\u0300\u11B7"},
+}
+
+func decomposeToLast(rb *reorderBuffer, s string) int {
+	buf := decomposeToLastBoundary(rb, []byte(s))
+	return len(buf)
+}
+
+func TestDecomposeToLastBoundary(t *testing.T) {
+	runPosTests(t, "TestDecomposeToLastBoundary", NFKC, decomposeToLast, decomposeToLastTests)
+}
+
+var lastBoundaryTests = []PositionTest{
+	// ends with inert character
+	{"Hello!", 6, ""},
+	{"\u0632", 2, ""},
+	// ends with non-inert starter
+	{"a", 0, ""},
+	// illegal runes
+	{"\xff", 1, ""},
+	{"aa\xff", 3, ""},
+	{"a\xff\u0300", 1, ""},
+	{"\xc0\x80\x80", 3, ""},
+	{"\xc0\x80\x80\u0300", 3, ""},
+	// ends with incomplete UTF-8 encoding
+	{"\xCC", -1, ""},
+	{"\xE0\x80", -1, ""},
+	{"\xF0\x80\x80", -1, ""},
+	{"a\xCC", 0, ""},
+	{"\x80\xCC", 1, ""},
+	{"\xCC\xCC", 1, ""},
+	// ends with combining characters
+	{"a\u0300\u0301", 0, ""},
+	{"aaaa\u0300\u0301", 3, ""},
+	{"\u0300a\u0300\u0301", 2, ""},
+	{"\u00C0", 0, ""},
+	{"a\u00C0", 1, ""},
+	// decomposition may recombine
+	{"\u0226", 0, ""},
+	// no boundary
+	{"", -1, ""},
+	{"\u0300\u0301", -1, ""},
+	{"\u0300", -1, ""},
+	{"\x80\x80", -1, ""},
+	{"\x80\x80\u0301", -1, ""},
+	// Hangul
+	{"다음", 3, ""},
+	{"다", 0, ""},
+	{"\u1103\u1161\u110B\u1173\u11B7", 6, ""},
+	{"\u110B\u1173\u11B7\u1103\u1161", 9, ""},
+	// too many combining characters.
+	{strings.Repeat("\u0300", maxCombiningChars-1), -1, ""},
+	{strings.Repeat("\u0300", maxCombiningChars), 60, ""},
+	{strings.Repeat("\u0300", maxCombiningChars+1), 62, ""},
+}
+
+func lastBoundaryF(rb *reorderBuffer, s string) int {
+	return rb.f.form.LastBoundary([]byte(s))
+}
+
+func TestLastBoundary(t *testing.T) {
+	runPosTests(t, "TestLastBoundary", NFC, lastBoundaryF, lastBoundaryTests)
+}
+
+var quickSpanTests = []PositionTest{
+	{"", 0, ""},
+	// starters
+	{"a", 1, ""},
+	{"abc", 3, ""},
+	{"\u043Eb", 3, ""},
+	// incomplete last rune.
+	{"\xCC", 1, ""},
+	{"a\xCC", 2, ""},
+	// incorrectly ordered combining characters
+	{"\u0300\u0316", 0, ""},
+	{"\u0300\u0316cd", 0, ""},
+	// have a maximum number of combining characters.
+	{strings.Repeat("\u035D", 30) + "\u035B", 62, ""},
+	{"a" + strings.Repeat("\u035D", 30) + "\u035B", 63, ""},
+	{"Ɵ" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""},
+	{"aa" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""},
+}
+
+var quickSpanNFDTests = []PositionTest{
+	// needs decomposing
+	{"\u00C0", 0, ""},
+	{"abc\u00C0", 3, ""},
+	// correctly ordered combining characters
+	{"\u0300", 2, ""},
+	{"ab\u0300", 4, ""},
+	{"ab\u0300cd", 6, ""},
+	{"\u0300cd", 4, ""},
+	{"\u0316\u0300", 4, ""},
+	{"ab\u0316\u0300", 6, ""},
+	{"ab\u0316\u0300cd", 8, ""},
+	{"ab\u0316\u0300\u00C0", 6, ""},
+	{"\u0316\u0300cd", 6, ""},
+	{"\u043E\u0308b", 5, ""},
+	// incorrectly ordered combining characters
+	{"ab\u0300\u0316", 1, ""}, // TODO(mpvl): we could skip 'b' as well.
+	{"ab\u0300\u0316cd", 1, ""},
+	// Hangul
+	{"같은", 0, ""},
+}
+
+var quickSpanNFCTests = []PositionTest{
+	// okay composed
+	{"\u00C0", 2, ""},
+	{"abc\u00C0", 5, ""},
+	// correctly ordered combining characters
+	{"ab\u0300", 1, ""},
+	{"ab\u0300cd", 1, ""},
+	{"ab\u0316\u0300", 1, ""},
+	{"ab\u0316\u0300cd", 1, ""},
+	{"\u00C0\u035D", 4, ""},
+	// we do not special case leading combining characters
+	{"\u0300cd", 0, ""},
+	{"\u0300", 0, ""},
+	{"\u0316\u0300", 0, ""},
+	{"\u0316\u0300cd", 0, ""},
+	// incorrectly ordered combining characters
+	{"ab\u0300\u0316", 1, ""},
+	{"ab\u0300\u0316cd", 1, ""},
+	// Hangul
+	{"같은", 6, ""},
+}
+
+func doQuickSpan(rb *reorderBuffer, s string) int {
+	return rb.f.form.QuickSpan([]byte(s))
+}
+
+func TestQuickSpan(t *testing.T) {
+	runPosTests(t, "TestQuickSpanNFD1", NFD, doQuickSpan, quickSpanTests)
+	runPosTests(t, "TestQuickSpanNFD2", NFD, doQuickSpan, quickSpanNFDTests)
+	runPosTests(t, "TestQuickSpanNFC1", NFC, doQuickSpan, quickSpanTests)
+	runPosTests(t, "TestQuickSpanNFC2", NFC, doQuickSpan, quickSpanNFCTests)
+}
+
+var isNormalTests = []PositionTest{
+	{"", 1, ""},
+	// illegal runes
+	{"\xff", 1, ""},
+	// starters
+	{"a", 1, ""},
+	{"abc", 1, ""},
+	{"\u043Eb", 1, ""},
+	// incorrectly ordered combining characters
+	{"\u0300\u0316", 0, ""},
+	{"ab\u0300\u0316", 0, ""},
+	{"ab\u0300\u0316cd", 0, ""},
+	{"\u0300\u0316cd", 0, ""},
+}
+var isNormalNFDTests = []PositionTest{
+	// needs decomposing
+	{"\u00C0", 0, ""},
+	{"abc\u00C0", 0, ""},
+	// correctly ordered combining characters
+	{"\u0300", 1, ""},
+	{"ab\u0300", 1, ""},
+	{"ab\u0300cd", 1, ""},
+	{"\u0300cd", 1, ""},
+	{"\u0316\u0300", 1, ""},
+	{"ab\u0316\u0300", 1, ""},
+	{"ab\u0316\u0300cd", 1, ""},
+	{"\u0316\u0300cd", 1, ""},
+	{"\u043E\u0308b", 1, ""},
+	// Hangul
+	{"같은", 0, ""},
+}
+var isNormalNFCTests = []PositionTest{
+	// okay composed
+	{"\u00C0", 1, ""},
+	{"abc\u00C0", 1, ""},
+	// need reordering
+	{"a\u0300", 0, ""},
+	{"a\u0300cd", 0, ""},
+	{"a\u0316\u0300", 0, ""},
+	{"a\u0316\u0300cd", 0, ""},
+	// correctly ordered combining characters
+	{"ab\u0300", 1, ""},
+	{"ab\u0300cd", 1, ""},
+	{"ab\u0316\u0300", 1, ""},
+	{"ab\u0316\u0300cd", 1, ""},
+	{"\u00C0\u035D", 1, ""},
+	{"\u0300", 1, ""},
+	{"\u0316\u0300cd", 1, ""},
+	// Hangul
+	{"같은", 1, ""},
+}
+
+func isNormal(rb *reorderBuffer, s string) int {
+	if rb.f.form.IsNormal([]byte(s)) {
+		return 1
+	}
+	return 0
+}
+
+func TestIsNormal(t *testing.T) {
+	runPosTests(t, "TestIsNormalNFD1", NFD, isNormal, isNormalTests)
+	runPosTests(t, "TestIsNormalNFD2", NFD, isNormal, isNormalNFDTests)
+	runPosTests(t, "TestIsNormalNFC1", NFC, isNormal, isNormalTests)
+	runPosTests(t, "TestIsNormalNFC2", NFC, isNormal, isNormalNFCTests)
+}
+
+type AppendTest struct {
+	left  string
+	right string
+	out   string
+}
+
+type appendFunc func(f Form, out []byte, s string) []byte
+
+func runAppendTests(t *testing.T, name string, f Form, fn appendFunc, tests []AppendTest) {
+	for i, test := range tests {
+		out := []byte(test.left)
+		out = fn(f, out, test.right)
+		outs := string(out)
+		if len(outs) != len(test.out) {
+			t.Errorf("%s:%d: length is %d; want %d", name, i, len(outs), len(test.out))
+		}
+		if outs != test.out {
+			// Find first rune that differs and show context.
+			ir := []int(outs)
+			ig := []int(test.out)
+			for j := 0; j < len(ir) && j < len(ig); j++ {
+				if ir[j] == ig[j] {
+					continue
+				}
+				if j -= 3; j < 0 {
+					j = 0
+				}
+				for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ {
+					t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, ir[j], ig[j])
+				}
+				break
+			}
+		}
+	}
+}
+
+var appendTests = []AppendTest{
+	// empty buffers
+	{"", "", ""},
+	{"a", "", "a"},
+	{"", "a", "a"},
+	{"", "\u0041\u0307\u0304", "\u01E0"},
+	// segment split across buffers
+	{"", "a\u0300b", "\u00E0b"},
+	{"a", "\u0300b", "\u00E0b"},
+	{"a", "\u0300\u0316", "\u00E0\u0316"},
+	{"a", "\u0316\u0300", "\u00E0\u0316"},
+	{"a", "\u0300a\u0300", "\u00E0\u00E0"},
+	{"a", "\u0300a\u0300a\u0300", "\u00E0\u00E0\u00E0"},
+	{"a", "\u0300aaa\u0300aaa\u0300", "\u00E0aa\u00E0aa\u00E0"},
+	{"a\u0300", "\u0327", "\u00E0\u0327"},
+	{"a\u0327", "\u0300", "\u00E0\u0327"},
+	{"a\u0316", "\u0300", "\u00E0\u0316"},
+	{"\u0041\u0307", "\u0304", "\u01E0"},
+	// Hangul
+	{"", "\u110B\u1173", "\uC73C"},
+	{"", "\u1103\u1161", "\uB2E4"},
+	{"", "\u110B\u1173\u11B7", "\uC74C"},
+	{"", "\u320E", "\x28\uAC00\x29"},
+	{"", "\x28\u1100\u1161\x29", "\x28\uAC00\x29"},
+	{"\u1103", "\u1161", "\uB2E4"},
+	{"\u110B", "\u1173\u11B7", "\uC74C"},
+	{"\u110B\u1173", "\u11B7", "\uC74C"},
+	{"\uC73C", "\u11B7", "\uC74C"},
+	// UTF-8 encoding split across buffers
+	{"a\xCC", "\x80", "\u00E0"},
+	{"a\xCC", "\x80b", "\u00E0b"},
+	{"a\xCC", "\x80a\u0300", "\u00E0\u00E0"},
+	{"a\xCC", "\x80\x80", "\u00E0\x80"},
+	{"a\xCC", "\x80\xCC", "\u00E0\xCC"},
+	{"a\u0316\xCC", "\x80a\u0316\u0300", "\u00E0\u0316\u00E0\u0316"},
+	// ending in incomplete UTF-8 encoding
+	{"", "\xCC", "\xCC"},
+	{"a", "\xCC", "a\xCC"},
+	{"a", "b\xCC", "ab\xCC"},
+	{"\u0226", "\xCC", "\u0226\xCC"},
+	// illegal runes
+	{"", "\x80", "\x80"},
+	{"", "\x80\x80\x80", "\x80\x80\x80"},
+	{"", "\xCC\x80\x80\x80", "\xCC\x80\x80\x80"},
+	{"", "a\x80", "a\x80"},
+	{"", "a\x80\x80\x80", "a\x80\x80\x80"},
+	{"", "a\x80\x80\x80\x80\x80\x80", "a\x80\x80\x80\x80\x80\x80"},
+	{"a", "\x80\x80\x80", "a\x80\x80\x80"},
+	// overflow
+	{"", strings.Repeat("\x80", 33), strings.Repeat("\x80", 33)},
+	{strings.Repeat("\x80", 33), "", strings.Repeat("\x80", 33)},
+	{strings.Repeat("\x80", 33), strings.Repeat("\x80", 33), strings.Repeat("\x80", 66)},
+	// overflow of combining characters
+	{strings.Repeat("\u0300", 33), "", strings.Repeat("\u0300", 33)},
+	// weird UTF-8
+	{"\u00E0\xE1", "\x86", "\u00E0\xE1\x86"},
+	{"a\u0300\u11B7", "\u0300", "\u00E0\u11B7\u0300"},
+	{"a\u0300\u11B7\u0300", "\u0300", "\u00E0\u11B7\u0300\u0300"},
+	{"\u0300", "\xF8\x80\x80\x80\x80\u0300", "\u0300\xF8\x80\x80\x80\x80\u0300"},
+	{"\u0300", "\xFC\x80\x80\x80\x80\x80\u0300", "\u0300\xFC\x80\x80\x80\x80\x80\u0300"},
+	{"\xF8\x80\x80\x80\x80\u0300", "\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"},
+	{"\xFC\x80\x80\x80\x80\x80\u0300", "\u0300", "\xFC\x80\x80\x80\x80\x80\u0300\u0300"},
+}
+
+func appendF(f Form, out []byte, s string) []byte {
+	return f.Append(out, []byte(s)...)
+}
+
+func TestAppend(t *testing.T) {
+	runAppendTests(t, "TestAppend", NFKC, appendF, appendTests)
+}
+
+func doFormBenchmark(b *testing.B, f Form, s string) {
+	b.StopTimer()
+	in := []byte(s)
+	buf := make([]byte, 2*len(in))
+	b.SetBytes(int64(len(s)))
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		buf = f.Append(buf[0:0], in...)
+		buf = buf[0:0]
+	}
+}
+
+var ascii = strings.Repeat("There is nothing to change here! ", 500)
+
+func BenchmarkNormalizeAsciiNFC(b *testing.B) {
+	doFormBenchmark(b, NFC, ascii)
+}
+func BenchmarkNormalizeAsciiNFD(b *testing.B) {
+	doFormBenchmark(b, NFD, ascii)
+}
+func BenchmarkNormalizeAsciiNFKC(b *testing.B) {
+	doFormBenchmark(b, NFKC, ascii)
+}
+func BenchmarkNormalizeAsciiNFKD(b *testing.B) {
+	doFormBenchmark(b, NFKD, ascii)
+}
+
+func doTextBenchmark(b *testing.B, s string) {
+	b.StopTimer()
+	in := make([]byte, len(s))
+	for i := range s {
+		in[i] = s[i]
+	}
+	// Using copy(in, s) makes many tests much slower!?
+	b.SetBytes(int64(len(s)) * 4)
+	var buf = make([]byte, 2*len(in))
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		buf = NFC.Append(buf[0:0], in...)
+		buf = NFD.Append(buf[0:0], in...)
+		buf = NFKC.Append(buf[0:0], in...)
+		buf = NFKD.Append(buf[0:0], in...)
+	}
+}
+
+func BenchmarkCanonicalOrdering(b *testing.B) {
+	doTextBenchmark(b, txt_canon)
+}
+func BenchmarkExtendedLatin(b *testing.B) {
+	doTextBenchmark(b, txt_vn)
+}
+func BenchmarkMiscTwoByteUtf8(b *testing.B) {
+	doTextBenchmark(b, twoByteUtf8)
+}
+func BenchmarkMiscThreeByteUtf8(b *testing.B) {
+	doTextBenchmark(b, threeByteUtf8)
+}
+func BenchmarkHangul(b *testing.B) {
+	doTextBenchmark(b, txt_kr)
+}
+func BenchmarkJapanese(b *testing.B) {
+	doTextBenchmark(b, txt_jp)
+}
+func BenchmarkChinese(b *testing.B) {
+	doTextBenchmark(b, txt_cn)
+}
+
+// Tests sampled from the Canonical ordering tests (Part 2) of
+// http://unicode.org/Public/UNIDATA/NormalizationTest.txt
+const txt_canon = `\u0061\u0315\u0300\u05AE\u0300\u0062 \u0061\u0300\u0315\u0300\u05AE\u0062
+\u0061\u0302\u0315\u0300\u05AE\u0062 \u0061\u0307\u0315\u0300\u05AE\u0062
+\u0061\u0315\u0300\u05AE\u030A\u0062 \u0061\u059A\u0316\u302A\u031C\u0062
+\u0061\u032E\u059A\u0316\u302A\u0062 \u0061\u0338\u093C\u0334\u0062 
+\u0061\u059A\u0316\u302A\u0339       \u0061\u0341\u0315\u0300\u05AE\u0062
+\u0061\u0348\u059A\u0316\u302A\u0062 \u0061\u0361\u0345\u035D\u035C\u0062
+\u0061\u0366\u0315\u0300\u05AE\u0062 \u0061\u0315\u0300\u05AE\u0486\u0062
+\u0061\u05A4\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0613\u0062
+\u0061\u0315\u0300\u05AE\u0615\u0062 \u0061\u0617\u0315\u0300\u05AE\u0062
+\u0061\u0619\u0618\u064D\u064E\u0062 \u0061\u0315\u0300\u05AE\u0654\u0062
+\u0061\u0315\u0300\u05AE\u06DC\u0062 \u0061\u0733\u0315\u0300\u05AE\u0062
+\u0061\u0744\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0745\u0062
+\u0061\u09CD\u05B0\u094D\u3099\u0062 \u0061\u0E38\u0E48\u0E38\u0C56\u0062
+\u0061\u0EB8\u0E48\u0E38\u0E49\u0062 \u0061\u0F72\u0F71\u0EC8\u0F71\u0062
+\u0061\u1039\u05B0\u094D\u3099\u0062 \u0061\u05B0\u094D\u3099\u1A60\u0062
+\u0061\u3099\u093C\u0334\u1BE6\u0062 \u0061\u3099\u093C\u0334\u1C37\u0062
+\u0061\u1CD9\u059A\u0316\u302A\u0062 \u0061\u2DED\u0315\u0300\u05AE\u0062
+\u0061\u2DEF\u0315\u0300\u05AE\u0062 \u0061\u302D\u302E\u059A\u0316\u0062`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/
+const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả. 
+Nếu bạn sử dụng, chuyển đổi, hoặc xây dựng dự án từ 
+nội dung được chia sẻ này, bạn phải áp dụng giấy phép này hoặc 
+một giấy phép khác có các điều khoản tương tự như giấy phép này
+cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào
+trên đây cũng có thể được miễn bỏ nếu bạn được sự cho phép của
+người sở hữu bản quyền. Phạm vi công chúng — Khi tác phẩm hoặc
+bất kỳ chương nào của tác phẩm đã trong vùng dành cho công
+chúng theo quy định của pháp luật thì tình trạng của nó không 
+bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru
+const txt_ru = `При обязательном соблюдении следующих условий:
+Attribution — Вы должны атрибутировать произведение (указывать
+автора и источник) в порядке, предусмотренном автором или
+лицензиаром (но только так, чтобы никоим образом не подразумевалось,
+что они поддерживают вас или использование вами данного произведения).
+Υπό τις ακόλουθες προϋποθέσεις:`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/
+const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με τον
+τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια
+(χωρίς όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή
+τη χρήση του έργου από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε,
+τροποποιήσετε ή δημιουργήσετε περαιτέρω βασισμένοι στο έργο θα
+μπορείτε να διανέμετε το έργο που θα προκύψει μόνο με την ίδια ή
+παρόμοια άδεια.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/deed.ar
+const txt_ar = `بموجب الشروط التالية نسب المصنف — يجب عليك أن
+تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من
+الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل).
+المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة
+من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد
+لهذا الترخيص.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/1.0/il/
+const txt_il = `בכפוף לתנאים הבאים: ייחוס — עליך לייחס את היצירה (לתת קרדיט) באופן
+המצויין על-ידי היוצר או מעניק הרישיון (אך לא בשום אופן המרמז על כך
+שהם תומכים בך או בשימוש שלך ביצירה). שיתוף זהה — אם תחליט/י לשנות,
+לעבד או ליצור יצירה נגזרת בהסתמך על יצירה זו, תוכל/י להפיץ את יצירתך
+החדשה רק תחת אותו הרישיון או רישיון דומה לרישיון זה.`
+
+const twoByteUtf8 = txt_ru + txt_gr + txt_ar + txt_il
+
+// Taken from http://creativecommons.org/licenses/by-sa/2.0/kr/
+const txt_kr = `다음과 같은 조건을 따라야 합니다: 저작자표시
+(Attribution) — 저작자나 이용허락자가 정한 방법으로 저작물의
+원저작자를 표시하여야 합니다(그러나 원저작자가 이용자나 이용자의
+이용을 보증하거나 추천한다는 의미로 표시해서는 안됩니다). 
+동일조건변경허락 — 이 저작물을 이용하여 만든 이차적 저작물에는 본
+라이선스와 동일한 라이선스를 적용해야 합니다.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/th/
+const txt_th = `ภายใต้เงื่อนไข ดังต่อไปนี้ : แสดงที่มา — คุณต้องแสดงที่
+มาของงานดังกล่าว ตามรูปแบบที่ผู้สร้างสรรค์หรือผู้อนุญาตกำหนด (แต่
+ไม่ใช่ในลักษณะที่ว่า พวกเขาสนับสนุนคุณหรือสนับสนุนการที่
+คุณนำงานไปใช้) อนุญาตแบบเดียวกัน — หากคุณดัดแปลง เปลี่ยนรูป หรื
+อต่อเติมงานนี้ คุณต้องใช้สัญญาอนุญาตแบบเดียวกันหรือแบบที่เหมื
+อนกับสัญญาอนุญาตที่ใช้กับงานนี้เท่านั้น`
+
+const threeByteUtf8 = txt_th
+
+// Taken from http://creativecommons.org/licenses/by-sa/2.0/jp/
+const txt_jp = `あなたの従うべき条件は以下の通りです。
+表示 — あなたは原著作者のクレジットを表示しなければなりません。
+継承 — もしあなたがこの作品を改変、変形または加工した場合、
+あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ
+頒布することができます。`
+
+// http://creativecommons.org/licenses/by-sa/2.5/cn/
+const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、
+广播或通过信息网络传播本作品 创作演绎作品
+对本作品进行商业性使用 惟须遵守下列条件:
+署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。
+相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作,
+您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。`
diff --git a/src/pkg/exp/norm/readwriter.go b/src/pkg/exp/norm/readwriter.go
new file mode 100644
index 0000000..fce6c40
--- /dev/null
+++ b/src/pkg/exp/norm/readwriter.go
@@ -0,0 +1,121 @@
+// 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 norm
+
+import (
+	"io"
+	"os"
+)
+
+type normWriter struct {
+	rb  reorderBuffer
+	w   io.Writer
+	buf []byte
+}
+
+// Write implements the standard write interface.  If the last characters are
+// not at a normalization boundary, the bytes will be buffered for the next
+// write. The remaining bytes will be written on close.
+func (w *normWriter) Write(data []byte) (n int, err os.Error) {
+	// Process data in pieces to keep w.buf size bounded.
+	const chunk = 4000
+
+	for len(data) > 0 {
+		// Normalize into w.buf.
+		m := len(data)
+		if m > chunk {
+			m = chunk
+		}
+		w.buf = doAppend(&w.rb, w.buf, data[:m])
+		data = data[m:]
+		n += m
+
+		// Write out complete prefix, save remainder.
+		// Note that lastBoundary looks back at most 30 runes.
+		i := lastBoundary(&w.rb.f, w.buf)
+		if i == -1 {
+			i = 0
+		}
+		if i > 0 {
+			if _, err = w.w.Write(w.buf[:i]); err != nil {
+				break
+			}
+			bn := copy(w.buf, w.buf[i:])
+			w.buf = w.buf[:bn]
+		}
+	}
+	return n, err
+}
+
+// Close forces data that remains in the buffer to be written.
+func (w *normWriter) Close() os.Error {
+	if len(w.buf) > 0 {
+		_, err := w.w.Write(w.buf)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Writer returns a new writer that implements Write(b)
+// by writing f(b) to w.  The returned writer may use an
+// an internal buffer to maintain state across Write calls.
+// Calling its Close method writes any buffered data to w.
+func (f Form) Writer(w io.Writer) io.WriteCloser {
+	return &normWriter{rb: reorderBuffer{f: *formTable[f]}, w: w}
+}
+
+type normReader struct {
+	rb           reorderBuffer
+	r            io.Reader
+	inbuf        []byte
+	outbuf       []byte
+	bufStart     int
+	lastBoundary int
+	err          os.Error
+}
+
+// Read implements the standard read interface.
+func (r *normReader) Read(p []byte) (int, os.Error) {
+	for {
+		if r.lastBoundary-r.bufStart > 0 {
+			n := copy(p, r.outbuf[r.bufStart:r.lastBoundary])
+			r.bufStart += n
+			if r.lastBoundary-r.bufStart > 0 {
+				return n, nil
+			}
+			return n, r.err
+		}
+		if r.err != nil {
+			return 0, r.err
+		}
+		outn := copy(r.outbuf, r.outbuf[r.lastBoundary:])
+		r.outbuf = r.outbuf[0:outn]
+		r.bufStart = 0
+
+		n, err := r.r.Read(r.inbuf)
+		r.err = err // save error for when done with buffer
+		if n > 0 {
+			r.outbuf = doAppend(&r.rb, r.outbuf, r.inbuf[0:n])
+		}
+		if err == os.EOF {
+			r.lastBoundary = len(r.outbuf)
+		} else {
+			r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf)
+			if r.lastBoundary == -1 {
+				r.lastBoundary = 0
+			}
+		}
+	}
+	panic("should not reach here")
+}
+
+// Reader returns a new reader that implements Read
+// by reading data from r and returning f(data).
+func (f Form) Reader(r io.Reader) io.Reader {
+	const chunk = 4000
+	return &normReader{rb: reorderBuffer{f: *formTable[f]}, r: r, inbuf: make([]byte, chunk)}
+}
diff --git a/src/pkg/exp/norm/readwriter_test.go b/src/pkg/exp/norm/readwriter_test.go
new file mode 100644
index 0000000..b415f2b
--- /dev/null
+++ b/src/pkg/exp/norm/readwriter_test.go
@@ -0,0 +1,69 @@
+// 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 norm
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"strings"
+	"testing"
+)
+
+var ioTests = []AppendTest{
+	{"", strings.Repeat("a\u0316\u0300", 6), strings.Repeat("\u00E0\u0316", 6)},
+	{"", strings.Repeat("a\u0300\u0316", 4000), strings.Repeat("\u00E0\u0316", 4000)},
+	{"", strings.Repeat("\x80\x80", 4000), strings.Repeat("\x80\x80", 4000)},
+	{"", "\u0041\u0307\u0304", "\u01E0"},
+}
+
+var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003}
+
+func readFunc(size int) appendFunc {
+	return func(f Form, out []byte, s string) []byte {
+		out = append(out, []byte(s)...)
+		r := f.Reader(bytes.NewBuffer(out))
+		buf := make([]byte, size)
+		result := []byte{}
+		for n, err := 0, os.Error(nil); err == nil; {
+			n, err = r.Read(buf)
+			result = append(result, buf[:n]...)
+		}
+		return result
+	}
+}
+
+func TestReader(t *testing.T) {
+	for _, s := range bufSizes {
+		name := fmt.Sprintf("TestReader%da", s)
+		runAppendTests(t, name, NFKC, readFunc(s), appendTests)
+		name = fmt.Sprintf("TestReader%db", s)
+		runAppendTests(t, name, NFKC, readFunc(s), ioTests)
+	}
+}
+
+func writeFunc(size int) appendFunc {
+	return func(f Form, out []byte, s string) []byte {
+		in := append(out, []byte(s)...)
+		result := new(bytes.Buffer)
+		w := f.Writer(result)
+		buf := make([]byte, size)
+		for n := 0; len(in) > 0; in = in[n:] {
+			n = copy(buf, in)
+			_, _ = w.Write(buf[:n])
+		}
+		w.Close()
+		return result.Bytes()
+	}
+}
+
+func TestWriter(t *testing.T) {
+	for _, s := range bufSizes {
+		name := fmt.Sprintf("TestWriter%da", s)
+		runAppendTests(t, name, NFKC, writeFunc(s), appendTests)
+		name = fmt.Sprintf("TestWriter%db", s)
+		runAppendTests(t, name, NFKC, writeFunc(s), ioTests)
+	}
+}
diff --git a/src/pkg/exp/norm/tables.go b/src/pkg/exp/norm/tables.go
index 76995c2..36dcd5c 100644
--- a/src/pkg/exp/norm/tables.go
+++ b/src/pkg/exp/norm/tables.go
@@ -5356,9 +5356,9 @@ var recompMap = map[uint32]uint32{
 	0x10A510BA: 0x110AB,
 }
 
-// charInfoValues: 10944 entries, 21888 bytes
+// charInfoValues: 11008 entries, 22016 bytes
 // Block 2 is the null block.
-var charInfoValues = [10944]uint16{
+var charInfoValues = [11008]uint16{
 	// Block 0x0, offset 0x0
 	0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800,
 	// Block 0x1, offset 0x40
@@ -5686,305 +5686,306 @@ var charInfoValues = [10944]uint16{
 	// Block 0x38, offset 0xe00
 	0x0e3c: 0x3000,
 	// Block 0x39, offset 0xe40
-	0x0e61: 0x6600, 0x0e62: 0x6600, 0x0e63: 0x6600,
-	0x0e64: 0x6600, 0x0e65: 0x6600, 0x0e66: 0x6600, 0x0e67: 0x6600, 0x0e68: 0x6600, 0x0e69: 0x6600,
-	0x0e6a: 0x6600, 0x0e6b: 0x6600, 0x0e6c: 0x6600, 0x0e6d: 0x6600, 0x0e6e: 0x6600, 0x0e6f: 0x6600,
-	0x0e70: 0x6600, 0x0e71: 0x6600, 0x0e72: 0x6600, 0x0e73: 0x6600, 0x0e74: 0x6600, 0x0e75: 0x6600,
+	0x0e40: 0x8800, 0x0e41: 0x8800, 0x0e42: 0x8800, 0x0e43: 0x8800, 0x0e44: 0x8800, 0x0e45: 0x8800,
+	0x0e46: 0x8800, 0x0e47: 0x8800, 0x0e48: 0x8800, 0x0e49: 0x8800, 0x0e4a: 0x8800, 0x0e4b: 0x8800,
+	0x0e4c: 0x8800, 0x0e4d: 0x8800, 0x0e4e: 0x8800, 0x0e4f: 0x8800, 0x0e50: 0x8800, 0x0e51: 0x8800,
+	0x0e52: 0x8800,
 	// Block 0x3a, offset 0xe80
-	0x0ea8: 0x6600, 0x0ea9: 0x6600,
-	0x0eaa: 0x6600, 0x0eab: 0x6600, 0x0eac: 0x6600, 0x0ead: 0x6600, 0x0eae: 0x6600, 0x0eaf: 0x6600,
-	0x0eb0: 0x6600, 0x0eb1: 0x6600, 0x0eb2: 0x6600, 0x0eb3: 0x6600, 0x0eb4: 0x6600, 0x0eb5: 0x6600,
-	0x0eb6: 0x6600, 0x0eb7: 0x6600, 0x0eb8: 0x6600, 0x0eb9: 0x6600, 0x0eba: 0x6600, 0x0ebb: 0x6600,
-	0x0ebc: 0x6600, 0x0ebd: 0x6600, 0x0ebe: 0x6600, 0x0ebf: 0x6600,
+	0x0ea1: 0xee00, 0x0ea2: 0xee00, 0x0ea3: 0xee00,
+	0x0ea4: 0xee00, 0x0ea5: 0xee00, 0x0ea6: 0xee00, 0x0ea7: 0xee00, 0x0ea8: 0xee00, 0x0ea9: 0xee00,
+	0x0eaa: 0xee00, 0x0eab: 0xee00, 0x0eac: 0xee00, 0x0ead: 0xee00, 0x0eae: 0xee00, 0x0eaf: 0xee00,
+	0x0eb0: 0xee00, 0x0eb1: 0xee00, 0x0eb2: 0xee00, 0x0eb3: 0xee00, 0x0eb4: 0xee00, 0x0eb5: 0xee00,
+	0x0eb6: 0xee00, 0x0eb7: 0xee00, 0x0eb8: 0xee00, 0x0eb9: 0xee00, 0x0eba: 0xee00, 0x0ebb: 0xee00,
+	0x0ebc: 0xee00, 0x0ebd: 0xee00, 0x0ebe: 0xee00, 0x0ebf: 0xee00,
 	// Block 0x3b, offset 0xec0
-	0x0ec0: 0x6600, 0x0ec1: 0x6600, 0x0ec2: 0x6600,
+	0x0ec0: 0xee00, 0x0ec1: 0xee00, 0x0ec2: 0xee00, 0x0ec3: 0xee00, 0x0ec4: 0xee00, 0x0ec5: 0xee00,
+	0x0ec6: 0xee00, 0x0ec7: 0xee00, 0x0ec8: 0xee00, 0x0ec9: 0xee00, 0x0eca: 0xee00, 0x0ecb: 0xee00,
+	0x0ecc: 0xee00, 0x0ecd: 0xee00, 0x0ece: 0xee00, 0x0ecf: 0xee00, 0x0ed0: 0xee00, 0x0ed1: 0xee00,
+	0x0ed2: 0xee00, 0x0ed3: 0xee00, 0x0ed4: 0xee00, 0x0ed5: 0xee00, 0x0ed6: 0xee00, 0x0ed7: 0xee00,
+	0x0ed8: 0xee00, 0x0ed9: 0xee00, 0x0eda: 0xee00, 0x0edb: 0xee00, 0x0edc: 0xee00, 0x0edd: 0xee00,
+	0x0ede: 0xee00, 0x0edf: 0xee00, 0x0ee0: 0xee00, 0x0ee1: 0xee00, 0x0ee2: 0xee00, 0x0ee3: 0xee00,
+	0x0ee4: 0xee00, 0x0ee5: 0xee00, 0x0ee6: 0xee00, 0x0ee7: 0xee00, 0x0ee8: 0xee00, 0x0ee9: 0xee00,
+	0x0eea: 0xee00, 0x0eeb: 0xee00, 0x0eec: 0xee00, 0x0eed: 0xee00, 0x0eee: 0xee00, 0x0eef: 0xee00,
+	0x0ef0: 0xee00, 0x0ef1: 0xee00, 0x0ef2: 0xee00, 0x0ef3: 0xee00, 0x0ef4: 0xee00, 0x0ef5: 0xee00,
+	0x0ef6: 0xee00, 0x0ef7: 0xee00, 0x0ef8: 0xee00, 0x0ef9: 0xee00, 0x0efa: 0xee00, 0x0efb: 0xee00,
+	0x0efc: 0xee00, 0x0efd: 0xee00, 0x0efe: 0xee00, 0x0eff: 0xee00,
 	// Block 0x3c, offset 0xf00
-	0x0f1d: 0x00e6,
-	0x0f1e: 0x00e6, 0x0f1f: 0x00e6,
+	0x0f00: 0xee00, 0x0f01: 0xee00, 0x0f02: 0xee00,
 	// Block 0x3d, offset 0xf40
-	0x0f54: 0x0009,
-	0x0f74: 0x0009,
+	0x0f5d: 0x00e6,
+	0x0f5e: 0x00e6, 0x0f5f: 0x00e6,
 	// Block 0x3e, offset 0xf80
-	0x0f92: 0x0009,
-	0x0f9d: 0x00e6,
+	0x0f94: 0x0009,
+	0x0fb4: 0x0009,
 	// Block 0x3f, offset 0xfc0
-	0x0fe9: 0x00e4,
+	0x0fd2: 0x0009,
+	0x0fdd: 0x00e6,
 	// Block 0x40, offset 0x1000
-	0x1039: 0x00de, 0x103a: 0x00e6, 0x103b: 0x00dc,
+	0x1029: 0x00e4,
 	// Block 0x41, offset 0x1040
-	0x1057: 0x00e6,
-	0x1058: 0x00dc,
+	0x1079: 0x00de, 0x107a: 0x00e6, 0x107b: 0x00dc,
 	// Block 0x42, offset 0x1080
-	0x10a0: 0x0009,
-	0x10b5: 0x00e6,
-	0x10b6: 0x00e6, 0x10b7: 0x00e6, 0x10b8: 0x00e6, 0x10b9: 0x00e6, 0x10ba: 0x00e6, 0x10bb: 0x00e6,
-	0x10bc: 0x00e6, 0x10bf: 0x00dc,
+	0x1097: 0x00e6,
+	0x1098: 0x00dc,
 	// Block 0x43, offset 0x10c0
-	0x10c5: 0x8800,
-	0x10c6: 0x1100, 0x10c7: 0x8800, 0x10c8: 0x1100, 0x10c9: 0x8800, 0x10ca: 0x1100, 0x10cb: 0x8800,
-	0x10cc: 0x1100, 0x10cd: 0x8800, 0x10ce: 0x1100, 0x10d1: 0x8800,
-	0x10d2: 0x1100,
-	0x10f4: 0x0007, 0x10f5: 0x6600,
-	0x10fa: 0x8800, 0x10fb: 0x1100,
-	0x10fc: 0x8800, 0x10fd: 0x1100, 0x10fe: 0x8800, 0x10ff: 0x8800,
+	0x10e0: 0x0009,
+	0x10f5: 0x00e6,
+	0x10f6: 0x00e6, 0x10f7: 0x00e6, 0x10f8: 0x00e6, 0x10f9: 0x00e6, 0x10fa: 0x00e6, 0x10fb: 0x00e6,
+	0x10fc: 0x00e6, 0x10ff: 0x00dc,
 	// Block 0x44, offset 0x1100
-	0x1100: 0x1100, 0x1101: 0x1100, 0x1102: 0x8800, 0x1103: 0x1100, 0x1104: 0x0009,
-	0x112b: 0x00e6, 0x112c: 0x00dc, 0x112d: 0x00e6, 0x112e: 0x00e6, 0x112f: 0x00e6,
-	0x1130: 0x00e6, 0x1131: 0x00e6, 0x1132: 0x00e6, 0x1133: 0x00e6,
+	0x1105: 0x8800,
+	0x1106: 0x1100, 0x1107: 0x8800, 0x1108: 0x1100, 0x1109: 0x8800, 0x110a: 0x1100, 0x110b: 0x8800,
+	0x110c: 0x1100, 0x110d: 0x8800, 0x110e: 0x1100, 0x1111: 0x8800,
+	0x1112: 0x1100,
+	0x1134: 0x0007, 0x1135: 0x6600,
+	0x113a: 0x8800, 0x113b: 0x1100,
+	0x113c: 0x8800, 0x113d: 0x1100, 0x113e: 0x8800, 0x113f: 0x8800,
 	// Block 0x45, offset 0x1140
-	0x116a: 0x0009,
+	0x1140: 0x1100, 0x1141: 0x1100, 0x1142: 0x8800, 0x1143: 0x1100, 0x1144: 0x0009,
+	0x116b: 0x00e6, 0x116c: 0x00dc, 0x116d: 0x00e6, 0x116e: 0x00e6, 0x116f: 0x00e6,
+	0x1170: 0x00e6, 0x1171: 0x00e6, 0x1172: 0x00e6, 0x1173: 0x00e6,
 	// Block 0x46, offset 0x1180
-	0x11a6: 0x0007,
-	0x11b2: 0x0009, 0x11b3: 0x0009,
+	0x11aa: 0x0009,
 	// Block 0x47, offset 0x11c0
-	0x11f7: 0x0007,
+	0x11e6: 0x0007,
+	0x11f2: 0x0009, 0x11f3: 0x0009,
 	// Block 0x48, offset 0x1200
-	0x1210: 0x00e6, 0x1211: 0x00e6,
-	0x1212: 0x00e6, 0x1214: 0x0001, 0x1215: 0x00dc, 0x1216: 0x00dc, 0x1217: 0x00dc,
-	0x1218: 0x00dc, 0x1219: 0x00dc, 0x121a: 0x00e6, 0x121b: 0x00e6, 0x121c: 0x00dc, 0x121d: 0x00dc,
-	0x121e: 0x00dc, 0x121f: 0x00dc, 0x1220: 0x00e6, 0x1222: 0x0001, 0x1223: 0x0001,
-	0x1224: 0x0001, 0x1225: 0x0001, 0x1226: 0x0001, 0x1227: 0x0001, 0x1228: 0x0001,
-	0x122d: 0x00dc,
+	0x1237: 0x0007,
 	// Block 0x49, offset 0x1240
-	0x126c: 0x3000, 0x126d: 0x3000, 0x126e: 0x3000,
-	0x1270: 0x3000, 0x1271: 0x3000, 0x1272: 0x3000, 0x1273: 0x3000, 0x1274: 0x3000, 0x1275: 0x3000,
-	0x1276: 0x3000, 0x1277: 0x3000, 0x1278: 0x3000, 0x1279: 0x3000, 0x127a: 0x3000,
-	0x127c: 0x3000, 0x127d: 0x3000, 0x127e: 0x3000, 0x127f: 0x3000,
+	0x1250: 0x00e6, 0x1251: 0x00e6,
+	0x1252: 0x00e6, 0x1254: 0x0001, 0x1255: 0x00dc, 0x1256: 0x00dc, 0x1257: 0x00dc,
+	0x1258: 0x00dc, 0x1259: 0x00dc, 0x125a: 0x00e6, 0x125b: 0x00e6, 0x125c: 0x00dc, 0x125d: 0x00dc,
+	0x125e: 0x00dc, 0x125f: 0x00dc, 0x1260: 0x00e6, 0x1262: 0x0001, 0x1263: 0x0001,
+	0x1264: 0x0001, 0x1265: 0x0001, 0x1266: 0x0001, 0x1267: 0x0001, 0x1268: 0x0001,
+	0x126d: 0x00dc,
 	// Block 0x4a, offset 0x1280
-	0x1280: 0x3000, 0x1281: 0x3000, 0x1282: 0x3000, 0x1283: 0x3000, 0x1284: 0x3000, 0x1285: 0x3000,
-	0x1286: 0x3000, 0x1287: 0x3000, 0x1288: 0x3000, 0x1289: 0x3000, 0x128a: 0x3000, 0x128b: 0x3000,
-	0x128c: 0x3000, 0x128d: 0x3000, 0x128f: 0x3000, 0x1290: 0x3000, 0x1291: 0x3000,
-	0x1292: 0x3000, 0x1293: 0x3000, 0x1294: 0x3000, 0x1295: 0x3000, 0x1296: 0x3000, 0x1297: 0x3000,
-	0x1298: 0x3000, 0x1299: 0x3000, 0x129a: 0x3000, 0x129b: 0x3000, 0x129c: 0x3000, 0x129d: 0x3000,
-	0x129e: 0x3000, 0x129f: 0x3000, 0x12a0: 0x3000, 0x12a1: 0x3000, 0x12a2: 0x3000, 0x12a3: 0x3000,
-	0x12a4: 0x3000, 0x12a5: 0x3000, 0x12a6: 0x3000, 0x12a7: 0x3000, 0x12a8: 0x3000, 0x12a9: 0x3000,
-	0x12aa: 0x3000,
-	0x12b8: 0x3000,
+	0x12ac: 0x3000, 0x12ad: 0x3000, 0x12ae: 0x3000,
+	0x12b0: 0x3000, 0x12b1: 0x3000, 0x12b2: 0x3000, 0x12b3: 0x3000, 0x12b4: 0x3000, 0x12b5: 0x3000,
+	0x12b6: 0x3000, 0x12b7: 0x3000, 0x12b8: 0x3000, 0x12b9: 0x3000, 0x12ba: 0x3000,
+	0x12bc: 0x3000, 0x12bd: 0x3000, 0x12be: 0x3000, 0x12bf: 0x3000,
 	// Block 0x4b, offset 0x12c0
-	0x12db: 0x3000, 0x12dc: 0x3000, 0x12dd: 0x3000,
+	0x12c0: 0x3000, 0x12c1: 0x3000, 0x12c2: 0x3000, 0x12c3: 0x3000, 0x12c4: 0x3000, 0x12c5: 0x3000,
+	0x12c6: 0x3000, 0x12c7: 0x3000, 0x12c8: 0x3000, 0x12c9: 0x3000, 0x12ca: 0x3000, 0x12cb: 0x3000,
+	0x12cc: 0x3000, 0x12cd: 0x3000, 0x12cf: 0x3000, 0x12d0: 0x3000, 0x12d1: 0x3000,
+	0x12d2: 0x3000, 0x12d3: 0x3000, 0x12d4: 0x3000, 0x12d5: 0x3000, 0x12d6: 0x3000, 0x12d7: 0x3000,
+	0x12d8: 0x3000, 0x12d9: 0x3000, 0x12da: 0x3000, 0x12db: 0x3000, 0x12dc: 0x3000, 0x12dd: 0x3000,
 	0x12de: 0x3000, 0x12df: 0x3000, 0x12e0: 0x3000, 0x12e1: 0x3000, 0x12e2: 0x3000, 0x12e3: 0x3000,
 	0x12e4: 0x3000, 0x12e5: 0x3000, 0x12e6: 0x3000, 0x12e7: 0x3000, 0x12e8: 0x3000, 0x12e9: 0x3000,
-	0x12ea: 0x3000, 0x12eb: 0x3000, 0x12ec: 0x3000, 0x12ed: 0x3000, 0x12ee: 0x3000, 0x12ef: 0x3000,
-	0x12f0: 0x3000, 0x12f1: 0x3000, 0x12f2: 0x3000, 0x12f3: 0x3000, 0x12f4: 0x3000, 0x12f5: 0x3000,
-	0x12f6: 0x3000, 0x12f7: 0x3000, 0x12f8: 0x3000, 0x12f9: 0x3000, 0x12fa: 0x3000, 0x12fb: 0x3000,
-	0x12fc: 0x3000, 0x12fd: 0x3000, 0x12fe: 0x3000, 0x12ff: 0x3000,
+	0x12ea: 0x3000,
+	0x12f8: 0x3000,
 	// Block 0x4c, offset 0x1300
-	0x1300: 0x00e6, 0x1301: 0x00e6, 0x1302: 0x00dc, 0x1303: 0x00e6, 0x1304: 0x00e6, 0x1305: 0x00e6,
-	0x1306: 0x00e6, 0x1307: 0x00e6, 0x1308: 0x00e6, 0x1309: 0x00e6, 0x130a: 0x00dc, 0x130b: 0x00e6,
-	0x130c: 0x00e6, 0x130d: 0x00ea, 0x130e: 0x00d6, 0x130f: 0x00dc, 0x1310: 0x00ca, 0x1311: 0x00e6,
-	0x1312: 0x00e6, 0x1313: 0x00e6, 0x1314: 0x00e6, 0x1315: 0x00e6, 0x1316: 0x00e6, 0x1317: 0x00e6,
-	0x1318: 0x00e6, 0x1319: 0x00e6, 0x131a: 0x00e6, 0x131b: 0x00e6, 0x131c: 0x00e6, 0x131d: 0x00e6,
-	0x131e: 0x00e6, 0x131f: 0x00e6, 0x1320: 0x00e6, 0x1321: 0x00e6, 0x1322: 0x00e6, 0x1323: 0x00e6,
-	0x1324: 0x00e6, 0x1325: 0x00e6, 0x1326: 0x00e6,
-	0x133c: 0x00e9, 0x133d: 0x00dc, 0x133e: 0x00e6, 0x133f: 0x00dc,
+	0x131b: 0x3000, 0x131c: 0x3000, 0x131d: 0x3000,
+	0x131e: 0x3000, 0x131f: 0x3000, 0x1320: 0x3000, 0x1321: 0x3000, 0x1322: 0x3000, 0x1323: 0x3000,
+	0x1324: 0x3000, 0x1325: 0x3000, 0x1326: 0x3000, 0x1327: 0x3000, 0x1328: 0x3000, 0x1329: 0x3000,
+	0x132a: 0x3000, 0x132b: 0x3000, 0x132c: 0x3000, 0x132d: 0x3000, 0x132e: 0x3000, 0x132f: 0x3000,
+	0x1330: 0x3000, 0x1331: 0x3000, 0x1332: 0x3000, 0x1333: 0x3000, 0x1334: 0x3000, 0x1335: 0x3000,
+	0x1336: 0x3000, 0x1337: 0x3000, 0x1338: 0x3000, 0x1339: 0x3000, 0x133a: 0x3000, 0x133b: 0x3000,
+	0x133c: 0x3000, 0x133d: 0x3000, 0x133e: 0x3000, 0x133f: 0x3000,
 	// Block 0x4d, offset 0x1340
-	0x1340: 0x1100, 0x1341: 0x1100, 0x1342: 0x1100, 0x1343: 0x1100, 0x1344: 0x1100, 0x1345: 0x1100,
-	0x1346: 0x1100, 0x1347: 0x1100, 0x1348: 0x1100, 0x1349: 0x1100, 0x134a: 0x1100, 0x134b: 0x1100,
-	0x134c: 0x1100, 0x134d: 0x1100, 0x134e: 0x1100, 0x134f: 0x1100, 0x1350: 0x1100, 0x1351: 0x1100,
-	0x1352: 0x1100, 0x1353: 0x1100, 0x1354: 0x1100, 0x1355: 0x1100, 0x1356: 0x1100, 0x1357: 0x1100,
-	0x1358: 0x1100, 0x1359: 0x1100, 0x135a: 0x1100, 0x135b: 0x1100, 0x135c: 0x1100, 0x135d: 0x1100,
-	0x135e: 0x1100, 0x135f: 0x1100, 0x1360: 0x1100, 0x1361: 0x1100, 0x1362: 0x1100, 0x1363: 0x1100,
-	0x1364: 0x1100, 0x1365: 0x1100, 0x1366: 0x1100, 0x1367: 0x1100, 0x1368: 0x1100, 0x1369: 0x1100,
-	0x136a: 0x1100, 0x136b: 0x1100, 0x136c: 0x1100, 0x136d: 0x1100, 0x136e: 0x1100, 0x136f: 0x1100,
-	0x1370: 0x1100, 0x1371: 0x1100, 0x1372: 0x1100, 0x1373: 0x1100, 0x1374: 0x1100, 0x1375: 0x1100,
-	0x1376: 0x9900, 0x1377: 0x9900, 0x1378: 0x1100, 0x1379: 0x1100, 0x137a: 0x1100, 0x137b: 0x1100,
-	0x137c: 0x1100, 0x137d: 0x1100, 0x137e: 0x1100, 0x137f: 0x1100,
+	0x1340: 0x00e6, 0x1341: 0x00e6, 0x1342: 0x00dc, 0x1343: 0x00e6, 0x1344: 0x00e6, 0x1345: 0x00e6,
+	0x1346: 0x00e6, 0x1347: 0x00e6, 0x1348: 0x00e6, 0x1349: 0x00e6, 0x134a: 0x00dc, 0x134b: 0x00e6,
+	0x134c: 0x00e6, 0x134d: 0x00ea, 0x134e: 0x00d6, 0x134f: 0x00dc, 0x1350: 0x00ca, 0x1351: 0x00e6,
+	0x1352: 0x00e6, 0x1353: 0x00e6, 0x1354: 0x00e6, 0x1355: 0x00e6, 0x1356: 0x00e6, 0x1357: 0x00e6,
+	0x1358: 0x00e6, 0x1359: 0x00e6, 0x135a: 0x00e6, 0x135b: 0x00e6, 0x135c: 0x00e6, 0x135d: 0x00e6,
+	0x135e: 0x00e6, 0x135f: 0x00e6, 0x1360: 0x00e6, 0x1361: 0x00e6, 0x1362: 0x00e6, 0x1363: 0x00e6,
+	0x1364: 0x00e6, 0x1365: 0x00e6, 0x1366: 0x00e6,
+	0x137c: 0x00e9, 0x137d: 0x00dc, 0x137e: 0x00e6, 0x137f: 0x00dc,
 	// Block 0x4e, offset 0x1380
 	0x1380: 0x1100, 0x1381: 0x1100, 0x1382: 0x1100, 0x1383: 0x1100, 0x1384: 0x1100, 0x1385: 0x1100,
 	0x1386: 0x1100, 0x1387: 0x1100, 0x1388: 0x1100, 0x1389: 0x1100, 0x138a: 0x1100, 0x138b: 0x1100,
 	0x138c: 0x1100, 0x138d: 0x1100, 0x138e: 0x1100, 0x138f: 0x1100, 0x1390: 0x1100, 0x1391: 0x1100,
 	0x1392: 0x1100, 0x1393: 0x1100, 0x1394: 0x1100, 0x1395: 0x1100, 0x1396: 0x1100, 0x1397: 0x1100,
-	0x1398: 0x1100, 0x1399: 0x1100, 0x139a: 0x9900, 0x139b: 0x9900, 0x139c: 0x1100, 0x139d: 0x1100,
-	0x139e: 0x1100, 0x139f: 0x1100, 0x13a0: 0x1100, 0x13a1: 0x1100, 0x13a2: 0x9900, 0x13a3: 0x9900,
+	0x1398: 0x1100, 0x1399: 0x1100, 0x139a: 0x1100, 0x139b: 0x1100, 0x139c: 0x1100, 0x139d: 0x1100,
+	0x139e: 0x1100, 0x139f: 0x1100, 0x13a0: 0x1100, 0x13a1: 0x1100, 0x13a2: 0x1100, 0x13a3: 0x1100,
 	0x13a4: 0x1100, 0x13a5: 0x1100, 0x13a6: 0x1100, 0x13a7: 0x1100, 0x13a8: 0x1100, 0x13a9: 0x1100,
 	0x13aa: 0x1100, 0x13ab: 0x1100, 0x13ac: 0x1100, 0x13ad: 0x1100, 0x13ae: 0x1100, 0x13af: 0x1100,
 	0x13b0: 0x1100, 0x13b1: 0x1100, 0x13b2: 0x1100, 0x13b3: 0x1100, 0x13b4: 0x1100, 0x13b5: 0x1100,
-	0x13b6: 0x1100, 0x13b7: 0x1100, 0x13b8: 0x1100, 0x13b9: 0x1100, 0x13ba: 0x1100, 0x13bb: 0x1100,
+	0x13b6: 0x9900, 0x13b7: 0x9900, 0x13b8: 0x1100, 0x13b9: 0x1100, 0x13ba: 0x1100, 0x13bb: 0x1100,
 	0x13bc: 0x1100, 0x13bd: 0x1100, 0x13be: 0x1100, 0x13bf: 0x1100,
 	// Block 0x4f, offset 0x13c0
 	0x13c0: 0x1100, 0x13c1: 0x1100, 0x13c2: 0x1100, 0x13c3: 0x1100, 0x13c4: 0x1100, 0x13c5: 0x1100,
 	0x13c6: 0x1100, 0x13c7: 0x1100, 0x13c8: 0x1100, 0x13c9: 0x1100, 0x13ca: 0x1100, 0x13cb: 0x1100,
 	0x13cc: 0x1100, 0x13cd: 0x1100, 0x13ce: 0x1100, 0x13cf: 0x1100, 0x13d0: 0x1100, 0x13d1: 0x1100,
 	0x13d2: 0x1100, 0x13d3: 0x1100, 0x13d4: 0x1100, 0x13d5: 0x1100, 0x13d6: 0x1100, 0x13d7: 0x1100,
-	0x13d8: 0x1100, 0x13d9: 0x1100, 0x13da: 0x3000, 0x13db: 0x3100,
-	0x13e0: 0x9900, 0x13e1: 0x9900, 0x13e2: 0x1100, 0x13e3: 0x1100,
+	0x13d8: 0x1100, 0x13d9: 0x1100, 0x13da: 0x9900, 0x13db: 0x9900, 0x13dc: 0x1100, 0x13dd: 0x1100,
+	0x13de: 0x1100, 0x13df: 0x1100, 0x13e0: 0x1100, 0x13e1: 0x1100, 0x13e2: 0x9900, 0x13e3: 0x9900,
 	0x13e4: 0x1100, 0x13e5: 0x1100, 0x13e6: 0x1100, 0x13e7: 0x1100, 0x13e8: 0x1100, 0x13e9: 0x1100,
 	0x13ea: 0x1100, 0x13eb: 0x1100, 0x13ec: 0x1100, 0x13ed: 0x1100, 0x13ee: 0x1100, 0x13ef: 0x1100,
 	0x13f0: 0x1100, 0x13f1: 0x1100, 0x13f2: 0x1100, 0x13f3: 0x1100, 0x13f4: 0x1100, 0x13f5: 0x1100,
-	0x13f6: 0x1100, 0x13f7: 0x1100, 0x13f8: 0x9900, 0x13f9: 0x9900, 0x13fa: 0x1100, 0x13fb: 0x1100,
+	0x13f6: 0x1100, 0x13f7: 0x1100, 0x13f8: 0x1100, 0x13f9: 0x1100, 0x13fa: 0x1100, 0x13fb: 0x1100,
 	0x13fc: 0x1100, 0x13fd: 0x1100, 0x13fe: 0x1100, 0x13ff: 0x1100,
 	// Block 0x50, offset 0x1400
 	0x1400: 0x1100, 0x1401: 0x1100, 0x1402: 0x1100, 0x1403: 0x1100, 0x1404: 0x1100, 0x1405: 0x1100,
 	0x1406: 0x1100, 0x1407: 0x1100, 0x1408: 0x1100, 0x1409: 0x1100, 0x140a: 0x1100, 0x140b: 0x1100,
-	0x140c: 0x9900, 0x140d: 0x9900, 0x140e: 0x1100, 0x140f: 0x1100, 0x1410: 0x1100, 0x1411: 0x1100,
+	0x140c: 0x1100, 0x140d: 0x1100, 0x140e: 0x1100, 0x140f: 0x1100, 0x1410: 0x1100, 0x1411: 0x1100,
 	0x1412: 0x1100, 0x1413: 0x1100, 0x1414: 0x1100, 0x1415: 0x1100, 0x1416: 0x1100, 0x1417: 0x1100,
-	0x1418: 0x1100, 0x1419: 0x1100, 0x141a: 0x1100, 0x141b: 0x1100, 0x141c: 0x1100, 0x141d: 0x1100,
-	0x141e: 0x1100, 0x141f: 0x1100, 0x1420: 0x1100, 0x1421: 0x1100, 0x1422: 0x1100, 0x1423: 0x1100,
+	0x1418: 0x1100, 0x1419: 0x1100, 0x141a: 0x3000, 0x141b: 0x3100,
+	0x1420: 0x9900, 0x1421: 0x9900, 0x1422: 0x1100, 0x1423: 0x1100,
 	0x1424: 0x1100, 0x1425: 0x1100, 0x1426: 0x1100, 0x1427: 0x1100, 0x1428: 0x1100, 0x1429: 0x1100,
 	0x142a: 0x1100, 0x142b: 0x1100, 0x142c: 0x1100, 0x142d: 0x1100, 0x142e: 0x1100, 0x142f: 0x1100,
 	0x1430: 0x1100, 0x1431: 0x1100, 0x1432: 0x1100, 0x1433: 0x1100, 0x1434: 0x1100, 0x1435: 0x1100,
-	0x1436: 0x1100, 0x1437: 0x1100, 0x1438: 0x1100, 0x1439: 0x1100,
+	0x1436: 0x1100, 0x1437: 0x1100, 0x1438: 0x9900, 0x1439: 0x9900, 0x143a: 0x1100, 0x143b: 0x1100,
+	0x143c: 0x1100, 0x143d: 0x1100, 0x143e: 0x1100, 0x143f: 0x1100,
 	// Block 0x51, offset 0x1440
-	0x1440: 0x9900, 0x1441: 0x9900, 0x1442: 0x9900, 0x1443: 0x9900, 0x1444: 0x9900, 0x1445: 0x9900,
-	0x1446: 0x9900, 0x1447: 0x9900, 0x1448: 0x9900, 0x1449: 0x9900, 0x144a: 0x9900, 0x144b: 0x9900,
-	0x144c: 0x9900, 0x144d: 0x9900, 0x144e: 0x9900, 0x144f: 0x9900, 0x1450: 0x9900, 0x1451: 0x9900,
-	0x1452: 0x1100, 0x1453: 0x1100, 0x1454: 0x1100, 0x1455: 0x1100,
-	0x1458: 0x9900, 0x1459: 0x9900, 0x145a: 0x1100, 0x145b: 0x1100, 0x145c: 0x1100, 0x145d: 0x1100,
-	0x1460: 0x9900, 0x1461: 0x9900, 0x1462: 0x9900, 0x1463: 0x9900,
-	0x1464: 0x9900, 0x1465: 0x9900, 0x1466: 0x9900, 0x1467: 0x9900, 0x1468: 0x9900, 0x1469: 0x9900,
-	0x146a: 0x9900, 0x146b: 0x9900, 0x146c: 0x9900, 0x146d: 0x9900, 0x146e: 0x9900, 0x146f: 0x9900,
-	0x1470: 0x9900, 0x1471: 0x9900, 0x1472: 0x1100, 0x1473: 0x1100, 0x1474: 0x1100, 0x1475: 0x1100,
-	0x1476: 0x1100, 0x1477: 0x1100, 0x1478: 0x9900, 0x1479: 0x9900, 0x147a: 0x1100, 0x147b: 0x1100,
-	0x147c: 0x1100, 0x147d: 0x1100, 0x147e: 0x1100, 0x147f: 0x1100,
+	0x1440: 0x1100, 0x1441: 0x1100, 0x1442: 0x1100, 0x1443: 0x1100, 0x1444: 0x1100, 0x1445: 0x1100,
+	0x1446: 0x1100, 0x1447: 0x1100, 0x1448: 0x1100, 0x1449: 0x1100, 0x144a: 0x1100, 0x144b: 0x1100,
+	0x144c: 0x9900, 0x144d: 0x9900, 0x144e: 0x1100, 0x144f: 0x1100, 0x1450: 0x1100, 0x1451: 0x1100,
+	0x1452: 0x1100, 0x1453: 0x1100, 0x1454: 0x1100, 0x1455: 0x1100, 0x1456: 0x1100, 0x1457: 0x1100,
+	0x1458: 0x1100, 0x1459: 0x1100, 0x145a: 0x1100, 0x145b: 0x1100, 0x145c: 0x1100, 0x145d: 0x1100,
+	0x145e: 0x1100, 0x145f: 0x1100, 0x1460: 0x1100, 0x1461: 0x1100, 0x1462: 0x1100, 0x1463: 0x1100,
+	0x1464: 0x1100, 0x1465: 0x1100, 0x1466: 0x1100, 0x1467: 0x1100, 0x1468: 0x1100, 0x1469: 0x1100,
+	0x146a: 0x1100, 0x146b: 0x1100, 0x146c: 0x1100, 0x146d: 0x1100, 0x146e: 0x1100, 0x146f: 0x1100,
+	0x1470: 0x1100, 0x1471: 0x1100, 0x1472: 0x1100, 0x1473: 0x1100, 0x1474: 0x1100, 0x1475: 0x1100,
+	0x1476: 0x1100, 0x1477: 0x1100, 0x1478: 0x1100, 0x1479: 0x1100,
 	// Block 0x52, offset 0x1480
-	0x1480: 0x9900, 0x1481: 0x9900, 0x1482: 0x1100, 0x1483: 0x1100, 0x1484: 0x1100, 0x1485: 0x1100,
-	0x1488: 0x9900, 0x1489: 0x9900, 0x148a: 0x1100, 0x148b: 0x1100,
-	0x148c: 0x1100, 0x148d: 0x1100, 0x1490: 0x9900, 0x1491: 0x9900,
-	0x1492: 0x1100, 0x1493: 0x1100, 0x1494: 0x1100, 0x1495: 0x1100, 0x1496: 0x1100, 0x1497: 0x1100,
-	0x1499: 0x9900, 0x149b: 0x1100, 0x149d: 0x1100,
-	0x149f: 0x1100, 0x14a0: 0x9900, 0x14a1: 0x9900, 0x14a2: 0x9900, 0x14a3: 0x9900,
+	0x1480: 0x9900, 0x1481: 0x9900, 0x1482: 0x9900, 0x1483: 0x9900, 0x1484: 0x9900, 0x1485: 0x9900,
+	0x1486: 0x9900, 0x1487: 0x9900, 0x1488: 0x9900, 0x1489: 0x9900, 0x148a: 0x9900, 0x148b: 0x9900,
+	0x148c: 0x9900, 0x148d: 0x9900, 0x148e: 0x9900, 0x148f: 0x9900, 0x1490: 0x9900, 0x1491: 0x9900,
+	0x1492: 0x1100, 0x1493: 0x1100, 0x1494: 0x1100, 0x1495: 0x1100,
+	0x1498: 0x9900, 0x1499: 0x9900, 0x149a: 0x1100, 0x149b: 0x1100, 0x149c: 0x1100, 0x149d: 0x1100,
+	0x14a0: 0x9900, 0x14a1: 0x9900, 0x14a2: 0x9900, 0x14a3: 0x9900,
 	0x14a4: 0x9900, 0x14a5: 0x9900, 0x14a6: 0x9900, 0x14a7: 0x9900, 0x14a8: 0x9900, 0x14a9: 0x9900,
 	0x14aa: 0x9900, 0x14ab: 0x9900, 0x14ac: 0x9900, 0x14ad: 0x9900, 0x14ae: 0x9900, 0x14af: 0x9900,
-	0x14b0: 0x9900, 0x14b1: 0x3300, 0x14b2: 0x1100, 0x14b3: 0x3300, 0x14b4: 0x9900, 0x14b5: 0x3300,
-	0x14b6: 0x1100, 0x14b7: 0x3300, 0x14b8: 0x1100, 0x14b9: 0x3300, 0x14ba: 0x1100, 0x14bb: 0x3300,
-	0x14bc: 0x9900, 0x14bd: 0x3300,
+	0x14b0: 0x9900, 0x14b1: 0x9900, 0x14b2: 0x1100, 0x14b3: 0x1100, 0x14b4: 0x1100, 0x14b5: 0x1100,
+	0x14b6: 0x1100, 0x14b7: 0x1100, 0x14b8: 0x9900, 0x14b9: 0x9900, 0x14ba: 0x1100, 0x14bb: 0x1100,
+	0x14bc: 0x1100, 0x14bd: 0x1100, 0x14be: 0x1100, 0x14bf: 0x1100,
 	// Block 0x53, offset 0x14c0
-	0x14c0: 0x1100, 0x14c1: 0x1100, 0x14c2: 0x1100, 0x14c3: 0x1100, 0x14c4: 0x1100, 0x14c5: 0x1100,
-	0x14c6: 0x1100, 0x14c7: 0x1100, 0x14c8: 0x1100, 0x14c9: 0x1100, 0x14ca: 0x1100, 0x14cb: 0x1100,
-	0x14cc: 0x1100, 0x14cd: 0x1100, 0x14ce: 0x1100, 0x14cf: 0x1100, 0x14d0: 0x1100, 0x14d1: 0x1100,
+	0x14c0: 0x9900, 0x14c1: 0x9900, 0x14c2: 0x1100, 0x14c3: 0x1100, 0x14c4: 0x1100, 0x14c5: 0x1100,
+	0x14c8: 0x9900, 0x14c9: 0x9900, 0x14ca: 0x1100, 0x14cb: 0x1100,
+	0x14cc: 0x1100, 0x14cd: 0x1100, 0x14d0: 0x9900, 0x14d1: 0x9900,
 	0x14d2: 0x1100, 0x14d3: 0x1100, 0x14d4: 0x1100, 0x14d5: 0x1100, 0x14d6: 0x1100, 0x14d7: 0x1100,
-	0x14d8: 0x1100, 0x14d9: 0x1100, 0x14da: 0x1100, 0x14db: 0x1100, 0x14dc: 0x1100, 0x14dd: 0x1100,
-	0x14de: 0x1100, 0x14df: 0x1100, 0x14e0: 0x1100, 0x14e1: 0x1100, 0x14e2: 0x1100, 0x14e3: 0x1100,
-	0x14e4: 0x1100, 0x14e5: 0x1100, 0x14e6: 0x1100, 0x14e7: 0x1100, 0x14e8: 0x1100, 0x14e9: 0x1100,
-	0x14ea: 0x1100, 0x14eb: 0x1100, 0x14ec: 0x1100, 0x14ed: 0x1100, 0x14ee: 0x1100, 0x14ef: 0x1100,
-	0x14f0: 0x1100, 0x14f1: 0x1100, 0x14f2: 0x1100, 0x14f3: 0x1100, 0x14f4: 0x1100,
-	0x14f6: 0x9900, 0x14f7: 0x1100, 0x14f8: 0x1100, 0x14f9: 0x1100, 0x14fa: 0x1100, 0x14fb: 0x3300,
-	0x14fc: 0x1100, 0x14fd: 0x3000, 0x14fe: 0x3300, 0x14ff: 0x3800,
+	0x14d9: 0x9900, 0x14db: 0x1100, 0x14dd: 0x1100,
+	0x14df: 0x1100, 0x14e0: 0x9900, 0x14e1: 0x9900, 0x14e2: 0x9900, 0x14e3: 0x9900,
+	0x14e4: 0x9900, 0x14e5: 0x9900, 0x14e6: 0x9900, 0x14e7: 0x9900, 0x14e8: 0x9900, 0x14e9: 0x9900,
+	0x14ea: 0x9900, 0x14eb: 0x9900, 0x14ec: 0x9900, 0x14ed: 0x9900, 0x14ee: 0x9900, 0x14ef: 0x9900,
+	0x14f0: 0x9900, 0x14f1: 0x3300, 0x14f2: 0x1100, 0x14f3: 0x3300, 0x14f4: 0x9900, 0x14f5: 0x3300,
+	0x14f6: 0x1100, 0x14f7: 0x3300, 0x14f8: 0x1100, 0x14f9: 0x3300, 0x14fa: 0x1100, 0x14fb: 0x3300,
+	0x14fc: 0x9900, 0x14fd: 0x3300,
 	// Block 0x54, offset 0x1500
-	0x1500: 0x3000, 0x1501: 0x3100, 0x1502: 0x1100, 0x1503: 0x1100, 0x1504: 0x1100,
-	0x1506: 0x9900, 0x1507: 0x1100, 0x1508: 0x1100, 0x1509: 0x3300, 0x150a: 0x1100, 0x150b: 0x3300,
-	0x150c: 0x1100, 0x150d: 0x3100, 0x150e: 0x3100, 0x150f: 0x3100, 0x1510: 0x1100, 0x1511: 0x1100,
-	0x1512: 0x1100, 0x1513: 0x3300, 0x1516: 0x1100, 0x1517: 0x1100,
-	0x1518: 0x1100, 0x1519: 0x1100, 0x151a: 0x1100, 0x151b: 0x3300, 0x151d: 0x3100,
-	0x151e: 0x3100, 0x151f: 0x3100, 0x1520: 0x1100, 0x1521: 0x1100, 0x1522: 0x1100, 0x1523: 0x3300,
+	0x1500: 0x1100, 0x1501: 0x1100, 0x1502: 0x1100, 0x1503: 0x1100, 0x1504: 0x1100, 0x1505: 0x1100,
+	0x1506: 0x1100, 0x1507: 0x1100, 0x1508: 0x1100, 0x1509: 0x1100, 0x150a: 0x1100, 0x150b: 0x1100,
+	0x150c: 0x1100, 0x150d: 0x1100, 0x150e: 0x1100, 0x150f: 0x1100, 0x1510: 0x1100, 0x1511: 0x1100,
+	0x1512: 0x1100, 0x1513: 0x1100, 0x1514: 0x1100, 0x1515: 0x1100, 0x1516: 0x1100, 0x1517: 0x1100,
+	0x1518: 0x1100, 0x1519: 0x1100, 0x151a: 0x1100, 0x151b: 0x1100, 0x151c: 0x1100, 0x151d: 0x1100,
+	0x151e: 0x1100, 0x151f: 0x1100, 0x1520: 0x1100, 0x1521: 0x1100, 0x1522: 0x1100, 0x1523: 0x1100,
 	0x1524: 0x1100, 0x1525: 0x1100, 0x1526: 0x1100, 0x1527: 0x1100, 0x1528: 0x1100, 0x1529: 0x1100,
-	0x152a: 0x1100, 0x152b: 0x3300, 0x152c: 0x1100, 0x152d: 0x3100, 0x152e: 0x3300, 0x152f: 0x3300,
-	0x1532: 0x1100, 0x1533: 0x1100, 0x1534: 0x1100,
-	0x1536: 0x9900, 0x1537: 0x1100, 0x1538: 0x1100, 0x1539: 0x3300, 0x153a: 0x1100, 0x153b: 0x3300,
-	0x153c: 0x1100, 0x153d: 0x3300, 0x153e: 0x3800,
+	0x152a: 0x1100, 0x152b: 0x1100, 0x152c: 0x1100, 0x152d: 0x1100, 0x152e: 0x1100, 0x152f: 0x1100,
+	0x1530: 0x1100, 0x1531: 0x1100, 0x1532: 0x1100, 0x1533: 0x1100, 0x1534: 0x1100,
+	0x1536: 0x9900, 0x1537: 0x1100, 0x1538: 0x1100, 0x1539: 0x1100, 0x153a: 0x1100, 0x153b: 0x3300,
+	0x153c: 0x1100, 0x153d: 0x3000, 0x153e: 0x3300, 0x153f: 0x3800,
 	// Block 0x55, offset 0x1540
-	0x1540: 0x3300, 0x1541: 0x3300, 0x1542: 0x3000, 0x1543: 0x3000, 0x1544: 0x3000, 0x1545: 0x3000,
-	0x1546: 0x3000, 0x1547: 0x3000, 0x1548: 0x3000, 0x1549: 0x3000, 0x154a: 0x3000,
-	0x1551: 0x3000,
-	0x1557: 0x3000,
-	0x1564: 0x3000, 0x1565: 0x3000, 0x1566: 0x3000,
-	0x156f: 0x3000,
-	0x1573: 0x3000, 0x1574: 0x3000,
-	0x1576: 0x3000, 0x1577: 0x3000,
-	0x157c: 0x3000, 0x157e: 0x3000,
+	0x1540: 0x3000, 0x1541: 0x3100, 0x1542: 0x1100, 0x1543: 0x1100, 0x1544: 0x1100,
+	0x1546: 0x9900, 0x1547: 0x1100, 0x1548: 0x1100, 0x1549: 0x3300, 0x154a: 0x1100, 0x154b: 0x3300,
+	0x154c: 0x1100, 0x154d: 0x3100, 0x154e: 0x3100, 0x154f: 0x3100, 0x1550: 0x1100, 0x1551: 0x1100,
+	0x1552: 0x1100, 0x1553: 0x3300, 0x1556: 0x1100, 0x1557: 0x1100,
+	0x1558: 0x1100, 0x1559: 0x1100, 0x155a: 0x1100, 0x155b: 0x3300, 0x155d: 0x3100,
+	0x155e: 0x3100, 0x155f: 0x3100, 0x1560: 0x1100, 0x1561: 0x1100, 0x1562: 0x1100, 0x1563: 0x3300,
+	0x1564: 0x1100, 0x1565: 0x1100, 0x1566: 0x1100, 0x1567: 0x1100, 0x1568: 0x1100, 0x1569: 0x1100,
+	0x156a: 0x1100, 0x156b: 0x3300, 0x156c: 0x1100, 0x156d: 0x3100, 0x156e: 0x3300, 0x156f: 0x3300,
+	0x1572: 0x1100, 0x1573: 0x1100, 0x1574: 0x1100,
+	0x1576: 0x9900, 0x1577: 0x1100, 0x1578: 0x1100, 0x1579: 0x3300, 0x157a: 0x1100, 0x157b: 0x3300,
+	0x157c: 0x1100, 0x157d: 0x3300, 0x157e: 0x3800,
 	// Block 0x56, offset 0x1580
-	0x1587: 0x3000, 0x1588: 0x3000, 0x1589: 0x3000,
+	0x1580: 0x3300, 0x1581: 0x3300, 0x1582: 0x3000, 0x1583: 0x3000, 0x1584: 0x3000, 0x1585: 0x3000,
+	0x1586: 0x3000, 0x1587: 0x3000, 0x1588: 0x3000, 0x1589: 0x3000, 0x158a: 0x3000,
+	0x1591: 0x3000,
 	0x1597: 0x3000,
-	0x159f: 0x3000,
-	0x15b0: 0x3000, 0x15b1: 0x3000, 0x15b4: 0x3000, 0x15b5: 0x3000,
-	0x15b6: 0x3000, 0x15b7: 0x3000, 0x15b8: 0x3000, 0x15b9: 0x3000, 0x15ba: 0x3000, 0x15bb: 0x3000,
-	0x15bc: 0x3000, 0x15bd: 0x3000, 0x15be: 0x3000, 0x15bf: 0x3000,
+	0x15a4: 0x3000, 0x15a5: 0x3000, 0x15a6: 0x3000,
+	0x15af: 0x3000,
+	0x15b3: 0x3000, 0x15b4: 0x3000,
+	0x15b6: 0x3000, 0x15b7: 0x3000,
+	0x15bc: 0x3000, 0x15be: 0x3000,
 	// Block 0x57, offset 0x15c0
-	0x15c0: 0x3000, 0x15c1: 0x3000, 0x15c2: 0x3000, 0x15c3: 0x3000, 0x15c4: 0x3000, 0x15c5: 0x3000,
-	0x15c6: 0x3000, 0x15c7: 0x3000, 0x15c8: 0x3000, 0x15c9: 0x3000, 0x15ca: 0x3000, 0x15cb: 0x3000,
-	0x15cc: 0x3000, 0x15cd: 0x3000, 0x15ce: 0x3000, 0x15d0: 0x3000, 0x15d1: 0x3000,
-	0x15d2: 0x3000, 0x15d3: 0x3000, 0x15d4: 0x3000, 0x15d5: 0x3000, 0x15d6: 0x3000, 0x15d7: 0x3000,
-	0x15d8: 0x3000, 0x15d9: 0x3000, 0x15da: 0x3000, 0x15db: 0x3000, 0x15dc: 0x3000,
-	0x15e8: 0x3000,
+	0x15c7: 0x3000, 0x15c8: 0x3000, 0x15c9: 0x3000,
+	0x15d7: 0x3000,
+	0x15df: 0x3000,
+	0x15f0: 0x3000, 0x15f1: 0x3000, 0x15f4: 0x3000, 0x15f5: 0x3000,
+	0x15f6: 0x3000, 0x15f7: 0x3000, 0x15f8: 0x3000, 0x15f9: 0x3000, 0x15fa: 0x3000, 0x15fb: 0x3000,
+	0x15fc: 0x3000, 0x15fd: 0x3000, 0x15fe: 0x3000, 0x15ff: 0x3000,
 	// Block 0x58, offset 0x1600
-	0x1610: 0x00e6, 0x1611: 0x00e6,
-	0x1612: 0x0001, 0x1613: 0x0001, 0x1614: 0x00e6, 0x1615: 0x00e6, 0x1616: 0x00e6, 0x1617: 0x00e6,
-	0x1618: 0x0001, 0x1619: 0x0001, 0x161a: 0x0001, 0x161b: 0x00e6, 0x161c: 0x00e6,
-	0x1621: 0x00e6,
-	0x1625: 0x0001, 0x1626: 0x0001, 0x1627: 0x00e6, 0x1628: 0x00dc, 0x1629: 0x00e6,
-	0x162a: 0x0001, 0x162b: 0x0001, 0x162c: 0x00dc, 0x162d: 0x00dc, 0x162e: 0x00dc, 0x162f: 0x00dc,
-	0x1630: 0x00e6,
+	0x1600: 0x3000, 0x1601: 0x3000, 0x1602: 0x3000, 0x1603: 0x3000, 0x1604: 0x3000, 0x1605: 0x3000,
+	0x1606: 0x3000, 0x1607: 0x3000, 0x1608: 0x3000, 0x1609: 0x3000, 0x160a: 0x3000, 0x160b: 0x3000,
+	0x160c: 0x3000, 0x160d: 0x3000, 0x160e: 0x3000, 0x1610: 0x3000, 0x1611: 0x3000,
+	0x1612: 0x3000, 0x1613: 0x3000, 0x1614: 0x3000, 0x1615: 0x3000, 0x1616: 0x3000, 0x1617: 0x3000,
+	0x1618: 0x3000, 0x1619: 0x3000, 0x161a: 0x3000, 0x161b: 0x3000, 0x161c: 0x3000,
+	0x1628: 0x3000,
 	// Block 0x59, offset 0x1640
-	0x1640: 0x3000, 0x1641: 0x3000, 0x1642: 0x3000, 0x1643: 0x3000, 0x1645: 0x3000,
-	0x1646: 0x3000, 0x1647: 0x3000, 0x1649: 0x3000, 0x164a: 0x3000, 0x164b: 0x3000,
-	0x164c: 0x3000, 0x164d: 0x3000, 0x164e: 0x3000, 0x164f: 0x3000, 0x1650: 0x3000, 0x1651: 0x3000,
-	0x1652: 0x3000, 0x1653: 0x3000, 0x1655: 0x3000, 0x1656: 0x3000,
-	0x1659: 0x3000, 0x165a: 0x3000, 0x165b: 0x3000, 0x165c: 0x3000, 0x165d: 0x3000,
-	0x1660: 0x3000, 0x1661: 0x3000, 0x1662: 0x3000,
-	0x1664: 0x3000, 0x1666: 0x3300, 0x1668: 0x3000,
-	0x166a: 0x3300, 0x166b: 0x3300, 0x166c: 0x3000, 0x166d: 0x3000, 0x166f: 0x3000,
-	0x1670: 0x3000, 0x1671: 0x3000, 0x1673: 0x3000, 0x1674: 0x3000, 0x1675: 0x3000,
-	0x1676: 0x3000, 0x1677: 0x3000, 0x1678: 0x3000, 0x1679: 0x3000, 0x167b: 0x3000,
-	0x167c: 0x3000, 0x167d: 0x3000, 0x167e: 0x3000, 0x167f: 0x3000,
+	0x1650: 0x00e6, 0x1651: 0x00e6,
+	0x1652: 0x0001, 0x1653: 0x0001, 0x1654: 0x00e6, 0x1655: 0x00e6, 0x1656: 0x00e6, 0x1657: 0x00e6,
+	0x1658: 0x0001, 0x1659: 0x0001, 0x165a: 0x0001, 0x165b: 0x00e6, 0x165c: 0x00e6,
+	0x1661: 0x00e6,
+	0x1665: 0x0001, 0x1666: 0x0001, 0x1667: 0x00e6, 0x1668: 0x00dc, 0x1669: 0x00e6,
+	0x166a: 0x0001, 0x166b: 0x0001, 0x166c: 0x00dc, 0x166d: 0x00dc, 0x166e: 0x00dc, 0x166f: 0x00dc,
+	0x1670: 0x00e6,
 	// Block 0x5a, offset 0x1680
-	0x1680: 0x3000, 0x1685: 0x3000,
-	0x1686: 0x3000, 0x1687: 0x3000, 0x1688: 0x3000, 0x1689: 0x3000,
-	0x1690: 0x3000, 0x1691: 0x3000,
-	0x1692: 0x3000, 0x1693: 0x3000, 0x1694: 0x3000, 0x1695: 0x3000, 0x1696: 0x3000, 0x1697: 0x3000,
-	0x1698: 0x3000, 0x1699: 0x3000, 0x169a: 0x3000, 0x169b: 0x3000, 0x169c: 0x3000, 0x169d: 0x3000,
-	0x169e: 0x3000, 0x169f: 0x3000, 0x16a0: 0x3000, 0x16a1: 0x3000, 0x16a2: 0x3000, 0x16a3: 0x3000,
-	0x16a4: 0x3000, 0x16a5: 0x3000, 0x16a6: 0x3000, 0x16a7: 0x3000, 0x16a8: 0x3000, 0x16a9: 0x3000,
-	0x16aa: 0x3000, 0x16ab: 0x3000, 0x16ac: 0x3000, 0x16ad: 0x3000, 0x16ae: 0x3000, 0x16af: 0x3000,
-	0x16b0: 0x3000, 0x16b1: 0x3000, 0x16b2: 0x3000, 0x16b3: 0x3000, 0x16b4: 0x3000, 0x16b5: 0x3000,
-	0x16b6: 0x3000, 0x16b7: 0x3000, 0x16b8: 0x3000, 0x16b9: 0x3000, 0x16ba: 0x3000, 0x16bb: 0x3000,
+	0x1680: 0x3000, 0x1681: 0x3000, 0x1682: 0x3000, 0x1683: 0x3000, 0x1685: 0x3000,
+	0x1686: 0x3000, 0x1687: 0x3000, 0x1689: 0x3000, 0x168a: 0x3000, 0x168b: 0x3000,
+	0x168c: 0x3000, 0x168d: 0x3000, 0x168e: 0x3000, 0x168f: 0x3000, 0x1690: 0x3000, 0x1691: 0x3000,
+	0x1692: 0x3000, 0x1693: 0x3000, 0x1695: 0x3000, 0x1696: 0x3000,
+	0x1699: 0x3000, 0x169a: 0x3000, 0x169b: 0x3000, 0x169c: 0x3000, 0x169d: 0x3000,
+	0x16a0: 0x3000, 0x16a1: 0x3000, 0x16a2: 0x3000,
+	0x16a4: 0x3000, 0x16a6: 0x3300, 0x16a8: 0x3000,
+	0x16aa: 0x3300, 0x16ab: 0x3300, 0x16ac: 0x3000, 0x16ad: 0x3000, 0x16af: 0x3000,
+	0x16b0: 0x3000, 0x16b1: 0x3000, 0x16b3: 0x3000, 0x16b4: 0x3000, 0x16b5: 0x3000,
+	0x16b6: 0x3000, 0x16b7: 0x3000, 0x16b8: 0x3000, 0x16b9: 0x3000, 0x16bb: 0x3000,
 	0x16bc: 0x3000, 0x16bd: 0x3000, 0x16be: 0x3000, 0x16bf: 0x3000,
 	// Block 0x5b, offset 0x16c0
-	0x16c9: 0x3000,
-	0x16d0: 0x8800,
-	0x16d2: 0x8800, 0x16d4: 0x8800,
-	0x16da: 0x1100, 0x16db: 0x1100,
-	0x16ee: 0x1100,
+	0x16c0: 0x3000, 0x16c5: 0x3000,
+	0x16c6: 0x3000, 0x16c7: 0x3000, 0x16c8: 0x3000, 0x16c9: 0x3000,
+	0x16d0: 0x3000, 0x16d1: 0x3000,
+	0x16d2: 0x3000, 0x16d3: 0x3000, 0x16d4: 0x3000, 0x16d5: 0x3000, 0x16d6: 0x3000, 0x16d7: 0x3000,
+	0x16d8: 0x3000, 0x16d9: 0x3000, 0x16da: 0x3000, 0x16db: 0x3000, 0x16dc: 0x3000, 0x16dd: 0x3000,
+	0x16de: 0x3000, 0x16df: 0x3000, 0x16e0: 0x3000, 0x16e1: 0x3000, 0x16e2: 0x3000, 0x16e3: 0x3000,
+	0x16e4: 0x3000, 0x16e5: 0x3000, 0x16e6: 0x3000, 0x16e7: 0x3000, 0x16e8: 0x3000, 0x16e9: 0x3000,
+	0x16ea: 0x3000, 0x16eb: 0x3000, 0x16ec: 0x3000, 0x16ed: 0x3000, 0x16ee: 0x3000, 0x16ef: 0x3000,
+	0x16f0: 0x3000, 0x16f1: 0x3000, 0x16f2: 0x3000, 0x16f3: 0x3000, 0x16f4: 0x3000, 0x16f5: 0x3000,
+	0x16f6: 0x3000, 0x16f7: 0x3000, 0x16f8: 0x3000, 0x16f9: 0x3000, 0x16fa: 0x3000, 0x16fb: 0x3000,
+	0x16fc: 0x3000, 0x16fd: 0x3000, 0x16fe: 0x3000, 0x16ff: 0x3000,
 	// Block 0x5c, offset 0x1700
-	0x170d: 0x1100, 0x170e: 0x1100, 0x170f: 0x1100, 0x1710: 0x8800,
+	0x1709: 0x3000,
+	0x1710: 0x8800,
 	0x1712: 0x8800, 0x1714: 0x8800,
+	0x171a: 0x1100, 0x171b: 0x1100,
+	0x172e: 0x1100,
 	// Block 0x5d, offset 0x1740
-	0x1743: 0x8800, 0x1744: 0x1100,
-	0x1748: 0x8800, 0x1749: 0x1100, 0x174b: 0x8800,
-	0x174c: 0x1100,
-	0x1763: 0x8800,
-	0x1764: 0x1100, 0x1765: 0x8800, 0x1766: 0x1100,
-	0x176c: 0x3000, 0x176d: 0x3000, 0x176f: 0x3000,
-	0x1770: 0x3000,
-	0x177c: 0x8800,
+	0x174d: 0x1100, 0x174e: 0x1100, 0x174f: 0x1100, 0x1750: 0x8800,
+	0x1752: 0x8800, 0x1754: 0x8800,
 	// Block 0x5e, offset 0x1780
-	0x1781: 0x1100, 0x1783: 0x8800, 0x1784: 0x1100, 0x1785: 0x8800,
-	0x1787: 0x1100, 0x1788: 0x8800, 0x1789: 0x1100,
-	0x178d: 0x8800,
-	0x17a0: 0x1100, 0x17a1: 0x8800, 0x17a2: 0x1100,
-	0x17a4: 0x8800, 0x17a5: 0x8800,
-	0x17ad: 0x1100, 0x17ae: 0x1100, 0x17af: 0x1100,
-	0x17b0: 0x1100, 0x17b1: 0x1100, 0x17b2: 0x8800, 0x17b3: 0x8800, 0x17b4: 0x1100, 0x17b5: 0x1100,
-	0x17b6: 0x8800, 0x17b7: 0x8800, 0x17b8: 0x1100, 0x17b9: 0x1100, 0x17ba: 0x8800, 0x17bb: 0x8800,
-	0x17bc: 0x8800, 0x17bd: 0x8800,
+	0x1783: 0x8800, 0x1784: 0x1100,
+	0x1788: 0x8800, 0x1789: 0x1100, 0x178b: 0x8800,
+	0x178c: 0x1100,
+	0x17a3: 0x8800,
+	0x17a4: 0x1100, 0x17a5: 0x8800, 0x17a6: 0x1100,
+	0x17ac: 0x3000, 0x17ad: 0x3000, 0x17af: 0x3000,
+	0x17b0: 0x3000,
+	0x17bc: 0x8800,
 	// Block 0x5f, offset 0x17c0
-	0x17c0: 0x1100, 0x17c1: 0x1100, 0x17c2: 0x8800, 0x17c3: 0x8800, 0x17c4: 0x1100, 0x17c5: 0x1100,
-	0x17c6: 0x8800, 0x17c7: 0x8800, 0x17c8: 0x1100, 0x17c9: 0x1100,
-	0x17d1: 0x8800,
-	0x17d2: 0x8800,
-	0x17e2: 0x8800,
-	0x17e8: 0x8800, 0x17e9: 0x8800,
-	0x17eb: 0x8800, 0x17ec: 0x1100, 0x17ed: 0x1100, 0x17ee: 0x1100, 0x17ef: 0x1100,
-	0x17f2: 0x8800, 0x17f3: 0x8800, 0x17f4: 0x8800, 0x17f5: 0x8800,
+	0x17c1: 0x1100, 0x17c3: 0x8800, 0x17c4: 0x1100, 0x17c5: 0x8800,
+	0x17c7: 0x1100, 0x17c8: 0x8800, 0x17c9: 0x1100,
+	0x17cd: 0x8800,
+	0x17e0: 0x1100, 0x17e1: 0x8800, 0x17e2: 0x1100,
+	0x17e4: 0x8800, 0x17e5: 0x8800,
+	0x17ed: 0x1100, 0x17ee: 0x1100, 0x17ef: 0x1100,
+	0x17f0: 0x1100, 0x17f1: 0x1100, 0x17f2: 0x8800, 0x17f3: 0x8800, 0x17f4: 0x1100, 0x17f5: 0x1100,
+	0x17f6: 0x8800, 0x17f7: 0x8800, 0x17f8: 0x1100, 0x17f9: 0x1100, 0x17fa: 0x8800, 0x17fb: 0x8800,
+	0x17fc: 0x8800, 0x17fd: 0x8800,
 	// Block 0x60, offset 0x1800
-	0x1820: 0x1100, 0x1821: 0x1100, 0x1822: 0x1100, 0x1823: 0x1100,
-	0x182a: 0x1100, 0x182b: 0x1100, 0x182c: 0x1100, 0x182d: 0x1100,
+	0x1800: 0x1100, 0x1801: 0x1100, 0x1802: 0x8800, 0x1803: 0x8800, 0x1804: 0x1100, 0x1805: 0x1100,
+	0x1806: 0x8800, 0x1807: 0x8800, 0x1808: 0x1100, 0x1809: 0x1100,
+	0x1811: 0x8800,
+	0x1812: 0x8800,
+	0x1822: 0x8800,
+	0x1828: 0x8800, 0x1829: 0x8800,
+	0x182b: 0x8800, 0x182c: 0x1100, 0x182d: 0x1100, 0x182e: 0x1100, 0x182f: 0x1100,
+	0x1832: 0x8800, 0x1833: 0x8800, 0x1834: 0x8800, 0x1835: 0x8800,
 	// Block 0x61, offset 0x1840
-	0x1869: 0x3300,
-	0x186a: 0x3300,
+	0x1860: 0x1100, 0x1861: 0x1100, 0x1862: 0x1100, 0x1863: 0x1100,
+	0x186a: 0x1100, 0x186b: 0x1100, 0x186c: 0x1100, 0x186d: 0x1100,
 	// Block 0x62, offset 0x1880
-	0x18a0: 0x3000, 0x18a1: 0x3000, 0x18a2: 0x3000, 0x18a3: 0x3000,
-	0x18a4: 0x3000, 0x18a5: 0x3000, 0x18a6: 0x3000, 0x18a7: 0x3000, 0x18a8: 0x3000, 0x18a9: 0x3000,
-	0x18aa: 0x3000, 0x18ab: 0x3000, 0x18ac: 0x3000, 0x18ad: 0x3000, 0x18ae: 0x3000, 0x18af: 0x3000,
-	0x18b0: 0x3000, 0x18b1: 0x3000, 0x18b2: 0x3000, 0x18b3: 0x3000, 0x18b4: 0x3000, 0x18b5: 0x3000,
-	0x18b6: 0x3000, 0x18b7: 0x3000, 0x18b8: 0x3000, 0x18b9: 0x3000, 0x18ba: 0x3000, 0x18bb: 0x3000,
-	0x18bc: 0x3000, 0x18bd: 0x3000, 0x18be: 0x3000, 0x18bf: 0x3000,
+	0x18a9: 0x3300,
+	0x18aa: 0x3300,
 	// Block 0x63, offset 0x18c0
-	0x18c0: 0x3000, 0x18c1: 0x3000, 0x18c2: 0x3000, 0x18c3: 0x3000, 0x18c4: 0x3000, 0x18c5: 0x3000,
-	0x18c6: 0x3000, 0x18c7: 0x3000, 0x18c8: 0x3000, 0x18c9: 0x3000, 0x18ca: 0x3000, 0x18cb: 0x3000,
-	0x18cc: 0x3000, 0x18cd: 0x3000, 0x18ce: 0x3000, 0x18cf: 0x3000, 0x18d0: 0x3000, 0x18d1: 0x3000,
-	0x18d2: 0x3000, 0x18d3: 0x3000, 0x18d4: 0x3000, 0x18d5: 0x3000, 0x18d6: 0x3000, 0x18d7: 0x3000,
-	0x18d8: 0x3000, 0x18d9: 0x3000, 0x18da: 0x3000, 0x18db: 0x3000, 0x18dc: 0x3000, 0x18dd: 0x3000,
-	0x18de: 0x3000, 0x18df: 0x3000, 0x18e0: 0x3000, 0x18e1: 0x3000, 0x18e2: 0x3000, 0x18e3: 0x3000,
+	0x18e0: 0x3000, 0x18e1: 0x3000, 0x18e2: 0x3000, 0x18e3: 0x3000,
 	0x18e4: 0x3000, 0x18e5: 0x3000, 0x18e6: 0x3000, 0x18e7: 0x3000, 0x18e8: 0x3000, 0x18e9: 0x3000,
 	0x18ea: 0x3000, 0x18eb: 0x3000, 0x18ec: 0x3000, 0x18ed: 0x3000, 0x18ee: 0x3000, 0x18ef: 0x3000,
 	0x18f0: 0x3000, 0x18f1: 0x3000, 0x18f2: 0x3000, 0x18f3: 0x3000, 0x18f4: 0x3000, 0x18f5: 0x3000,
@@ -5998,111 +5999,111 @@ var charInfoValues = [10944]uint16{
 	0x1918: 0x3000, 0x1919: 0x3000, 0x191a: 0x3000, 0x191b: 0x3000, 0x191c: 0x3000, 0x191d: 0x3000,
 	0x191e: 0x3000, 0x191f: 0x3000, 0x1920: 0x3000, 0x1921: 0x3000, 0x1922: 0x3000, 0x1923: 0x3000,
 	0x1924: 0x3000, 0x1925: 0x3000, 0x1926: 0x3000, 0x1927: 0x3000, 0x1928: 0x3000, 0x1929: 0x3000,
-	0x192a: 0x3000,
+	0x192a: 0x3000, 0x192b: 0x3000, 0x192c: 0x3000, 0x192d: 0x3000, 0x192e: 0x3000, 0x192f: 0x3000,
+	0x1930: 0x3000, 0x1931: 0x3000, 0x1932: 0x3000, 0x1933: 0x3000, 0x1934: 0x3000, 0x1935: 0x3000,
+	0x1936: 0x3000, 0x1937: 0x3000, 0x1938: 0x3000, 0x1939: 0x3000, 0x193a: 0x3000, 0x193b: 0x3000,
+	0x193c: 0x3000, 0x193d: 0x3000, 0x193e: 0x3000, 0x193f: 0x3000,
 	// Block 0x65, offset 0x1940
-	0x194c: 0x3000,
+	0x1940: 0x3000, 0x1941: 0x3000, 0x1942: 0x3000, 0x1943: 0x3000, 0x1944: 0x3000, 0x1945: 0x3000,
+	0x1946: 0x3000, 0x1947: 0x3000, 0x1948: 0x3000, 0x1949: 0x3000, 0x194a: 0x3000, 0x194b: 0x3000,
+	0x194c: 0x3000, 0x194d: 0x3000, 0x194e: 0x3000, 0x194f: 0x3000, 0x1950: 0x3000, 0x1951: 0x3000,
+	0x1952: 0x3000, 0x1953: 0x3000, 0x1954: 0x3000, 0x1955: 0x3000, 0x1956: 0x3000, 0x1957: 0x3000,
+	0x1958: 0x3000, 0x1959: 0x3000, 0x195a: 0x3000, 0x195b: 0x3000, 0x195c: 0x3000, 0x195d: 0x3000,
+	0x195e: 0x3000, 0x195f: 0x3000, 0x1960: 0x3000, 0x1961: 0x3000, 0x1962: 0x3000, 0x1963: 0x3000,
+	0x1964: 0x3000, 0x1965: 0x3000, 0x1966: 0x3000, 0x1967: 0x3000, 0x1968: 0x3000, 0x1969: 0x3000,
+	0x196a: 0x3000,
 	// Block 0x66, offset 0x1980
-	0x19b4: 0x3000, 0x19b5: 0x3000,
-	0x19b6: 0x3000,
+	0x198c: 0x3000,
 	// Block 0x67, offset 0x19c0
-	0x19dc: 0x3300,
+	0x19f4: 0x3000, 0x19f5: 0x3000,
+	0x19f6: 0x3000,
 	// Block 0x68, offset 0x1a00
-	0x1a3c: 0x3000, 0x1a3d: 0x3000,
+	0x1a1c: 0x3300,
 	// Block 0x69, offset 0x1a40
-	0x1a6f: 0x00e6,
-	0x1a70: 0x00e6, 0x1a71: 0x00e6,
+	0x1a7c: 0x3000, 0x1a7d: 0x3000,
 	// Block 0x6a, offset 0x1a80
-	0x1aaf: 0x3000,
-	0x1abf: 0x0009,
+	0x1aaf: 0x00e6,
+	0x1ab0: 0x00e6, 0x1ab1: 0x00e6,
 	// Block 0x6b, offset 0x1ac0
-	0x1ae0: 0x00e6, 0x1ae1: 0x00e6, 0x1ae2: 0x00e6, 0x1ae3: 0x00e6,
-	0x1ae4: 0x00e6, 0x1ae5: 0x00e6, 0x1ae6: 0x00e6, 0x1ae7: 0x00e6, 0x1ae8: 0x00e6, 0x1ae9: 0x00e6,
-	0x1aea: 0x00e6, 0x1aeb: 0x00e6, 0x1aec: 0x00e6, 0x1aed: 0x00e6, 0x1aee: 0x00e6, 0x1aef: 0x00e6,
-	0x1af0: 0x00e6, 0x1af1: 0x00e6, 0x1af2: 0x00e6, 0x1af3: 0x00e6, 0x1af4: 0x00e6, 0x1af5: 0x00e6,
-	0x1af6: 0x00e6, 0x1af7: 0x00e6, 0x1af8: 0x00e6, 0x1af9: 0x00e6, 0x1afa: 0x00e6, 0x1afb: 0x00e6,
-	0x1afc: 0x00e6, 0x1afd: 0x00e6, 0x1afe: 0x00e6, 0x1aff: 0x00e6,
+	0x1aef: 0x3000,
+	0x1aff: 0x0009,
 	// Block 0x6c, offset 0x1b00
-	0x1b1f: 0x3000,
+	0x1b20: 0x00e6, 0x1b21: 0x00e6, 0x1b22: 0x00e6, 0x1b23: 0x00e6,
+	0x1b24: 0x00e6, 0x1b25: 0x00e6, 0x1b26: 0x00e6, 0x1b27: 0x00e6, 0x1b28: 0x00e6, 0x1b29: 0x00e6,
+	0x1b2a: 0x00e6, 0x1b2b: 0x00e6, 0x1b2c: 0x00e6, 0x1b2d: 0x00e6, 0x1b2e: 0x00e6, 0x1b2f: 0x00e6,
+	0x1b30: 0x00e6, 0x1b31: 0x00e6, 0x1b32: 0x00e6, 0x1b33: 0x00e6, 0x1b34: 0x00e6, 0x1b35: 0x00e6,
+	0x1b36: 0x00e6, 0x1b37: 0x00e6, 0x1b38: 0x00e6, 0x1b39: 0x00e6, 0x1b3a: 0x00e6, 0x1b3b: 0x00e6,
+	0x1b3c: 0x00e6, 0x1b3d: 0x00e6, 0x1b3e: 0x00e6, 0x1b3f: 0x00e6,
 	// Block 0x6d, offset 0x1b40
-	0x1b73: 0x3000,
+	0x1b5f: 0x3000,
 	// Block 0x6e, offset 0x1b80
-	0x1b80: 0x3000, 0x1b81: 0x3000, 0x1b82: 0x3000, 0x1b83: 0x3000, 0x1b84: 0x3000, 0x1b85: 0x3000,
-	0x1b86: 0x3000, 0x1b87: 0x3000, 0x1b88: 0x3000, 0x1b89: 0x3000, 0x1b8a: 0x3000, 0x1b8b: 0x3000,
-	0x1b8c: 0x3000, 0x1b8d: 0x3000, 0x1b8e: 0x3000, 0x1b8f: 0x3000, 0x1b90: 0x3000, 0x1b91: 0x3000,
-	0x1b92: 0x3000, 0x1b93: 0x3000, 0x1b94: 0x3000, 0x1b95: 0x3000,
+	0x1bb3: 0x3000,
 	// Block 0x6f, offset 0x1bc0
-	0x1bc0: 0x3000,
-	0x1bea: 0x00da, 0x1beb: 0x00e4, 0x1bec: 0x00e8, 0x1bed: 0x00de, 0x1bee: 0x00e0, 0x1bef: 0x00e0,
-	0x1bf6: 0x3000, 0x1bf8: 0x3000, 0x1bf9: 0x3000, 0x1bfa: 0x3000,
+	0x1bc0: 0x3000, 0x1bc1: 0x3000, 0x1bc2: 0x3000, 0x1bc3: 0x3000, 0x1bc4: 0x3000, 0x1bc5: 0x3000,
+	0x1bc6: 0x3000, 0x1bc7: 0x3000, 0x1bc8: 0x3000, 0x1bc9: 0x3000, 0x1bca: 0x3000, 0x1bcb: 0x3000,
+	0x1bcc: 0x3000, 0x1bcd: 0x3000, 0x1bce: 0x3000, 0x1bcf: 0x3000, 0x1bd0: 0x3000, 0x1bd1: 0x3000,
+	0x1bd2: 0x3000, 0x1bd3: 0x3000, 0x1bd4: 0x3000, 0x1bd5: 0x3000,
 	// Block 0x70, offset 0x1c00
-	0x1c06: 0x8800, 0x1c0b: 0x8800,
-	0x1c0c: 0x1100, 0x1c0d: 0x8800, 0x1c0e: 0x1100, 0x1c0f: 0x8800, 0x1c10: 0x1100, 0x1c11: 0x8800,
-	0x1c12: 0x1100, 0x1c13: 0x8800, 0x1c14: 0x1100, 0x1c15: 0x8800, 0x1c16: 0x1100, 0x1c17: 0x8800,
-	0x1c18: 0x1100, 0x1c19: 0x8800, 0x1c1a: 0x1100, 0x1c1b: 0x8800, 0x1c1c: 0x1100, 0x1c1d: 0x8800,
-	0x1c1e: 0x1100, 0x1c1f: 0x8800, 0x1c20: 0x1100, 0x1c21: 0x8800, 0x1c22: 0x1100,
-	0x1c24: 0x8800, 0x1c25: 0x1100, 0x1c26: 0x8800, 0x1c27: 0x1100, 0x1c28: 0x8800, 0x1c29: 0x1100,
-	0x1c2f: 0x8800,
-	0x1c30: 0x1100, 0x1c31: 0x1100, 0x1c32: 0x8800, 0x1c33: 0x1100, 0x1c34: 0x1100, 0x1c35: 0x8800,
-	0x1c36: 0x1100, 0x1c37: 0x1100, 0x1c38: 0x8800, 0x1c39: 0x1100, 0x1c3a: 0x1100, 0x1c3b: 0x8800,
-	0x1c3c: 0x1100, 0x1c3d: 0x1100,
+	0x1c00: 0x3000,
+	0x1c2a: 0x00da, 0x1c2b: 0x00e4, 0x1c2c: 0x00e8, 0x1c2d: 0x00de, 0x1c2e: 0x00e0, 0x1c2f: 0x00e0,
+	0x1c36: 0x3000, 0x1c38: 0x3000, 0x1c39: 0x3000, 0x1c3a: 0x3000,
 	// Block 0x71, offset 0x1c40
-	0x1c54: 0x1100,
-	0x1c59: 0x6608, 0x1c5a: 0x6608, 0x1c5b: 0x3000, 0x1c5c: 0x3000, 0x1c5d: 0x8800,
-	0x1c5e: 0x1100, 0x1c5f: 0x3000,
-	0x1c66: 0x8800,
-	0x1c6b: 0x8800, 0x1c6c: 0x1100, 0x1c6d: 0x8800, 0x1c6e: 0x1100, 0x1c6f: 0x8800,
-	0x1c70: 0x1100, 0x1c71: 0x8800, 0x1c72: 0x1100, 0x1c73: 0x8800, 0x1c74: 0x1100, 0x1c75: 0x8800,
-	0x1c76: 0x1100, 0x1c77: 0x8800, 0x1c78: 0x1100, 0x1c79: 0x8800, 0x1c7a: 0x1100, 0x1c7b: 0x8800,
-	0x1c7c: 0x1100, 0x1c7d: 0x8800, 0x1c7e: 0x1100, 0x1c7f: 0x8800,
+	0x1c46: 0x8800, 0x1c4b: 0x8800,
+	0x1c4c: 0x1100, 0x1c4d: 0x8800, 0x1c4e: 0x1100, 0x1c4f: 0x8800, 0x1c50: 0x1100, 0x1c51: 0x8800,
+	0x1c52: 0x1100, 0x1c53: 0x8800, 0x1c54: 0x1100, 0x1c55: 0x8800, 0x1c56: 0x1100, 0x1c57: 0x8800,
+	0x1c58: 0x1100, 0x1c59: 0x8800, 0x1c5a: 0x1100, 0x1c5b: 0x8800, 0x1c5c: 0x1100, 0x1c5d: 0x8800,
+	0x1c5e: 0x1100, 0x1c5f: 0x8800, 0x1c60: 0x1100, 0x1c61: 0x8800, 0x1c62: 0x1100,
+	0x1c64: 0x8800, 0x1c65: 0x1100, 0x1c66: 0x8800, 0x1c67: 0x1100, 0x1c68: 0x8800, 0x1c69: 0x1100,
+	0x1c6f: 0x8800,
+	0x1c70: 0x1100, 0x1c71: 0x1100, 0x1c72: 0x8800, 0x1c73: 0x1100, 0x1c74: 0x1100, 0x1c75: 0x8800,
+	0x1c76: 0x1100, 0x1c77: 0x1100, 0x1c78: 0x8800, 0x1c79: 0x1100, 0x1c7a: 0x1100, 0x1c7b: 0x8800,
+	0x1c7c: 0x1100, 0x1c7d: 0x1100,
 	// Block 0x72, offset 0x1c80
-	0x1c80: 0x1100, 0x1c81: 0x8800, 0x1c82: 0x1100, 0x1c84: 0x8800, 0x1c85: 0x1100,
-	0x1c86: 0x8800, 0x1c87: 0x1100, 0x1c88: 0x8800, 0x1c89: 0x1100,
-	0x1c8f: 0x8800, 0x1c90: 0x1100, 0x1c91: 0x1100,
-	0x1c92: 0x8800, 0x1c93: 0x1100, 0x1c94: 0x1100, 0x1c95: 0x8800, 0x1c96: 0x1100, 0x1c97: 0x1100,
-	0x1c98: 0x8800, 0x1c99: 0x1100, 0x1c9a: 0x1100, 0x1c9b: 0x8800, 0x1c9c: 0x1100, 0x1c9d: 0x1100,
-	0x1caf: 0x8800,
-	0x1cb0: 0x8800, 0x1cb1: 0x8800, 0x1cb2: 0x8800, 0x1cb4: 0x1100,
-	0x1cb7: 0x1100, 0x1cb8: 0x1100, 0x1cb9: 0x1100, 0x1cba: 0x1100,
-	0x1cbd: 0x8800, 0x1cbe: 0x1100, 0x1cbf: 0x3000,
+	0x1c94: 0x1100,
+	0x1c99: 0x6608, 0x1c9a: 0x6608, 0x1c9b: 0x3000, 0x1c9c: 0x3000, 0x1c9d: 0x8800,
+	0x1c9e: 0x1100, 0x1c9f: 0x3000,
+	0x1ca6: 0x8800,
+	0x1cab: 0x8800, 0x1cac: 0x1100, 0x1cad: 0x8800, 0x1cae: 0x1100, 0x1caf: 0x8800,
+	0x1cb0: 0x1100, 0x1cb1: 0x8800, 0x1cb2: 0x1100, 0x1cb3: 0x8800, 0x1cb4: 0x1100, 0x1cb5: 0x8800,
+	0x1cb6: 0x1100, 0x1cb7: 0x8800, 0x1cb8: 0x1100, 0x1cb9: 0x8800, 0x1cba: 0x1100, 0x1cbb: 0x8800,
+	0x1cbc: 0x1100, 0x1cbd: 0x8800, 0x1cbe: 0x1100, 0x1cbf: 0x8800,
 	// Block 0x73, offset 0x1cc0
-	0x1cf1: 0x3000, 0x1cf2: 0x3000, 0x1cf3: 0x3000, 0x1cf4: 0x3000, 0x1cf5: 0x3000,
-	0x1cf6: 0x3000, 0x1cf7: 0x3000, 0x1cf8: 0x3000, 0x1cf9: 0x3000, 0x1cfa: 0x3000, 0x1cfb: 0x3000,
-	0x1cfc: 0x3000, 0x1cfd: 0x3000, 0x1cfe: 0x3000, 0x1cff: 0x3000,
+	0x1cc0: 0x1100, 0x1cc1: 0x8800, 0x1cc2: 0x1100, 0x1cc4: 0x8800, 0x1cc5: 0x1100,
+	0x1cc6: 0x8800, 0x1cc7: 0x1100, 0x1cc8: 0x8800, 0x1cc9: 0x1100,
+	0x1ccf: 0x8800, 0x1cd0: 0x1100, 0x1cd1: 0x1100,
+	0x1cd2: 0x8800, 0x1cd3: 0x1100, 0x1cd4: 0x1100, 0x1cd5: 0x8800, 0x1cd6: 0x1100, 0x1cd7: 0x1100,
+	0x1cd8: 0x8800, 0x1cd9: 0x1100, 0x1cda: 0x1100, 0x1cdb: 0x8800, 0x1cdc: 0x1100, 0x1cdd: 0x1100,
+	0x1cef: 0x8800,
+	0x1cf0: 0x8800, 0x1cf1: 0x8800, 0x1cf2: 0x8800, 0x1cf4: 0x1100,
+	0x1cf7: 0x1100, 0x1cf8: 0x1100, 0x1cf9: 0x1100, 0x1cfa: 0x1100,
+	0x1cfd: 0x8800, 0x1cfe: 0x1100, 0x1cff: 0x3000,
 	// Block 0x74, offset 0x1d00
-	0x1d00: 0x3000, 0x1d01: 0x3000, 0x1d02: 0x3000, 0x1d03: 0x3000, 0x1d04: 0x3000, 0x1d05: 0x3000,
-	0x1d06: 0x3000, 0x1d07: 0x3000, 0x1d08: 0x3000, 0x1d09: 0x3000, 0x1d0a: 0x3000, 0x1d0b: 0x3000,
-	0x1d0c: 0x3000, 0x1d0d: 0x3000, 0x1d0e: 0x3000,
-	0x1d12: 0x3000, 0x1d13: 0x3000, 0x1d14: 0x3000, 0x1d15: 0x3000, 0x1d16: 0x3000, 0x1d17: 0x3000,
-	0x1d18: 0x3000, 0x1d19: 0x3000, 0x1d1a: 0x3000, 0x1d1b: 0x3000, 0x1d1c: 0x3000, 0x1d1d: 0x3000,
-	0x1d1e: 0x3000, 0x1d1f: 0x3000,
+	0x1d31: 0x3000, 0x1d32: 0x3000, 0x1d33: 0x3000, 0x1d34: 0x3000, 0x1d35: 0x3000,
+	0x1d36: 0x3000, 0x1d37: 0x3000, 0x1d38: 0x3000, 0x1d39: 0x3000, 0x1d3a: 0x3000, 0x1d3b: 0x3000,
+	0x1d3c: 0x3000, 0x1d3d: 0x3000, 0x1d3e: 0x3000, 0x1d3f: 0x3000,
 	// Block 0x75, offset 0x1d40
 	0x1d40: 0x3000, 0x1d41: 0x3000, 0x1d42: 0x3000, 0x1d43: 0x3000, 0x1d44: 0x3000, 0x1d45: 0x3000,
 	0x1d46: 0x3000, 0x1d47: 0x3000, 0x1d48: 0x3000, 0x1d49: 0x3000, 0x1d4a: 0x3000, 0x1d4b: 0x3000,
-	0x1d4c: 0x3000, 0x1d4d: 0x3000, 0x1d4e: 0x3000, 0x1d4f: 0x3000, 0x1d50: 0x3000, 0x1d51: 0x3000,
+	0x1d4c: 0x3000, 0x1d4d: 0x3000, 0x1d4e: 0x3000,
 	0x1d52: 0x3000, 0x1d53: 0x3000, 0x1d54: 0x3000, 0x1d55: 0x3000, 0x1d56: 0x3000, 0x1d57: 0x3000,
 	0x1d58: 0x3000, 0x1d59: 0x3000, 0x1d5a: 0x3000, 0x1d5b: 0x3000, 0x1d5c: 0x3000, 0x1d5d: 0x3000,
-	0x1d5e: 0x3000, 0x1d60: 0x3000, 0x1d61: 0x3000, 0x1d62: 0x3000, 0x1d63: 0x3000,
-	0x1d64: 0x3000, 0x1d65: 0x3000, 0x1d66: 0x3000, 0x1d67: 0x3000, 0x1d68: 0x3000, 0x1d69: 0x3000,
-	0x1d6a: 0x3000, 0x1d6b: 0x3000, 0x1d6c: 0x3000, 0x1d6d: 0x3000, 0x1d6e: 0x3000, 0x1d6f: 0x3000,
-	0x1d70: 0x3000, 0x1d71: 0x3000, 0x1d72: 0x3000, 0x1d73: 0x3000, 0x1d74: 0x3000, 0x1d75: 0x3000,
-	0x1d76: 0x3000, 0x1d77: 0x3000, 0x1d78: 0x3000, 0x1d79: 0x3000, 0x1d7a: 0x3000, 0x1d7b: 0x3000,
-	0x1d7c: 0x3000, 0x1d7d: 0x3000, 0x1d7e: 0x3000, 0x1d7f: 0x3000,
+	0x1d5e: 0x3000, 0x1d5f: 0x3000,
 	// Block 0x76, offset 0x1d80
 	0x1d80: 0x3000, 0x1d81: 0x3000, 0x1d82: 0x3000, 0x1d83: 0x3000, 0x1d84: 0x3000, 0x1d85: 0x3000,
-	0x1d86: 0x3000, 0x1d87: 0x3000,
-	0x1d90: 0x3000, 0x1d91: 0x3000,
+	0x1d86: 0x3000, 0x1d87: 0x3000, 0x1d88: 0x3000, 0x1d89: 0x3000, 0x1d8a: 0x3000, 0x1d8b: 0x3000,
+	0x1d8c: 0x3000, 0x1d8d: 0x3000, 0x1d8e: 0x3000, 0x1d8f: 0x3000, 0x1d90: 0x3000, 0x1d91: 0x3000,
 	0x1d92: 0x3000, 0x1d93: 0x3000, 0x1d94: 0x3000, 0x1d95: 0x3000, 0x1d96: 0x3000, 0x1d97: 0x3000,
 	0x1d98: 0x3000, 0x1d99: 0x3000, 0x1d9a: 0x3000, 0x1d9b: 0x3000, 0x1d9c: 0x3000, 0x1d9d: 0x3000,
-	0x1d9e: 0x3000, 0x1d9f: 0x3000, 0x1da0: 0x3000, 0x1da1: 0x3000, 0x1da2: 0x3000, 0x1da3: 0x3000,
+	0x1d9e: 0x3000, 0x1da0: 0x3000, 0x1da1: 0x3000, 0x1da2: 0x3000, 0x1da3: 0x3000,
 	0x1da4: 0x3000, 0x1da5: 0x3000, 0x1da6: 0x3000, 0x1da7: 0x3000, 0x1da8: 0x3000, 0x1da9: 0x3000,
 	0x1daa: 0x3000, 0x1dab: 0x3000, 0x1dac: 0x3000, 0x1dad: 0x3000, 0x1dae: 0x3000, 0x1daf: 0x3000,
 	0x1db0: 0x3000, 0x1db1: 0x3000, 0x1db2: 0x3000, 0x1db3: 0x3000, 0x1db4: 0x3000, 0x1db5: 0x3000,
 	0x1db6: 0x3000, 0x1db7: 0x3000, 0x1db8: 0x3000, 0x1db9: 0x3000, 0x1dba: 0x3000, 0x1dbb: 0x3000,
-	0x1dbc: 0x3000, 0x1dbd: 0x3000, 0x1dbe: 0x3000,
+	0x1dbc: 0x3000, 0x1dbd: 0x3000, 0x1dbe: 0x3000, 0x1dbf: 0x3000,
 	// Block 0x77, offset 0x1dc0
 	0x1dc0: 0x3000, 0x1dc1: 0x3000, 0x1dc2: 0x3000, 0x1dc3: 0x3000, 0x1dc4: 0x3000, 0x1dc5: 0x3000,
-	0x1dc6: 0x3000, 0x1dc7: 0x3000, 0x1dc8: 0x3000, 0x1dc9: 0x3000, 0x1dca: 0x3000, 0x1dcb: 0x3000,
-	0x1dcc: 0x3000, 0x1dcd: 0x3000, 0x1dce: 0x3000, 0x1dcf: 0x3000, 0x1dd0: 0x3000, 0x1dd1: 0x3000,
+	0x1dc6: 0x3000, 0x1dc7: 0x3000,
+	0x1dd0: 0x3000, 0x1dd1: 0x3000,
 	0x1dd2: 0x3000, 0x1dd3: 0x3000, 0x1dd4: 0x3000, 0x1dd5: 0x3000, 0x1dd6: 0x3000, 0x1dd7: 0x3000,
 	0x1dd8: 0x3000, 0x1dd9: 0x3000, 0x1dda: 0x3000, 0x1ddb: 0x3000, 0x1ddc: 0x3000, 0x1ddd: 0x3000,
 	0x1dde: 0x3000, 0x1ddf: 0x3000, 0x1de0: 0x3000, 0x1de1: 0x3000, 0x1de2: 0x3000, 0x1de3: 0x3000,
@@ -6112,48 +6113,48 @@ var charInfoValues = [10944]uint16{
 	0x1df6: 0x3000, 0x1df7: 0x3000, 0x1df8: 0x3000, 0x1df9: 0x3000, 0x1dfa: 0x3000, 0x1dfb: 0x3000,
 	0x1dfc: 0x3000, 0x1dfd: 0x3000, 0x1dfe: 0x3000,
 	// Block 0x78, offset 0x1e00
-	0x1e2f: 0x00e6,
-	0x1e3c: 0x00e6, 0x1e3d: 0x00e6,
+	0x1e00: 0x3000, 0x1e01: 0x3000, 0x1e02: 0x3000, 0x1e03: 0x3000, 0x1e04: 0x3000, 0x1e05: 0x3000,
+	0x1e06: 0x3000, 0x1e07: 0x3000, 0x1e08: 0x3000, 0x1e09: 0x3000, 0x1e0a: 0x3000, 0x1e0b: 0x3000,
+	0x1e0c: 0x3000, 0x1e0d: 0x3000, 0x1e0e: 0x3000, 0x1e0f: 0x3000, 0x1e10: 0x3000, 0x1e11: 0x3000,
+	0x1e12: 0x3000, 0x1e13: 0x3000, 0x1e14: 0x3000, 0x1e15: 0x3000, 0x1e16: 0x3000, 0x1e17: 0x3000,
+	0x1e18: 0x3000, 0x1e19: 0x3000, 0x1e1a: 0x3000, 0x1e1b: 0x3000, 0x1e1c: 0x3000, 0x1e1d: 0x3000,
+	0x1e1e: 0x3000, 0x1e1f: 0x3000, 0x1e20: 0x3000, 0x1e21: 0x3000, 0x1e22: 0x3000, 0x1e23: 0x3000,
+	0x1e24: 0x3000, 0x1e25: 0x3000, 0x1e26: 0x3000, 0x1e27: 0x3000, 0x1e28: 0x3000, 0x1e29: 0x3000,
+	0x1e2a: 0x3000, 0x1e2b: 0x3000, 0x1e2c: 0x3000, 0x1e2d: 0x3000, 0x1e2e: 0x3000, 0x1e2f: 0x3000,
+	0x1e30: 0x3000, 0x1e31: 0x3000, 0x1e32: 0x3000, 0x1e33: 0x3000, 0x1e34: 0x3000, 0x1e35: 0x3000,
+	0x1e36: 0x3000, 0x1e37: 0x3000, 0x1e38: 0x3000, 0x1e39: 0x3000, 0x1e3a: 0x3000, 0x1e3b: 0x3000,
+	0x1e3c: 0x3000, 0x1e3d: 0x3000, 0x1e3e: 0x3000,
 	// Block 0x79, offset 0x1e40
-	0x1e70: 0x00e6, 0x1e71: 0x00e6,
+	0x1e6f: 0x00e6,
+	0x1e7c: 0x00e6, 0x1e7d: 0x00e6,
 	// Block 0x7a, offset 0x1e80
-	0x1eb0: 0x3000,
+	0x1eb0: 0x00e6, 0x1eb1: 0x00e6,
 	// Block 0x7b, offset 0x1ec0
-	0x1ec6: 0x0009,
+	0x1ef0: 0x3000,
 	// Block 0x7c, offset 0x1f00
-	0x1f04: 0x0009,
-	0x1f20: 0x00e6, 0x1f21: 0x00e6, 0x1f22: 0x00e6, 0x1f23: 0x00e6,
-	0x1f24: 0x00e6, 0x1f25: 0x00e6, 0x1f26: 0x00e6, 0x1f27: 0x00e6, 0x1f28: 0x00e6, 0x1f29: 0x00e6,
-	0x1f2a: 0x00e6, 0x1f2b: 0x00e6, 0x1f2c: 0x00e6, 0x1f2d: 0x00e6, 0x1f2e: 0x00e6, 0x1f2f: 0x00e6,
-	0x1f30: 0x00e6, 0x1f31: 0x00e6,
+	0x1f06: 0x0009,
 	// Block 0x7d, offset 0x1f40
-	0x1f6b: 0x00dc, 0x1f6c: 0x00dc, 0x1f6d: 0x00dc,
+	0x1f44: 0x0009,
+	0x1f60: 0x00e6, 0x1f61: 0x00e6, 0x1f62: 0x00e6, 0x1f63: 0x00e6,
+	0x1f64: 0x00e6, 0x1f65: 0x00e6, 0x1f66: 0x00e6, 0x1f67: 0x00e6, 0x1f68: 0x00e6, 0x1f69: 0x00e6,
+	0x1f6a: 0x00e6, 0x1f6b: 0x00e6, 0x1f6c: 0x00e6, 0x1f6d: 0x00e6, 0x1f6e: 0x00e6, 0x1f6f: 0x00e6,
+	0x1f70: 0x00e6, 0x1f71: 0x00e6,
 	// Block 0x7e, offset 0x1f80
-	0x1f93: 0x0009,
+	0x1fab: 0x00dc, 0x1fac: 0x00dc, 0x1fad: 0x00dc,
 	// Block 0x7f, offset 0x1fc0
-	0x1ff3: 0x0007,
+	0x1fd3: 0x0009,
 	// Block 0x80, offset 0x2000
-	0x2000: 0x0009,
+	0x2033: 0x0007,
 	// Block 0x81, offset 0x2040
-	0x2070: 0x00e6, 0x2072: 0x00e6, 0x2073: 0x00e6, 0x2074: 0x00dc,
-	0x2077: 0x00e6, 0x2078: 0x00e6,
-	0x207e: 0x00e6, 0x207f: 0x00e6,
+	0x2040: 0x0009,
 	// Block 0x82, offset 0x2080
-	0x2081: 0x00e6,
+	0x20b0: 0x00e6, 0x20b2: 0x00e6, 0x20b3: 0x00e6, 0x20b4: 0x00dc,
+	0x20b7: 0x00e6, 0x20b8: 0x00e6,
+	0x20be: 0x00e6, 0x20bf: 0x00e6,
 	// Block 0x83, offset 0x20c0
-	0x20ed: 0x0009,
+	0x20c1: 0x00e6,
 	// Block 0x84, offset 0x2100
-	0x2100: 0x1100, 0x2101: 0x1100, 0x2102: 0x1100, 0x2103: 0x1100, 0x2104: 0x1100, 0x2105: 0x1100,
-	0x2106: 0x1100, 0x2107: 0x1100, 0x2108: 0x1100, 0x2109: 0x1100, 0x210a: 0x1100, 0x210b: 0x1100,
-	0x210c: 0x1100, 0x210d: 0x1100, 0x210e: 0x1100, 0x210f: 0x1100, 0x2110: 0x1100, 0x2111: 0x1100,
-	0x2112: 0x1100, 0x2113: 0x1100, 0x2114: 0x1100, 0x2115: 0x1100, 0x2116: 0x1100, 0x2117: 0x1100,
-	0x2118: 0x1100, 0x2119: 0x1100, 0x211a: 0x1100, 0x211b: 0x1100, 0x211c: 0x1100, 0x211d: 0x1100,
-	0x211e: 0x1100, 0x211f: 0x1100, 0x2120: 0x1100, 0x2121: 0x1100, 0x2122: 0x1100, 0x2123: 0x1100,
-	0x2124: 0x1100, 0x2125: 0x1100, 0x2126: 0x1100, 0x2127: 0x1100, 0x2128: 0x1100, 0x2129: 0x1100,
-	0x212a: 0x1100, 0x212b: 0x1100, 0x212c: 0x1100, 0x212d: 0x1100, 0x212e: 0x1100, 0x212f: 0x1100,
-	0x2130: 0x1100, 0x2131: 0x1100, 0x2132: 0x1100, 0x2133: 0x1100, 0x2134: 0x1100, 0x2135: 0x1100,
-	0x2136: 0x1100, 0x2137: 0x1100, 0x2138: 0x1100, 0x2139: 0x1100, 0x213a: 0x1100, 0x213b: 0x1100,
-	0x213c: 0x1100, 0x213d: 0x1100, 0x213e: 0x1100, 0x213f: 0x1100,
+	0x212d: 0x0009,
 	// Block 0x85, offset 0x2140
 	0x2140: 0x1100, 0x2141: 0x1100, 0x2142: 0x1100, 0x2143: 0x1100, 0x2144: 0x1100, 0x2145: 0x1100,
 	0x2146: 0x1100, 0x2147: 0x1100, 0x2148: 0x1100, 0x2149: 0x1100, 0x214a: 0x1100, 0x214b: 0x1100,
@@ -6161,38 +6162,38 @@ var charInfoValues = [10944]uint16{
 	0x2152: 0x1100, 0x2153: 0x1100, 0x2154: 0x1100, 0x2155: 0x1100, 0x2156: 0x1100, 0x2157: 0x1100,
 	0x2158: 0x1100, 0x2159: 0x1100, 0x215a: 0x1100, 0x215b: 0x1100, 0x215c: 0x1100, 0x215d: 0x1100,
 	0x215e: 0x1100, 0x215f: 0x1100, 0x2160: 0x1100, 0x2161: 0x1100, 0x2162: 0x1100, 0x2163: 0x1100,
+	0x2164: 0x1100, 0x2165: 0x1100, 0x2166: 0x1100, 0x2167: 0x1100, 0x2168: 0x1100, 0x2169: 0x1100,
+	0x216a: 0x1100, 0x216b: 0x1100, 0x216c: 0x1100, 0x216d: 0x1100, 0x216e: 0x1100, 0x216f: 0x1100,
+	0x2170: 0x1100, 0x2171: 0x1100, 0x2172: 0x1100, 0x2173: 0x1100, 0x2174: 0x1100, 0x2175: 0x1100,
+	0x2176: 0x1100, 0x2177: 0x1100, 0x2178: 0x1100, 0x2179: 0x1100, 0x217a: 0x1100, 0x217b: 0x1100,
+	0x217c: 0x1100, 0x217d: 0x1100, 0x217e: 0x1100, 0x217f: 0x1100,
 	// Block 0x86, offset 0x2180
-	0x2180: 0x3300, 0x2181: 0x3300, 0x2182: 0x3300, 0x2183: 0x3300, 0x2184: 0x3300, 0x2185: 0x3300,
-	0x2186: 0x3300, 0x2187: 0x3300, 0x2188: 0x3300, 0x2189: 0x3300, 0x218a: 0x3300, 0x218b: 0x3300,
-	0x218c: 0x3300, 0x218d: 0x3300, 0x218e: 0x3300, 0x218f: 0x3300, 0x2190: 0x3300, 0x2191: 0x3300,
-	0x2192: 0x3300, 0x2193: 0x3300, 0x2194: 0x3300, 0x2195: 0x3300, 0x2196: 0x3300, 0x2197: 0x3300,
-	0x2198: 0x3300, 0x2199: 0x3300, 0x219a: 0x3300, 0x219b: 0x3300, 0x219c: 0x3300, 0x219d: 0x3300,
-	0x219e: 0x3300, 0x219f: 0x3300, 0x21a0: 0x3300, 0x21a1: 0x3300, 0x21a2: 0x3300, 0x21a3: 0x3300,
-	0x21a4: 0x3300, 0x21a5: 0x3300, 0x21a6: 0x3300, 0x21a7: 0x3300, 0x21a8: 0x3300, 0x21a9: 0x3300,
-	0x21aa: 0x3300, 0x21ab: 0x3300, 0x21ac: 0x3300, 0x21ad: 0x3300, 0x21ae: 0x3300, 0x21af: 0x3300,
-	0x21b0: 0x3300, 0x21b1: 0x3300, 0x21b2: 0x3300, 0x21b3: 0x3300, 0x21b4: 0x3300, 0x21b5: 0x3300,
-	0x21b6: 0x3300, 0x21b7: 0x3300, 0x21b8: 0x3300, 0x21b9: 0x3300, 0x21ba: 0x3300, 0x21bb: 0x3300,
-	0x21bc: 0x3300, 0x21bd: 0x3300, 0x21be: 0x3300, 0x21bf: 0x3300,
+	0x2180: 0x1100, 0x2181: 0x1100, 0x2182: 0x1100, 0x2183: 0x1100, 0x2184: 0x1100, 0x2185: 0x1100,
+	0x2186: 0x1100, 0x2187: 0x1100, 0x2188: 0x1100, 0x2189: 0x1100, 0x218a: 0x1100, 0x218b: 0x1100,
+	0x218c: 0x1100, 0x218d: 0x1100, 0x218e: 0x1100, 0x218f: 0x1100, 0x2190: 0x1100, 0x2191: 0x1100,
+	0x2192: 0x1100, 0x2193: 0x1100, 0x2194: 0x1100, 0x2195: 0x1100, 0x2196: 0x1100, 0x2197: 0x1100,
+	0x2198: 0x1100, 0x2199: 0x1100, 0x219a: 0x1100, 0x219b: 0x1100, 0x219c: 0x1100, 0x219d: 0x1100,
+	0x219e: 0x1100, 0x219f: 0x1100, 0x21a0: 0x1100, 0x21a1: 0x1100, 0x21a2: 0x1100, 0x21a3: 0x1100,
 	// Block 0x87, offset 0x21c0
 	0x21c0: 0x3300, 0x21c1: 0x3300, 0x21c2: 0x3300, 0x21c3: 0x3300, 0x21c4: 0x3300, 0x21c5: 0x3300,
 	0x21c6: 0x3300, 0x21c7: 0x3300, 0x21c8: 0x3300, 0x21c9: 0x3300, 0x21ca: 0x3300, 0x21cb: 0x3300,
-	0x21cc: 0x3300, 0x21cd: 0x3300, 0x21d0: 0x3300,
-	0x21d2: 0x3300, 0x21d5: 0x3300, 0x21d6: 0x3300, 0x21d7: 0x3300,
+	0x21cc: 0x3300, 0x21cd: 0x3300, 0x21ce: 0x3300, 0x21cf: 0x3300, 0x21d0: 0x3300, 0x21d1: 0x3300,
+	0x21d2: 0x3300, 0x21d3: 0x3300, 0x21d4: 0x3300, 0x21d5: 0x3300, 0x21d6: 0x3300, 0x21d7: 0x3300,
 	0x21d8: 0x3300, 0x21d9: 0x3300, 0x21da: 0x3300, 0x21db: 0x3300, 0x21dc: 0x3300, 0x21dd: 0x3300,
-	0x21de: 0x3300, 0x21e0: 0x3300, 0x21e2: 0x3300,
-	0x21e5: 0x3300, 0x21e6: 0x3300,
-	0x21ea: 0x3300, 0x21eb: 0x3300, 0x21ec: 0x3300, 0x21ed: 0x3300,
+	0x21de: 0x3300, 0x21df: 0x3300, 0x21e0: 0x3300, 0x21e1: 0x3300, 0x21e2: 0x3300, 0x21e3: 0x3300,
+	0x21e4: 0x3300, 0x21e5: 0x3300, 0x21e6: 0x3300, 0x21e7: 0x3300, 0x21e8: 0x3300, 0x21e9: 0x3300,
+	0x21ea: 0x3300, 0x21eb: 0x3300, 0x21ec: 0x3300, 0x21ed: 0x3300, 0x21ee: 0x3300, 0x21ef: 0x3300,
 	0x21f0: 0x3300, 0x21f1: 0x3300, 0x21f2: 0x3300, 0x21f3: 0x3300, 0x21f4: 0x3300, 0x21f5: 0x3300,
 	0x21f6: 0x3300, 0x21f7: 0x3300, 0x21f8: 0x3300, 0x21f9: 0x3300, 0x21fa: 0x3300, 0x21fb: 0x3300,
 	0x21fc: 0x3300, 0x21fd: 0x3300, 0x21fe: 0x3300, 0x21ff: 0x3300,
 	// Block 0x88, offset 0x2200
 	0x2200: 0x3300, 0x2201: 0x3300, 0x2202: 0x3300, 0x2203: 0x3300, 0x2204: 0x3300, 0x2205: 0x3300,
 	0x2206: 0x3300, 0x2207: 0x3300, 0x2208: 0x3300, 0x2209: 0x3300, 0x220a: 0x3300, 0x220b: 0x3300,
-	0x220c: 0x3300, 0x220d: 0x3300, 0x220e: 0x3300, 0x220f: 0x3300, 0x2210: 0x3300, 0x2211: 0x3300,
-	0x2212: 0x3300, 0x2213: 0x3300, 0x2214: 0x3300, 0x2215: 0x3300, 0x2216: 0x3300, 0x2217: 0x3300,
+	0x220c: 0x3300, 0x220d: 0x3300, 0x2210: 0x3300,
+	0x2212: 0x3300, 0x2215: 0x3300, 0x2216: 0x3300, 0x2217: 0x3300,
 	0x2218: 0x3300, 0x2219: 0x3300, 0x221a: 0x3300, 0x221b: 0x3300, 0x221c: 0x3300, 0x221d: 0x3300,
-	0x221e: 0x3300, 0x221f: 0x3300, 0x2220: 0x3300, 0x2221: 0x3300, 0x2222: 0x3300, 0x2223: 0x3300,
-	0x2224: 0x3300, 0x2225: 0x3300, 0x2226: 0x3300, 0x2227: 0x3300, 0x2228: 0x3300, 0x2229: 0x3300,
+	0x221e: 0x3300, 0x2220: 0x3300, 0x2222: 0x3300,
+	0x2225: 0x3300, 0x2226: 0x3300,
 	0x222a: 0x3300, 0x222b: 0x3300, 0x222c: 0x3300, 0x222d: 0x3300,
 	0x2230: 0x3300, 0x2231: 0x3300, 0x2232: 0x3300, 0x2233: 0x3300, 0x2234: 0x3300, 0x2235: 0x3300,
 	0x2236: 0x3300, 0x2237: 0x3300, 0x2238: 0x3300, 0x2239: 0x3300, 0x223a: 0x3300, 0x223b: 0x3300,
@@ -6202,63 +6203,65 @@ var charInfoValues = [10944]uint16{
 	0x2246: 0x3300, 0x2247: 0x3300, 0x2248: 0x3300, 0x2249: 0x3300, 0x224a: 0x3300, 0x224b: 0x3300,
 	0x224c: 0x3300, 0x224d: 0x3300, 0x224e: 0x3300, 0x224f: 0x3300, 0x2250: 0x3300, 0x2251: 0x3300,
 	0x2252: 0x3300, 0x2253: 0x3300, 0x2254: 0x3300, 0x2255: 0x3300, 0x2256: 0x3300, 0x2257: 0x3300,
-	0x2258: 0x3300, 0x2259: 0x3300,
+	0x2258: 0x3300, 0x2259: 0x3300, 0x225a: 0x3300, 0x225b: 0x3300, 0x225c: 0x3300, 0x225d: 0x3300,
+	0x225e: 0x3300, 0x225f: 0x3300, 0x2260: 0x3300, 0x2261: 0x3300, 0x2262: 0x3300, 0x2263: 0x3300,
+	0x2264: 0x3300, 0x2265: 0x3300, 0x2266: 0x3300, 0x2267: 0x3300, 0x2268: 0x3300, 0x2269: 0x3300,
+	0x226a: 0x3300, 0x226b: 0x3300, 0x226c: 0x3300, 0x226d: 0x3300,
+	0x2270: 0x3300, 0x2271: 0x3300, 0x2272: 0x3300, 0x2273: 0x3300, 0x2274: 0x3300, 0x2275: 0x3300,
+	0x2276: 0x3300, 0x2277: 0x3300, 0x2278: 0x3300, 0x2279: 0x3300, 0x227a: 0x3300, 0x227b: 0x3300,
+	0x227c: 0x3300, 0x227d: 0x3300, 0x227e: 0x3300, 0x227f: 0x3300,
 	// Block 0x8a, offset 0x2280
-	0x2280: 0x3000, 0x2281: 0x3000, 0x2282: 0x3000, 0x2283: 0x3000, 0x2284: 0x3000, 0x2285: 0x3000,
-	0x2286: 0x3000,
-	0x2293: 0x3000, 0x2294: 0x3000, 0x2295: 0x3000, 0x2296: 0x3000, 0x2297: 0x3000,
-	0x229d: 0x3300,
-	0x229e: 0x001a, 0x229f: 0x3300, 0x22a0: 0x3000, 0x22a1: 0x3000, 0x22a2: 0x3000, 0x22a3: 0x3000,
-	0x22a4: 0x3000, 0x22a5: 0x3000, 0x22a6: 0x3000, 0x22a7: 0x3000, 0x22a8: 0x3000, 0x22a9: 0x3000,
-	0x22aa: 0x3300, 0x22ab: 0x3300, 0x22ac: 0x3300, 0x22ad: 0x3300, 0x22ae: 0x3300, 0x22af: 0x3300,
-	0x22b0: 0x3300, 0x22b1: 0x3300, 0x22b2: 0x3300, 0x22b3: 0x3300, 0x22b4: 0x3300, 0x22b5: 0x3300,
-	0x22b6: 0x3300, 0x22b8: 0x3300, 0x22b9: 0x3300, 0x22ba: 0x3300, 0x22bb: 0x3300,
-	0x22bc: 0x3300, 0x22be: 0x3300,
+	0x2280: 0x3300, 0x2281: 0x3300, 0x2282: 0x3300, 0x2283: 0x3300, 0x2284: 0x3300, 0x2285: 0x3300,
+	0x2286: 0x3300, 0x2287: 0x3300, 0x2288: 0x3300, 0x2289: 0x3300, 0x228a: 0x3300, 0x228b: 0x3300,
+	0x228c: 0x3300, 0x228d: 0x3300, 0x228e: 0x3300, 0x228f: 0x3300, 0x2290: 0x3300, 0x2291: 0x3300,
+	0x2292: 0x3300, 0x2293: 0x3300, 0x2294: 0x3300, 0x2295: 0x3300, 0x2296: 0x3300, 0x2297: 0x3300,
+	0x2298: 0x3300, 0x2299: 0x3300,
 	// Block 0x8b, offset 0x22c0
-	0x22c0: 0x3300, 0x22c1: 0x3300, 0x22c3: 0x3300, 0x22c4: 0x3300,
-	0x22c6: 0x3300, 0x22c7: 0x3300, 0x22c8: 0x3300, 0x22c9: 0x3300, 0x22ca: 0x3300, 0x22cb: 0x3300,
-	0x22cc: 0x3300, 0x22cd: 0x3300, 0x22ce: 0x3300, 0x22cf: 0x3000, 0x22d0: 0x3000, 0x22d1: 0x3000,
-	0x22d2: 0x3000, 0x22d3: 0x3000, 0x22d4: 0x3000, 0x22d5: 0x3000, 0x22d6: 0x3000, 0x22d7: 0x3000,
-	0x22d8: 0x3000, 0x22d9: 0x3000, 0x22da: 0x3000, 0x22db: 0x3000, 0x22dc: 0x3000, 0x22dd: 0x3000,
-	0x22de: 0x3000, 0x22df: 0x3000, 0x22e0: 0x3000, 0x22e1: 0x3000, 0x22e2: 0x3000, 0x22e3: 0x3000,
+	0x22c0: 0x3000, 0x22c1: 0x3000, 0x22c2: 0x3000, 0x22c3: 0x3000, 0x22c4: 0x3000, 0x22c5: 0x3000,
+	0x22c6: 0x3000,
+	0x22d3: 0x3000, 0x22d4: 0x3000, 0x22d5: 0x3000, 0x22d6: 0x3000, 0x22d7: 0x3000,
+	0x22dd: 0x3300,
+	0x22de: 0x001a, 0x22df: 0x3300, 0x22e0: 0x3000, 0x22e1: 0x3000, 0x22e2: 0x3000, 0x22e3: 0x3000,
 	0x22e4: 0x3000, 0x22e5: 0x3000, 0x22e6: 0x3000, 0x22e7: 0x3000, 0x22e8: 0x3000, 0x22e9: 0x3000,
-	0x22ea: 0x3000, 0x22eb: 0x3000, 0x22ec: 0x3000, 0x22ed: 0x3000, 0x22ee: 0x3000, 0x22ef: 0x3000,
-	0x22f0: 0x3000, 0x22f1: 0x3000, 0x22f2: 0x3000, 0x22f3: 0x3000, 0x22f4: 0x3000, 0x22f5: 0x3000,
-	0x22f6: 0x3000, 0x22f7: 0x3000, 0x22f8: 0x3000, 0x22f9: 0x3000, 0x22fa: 0x3000, 0x22fb: 0x3000,
-	0x22fc: 0x3000, 0x22fd: 0x3000, 0x22fe: 0x3000, 0x22ff: 0x3000,
+	0x22ea: 0x3300, 0x22eb: 0x3300, 0x22ec: 0x3300, 0x22ed: 0x3300, 0x22ee: 0x3300, 0x22ef: 0x3300,
+	0x22f0: 0x3300, 0x22f1: 0x3300, 0x22f2: 0x3300, 0x22f3: 0x3300, 0x22f4: 0x3300, 0x22f5: 0x3300,
+	0x22f6: 0x3300, 0x22f8: 0x3300, 0x22f9: 0x3300, 0x22fa: 0x3300, 0x22fb: 0x3300,
+	0x22fc: 0x3300, 0x22fe: 0x3300,
 	// Block 0x8c, offset 0x2300
-	0x2300: 0x3000, 0x2301: 0x3000, 0x2302: 0x3000, 0x2303: 0x3000, 0x2304: 0x3000, 0x2305: 0x3000,
-	0x2306: 0x3000, 0x2307: 0x3000, 0x2308: 0x3000, 0x2309: 0x3000, 0x230a: 0x3000, 0x230b: 0x3000,
-	0x230c: 0x3000, 0x230d: 0x3000, 0x230e: 0x3000, 0x230f: 0x3000, 0x2310: 0x3000, 0x2311: 0x3000,
+	0x2300: 0x3300, 0x2301: 0x3300, 0x2303: 0x3300, 0x2304: 0x3300,
+	0x2306: 0x3300, 0x2307: 0x3300, 0x2308: 0x3300, 0x2309: 0x3300, 0x230a: 0x3300, 0x230b: 0x3300,
+	0x230c: 0x3300, 0x230d: 0x3300, 0x230e: 0x3300, 0x230f: 0x3000, 0x2310: 0x3000, 0x2311: 0x3000,
 	0x2312: 0x3000, 0x2313: 0x3000, 0x2314: 0x3000, 0x2315: 0x3000, 0x2316: 0x3000, 0x2317: 0x3000,
 	0x2318: 0x3000, 0x2319: 0x3000, 0x231a: 0x3000, 0x231b: 0x3000, 0x231c: 0x3000, 0x231d: 0x3000,
 	0x231e: 0x3000, 0x231f: 0x3000, 0x2320: 0x3000, 0x2321: 0x3000, 0x2322: 0x3000, 0x2323: 0x3000,
 	0x2324: 0x3000, 0x2325: 0x3000, 0x2326: 0x3000, 0x2327: 0x3000, 0x2328: 0x3000, 0x2329: 0x3000,
 	0x232a: 0x3000, 0x232b: 0x3000, 0x232c: 0x3000, 0x232d: 0x3000, 0x232e: 0x3000, 0x232f: 0x3000,
-	0x2330: 0x3000, 0x2331: 0x3000,
+	0x2330: 0x3000, 0x2331: 0x3000, 0x2332: 0x3000, 0x2333: 0x3000, 0x2334: 0x3000, 0x2335: 0x3000,
+	0x2336: 0x3000, 0x2337: 0x3000, 0x2338: 0x3000, 0x2339: 0x3000, 0x233a: 0x3000, 0x233b: 0x3000,
+	0x233c: 0x3000, 0x233d: 0x3000, 0x233e: 0x3000, 0x233f: 0x3000,
 	// Block 0x8d, offset 0x2340
-	0x2353: 0x3000, 0x2354: 0x3000, 0x2355: 0x3000, 0x2356: 0x3000, 0x2357: 0x3000,
+	0x2340: 0x3000, 0x2341: 0x3000, 0x2342: 0x3000, 0x2343: 0x3000, 0x2344: 0x3000, 0x2345: 0x3000,
+	0x2346: 0x3000, 0x2347: 0x3000, 0x2348: 0x3000, 0x2349: 0x3000, 0x234a: 0x3000, 0x234b: 0x3000,
+	0x234c: 0x3000, 0x234d: 0x3000, 0x234e: 0x3000, 0x234f: 0x3000, 0x2350: 0x3000, 0x2351: 0x3000,
+	0x2352: 0x3000, 0x2353: 0x3000, 0x2354: 0x3000, 0x2355: 0x3000, 0x2356: 0x3000, 0x2357: 0x3000,
 	0x2358: 0x3000, 0x2359: 0x3000, 0x235a: 0x3000, 0x235b: 0x3000, 0x235c: 0x3000, 0x235d: 0x3000,
 	0x235e: 0x3000, 0x235f: 0x3000, 0x2360: 0x3000, 0x2361: 0x3000, 0x2362: 0x3000, 0x2363: 0x3000,
 	0x2364: 0x3000, 0x2365: 0x3000, 0x2366: 0x3000, 0x2367: 0x3000, 0x2368: 0x3000, 0x2369: 0x3000,
 	0x236a: 0x3000, 0x236b: 0x3000, 0x236c: 0x3000, 0x236d: 0x3000, 0x236e: 0x3000, 0x236f: 0x3000,
-	0x2370: 0x3000, 0x2371: 0x3000, 0x2372: 0x3000, 0x2373: 0x3000, 0x2374: 0x3000, 0x2375: 0x3000,
-	0x2376: 0x3000, 0x2377: 0x3000, 0x2378: 0x3000, 0x2379: 0x3000, 0x237a: 0x3000, 0x237b: 0x3000,
-	0x237c: 0x3000, 0x237d: 0x3000, 0x237e: 0x3000, 0x237f: 0x3000,
+	0x2370: 0x3000, 0x2371: 0x3000,
 	// Block 0x8e, offset 0x2380
-	0x2380: 0x3000, 0x2381: 0x3000, 0x2382: 0x3000, 0x2383: 0x3000, 0x2384: 0x3000, 0x2385: 0x3000,
-	0x2386: 0x3000, 0x2387: 0x3000, 0x2388: 0x3000, 0x2389: 0x3000, 0x238a: 0x3000, 0x238b: 0x3000,
-	0x238c: 0x3000, 0x238d: 0x3000, 0x238e: 0x3000, 0x238f: 0x3000, 0x2390: 0x3000, 0x2391: 0x3000,
-	0x2392: 0x3000, 0x2393: 0x3000, 0x2394: 0x3000, 0x2395: 0x3000, 0x2396: 0x3000, 0x2397: 0x3000,
+	0x2393: 0x3000, 0x2394: 0x3000, 0x2395: 0x3000, 0x2396: 0x3000, 0x2397: 0x3000,
 	0x2398: 0x3000, 0x2399: 0x3000, 0x239a: 0x3000, 0x239b: 0x3000, 0x239c: 0x3000, 0x239d: 0x3000,
 	0x239e: 0x3000, 0x239f: 0x3000, 0x23a0: 0x3000, 0x23a1: 0x3000, 0x23a2: 0x3000, 0x23a3: 0x3000,
 	0x23a4: 0x3000, 0x23a5: 0x3000, 0x23a6: 0x3000, 0x23a7: 0x3000, 0x23a8: 0x3000, 0x23a9: 0x3000,
 	0x23aa: 0x3000, 0x23ab: 0x3000, 0x23ac: 0x3000, 0x23ad: 0x3000, 0x23ae: 0x3000, 0x23af: 0x3000,
 	0x23b0: 0x3000, 0x23b1: 0x3000, 0x23b2: 0x3000, 0x23b3: 0x3000, 0x23b4: 0x3000, 0x23b5: 0x3000,
 	0x23b6: 0x3000, 0x23b7: 0x3000, 0x23b8: 0x3000, 0x23b9: 0x3000, 0x23ba: 0x3000, 0x23bb: 0x3000,
-	0x23bc: 0x3000, 0x23bd: 0x3000,
+	0x23bc: 0x3000, 0x23bd: 0x3000, 0x23be: 0x3000, 0x23bf: 0x3000,
 	// Block 0x8f, offset 0x23c0
-	0x23d0: 0x3000, 0x23d1: 0x3000,
+	0x23c0: 0x3000, 0x23c1: 0x3000, 0x23c2: 0x3000, 0x23c3: 0x3000, 0x23c4: 0x3000, 0x23c5: 0x3000,
+	0x23c6: 0x3000, 0x23c7: 0x3000, 0x23c8: 0x3000, 0x23c9: 0x3000, 0x23ca: 0x3000, 0x23cb: 0x3000,
+	0x23cc: 0x3000, 0x23cd: 0x3000, 0x23ce: 0x3000, 0x23cf: 0x3000, 0x23d0: 0x3000, 0x23d1: 0x3000,
 	0x23d2: 0x3000, 0x23d3: 0x3000, 0x23d4: 0x3000, 0x23d5: 0x3000, 0x23d6: 0x3000, 0x23d7: 0x3000,
 	0x23d8: 0x3000, 0x23d9: 0x3000, 0x23da: 0x3000, 0x23db: 0x3000, 0x23dc: 0x3000, 0x23dd: 0x3000,
 	0x23de: 0x3000, 0x23df: 0x3000, 0x23e0: 0x3000, 0x23e1: 0x3000, 0x23e2: 0x3000, 0x23e3: 0x3000,
@@ -6266,11 +6269,9 @@ var charInfoValues = [10944]uint16{
 	0x23ea: 0x3000, 0x23eb: 0x3000, 0x23ec: 0x3000, 0x23ed: 0x3000, 0x23ee: 0x3000, 0x23ef: 0x3000,
 	0x23f0: 0x3000, 0x23f1: 0x3000, 0x23f2: 0x3000, 0x23f3: 0x3000, 0x23f4: 0x3000, 0x23f5: 0x3000,
 	0x23f6: 0x3000, 0x23f7: 0x3000, 0x23f8: 0x3000, 0x23f9: 0x3000, 0x23fa: 0x3000, 0x23fb: 0x3000,
-	0x23fc: 0x3000, 0x23fd: 0x3000, 0x23fe: 0x3000, 0x23ff: 0x3000,
+	0x23fc: 0x3000, 0x23fd: 0x3000,
 	// Block 0x90, offset 0x2400
-	0x2400: 0x3000, 0x2401: 0x3000, 0x2402: 0x3000, 0x2403: 0x3000, 0x2404: 0x3000, 0x2405: 0x3000,
-	0x2406: 0x3000, 0x2407: 0x3000, 0x2408: 0x3000, 0x2409: 0x3000, 0x240a: 0x3000, 0x240b: 0x3000,
-	0x240c: 0x3000, 0x240d: 0x3000, 0x240e: 0x3000, 0x240f: 0x3000,
+	0x2410: 0x3000, 0x2411: 0x3000,
 	0x2412: 0x3000, 0x2413: 0x3000, 0x2414: 0x3000, 0x2415: 0x3000, 0x2416: 0x3000, 0x2417: 0x3000,
 	0x2418: 0x3000, 0x2419: 0x3000, 0x241a: 0x3000, 0x241b: 0x3000, 0x241c: 0x3000, 0x241d: 0x3000,
 	0x241e: 0x3000, 0x241f: 0x3000, 0x2420: 0x3000, 0x2421: 0x3000, 0x2422: 0x3000, 0x2423: 0x3000,
@@ -6281,45 +6282,45 @@ var charInfoValues = [10944]uint16{
 	0x243c: 0x3000, 0x243d: 0x3000, 0x243e: 0x3000, 0x243f: 0x3000,
 	// Block 0x91, offset 0x2440
 	0x2440: 0x3000, 0x2441: 0x3000, 0x2442: 0x3000, 0x2443: 0x3000, 0x2444: 0x3000, 0x2445: 0x3000,
-	0x2446: 0x3000, 0x2447: 0x3000,
+	0x2446: 0x3000, 0x2447: 0x3000, 0x2448: 0x3000, 0x2449: 0x3000, 0x244a: 0x3000, 0x244b: 0x3000,
+	0x244c: 0x3000, 0x244d: 0x3000, 0x244e: 0x3000, 0x244f: 0x3000,
+	0x2452: 0x3000, 0x2453: 0x3000, 0x2454: 0x3000, 0x2455: 0x3000, 0x2456: 0x3000, 0x2457: 0x3000,
+	0x2458: 0x3000, 0x2459: 0x3000, 0x245a: 0x3000, 0x245b: 0x3000, 0x245c: 0x3000, 0x245d: 0x3000,
+	0x245e: 0x3000, 0x245f: 0x3000, 0x2460: 0x3000, 0x2461: 0x3000, 0x2462: 0x3000, 0x2463: 0x3000,
+	0x2464: 0x3000, 0x2465: 0x3000, 0x2466: 0x3000, 0x2467: 0x3000, 0x2468: 0x3000, 0x2469: 0x3000,
+	0x246a: 0x3000, 0x246b: 0x3000, 0x246c: 0x3000, 0x246d: 0x3000, 0x246e: 0x3000, 0x246f: 0x3000,
 	0x2470: 0x3000, 0x2471: 0x3000, 0x2472: 0x3000, 0x2473: 0x3000, 0x2474: 0x3000, 0x2475: 0x3000,
 	0x2476: 0x3000, 0x2477: 0x3000, 0x2478: 0x3000, 0x2479: 0x3000, 0x247a: 0x3000, 0x247b: 0x3000,
-	0x247c: 0x3000,
+	0x247c: 0x3000, 0x247d: 0x3000, 0x247e: 0x3000, 0x247f: 0x3000,
 	// Block 0x92, offset 0x2480
-	0x2490: 0x3000, 0x2491: 0x3000,
-	0x2492: 0x3000, 0x2493: 0x3000, 0x2494: 0x3000, 0x2495: 0x3000, 0x2496: 0x3000, 0x2497: 0x3000,
-	0x2498: 0x3000, 0x2499: 0x3000,
-	0x24a0: 0x00e6, 0x24a1: 0x00e6, 0x24a2: 0x00e6, 0x24a3: 0x00e6,
-	0x24a4: 0x00e6, 0x24a5: 0x00e6, 0x24a6: 0x00e6,
+	0x2480: 0x3000, 0x2481: 0x3000, 0x2482: 0x3000, 0x2483: 0x3000, 0x2484: 0x3000, 0x2485: 0x3000,
+	0x2486: 0x3000, 0x2487: 0x3000,
 	0x24b0: 0x3000, 0x24b1: 0x3000, 0x24b2: 0x3000, 0x24b3: 0x3000, 0x24b4: 0x3000, 0x24b5: 0x3000,
 	0x24b6: 0x3000, 0x24b7: 0x3000, 0x24b8: 0x3000, 0x24b9: 0x3000, 0x24ba: 0x3000, 0x24bb: 0x3000,
-	0x24bc: 0x3000, 0x24bd: 0x3000, 0x24be: 0x3000, 0x24bf: 0x3000,
+	0x24bc: 0x3000,
 	// Block 0x93, offset 0x24c0
-	0x24c0: 0x3000, 0x24c1: 0x3000, 0x24c2: 0x3000, 0x24c3: 0x3000, 0x24c4: 0x3000,
-	0x24c7: 0x3000, 0x24c8: 0x3000, 0x24c9: 0x3000, 0x24ca: 0x3000, 0x24cb: 0x3000,
-	0x24cc: 0x3000, 0x24cd: 0x3000, 0x24ce: 0x3000, 0x24cf: 0x3000, 0x24d0: 0x3000, 0x24d1: 0x3000,
-	0x24d2: 0x3000, 0x24d4: 0x3000, 0x24d5: 0x3000, 0x24d6: 0x3000, 0x24d7: 0x3000,
-	0x24d8: 0x3000, 0x24d9: 0x3000, 0x24da: 0x3000, 0x24db: 0x3000, 0x24dc: 0x3000, 0x24dd: 0x3000,
-	0x24de: 0x3000, 0x24df: 0x3000, 0x24e0: 0x3000, 0x24e1: 0x3000, 0x24e2: 0x3000, 0x24e3: 0x3000,
-	0x24e4: 0x3000, 0x24e5: 0x3000, 0x24e6: 0x3000, 0x24e8: 0x3000, 0x24e9: 0x3000,
-	0x24ea: 0x3000, 0x24eb: 0x3000,
-	0x24f0: 0x3000, 0x24f1: 0x3000, 0x24f2: 0x3000, 0x24f4: 0x3000,
+	0x24d0: 0x3000, 0x24d1: 0x3000,
+	0x24d2: 0x3000, 0x24d3: 0x3000, 0x24d4: 0x3000, 0x24d5: 0x3000, 0x24d6: 0x3000, 0x24d7: 0x3000,
+	0x24d8: 0x3000, 0x24d9: 0x3000,
+	0x24e0: 0x00e6, 0x24e1: 0x00e6, 0x24e2: 0x00e6, 0x24e3: 0x00e6,
+	0x24e4: 0x00e6, 0x24e5: 0x00e6, 0x24e6: 0x00e6,
+	0x24f0: 0x3000, 0x24f1: 0x3000, 0x24f2: 0x3000, 0x24f3: 0x3000, 0x24f4: 0x3000, 0x24f5: 0x3000,
 	0x24f6: 0x3000, 0x24f7: 0x3000, 0x24f8: 0x3000, 0x24f9: 0x3000, 0x24fa: 0x3000, 0x24fb: 0x3000,
 	0x24fc: 0x3000, 0x24fd: 0x3000, 0x24fe: 0x3000, 0x24ff: 0x3000,
 	// Block 0x94, offset 0x2500
-	0x2500: 0x3000, 0x2501: 0x3000, 0x2502: 0x3000, 0x2503: 0x3000, 0x2504: 0x3000, 0x2505: 0x3000,
-	0x2506: 0x3000, 0x2507: 0x3000, 0x2508: 0x3000, 0x2509: 0x3000, 0x250a: 0x3000, 0x250b: 0x3000,
+	0x2500: 0x3000, 0x2501: 0x3000, 0x2502: 0x3000, 0x2503: 0x3000, 0x2504: 0x3000,
+	0x2507: 0x3000, 0x2508: 0x3000, 0x2509: 0x3000, 0x250a: 0x3000, 0x250b: 0x3000,
 	0x250c: 0x3000, 0x250d: 0x3000, 0x250e: 0x3000, 0x250f: 0x3000, 0x2510: 0x3000, 0x2511: 0x3000,
-	0x2512: 0x3000, 0x2513: 0x3000, 0x2514: 0x3000, 0x2515: 0x3000, 0x2516: 0x3000, 0x2517: 0x3000,
+	0x2512: 0x3000, 0x2514: 0x3000, 0x2515: 0x3000, 0x2516: 0x3000, 0x2517: 0x3000,
 	0x2518: 0x3000, 0x2519: 0x3000, 0x251a: 0x3000, 0x251b: 0x3000, 0x251c: 0x3000, 0x251d: 0x3000,
 	0x251e: 0x3000, 0x251f: 0x3000, 0x2520: 0x3000, 0x2521: 0x3000, 0x2522: 0x3000, 0x2523: 0x3000,
-	0x2524: 0x3000, 0x2525: 0x3000, 0x2526: 0x3000, 0x2527: 0x3000, 0x2528: 0x3000, 0x2529: 0x3000,
-	0x252a: 0x3000, 0x252b: 0x3000, 0x252c: 0x3000, 0x252d: 0x3000, 0x252e: 0x3000, 0x252f: 0x3000,
-	0x2530: 0x3000, 0x2531: 0x3000, 0x2532: 0x3000, 0x2533: 0x3000, 0x2534: 0x3000, 0x2535: 0x3000,
+	0x2524: 0x3000, 0x2525: 0x3000, 0x2526: 0x3000, 0x2528: 0x3000, 0x2529: 0x3000,
+	0x252a: 0x3000, 0x252b: 0x3000,
+	0x2530: 0x3000, 0x2531: 0x3000, 0x2532: 0x3000, 0x2534: 0x3000,
 	0x2536: 0x3000, 0x2537: 0x3000, 0x2538: 0x3000, 0x2539: 0x3000, 0x253a: 0x3000, 0x253b: 0x3000,
-	0x253c: 0x3000,
+	0x253c: 0x3000, 0x253d: 0x3000, 0x253e: 0x3000, 0x253f: 0x3000,
 	// Block 0x95, offset 0x2540
-	0x2541: 0x3000, 0x2542: 0x3000, 0x2543: 0x3000, 0x2544: 0x3000, 0x2545: 0x3000,
+	0x2540: 0x3000, 0x2541: 0x3000, 0x2542: 0x3000, 0x2543: 0x3000, 0x2544: 0x3000, 0x2545: 0x3000,
 	0x2546: 0x3000, 0x2547: 0x3000, 0x2548: 0x3000, 0x2549: 0x3000, 0x254a: 0x3000, 0x254b: 0x3000,
 	0x254c: 0x3000, 0x254d: 0x3000, 0x254e: 0x3000, 0x254f: 0x3000, 0x2550: 0x3000, 0x2551: 0x3000,
 	0x2552: 0x3000, 0x2553: 0x3000, 0x2554: 0x3000, 0x2555: 0x3000, 0x2556: 0x3000, 0x2557: 0x3000,
@@ -6329,112 +6330,112 @@ var charInfoValues = [10944]uint16{
 	0x256a: 0x3000, 0x256b: 0x3000, 0x256c: 0x3000, 0x256d: 0x3000, 0x256e: 0x3000, 0x256f: 0x3000,
 	0x2570: 0x3000, 0x2571: 0x3000, 0x2572: 0x3000, 0x2573: 0x3000, 0x2574: 0x3000, 0x2575: 0x3000,
 	0x2576: 0x3000, 0x2577: 0x3000, 0x2578: 0x3000, 0x2579: 0x3000, 0x257a: 0x3000, 0x257b: 0x3000,
-	0x257c: 0x3000, 0x257d: 0x3000, 0x257e: 0x3000, 0x257f: 0x3000,
+	0x257c: 0x3000,
 	// Block 0x96, offset 0x2580
-	0x2582: 0x3000, 0x2583: 0x3000, 0x2584: 0x3000, 0x2585: 0x3000,
-	0x2586: 0x3000, 0x2587: 0x3000, 0x258a: 0x3000, 0x258b: 0x3000,
-	0x258c: 0x3000, 0x258d: 0x3000, 0x258e: 0x3000, 0x258f: 0x3000,
+	0x2581: 0x3000, 0x2582: 0x3000, 0x2583: 0x3000, 0x2584: 0x3000, 0x2585: 0x3000,
+	0x2586: 0x3000, 0x2587: 0x3000, 0x2588: 0x3000, 0x2589: 0x3000, 0x258a: 0x3000, 0x258b: 0x3000,
+	0x258c: 0x3000, 0x258d: 0x3000, 0x258e: 0x3000, 0x258f: 0x3000, 0x2590: 0x3000, 0x2591: 0x3000,
 	0x2592: 0x3000, 0x2593: 0x3000, 0x2594: 0x3000, 0x2595: 0x3000, 0x2596: 0x3000, 0x2597: 0x3000,
-	0x259a: 0x3000, 0x259b: 0x3000, 0x259c: 0x3000,
-	0x25a0: 0x3000, 0x25a1: 0x3000, 0x25a2: 0x3000, 0x25a3: 0x3000,
-	0x25a4: 0x3000, 0x25a5: 0x3000, 0x25a6: 0x3000, 0x25a8: 0x3000, 0x25a9: 0x3000,
-	0x25aa: 0x3000, 0x25ab: 0x3000, 0x25ac: 0x3000, 0x25ad: 0x3000, 0x25ae: 0x3000,
+	0x2598: 0x3000, 0x2599: 0x3000, 0x259a: 0x3000, 0x259b: 0x3000, 0x259c: 0x3000, 0x259d: 0x3000,
+	0x259e: 0x3000, 0x259f: 0x3000, 0x25a0: 0x3000, 0x25a1: 0x3000, 0x25a2: 0x3000, 0x25a3: 0x3000,
+	0x25a4: 0x3000, 0x25a5: 0x3000, 0x25a6: 0x3000, 0x25a7: 0x3000, 0x25a8: 0x3000, 0x25a9: 0x3000,
+	0x25aa: 0x3000, 0x25ab: 0x3000, 0x25ac: 0x3000, 0x25ad: 0x3000, 0x25ae: 0x3000, 0x25af: 0x3000,
+	0x25b0: 0x3000, 0x25b1: 0x3000, 0x25b2: 0x3000, 0x25b3: 0x3000, 0x25b4: 0x3000, 0x25b5: 0x3000,
+	0x25b6: 0x3000, 0x25b7: 0x3000, 0x25b8: 0x3000, 0x25b9: 0x3000, 0x25ba: 0x3000, 0x25bb: 0x3000,
+	0x25bc: 0x3000, 0x25bd: 0x3000, 0x25be: 0x3000, 0x25bf: 0x3000,
 	// Block 0x97, offset 0x25c0
-	0x25fd: 0x00dc,
+	0x25c2: 0x3000, 0x25c3: 0x3000, 0x25c4: 0x3000, 0x25c5: 0x3000,
+	0x25c6: 0x3000, 0x25c7: 0x3000, 0x25ca: 0x3000, 0x25cb: 0x3000,
+	0x25cc: 0x3000, 0x25cd: 0x3000, 0x25ce: 0x3000, 0x25cf: 0x3000,
+	0x25d2: 0x3000, 0x25d3: 0x3000, 0x25d4: 0x3000, 0x25d5: 0x3000, 0x25d6: 0x3000, 0x25d7: 0x3000,
+	0x25da: 0x3000, 0x25db: 0x3000, 0x25dc: 0x3000,
+	0x25e0: 0x3000, 0x25e1: 0x3000, 0x25e2: 0x3000, 0x25e3: 0x3000,
+	0x25e4: 0x3000, 0x25e5: 0x3000, 0x25e6: 0x3000, 0x25e8: 0x3000, 0x25e9: 0x3000,
+	0x25ea: 0x3000, 0x25eb: 0x3000, 0x25ec: 0x3000, 0x25ed: 0x3000, 0x25ee: 0x3000,
 	// Block 0x98, offset 0x2600
-	0x260d: 0x00dc, 0x260f: 0x00e6,
-	0x2638: 0x00e6, 0x2639: 0x0001, 0x263a: 0x00dc,
-	0x263f: 0x0009,
+	0x263d: 0x00dc,
 	// Block 0x99, offset 0x2640
-	0x2659: 0x8800, 0x265a: 0x1100, 0x265b: 0x8800, 0x265c: 0x1100,
-	0x2665: 0x8800,
-	0x266b: 0x1100,
-	0x2679: 0x0009, 0x267a: 0x6607,
+	0x264d: 0x00dc, 0x264f: 0x00e6,
+	0x2678: 0x00e6, 0x2679: 0x0001, 0x267a: 0x00dc,
+	0x267f: 0x0009,
 	// Block 0x9a, offset 0x2680
-	0x269e: 0x3300, 0x269f: 0x3300, 0x26a0: 0x3300, 0x26a1: 0x3300, 0x26a2: 0x3300, 0x26a3: 0x3300,
-	0x26a4: 0x3300, 0x26a5: 0x00d8, 0x26a6: 0x00d8, 0x26a7: 0x0001, 0x26a8: 0x0001, 0x26a9: 0x0001,
-	0x26ad: 0x00e2, 0x26ae: 0x00d8, 0x26af: 0x00d8,
-	0x26b0: 0x00d8, 0x26b1: 0x00d8, 0x26b2: 0x00d8,
-	0x26bb: 0x00dc,
-	0x26bc: 0x00dc, 0x26bd: 0x00dc, 0x26be: 0x00dc, 0x26bf: 0x00dc,
+	0x2699: 0x8800, 0x269a: 0x1100, 0x269b: 0x8800, 0x269c: 0x1100,
+	0x26a5: 0x8800,
+	0x26ab: 0x1100,
+	0x26b9: 0x0009, 0x26ba: 0x6607,
 	// Block 0x9b, offset 0x26c0
-	0x26c0: 0x00dc, 0x26c1: 0x00dc, 0x26c2: 0x00dc, 0x26c5: 0x00e6,
-	0x26c6: 0x00e6, 0x26c7: 0x00e6, 0x26c8: 0x00e6, 0x26c9: 0x00e6, 0x26ca: 0x00dc, 0x26cb: 0x00dc,
-	0x26ea: 0x00e6, 0x26eb: 0x00e6, 0x26ec: 0x00e6, 0x26ed: 0x00e6,
-	0x26fb: 0x3300,
-	0x26fc: 0x3300, 0x26fd: 0x3300, 0x26fe: 0x3300, 0x26ff: 0x3300,
+	0x26de: 0x3300, 0x26df: 0x3300, 0x26e0: 0x3300, 0x26e1: 0x3300, 0x26e2: 0x3300, 0x26e3: 0x3300,
+	0x26e4: 0x3300, 0x26e5: 0x00d8, 0x26e6: 0x00d8, 0x26e7: 0x0001, 0x26e8: 0x0001, 0x26e9: 0x0001,
+	0x26ed: 0x00e2, 0x26ee: 0x00d8, 0x26ef: 0x00d8,
+	0x26f0: 0x00d8, 0x26f1: 0x00d8, 0x26f2: 0x00d8,
+	0x26fb: 0x00dc,
+	0x26fc: 0x00dc, 0x26fd: 0x00dc, 0x26fe: 0x00dc, 0x26ff: 0x00dc,
 	// Block 0x9c, offset 0x2700
-	0x2700: 0x3300,
+	0x2700: 0x00dc, 0x2701: 0x00dc, 0x2702: 0x00dc, 0x2705: 0x00e6,
+	0x2706: 0x00e6, 0x2707: 0x00e6, 0x2708: 0x00e6, 0x2709: 0x00e6, 0x270a: 0x00dc, 0x270b: 0x00dc,
+	0x272a: 0x00e6, 0x272b: 0x00e6, 0x272c: 0x00e6, 0x272d: 0x00e6,
+	0x273b: 0x3300,
+	0x273c: 0x3300, 0x273d: 0x3300, 0x273e: 0x3300, 0x273f: 0x3300,
 	// Block 0x9d, offset 0x2740
-	0x2742: 0x00e6, 0x2743: 0x00e6, 0x2744: 0x00e6,
+	0x2740: 0x3300,
 	// Block 0x9e, offset 0x2780
-	0x2780: 0x3000, 0x2781: 0x3000, 0x2782: 0x3000, 0x2783: 0x3000, 0x2784: 0x3000, 0x2785: 0x3000,
-	0x2786: 0x3000, 0x2787: 0x3000, 0x2788: 0x3000, 0x2789: 0x3000, 0x278a: 0x3000, 0x278b: 0x3000,
-	0x278c: 0x3000, 0x278d: 0x3000, 0x278e: 0x3000, 0x278f: 0x3000, 0x2790: 0x3000, 0x2791: 0x3000,
-	0x2792: 0x3000, 0x2793: 0x3000, 0x2794: 0x3000, 0x2796: 0x3000, 0x2797: 0x3000,
-	0x2798: 0x3000, 0x2799: 0x3000, 0x279a: 0x3000, 0x279b: 0x3000, 0x279c: 0x3000, 0x279d: 0x3000,
-	0x279e: 0x3000, 0x279f: 0x3000, 0x27a0: 0x3000, 0x27a1: 0x3000, 0x27a2: 0x3000, 0x27a3: 0x3000,
-	0x27a4: 0x3000, 0x27a5: 0x3000, 0x27a6: 0x3000, 0x27a7: 0x3000, 0x27a8: 0x3000, 0x27a9: 0x3000,
-	0x27aa: 0x3000, 0x27ab: 0x3000, 0x27ac: 0x3000, 0x27ad: 0x3000, 0x27ae: 0x3000, 0x27af: 0x3000,
-	0x27b0: 0x3000, 0x27b1: 0x3000, 0x27b2: 0x3000, 0x27b3: 0x3000, 0x27b4: 0x3000, 0x27b5: 0x3000,
-	0x27b6: 0x3000, 0x27b7: 0x3000, 0x27b8: 0x3000, 0x27b9: 0x3000, 0x27ba: 0x3000, 0x27bb: 0x3000,
-	0x27bc: 0x3000, 0x27bd: 0x3000, 0x27be: 0x3000, 0x27bf: 0x3000,
+	0x2782: 0x00e6, 0x2783: 0x00e6, 0x2784: 0x00e6,
 	// Block 0x9f, offset 0x27c0
 	0x27c0: 0x3000, 0x27c1: 0x3000, 0x27c2: 0x3000, 0x27c3: 0x3000, 0x27c4: 0x3000, 0x27c5: 0x3000,
 	0x27c6: 0x3000, 0x27c7: 0x3000, 0x27c8: 0x3000, 0x27c9: 0x3000, 0x27ca: 0x3000, 0x27cb: 0x3000,
 	0x27cc: 0x3000, 0x27cd: 0x3000, 0x27ce: 0x3000, 0x27cf: 0x3000, 0x27d0: 0x3000, 0x27d1: 0x3000,
-	0x27d2: 0x3000, 0x27d3: 0x3000, 0x27d4: 0x3000, 0x27d5: 0x3000, 0x27d6: 0x3000, 0x27d7: 0x3000,
-	0x27d8: 0x3000, 0x27d9: 0x3000, 0x27da: 0x3000, 0x27db: 0x3000, 0x27dc: 0x3000,
-	0x27de: 0x3000, 0x27df: 0x3000, 0x27e2: 0x3000,
-	0x27e5: 0x3000, 0x27e6: 0x3000, 0x27e9: 0x3000,
-	0x27ea: 0x3000, 0x27eb: 0x3000, 0x27ec: 0x3000, 0x27ee: 0x3000, 0x27ef: 0x3000,
+	0x27d2: 0x3000, 0x27d3: 0x3000, 0x27d4: 0x3000, 0x27d6: 0x3000, 0x27d7: 0x3000,
+	0x27d8: 0x3000, 0x27d9: 0x3000, 0x27da: 0x3000, 0x27db: 0x3000, 0x27dc: 0x3000, 0x27dd: 0x3000,
+	0x27de: 0x3000, 0x27df: 0x3000, 0x27e0: 0x3000, 0x27e1: 0x3000, 0x27e2: 0x3000, 0x27e3: 0x3000,
+	0x27e4: 0x3000, 0x27e5: 0x3000, 0x27e6: 0x3000, 0x27e7: 0x3000, 0x27e8: 0x3000, 0x27e9: 0x3000,
+	0x27ea: 0x3000, 0x27eb: 0x3000, 0x27ec: 0x3000, 0x27ed: 0x3000, 0x27ee: 0x3000, 0x27ef: 0x3000,
 	0x27f0: 0x3000, 0x27f1: 0x3000, 0x27f2: 0x3000, 0x27f3: 0x3000, 0x27f4: 0x3000, 0x27f5: 0x3000,
-	0x27f6: 0x3000, 0x27f7: 0x3000, 0x27f8: 0x3000, 0x27f9: 0x3000, 0x27fb: 0x3000,
-	0x27fd: 0x3000, 0x27fe: 0x3000, 0x27ff: 0x3000,
+	0x27f6: 0x3000, 0x27f7: 0x3000, 0x27f8: 0x3000, 0x27f9: 0x3000, 0x27fa: 0x3000, 0x27fb: 0x3000,
+	0x27fc: 0x3000, 0x27fd: 0x3000, 0x27fe: 0x3000, 0x27ff: 0x3000,
 	// Block 0xa0, offset 0x2800
-	0x2800: 0x3000, 0x2801: 0x3000, 0x2802: 0x3000, 0x2803: 0x3000, 0x2805: 0x3000,
+	0x2800: 0x3000, 0x2801: 0x3000, 0x2802: 0x3000, 0x2803: 0x3000, 0x2804: 0x3000, 0x2805: 0x3000,
 	0x2806: 0x3000, 0x2807: 0x3000, 0x2808: 0x3000, 0x2809: 0x3000, 0x280a: 0x3000, 0x280b: 0x3000,
 	0x280c: 0x3000, 0x280d: 0x3000, 0x280e: 0x3000, 0x280f: 0x3000, 0x2810: 0x3000, 0x2811: 0x3000,
 	0x2812: 0x3000, 0x2813: 0x3000, 0x2814: 0x3000, 0x2815: 0x3000, 0x2816: 0x3000, 0x2817: 0x3000,
-	0x2818: 0x3000, 0x2819: 0x3000, 0x281a: 0x3000, 0x281b: 0x3000, 0x281c: 0x3000, 0x281d: 0x3000,
-	0x281e: 0x3000, 0x281f: 0x3000, 0x2820: 0x3000, 0x2821: 0x3000, 0x2822: 0x3000, 0x2823: 0x3000,
-	0x2824: 0x3000, 0x2825: 0x3000, 0x2826: 0x3000, 0x2827: 0x3000, 0x2828: 0x3000, 0x2829: 0x3000,
-	0x282a: 0x3000, 0x282b: 0x3000, 0x282c: 0x3000, 0x282d: 0x3000, 0x282e: 0x3000, 0x282f: 0x3000,
+	0x2818: 0x3000, 0x2819: 0x3000, 0x281a: 0x3000, 0x281b: 0x3000, 0x281c: 0x3000,
+	0x281e: 0x3000, 0x281f: 0x3000, 0x2822: 0x3000,
+	0x2825: 0x3000, 0x2826: 0x3000, 0x2829: 0x3000,
+	0x282a: 0x3000, 0x282b: 0x3000, 0x282c: 0x3000, 0x282e: 0x3000, 0x282f: 0x3000,
 	0x2830: 0x3000, 0x2831: 0x3000, 0x2832: 0x3000, 0x2833: 0x3000, 0x2834: 0x3000, 0x2835: 0x3000,
-	0x2836: 0x3000, 0x2837: 0x3000, 0x2838: 0x3000, 0x2839: 0x3000, 0x283a: 0x3000, 0x283b: 0x3000,
-	0x283c: 0x3000, 0x283d: 0x3000, 0x283e: 0x3000, 0x283f: 0x3000,
+	0x2836: 0x3000, 0x2837: 0x3000, 0x2838: 0x3000, 0x2839: 0x3000, 0x283b: 0x3000,
+	0x283d: 0x3000, 0x283e: 0x3000, 0x283f: 0x3000,
 	// Block 0xa1, offset 0x2840
-	0x2840: 0x3000, 0x2841: 0x3000, 0x2842: 0x3000, 0x2843: 0x3000, 0x2844: 0x3000, 0x2845: 0x3000,
-	0x2847: 0x3000, 0x2848: 0x3000, 0x2849: 0x3000, 0x284a: 0x3000,
-	0x284d: 0x3000, 0x284e: 0x3000, 0x284f: 0x3000, 0x2850: 0x3000, 0x2851: 0x3000,
-	0x2852: 0x3000, 0x2853: 0x3000, 0x2854: 0x3000, 0x2856: 0x3000, 0x2857: 0x3000,
-	0x2858: 0x3000, 0x2859: 0x3000, 0x285a: 0x3000, 0x285b: 0x3000, 0x285c: 0x3000,
+	0x2840: 0x3000, 0x2841: 0x3000, 0x2842: 0x3000, 0x2843: 0x3000, 0x2845: 0x3000,
+	0x2846: 0x3000, 0x2847: 0x3000, 0x2848: 0x3000, 0x2849: 0x3000, 0x284a: 0x3000, 0x284b: 0x3000,
+	0x284c: 0x3000, 0x284d: 0x3000, 0x284e: 0x3000, 0x284f: 0x3000, 0x2850: 0x3000, 0x2851: 0x3000,
+	0x2852: 0x3000, 0x2853: 0x3000, 0x2854: 0x3000, 0x2855: 0x3000, 0x2856: 0x3000, 0x2857: 0x3000,
+	0x2858: 0x3000, 0x2859: 0x3000, 0x285a: 0x3000, 0x285b: 0x3000, 0x285c: 0x3000, 0x285d: 0x3000,
 	0x285e: 0x3000, 0x285f: 0x3000, 0x2860: 0x3000, 0x2861: 0x3000, 0x2862: 0x3000, 0x2863: 0x3000,
 	0x2864: 0x3000, 0x2865: 0x3000, 0x2866: 0x3000, 0x2867: 0x3000, 0x2868: 0x3000, 0x2869: 0x3000,
 	0x286a: 0x3000, 0x286b: 0x3000, 0x286c: 0x3000, 0x286d: 0x3000, 0x286e: 0x3000, 0x286f: 0x3000,
 	0x2870: 0x3000, 0x2871: 0x3000, 0x2872: 0x3000, 0x2873: 0x3000, 0x2874: 0x3000, 0x2875: 0x3000,
-	0x2876: 0x3000, 0x2877: 0x3000, 0x2878: 0x3000, 0x2879: 0x3000, 0x287b: 0x3000,
-	0x287c: 0x3000, 0x287d: 0x3000, 0x287e: 0x3000,
+	0x2876: 0x3000, 0x2877: 0x3000, 0x2878: 0x3000, 0x2879: 0x3000, 0x287a: 0x3000, 0x287b: 0x3000,
+	0x287c: 0x3000, 0x287d: 0x3000, 0x287e: 0x3000, 0x287f: 0x3000,
 	// Block 0xa2, offset 0x2880
-	0x2880: 0x3000, 0x2881: 0x3000, 0x2882: 0x3000, 0x2883: 0x3000, 0x2884: 0x3000,
-	0x2886: 0x3000, 0x288a: 0x3000, 0x288b: 0x3000,
-	0x288c: 0x3000, 0x288d: 0x3000, 0x288e: 0x3000, 0x288f: 0x3000, 0x2890: 0x3000,
-	0x2892: 0x3000, 0x2893: 0x3000, 0x2894: 0x3000, 0x2895: 0x3000, 0x2896: 0x3000, 0x2897: 0x3000,
-	0x2898: 0x3000, 0x2899: 0x3000, 0x289a: 0x3000, 0x289b: 0x3000, 0x289c: 0x3000, 0x289d: 0x3000,
+	0x2880: 0x3000, 0x2881: 0x3000, 0x2882: 0x3000, 0x2883: 0x3000, 0x2884: 0x3000, 0x2885: 0x3000,
+	0x2887: 0x3000, 0x2888: 0x3000, 0x2889: 0x3000, 0x288a: 0x3000,
+	0x288d: 0x3000, 0x288e: 0x3000, 0x288f: 0x3000, 0x2890: 0x3000, 0x2891: 0x3000,
+	0x2892: 0x3000, 0x2893: 0x3000, 0x2894: 0x3000, 0x2896: 0x3000, 0x2897: 0x3000,
+	0x2898: 0x3000, 0x2899: 0x3000, 0x289a: 0x3000, 0x289b: 0x3000, 0x289c: 0x3000,
 	0x289e: 0x3000, 0x289f: 0x3000, 0x28a0: 0x3000, 0x28a1: 0x3000, 0x28a2: 0x3000, 0x28a3: 0x3000,
 	0x28a4: 0x3000, 0x28a5: 0x3000, 0x28a6: 0x3000, 0x28a7: 0x3000, 0x28a8: 0x3000, 0x28a9: 0x3000,
 	0x28aa: 0x3000, 0x28ab: 0x3000, 0x28ac: 0x3000, 0x28ad: 0x3000, 0x28ae: 0x3000, 0x28af: 0x3000,
 	0x28b0: 0x3000, 0x28b1: 0x3000, 0x28b2: 0x3000, 0x28b3: 0x3000, 0x28b4: 0x3000, 0x28b5: 0x3000,
-	0x28b6: 0x3000, 0x28b7: 0x3000, 0x28b8: 0x3000, 0x28b9: 0x3000, 0x28ba: 0x3000, 0x28bb: 0x3000,
-	0x28bc: 0x3000, 0x28bd: 0x3000, 0x28be: 0x3000, 0x28bf: 0x3000,
+	0x28b6: 0x3000, 0x28b7: 0x3000, 0x28b8: 0x3000, 0x28b9: 0x3000, 0x28bb: 0x3000,
+	0x28bc: 0x3000, 0x28bd: 0x3000, 0x28be: 0x3000,
 	// Block 0xa3, offset 0x28c0
-	0x28c0: 0x3000, 0x28c1: 0x3000, 0x28c2: 0x3000, 0x28c3: 0x3000, 0x28c4: 0x3000, 0x28c5: 0x3000,
-	0x28c6: 0x3000, 0x28c7: 0x3000, 0x28c8: 0x3000, 0x28c9: 0x3000, 0x28ca: 0x3000, 0x28cb: 0x3000,
-	0x28cc: 0x3000, 0x28cd: 0x3000, 0x28ce: 0x3000, 0x28cf: 0x3000, 0x28d0: 0x3000, 0x28d1: 0x3000,
+	0x28c0: 0x3000, 0x28c1: 0x3000, 0x28c2: 0x3000, 0x28c3: 0x3000, 0x28c4: 0x3000,
+	0x28c6: 0x3000, 0x28ca: 0x3000, 0x28cb: 0x3000,
+	0x28cc: 0x3000, 0x28cd: 0x3000, 0x28ce: 0x3000, 0x28cf: 0x3000, 0x28d0: 0x3000,
 	0x28d2: 0x3000, 0x28d3: 0x3000, 0x28d4: 0x3000, 0x28d5: 0x3000, 0x28d6: 0x3000, 0x28d7: 0x3000,
 	0x28d8: 0x3000, 0x28d9: 0x3000, 0x28da: 0x3000, 0x28db: 0x3000, 0x28dc: 0x3000, 0x28dd: 0x3000,
 	0x28de: 0x3000, 0x28df: 0x3000, 0x28e0: 0x3000, 0x28e1: 0x3000, 0x28e2: 0x3000, 0x28e3: 0x3000,
-	0x28e4: 0x3000, 0x28e5: 0x3000, 0x28e8: 0x3000, 0x28e9: 0x3000,
+	0x28e4: 0x3000, 0x28e5: 0x3000, 0x28e6: 0x3000, 0x28e7: 0x3000, 0x28e8: 0x3000, 0x28e9: 0x3000,
 	0x28ea: 0x3000, 0x28eb: 0x3000, 0x28ec: 0x3000, 0x28ed: 0x3000, 0x28ee: 0x3000, 0x28ef: 0x3000,
 	0x28f0: 0x3000, 0x28f1: 0x3000, 0x28f2: 0x3000, 0x28f3: 0x3000, 0x28f4: 0x3000, 0x28f5: 0x3000,
 	0x28f6: 0x3000, 0x28f7: 0x3000, 0x28f8: 0x3000, 0x28f9: 0x3000, 0x28fa: 0x3000, 0x28fb: 0x3000,
@@ -6442,53 +6443,65 @@ var charInfoValues = [10944]uint16{
 	// Block 0xa4, offset 0x2900
 	0x2900: 0x3000, 0x2901: 0x3000, 0x2902: 0x3000, 0x2903: 0x3000, 0x2904: 0x3000, 0x2905: 0x3000,
 	0x2906: 0x3000, 0x2907: 0x3000, 0x2908: 0x3000, 0x2909: 0x3000, 0x290a: 0x3000, 0x290b: 0x3000,
-	0x290e: 0x3000, 0x290f: 0x3000, 0x2910: 0x3000, 0x2911: 0x3000,
+	0x290c: 0x3000, 0x290d: 0x3000, 0x290e: 0x3000, 0x290f: 0x3000, 0x2910: 0x3000, 0x2911: 0x3000,
 	0x2912: 0x3000, 0x2913: 0x3000, 0x2914: 0x3000, 0x2915: 0x3000, 0x2916: 0x3000, 0x2917: 0x3000,
 	0x2918: 0x3000, 0x2919: 0x3000, 0x291a: 0x3000, 0x291b: 0x3000, 0x291c: 0x3000, 0x291d: 0x3000,
 	0x291e: 0x3000, 0x291f: 0x3000, 0x2920: 0x3000, 0x2921: 0x3000, 0x2922: 0x3000, 0x2923: 0x3000,
-	0x2924: 0x3000, 0x2925: 0x3000, 0x2926: 0x3000, 0x2927: 0x3000, 0x2928: 0x3000, 0x2929: 0x3000,
+	0x2924: 0x3000, 0x2925: 0x3000, 0x2928: 0x3000, 0x2929: 0x3000,
 	0x292a: 0x3000, 0x292b: 0x3000, 0x292c: 0x3000, 0x292d: 0x3000, 0x292e: 0x3000, 0x292f: 0x3000,
 	0x2930: 0x3000, 0x2931: 0x3000, 0x2932: 0x3000, 0x2933: 0x3000, 0x2934: 0x3000, 0x2935: 0x3000,
 	0x2936: 0x3000, 0x2937: 0x3000, 0x2938: 0x3000, 0x2939: 0x3000, 0x293a: 0x3000, 0x293b: 0x3000,
 	0x293c: 0x3000, 0x293d: 0x3000, 0x293e: 0x3000, 0x293f: 0x3000,
 	// Block 0xa5, offset 0x2940
 	0x2940: 0x3000, 0x2941: 0x3000, 0x2942: 0x3000, 0x2943: 0x3000, 0x2944: 0x3000, 0x2945: 0x3000,
-	0x2946: 0x3000, 0x2947: 0x3000, 0x2948: 0x3000, 0x2949: 0x3000, 0x294a: 0x3000,
-	0x2950: 0x3000, 0x2951: 0x3000,
+	0x2946: 0x3000, 0x2947: 0x3000, 0x2948: 0x3000, 0x2949: 0x3000, 0x294a: 0x3000, 0x294b: 0x3000,
+	0x294e: 0x3000, 0x294f: 0x3000, 0x2950: 0x3000, 0x2951: 0x3000,
 	0x2952: 0x3000, 0x2953: 0x3000, 0x2954: 0x3000, 0x2955: 0x3000, 0x2956: 0x3000, 0x2957: 0x3000,
 	0x2958: 0x3000, 0x2959: 0x3000, 0x295a: 0x3000, 0x295b: 0x3000, 0x295c: 0x3000, 0x295d: 0x3000,
 	0x295e: 0x3000, 0x295f: 0x3000, 0x2960: 0x3000, 0x2961: 0x3000, 0x2962: 0x3000, 0x2963: 0x3000,
 	0x2964: 0x3000, 0x2965: 0x3000, 0x2966: 0x3000, 0x2967: 0x3000, 0x2968: 0x3000, 0x2969: 0x3000,
-	0x296a: 0x3000, 0x296b: 0x3000, 0x296c: 0x3000, 0x296d: 0x3000, 0x296e: 0x3000,
+	0x296a: 0x3000, 0x296b: 0x3000, 0x296c: 0x3000, 0x296d: 0x3000, 0x296e: 0x3000, 0x296f: 0x3000,
 	0x2970: 0x3000, 0x2971: 0x3000, 0x2972: 0x3000, 0x2973: 0x3000, 0x2974: 0x3000, 0x2975: 0x3000,
 	0x2976: 0x3000, 0x2977: 0x3000, 0x2978: 0x3000, 0x2979: 0x3000, 0x297a: 0x3000, 0x297b: 0x3000,
 	0x297c: 0x3000, 0x297d: 0x3000, 0x297e: 0x3000, 0x297f: 0x3000,
 	// Block 0xa6, offset 0x2980
 	0x2980: 0x3000, 0x2981: 0x3000, 0x2982: 0x3000, 0x2983: 0x3000, 0x2984: 0x3000, 0x2985: 0x3000,
-	0x2986: 0x3000, 0x2987: 0x3000, 0x2988: 0x3000, 0x2989: 0x3000, 0x298a: 0x3000, 0x298b: 0x3000,
-	0x298c: 0x3000, 0x298d: 0x3000, 0x298e: 0x3000, 0x298f: 0x3000,
+	0x2986: 0x3000, 0x2987: 0x3000, 0x2988: 0x3000, 0x2989: 0x3000, 0x298a: 0x3000,
+	0x2990: 0x3000, 0x2991: 0x3000,
+	0x2992: 0x3000, 0x2993: 0x3000, 0x2994: 0x3000, 0x2995: 0x3000, 0x2996: 0x3000, 0x2997: 0x3000,
+	0x2998: 0x3000, 0x2999: 0x3000, 0x299a: 0x3000, 0x299b: 0x3000, 0x299c: 0x3000, 0x299d: 0x3000,
+	0x299e: 0x3000, 0x299f: 0x3000, 0x29a0: 0x3000, 0x29a1: 0x3000, 0x29a2: 0x3000, 0x29a3: 0x3000,
+	0x29a4: 0x3000, 0x29a5: 0x3000, 0x29a6: 0x3000, 0x29a7: 0x3000, 0x29a8: 0x3000, 0x29a9: 0x3000,
+	0x29aa: 0x3000, 0x29ab: 0x3000, 0x29ac: 0x3000, 0x29ad: 0x3000, 0x29ae: 0x3000,
+	0x29b0: 0x3000, 0x29b1: 0x3000, 0x29b2: 0x3000, 0x29b3: 0x3000, 0x29b4: 0x3000, 0x29b5: 0x3000,
+	0x29b6: 0x3000, 0x29b7: 0x3000, 0x29b8: 0x3000, 0x29b9: 0x3000, 0x29ba: 0x3000, 0x29bb: 0x3000,
+	0x29bc: 0x3000, 0x29bd: 0x3000, 0x29be: 0x3000, 0x29bf: 0x3000,
 	// Block 0xa7, offset 0x29c0
-	0x29d0: 0x3000,
+	0x29c0: 0x3000, 0x29c1: 0x3000, 0x29c2: 0x3000, 0x29c3: 0x3000, 0x29c4: 0x3000, 0x29c5: 0x3000,
+	0x29c6: 0x3000, 0x29c7: 0x3000, 0x29c8: 0x3000, 0x29c9: 0x3000, 0x29ca: 0x3000, 0x29cb: 0x3000,
+	0x29cc: 0x3000, 0x29cd: 0x3000, 0x29ce: 0x3000, 0x29cf: 0x3000,
 	// Block 0xa8, offset 0x2a00
-	0x2a00: 0x3000, 0x2a01: 0x3000, 0x2a02: 0x3000,
-	0x2a10: 0x3000, 0x2a11: 0x3000,
-	0x2a12: 0x3000, 0x2a13: 0x3000, 0x2a14: 0x3000, 0x2a15: 0x3000, 0x2a16: 0x3000, 0x2a17: 0x3000,
-	0x2a18: 0x3000, 0x2a19: 0x3000, 0x2a1a: 0x3000, 0x2a1b: 0x3000, 0x2a1c: 0x3000, 0x2a1d: 0x3000,
-	0x2a1e: 0x3000, 0x2a1f: 0x3000, 0x2a20: 0x3000, 0x2a21: 0x3000, 0x2a22: 0x3000, 0x2a23: 0x3000,
-	0x2a24: 0x3000, 0x2a25: 0x3000, 0x2a26: 0x3000, 0x2a27: 0x3000, 0x2a28: 0x3000, 0x2a29: 0x3000,
-	0x2a2a: 0x3000, 0x2a2b: 0x3000, 0x2a2c: 0x3000, 0x2a2d: 0x3000, 0x2a2e: 0x3000, 0x2a2f: 0x3000,
-	0x2a30: 0x3000, 0x2a31: 0x3000, 0x2a32: 0x3000, 0x2a33: 0x3000, 0x2a34: 0x3000, 0x2a35: 0x3000,
-	0x2a36: 0x3000, 0x2a37: 0x3000, 0x2a38: 0x3000, 0x2a39: 0x3000, 0x2a3a: 0x3000,
+	0x2a10: 0x3000,
 	// Block 0xa9, offset 0x2a40
-	0x2a40: 0x3000, 0x2a41: 0x3000, 0x2a42: 0x3000, 0x2a43: 0x3000, 0x2a44: 0x3000, 0x2a45: 0x3000,
-	0x2a46: 0x3000, 0x2a47: 0x3000, 0x2a48: 0x3000,
+	0x2a40: 0x3000, 0x2a41: 0x3000, 0x2a42: 0x3000,
 	0x2a50: 0x3000, 0x2a51: 0x3000,
+	0x2a52: 0x3000, 0x2a53: 0x3000, 0x2a54: 0x3000, 0x2a55: 0x3000, 0x2a56: 0x3000, 0x2a57: 0x3000,
+	0x2a58: 0x3000, 0x2a59: 0x3000, 0x2a5a: 0x3000, 0x2a5b: 0x3000, 0x2a5c: 0x3000, 0x2a5d: 0x3000,
+	0x2a5e: 0x3000, 0x2a5f: 0x3000, 0x2a60: 0x3000, 0x2a61: 0x3000, 0x2a62: 0x3000, 0x2a63: 0x3000,
+	0x2a64: 0x3000, 0x2a65: 0x3000, 0x2a66: 0x3000, 0x2a67: 0x3000, 0x2a68: 0x3000, 0x2a69: 0x3000,
+	0x2a6a: 0x3000, 0x2a6b: 0x3000, 0x2a6c: 0x3000, 0x2a6d: 0x3000, 0x2a6e: 0x3000, 0x2a6f: 0x3000,
+	0x2a70: 0x3000, 0x2a71: 0x3000, 0x2a72: 0x3000, 0x2a73: 0x3000, 0x2a74: 0x3000, 0x2a75: 0x3000,
+	0x2a76: 0x3000, 0x2a77: 0x3000, 0x2a78: 0x3000, 0x2a79: 0x3000, 0x2a7a: 0x3000,
 	// Block 0xaa, offset 0x2a80
-	0x2a80: 0x3300, 0x2a81: 0x3300, 0x2a82: 0x3300, 0x2a83: 0x3300, 0x2a84: 0x3300, 0x2a85: 0x3300,
-	0x2a86: 0x3300, 0x2a87: 0x3300, 0x2a88: 0x3300, 0x2a89: 0x3300, 0x2a8a: 0x3300, 0x2a8b: 0x3300,
-	0x2a8c: 0x3300, 0x2a8d: 0x3300, 0x2a8e: 0x3300, 0x2a8f: 0x3300, 0x2a90: 0x3300, 0x2a91: 0x3300,
-	0x2a92: 0x3300, 0x2a93: 0x3300, 0x2a94: 0x3300, 0x2a95: 0x3300, 0x2a96: 0x3300, 0x2a97: 0x3300,
-	0x2a98: 0x3300, 0x2a99: 0x3300, 0x2a9a: 0x3300, 0x2a9b: 0x3300, 0x2a9c: 0x3300, 0x2a9d: 0x3300,
+	0x2a80: 0x3000, 0x2a81: 0x3000, 0x2a82: 0x3000, 0x2a83: 0x3000, 0x2a84: 0x3000, 0x2a85: 0x3000,
+	0x2a86: 0x3000, 0x2a87: 0x3000, 0x2a88: 0x3000,
+	0x2a90: 0x3000, 0x2a91: 0x3000,
+	// Block 0xab, offset 0x2ac0
+	0x2ac0: 0x3300, 0x2ac1: 0x3300, 0x2ac2: 0x3300, 0x2ac3: 0x3300, 0x2ac4: 0x3300, 0x2ac5: 0x3300,
+	0x2ac6: 0x3300, 0x2ac7: 0x3300, 0x2ac8: 0x3300, 0x2ac9: 0x3300, 0x2aca: 0x3300, 0x2acb: 0x3300,
+	0x2acc: 0x3300, 0x2acd: 0x3300, 0x2ace: 0x3300, 0x2acf: 0x3300, 0x2ad0: 0x3300, 0x2ad1: 0x3300,
+	0x2ad2: 0x3300, 0x2ad3: 0x3300, 0x2ad4: 0x3300, 0x2ad5: 0x3300, 0x2ad6: 0x3300, 0x2ad7: 0x3300,
+	0x2ad8: 0x3300, 0x2ad9: 0x3300, 0x2ada: 0x3300, 0x2adb: 0x3300, 0x2adc: 0x3300, 0x2add: 0x3300,
 }
 
 // charInfoLookup: 1152 bytes
@@ -6511,64 +6524,64 @@ var charInfoLookup = [1152]uint8{
 	0x131: 0x29, 0x132: 0x2a, 0x133: 0x2b, 0x134: 0x2c, 0x135: 0x28, 0x137: 0x2d,
 	0x138: 0x2e, 0x139: 0x2f, 0x13a: 0x30, 0x13b: 0x31, 0x13c: 0x32, 0x13d: 0x33, 0x13e: 0x34, 0x13f: 0x35,
 	// Block 0x5, offset 0x140
-	0x140: 0x36, 0x142: 0x37, 0x143: 0x38, 0x145: 0x39, 0x146: 0x3a, 0x147: 0x3b,
-	0x14d: 0x3c,
-	0x15c: 0x3d, 0x15f: 0x3e,
-	0x162: 0x3f, 0x164: 0x40,
-	0x168: 0x41, 0x169: 0x42, 0x16c: 0x43, 0x16d: 0x44, 0x16e: 0x45, 0x16f: 0x46,
-	0x170: 0x47, 0x173: 0x48, 0x174: 0x49, 0x175: 0x4a, 0x176: 0x4b, 0x177: 0x4c,
-	0x178: 0x4d, 0x179: 0x4e, 0x17a: 0x4f, 0x17b: 0x50, 0x17c: 0x51, 0x17d: 0x52, 0x17e: 0x53, 0x17f: 0x54,
+	0x140: 0x36, 0x142: 0x37, 0x143: 0x38, 0x144: 0x39, 0x145: 0x3a, 0x146: 0x3b, 0x147: 0x3c,
+	0x14d: 0x3d,
+	0x15c: 0x3e, 0x15f: 0x3f,
+	0x162: 0x40, 0x164: 0x41,
+	0x168: 0x42, 0x169: 0x43, 0x16c: 0x44, 0x16d: 0x45, 0x16e: 0x46, 0x16f: 0x47,
+	0x170: 0x48, 0x173: 0x49, 0x174: 0x4a, 0x175: 0x4b, 0x176: 0x4c, 0x177: 0x4d,
+	0x178: 0x4e, 0x179: 0x4f, 0x17a: 0x50, 0x17b: 0x51, 0x17c: 0x52, 0x17d: 0x53, 0x17e: 0x54, 0x17f: 0x55,
 	// Block 0x6, offset 0x180
-	0x180: 0x55, 0x181: 0x56, 0x182: 0x57, 0x183: 0x58, 0x184: 0x59, 0x185: 0x5a, 0x186: 0x5b, 0x187: 0x5c,
-	0x188: 0x5d, 0x189: 0x5e, 0x18a: 0x5f, 0x18b: 0x60, 0x18c: 0x61,
-	0x191: 0x62, 0x192: 0x63, 0x193: 0x64,
-	0x1a8: 0x65, 0x1a9: 0x66, 0x1ab: 0x67,
-	0x1b1: 0x68, 0x1b3: 0x69, 0x1b5: 0x6a, 0x1b7: 0x6b,
-	0x1ba: 0x6c, 0x1bb: 0x6d, 0x1bc: 0x63, 0x1bd: 0x63, 0x1be: 0x63, 0x1bf: 0x6e,
+	0x180: 0x56, 0x181: 0x57, 0x182: 0x58, 0x183: 0x59, 0x184: 0x5a, 0x185: 0x5b, 0x186: 0x5c, 0x187: 0x5d,
+	0x188: 0x5e, 0x189: 0x5f, 0x18a: 0x60, 0x18b: 0x61, 0x18c: 0x62,
+	0x191: 0x63, 0x192: 0x64, 0x193: 0x65,
+	0x1a8: 0x66, 0x1a9: 0x67, 0x1ab: 0x68,
+	0x1b1: 0x69, 0x1b3: 0x6a, 0x1b5: 0x6b, 0x1b7: 0x6c,
+	0x1ba: 0x6d, 0x1bb: 0x6e, 0x1bc: 0x64, 0x1bd: 0x64, 0x1be: 0x64, 0x1bf: 0x6f,
 	// Block 0x7, offset 0x1c0
-	0x1c0: 0x6f, 0x1c1: 0x70, 0x1c2: 0x71, 0x1c3: 0x72, 0x1c4: 0x73, 0x1c5: 0x63, 0x1c6: 0x74,
-	0x1c8: 0x75, 0x1c9: 0x76, 0x1ca: 0x63, 0x1cb: 0x77, 0x1cc: 0x63, 0x1cd: 0x63, 0x1ce: 0x63, 0x1cf: 0x63,
+	0x1c0: 0x70, 0x1c1: 0x71, 0x1c2: 0x72, 0x1c3: 0x73, 0x1c4: 0x74, 0x1c5: 0x64, 0x1c6: 0x75,
+	0x1c8: 0x76, 0x1c9: 0x77, 0x1ca: 0x64, 0x1cb: 0x78, 0x1cc: 0x64, 0x1cd: 0x64, 0x1ce: 0x64, 0x1cf: 0x64,
 	// Block 0x8, offset 0x200
-	0x219: 0x78, 0x21b: 0x79, 0x21d: 0x7a,
-	0x220: 0x7b, 0x223: 0x7c, 0x224: 0x7d, 0x225: 0x7e, 0x226: 0x7f, 0x227: 0x80,
-	0x22a: 0x81, 0x22b: 0x82, 0x22f: 0x83,
-	0x230: 0x84, 0x231: 0x84, 0x232: 0x84, 0x233: 0x84, 0x234: 0x84, 0x235: 0x84, 0x236: 0x84, 0x237: 0x84,
-	0x238: 0x84, 0x239: 0x84, 0x23a: 0x84, 0x23b: 0x84, 0x23c: 0x84, 0x23d: 0x84, 0x23e: 0x84, 0x23f: 0x84,
+	0x219: 0x79, 0x21b: 0x7a, 0x21d: 0x7b,
+	0x220: 0x7c, 0x223: 0x7d, 0x224: 0x7e, 0x225: 0x7f, 0x226: 0x80, 0x227: 0x81,
+	0x22a: 0x82, 0x22b: 0x83, 0x22f: 0x84,
+	0x230: 0x85, 0x231: 0x85, 0x232: 0x85, 0x233: 0x85, 0x234: 0x85, 0x235: 0x85, 0x236: 0x85, 0x237: 0x85,
+	0x238: 0x85, 0x239: 0x85, 0x23a: 0x85, 0x23b: 0x85, 0x23c: 0x85, 0x23d: 0x85, 0x23e: 0x85, 0x23f: 0x85,
 	// Block 0x9, offset 0x240
-	0x240: 0x84, 0x241: 0x84, 0x242: 0x84, 0x243: 0x84, 0x244: 0x84, 0x245: 0x84, 0x246: 0x84, 0x247: 0x84,
-	0x248: 0x84, 0x249: 0x84, 0x24a: 0x84, 0x24b: 0x84, 0x24c: 0x84, 0x24d: 0x84, 0x24e: 0x84, 0x24f: 0x84,
-	0x250: 0x84, 0x251: 0x84, 0x252: 0x84, 0x253: 0x84, 0x254: 0x84, 0x255: 0x84, 0x256: 0x84, 0x257: 0x84,
-	0x258: 0x84, 0x259: 0x84, 0x25a: 0x84, 0x25b: 0x84, 0x25c: 0x84, 0x25d: 0x84, 0x25e: 0x84, 0x25f: 0x84,
-	0x260: 0x84, 0x261: 0x84, 0x262: 0x84, 0x263: 0x84, 0x264: 0x84, 0x265: 0x84, 0x266: 0x84, 0x267: 0x84,
-	0x268: 0x84, 0x269: 0x84, 0x26a: 0x84, 0x26b: 0x84, 0x26c: 0x84, 0x26d: 0x84, 0x26e: 0x84, 0x26f: 0x84,
-	0x270: 0x84, 0x271: 0x84, 0x272: 0x84, 0x273: 0x84, 0x274: 0x84, 0x275: 0x84, 0x276: 0x84, 0x277: 0x84,
-	0x278: 0x84, 0x279: 0x84, 0x27a: 0x84, 0x27b: 0x84, 0x27c: 0x84, 0x27d: 0x84, 0x27e: 0x84, 0x27f: 0x84,
+	0x240: 0x85, 0x241: 0x85, 0x242: 0x85, 0x243: 0x85, 0x244: 0x85, 0x245: 0x85, 0x246: 0x85, 0x247: 0x85,
+	0x248: 0x85, 0x249: 0x85, 0x24a: 0x85, 0x24b: 0x85, 0x24c: 0x85, 0x24d: 0x85, 0x24e: 0x85, 0x24f: 0x85,
+	0x250: 0x85, 0x251: 0x85, 0x252: 0x85, 0x253: 0x85, 0x254: 0x85, 0x255: 0x85, 0x256: 0x85, 0x257: 0x85,
+	0x258: 0x85, 0x259: 0x85, 0x25a: 0x85, 0x25b: 0x85, 0x25c: 0x85, 0x25d: 0x85, 0x25e: 0x85, 0x25f: 0x85,
+	0x260: 0x85, 0x261: 0x85, 0x262: 0x85, 0x263: 0x85, 0x264: 0x85, 0x265: 0x85, 0x266: 0x85, 0x267: 0x85,
+	0x268: 0x85, 0x269: 0x85, 0x26a: 0x85, 0x26b: 0x85, 0x26c: 0x85, 0x26d: 0x85, 0x26e: 0x85, 0x26f: 0x85,
+	0x270: 0x85, 0x271: 0x85, 0x272: 0x85, 0x273: 0x85, 0x274: 0x85, 0x275: 0x85, 0x276: 0x85, 0x277: 0x85,
+	0x278: 0x85, 0x279: 0x85, 0x27a: 0x85, 0x27b: 0x85, 0x27c: 0x85, 0x27d: 0x85, 0x27e: 0x85, 0x27f: 0x85,
 	// Block 0xa, offset 0x280
-	0x280: 0x84, 0x281: 0x84, 0x282: 0x84, 0x283: 0x84, 0x284: 0x84, 0x285: 0x84, 0x286: 0x84, 0x287: 0x84,
-	0x288: 0x84, 0x289: 0x84, 0x28a: 0x84, 0x28b: 0x84, 0x28c: 0x84, 0x28d: 0x84, 0x28e: 0x84, 0x28f: 0x84,
-	0x290: 0x84, 0x291: 0x84, 0x292: 0x84, 0x293: 0x84, 0x294: 0x84, 0x295: 0x84, 0x296: 0x84, 0x297: 0x84,
-	0x298: 0x84, 0x299: 0x84, 0x29a: 0x84, 0x29b: 0x84, 0x29c: 0x84, 0x29d: 0x84, 0x29e: 0x85,
+	0x280: 0x85, 0x281: 0x85, 0x282: 0x85, 0x283: 0x85, 0x284: 0x85, 0x285: 0x85, 0x286: 0x85, 0x287: 0x85,
+	0x288: 0x85, 0x289: 0x85, 0x28a: 0x85, 0x28b: 0x85, 0x28c: 0x85, 0x28d: 0x85, 0x28e: 0x85, 0x28f: 0x85,
+	0x290: 0x85, 0x291: 0x85, 0x292: 0x85, 0x293: 0x85, 0x294: 0x85, 0x295: 0x85, 0x296: 0x85, 0x297: 0x85,
+	0x298: 0x85, 0x299: 0x85, 0x29a: 0x85, 0x29b: 0x85, 0x29c: 0x85, 0x29d: 0x85, 0x29e: 0x86,
 	// Block 0xb, offset 0x2c0
-	0x2e4: 0x86, 0x2e5: 0x86, 0x2e6: 0x86, 0x2e7: 0x86,
-	0x2e8: 0x87, 0x2e9: 0x88, 0x2ea: 0x86, 0x2eb: 0x89, 0x2ec: 0x8a, 0x2ed: 0x8b, 0x2ee: 0x8c, 0x2ef: 0x8d,
-	0x2f0: 0x63, 0x2f1: 0x63, 0x2f2: 0x63, 0x2f3: 0x63, 0x2f4: 0x8e, 0x2f5: 0x8f, 0x2f6: 0x90, 0x2f7: 0x91,
-	0x2f8: 0x92, 0x2f9: 0x93, 0x2fa: 0x63, 0x2fb: 0x94, 0x2fc: 0x95, 0x2fd: 0x63, 0x2fe: 0x77, 0x2ff: 0x96,
+	0x2e4: 0x87, 0x2e5: 0x87, 0x2e6: 0x87, 0x2e7: 0x87,
+	0x2e8: 0x88, 0x2e9: 0x89, 0x2ea: 0x87, 0x2eb: 0x8a, 0x2ec: 0x8b, 0x2ed: 0x8c, 0x2ee: 0x8d, 0x2ef: 0x8e,
+	0x2f0: 0x64, 0x2f1: 0x64, 0x2f2: 0x64, 0x2f3: 0x64, 0x2f4: 0x8f, 0x2f5: 0x90, 0x2f6: 0x91, 0x2f7: 0x92,
+	0x2f8: 0x93, 0x2f9: 0x94, 0x2fa: 0x64, 0x2fb: 0x95, 0x2fc: 0x96, 0x2fd: 0x64, 0x2fe: 0x78, 0x2ff: 0x97,
 	// Block 0xc, offset 0x300
-	0x307: 0x97,
-	0x328: 0x98,
+	0x307: 0x98,
+	0x328: 0x99,
 	// Block 0xd, offset 0x340
-	0x341: 0x7b, 0x342: 0x99,
+	0x341: 0x7c, 0x342: 0x9a,
 	// Block 0xe, offset 0x380
-	0x385: 0x9a, 0x386: 0x9b, 0x387: 0x9c,
-	0x389: 0x9d,
-	0x390: 0x63, 0x391: 0x9e, 0x392: 0x9f, 0x393: 0xa0, 0x394: 0xa1, 0x395: 0xa2, 0x396: 0x63, 0x397: 0x63,
-	0x398: 0x63, 0x399: 0x63, 0x39a: 0xa3, 0x39b: 0x63, 0x39c: 0x63, 0x39d: 0x63, 0x39e: 0x63, 0x39f: 0xa4,
+	0x385: 0x9b, 0x386: 0x9c, 0x387: 0x9d,
+	0x389: 0x9e,
+	0x390: 0x64, 0x391: 0x9f, 0x392: 0xa0, 0x393: 0xa1, 0x394: 0xa2, 0x395: 0xa3, 0x396: 0x64, 0x397: 0x64,
+	0x398: 0x64, 0x399: 0x64, 0x39a: 0xa4, 0x39b: 0x64, 0x39c: 0x64, 0x39d: 0x64, 0x39e: 0x64, 0x39f: 0xa5,
 	// Block 0xf, offset 0x3c0
-	0x3c4: 0xa5, 0x3c5: 0xa6, 0x3c6: 0xa7,
-	0x3c8: 0xa8, 0x3c9: 0xa9,
+	0x3c4: 0xa6, 0x3c5: 0xa7, 0x3c6: 0xa8,
+	0x3c8: 0xa9, 0x3c9: 0xaa,
 	// Block 0x10, offset 0x400
-	0x420: 0x86, 0x421: 0x86, 0x422: 0x86, 0x423: 0x86, 0x424: 0x86, 0x425: 0x86, 0x426: 0x86, 0x427: 0x86,
-	0x428: 0xaa,
+	0x420: 0x87, 0x421: 0x87, 0x422: 0x87, 0x423: 0x87, 0x424: 0x87, 0x425: 0x87, 0x426: 0x87, 0x427: 0x87,
+	0x428: 0xab,
 	// Block 0x11, offset 0x440
 	0x450: 0x0c, 0x451: 0x0d,
 	0x45d: 0x0e, 0x45f: 0x0f,
@@ -6577,4 +6590,4 @@ var charInfoLookup = [1152]uint8{
 
 var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:]}
 
-// Total size of tables: 78KB (80234 bytes)
+// Total size of tables: 78KB (80362 bytes)
diff --git a/src/pkg/exp/norm/trie.go b/src/pkg/exp/norm/trie.go
index 6b65401..edae2c2 100644
--- a/src/pkg/exp/norm/trie.go
+++ b/src/pkg/exp/norm/trie.go
@@ -86,16 +86,6 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) {
 		}
 		o = uint16(i)<<6 + uint16(c3)&maskx
 		return t.values[o], 4
-	case c0 < t6:
-		if len(s) < 5 {
-			return 0, 0
-		}
-		return 0, 5
-	case c0 < te:
-		if len(s) < 6 {
-			return 0, 0
-		}
-		return 0, 6
 	}
 	// Illegal rune
 	return 0, 1
@@ -162,16 +152,6 @@ func (t *trie) lookupString(s string) (v uint16, sz int) {
 		}
 		o = uint16(i)<<6 + uint16(c3)&maskx
 		return t.values[o], 4
-	case c0 < t6:
-		if len(s) < 5 {
-			return 0, 0
-		}
-		return 0, 5
-	case c0 < te:
-		if len(s) < 6 {
-			return 0, 0
-		}
-		return 0, 6
 	}
 	// Illegal rune
 	return 0, 1
diff --git a/src/pkg/exp/norm/trie_test.go b/src/pkg/exp/norm/trie_test.go
index ad87d97..6a30942 100644
--- a/src/pkg/exp/norm/trie_test.go
+++ b/src/pkg/exp/norm/trie_test.go
@@ -32,8 +32,10 @@ var tests = []trietest{
 	{0, []byte{t2}},
 	{0, []byte{t3, tx}},
 	{0, []byte{t4, tx, tx}},
-	{0, []byte{t5, tx, tx, tx}},
-	{0, []byte{t6, tx, tx, tx, tx}},
+
+	// we only support UTF-8 up to utf8.UTFMax bytes (4 bytes)
+	{1, []byte{t5, tx, tx, tx, tx}},
+	{1, []byte{t6, tx, tx, tx, tx, tx}},
 }
 
 func mkUtf8(rune int) ([]byte, int) {
diff --git a/src/pkg/exp/regexp/exec.go b/src/pkg/exp/regexp/exec.go
index 0670bb9..88b1603 100644
--- a/src/pkg/exp/regexp/exec.go
+++ b/src/pkg/exp/regexp/exec.go
@@ -90,23 +90,12 @@ func (m *machine) match(i input, pos int) bool {
 	if rune != endOfText {
 		rune1, width1 = i.step(pos + width)
 	}
-	// TODO: Let caller specify the initial flag setting.
-	// For now assume pos == 0 is beginning of text and
-	// pos != 0 is not even beginning of line.
-	// TODO: Word boundary.
 	var flag syntax.EmptyOp
 	if pos == 0 {
-		flag = syntax.EmptyBeginText | syntax.EmptyBeginLine
-	}
-
-	// Update flag using lookahead rune.
-	if rune1 == '\n' {
-		flag |= syntax.EmptyEndLine
-	}
-	if rune1 == endOfText {
-		flag |= syntax.EmptyEndText
+		flag = syntax.EmptyOpContext(-1, rune)
+	} else {
+		flag = i.context(pos)
 	}
-
 	for {
 		if len(runq.dense) == 0 {
 			if startCond&syntax.EmptyBeginText != 0 && pos != 0 {
@@ -134,17 +123,7 @@ func (m *machine) match(i input, pos int) bool {
 			}
 			m.add(runq, uint32(m.p.Start), pos, m.matchcap, flag)
 		}
-		// TODO: word boundary
-		flag = 0
-		if rune == '\n' {
-			flag |= syntax.EmptyBeginLine
-		}
-		if rune1 == '\n' {
-			flag |= syntax.EmptyEndLine
-		}
-		if rune1 == endOfText {
-			flag |= syntax.EmptyEndText
-		}
+		flag = syntax.EmptyOpContext(rune, rune1)
 		m.step(runq, nextq, pos, pos+width, rune, flag)
 		if width == 0 {
 			break
diff --git a/src/pkg/exp/regexp/exec_test.go b/src/pkg/exp/regexp/exec_test.go
new file mode 100644
index 0000000..15c4c53
--- /dev/null
+++ b/src/pkg/exp/regexp/exec_test.go
@@ -0,0 +1,271 @@
+// 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 regexp
+
+import (
+	"bufio"
+	"compress/gzip"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+	"utf8"
+)
+
+// TestRE2 tests this package's regexp API against test cases
+// considered during RE2's exhaustive tests, which run all possible
+// regexps over a given set of atoms and operators, up to a given
+// complexity, over all possible strings over a given alphabet,
+// up to a given size.  Rather than try to link with RE2, we read a
+// log file containing the test cases and the expected matches.
+// The log file, re2.txt, is generated by running 'make exhaustive-log'
+// in the open source RE2 distribution.  http://code.google.com/p/re2/
+//
+// The test file format is a sequence of stanzas like:
+//
+//	strings
+//	"abc"
+//	"123x"
+//	regexps
+//	"[a-z]+"
+//	0-3;0-3
+//	-;-
+//	"([0-9])([0-9])([0-9])"
+//	-;-
+//	-;0-3 0-1 1-2 2-3
+//
+// The stanza begins by defining a set of strings, quoted
+// using Go double-quote syntax, one per line.  Then the
+// regexps section gives a sequence of regexps to run on
+// the strings.  In the block that follows a regexp, each line
+// gives the semicolon-separated match results of running
+// the regexp on the corresponding string.
+// Each match result is either a single -, meaning no match, or a
+// space-separated sequence of pairs giving the match and
+// submatch indices.  An unmatched subexpression formats
+// its pair as a single - (not illustrated above).  For now
+// each regexp run produces two match results, one for a
+// ``full match'' that restricts the regexp to matching the entire
+// string or nothing, and one for a ``partial match'' that gives
+// the leftmost first match found in the string.
+//
+// Lines beginning with # are comments.  Lines beginning with
+// a capital letter are test names printed during RE2's test suite
+// and are echoed into t but otherwise ignored.
+//
+// At time of writing, re2.txt is 32 MB but compresses to 760 kB,
+// so we store re2.txt.gz in the repository and decompress it on the fly.
+//
+func TestRE2(t *testing.T) {
+	if testing.Short() {
+		t.Log("skipping TestRE2 during short test")
+		return
+	}
+
+	f, err := os.Open("re2.txt.gz")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+	gz, err := gzip.NewReader(f)
+	if err != nil {
+		t.Fatalf("decompress re2.txt.gz: %v", err)
+	}
+	defer gz.Close()
+	lineno := 0
+	r := bufio.NewReader(gz)
+	var (
+		str       []string
+		input     []string
+		inStrings bool
+		re        *Regexp
+		refull    *Regexp
+		nfail     int
+		ncase     int
+	)
+	for {
+		line, err := r.ReadString('\n')
+		if err != nil {
+			if err == os.EOF {
+				break
+			}
+			t.Fatalf("re2.txt:%d: %v", lineno, err)
+		}
+		line = line[:len(line)-1] // chop \n
+		lineno++
+		switch {
+		case line == "":
+			t.Fatalf("re2.txt:%d: unexpected blank line", lineno)
+		case line[0] == '#':
+			continue
+		case 'A' <= line[0] && line[0] <= 'Z':
+			// Test name.
+			t.Logf("%s\n", line)
+			continue
+		case line == "strings":
+			str = str[:0]
+			inStrings = true
+		case line == "regexps":
+			inStrings = false
+		case line[0] == '"':
+			q, err := strconv.Unquote(line)
+			if err != nil {
+				// Fatal because we'll get out of sync.
+				t.Fatalf("re2.txt:%d: unquote %s: %v", lineno, line, err)
+			}
+			if inStrings {
+				str = append(str, q)
+				continue
+			}
+			// Is a regexp.
+			if len(input) != 0 {
+				t.Fatalf("re2.txt:%d: out of sync: have %d strings left before %#q", lineno, len(input), q)
+			}
+			re, err = tryCompile(q)
+			if err != nil {
+				if err.String() == "error parsing regexp: invalid escape sequence: `\\C`" {
+					// We don't and likely never will support \C; keep going.
+					continue
+				}
+				t.Errorf("re2.txt:%d: compile %#q: %v", lineno, q, err)
+				if nfail++; nfail >= 100 {
+					t.Fatalf("stopping after %d errors", nfail)
+				}
+				continue
+			}
+			full := `\A(?:` + q + `)\z`
+			refull, err = tryCompile(full)
+			if err != nil {
+				// Fatal because q worked, so this should always work.
+				t.Fatalf("re2.txt:%d: compile full %#q: %v", lineno, full, err)
+			}
+			input = str
+		case line[0] == '-' || '0' <= line[0] && line[0] <= '9':
+			// A sequence of match results.
+			ncase++
+			if re == nil {
+				// Failed to compile: skip results.
+				continue
+			}
+			if len(input) == 0 {
+				t.Fatalf("re2.txt:%d: out of sync: no input remaining", lineno)
+			}
+			var text string
+			text, input = input[0], input[1:]
+			if !isSingleBytes(text) && strings.Contains(re.String(), `\B`) {
+				// RE2's \B considers every byte position,
+				// so it sees 'not word boundary' in the
+				// middle of UTF-8 sequences.  This package
+				// only considers the positions between runes,
+				// so it disagrees.  Skip those cases.
+				continue
+			}
+			res := strings.Split(line, ";")
+			if len(res) != 2 {
+				t.Fatalf("re2.txt:%d: have %d test results, want 2", lineno, len(res))
+			}
+			// res[0] is full match
+			// res[1] is partial match
+			// Run partial match first; don't bother with full if partial fails.
+			have := re.FindStringSubmatchIndex(text)
+			want := parseResult(t, lineno, res[1])
+			if !same(have, want) {
+				t.Errorf("re2.txt:%d: %#q.FindSubmatchIndex(%#q) = %v, want %v", lineno, re, text, have, want)
+				if nfail++; nfail >= 100 {
+					t.Fatalf("stopping after %d errors", nfail)
+				}
+				continue
+			}
+			have = refull.FindStringSubmatchIndex(text)
+			want = parseResult(t, lineno, res[0])
+			if !same(have, want) {
+				t.Errorf("re2.txt:%d: %#q.FindSubmatchIndex(%#q) = %v, want %v", lineno, refull, text, have, want)
+				if nfail++; nfail >= 100 {
+					t.Fatalf("stopping after %d errors", nfail)
+				}
+			}
+		default:
+			t.Fatalf("re2.txt:%d: out of sync: %s\n", lineno, line)
+		}
+	}
+	if len(input) != 0 {
+		t.Fatalf("re2.txt:%d: out of sync: have %d strings left at EOF", lineno, len(input))
+	}
+	t.Logf("%d cases tested", ncase)
+}
+
+func isSingleBytes(s string) bool {
+	for _, c := range s {
+		if c >= utf8.RuneSelf {
+			return false
+		}
+	}
+	return true
+}
+
+func tryCompile(s string) (re *Regexp, err os.Error) {
+	// Protect against panic during Compile.
+	defer func() {
+		if r := recover(); r != nil {
+			err = fmt.Errorf("panic: %v", r)
+		}
+	}()
+	return Compile(s)
+}
+
+func parseResult(t *testing.T, lineno int, res string) []int {
+	// A single - indicates no match.
+	if res == "-" {
+		return nil
+	}
+	// Otherwise, a space-separated list of pairs.
+	n := 1
+	for j := 0; j < len(res); j++ {
+		if res[j] == ' ' {
+			n++
+		}
+	}
+	out := make([]int, 2*n)
+	i := 0
+	n = 0
+	for j := 0; j <= len(res); j++ {
+		if j == len(res) || res[j] == ' ' {
+			// Process a single pair.  - means no submatch.
+			pair := res[i:j]
+			if pair == "-" {
+				out[n] = -1
+				out[n+1] = -1
+			} else {
+				k := strings.Index(pair, "-")
+				if k < 0 {
+					t.Fatalf("re2.txt:%d: invalid pair %s", lineno, pair)
+				}
+				lo, err1 := strconv.Atoi(pair[:k])
+				hi, err2 := strconv.Atoi(pair[k+1:])
+				if err1 != nil || err2 != nil || lo > hi {
+					t.Fatalf("re2.txt:%d: invalid pair %s", lineno, pair)
+				}
+				out[n] = lo
+				out[n+1] = hi
+			}
+			n += 2
+			i = j + 1
+		}
+	}
+	return out
+}
+
+func same(x, y []int) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, xi := range x {
+		if xi != y[i] {
+			return false
+		}
+	}
+	return true
+}
diff --git a/src/pkg/exp/regexp/find_test.go b/src/pkg/exp/regexp/find_test.go
index dddc348..6406bb6 100644
--- a/src/pkg/exp/regexp/find_test.go
+++ b/src/pkg/exp/regexp/find_test.go
@@ -80,6 +80,23 @@ var findTests = []FindTest{
 	{`data`, "daXY data", build(1, 5, 9)},
 	{`da(.)a$`, "daXY data", build(1, 5, 9, 7, 8)},
 	{`zx+`, "zzx", build(1, 1, 3)},
+	{`ab$`, "abcab", build(1, 3, 5)},
+	{`(aa)*$`, "a", build(1, 1, 1, -1, -1)},
+	{`(?:.|(?:.a))`, "", nil},
+	{`(?:A(?:A|a))`, "Aa", build(1, 0, 2)},
+	{`(?:A|(?:A|a))`, "a", build(1, 0, 1)},
+	{`(a){0}`, "", build(1, 0, 0, -1, -1)},
+	{`(?-s)(?:(?:^).)`, "\n", nil},
+	{`(?s)(?:(?:^).)`, "\n", build(1, 0, 1)},
+	{`(?:(?:^).)`, "\n", nil},
+	{`\b`, "x", build(2, 0, 0, 1, 1)},
+	{`\b`, "xx", build(2, 0, 0, 2, 2)},
+	{`\b`, "x y", build(4, 0, 0, 1, 1, 2, 2, 3, 3)},
+	{`\b`, "xx yy", build(4, 0, 0, 2, 2, 3, 3, 5, 5)},
+	{`\B`, "x", nil},
+	{`\B`, "xx", build(1, 1, 1)},
+	{`\B`, "x y", nil},
+	{`\B`, "xx yy", build(2, 1, 1, 4, 4)},
 
 	// can backslash-escape any punctuation
 	{`\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\{\|\}\~`,
diff --git a/src/pkg/exp/regexp/re2.txt.gz b/src/pkg/exp/regexp/re2.txt.gz
new file mode 100644
index 0000000..2b8c832
Binary files /dev/null and b/src/pkg/exp/regexp/re2.txt.gz differ
diff --git a/src/pkg/exp/regexp/regexp.go b/src/pkg/exp/regexp/regexp.go
index 1b75900..11feecd 100644
--- a/src/pkg/exp/regexp/regexp.go
+++ b/src/pkg/exp/regexp/regexp.go
@@ -84,6 +84,7 @@ type Regexp struct {
 	prefixComplete bool           // prefix is the entire regexp
 	prefixRune     int            // first rune in prefix
 	cond           syntax.EmptyOp // empty-width conditions required at start of match
+	numSubexp      int
 
 	// cache of machines for running regexp
 	mu      sync.Mutex
@@ -102,13 +103,16 @@ func Compile(expr string) (*Regexp, os.Error) {
 	if err != nil {
 		return nil, err
 	}
+	maxCap := re.MaxCap()
+	re = re.Simplify()
 	prog, err := syntax.Compile(re)
 	if err != nil {
 		return nil, err
 	}
 	regexp := &Regexp{
-		expr: expr,
-		prog: prog,
+		expr:      expr,
+		prog:      prog,
+		numSubexp: maxCap,
 	}
 	regexp.prefix, regexp.prefixComplete = prog.Prefix()
 	if regexp.prefix != "" {
@@ -161,9 +165,7 @@ func MustCompile(str string) *Regexp {
 
 // NumSubexp returns the number of parenthesized subexpressions in this Regexp.
 func (re *Regexp) NumSubexp() int {
-	// NumCap/2 because captures count ( and ) separately.
-	// -1 because NumCap counts $0 but NumSubexp does not.
-	return re.prog.NumCap/2 - 1
+	return re.numSubexp
 }
 
 const endOfText = -1
@@ -175,6 +177,7 @@ type input interface {
 	canCheckPrefix() bool               // can we look ahead without losing info?
 	hasPrefix(re *Regexp) bool
 	index(re *Regexp, pos int) int
+	context(pos int) syntax.EmptyOp
 }
 
 // inputString scans a string.
@@ -205,6 +208,17 @@ func (i *inputString) index(re *Regexp, pos int) int {
 	return strings.Index(i.str[pos:], re.prefix)
 }
 
+func (i *inputString) context(pos int) syntax.EmptyOp {
+	r1, r2 := -1, -1
+	if pos > 0 && pos <= len(i.str) {
+		r1, _ = utf8.DecodeLastRuneInString(i.str[:pos])
+	}
+	if pos < len(i.str) {
+		r2, _ = utf8.DecodeRuneInString(i.str[pos:])
+	}
+	return syntax.EmptyOpContext(r1, r2)
+}
+
 // inputBytes scans a byte slice.
 type inputBytes struct {
 	str []byte
@@ -233,6 +247,17 @@ func (i *inputBytes) index(re *Regexp, pos int) int {
 	return bytes.Index(i.str[pos:], re.prefixBytes)
 }
 
+func (i *inputBytes) context(pos int) syntax.EmptyOp {
+	r1, r2 := -1, -1
+	if pos > 0 && pos <= len(i.str) {
+		r1, _ = utf8.DecodeLastRune(i.str[:pos])
+	}
+	if pos < len(i.str) {
+		r2, _ = utf8.DecodeRune(i.str[pos:])
+	}
+	return syntax.EmptyOpContext(r1, r2)
+}
+
 // inputReader scans a RuneReader.
 type inputReader struct {
 	r     io.RuneReader
@@ -270,6 +295,10 @@ func (i *inputReader) index(re *Regexp, pos int) int {
 	return -1
 }
 
+func (i *inputReader) context(pos int) syntax.EmptyOp {
+	return 0
+}
+
 // LiteralPrefix returns a literal string that must begin any match
 // of the regular expression re.  It returns the boolean true if the
 // literal string comprises the entire regular expression.
@@ -458,6 +487,23 @@ func QuoteMeta(s string) string {
 	return string(b[0:j])
 }
 
+// The number of capture values in the program may correspond
+// to fewer capturing expressions than are in the regexp.
+// For example, "(a){0}" turns into an empty program, so the
+// maximum capture in the program is 0 but we need to return
+// an expression for \1.  Pad appends -1s to the slice a as needed.
+func (re *Regexp) pad(a []int) []int {
+	if a == nil {
+		// No match.
+		return nil
+	}
+	n := (1 + re.numSubexp) * 2
+	for len(a) < n {
+		a = append(a, -1)
+	}
+	return a
+}
+
 // Find matches in slice b if b is non-nil, otherwise find matches in string s.
 func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) {
 	var end int
@@ -505,7 +551,7 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) {
 		prevMatchEnd = matches[1]
 
 		if accept {
-			deliver(matches)
+			deliver(re.pad(matches))
 			i++
 		}
 	}
@@ -580,9 +626,9 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
 	if a == nil {
 		return nil
 	}
-	ret := make([][]byte, len(a)/2)
+	ret := make([][]byte, 1+re.numSubexp)
 	for i := range ret {
-		if a[2*i] >= 0 {
+		if 2*i < len(a) && a[2*i] >= 0 {
 			ret[i] = b[a[2*i]:a[2*i+1]]
 		}
 	}
@@ -595,7 +641,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
 // in the package comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindSubmatchIndex(b []byte) []int {
-	return re.doExecute(newInputBytes(b), 0, re.prog.NumCap)
+	return re.pad(re.doExecute(newInputBytes(b), 0, re.prog.NumCap))
 }
 
 // FindStringSubmatch returns a slice of strings holding the text of the
@@ -608,9 +654,9 @@ func (re *Regexp) FindStringSubmatch(s string) []string {
 	if a == nil {
 		return nil
 	}
-	ret := make([]string, len(a)/2)
+	ret := make([]string, 1+re.numSubexp)
 	for i := range ret {
-		if a[2*i] >= 0 {
+		if 2*i < len(a) && a[2*i] >= 0 {
 			ret[i] = s[a[2*i]:a[2*i+1]]
 		}
 	}
@@ -623,7 +669,7 @@ func (re *Regexp) FindStringSubmatch(s string) []string {
 // 'Index' descriptions in the package comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindStringSubmatchIndex(s string) []int {
-	return re.doExecute(newInputString(s), 0, re.prog.NumCap)
+	return re.pad(re.doExecute(newInputString(s), 0, re.prog.NumCap))
 }
 
 // FindReaderSubmatchIndex returns a slice holding the index pairs
@@ -632,7 +678,7 @@ func (re *Regexp) FindStringSubmatchIndex(s string) []int {
 // by the 'Submatch' and 'Index' descriptions in the package comment.  A
 // return value of nil indicates no match.
 func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int {
-	return re.doExecute(newInputReader(r), 0, re.prog.NumCap)
+	return re.pad(re.doExecute(newInputReader(r), 0, re.prog.NumCap))
 }
 
 const startSize = 10 // The size at which to start a slice in the 'All' routines.
diff --git a/src/pkg/exp/regexp/syntax/compile.go b/src/pkg/exp/regexp/syntax/compile.go
index 5ea2425..6b6d062 100644
--- a/src/pkg/exp/regexp/syntax/compile.go
+++ b/src/pkg/exp/regexp/syntax/compile.go
@@ -75,6 +75,7 @@ type compiler struct {
 }
 
 // Compile compiles the regexp into a program to be executed.
+// The regexp should have been simplified already (returned from re.Simplify).
 func Compile(re *Regexp) (*Prog, os.Error) {
 	var c compiler
 	c.init()
@@ -90,7 +91,7 @@ func (c *compiler) init() {
 	c.inst(InstFail)
 }
 
-var anyRuneNotNL = []int{0, '\n' - 1, '\n' - 1, unicode.MaxRune}
+var anyRuneNotNL = []int{0, '\n' - 1, '\n' + 1, unicode.MaxRune}
 var anyRune = []int{0, unicode.MaxRune}
 
 func (c *compiler) compile(re *Regexp) frag {
@@ -105,7 +106,7 @@ func (c *compiler) compile(re *Regexp) frag {
 		}
 		var f frag
 		for j := range re.Rune {
-			f1 := c.rune(re.Rune[j : j+1])
+			f1 := c.rune(re.Rune[j:j+1], re.Flags)
 			if j == 0 {
 				f = f1
 			} else {
@@ -114,11 +115,11 @@ func (c *compiler) compile(re *Regexp) frag {
 		}
 		return f
 	case OpCharClass:
-		return c.rune(re.Rune)
+		return c.rune(re.Rune, re.Flags)
 	case OpAnyCharNotNL:
-		return c.rune(anyRuneNotNL)
+		return c.rune(anyRuneNotNL, 0)
 	case OpAnyChar:
-		return c.rune(anyRune)
+		return c.rune(anyRune, 0)
 	case OpBeginLine:
 		return c.empty(EmptyBeginLine)
 	case OpEndLine:
@@ -261,9 +262,16 @@ func (c *compiler) empty(op EmptyOp) frag {
 	return f
 }
 
-func (c *compiler) rune(rune []int) frag {
+func (c *compiler) rune(rune []int, flags Flags) frag {
 	f := c.inst(InstRune)
-	c.p.Inst[f.i].Rune = rune
+	i := &c.p.Inst[f.i]
+	i.Rune = rune
+	flags &= FoldCase // only relevant flag is FoldCase
+	if len(rune) != 1 || unicode.SimpleFold(rune[0]) == rune[0] {
+		// and sometimes not even that
+		flags &^= FoldCase
+	}
+	i.Arg = uint32(flags)
 	f.out = patchList(f.i << 1)
 	return f
 }
diff --git a/src/pkg/exp/regexp/syntax/parse.go b/src/pkg/exp/regexp/syntax/parse.go
index 4eed182..954a0ad 100644
--- a/src/pkg/exp/regexp/syntax/parse.go
+++ b/src/pkg/exp/regexp/syntax/parse.go
@@ -419,8 +419,7 @@ func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp {
 		// used or marked for reuse, and the slice space has been reused
 		// for out (len(out) <= start).
 		//
-		// Invariant: sub[start:i] consists of regexps that all begin
-		// with str as modified by strflags.
+		// Invariant: sub[start:i] consists of regexps that all begin with ifirst.
 		var ifirst *Regexp
 		if i < len(sub) {
 			ifirst = p.leadingRegexp(sub[i])
@@ -441,7 +440,6 @@ func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp {
 		} else {
 			// Construct factored form: prefix(suffix1|suffix2|...)
 			prefix := first
-
 			for j := start; j < i; j++ {
 				reuse := j != start // prefix came from sub[start] 
 				sub[j] = p.removeLeadingRegexp(sub[j], reuse)
@@ -605,8 +603,10 @@ func (p *parser) removeLeadingRegexp(re *Regexp, reuse bool) *Regexp {
 		}
 		return re
 	}
-	re.Op = OpEmptyMatch
-	return re
+	if reuse {
+		p.reuse(re)
+	}
+	return p.newRegexp(OpEmptyMatch)
 }
 
 func literalRegexp(s string, flags Flags) *Regexp {
@@ -1053,18 +1053,18 @@ func mergeCharClass(dst, src *Regexp) {
 	case OpCharClass:
 		// src is simpler, so either literal or char class
 		if src.Op == OpLiteral {
-			dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0])
+			dst.Rune = appendLiteral(dst.Rune, src.Rune[0], src.Flags)
 		} else {
 			dst.Rune = appendClass(dst.Rune, src.Rune)
 		}
 	case OpLiteral:
 		// both literal
-		if src.Rune[0] == dst.Rune[0] {
+		if src.Rune[0] == dst.Rune[0] && src.Flags == dst.Flags {
 			break
 		}
 		dst.Op = OpCharClass
-		dst.Rune = append(dst.Rune, dst.Rune[0])
-		dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0])
+		dst.Rune = appendLiteral(dst.Rune[:0], dst.Rune[0], dst.Flags)
+		dst.Rune = appendLiteral(dst.Rune, src.Rune[0], src.Flags)
 	}
 }
 
@@ -1544,6 +1544,14 @@ func cleanClass(rp *[]int) []int {
 	return r[:w]
 }
 
+// appendLiteral returns the result of appending the literal x to the class r.
+func appendLiteral(r []int, x int, flags Flags) []int {
+	if flags&FoldCase != 0 {
+		return appendFoldedRange(r, x, x)
+	}
+	return appendRange(r, x, x)
+}
+
 // appendRange returns the result of appending the range lo-hi to the class r.
 func appendRange(r []int, lo, hi int) []int {
 	// Expand last range or next to last range if it overlaps or abuts.
diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go
index 779b9af..a146c89 100644
--- a/src/pkg/exp/regexp/syntax/parse_test.go
+++ b/src/pkg/exp/regexp/syntax/parse_test.go
@@ -162,6 +162,18 @@ var parseTests = []struct {
 	// Factoring.
 	{`abc|abd|aef|bcx|bcy`, `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}cat{str{bc}cc{0x78-0x79}}}`},
 	{`ax+y|ax+z|ay+w`, `cat{lit{a}alt{cat{plus{lit{x}}cc{0x79-0x7a}}cat{plus{lit{y}}lit{w}}}}`},
+
+	// Bug fixes.
+	{`(?:.)`, `dot{}`},
+	{`(?:x|(?:xa))`, `cat{lit{x}alt{emp{}lit{a}}}`},
+	{`(?:.|(?:.a))`, `cat{dot{}alt{emp{}lit{a}}}`},
+	{`(?:A(?:A|a))`, `cat{lit{A}litfold{A}}`},
+	{`(?:A|a)`, `litfold{A}`},
+	{`A|(?:A|a)`, `litfold{A}`},
+	{`(?s).`, `dot{}`},
+	{`(?-s).`, `dnl{}`},
+	{`(?:(?:^).)`, `cat{bol{}dot{}}`},
+	{`(?-s)(?:(?:^).)`, `cat{bol{}dnl{}}`},
 }
 
 const testFlags = MatchNL | PerlX | UnicodeGroups
diff --git a/src/pkg/exp/regexp/syntax/prog.go b/src/pkg/exp/regexp/syntax/prog.go
index bf85b72..d214d70 100644
--- a/src/pkg/exp/regexp/syntax/prog.go
+++ b/src/pkg/exp/regexp/syntax/prog.go
@@ -3,6 +3,7 @@ package syntax
 import (
 	"bytes"
 	"strconv"
+	"unicode"
 )
 
 // Compiled program.
@@ -41,6 +42,41 @@ const (
 	EmptyNoWordBoundary
 )
 
+// EmptyOpContext returns the zero-width assertions
+// satisfied at the position between the runes r1 and r2.
+// Passing r1 == -1 indicates that the position is
+// at the beginning of the text.
+// Passing r2 == -1 indicates that the position is
+// at the end of the text.
+func EmptyOpContext(r1, r2 int) EmptyOp {
+	var op EmptyOp
+	if r1 < 0 {
+		op |= EmptyBeginText | EmptyBeginLine
+	}
+	if r1 == '\n' {
+		op |= EmptyBeginLine
+	}
+	if r2 < 0 {
+		op |= EmptyEndText
+	}
+	if r2 == '\n' {
+		op |= EmptyEndLine
+	}
+	if IsWordChar(r1) != IsWordChar(r2) {
+		op |= EmptyWordBoundary
+	} else {
+		op |= EmptyNoWordBoundary
+	}
+	return op
+}
+
+// IsWordChar reports whether r is consider a ``word character''
+// during the evaluation of the \b and \B zero-width assertions.
+// These assertions are ASCII-only: the word characters are [A-Za-z0-9_].
+func IsWordChar(r int) bool {
+	return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
+}
+
 // An Inst is a single instruction in a regular expression program.
 type Inst struct {
 	Op   InstOp
@@ -79,7 +115,7 @@ func (p *Prog) Prefix() (prefix string, complete bool) {
 
 	// Have prefix; gather characters.
 	var buf bytes.Buffer
-	for i.Op == InstRune && len(i.Rune) == 1 {
+	for i.Op == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 {
 		buf.WriteRune(i.Rune[0])
 		i = p.skipNop(i.Out)
 	}
@@ -116,9 +152,19 @@ func (i *Inst) MatchRune(r int) bool {
 	rune := i.Rune
 
 	// Special case: single-rune slice is from literal string, not char class.
-	// TODO: Case folding.
 	if len(rune) == 1 {
-		return r == rune[0]
+		r0 := rune[0]
+		if r == r0 {
+			return true
+		}
+		if Flags(i.Arg)&FoldCase != 0 {
+			for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
+				if r == r1 {
+					return true
+				}
+			}
+		}
+		return false
 	}
 
 	// Peek at the first few pairs.
@@ -232,6 +278,10 @@ func dumpInst(b *bytes.Buffer, i *Inst) {
 			// shouldn't happen
 			bw(b, "rune <nil>")
 		}
-		bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out))
+		bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)))
+		if Flags(i.Arg)&FoldCase != 0 {
+			bw(b, "/i")
+		}
+		bw(b, " -> ", u32(i.Out))
 	}
 }
diff --git a/src/pkg/exp/regexp/syntax/prog_test.go b/src/pkg/exp/regexp/syntax/prog_test.go
index 7be4281..3fe0c58 100644
--- a/src/pkg/exp/regexp/syntax/prog_test.go
+++ b/src/pkg/exp/regexp/syntax/prog_test.go
@@ -77,6 +77,16 @@ var compileTests = []struct {
   5*	alt -> 1, 3
   6	match
 `},
+	{"A[Aa]", `  0	fail
+  1*	rune "A" -> 2
+  2	rune "A"/i -> 3
+  3	match
+`},
+	{"(?:(?:^).)", `  0	fail
+  1*	empty 4 -> 2
+  2	rune "\x00\t\v\U0010ffff" -> 3
+  3	match
+`},
 }
 
 func TestCompile(t *testing.T) {
diff --git a/src/pkg/exp/regexp/syntax/regexp.go b/src/pkg/exp/regexp/syntax/regexp.go
index 00a4add..d8f51b9 100644
--- a/src/pkg/exp/regexp/syntax/regexp.go
+++ b/src/pkg/exp/regexp/syntax/regexp.go
@@ -282,3 +282,17 @@ func escape(b *bytes.Buffer, r int, force bool) {
 		b.WriteString(`}`)
 	}
 }
+
+// MaxCap walks the regexp to find the maximum capture index.
+func (re *Regexp) MaxCap() int {
+	m := 0
+	if re.Op == OpCapture {
+		m = re.Cap
+	}
+	for _, sub := range re.Sub {
+		if n := sub.MaxCap(); m < n {
+			m = n
+		}
+	}
+	return m
+}
diff --git a/src/pkg/exp/template/html/Makefile b/src/pkg/exp/template/html/Makefile
index 2f107da..3a93beb 100644
--- a/src/pkg/exp/template/html/Makefile
+++ b/src/pkg/exp/template/html/Makefile
@@ -6,6 +6,8 @@ include ../../../../Make.inc
 
 TARG=exp/template/html
 GOFILES=\
-	escape.go
+	context.go\
+	escape.go\
+	js.go\
 
 include ../../../../Make.pkg
diff --git a/src/pkg/exp/template/html/context.go b/src/pkg/exp/template/html/context.go
index 4110068..428b3d0 100644
--- a/src/pkg/exp/template/html/context.go
+++ b/src/pkg/exp/template/html/context.go
@@ -16,56 +16,77 @@ import (
 // http://www.w3.org/TR/html5/the-end.html#parsing-html-fragments
 // where the context element is null.
 type context struct {
-	state state
-	delim delim
+	state   state
+	delim   delim
+	urlPart urlPart
+	jsCtx   jsCtx
+	errLine int
+	errStr  string
 }
 
-func (c context) String() string {
-	return fmt.Sprintf("context{state: %s, delim: %s", c.state, c.delim)
-}
-
-// eq is true if the two contexts are identical field-wise.
+// eq returns whether two contexts are equal.
 func (c context) eq(d context) bool {
-	return c.state == d.state && c.delim == d.delim
+	return c.state == d.state && c.delim == d.delim && c.urlPart == d.urlPart && c.jsCtx == d.jsCtx && c.errLine == d.errLine && c.errStr == d.errStr
 }
 
 // state describes a high-level HTML parser state.
 //
-// It bounds the top of the element stack, and by extension the HTML
-// insertion mode, but also contains state that does not correspond to
-// anything in the HTML5 parsing algorithm because a single token 
-// production in the HTML grammar may contain embedded actions in a template.
-// For instance, the quoted HTML attribute produced by
+// It bounds the top of the element stack, and by extension the HTML insertion
+// mode, but also contains state that does not correspond to anything in the
+// HTML5 parsing algorithm because a single token production in the HTML
+// grammar may contain embedded actions in a template. For instance, the quoted
+// HTML attribute produced by
 //     <div title="Hello {{.World}}">
 // is a single token in HTML's grammar but in a template spans several nodes.
 type state uint8
 
 const (
-	// statePCDATA is parsed character data.  An HTML parser is in
+	// stateText is parsed character data. An HTML parser is in
 	// this state when its parse position is outside an HTML tag,
 	// directive, comment, and special element body.
-	statePCDATA state = iota
+	stateText state = iota
 	// stateTag occurs before an HTML attribute or the end of a tag.
 	stateTag
-	// stateURI occurs inside an HTML attribute whose content is a URI.
-	stateURI
+	// stateAttr occurs inside an HTML attribute whose content is text.
+	stateAttr
+	// stateURL occurs inside an HTML attribute whose content is a URL.
+	stateURL
+	// stateJS occurs inside an event handler or script element.
+	stateJS
+	// stateJSDqStr occurs inside a JavaScript double quoted string.
+	stateJSDqStr
+	// stateJSSqStr occurs inside a JavaScript single quoted string.
+	stateJSSqStr
+	// stateJSRegexp occurs inside a JavaScript regexp literal.
+	stateJSRegexp
+	// stateJSBlockCmt occurs inside a JavaScript /* block comment */.
+	stateJSBlockCmt
+	// stateJSLineCmt occurs inside a JavaScript // line comment.
+	stateJSLineCmt
 	// stateError is an infectious error state outside any valid
 	// HTML/CSS/JS construct.
 	stateError
 )
 
 var stateNames = [...]string{
-	statePCDATA: "statePCDATA",
-	stateTag:    "stateTag",
-	stateURI:    "stateURI",
-	stateError:  "stateError",
+	stateText:       "stateText",
+	stateTag:        "stateTag",
+	stateAttr:       "stateAttr",
+	stateURL:        "stateURL",
+	stateJS:         "stateJS",
+	stateJSDqStr:    "stateJSDqStr",
+	stateJSSqStr:    "stateJSSqStr",
+	stateJSRegexp:   "stateJSRegexp",
+	stateJSBlockCmt: "stateJSBlockCmt",
+	stateJSLineCmt:  "stateJSLineCmt",
+	stateError:      "stateError",
 }
 
 func (s state) String() string {
-	if uint(s) < uint(len(stateNames)) {
+	if int(s) < len(stateNames) {
 		return stateNames[s]
 	}
-	return fmt.Sprintf("illegal state %d", uint(s))
+	return fmt.Sprintf("illegal state %d", s)
 }
 
 // delim is the delimiter that will end the current HTML attribute.
@@ -91,8 +112,62 @@ var delimNames = [...]string{
 }
 
 func (d delim) String() string {
-	if uint(d) < uint(len(delimNames)) {
+	if int(d) < len(delimNames) {
 		return delimNames[d]
 	}
-	return fmt.Sprintf("illegal delim %d", uint(d))
+	return fmt.Sprintf("illegal delim %d", d)
+}
+
+// urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
+// encoding strategies.
+type urlPart uint8
+
+const (
+	// urlPartNone occurs when not in a URL, or possibly at the start:
+	// ^ in "^http://auth/path?k=v#frag".
+	urlPartNone urlPart = iota
+	// urlPartPreQuery occurs in the scheme, authority, or path; between the
+	// ^s in "h^ttp://auth/path^?k=v#frag".
+	urlPartPreQuery
+	// urlPartQueryOrFrag occurs in the query portion between the ^s in
+	// "http://auth/path?^k=v#frag^".
+	urlPartQueryOrFrag
+	// urlPartUnknown occurs due to joining of contexts both before and after
+	// the query separator.
+	urlPartUnknown
+)
+
+var urlPartNames = [...]string{
+	urlPartNone:        "urlPartNone",
+	urlPartPreQuery:    "urlPartPreQuery",
+	urlPartQueryOrFrag: "urlPartQueryOrFrag",
+	urlPartUnknown:     "urlPartUnknown",
+}
+
+func (u urlPart) String() string {
+	if int(u) < len(urlPartNames) {
+		return urlPartNames[u]
+	}
+	return fmt.Sprintf("illegal urlPart %d", u)
+}
+
+// jsCtx determines whether a '/' starts a regular expression literal or a
+// division operator.
+type jsCtx uint8
+
+const (
+	// jsCtxRegexp occurs where a '/' would start a regexp literal.
+	jsCtxRegexp jsCtx = iota
+	// jsCtxDivOp occurs where a '/' would start a division operator.
+	jsCtxDivOp
+)
+
+func (c jsCtx) String() string {
+	switch c {
+	case jsCtxRegexp:
+		return "jsCtxRegexp"
+	case jsCtxDivOp:
+		return "jsCtxDivOp"
+	}
+	return fmt.Sprintf("illegal jsCtx %d", c)
 }
diff --git a/src/pkg/exp/template/html/escape.go b/src/pkg/exp/template/html/escape.go
index e0e87b9..0eb8dfe 100644
--- a/src/pkg/exp/template/html/escape.go
+++ b/src/pkg/exp/template/html/escape.go
@@ -2,104 +2,638 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package html is a specialization of exp/template that automates the
+// Package html is a specialization of template that automates the
 // construction of safe HTML output.
-// At the moment, the escaping is naive.  All dynamic content is assumed to be
-// plain text interpolated in an HTML PCDATA context.
+// INCOMPLETE.
 package html
 
 import (
+	"bytes"
+	"fmt"
+	"html"
+	"os"
+	"strings"
 	"template"
 	"template/parse"
 )
 
-// Escape rewrites each action in the template to guarantee the output is
+// Escape rewrites each action in the template to guarantee that the output is
 // HTML-escaped.
-func Escape(t *template.Template) {
-	// If the parser shares trees based on common-subexpression
-	// joining then we will need to avoid multiply escaping the same action.
-	escapeListNode(t.Tree.Root)
+func Escape(t *template.Template) (*template.Template, os.Error) {
+	c := escapeList(context{}, t.Tree.Root)
+	if c.errStr != "" {
+		return nil, fmt.Errorf("%s:%d: %s", t.Name(), c.errLine, c.errStr)
+	}
+	if c.state != stateText {
+		return nil, fmt.Errorf("%s ends in a non-text context: %v", t.Name(), c)
+	}
+	t.Funcs(funcMap)
+	return t, nil
 }
 
-// escapeNode dispatches to escape<NodeType> helpers by type.
-func escapeNode(node parse.Node) {
-	switch n := node.(type) {
-	case *parse.ListNode:
-		escapeListNode(n)
-	case *parse.TextNode:
-		// Nothing to do.
+// funcMap maps command names to functions that render their inputs safe.
+var funcMap = template.FuncMap{
+	"exp_template_html_urlfilter":       urlFilter,
+	"exp_template_html_jsvalescaper":    jsValEscaper,
+	"exp_template_html_jsstrescaper":    jsStrEscaper,
+	"exp_template_html_jsregexpescaper": jsRegexpEscaper,
+}
+
+// escape escapes a template node.
+func escape(c context, n parse.Node) context {
+	switch n := n.(type) {
 	case *parse.ActionNode:
-		escapeActionNode(n)
+		return escapeAction(c, n)
 	case *parse.IfNode:
-		escapeIfNode(n)
+		return escapeBranch(c, &n.BranchNode, "if")
+	case *parse.ListNode:
+		return escapeList(c, n)
 	case *parse.RangeNode:
-		escapeRangeNode(n)
-	case *parse.TemplateNode:
-		// Nothing to do.
+		return escapeBranch(c, &n.BranchNode, "range")
+	case *parse.TextNode:
+		return escapeText(c, n.Text)
 	case *parse.WithNode:
-		escapeWithNode(n)
+		return escapeBranch(c, &n.BranchNode, "with")
+	}
+	// TODO: handle a *parse.TemplateNode. Should Escape take a *template.Set?
+	panic("escaping " + n.String() + " is unimplemented")
+}
+
+// escapeAction escapes an action template node.
+func escapeAction(c context, n *parse.ActionNode) context {
+	s := make([]string, 0, 2)
+	switch c.state {
+	case stateURL:
+		switch c.urlPart {
+		case urlPartNone:
+			s = append(s, "exp_template_html_urlfilter")
+		case urlPartQueryOrFrag:
+			s = append(s, "urlquery")
+		case urlPartPreQuery:
+			s = append(s, "html")
+		case urlPartUnknown:
+			return context{
+				state:   stateError,
+				errLine: n.Line,
+				errStr:  fmt.Sprintf("%s appears in an ambiguous URL context", n),
+			}
+		default:
+			panic(c.urlPart.String())
+		}
+	case stateJS:
+		s = append(s, "exp_template_html_jsvalescaper")
+		if c.delim != delimNone {
+			s = append(s, "html")
+		}
+	case stateJSDqStr, stateJSSqStr:
+		s = append(s, "exp_template_html_jsstrescaper")
+	case stateJSRegexp:
+		s = append(s, "exp_template_html_jsregexpescaper")
+	case stateJSBlockCmt, stateJSLineCmt:
+		return context{
+			state:   stateError,
+			errLine: n.Line,
+			errStr:  fmt.Sprintf("%s appears inside a comment", n),
+		}
 	default:
-		panic("handling for " + node.String() + " not implemented")
-		// TODO: Handle other inner node types.
+		s = append(s, "html")
 	}
+	ensurePipelineContains(n.Pipe, s)
+	return c
 }
 
-// escapeListNode recursively escapes its input's children.
-func escapeListNode(node *parse.ListNode) {
-	if node == nil {
+// ensurePipelineContains ensures that the pipeline has commands with
+// the identifiers in s in order.
+// If the pipeline already has some of the sanitizers, do not interfere.
+// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it
+// has one matching, "html", and one to insert, "escapeJSVal", to produce
+// (.X | escapeJSVal | html).
+func ensurePipelineContains(p *parse.PipeNode, s []string) {
+	if len(s) == 0 {
 		return
 	}
-	children := node.Nodes
-	for _, child := range children {
-		escapeNode(child)
+	n := len(p.Cmds)
+	// Find the identifiers at the end of the command chain.
+	idents := p.Cmds
+	for i := n - 1; i >= 0; i-- {
+		if cmd := p.Cmds[i]; len(cmd.Args) != 0 {
+			if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
+				continue
+			}
+		}
+		idents = p.Cmds[i+1:]
 	}
+	dups := 0
+	for _, id := range idents {
+		if s[dups] == (id.Args[0].(*parse.IdentifierNode)).Ident {
+			dups++
+			if dups == len(s) {
+				return
+			}
+		}
+	}
+	newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups)
+	copy(newCmds, p.Cmds)
+	// Merge existing identifier commands with the sanitizers needed.
+	for _, id := range idents {
+		i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s)
+		if i != -1 {
+			for _, name := range s[:i] {
+				newCmds = append(newCmds, newIdentCmd(name))
+			}
+			s = s[i+1:]
+		}
+		newCmds = append(newCmds, id)
+	}
+	// Create any remaining sanitizers.
+	for _, name := range s {
+		newCmds = append(newCmds, newIdentCmd(name))
+	}
+	p.Cmds = newCmds
 }
 
-// escapeActionNode adds a pipeline call to the end that escapes the result
-// of the expression before it is interpolated into the template output.
-func escapeActionNode(node *parse.ActionNode) {
-	pipe := node.Pipe
+// indexOfStr is the least i such that strs[i] == s or -1 if s is not in strs.
+func indexOfStr(s string, strs []string) int {
+	for i, t := range strs {
+		if s == t {
+			return i
+		}
+	}
+	return -1
+}
 
-	cmds := pipe.Cmds
-	nCmds := len(cmds)
+// newIdentCmd produces a command containing a single identifier node.
+func newIdentCmd(identifier string) *parse.CommandNode {
+	return &parse.CommandNode{
+		NodeType: parse.NodeCommand,
+		Args:     []parse.Node{parse.NewIdentifier(identifier)},
+	}
+}
 
-	// If it already has an escaping command, do not interfere.
-	if nCmds != 0 {
-		if lastCmd := cmds[nCmds-1]; len(lastCmd.Args) != 0 {
-			// TODO: Recognize url and js as escaping functions once
-			// we have enough context to know whether additional
-			// escaping is necessary.
-			if arg, ok := lastCmd.Args[0].(*parse.IdentifierNode); ok && arg.Ident == "html" {
-				return
+// join joins the two contexts of a branch template node. The result is an
+// error context if either of the input contexts are error contexts, or if the
+// the input contexts differ.
+func join(a, b context, line int, nodeName string) context {
+	if a.state == stateError {
+		return a
+	}
+	if b.state == stateError {
+		return b
+	}
+	if a.eq(b) {
+		return a
+	}
+
+	c := a
+	c.urlPart = b.urlPart
+	if c.eq(b) {
+		// The contexts differ only by urlPart.
+		c.urlPart = urlPartUnknown
+		return c
+	}
+
+	return context{
+		state:   stateError,
+		errLine: line,
+		errStr:  fmt.Sprintf("{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
+	}
+}
+
+// escapeBranch escapes a branch template node: "if", "range" and "with".
+func escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
+	c0 := escapeList(c, n.List)
+	if nodeName == "range" && c0.state != stateError {
+		// The "true" branch of a "range" node can execute multiple times.
+		// We check that executing n.List once results in the same context
+		// as executing n.List twice.
+		c0 = join(c0, escapeList(c0, n.List), n.Line, nodeName)
+		if c0.state == stateError {
+			// Make clear that this is a problem on loop re-entry
+			// since developers tend to overlook that branch when
+			// debugging templates.
+			c0.errLine = n.Line
+			c0.errStr = "on range loop re-entry: " + c0.errStr
+			return c0
+		}
+	}
+	c1 := escapeList(c, n.ElseList)
+	return join(c0, c1, n.Line, nodeName)
+}
+
+// escapeList escapes a list template node.
+func escapeList(c context, n *parse.ListNode) context {
+	if n == nil {
+		return c
+	}
+	for _, m := range n.Nodes {
+		c = escape(c, m)
+	}
+	return c
+}
+
+// delimEnds maps each delim to a string of characters that terminate it.
+var delimEnds = [...]string{
+	delimDoubleQuote: `"`,
+	delimSingleQuote: "'",
+	// Determined empirically by running the below in various browsers.
+	// var div = document.createElement("DIV");
+	// for (var i = 0; i < 0x10000; ++i) {
+	//   div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>";
+	//   if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0)
+	//     document.write("<p>U+" + i.toString(16));
+	// }
+	delimSpaceOrTagEnd: " \t\n\f\r>",
+}
+
+// escapeText escapes a text template node.
+func escapeText(c context, s []byte) context {
+	for len(s) > 0 {
+		if c.delim == delimNone {
+			c, s = transitionFunc[c.state](c, s)
+			continue
+		}
+
+		i := bytes.IndexAny(s, delimEnds[c.delim])
+		if i == -1 {
+			// Remain inside the attribute.
+			// Decode the value so non-HTML rules can easily handle
+			//     <button onclick="alert("Hi!")">
+			// without having to entity decode token boundaries.
+			d := c.delim
+			c.delim = delimNone
+			c = escapeText(c, []byte(html.UnescapeString(string(s))))
+			if c.state != stateError {
+				c.delim = d
 			}
+			return c
 		}
+		if c.delim != delimSpaceOrTagEnd {
+			// Consume any quote.
+			i++
+		}
+		c, s = context{state: stateTag}, s[i:]
 	}
+	return c
+}
 
-	htmlEscapeCommand := parse.CommandNode{
-		NodeType: parse.NodeCommand,
-		Args:     []parse.Node{parse.NewIdentifier("html")},
+// transitionFunc is the array of context transition functions for text nodes.
+// A transition function takes a context and template text input, and returns
+// the updated context and any unconsumed text.
+var transitionFunc = [...]func(context, []byte) (context, []byte){
+	stateText:       tText,
+	stateTag:        tTag,
+	stateURL:        tURL,
+	stateJS:         tJS,
+	stateJSDqStr:    tJSStr,
+	stateJSSqStr:    tJSStr,
+	stateJSRegexp:   tJSRegexp,
+	stateJSBlockCmt: tJSBlockCmt,
+	stateJSLineCmt:  tJSLineCmt,
+	stateAttr:       tAttr,
+	stateError:      tError,
+}
+
+// tText is the context transition function for the text state.
+func tText(c context, s []byte) (context, []byte) {
+	for {
+		i := bytes.IndexByte(s, '<')
+		if i == -1 || i+1 == len(s) {
+			return c, nil
+		}
+		i++
+		if s[i] == '/' {
+			if i+1 == len(s) {
+				return c, nil
+			}
+			i++
+		}
+		j := eatTagName(s, i)
+		if j != i {
+			// We've found an HTML tag.
+			return context{state: stateTag}, s[j:]
+		}
+		s = s[j:]
+	}
+	panic("unreachable")
+}
+
+// tTag is the context transition function for the tag state.
+func tTag(c context, s []byte) (context, []byte) {
+	// Find the attribute name.
+	attrStart := eatWhiteSpace(s, 0)
+	i, err := eatAttrName(s, attrStart)
+	if err != nil {
+		return context{
+			state:  stateError,
+			errStr: err.String(),
+		}, nil
 	}
+	if i == len(s) {
+		return context{state: stateTag}, nil
+	}
+	state := stateAttr
+	canonAttrName := strings.ToLower(string(s[attrStart:i]))
+	if urlAttr[canonAttrName] {
+		state = stateURL
+	} else if strings.HasPrefix(canonAttrName, "on") {
+		state = stateJS
+	}
+
+	// Look for the start of the value.
+	i = eatWhiteSpace(s, i)
+	if i == len(s) {
+		return context{state: stateTag}, s[i:]
+	}
+	if s[i] == '>' {
+		return context{state: stateText}, s[i+1:]
+	} else if s[i] != '=' {
+		// Possible due to a valueless attribute or '/' in "<input />".
+		return context{state: stateTag}, s[i:]
+	}
+	// Consume the "=".
+	i = eatWhiteSpace(s, i+1)
 
-	node.Pipe.Cmds = append(node.Pipe.Cmds, &htmlEscapeCommand)
+	// Find the attribute delimiter.
+	delim := delimSpaceOrTagEnd
+	if i < len(s) {
+		switch s[i] {
+		case '\'':
+			delim, i = delimSingleQuote, i+1
+		case '"':
+			delim, i = delimDoubleQuote, i+1
+		}
+	}
+
+	return context{state: state, delim: delim}, s[i:]
 }
 
-// escapeIfNode recursively escapes the if and then clauses but leaves the
-// condition unchanged.
-func escapeIfNode(node *parse.IfNode) {
-	escapeListNode(node.List)
-	escapeListNode(node.ElseList)
+// tAttr is the context transition function for the attribute state.
+func tAttr(c context, s []byte) (context, []byte) {
+	return c, nil
 }
 
-// escapeRangeNode recursively escapes the loop body and else clause but
-// leaves the series unchanged.
-func escapeRangeNode(node *parse.RangeNode) {
-	escapeListNode(node.List)
-	escapeListNode(node.ElseList)
+// tURL is the context transition function for the URL state.
+func tURL(c context, s []byte) (context, []byte) {
+	if bytes.IndexAny(s, "#?") >= 0 {
+		c.urlPart = urlPartQueryOrFrag
+	} else if c.urlPart == urlPartNone {
+		c.urlPart = urlPartPreQuery
+	}
+	return c, nil
 }
 
-// escapeWithNode recursively escapes the scope body and else clause but
-// leaves the pipeline unchanged.
-func escapeWithNode(node *parse.WithNode) {
-	escapeListNode(node.List)
-	escapeListNode(node.ElseList)
+// tJS is the context transition function for the JS state.
+func tJS(c context, s []byte) (context, []byte) {
+	// TODO: delegate to tSpecialTagEnd to find any </script> once that CL
+	// has been merged.
+
+	i := bytes.IndexAny(s, `"'/`)
+	if i == -1 {
+		// Entire input is non string, comment, regexp tokens.
+		c.jsCtx = nextJSCtx(s, c.jsCtx)
+		return c, nil
+	}
+	c.jsCtx = nextJSCtx(s[:i], c.jsCtx)
+	switch s[i] {
+	case '"':
+		c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
+	case '\'':
+		c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
+	case '/':
+		switch {
+		case i+1 < len(s) && s[i+1] == '/':
+			c.state = stateJSLineCmt
+		case i+1 < len(s) && s[i+1] == '*':
+			c.state = stateJSBlockCmt
+		case c.jsCtx == jsCtxRegexp:
+			c.state = stateJSRegexp
+		default:
+			c.jsCtx = jsCtxRegexp
+		}
+	default:
+		panic("unreachable")
+	}
+	return c, s[i+1:]
+}
+
+// tJSStr is the context transition function for the JS string states.
+func tJSStr(c context, s []byte) (context, []byte) {
+	// TODO: delegate to tSpecialTagEnd to find any </script> once that CL
+	// has been merged.
+
+	quoteAndEsc := `\"`
+	if c.state == stateJSSqStr {
+		quoteAndEsc = `\'`
+	}
+
+	b := s
+	for {
+		i := bytes.IndexAny(b, quoteAndEsc)
+		if i == -1 {
+			return c, nil
+		}
+		if b[i] == '\\' {
+			i++
+			if i == len(b) {
+				return context{
+					state:  stateError,
+					errStr: fmt.Sprintf("unfinished escape sequence in JS string: %q", s),
+				}, nil
+			}
+		} else {
+			c.state, c.jsCtx = stateJS, jsCtxDivOp
+			return c, b[i+1:]
+		}
+		b = b[i+1:]
+	}
+	panic("unreachable")
+}
+
+// tJSRegexp is the context transition function for the /RegExp/ literal state.
+func tJSRegexp(c context, s []byte) (context, []byte) {
+	// TODO: delegate to tSpecialTagEnd to find any </script> once that CL
+	// has been merged.
+
+	b := s
+	inCharset := false
+	for {
+		i := bytes.IndexAny(b, `/[\]`)
+		if i == -1 {
+			break
+		}
+		switch b[i] {
+		case '/':
+			if !inCharset {
+				c.state, c.jsCtx = stateJS, jsCtxDivOp
+				return c, b[i+1:]
+			}
+		case '\\':
+			i++
+			if i == len(b) {
+				return context{
+					state:  stateError,
+					errStr: fmt.Sprintf("unfinished escape sequence in JS regexp: %q", s),
+				}, nil
+			}
+		case '[':
+			inCharset = true
+		case ']':
+			inCharset = false
+		default:
+			panic("unreachable")
+		}
+		b = b[i+1:]
+	}
+
+	if inCharset {
+		// This can be fixed by making context richer if interpolation
+		// into charsets is desired.
+		return context{
+			state:  stateError,
+			errStr: fmt.Sprintf("unfinished JS regexp charset: %q", s),
+		}, nil
+	}
+
+	return c, nil
+}
+
+var blockCommentEnd = []byte("*/")
+
+// tJSBlockCmt is the context transition function for the JS /*comment*/ state.
+func tJSBlockCmt(c context, s []byte) (context, []byte) {
+	// TODO: delegate to tSpecialTagEnd to find any </script> once that CL
+	// has been merged.
+
+	i := bytes.Index(s, blockCommentEnd)
+	if i == -1 {
+		return c, nil
+	}
+	c.state = stateJS
+	return c, s[i+2:]
+}
+
+// tJSLineCmt is the context transition function for the JS //comment state.
+func tJSLineCmt(c context, s []byte) (context, []byte) {
+	// TODO: delegate to tSpecialTagEnd to find any </script> once that CL
+	// has been merged.
+
+	i := bytes.IndexAny(s, "\r\n\u2028\u2029")
+	if i == -1 {
+		return c, nil
+	}
+	c.state = stateJS
+	// Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4
+	// "However, the LineTerminator at the end of the line is not
+	// considered to be part of the single-line comment; it is recognised
+	// separately by the lexical grammar and becomes part of the stream of
+	// input elements for the syntactic grammar."
+	return c, s[i:]
+}
+
+// tError is the context transition function for the error state.
+func tError(c context, s []byte) (context, []byte) {
+	return c, nil
+}
+
+// eatAttrName returns the largest j such that s[i:j] is an attribute name.
+// It returns an error if s[i:] does not look like it begins with an
+// attribute name, such as encountering a quote mark without a preceding
+// equals sign.
+func eatAttrName(s []byte, i int) (int, os.Error) {
+	for j := i; j < len(s); j++ {
+		switch s[j] {
+		case ' ', '\t', '\n', '\f', '\r', '=', '>':
+			return j, nil
+		case '\'', '"', '<':
+			// These result in a parse warning in HTML5 and are
+			// indicative of serious problems if seen in an attr
+			// name in a template.
+			return 0, fmt.Errorf("%q in attribute name: %.32q", s[j:j+1], s)
+		default:
+			// No-op.
+		}
+	}
+	return len(s), nil
+}
+
+// eatTagName returns the largest j such that s[i:j] is a tag name.
+func eatTagName(s []byte, i int) int {
+	for j := i; j < len(s); j++ {
+		x := s[j]
+		switch {
+		case 'a' <= x && x <= 'z':
+			// No-op.
+		case 'A' <= x && x <= 'Z':
+			// No-op.
+		case '0' <= x && x <= '9' && i != j:
+			// No-op.
+		default:
+			return j
+		}
+	}
+	return len(s)
+}
+
+// eatWhiteSpace returns the largest j such that s[i:j] is white space.
+func eatWhiteSpace(s []byte, i int) int {
+	for j := i; j < len(s); j++ {
+		switch s[j] {
+		case ' ', '\t', '\n', '\f', '\r':
+			// No-op.
+		default:
+			return j
+		}
+	}
+	return len(s)
+}
+
+// urlAttr is the set of attribute names whose values are URLs.
+// It consists of all "%URI"-typed attributes from
+// http://www.w3.org/TR/html4/index/attributes.html
+// as well as those attributes defined at
+// http://dev.w3.org/html5/spec/index.html#attributes-1
+// whose Value column in that table matches
+// "Valid [non-empty] URL potentially surrounded by spaces".
+var urlAttr = map[string]bool{
+	"action":     true,
+	"archive":    true,
+	"background": true,
+	"cite":       true,
+	"classid":    true,
+	"codebase":   true,
+	"data":       true,
+	"formaction": true,
+	"href":       true,
+	"icon":       true,
+	"longdesc":   true,
+	"manifest":   true,
+	"poster":     true,
+	"profile":    true,
+	"src":        true,
+	"usemap":     true,
+}
+
+// urlFilter returns the HTML equivalent of its input unless it contains an
+// unsafe protocol in which case it defangs the entire URL.
+func urlFilter(args ...interface{}) string {
+	ok := false
+	var s string
+	if len(args) == 1 {
+		s, ok = args[0].(string)
+	}
+	if !ok {
+		s = fmt.Sprint(args...)
+	}
+	i := strings.IndexRune(s, ':')
+	if i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
+		protocol := strings.ToLower(s[:i])
+		if protocol != "http" && protocol != "https" && protocol != "mailto" {
+			// Return a value that someone investigating a bug
+			// report can put into a search engine.
+			return "#ZgotmplZ"
+		}
+	}
+	// TODO: Once we handle <style>#id { background: url({{.Img}}) }</style>
+	// we will need to stop this from HTML escaping and pipeline sanitizers.
+	return template.HTMLEscapeString(s)
 }
diff --git a/src/pkg/exp/template/html/escape_test.go b/src/pkg/exp/template/html/escape_test.go
index 345a752..6f5ecf6 100644
--- a/src/pkg/exp/template/html/escape_test.go
+++ b/src/pkg/exp/template/html/escape_test.go
@@ -6,70 +6,612 @@ package html
 
 import (
 	"bytes"
+	"strings"
 	"template"
+	"template/parse"
 	"testing"
 )
 
-type data struct {
-	F, T    bool
-	C, G, H string
-	A, E    []string
-}
+func TestEscape(t *testing.T) {
+	var data = struct {
+		F, T    bool
+		C, G, H string
+		A, E    []string
+		N       int
+		Z       *int
+	}{
+		F: false,
+		T: true,
+		C: "<Cincinatti>",
+		G: "<Goodbye>",
+		H: "<Hello>",
+		A: []string{"<a>", "<b>"},
+		E: []string{},
+		N: 42,
+		Z: nil,
+	}
 
-var testData = data{
-	F: false,
-	T: true,
-	C: "<Cincinatti>",
-	G: "<Goodbye>",
-	H: "<Hello>",
-	A: []string{"<a>", "<b>"},
-	E: []string{},
-}
+	tests := []struct {
+		name   string
+		input  string
+		output string
+	}{
+		{
+			"if",
+			"{{if .T}}Hello{{end}}, {{.C}}!",
+			"Hello, <Cincinatti>!",
+		},
+		{
+			"else",
+			"{{if .F}}{{.H}}{{else}}{{.G}}{{end}}!",
+			"<Goodbye>!",
+		},
+		{
+			"overescaping",
+			"Hello, {{.C | html}}!",
+			"Hello, <Cincinatti>!",
+		},
+		{
+			"assignment",
+			"{{if $x := .H}}{{$x}}{{end}}",
+			"<Hello>",
+		},
+		{
+			"withBody",
+			"{{with .H}}{{.}}{{end}}",
+			"<Hello>",
+		},
+		{
+			"withElse",
+			"{{with .E}}{{.}}{{else}}{{.H}}{{end}}",
+			"<Hello>",
+		},
+		{
+			"rangeBody",
+			"{{range .A}}{{.}}{{end}}",
+			"<a><b>",
+		},
+		{
+			"rangeElse",
+			"{{range .E}}{{.}}{{else}}{{.H}}{{end}}",
+			"<Hello>",
+		},
+		{
+			"nonStringValue",
+			"{{.T}}",
+			"true",
+		},
+		{
+			// TODO: Make sure the URL escaper escapes single quotes so it can
+			// be embedded in single quoted URI attributes and CSS url(...)
+			// constructs. Single quotes are reserved in URLs, but are only used
+			// in the obsolete "mark" rule in an appendix in RFC 3986 so can be
+			// safely encoded.
+			"constant",
+			`<a href="/search?q={{"'a<b'"}}">`,
+			`<a href="/search?q='a%3Cb'">`,
+		},
+		{
+			"multipleAttrs",
+			"<a b=1 c={{.H}}>",
+			"<a b=1 c=<Hello>>",
+		},
+		{
+			"urlStartRel",
+			`<a href='{{"/foo/bar?a=b&c=d"}}'>`,
+			`<a href='/foo/bar?a=b&c=d'>`,
+		},
+		{
+			"urlStartAbsOk",
+			`<a href='{{"http://example.com/foo/bar?a=b&c=d"}}'>`,
+			`<a href='http://example.com/foo/bar?a=b&c=d'>`,
+		},
+		{
+			"protocolRelativeURLStart",
+			`<a href='{{"//example.com:8000/foo/bar?a=b&c=d"}}'>`,
+			`<a href='//example.com:8000/foo/bar?a=b&c=d'>`,
+		},
+		{
+			"pathRelativeURLStart",
+			`<a href="{{"/javascript:80/foo/bar"}}">`,
+			`<a href="/javascript:80/foo/bar">`,
+		},
+		{
+			"dangerousURLStart",
+			`<a href='{{"javascript:alert(%22pwned%22)"}}'>`,
+			`<a href='#ZgotmplZ'>`,
+		},
+		{
+			"urlPath",
+			`<a href='http://{{"javascript:80"}}/foo'>`,
+			`<a href='http://javascript:80/foo'>`,
+		},
+		{
+			"urlQuery",
+			`<a href='/search?q={{.H}}'>`,
+			`<a href='/search?q=%3CHello%3E'>`,
+		},
+		{
+			"urlFragment",
+			`<a href='/faq#{{.H}}'>`,
+			`<a href='/faq#%3CHello%3E'>`,
+		},
+		{
+			"urlBranch",
+			`<a href="{{if .F}}/foo?a=b{{else}}/bar{{end}}">`,
+			`<a href="/bar">`,
+		},
+		{
+			"urlBranchConflictMoot",
+			`<a href="{{if .T}}/foo?a={{else}}/bar#{{end}}{{.C}}">`,
+			`<a href="/foo?a=%3CCincinatti%3E">`,
+		},
+		{
+			"jsStrValue",
+			"<button onclick='alert({{.H}})'>",
+			`<button onclick='alert("\u003cHello\u003e")'>`,
+		},
+		{
+			"jsNumericValue",
+			"<button onclick='alert({{.N}})'>",
+			`<button onclick='alert( 42 )'>`,
+		},
+		{
+			"jsBoolValue",
+			"<button onclick='alert({{.T}})'>",
+			`<button onclick='alert( true )'>`,
+		},
+		{
+			"jsNilValue",
+			"<button onclick='alert(typeof{{.Z}})'>",
+			`<button onclick='alert(typeof null )'>`,
+		},
+		{
+			"jsObjValue",
+			"<button onclick='alert({{.A}})'>",
+			`<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`,
+		},
+		{
+			"jsObjValueNotOverEscaped",
+			"<button onclick='alert({{.A | html}})'>",
+			`<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`,
+		},
+		{
+			"jsStr",
+			"<button onclick='alert("{{.H}}")'>",
+			`<button onclick='alert("\x3cHello\x3e")'>`,
+		},
+		{
+			"jsStrNotUnderEscaped",
+			"<button onclick='alert({{.C | urlquery}})'>",
+			// URL escaped, then quoted for JS.
+			`<button onclick='alert("%3CCincinatti%3E")'>`,
+		},
+		{
+			"jsRe",
+			"<button onclick='alert("{{.H}}")'>",
+			`<button onclick='alert("\x3cHello\x3e")'>`,
+		},
+	}
 
-type testCase struct {
-	name   string
-	input  string
-	output string
+	for _, test := range tests {
+		tmpl := template.Must(template.New(test.name).Parse(test.input))
+		tmpl, err := Escape(tmpl)
+		b := new(bytes.Buffer)
+		if err = tmpl.Execute(b, data); err != nil {
+			t.Errorf("%s: template execution failed: %s", test.name, err)
+			continue
+		}
+		if w, g := test.output, b.String(); w != g {
+			t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
+			continue
+		}
+	}
 }
 
-var testCases = []testCase{
-	{"if", "{{if .T}}Hello{{end}}, {{.C}}!", "Hello, <Cincinatti>!"},
-	{"else", "{{if .F}}{{.H}}{{else}}{{.G}}{{end}}!", "<Goodbye>!"},
-	{"overescaping", "Hello, {{.C | html}}!", "Hello, <Cincinatti>!"},
-	{"assignment", "{{if $x := .H}}{{$x}}{{end}}", "<Hello>"},
-	{"withBody", "{{with .H}}{{.}}{{end}}", "<Hello>"},
-	{"withElse", "{{with .E}}{{.}}{{else}}{{.H}}{{end}}", "<Hello>"},
-	{"rangeBody", "{{range .A}}{{.}}{{end}}", "<a><b>"},
-	{"rangeElse", "{{range .E}}{{.}}{{else}}{{.H}}{{end}}", "<Hello>"},
-	{"nonStringValue", "{{.T}}", "true"},
-	{"constant", `<a href="{{"'str'"}}">`, `<a href="'str'">`},
-}
+func TestErrors(t *testing.T) {
+	tests := []struct {
+		input string
+		err   string
+	}{
+		// Non-error cases.
+		{
+			"{{if .Cond}}<a>{{else}}<b>{{end}}",
+			"",
+		},
+		{
+			"{{if .Cond}}<a>{{end}}",
+			"",
+		},
+		{
+			"{{if .Cond}}{{else}}<b>{{end}}",
+			"",
+		},
+		{
+			"{{with .Cond}}<div>{{end}}",
+			"",
+		},
+		{
+			"{{range .Items}}<a>{{end}}",
+			"",
+		},
+		{
+			"<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>",
+			"",
+		},
+		// Error cases.
+		{
+			"{{if .Cond}}<a{{end}}",
+			"z:1: {{if}} branches",
+		},
+		{
+			"{{if .Cond}}\n{{else}}\n<a{{end}}",
+			"z:1: {{if}} branches",
+		},
+		{
+			// Missing quote in the else branch.
+			`{{if .Cond}}<a href="foo">{{else}}<a href="bar>{{end}}`,
+			"z:1: {{if}} branches",
+		},
+		{
+			// Different kind of attribute: href implies a URL.
+			"<a {{if .Cond}}href='{{else}}title='{{end}}{{.X}}'>",
+			"z:1: {{if}} branches",
+		},
+		{
+			"\n{{with .X}}<a{{end}}",
+			"z:2: {{with}} branches",
+		},
+		{
+			"\n{{with .X}}<a>{{else}}<a{{end}}",
+			"z:2: {{with}} branches",
+		},
+		{
+			"{{range .Items}}<a{{end}}",
+			`z:1: on range loop re-entry: "<" in attribute name: "<a"`,
+		},
+		{
+			"\n{{range .Items}} x='<a{{end}}",
+			"z:2: on range loop re-entry: {{range}} branches",
+		},
+		{
+			"<a b=1 c={{.H}}",
+			"z ends in a non-text context: {stateAttr delimSpaceOrTagEnd",
+		},
+		{
+			`<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`,
+			"z:1: (action: [(command: [F=[H]])]) appears in an ambiguous URL context",
+		},
+		{
+			`<a onclick="alert('Hello \`,
+			`unfinished escape sequence in JS string: "Hello \\"`,
+		},
+		{
+			`<a onclick='alert("Hello\, World\`,
+			`unfinished escape sequence in JS string: "Hello\\, World\\"`,
+		},
+		{
+			`<a onclick='alert(/x+\`,
+			`unfinished escape sequence in JS regexp: "x+\\"`,
+		},
+		{
+			`<a onclick="/foo[\]/`,
+			`unfinished JS regexp charset: "foo[\\]/"`,
+		},
+		{
+			`<a onclick="/* alert({{.X}} */">`,
+			`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
+		},
+		{
+			`<a onclick="// alert({{.X}}">`,
+			`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
+		},
+	}
 
-func TestAutoesc(t *testing.T) {
-	for _, testCase := range testCases {
-		name := testCase.name
-		tmpl := template.New(name)
-		tmpl, err := tmpl.Parse(testCase.input)
-		if err != nil {
-			t.Errorf("%s: failed to parse template: %s", name, err)
+	for _, test := range tests {
+		tmpl := template.Must(template.New("z").Parse(test.input))
+		var got string
+		if _, err := Escape(tmpl); err != nil {
+			got = err.String()
+		}
+		if test.err == "" {
+			if got != "" {
+				t.Errorf("input=%q: unexpected error %q", test.input, got)
+			}
 			continue
 		}
+		if strings.Index(got, test.err) == -1 {
+			t.Errorf("input=%q: error %q does not contain expected string %q", test.input, got, test.err)
+			continue
+		}
+	}
+}
 
-		Escape(tmpl)
-
-		buffer := new(bytes.Buffer)
+func TestEscapeText(t *testing.T) {
+	tests := []struct {
+		input  string
+		output context
+	}{
+		{
+			``,
+			context{},
+		},
+		{
+			`Hello, World!`,
+			context{},
+		},
+		{
+			// An orphaned "<" is OK.
+			`I <3 Ponies!`,
+			context{},
+		},
+		{
+			`<a`,
+			context{state: stateTag},
+		},
+		{
+			`<a `,
+			context{state: stateTag},
+		},
+		{
+			`<a>`,
+			context{state: stateText},
+		},
+		{
+			`<a href=`,
+			context{state: stateURL, delim: delimSpaceOrTagEnd},
+		},
+		{
+			`<a href=x`,
+			context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href=x `,
+			context{state: stateTag},
+		},
+		{
+			`<a href=>`,
+			context{state: stateText},
+		},
+		{
+			`<a href=x>`,
+			context{state: stateText},
+		},
+		{
+			`<a href ='`,
+			context{state: stateURL, delim: delimSingleQuote},
+		},
+		{
+			`<a href=''`,
+			context{state: stateTag},
+		},
+		{
+			`<a href= "`,
+			context{state: stateURL, delim: delimDoubleQuote},
+		},
+		{
+			`<a href=""`,
+			context{state: stateTag},
+		},
+		{
+			`<a title="`,
+			context{state: stateAttr, delim: delimDoubleQuote},
+		},
+		{
+			`<a HREF='http:`,
+			context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a Href='/`,
+			context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href='"`,
+			context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href="'`,
+			context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href=''`,
+			context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href=""`,
+			context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href=""`,
+			context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+		},
+		{
+			`<a href="`,
+			context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
+		},
+		{
+			`<img alt="1">`,
+			context{state: stateText},
+		},
+		{
+			`<img alt="1>"`,
+			context{state: stateTag},
+		},
+		{
+			`<img alt="1>">`,
+			context{state: stateText},
+		},
+		{
+			`<input checked type="checkbox"`,
+			context{state: stateTag},
+		},
+		{
+			`<a onclick="`,
+			context{state: stateJS, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="//foo`,
+			context{state: stateJSLineCmt, delim: delimDoubleQuote},
+		},
+		{
+			"<a onclick='//\n",
+			context{state: stateJS, delim: delimSingleQuote},
+		},
+		{
+			"<a onclick='//\r\n",
+			context{state: stateJS, delim: delimSingleQuote},
+		},
+		{
+			"<a onclick='//\u2028",
+			context{state: stateJS, delim: delimSingleQuote},
+		},
+		{
+			`<a onclick="/*`,
+			context{state: stateJSBlockCmt, delim: delimDoubleQuote},
+		},
+		{
+			`<a onkeypress=""`,
+			context{state: stateJSDqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick='"foo"`,
+			context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick='foo'`,
+			context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick='foo`,
+			context{state: stateJSSqStr, delim: delimSpaceOrTagEnd},
+		},
+		{
+			`<a onclick=""foo'`,
+			context{state: stateJSDqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="'foo"`,
+			context{state: stateJSSqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<A ONCLICK="'`,
+			context{state: stateJSSqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="/`,
+			context{state: stateJSRegexp, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="'foo'`,
+			context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick="'foo\'`,
+			context{state: stateJSSqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="'foo\'`,
+			context{state: stateJSSqStr, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="/foo/`,
+			context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick="1 /foo`,
+			context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick="1 /*c*/ /foo`,
+			context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+		},
+		{
+			`<a onclick="/foo[/]`,
+			context{state: stateJSRegexp, delim: delimDoubleQuote},
+		},
+		{
+			`<a onclick="/foo\/`,
+			context{state: stateJSRegexp, delim: delimDoubleQuote},
+		},
+	}
 
-		err = tmpl.Execute(buffer, testData)
-		if err != nil {
-			t.Errorf("%s: template execution failed: %s", name, err)
+	for _, test := range tests {
+		b := []byte(test.input)
+		c := escapeText(context{}, b)
+		if !test.output.eq(c) {
+			t.Errorf("input %q: want context\n\t%v\ngot\n\t%v", test.input, test.output, c)
+			continue
+		}
+		if test.input != string(b) {
+			t.Errorf("input %q: text node was modified: want %q got %q", test.input, test.input, b)
 			continue
 		}
+	}
+}
 
-		output := testCase.output
-		actual := buffer.String()
-		if output != actual {
-			t.Errorf("%s: escaped output: %q != %q",
-				name, output, actual)
+func TestEnsurePipelineContains(t *testing.T) {
+	tests := []struct {
+		input, output string
+		ids           []string
+	}{
+		{
+			"{{.X}}",
+			"[(command: [F=[X]])]",
+			[]string{},
+		},
+		{
+			"{{.X | html}}",
+			"[(command: [F=[X]]) (command: [I=html])]",
+			[]string{},
+		},
+		{
+			"{{.X}}",
+			"[(command: [F=[X]]) (command: [I=html])]",
+			[]string{"html"},
+		},
+		{
+			"{{.X | html}}",
+			"[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]",
+			[]string{"urlquery"},
+		},
+		{
+			"{{.X | html | urlquery}}",
+			"[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]",
+			[]string{"urlquery"},
+		},
+		{
+			"{{.X | html | urlquery}}",
+			"[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]",
+			[]string{"html", "urlquery"},
+		},
+		{
+			"{{.X | html | urlquery}}",
+			"[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]",
+			[]string{"html"},
+		},
+		{
+			"{{.X | urlquery}}",
+			"[(command: [F=[X]]) (command: [I=html]) (command: [I=urlquery])]",
+			[]string{"html", "urlquery"},
+		},
+		{
+			"{{.X | html | print}}",
+			"[(command: [F=[X]]) (command: [I=urlquery]) (command: [I=html]) (command: [I=print])]",
+			[]string{"urlquery", "html"},
+		},
+	}
+	for _, test := range tests {
+		tmpl := template.Must(template.New("test").Parse(test.input))
+		action, ok := (tmpl.Tree.Root.Nodes[0].(*parse.ActionNode))
+		if !ok {
+			t.Errorf("First node is not an action: %s", test.input)
+			continue
+		}
+		pipe := action.Pipe
+		ensurePipelineContains(pipe, test.ids)
+		got := pipe.String()
+		if got != test.output {
+			t.Errorf("%s, %v: want\n\t%s\ngot\n\t%s", test.input, test.ids, test.output, got)
 		}
 	}
 }
diff --git a/src/pkg/exp/template/html/js.go b/src/pkg/exp/template/html/js.go
new file mode 100644
index 0000000..65479bc
--- /dev/null
+++ b/src/pkg/exp/template/html/js.go
@@ -0,0 +1,290 @@
+// 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 (
+	"bytes"
+	"fmt"
+	"json"
+	"strings"
+	"utf8"
+)
+
+// nextJSCtx returns the context that determines whether a slash after the
+// given run of tokens tokens starts a regular expression instead of a division
+// operator: / or /=.
+//
+// This assumes that the token run does not include any string tokens, comment
+// tokens, regular expression literal tokens, or division operators.
+//
+// This fails on some valid but nonsensical JavaScript programs like
+// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to
+// fail on any known useful programs. It is based on the draft
+// JavaScript 2.0 lexical grammar and requires one token of lookbehind:
+// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html
+func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
+	s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029")
+	if len(s) == 0 {
+		return preceding
+	}
+
+	// All cases below are in the single-byte UTF-8 group.
+	switch c, n := s[len(s)-1], len(s); c {
+	case '+', '-':
+		// ++ and -- are not regexp preceders, but + and - are whether
+		// they are used as infix or prefix operators.
+		start := n - 1
+		// Count the number of adjacent dashes or pluses.
+		for start > 0 && s[start-1] == c {
+			start--
+		}
+		if (n-start)&1 == 1 {
+			// Reached for trailing minus signs since "---" is the
+			// same as "-- -".
+			return jsCtxRegexp
+		}
+		return jsCtxDivOp
+	case '.':
+		// Handle "42."
+		if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' {
+			return jsCtxDivOp
+		}
+		return jsCtxRegexp
+	// Suffixes for all punctuators from section 7.7 of the language spec
+	// that only end binary operators not handled above.
+	case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?':
+		return jsCtxRegexp
+	// Suffixes for all punctuators from section 7.7 of the language spec
+	// that are prefix operators not handled above.
+	case '!', '~':
+		return jsCtxRegexp
+	// Matches all the punctuators from section 7.7 of the language spec
+	// that are open brackets not handled above.
+	case '(', '[':
+		return jsCtxRegexp
+	// Matches all the punctuators from section 7.7 of the language spec
+	// that precede expression starts.
+	case ':', ';', '{':
+		return jsCtxRegexp
+	// CAVEAT: the close punctuators ('}', ']', ')') precede div ops and
+	// are handled in the default except for '}' which can precede a
+	// division op as in
+	//    ({ valueOf: function () { return 42 } } / 2
+	// which is valid, but, in practice, developers don't divide object
+	// literals, so our heuristic works well for code like
+	//    function () { ... }  /foo/.test(x) && sideEffect();
+	// The ')' punctuator can precede a regular expression as in
+	//     if (b) /foo/.test(x) && ...
+	// but this is much less likely than
+	//     (a + b) / c
+	case '}':
+		return jsCtxRegexp
+	default:
+		// Look for an IdentifierName and see if it is a keyword that
+		// can precede a regular expression.
+		j := n
+		for j > 0 && isJSIdentPart(int(s[j-1])) {
+			j--
+		}
+		if regexpPrecederKeywords[string(s[j:])] {
+			return jsCtxRegexp
+		}
+	}
+	// Otherwise is a punctuator not listed above, or
+	// a string which precedes a div op, or an identifier
+	// which precedes a div op.
+	return jsCtxDivOp
+}
+
+// regexPrecederKeywords is a set of reserved JS keywords that can precede a
+// regular expression in JS source.
+var regexpPrecederKeywords = map[string]bool{
+	"break":      true,
+	"case":       true,
+	"continue":   true,
+	"delete":     true,
+	"do":         true,
+	"else":       true,
+	"finally":    true,
+	"in":         true,
+	"instanceof": true,
+	"return":     true,
+	"throw":      true,
+	"try":        true,
+	"typeof":     true,
+	"void":       true,
+}
+
+// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
+// nether side-effects nor free variables outside (NaN, Infinity).
+func jsValEscaper(args ...interface{}) string {
+	var a interface{}
+	if len(args) == 1 {
+		a = args[0]
+	} else {
+		a = fmt.Sprint(args...)
+	}
+	// TODO: detect cycles before calling Marshal which loops infinitely on
+	// cyclic data. This may be an unnacceptable DoS risk.
+
+	// TODO: make sure that json.Marshal escapes codepoints U+2028 & U+2029
+	// so it falls within the subset of JSON which is valid JS and maybe
+	// post-process to prevent it from containing
+	// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
+	// in case custom marshallers produce output containing those.
+
+	// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
+
+	// TODO: JSON allows arbitrary unicode codepoints, but EcmaScript
+	// defines a SourceCharacter as either a UTF-16 or UCS-2 code-unit.
+	// Determine whether supplemental codepoints in UTF-8 encoded JS inside
+	// string literals are properly interpreted by major interpreters.
+
+	b, err := json.Marshal(a)
+	if err != nil {
+		// Put a space before comment so that if it is flush against
+		// a division operator it is not turned into a line comment:
+		//     x/{{y}}
+		// turning into
+		//     x//* error marshalling y:
+		//          second line of error message */null
+		return fmt.Sprintf(" /* %s */null ", strings.Replace(err.String(), "*/", "* /", -1))
+	}
+	if len(b) != 0 {
+		first, _ := utf8.DecodeRune(b)
+		last, _ := utf8.DecodeLastRune(b)
+		if isJSIdentPart(first) || isJSIdentPart(last) {
+			return " " + string(b) + " "
+		}
+	}
+	return string(b)
+}
+
+// jsStrEscaper produces a string that can be included between quotes in
+// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
+// or in an HTML5 event handler attribute such as onclick.
+func jsStrEscaper(args ...interface{}) string {
+	return replace(stringify(args...), jsStrReplacementTable)
+}
+
+// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
+// specials so the result is treated literally when included in a regular
+// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
+// the literal text of {{.X}} followed by the string "bar".
+func jsRegexpEscaper(args ...interface{}) string {
+	return replace(stringify(args...), jsRegexpReplacementTable)
+}
+
+// stringify is an optimized form of fmt.Sprint.
+func stringify(args ...interface{}) string {
+	if len(args) == 1 {
+		if s, ok := args[0].(string); ok {
+			return s
+		}
+	}
+	return fmt.Sprint(args...)
+}
+
+// replace replaces each rune r of s with replacementTable[r], provided that
+// r < len(replacementTable). If replacementTable[r] is the empty string then
+// no replacement is made.
+// It also replaces the runes '\u2028' and '\u2029' with the strings
+// `\u2028` and `\u2029`. Note the different quotes used.
+func replace(s string, replacementTable []string) string {
+	var b bytes.Buffer
+	written := 0
+	for i, r := range s {
+		var repl string
+		switch {
+		case r < len(replacementTable) && replacementTable[r] != "":
+			repl = replacementTable[r]
+		case r == '\u2028':
+			repl = `\u2028`
+		case r == '\u2029':
+			repl = `\u2029`
+		default:
+			continue
+		}
+		b.WriteString(s[written:i])
+		b.WriteString(repl)
+		written = i + utf8.RuneLen(r)
+	}
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
+
+var jsStrReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\t`,
+	'\n': `\n`,
+	'\v': `\x0b`, // "\v" == "v" on IE 6.
+	'\f': `\f`,
+	'\r': `\r`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\x22`,
+	'&':  `\x26`,
+	'\'': `\x27`,
+	'+':  `\x2b`,
+	'/':  `\/`,
+	'<':  `\x3c`,
+	'>':  `\x3e`,
+	'\\': `\\`,
+}
+
+var jsRegexpReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\t`,
+	'\n': `\n`,
+	'\v': `\x0b`, // "\v" == "v" on IE 6.
+	'\f': `\f`,
+	'\r': `\r`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\x22`,
+	'$':  `\$`,
+	'&':  `\x26`,
+	'\'': `\x27`,
+	'(':  `\(`,
+	')':  `\)`,
+	'*':  `\*`,
+	'+':  `\x2b`,
+	'-':  `\-`,
+	'.':  `\.`,
+	'/':  `\/`,
+	'<':  `\x3c`,
+	'>':  `\x3e`,
+	'?':  `\?`,
+	'[':  `\[`,
+	'\\': `\\`,
+	']':  `\]`,
+	'^':  `\^`,
+	'{':  `\{`,
+	'|':  `\|`,
+	'}':  `\}`,
+}
+
+// isJSIdentPart is true if the given rune is a JS identifier part.
+// It does not handle all the non-Latin letters, joiners, and combining marks,
+// but it does handle every codepoint that can occur in a numeric literal or
+// a keyword.
+func isJSIdentPart(rune int) bool {
+	switch {
+	case '$' == rune:
+		return true
+	case '0' <= rune && rune <= '9':
+		return true
+	case 'A' <= rune && rune <= 'Z':
+		return true
+	case '_' == rune:
+		return true
+	case 'a' <= rune && rune <= 'z':
+		return true
+	}
+	return false
+}
diff --git a/src/pkg/exp/template/html/js_test.go b/src/pkg/exp/template/html/js_test.go
new file mode 100644
index 0000000..0ae70d1
--- /dev/null
+++ b/src/pkg/exp/template/html/js_test.go
@@ -0,0 +1,356 @@
+// 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 (
+	"bytes"
+	"math"
+	"strings"
+	"testing"
+)
+
+func TestNextJsCtx(t *testing.T) {
+	tests := []struct {
+		jsCtx jsCtx
+		s     string
+	}{
+		// Statement terminators precede regexps.
+		{jsCtxRegexp, ";"},
+		// This is not airtight.
+		//     ({ valueOf: function () { return 1 } } / 2)
+		// is valid JavaScript but in practice, devs do not do this.
+		// A block followed by a statement starting with a RegExp is
+		// much more common:
+		//     while (x) {...} /foo/.test(x) || panic()
+		{jsCtxRegexp, "}"},
+		// But member, call, grouping, and array expression terminators
+		// precede div ops.
+		{jsCtxDivOp, ")"},
+		{jsCtxDivOp, "]"},
+		// At the start of a primary expression, array, or expression
+		// statement, expect a regexp.
+		{jsCtxRegexp, "("},
+		{jsCtxRegexp, "["},
+		{jsCtxRegexp, "{"},
+		// Assignment operators precede regexps as do all exclusively
+		// prefix and binary operators.
+		{jsCtxRegexp, "="},
+		{jsCtxRegexp, "+="},
+		{jsCtxRegexp, "*="},
+		{jsCtxRegexp, "*"},
+		{jsCtxRegexp, "!"},
+		// Whether the + or - is infix or prefix, it cannot precede a
+		// div op.
+		{jsCtxRegexp, "+"},
+		{jsCtxRegexp, "-"},
+		// An incr/decr op precedes a div operator.
+		// This is not airtight. In (g = ++/h/i) a regexp follows a
+		// pre-increment operator, but in practice devs do not try to
+		// increment or decrement regular expressions.
+		// (g++/h/i) where ++ is a postfix operator on g is much more
+		// common.
+		{jsCtxDivOp, "--"},
+		{jsCtxDivOp, "++"},
+		{jsCtxDivOp, "x--"},
+		// When we have many dashes or pluses, then they are grouped
+		// left to right.
+		{jsCtxRegexp, "x---"}, // A postfix -- then a -.
+		// return followed by a slash returns the regexp literal or the
+		// slash starts a regexp literal in an expression statement that
+		// is dead code.
+		{jsCtxRegexp, "return"},
+		{jsCtxRegexp, "return "},
+		{jsCtxRegexp, "return\t"},
+		{jsCtxRegexp, "return\n"},
+		{jsCtxRegexp, "return\u2028"},
+		// Identifiers can be divided and cannot validly be preceded by
+		// a regular expressions. Semicolon insertion cannot happen
+		// between an identifier and a regular expression on a new line
+		// because the one token lookahead for semicolon insertion has
+		// to conclude that it could be a div binary op and treat it as
+		// such.
+		{jsCtxDivOp, "x"},
+		{jsCtxDivOp, "x "},
+		{jsCtxDivOp, "x\t"},
+		{jsCtxDivOp, "x\n"},
+		{jsCtxDivOp, "x\u2028"},
+		{jsCtxDivOp, "preturn"},
+		// Numbers precede div ops.
+		{jsCtxDivOp, "0"},
+		// Dots that are part of a number are div preceders.
+		{jsCtxDivOp, "0."},
+	}
+
+	for _, test := range tests {
+		if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx {
+			t.Errorf("want %s got %q", test.jsCtx, test.s)
+		}
+		if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx {
+			t.Errorf("want %s got %q", test.jsCtx, test.s)
+		}
+	}
+
+	if nextJSCtx([]byte("   "), jsCtxRegexp) != jsCtxRegexp {
+		t.Error("Blank tokens")
+	}
+
+	if nextJSCtx([]byte("   "), jsCtxDivOp) != jsCtxDivOp {
+		t.Error("Blank tokens")
+	}
+}
+
+func TestJSValEscaper(t *testing.T) {
+	tests := []struct {
+		x  interface{}
+		js string
+	}{
+		{int(42), " 42 "},
+		{uint(42), " 42 "},
+		{int16(42), " 42 "},
+		{uint16(42), " 42 "},
+		{int32(-42), " -42 "},
+		{uint32(42), " 42 "},
+		{int16(-42), " -42 "},
+		{uint16(42), " 42 "},
+		{int64(-42), " -42 "},
+		{uint64(42), " 42 "},
+		{uint64(1) << 53, " 9007199254740992 "},
+		// ulp(1 << 53) > 1 so this loses precision in JS
+		// but it is still a representable integer literal.
+		{uint64(1)<<53 + 1, " 9007199254740993 "},
+		{float32(1.0), " 1 "},
+		{float32(-1.0), " -1 "},
+		{float32(0.5), " 0.5 "},
+		{float32(-0.5), " -0.5 "},
+		{float32(1.0) / float32(256), " 0.00390625 "},
+		{float32(0), " 0 "},
+		{math.Copysign(0, -1), " -0 "},
+		{float64(1.0), " 1 "},
+		{float64(-1.0), " -1 "},
+		{float64(0.5), " 0.5 "},
+		{float64(-0.5), " -0.5 "},
+		{float64(0), " 0 "},
+		{math.Copysign(0, -1), " -0 "},
+		{"", `""`},
+		{"foo", `"foo"`},
+		// Newlines.
+		// {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, // TODO: FAILING. Maybe fix in json package.
+		// "\v" == "v" on IE 6 so use "\x0b" instead.
+		{"\t\x0b", `"\u0009\u000b"`},
+		{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
+		{[]interface{}{}, "[]"},
+		{[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
+		{"<!--", `"\u003c!--"`},
+		{"-->", `"--\u003e"`},
+		{"<![CDATA[", `"\u003c![CDATA["`},
+		{"]]>", `"]]\u003e"`},
+		{"</script", `"\u003c/script"`},
+		{"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
+	}
+
+	for _, test := range tests {
+		if js := jsValEscaper(test.x); js != test.js {
+			t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
+		}
+		// Make sure that escaping corner cases are not broken
+		// by nesting.
+		a := []interface{}{test.x}
+		want := "[" + strings.TrimSpace(test.js) + "]"
+		if js := jsValEscaper(a); js != want {
+			t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js)
+		}
+	}
+}
+
+func TestJSStrEscaper(t *testing.T) {
+	tests := []struct {
+		x   interface{}
+		esc string
+	}{
+		{"", ``},
+		{"foo", `foo`},
+		{"\u0000", `\0`},
+		{"\t", `\t`},
+		{"\n", `\n`},
+		{"\r", `\r`},
+		{"\u2028", `\u2028`},
+		{"\u2029", `\u2029`},
+		{"\\", `\\`},
+		{"\\n", `\\n`},
+		{"foo\r\nbar", `foo\r\nbar`},
+		// Preserve attribute boundaries.
+		{`"`, `\x22`},
+		{`'`, `\x27`},
+		// Allow embedding in HTML without further escaping.
+		{`&`, `\x26amp;`},
+		// Prevent breaking out of text node and element boundaries.
+		{"</script>", `\x3c\/script\x3e`},
+		{"<![CDATA[", `\x3c![CDATA[`},
+		{"]]>", `]]\x3e`},
+		// http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
+		//   "The text in style, script, title, and textarea elements
+		//   must not have an escaping text span start that is not
+		//   followed by an escaping text span end."
+		// Furthermore, spoofing an escaping text span end could lead
+		// to different interpretation of a </script> sequence otherwise
+		// masked by the escaping text span, and spoofing a start could
+		// allow regular text content to be interpreted as script
+		// allowing script execution via a combination of a JS string
+		// injection followed by an HTML text injection.
+		{"<!--", `\x3c!--`},
+		{"-->", `--\x3e`},
+		// From http://code.google.com/p/doctype/wiki/ArticleUtf7
+		{"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
+			`\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
+		},
+		// Invalid UTF-8 sequence
+		{"foo\xA0bar", "foo\xA0bar"},
+		// Invalid unicode scalar value.
+		{"foo\xed\xa0\x80bar", "foo\xed\xa0\x80bar"},
+	}
+
+	for _, test := range tests {
+		esc := jsStrEscaper(test.x)
+		if esc != test.esc {
+			t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
+		}
+	}
+}
+
+func TestJSRegexpEscaper(t *testing.T) {
+	tests := []struct {
+		x   interface{}
+		esc string
+	}{
+		{"", ``},
+		{"foo", `foo`},
+		{"\u0000", `\0`},
+		{"\t", `\t`},
+		{"\n", `\n`},
+		{"\r", `\r`},
+		{"\u2028", `\u2028`},
+		{"\u2029", `\u2029`},
+		{"\\", `\\`},
+		{"\\n", `\\n`},
+		{"foo\r\nbar", `foo\r\nbar`},
+		// Preserve attribute boundaries.
+		{`"`, `\x22`},
+		{`'`, `\x27`},
+		// Allow embedding in HTML without further escaping.
+		{`&`, `\x26amp;`},
+		// Prevent breaking out of text node and element boundaries.
+		{"</script>", `\x3c\/script\x3e`},
+		{"<![CDATA[", `\x3c!\[CDATA\[`},
+		{"]]>", `\]\]\x3e`},
+		// Escaping text spans.
+		{"<!--", `\x3c!\-\-`},
+		{"-->", `\-\-\x3e`},
+		{"*", `\*`},
+		{"+", `\x2b`},
+		{"?", `\?`},
+		{"[](){}", `\[\]\(\)\{\}`},
+		{"$foo|x.y", `\$foo\|x\.y`},
+		{"x^y", `x\^y`},
+	}
+
+	for _, test := range tests {
+		esc := jsRegexpEscaper(test.x)
+		if esc != test.esc {
+			t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
+		}
+	}
+}
+
+func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
+	input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
+		"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
+		` !"#$%&'()*+,-./` +
+		`0123456789:;<=>?` +
+		`@ABCDEFGHIJKLMNO` +
+		`PQRSTUVWXYZ[\]^_` +
+		"`abcdefghijklmno" +
+		"pqrstuvwxyz{|}~\x7f" +
+		"\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
+
+	tests := []struct {
+		name    string
+		escaper func(...interface{}) string
+		escaped string
+	}{
+		{
+			"jsStrEscaper",
+			jsStrEscaper,
+			"\\0\x01\x02\x03\x04\x05\x06\x07" +
+				"\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
+				"\x10\x11\x12\x13\x14\x15\x16\x17" +
+				"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
+				` !\x22#$%\x26\x27()*\x2b,-.\/` +
+				`0123456789:;\x3c=\x3e?` +
+				`@ABCDEFGHIJKLMNO` +
+				`PQRSTUVWXYZ[\\]^_` +
+				"`abcdefghijklmno" +
+				"pqrstuvwxyz{|}~\x7f" +
+				"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
+		},
+		{
+			"jsRegexpEscaper",
+			jsRegexpEscaper,
+			"\\0\x01\x02\x03\x04\x05\x06\x07" +
+				"\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
+				"\x10\x11\x12\x13\x14\x15\x16\x17" +
+				"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
+				` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
+				`0123456789:;\x3c=\x3e\?` +
+				`@ABCDEFGHIJKLMNO` +
+				`PQRSTUVWXYZ\[\\\]\^_` +
+				"`abcdefghijklmno" +
+				`pqrstuvwxyz\{\|\}~` + "\u007f" +
+				"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
+		},
+	}
+
+	for _, test := range tests {
+		if s := test.escaper(input); s != test.escaped {
+			t.Errorf("%s once: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
+			continue
+		}
+
+		// Escape it rune by rune to make sure that any
+		// fast-path checking does not break escaping.
+		var buf bytes.Buffer
+		for _, c := range input {
+			buf.WriteString(test.escaper(string(c)))
+		}
+
+		if s := buf.String(); s != test.escaped {
+			t.Errorf("%s rune-wise: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
+			continue
+		}
+	}
+}
+
+func BenchmarkJSStrEscaperNoSpecials(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		jsStrEscaper("The quick, brown fox jumps over the lazy dog.")
+	}
+}
+
+func BenchmarkJSStrEscaper(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		jsStrEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
+	}
+}
+
+func BenchmarkJSRegexpEscaperNoSpecials(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		jsRegexpEscaper("The quick, brown fox jumps over the lazy dog")
+	}
+}
+
+func BenchmarkJSRegexpEscaper(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		jsRegexpEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
+	}
+}
diff --git a/src/pkg/exp/wingui/gui.go b/src/pkg/exp/wingui/gui.go
index cf39293..a2f16f2 100644
--- a/src/pkg/exp/wingui/gui.go
+++ b/src/pkg/exp/wingui/gui.go
@@ -25,13 +25,12 @@ func abortErrNo(funcname string, err int) {
 // global vars
 
 var (
-	mh uint32
-	bh uint32
+	mh syscall.Handle
+	bh syscall.Handle
 )
 
 // WinProc called by windows to notify us of all windows events we might be interested in.
-func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr {
-	var rc int32
+func WndProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
 	switch msg {
 	case WM_CREATE:
 		var e int
@@ -49,7 +48,7 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr {
 		fmt.Printf("button handle is %x\n", bh)
 		rc = DefWindowProc(hwnd, msg, wparam, lparam)
 	case WM_COMMAND:
-		switch uint32(lparam) {
+		switch syscall.Handle(lparam) {
 		case bh:
 			e := PostMessage(hwnd, WM_CLOSE, 0, 0)
 			if e != 0 {
@@ -66,7 +65,7 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr {
 		rc = DefWindowProc(hwnd, msg, wparam, lparam)
 	}
 	//fmt.Printf("WndProc(0x%08x, %d, 0x%08x, 0x%08x) (%d)\n", hwnd, msg, wparam, lparam, rc)
-	return uintptr(rc)
+	return
 }
 
 func rungui() int {
diff --git a/src/pkg/exp/wingui/winapi.go b/src/pkg/exp/wingui/winapi.go
index 31b57a2..3201528 100644
--- a/src/pkg/exp/wingui/winapi.go
+++ b/src/pkg/exp/wingui/winapi.go
@@ -6,6 +6,7 @@ package main
 
 import (
 	"unsafe"
+	"syscall"
 )
 
 type Wndclassex struct {
@@ -14,25 +15,25 @@ type Wndclassex struct {
 	WndProc    uintptr
 	ClsExtra   int32
 	WndExtra   int32
-	Instance   uint32
-	Icon       uint32
-	Cursor     uint32
-	Background uint32
+	Instance   syscall.Handle
+	Icon       syscall.Handle
+	Cursor     syscall.Handle
+	Background syscall.Handle
 	MenuName   *uint16
 	ClassName  *uint16
-	IconSm     uint32
+	IconSm     syscall.Handle
 }
 
 type Point struct {
-	X int32
-	Y int32
+	X uintptr
+	Y uintptr
 }
 
 type Msg struct {
-	Hwnd    uint32
+	Hwnd    syscall.Handle
 	Message uint32
-	Wparam  int32
-	Lparam  int32
+	Wparam  uintptr
+	Lparam  uintptr
 	Time    uint32
 	Pt      Point
 }
@@ -109,22 +110,22 @@ var (
 	IDI_INFORMATION = IDI_ASTERISK
 )
 
-//sys	GetModuleHandle(modname *uint16) (handle uint32, errno int) = GetModuleHandleW
+//sys	GetModuleHandle(modname *uint16) (handle syscall.Handle, errno int) = GetModuleHandleW
 //sys	RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) = user32.RegisterClassExW
-//sys	CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) = user32.CreateWindowExW
-//sys	DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.DefWindowProcW
-//sys	DestroyWindow(hwnd uint32) (errno int) = user32.DestroyWindow
+//sys	CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, errno int) = user32.CreateWindowExW
+//sys	DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.DefWindowProcW
+//sys	DestroyWindow(hwnd syscall.Handle) (errno int) = user32.DestroyWindow
 //sys	PostQuitMessage(exitcode int32) = user32.PostQuitMessage
-//sys	ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) = user32.ShowWindow
-//sys	UpdateWindow(hwnd uint32) (errno int) = user32.UpdateWindow
-//sys	GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW
+//sys	ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) = user32.ShowWindow
+//sys	UpdateWindow(hwnd syscall.Handle) (errno int) = user32.UpdateWindow
+//sys	GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW
 //sys	TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage
 //sys	DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW
-//sys	LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) = user32.LoadIconW
-//sys	LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) = user32.LoadCursorW
-//sys	SetCursor(cursor uint32) (precursor uint32, errno int) = user32.SetCursor
-//sys	SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.SendMessageW
-//sys	PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) = user32.PostMessageW
+//sys	LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, errno int) = user32.LoadIconW
+//sys	LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, errno int) = user32.LoadCursorW
+//sys	SetCursor(cursor syscall.Handle) (precursor syscall.Handle, errno int) = user32.SetCursor
+//sys	SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) = user32.SendMessageW
+//sys	PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (errno int) = user32.PostMessageW
 
 func MakeIntResource(id uint16) *uint16 {
 	return (*uint16)(unsafe.Pointer(uintptr(id)))
diff --git a/src/pkg/exp/wingui/zwinapi.go b/src/pkg/exp/wingui/zwinapi.go
index 4c009dd..38e93ee 100644
--- a/src/pkg/exp/wingui/zwinapi.go
+++ b/src/pkg/exp/wingui/zwinapi.go
@@ -28,9 +28,9 @@ var (
 	procPostMessageW     = moduser32.NewProc("PostMessageW")
 )
 
-func GetModuleHandle(modname *uint16) (handle uint32, errno int) {
+func GetModuleHandle(modname *uint16) (handle syscall.Handle, errno int) {
 	r0, _, e1 := syscall.Syscall(procGetModuleHandleW.Addr(), 1, uintptr(unsafe.Pointer(modname)), 0, 0)
-	handle = uint32(r0)
+	handle = syscall.Handle(r0)
 	if handle == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -58,9 +58,9 @@ func RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) {
 	return
 }
 
-func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) {
+func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent syscall.Handle, menu syscall.Handle, instance syscall.Handle, param uintptr) (hwnd syscall.Handle, errno int) {
 	r0, _, e1 := syscall.Syscall12(procCreateWindowExW.Addr(), 12, uintptr(exstyle), uintptr(unsafe.Pointer(classname)), uintptr(unsafe.Pointer(windowname)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(wndparent), uintptr(menu), uintptr(instance), uintptr(param))
-	hwnd = uint32(r0)
+	hwnd = syscall.Handle(r0)
 	if hwnd == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -73,13 +73,13 @@ func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style
 	return
 }
 
-func DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) {
+func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) {
 	r0, _, _ := syscall.Syscall6(procDefWindowProcW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
-	lresult = int32(r0)
+	lresult = uintptr(r0)
 	return
 }
 
-func DestroyWindow(hwnd uint32) (errno int) {
+func DestroyWindow(hwnd syscall.Handle) (errno int) {
 	r1, _, e1 := syscall.Syscall(procDestroyWindow.Addr(), 1, uintptr(hwnd), 0, 0)
 	if int(r1) == 0 {
 		if e1 != 0 {
@@ -98,13 +98,13 @@ func PostQuitMessage(exitcode int32) {
 	return
 }
 
-func ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) {
+func ShowWindow(hwnd syscall.Handle, cmdshow int32) (wasvisible bool) {
 	r0, _, _ := syscall.Syscall(procShowWindow.Addr(), 2, uintptr(hwnd), uintptr(cmdshow), 0)
 	wasvisible = bool(r0 != 0)
 	return
 }
 
-func UpdateWindow(hwnd uint32) (errno int) {
+func UpdateWindow(hwnd syscall.Handle) (errno int) {
 	r1, _, e1 := syscall.Syscall(procUpdateWindow.Addr(), 1, uintptr(hwnd), 0, 0)
 	if int(r1) == 0 {
 		if e1 != 0 {
@@ -118,7 +118,7 @@ func UpdateWindow(hwnd uint32) (errno int) {
 	return
 }
 
-func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) {
+func GetMessage(msg *Msg, hwnd syscall.Handle, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) {
 	r0, _, e1 := syscall.Syscall6(procGetMessageW.Addr(), 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax), 0, 0)
 	ret = int32(r0)
 	if ret == -1 {
@@ -145,9 +145,9 @@ func DispatchMessage(msg *Msg) (ret int32) {
 	return
 }
 
-func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) {
+func LoadIcon(instance syscall.Handle, iconname *uint16) (icon syscall.Handle, errno int) {
 	r0, _, e1 := syscall.Syscall(procLoadIconW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(iconname)), 0)
-	icon = uint32(r0)
+	icon = syscall.Handle(r0)
 	if icon == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -160,9 +160,9 @@ func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) {
 	return
 }
 
-func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) {
+func LoadCursor(instance syscall.Handle, cursorname *uint16) (cursor syscall.Handle, errno int) {
 	r0, _, e1 := syscall.Syscall(procLoadCursorW.Addr(), 2, uintptr(instance), uintptr(unsafe.Pointer(cursorname)), 0)
-	cursor = uint32(r0)
+	cursor = syscall.Handle(r0)
 	if cursor == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -175,9 +175,9 @@ func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int)
 	return
 }
 
-func SetCursor(cursor uint32) (precursor uint32, errno int) {
+func SetCursor(cursor syscall.Handle) (precursor syscall.Handle, errno int) {
 	r0, _, e1 := syscall.Syscall(procSetCursor.Addr(), 1, uintptr(cursor), 0, 0)
-	precursor = uint32(r0)
+	precursor = syscall.Handle(r0)
 	if precursor == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -190,13 +190,13 @@ func SetCursor(cursor uint32) (precursor uint32, errno int) {
 	return
 }
 
-func SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) {
+func SendMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (lresult uintptr) {
 	r0, _, _ := syscall.Syscall6(procSendMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
-	lresult = int32(r0)
+	lresult = uintptr(r0)
 	return
 }
 
-func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) {
+func PostMessage(hwnd syscall.Handle, msg uint32, wparam uintptr, lparam uintptr) (errno int) {
 	r1, _, e1 := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
 	if int(r1) == 0 {
 		if e1 != 0 {
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index 01bbc37..38b65d1 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -204,6 +204,7 @@ type FlagSet struct {
 	Usage func()
 
 	name          string
+	parsed        bool
 	actual        map[string]*Flag
 	formal        map[string]*Flag
 	args          []string // arguments after flags
@@ -318,10 +319,15 @@ func defaultUsage(f *FlagSet) {
 	f.PrintDefaults()
 }
 
+// NOTE: Usage is not just defaultUsage(commandLine)
+// because it serves (via godoc flag Usage) as the example
+// for how to write your own usage function.
+
 // Usage prints to standard error a usage message documenting all defined command-line flags.
 // The function is a variable that may be changed to point to a custom function.
 var Usage = func() {
-	defaultUsage(commandLine)
+	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+	PrintDefaults()
 }
 
 // NFlag returns the number of flags that have been set.
@@ -660,6 +666,7 @@ func (f *FlagSet) parseOne() (bool, os.Error) {
 // are defined and before flags are accessed by the program.
 // The return value will be ErrHelp if -help was set but not defined.
 func (f *FlagSet) Parse(arguments []string) os.Error {
+	f.parsed = true
 	f.args = arguments
 	for {
 		seen, err := f.parseOne()
@@ -681,6 +688,11 @@ func (f *FlagSet) Parse(arguments []string) os.Error {
 	return nil
 }
 
+// Parsed reports whether f.Parse has been called.
+func (f *FlagSet) Parsed() bool {
+	return f.parsed
+}
+
 // Parse parses the command-line flags from os.Args[1:].  Must be called
 // after all flags are defined and before flags are accessed by the program.
 func Parse() {
@@ -688,6 +700,11 @@ func Parse() {
 	commandLine.Parse(os.Args[1:])
 }
 
+// Parsed returns true if the command-line flags have been parsed.
+func Parsed() bool {
+	return commandLine.Parsed()
+}
+
 // The default set of command-line flags, parsed from os.Args.
 var commandLine = NewFlagSet(os.Args[0], ExitOnError)
 
diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go
index 63d0a9f..19c0dea 100644
--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -98,6 +98,9 @@ func TestUsage(t *testing.T) {
 }
 
 func testParse(f *FlagSet, t *testing.T) {
+	if f.Parsed() {
+		t.Error("f.Parse() = true before Parse")
+	}
 	boolFlag := f.Bool("bool", false, "bool value")
 	bool2Flag := f.Bool("bool2", false, "bool2 value")
 	intFlag := f.Int("int", 0, "int value")
@@ -121,6 +124,9 @@ func testParse(f *FlagSet, t *testing.T) {
 	if err := f.Parse(args); err != nil {
 		t.Fatal(err)
 	}
+	if !f.Parsed() {
+		t.Error("f.Parse() = false after Parse")
+	}
 	if *boolFlag != true {
 		t.Error("bool flag should be true, is ", *boolFlag)
 	}
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 1142c9f..b1ad345 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -5,6 +5,7 @@
 package fmt_test
 
 import (
+	"bytes"
 	. "fmt"
 	"io"
 	"math"
@@ -504,11 +505,38 @@ func TestCountMallocs(t *testing.T) {
 	runtime.UpdateMemStats()
 	mallocs = 0 - runtime.MemStats.Mallocs
 	for i := 0; i < 100; i++ {
+		Sprintf("%s", "hello")
+	}
+	runtime.UpdateMemStats()
+	mallocs += runtime.MemStats.Mallocs
+	Printf("mallocs per Sprintf(\"%%s\"): %d\n", mallocs/100)
+	runtime.UpdateMemStats()
+	mallocs = 0 - runtime.MemStats.Mallocs
+	for i := 0; i < 100; i++ {
 		Sprintf("%x %x", i, i)
 	}
 	runtime.UpdateMemStats()
 	mallocs += runtime.MemStats.Mallocs
 	Printf("mallocs per Sprintf(\"%%x %%x\"): %d\n", mallocs/100)
+	buf := new(bytes.Buffer)
+	runtime.UpdateMemStats()
+	mallocs = 0 - runtime.MemStats.Mallocs
+	for i := 0; i < 100; i++ {
+		buf.Reset()
+		Fprintf(buf, "%x %x %x", i, i, i)
+	}
+	runtime.UpdateMemStats()
+	mallocs += runtime.MemStats.Mallocs
+	Printf("mallocs per Fprintf(buf, \"%%x %%x %%x\"): %d\n", mallocs/100)
+	runtime.UpdateMemStats()
+	mallocs = 0 - runtime.MemStats.Mallocs
+	for i := 0; i < 100; i++ {
+		buf.Reset()
+		Fprintf(buf, "%s", "hello")
+	}
+	runtime.UpdateMemStats()
+	mallocs += runtime.MemStats.Mallocs
+	Printf("mallocs per Fprintf(buf, \"%%s\"): %d\n", mallocs/100)
 }
 
 type flagPrinter struct{}
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 2673343..1bd8990 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -9,10 +9,45 @@ import "go/token"
 // ----------------------------------------------------------------------------
 // Export filtering
 
-func identListExports(list []*Ident) []*Ident {
+// exportFilter is a special filter function to extract exported nodes.
+func exportFilter(name string) bool {
+	return IsExported(name)
+}
+
+// FileExports trims the AST for a Go source file in place such that
+// only exported nodes remain: all top-level identifiers which are not exported
+// and their associated information (such as type, initial value, or function
+// body) are removed. Non-exported fields and methods of exported types are
+// stripped, and the function bodies of exported functions are set to nil.
+// The File.Comments list is not changed.
+//
+// FileExports returns true if there are exported declarationa;
+// it returns false otherwise.
+//
+func FileExports(src *File) bool {
+	return FilterFile(src, exportFilter)
+}
+
+// PackageExports trims the AST for a Go package in place such that
+// only exported nodes remain. The pkg.Files list is not changed, so that
+// file names and top-level package comments don't get lost.
+//
+// PackageExports returns true if there are exported declarations;
+// it returns false otherwise.
+//
+func PackageExports(pkg *Package) bool {
+	return FilterPackage(pkg, exportFilter)
+}
+
+// ----------------------------------------------------------------------------
+// General filtering
+
+type Filter func(string) bool
+
+func filterIdentList(list []*Ident, f Filter) []*Ident {
 	j := 0
 	for _, x := range list {
-		if x.IsExported() {
+		if f(x.Name) {
 			list[j] = x
 			j++
 		}
@@ -38,33 +73,30 @@ func fieldName(x Expr) *Ident {
 	return nil
 }
 
-func fieldListExports(fields *FieldList) (removedFields bool) {
+func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
 	if fields == nil {
-		return
+		return false
 	}
 	list := fields.List
 	j := 0
 	for _, f := range list {
-		exported := false
+		keepField := false
 		if len(f.Names) == 0 {
 			// anonymous field
-			// (Note that a non-exported anonymous field
-			// may still refer to a type with exported
-			// fields, so this is not absolutely correct.
-			// However, this cannot be done w/o complete
-			// type information.)
 			name := fieldName(f.Type)
-			exported = name != nil && name.IsExported()
+			keepField = name != nil && filter(name.Name)
 		} else {
 			n := len(f.Names)
-			f.Names = identListExports(f.Names)
+			f.Names = filterIdentList(f.Names, filter)
 			if len(f.Names) < n {
 				removedFields = true
 			}
-			exported = len(f.Names) > 0
+			keepField = len(f.Names) > 0
 		}
-		if exported {
-			typeExports(f.Type)
+		if keepField {
+			if filter == exportFilter {
+				filterType(f.Type, filter)
+			}
 			list[j] = f
 			j++
 		}
@@ -76,185 +108,75 @@ func fieldListExports(fields *FieldList) (removedFields bool) {
 	return
 }
 
-func paramListExports(fields *FieldList) {
+func filterParamList(fields *FieldList, filter Filter) bool {
 	if fields == nil {
-		return
+		return false
 	}
+	var b bool
 	for _, f := range fields.List {
-		typeExports(f.Type)
+		if filterType(f.Type, filter) {
+			b = true
+		}
 	}
+	return b
 }
 
-func typeExports(typ Expr) {
+func filterType(typ Expr, f Filter) bool {
 	switch t := typ.(type) {
+	case *Ident:
+		return f(t.Name)
+	case *ParenExpr:
+		return filterType(t.X, f)
 	case *ArrayType:
-		typeExports(t.Elt)
+		return filterType(t.Elt, f)
 	case *StructType:
-		if fieldListExports(t.Fields) {
+		if filterFieldList(t.Fields, f) {
 			t.Incomplete = true
 		}
+		return len(t.Fields.List) > 0
 	case *FuncType:
-		paramListExports(t.Params)
-		paramListExports(t.Results)
+		b1 := filterParamList(t.Params, f)
+		b2 := filterParamList(t.Results, f)
+		return b1 || b2
 	case *InterfaceType:
-		if fieldListExports(t.Methods) {
+		if filterFieldList(t.Methods, f) {
 			t.Incomplete = true
 		}
+		return len(t.Methods.List) > 0
 	case *MapType:
-		typeExports(t.Key)
-		typeExports(t.Value)
+		b1 := filterType(t.Key, f)
+		b2 := filterType(t.Value, f)
+		return b1 || b2
 	case *ChanType:
-		typeExports(t.Value)
-	}
-}
-
-func specExports(spec Spec) bool {
-	switch s := spec.(type) {
-	case *ValueSpec:
-		s.Names = identListExports(s.Names)
-		if len(s.Names) > 0 {
-			typeExports(s.Type)
-			return true
-		}
-	case *TypeSpec:
-		if s.Name.IsExported() {
-			typeExports(s.Type)
-			return true
-		}
+		return filterType(t.Value, f)
 	}
 	return false
 }
 
-func specListExports(list []Spec) []Spec {
-	j := 0
-	for _, s := range list {
-		if specExports(s) {
-			list[j] = s
-			j++
-		}
-	}
-	return list[0:j]
-}
-
-func declExports(decl Decl) bool {
-	switch d := decl.(type) {
-	case *GenDecl:
-		d.Specs = specListExports(d.Specs)
-		return len(d.Specs) > 0
-	case *FuncDecl:
-		d.Body = nil // strip body
-		return d.Name.IsExported()
-	}
-	return false
-}
-
-// FileExports trims the AST for a Go source file in place such that only
-// exported nodes remain: all top-level identifiers which are not exported
-// and their associated information (such as type, initial value, or function
-// body) are removed. Non-exported fields and methods of exported types are
-// stripped, and the function bodies of exported functions are set to nil.
-// The File.comments list is not changed.
-//
-// FileExports returns true if there is an exported declaration; it returns
-// false otherwise.
-//
-func FileExports(src *File) bool {
-	j := 0
-	for _, d := range src.Decls {
-		if declExports(d) {
-			src.Decls[j] = d
-			j++
-		}
-	}
-	src.Decls = src.Decls[0:j]
-	return j > 0
-}
-
-// PackageExports trims the AST for a Go package in place such that only
-// exported nodes remain. The pkg.Files list is not changed, so that file
-// names and top-level package comments don't get lost.
-//
-// PackageExports returns true if there is an exported declaration; it
-// returns false otherwise.
-//
-func PackageExports(pkg *Package) bool {
-	hasExports := false
-	for _, f := range pkg.Files {
-		if FileExports(f) {
-			hasExports = true
-		}
-	}
-	return hasExports
-}
-
-// ----------------------------------------------------------------------------
-// General filtering
-
-type Filter func(string) bool
-
-func filterIdentList(list []*Ident, f Filter) []*Ident {
-	j := 0
-	for _, x := range list {
-		if f(x.Name) {
-			list[j] = x
-			j++
-		}
-	}
-	return list[0:j]
-}
-
-func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
-	if fields == nil {
-		return false
-	}
-	list := fields.List
-	j := 0
-	for _, f := range list {
-		keepField := false
-		if len(f.Names) == 0 {
-			// anonymous field
-			name := fieldName(f.Type)
-			keepField = name != nil && filter(name.Name)
-		} else {
-			n := len(f.Names)
-			f.Names = filterIdentList(f.Names, filter)
-			if len(f.Names) < n {
-				removedFields = true
-			}
-			keepField = len(f.Names) > 0
-		}
-		if keepField {
-			list[j] = f
-			j++
-		}
-	}
-	if j < len(list) {
-		removedFields = true
-	}
-	fields.List = list[0:j]
-	return
-}
-
 func filterSpec(spec Spec, f Filter) bool {
 	switch s := spec.(type) {
 	case *ValueSpec:
 		s.Names = filterIdentList(s.Names, f)
-		return len(s.Names) > 0
+		if len(s.Names) > 0 {
+			if f == exportFilter {
+				filterType(s.Type, f)
+			}
+			return true
+		}
 	case *TypeSpec:
 		if f(s.Name.Name) {
+			if f == exportFilter {
+				filterType(s.Type, f)
+			}
 			return true
 		}
-		switch t := s.Type.(type) {
-		case *StructType:
-			if filterFieldList(t.Fields, f) {
-				t.Incomplete = true
-			}
-			return len(t.Fields.List) > 0
-		case *InterfaceType:
-			if filterFieldList(t.Methods, f) {
-				t.Incomplete = true
-			}
-			return len(t.Methods.List) > 0
+		if f != exportFilter {
+			// For general filtering (not just exports),
+			// filter type even if name is not filtered
+			// out.
+			// If the type contains filtered elements,
+			// keep the declaration.
+			return filterType(s.Type, f)
 		}
 	}
 	return false
@@ -284,6 +206,9 @@ func FilterDecl(decl Decl, f Filter) bool {
 		d.Specs = filterSpecList(d.Specs, f)
 		return len(d.Specs) > 0
 	case *FuncDecl:
+		if f == exportFilter {
+			d.Body = nil // strip body
+		}
 		return f(d.Name.Name)
 	}
 	return false
@@ -293,8 +218,8 @@ func FilterDecl(decl Decl, f Filter) bool {
 // names from top-level declarations (including struct field and
 // interface method names, but not from parameter lists) that don't
 // pass through the filter f. If the declaration is empty afterwards,
-// the declaration is removed from the AST.
-// The File.comments list is not changed.
+// the declaration is removed from the AST. The File.Comments list
+// is not changed.
 //
 // FilterFile returns true if there are any top-level declarations
 // left after filtering; it returns false otherwise.
@@ -311,13 +236,13 @@ func FilterFile(src *File, f Filter) bool {
 	return j > 0
 }
 
-// FilterPackage trims the AST for a Go package in place by removing all
-// names from top-level declarations (including struct field and
+// FilterPackage trims the AST for a Go package in place by removing
+// all names from top-level declarations (including struct field and
 // interface method names, but not from parameter lists) that don't
 // pass through the filter f. If the declaration is empty afterwards,
-// the declaration is removed from the AST.
-// The pkg.Files list is not changed, so that file names and top-level
-// package comments don't get lost.
+// the declaration is removed from the AST. The pkg.Files list is not
+// changed, so that file names and top-level package comments don't get
+// lost.
 //
 // FilterPackage returns true if there are any top-level declarations
 // left after filtering; it returns false otherwise.
@@ -344,6 +269,8 @@ const (
 	// If set, comments that are not associated with a specific
 	// AST node (as Doc or Comment) are excluded.
 	FilterUnassociatedComments
+	// If set, duplicate import declarations are excluded.
+	FilterImportDuplicates
 )
 
 // separator is an empty //-style comment that is interspersed between
@@ -459,6 +386,31 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
 		}
 	}
 
+	// Collect import specs from all package files.
+	var imports []*ImportSpec
+	if mode&FilterImportDuplicates != 0 {
+		seen := make(map[string]bool)
+		for _, f := range pkg.Files {
+			for _, imp := range f.Imports {
+				if path := imp.Path.Value; !seen[path] {
+					// TODO: consider handling cases where:
+					// - 2 imports exist with the same import path but
+					//   have different local names (one should probably 
+					//   keep both of them)
+					// - 2 imports exist but only one has a comment
+					// - 2 imports exist and they both have (possibly
+					//   different) comments
+					imports = append(imports, imp)
+					seen[path] = true
+				}
+			}
+		}
+	} else {
+		for _, f := range pkg.Files {
+			imports = append(imports, f.Imports...)
+		}
+	}
+
 	// Collect comments from all package files.
 	var comments []*CommentGroup
 	if mode&FilterUnassociatedComments == 0 {
@@ -469,7 +421,6 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
 		}
 	}
 
-	// TODO(gri) need to compute pkgScope and unresolved identifiers!
-	// TODO(gri) need to compute imports!
-	return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
+	// TODO(gri) need to compute unresolved identifiers!
+	return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
 }
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index e59d876..592ebbd 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -7,44 +7,85 @@ package build
 import (
 	"exec"
 	"path/filepath"
+	"reflect"
+	"runtime"
+	"sort"
 	"testing"
 )
 
-var buildPkgs = []string{
-	"go/build/pkgtest",
-	"go/build/cmdtest",
-	"go/build/cgotest",
+func sortstr(x []string) []string {
+	sort.Strings(x)
+	return x
+}
+
+var buildPkgs = []struct {
+	dir  string
+	info *DirInfo
+}{
+	{
+		"go/build/pkgtest",
+		&DirInfo{
+			GoFiles:      []string{"pkgtest.go"},
+			SFiles:       []string{"sqrt_" + runtime.GOARCH + ".s"},
+			PkgName:      "pkgtest",
+			Imports:      []string{"os"},
+			TestImports:  []string{"fmt", "pkgtest"},
+			TestGoFiles:  sortstr([]string{"sqrt_test.go", "sqrt_" + runtime.GOARCH + "_test.go"}),
+			XTestGoFiles: []string{"xsqrt_test.go"},
+		},
+	},
+	{
+		"go/build/cmdtest",
+		&DirInfo{
+			GoFiles: []string{"main.go"},
+			PkgName: "main",
+			Imports: []string{"go/build/pkgtest"},
+		},
+	},
+	{
+		"go/build/cgotest",
+		&DirInfo{
+			CgoFiles: []string{"cgotest.go"},
+			CFiles:   []string{"cgotest.c"},
+			Imports:  []string{"C", "unsafe"},
+			PkgName:  "cgotest",
+		},
+	},
 }
 
 const cmdtestOutput = "3"
 
 func TestBuild(t *testing.T) {
-	for _, pkg := range buildPkgs {
+	for _, tt := range buildPkgs {
 		tree := Path[0] // Goroot
-		dir := filepath.Join(tree.SrcDir(), pkg)
+		dir := filepath.Join(tree.SrcDir(), tt.dir)
 
 		info, err := ScanDir(dir, true)
 		if err != nil {
-			t.Error("ScanDir:", err)
+			t.Errorf("ScanDir(%#q): %v", tt.dir, err)
+			continue
+		}
+		if !reflect.DeepEqual(info, tt.info) {
+			t.Errorf("ScanDir(%#q) = %#v, want %#v\n", tt.dir, info, tt.info)
 			continue
 		}
 
-		s, err := Build(tree, pkg, info)
+		s, err := Build(tree, tt.dir, info)
 		if err != nil {
-			t.Error("Build:", err)
+			t.Errorf("Build(%#q): %v", tt.dir, err)
 			continue
 		}
 
 		if err := s.Run(); err != nil {
-			t.Error("Run:", err)
+			t.Errorf("Run(%#q): %v", tt.dir, err)
 			continue
 		}
 
-		if pkg == "go/build/cmdtest" {
+		if tt.dir == "go/build/cmdtest" {
 			bin := s.Output[0]
 			b, err := exec.Command(bin).CombinedOutput()
 			if err != nil {
-				t.Errorf("exec: %s: %v", bin, err)
+				t.Errorf("exec %s: %v", bin, err)
 				continue
 			}
 			if string(b) != cmdtestOutput {
@@ -52,6 +93,7 @@ func TestBuild(t *testing.T) {
 			}
 		}
 
+		// Deferred because cmdtest depends on pkgtest.
 		defer func(s *Script) {
 			if err := s.Nuke(); err != nil {
 				t.Errorf("nuking: %v", err)
diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go
index e0000b5..fa4d9e9 100644
--- a/src/pkg/go/build/dir.go
+++ b/src/pkg/go/build/dir.go
@@ -7,27 +7,60 @@ package build
 import (
 	"go/parser"
 	"go/token"
+	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
+	"sort"
 	"strconv"
 	"strings"
 	"runtime"
 )
 
+// A Context specifies the supporting context for a build.
+type Context struct {
+	GOARCH string // target architecture
+	GOOS   string // target operating system
+	// TODO(rsc,adg): GOPATH
+}
+
+// The DefaultContext is the default Context for builds.
+// It uses the GOARCH and GOOS environment variables
+// if set, or else the compiled code's GOARCH and GOOS.
+var DefaultContext = Context{
+	envOr("GOARCH", runtime.GOARCH),
+	envOr("GOOS", runtime.GOOS),
+}
+
+func envOr(name, def string) string {
+	s := os.Getenv(name)
+	if s == "" {
+		return def
+	}
+	return s
+}
+
 type DirInfo struct {
-	GoFiles  []string // .go files in dir (excluding CgoFiles)
-	CgoFiles []string // .go files that import "C"
-	CFiles   []string // .c files in dir
-	SFiles   []string // .s files in dir
-	Imports  []string // All packages imported by goFiles
-	PkgName  string   // Name of package in dir
+	GoFiles      []string // .go files in dir (excluding CgoFiles)
+	CgoFiles     []string // .go files that import "C"
+	CFiles       []string // .c files in dir
+	SFiles       []string // .s files in dir
+	Imports      []string // All packages imported by GoFiles
+	TestImports  []string // All packages imported by (X)TestGoFiles
+	PkgName      string   // Name of package in dir
+	TestGoFiles  []string // _test.go files in package
+	XTestGoFiles []string // _test.go files outside package
 }
 
 func (d *DirInfo) IsCommand() bool {
 	return d.PkgName == "main"
 }
 
+// ScanDir calls DefaultContext.ScanDir.
+func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
+	return DefaultContext.ScanDir(dir, allowMain)
+}
+
 // ScanDir returns a structure with details about the Go content found
 // in the given directory. The file lists exclude:
 //
@@ -36,36 +69,29 @@ func (d *DirInfo) IsCommand() bool {
 //	- files ending in _test.go
 // 	- files starting with _ or .
 //
-// Only files that satisfy the goodOSArch function are included.
-func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
-	f, err := os.Open(dir)
-	if err != nil {
-		return nil, err
-	}
-	dirs, err := f.Readdir(-1)
-	f.Close()
+func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
+	dirs, err := ioutil.ReadDir(dir)
 	if err != nil {
 		return nil, err
 	}
 
 	var di DirInfo
 	imported := make(map[string]bool)
+	testImported := make(map[string]bool)
 	fset := token.NewFileSet()
-	for i := range dirs {
-		d := &dirs[i]
+	for _, d := range dirs {
 		if strings.HasPrefix(d.Name, "_") ||
 			strings.HasPrefix(d.Name, ".") {
 			continue
 		}
-		if !goodOSArch(d.Name) {
+		if !ctxt.goodOSArch(d.Name) {
 			continue
 		}
 
+		isTest := false
 		switch filepath.Ext(d.Name) {
 		case ".go":
-			if strings.HasSuffix(d.Name, "_test.go") {
-				continue
-			}
+			isTest = strings.HasSuffix(d.Name, "_test.go")
 		case ".c":
 			di.CFiles = append(di.CFiles, d.Name)
 			continue
@@ -81,21 +107,24 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
 		if err != nil {
 			return nil, err
 		}
-		s := string(pf.Name.Name)
-		if s == "main" && !allowMain {
+		pkg := string(pf.Name.Name)
+		if pkg == "main" && !allowMain {
 			continue
 		}
-		if s == "documentation" {
+		if pkg == "documentation" {
 			continue
 		}
+		if isTest && strings.HasSuffix(pkg, "_test") {
+			pkg = pkg[:len(pkg)-len("_test")]
+		}
 		if di.PkgName == "" {
-			di.PkgName = s
-		} else if di.PkgName != s {
+			di.PkgName = pkg
+		} else if di.PkgName != pkg {
 			// Only if all files in the directory are in package main
 			// do we return PkgName=="main".
 			// A mix of main and another package reverts
 			// to the original (allowMain=false) behaviour.
-			if s == "main" || di.PkgName == "main" {
+			if pkg == "main" || di.PkgName == "main" {
 				return ScanDir(dir, false)
 			}
 			return nil, os.NewError("multiple package names in " + dir)
@@ -107,13 +136,26 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
 			if err != nil {
 				log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
 			}
-			imported[path] = true
+			if isTest {
+				testImported[path] = true
+			} else {
+				imported[path] = true
+			}
 			if path == "C" {
+				if isTest {
+					return nil, os.NewError("use of cgo in test " + filename)
+				}
 				isCgo = true
 			}
 		}
 		if isCgo {
 			di.CgoFiles = append(di.CgoFiles, d.Name)
+		} else if isTest {
+			if pkg == string(pf.Name.Name) {
+				di.TestGoFiles = append(di.TestGoFiles, d.Name)
+			} else {
+				di.XTestGoFiles = append(di.XTestGoFiles, d.Name)
+			}
 		} else {
 			di.GoFiles = append(di.GoFiles, d.Name)
 		}
@@ -124,49 +166,58 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
 		di.Imports[i] = p
 		i++
 	}
+	di.TestImports = make([]string, len(testImported))
+	i = 0
+	for p := range testImported {
+		di.TestImports[i] = p
+		i++
+	}
+	// File name lists are sorted because ioutil.ReadDir sorts.
+	sort.Strings(di.Imports)
+	sort.Strings(di.TestImports)
 	return &di, nil
 }
 
-// goodOSArch returns false if the filename contains a $GOOS or $GOARCH
+// goodOSArch returns false if the name contains a $GOOS or $GOARCH
 // suffix which does not match the current system.
-// The recognized filename formats are:
+// The recognized name formats are:
 //
 //     name_$(GOOS).*
 //     name_$(GOARCH).*
 //     name_$(GOOS)_$(GOARCH).*
+//     name_$(GOOS)_test.*
+//     name_$(GOARCH)_test.*
+//     name_$(GOOS)_$(GOARCH)_test.*
 //
-func goodOSArch(filename string) bool {
-	if dot := strings.Index(filename, "."); dot != -1 {
-		filename = filename[:dot]
+func (ctxt *Context) goodOSArch(name string) bool {
+	if dot := strings.Index(name, "."); dot != -1 {
+		name = name[:dot]
+	}
+	l := strings.Split(name, "_")
+	if n := len(l); n > 0 && l[n-1] == "test" {
+		l = l[:n-1]
 	}
-	l := strings.Split(filename, "_")
 	n := len(l)
-	if n == 0 {
-		return true
+	if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+		return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
 	}
-	if good, known := goodOS[l[n-1]]; known {
-		return good
+	if n >= 1 && knownOS[l[n-1]] {
+		return l[n-1] == ctxt.GOOS
 	}
-	if good, known := goodArch[l[n-1]]; known {
-		if !good || n < 2 {
-			return false
-		}
-		good, known = goodOS[l[n-2]]
-		return good || !known
+	if n >= 1 && knownArch[l[n-1]] {
+		return l[n-1] == ctxt.GOARCH
 	}
 	return true
 }
 
-var goodOS = make(map[string]bool)
-var goodArch = make(map[string]bool)
+var knownOS = make(map[string]bool)
+var knownArch = make(map[string]bool)
 
 func init() {
-	goodOS = make(map[string]bool)
-	goodArch = make(map[string]bool)
 	for _, v := range strings.Fields(goosList) {
-		goodOS[v] = v == runtime.GOOS
+		knownOS[v] = true
 	}
 	for _, v := range strings.Fields(goarchList) {
-		goodArch[v] = v == runtime.GOARCH
+		knownArch[v] = true
 	}
 }
diff --git a/src/pkg/go/build/pkgtest/pkgtest.go b/src/pkg/go/build/pkgtest/pkgtest.go
index 9322f5e..03ebb98 100644
--- a/src/pkg/go/build/pkgtest/pkgtest.go
+++ b/src/pkg/go/build/pkgtest/pkgtest.go
@@ -4,6 +4,10 @@
 
 package pkgtest
 
-func Foo() {}
+import "os"
+
+func Foo() os.Error {
+	return nil
+}
 
 func Sqrt(x float64) float64
diff --git a/src/pkg/go/build/pkgtest/sqrt_386_test.go b/src/pkg/go/build/pkgtest/sqrt_386_test.go
new file mode 100644
index 0000000..26b483f
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_386_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_amd64_test.go b/src/pkg/go/build/pkgtest/sqrt_amd64_test.go
new file mode 100644
index 0000000..26b483f
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_amd64_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_arm_test.go b/src/pkg/go/build/pkgtest/sqrt_arm_test.go
new file mode 100644
index 0000000..26b483f
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_arm_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_test.go b/src/pkg/go/build/pkgtest/sqrt_test.go
new file mode 100644
index 0000000..95fb625
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_test.go
@@ -0,0 +1,5 @@
+package pkgtest
+
+import "fmt"
+
+var _ = fmt.Printf
diff --git a/src/pkg/go/build/pkgtest/xsqrt_test.go b/src/pkg/go/build/pkgtest/xsqrt_test.go
new file mode 100644
index 0000000..77e903d
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/xsqrt_test.go
@@ -0,0 +1,5 @@
+package pkgtest_test
+
+import "pkgtest"
+
+var _ = pkgtest.Foo
diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go
index eb0e5dc..2e8b4c8 100644
--- a/src/pkg/go/build/syslist_test.go
+++ b/src/pkg/go/build/syslist_test.go
@@ -55,7 +55,7 @@ var tests = []GoodFileTest{
 
 func TestGoodOSArch(t *testing.T) {
 	for _, test := range tests {
-		if goodOSArch(test.name) != test.result {
+		if DefaultContext.goodOSArch(test.name) != test.result {
 			t.Fatalf("goodOSArch(%q) != %v", test.name, test.result)
 		}
 	}
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 9c14d16..be82b2f 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -587,7 +587,6 @@ func (p *parser) parseStructType() *ast.StructType {
 	}
 	rbrace := p.expect(token.RBRACE)
 
-	// TODO(gri): store struct scope in AST
 	return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -611,9 +610,6 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
 			p.error(pos, "'...' parameter is missing type")
 			typ = &ast.BadExpr{pos, p.pos}
 		}
-		if p.tok != token.RPAREN {
-			p.error(pos, "can use '...' with last parameter type only")
-		}
 		return &ast.Ellipsis{pos, typ}
 	}
 	return p.tryIdentOrType(false)
@@ -636,21 +632,21 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
 	}
 
 	// a list of identifiers looks like a list of type names
-	for {
-		// parseVarType accepts any type (including parenthesized ones)
-		// even though the syntax does not permit them here: we
-		// accept them all for more robust parsing and complain
-		// afterwards
-		list = append(list, p.parseVarType(isParam))
+	//
+	// parse/tryVarType accepts any type (including parenthesized
+	// ones) even though the syntax does not permit them here: we
+	// accept them all for more robust parsing and complain later
+	for typ := p.parseVarType(isParam); typ != nil; {
+		list = append(list, typ)
 		if p.tok != token.COMMA {
 			break
 		}
 		p.next()
+		typ = p.tryVarType(isParam) // maybe nil as in: func f(int,) {}
 	}
 
 	// if we had a list of identifiers, it must be followed by a type
-	typ = p.tryVarType(isParam)
-	if typ != nil {
+	if typ = p.tryVarType(isParam); typ != nil {
 		p.resolve(typ)
 	}
 
@@ -800,7 +796,6 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
 	}
 	rbrace := p.expect(token.RBRACE)
 
-	// TODO(gri): store interface scope in AST
 	return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -1436,14 +1431,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
 	case token.ARROW:
 		// send statement
 		arrow := p.pos
-		p.next() // consume "<-"
+		p.next()
 		y := p.parseRhs()
 		return &ast.SendStmt{x[0], arrow, y}, false
 
 	case token.INC, token.DEC:
 		// increment or decrement
 		s := &ast.IncDecStmt{x[0], p.pos, p.tok}
-		p.next() // consume "++" or "--"
+		p.next()
 		return s, false
 	}
 
@@ -1591,7 +1586,7 @@ func (p *parser) parseTypeList() (list []ast.Expr) {
 	return
 }
 
-func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
+func (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause {
 	if p.trace {
 		defer un(trace(p, "CaseClause"))
 	}
@@ -1600,10 +1595,10 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
 	var list []ast.Expr
 	if p.tok == token.CASE {
 		p.next()
-		if exprSwitch {
-			list = p.parseRhsList()
-		} else {
+		if typeSwitch {
 			list = p.parseTypeList()
+		} else {
+			list = p.parseRhsList()
 		}
 	} else {
 		p.expect(token.DEFAULT)
@@ -1617,15 +1612,19 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
 	return &ast.CaseClause{pos, list, colon, body}
 }
 
-func isExprSwitch(s ast.Stmt) bool {
-	if s == nil {
-		return true
-	}
-	if e, ok := s.(*ast.ExprStmt); ok {
-		if a, ok := e.X.(*ast.TypeAssertExpr); ok {
-			return a.Type != nil // regular type assertion
-		}
-		return true
+func isTypeSwitchAssert(x ast.Expr) bool {
+	a, ok := x.(*ast.TypeAssertExpr)
+	return ok && a.Type == nil
+}
+
+func isTypeSwitchGuard(s ast.Stmt) bool {
+	switch t := s.(type) {
+	case *ast.ExprStmt:
+		// x.(nil)
+		return isTypeSwitchAssert(t.X)
+	case *ast.AssignStmt:
+		// v := x.(nil)
+		return len(t.Lhs) == 1 && t.Tok == token.DEFINE && len(t.Rhs) == 1 && isTypeSwitchAssert(t.Rhs[0])
 	}
 	return false
 }
@@ -1651,28 +1650,41 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
 			s1 = s2
 			s2 = nil
 			if p.tok != token.LBRACE {
+				// A TypeSwitchGuard may declare a variable in addition
+				// to the variable declared in the initial SimpleStmt.
+				// Introduce extra scope to avoid redeclaration errors:
+				//
+				//	switch t := 0; t := x.(T) { ... }
+				//
+				// (this code is not valid Go because the first t will
+				// cannot be accessed and thus is never used, the extra
+				// scope is needed for the correct error message).
+				//
+				// If we don't have a type switch, s2 must be an expression.
+				// Having the extra nested but empty scope won't affect it.
+				p.openScope()
+				defer p.closeScope()
 				s2, _ = p.parseSimpleStmt(basic)
 			}
 		}
 		p.exprLev = prevLev
 	}
 
-	exprSwitch := isExprSwitch(s2)
+	typeSwitch := isTypeSwitchGuard(s2)
 	lbrace := p.expect(token.LBRACE)
 	var list []ast.Stmt
 	for p.tok == token.CASE || p.tok == token.DEFAULT {
-		list = append(list, p.parseCaseClause(exprSwitch))
+		list = append(list, p.parseCaseClause(typeSwitch))
 	}
 	rbrace := p.expect(token.RBRACE)
 	p.expectSemi()
 	body := &ast.BlockStmt{lbrace, list, rbrace}
 
-	if exprSwitch {
-		return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
+	if typeSwitch {
+		return &ast.TypeSwitchStmt{pos, s1, s2, body}
 	}
-	// type switch
-	// TODO(gri): do all the checks!
-	return &ast.TypeSwitchStmt{pos, s1, s2, body}
+
+	return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
 }
 
 func (p *parser) parseCommClause() *ast.CommClause {
@@ -2001,14 +2013,12 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
 		defer un(trace(p, "Receiver"))
 	}
 
-	pos := p.pos
 	par := p.parseParameters(scope, false)
 
 	// must have exactly one receiver
 	if par.NumFields() != 1 {
-		p.errorExpected(pos, "exactly one receiver")
-		// TODO determine a better range for BadExpr below
-		par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{pos, pos}}}
+		p.errorExpected(par.Opening, "exactly one receiver")
+		par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{par.Opening, par.Closing + 1}}}
 		return par
 	}
 
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 39a78e5..9705dcf 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -26,6 +26,9 @@ var illegalInputs = []interface{}{
 	`package p; func f() { for _ = range x ; ; {} };`,
 	`package p; func f() { for ; ; _ = range x {} };`,
 	`package p; func f() { for ; _ = range x ; {} };`,
+	`package p; func f() { switch t = t.(type) {} };`,
+	`package p; func f() { switch t, t = t.(type) {} };`,
+	`package p; func f() { switch t = t.(type), t {} };`,
 	`package p; var a = [1]int; /* illegal expression */`,
 	`package p; var a = [...]int; /* illegal expression */`,
 	`package p; var a = struct{} /* illegal expression */`,
@@ -61,6 +64,9 @@ var validPrograms = []interface{}{
 	`package p; func f(...T);`,
 	`package p; func f(float, ...int);`,
 	`package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`,
+	`package p; func f(int,) {};`,
+	`package p; func f(...int,) {};`,
+	`package p; func f(x ...int,) {};`,
 	`package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`,
 	`package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
 	`package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
@@ -74,7 +80,7 @@ var validPrograms = []interface{}{
 
 func TestParseValidPrograms(t *testing.T) {
 	for _, src := range validPrograms {
-		_, err := ParseFile(fset, "", src, 0)
+		_, err := ParseFile(fset, "", src, SpuriousErrors)
 		if err != nil {
 			t.Errorf("ParseFile(%q): %v", src, err)
 		}
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 9cd975e..3645306 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -269,6 +269,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
 func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
 	p.print(fields.Opening, token.LPAREN)
 	if len(fields.List) > 0 {
+		ws := indent
 		var prevLine, line int
 		for i, par := range fields.List {
 			if i > 0 {
@@ -278,19 +279,30 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
 				} else {
 					line = p.fset.Position(par.Type.Pos()).Line
 				}
-				if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ignore, true) {
+				if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ws, true) {
+					ws = ignore
 					*multiLine = true
 				} else {
 					p.print(blank)
 				}
 			}
 			if len(par.Names) > 0 {
-				p.identList(par.Names, false, multiLine)
+				// Very subtle: If we indented before (ws == ignore), identList
+				// won't indent again. If we didn't (ws == indent), identList will
+				// indent if the identList spans multiple lines, and it will outdent
+				// again at the end (and still ws == indent). Thus, a subsequent indent
+				// by a linebreak call after a type, or in the next multi-line identList
+				// will do the right thing.
+				p.identList(par.Names, ws == indent, multiLine)
 				p.print(blank)
 			}
 			p.expr(par.Type, multiLine)
 			prevLine = p.fset.Position(par.Type.Pos()).Line
 		}
+		if ws == ignore {
+			// unindent if we indented
+			p.print(unindent)
+		}
 	}
 	p.print(fields.Closing, token.RPAREN)
 }
diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden
index 970533e..bfa2568 100644
--- a/src/pkg/go/printer/testdata/declarations.golden
+++ b/src/pkg/go/printer/testdata/declarations.golden
@@ -692,56 +692,119 @@ func _(x ...chan int)
 
 // these parameter lists must remain multi-line since they are multi-line in the source
 func _(bool,
-int) {
+	int) {
 }
 func _(x bool,
-y int) {
+	y int) {
 }
 func _(x,
-y bool) {
+	y bool) {
 }
 func _(bool,	// comment
-int) {
+	int) {
 }
 func _(x bool,	// comment
-y int) {
+	y int) {
 }
 func _(x,	// comment
-y bool) {
+	y bool) {
 }
 func _(bool,	// comment
-// comment
-int) {
+	// comment
+	int) {
 }
 func _(x bool,	// comment
-// comment
-y int) {
+	// comment
+	y int) {
 }
 func _(x,	// comment
-// comment
-y bool) {
+	// comment
+	y bool) {
 }
 func _(bool,
-// comment
-int) {
+	// comment
+	int) {
 }
 func _(x bool,
-// comment
-y int) {
+	// comment
+	y int) {
 }
 func _(x,
-// comment
-y bool) {
+	// comment
+	y bool) {
 }
 func _(x,	// comment
-y,	// comment
-z bool) {
+	y,	// comment
+	z bool) {
 }
 func _(x,	// comment
-y,	// comment
-z bool) {
+	y,	// comment
+	z bool) {
 }
 func _(x int,	// comment
-y float,	// comment
-z bool) {
+	y float,	// comment
+	z bool) {
+}
+
+// properly indent multi-line signatures
+func ManageStatus(in <-chan *Status, req <-chan Request,
+	stat chan<- *TargetInfo,
+	TargetHistorySize int) {
+}
+
+func MultiLineSignature0(a, b, c int) {
+}
+
+func MultiLineSignature1(a, b, c int,
+	u, v, w float) {
+}
+
+func MultiLineSignature2(a, b,
+	c int) {
+}
+
+func MultiLineSignature3(a, b,
+	c int, u, v,
+	w float,
+	x ...int) {
+}
+
+func MultiLineSignature4(a, b, c int,
+	u, v,
+	w float,
+	x ...int) {
+}
+
+func MultiLineSignature5(a, b, c int,
+	u, v, w float,
+	p, q,
+	r string,
+	x ...int) {
+}
+
+// make sure it also works for methods in interfaces
+type _ interface {
+	MultiLineSignature0(a, b, c int)
+
+	MultiLineSignature1(a, b, c int,
+		u, v, w float)
+
+	MultiLineSignature2(a, b,
+		c int)
+
+	MultiLineSignature3(a, b,
+		c int, u, v,
+		w float,
+		x ...int)
+
+	MultiLineSignature4(a, b, c int,
+		u, v,
+		w float,
+		x ...int)
+
+	MultiLineSignature5(a, b, c int,
+		u, v, w float,
+		p, q,
+		r string,
+		x ...int)
 }
diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input
index c613409..1d69c57 100644
--- a/src/pkg/go/printer/testdata/declarations.input
+++ b/src/pkg/go/printer/testdata/declarations.input
@@ -755,3 +755,79 @@ func _(x int,	// comment
 	y float,	// comment
 	z bool) {
 }
+
+
+// properly indent multi-line signatures
+func ManageStatus(in <-chan *Status, req <-chan Request,
+stat chan<- *TargetInfo,
+TargetHistorySize int) {
+}
+
+func MultiLineSignature0(
+a, b, c int,
+) {}
+
+func MultiLineSignature1(
+a, b, c int,
+u, v, w float,
+) {}
+
+func MultiLineSignature2(
+a, b,
+c int,
+) {}
+
+func MultiLineSignature3(
+a, b,
+c int, u, v,
+w float,
+		x ...int) {}
+
+func MultiLineSignature4(
+a, b, c int,
+u, v,
+w float,
+		x ...int) {}
+
+func MultiLineSignature5(
+a, b, c int,
+u, v, w float,
+p, q,
+r string,
+		x ...int) {}
+
+// make sure it also works for methods in interfaces
+type _ interface {
+MultiLineSignature0(
+a, b, c int,
+)
+
+MultiLineSignature1(
+a, b, c int,
+u, v, w float,
+)
+
+MultiLineSignature2(
+a, b,
+c int,
+)
+
+MultiLineSignature3(
+a, b,
+c int, u, v,
+w float,
+		x ...int)
+
+MultiLineSignature4(
+a, b, c int,
+u, v,
+w float,
+		x ...int)
+
+MultiLineSignature5(
+a, b, c int,
+u, v, w float,
+p, q,
+r string,
+		x ...int)
+}
diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go
index ce8a6ff..16c2194 100644
--- a/src/pkg/gob/debug.go
+++ b/src/pkg/gob/debug.go
@@ -154,6 +154,7 @@ func (deb *debugger) dump(format string, args ...interface{}) {
 }
 
 // Debug prints a human-readable representation of the gob data read from r.
+// It is a no-op unless debugging was enabled when the package was built.
 func Debug(r io.Reader) {
 	err := debug(r)
 	if err != nil {
diff --git a/src/pkg/http/Makefile b/src/pkg/http/Makefile
index df4ab95..af4fbc1 100644
--- a/src/pkg/http/Makefile
+++ b/src/pkg/http/Makefile
@@ -10,6 +10,7 @@ GOFILES=\
 	client.go\
 	cookie.go\
 	dump.go\
+	filetransport.go\
 	fs.go\
 	header.go\
 	lex.go\
diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go
index f7de89f..d36eaa1 100644
--- a/src/pkg/http/cgi/host.go
+++ b/src/pkg/http/cgi/host.go
@@ -32,13 +32,13 @@ import (
 var trailingPort = regexp.MustCompile(`:([0-9]+)$`)
 
 var osDefaultInheritEnv = map[string][]string{
-	"darwin":  []string{"DYLD_LIBRARY_PATH"},
-	"freebsd": []string{"LD_LIBRARY_PATH"},
-	"hpux":    []string{"LD_LIBRARY_PATH", "SHLIB_PATH"},
-	"irix":    []string{"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
-	"linux":   []string{"LD_LIBRARY_PATH"},
-	"solaris": []string{"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
-	"windows": []string{"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
+	"darwin":  {"DYLD_LIBRARY_PATH"},
+	"freebsd": {"LD_LIBRARY_PATH"},
+	"hpux":    {"LD_LIBRARY_PATH", "SHLIB_PATH"},
+	"irix":    {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
+	"linux":   {"LD_LIBRARY_PATH"},
+	"solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
+	"windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
 }
 
 // Handler runs an executable in a subprocess with a CGI environment.
diff --git a/src/pkg/http/fcgi/fcgi_test.go b/src/pkg/http/fcgi/fcgi_test.go
index 16a6243..5c8e46b 100644
--- a/src/pkg/http/fcgi/fcgi_test.go
+++ b/src/pkg/http/fcgi/fcgi_test.go
@@ -53,13 +53,13 @@ var streamTests = []struct {
 	{"two records", typeStdin, 300, make([]byte, 66000),
 		bytes.Join([][]byte{
 			// header for the first record
-			[]byte{1, typeStdin, 0x01, 0x2C, 0xFF, 0xFF, 1, 0},
+			{1, typeStdin, 0x01, 0x2C, 0xFF, 0xFF, 1, 0},
 			make([]byte, 65536),
 			// header for the second
-			[]byte{1, typeStdin, 0x01, 0x2C, 0x01, 0xD1, 7, 0},
+			{1, typeStdin, 0x01, 0x2C, 0x01, 0xD1, 7, 0},
 			make([]byte, 472),
 			// header for the empty record
-			[]byte{1, typeStdin, 0x01, 0x2C, 0, 0, 0, 0},
+			{1, typeStdin, 0x01, 0x2C, 0, 0, 0, 0},
 		},
 			nil),
 	},
diff --git a/src/pkg/http/filetransport.go b/src/pkg/http/filetransport.go
new file mode 100644
index 0000000..78f3aa2
--- /dev/null
+++ b/src/pkg/http/filetransport.go
@@ -0,0 +1,124 @@
+// 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 http
+
+import (
+	"fmt"
+	"io"
+	"os"
+)
+
+// fileTransport implements RoundTripper for the 'file' protocol.
+type fileTransport struct {
+	fh fileHandler
+}
+
+// NewFileTransport returns a new RoundTripper, serving the provided
+// FileSystem. The returned RoundTripper ignores the URL host in its
+// incoming requests, as well as most other properties of the
+// request.
+//
+// The typical use case for NewFileTransport is to register the "file"
+// protocol with a Transport, as in:
+//
+//   t := &http.Transport{}
+//   t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
+//   c := &http.Client{Transport: t}
+//   res, err := c.Get("file:///etc/passwd")
+//   ...
+func NewFileTransport(fs FileSystem) RoundTripper {
+	return fileTransport{fileHandler{fs}}
+}
+
+func (t fileTransport) RoundTrip(req *Request) (resp *Response, err os.Error) {
+	// We start ServeHTTP in a goroutine, which may take a long
+	// time if the file is large.  The newPopulateResponseWriter
+	// call returns a channel which either ServeHTTP or finish()
+	// sends our *Response on, once the *Response itself has been
+	// populated (even if the body itself is still being
+	// written to the res.Body, a pipe)
+	rw, resc := newPopulateResponseWriter()
+	go func() {
+		t.fh.ServeHTTP(rw, req)
+		rw.finish()
+	}()
+	return <-resc, nil
+}
+
+func newPopulateResponseWriter() (*populateResponse, <-chan *Response) {
+	pr, pw := io.Pipe()
+	rw := &populateResponse{
+		ch: make(chan *Response),
+		pw: pw,
+		res: &Response{
+			Proto:      "HTTP/1.0",
+			ProtoMajor: 1,
+			Header:     make(Header),
+			Close:      true,
+			Body:       pr,
+		},
+	}
+	return rw, rw.ch
+}
+
+// populateResponse is a ResponseWriter that populates the *Response
+// in res, and writes its body to a pipe connected to the response
+// body. Once writes begin or finish() is called, the response is sent
+// on ch.
+type populateResponse struct {
+	res          *Response
+	ch           chan *Response
+	wroteHeader  bool
+	hasContent   bool
+	sentResponse bool
+	pw           *io.PipeWriter
+}
+
+func (pr *populateResponse) finish() {
+	if !pr.wroteHeader {
+		pr.WriteHeader(500)
+	}
+	if !pr.sentResponse {
+		pr.sendResponse()
+	}
+	pr.pw.Close()
+}
+
+func (pr *populateResponse) sendResponse() {
+	if pr.sentResponse {
+		return
+	}
+	pr.sentResponse = true
+
+	if pr.hasContent {
+		pr.res.ContentLength = -1
+	}
+	pr.ch <- pr.res
+}
+
+func (pr *populateResponse) Header() Header {
+	return pr.res.Header
+}
+
+func (pr *populateResponse) WriteHeader(code int) {
+	if pr.wroteHeader {
+		return
+	}
+	pr.wroteHeader = true
+
+	pr.res.StatusCode = code
+	pr.res.Status = fmt.Sprintf("%d %s", code, StatusText(code))
+}
+
+func (pr *populateResponse) Write(p []byte) (n int, err os.Error) {
+	if !pr.wroteHeader {
+		pr.WriteHeader(StatusOK)
+	}
+	pr.hasContent = true
+	if !pr.sentResponse {
+		pr.sendResponse()
+	}
+	return pr.pw.Write(p)
+}
diff --git a/src/pkg/http/filetransport_test.go b/src/pkg/http/filetransport_test.go
new file mode 100644
index 0000000..2634243
--- /dev/null
+++ b/src/pkg/http/filetransport_test.go
@@ -0,0 +1,63 @@
+// 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 http_test
+
+import (
+	"http"
+	"io/ioutil"
+	"path/filepath"
+	"os"
+	"testing"
+)
+
+func checker(t *testing.T) func(string, os.Error) {
+	return func(call string, err os.Error) {
+		if err == nil {
+			return
+		}
+		t.Fatalf("%s: %v", call, err)
+	}
+}
+
+func TestFileTransport(t *testing.T) {
+	check := checker(t)
+
+	dname, err := ioutil.TempDir("", "")
+	check("TempDir", err)
+	fname := filepath.Join(dname, "foo.txt")
+	err = ioutil.WriteFile(fname, []byte("Bar"), 0644)
+	check("WriteFile", err)
+
+	tr := &http.Transport{}
+	tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname)))
+	c := &http.Client{Transport: tr}
+
+	fooURLs := []string{"file:///foo.txt", "file://../foo.txt"}
+	for _, urlstr := range fooURLs {
+		res, err := c.Get(urlstr)
+		check("Get "+urlstr, err)
+		if res.StatusCode != 200 {
+			t.Errorf("for %s, StatusCode = %d, want 200", urlstr, res.StatusCode)
+		}
+		if res.ContentLength != -1 {
+			t.Errorf("for %s, ContentLength = %d, want -1", urlstr, res.ContentLength)
+		}
+		if res.Body == nil {
+			t.Fatalf("for %s, nil Body", urlstr)
+		}
+		slurp, err := ioutil.ReadAll(res.Body)
+		check("ReadAll "+urlstr, err)
+		if string(slurp) != "Bar" {
+			t.Errorf("for %s, got content %q, want %q", urlstr, string(slurp), "Bar")
+		}
+	}
+
+	const badURL = "file://../no-exist.txt"
+	res, err := c.Get(badURL)
+	check("Get "+badURL, err)
+	if res.StatusCode != 404 {
+		t.Errorf("for %s, StatusCode = %d, want 404", badURL, res.StatusCode)
+	}
+}
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go
index ed41fa4..6102231 100644
--- a/src/pkg/http/request.go
+++ b/src/pkg/http/request.go
@@ -234,8 +234,8 @@ func (r *Request) multipartReader() (*multipart.Reader, os.Error) {
 	if v == "" {
 		return nil, ErrNotMultipart
 	}
-	d, params := mime.ParseMediaType(v)
-	if d != "multipart/form-data" {
+	d, params, err := mime.ParseMediaType(v)
+	if err != nil || d != "multipart/form-data" {
 		return nil, ErrNotMultipart
 	}
 	boundary, ok := params["boundary"]
@@ -608,27 +608,77 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {
 	return req, nil
 }
 
-// ParseForm parses the raw query.
-// For POST requests, it also parses the request body as a form.
+// MaxBytesReader is similar to io.LimitReader, but is intended for
+// limiting the size of incoming request bodies. In contrast to
+// io.LimitReader, MaxBytesReader is a ReadCloser, returns a non-EOF
+// error if the body is too large, and also takes care of closing the
+// underlying io.ReadCloser connection (if applicable, usually a TCP
+// connection) when the limit is hit.  This prevents clients from
+// accidentally or maliciously sending a large request and wasting
+// server resources.
+func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
+	return &maxBytesReader{w: w, r: r, n: n}
+}
+
+type maxBytesReader struct {
+	w       ResponseWriter
+	r       io.ReadCloser // underlying reader
+	n       int64         // max bytes remaining
+	stopped bool
+}
+
+func (l *maxBytesReader) Read(p []byte) (n int, err os.Error) {
+	if l.n <= 0 {
+		if !l.stopped {
+			l.stopped = true
+			if res, ok := l.w.(*response); ok {
+				res.requestTooLarge()
+			}
+		}
+		return 0, os.NewError("http: request body too large")
+	}
+	if int64(len(p)) > l.n {
+		p = p[:l.n]
+	}
+	n, err = l.r.Read(p)
+	l.n -= int64(n)
+	return
+}
+
+func (l *maxBytesReader) Close() os.Error {
+	return l.r.Close()
+}
+
+// ParseForm parses the raw query from the URL.
+//
+// For POST or PUT requests, it also parses the request body as a form.
+// If the request Body's size has not already been limited by MaxBytesReader,
+// the size is capped at 10MB.
+//
 // ParseMultipartForm calls ParseForm automatically.
 // It is idempotent.
 func (r *Request) ParseForm() (err os.Error) {
 	if r.Form != nil {
 		return
 	}
-
 	if r.URL != nil {
 		r.Form, err = url.ParseQuery(r.URL.RawQuery)
 	}
-	if r.Method == "POST" {
+	if r.Method == "POST" || r.Method == "PUT" {
 		if r.Body == nil {
 			return os.NewError("missing form body")
 		}
 		ct := r.Header.Get("Content-Type")
-		switch strings.SplitN(ct, ";", 2)[0] {
-		case "text/plain", "application/x-www-form-urlencoded", "":
-			const maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
-			b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1))
+		ct, _, err := mime.ParseMediaType(ct)
+		switch {
+		case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "":
+			var reader io.Reader = r.Body
+			maxFormSize := int64((1 << 63) - 1)
+			if _, ok := r.Body.(*maxBytesReader); !ok {
+				maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
+				reader = io.LimitReader(r.Body, maxFormSize+1)
+			}
+			b, e := ioutil.ReadAll(reader)
 			if e != nil {
 				if err == nil {
 					err = e
@@ -652,8 +702,13 @@ func (r *Request) ParseForm() (err os.Error) {
 					r.Form.Add(k, value)
 				}
 			}
-		case "multipart/form-data":
-			// handled by ParseMultipartForm
+		case ct == "multipart/form-data":
+			// handled by ParseMultipartForm (which is calling us, or should be)
+			// TODO(bradfitz): there are too many possible
+			// orders to call too many functions here.
+			// Clean this up and write more tests.
+			// request_test.go contains the start of this,
+			// in TestRequestMultipartCallOrder.
 		default:
 			return &badStringError{"unknown Content-Type", ct}
 		}
diff --git a/src/pkg/http/request_test.go b/src/pkg/http/request_test.go
index 869cd57..175d6f1 100644
--- a/src/pkg/http/request_test.go
+++ b/src/pkg/http/request_test.go
@@ -20,57 +20,6 @@ import (
 	"url"
 )
 
-type stringMultimap map[string][]string
-
-type parseTest struct {
-	query string
-	out   stringMultimap
-}
-
-var parseTests = []parseTest{
-	{
-		query: "a=1&b=2",
-		out:   stringMultimap{"a": []string{"1"}, "b": []string{"2"}},
-	},
-	{
-		query: "a=1&a=2&a=banana",
-		out:   stringMultimap{"a": []string{"1", "2", "banana"}},
-	},
-	{
-		query: "ascii=%3Ckey%3A+0x90%3E",
-		out:   stringMultimap{"ascii": []string{"<key: 0x90>"}},
-	},
-}
-
-func TestParseForm(t *testing.T) {
-	for i, test := range parseTests {
-		form, err := url.ParseQuery(test.query)
-		if err != nil {
-			t.Errorf("test %d: Unexpected error: %v", i, err)
-			continue
-		}
-		if len(form) != len(test.out) {
-			t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
-		}
-		for k, evs := range test.out {
-			vs, ok := form[k]
-			if !ok {
-				t.Errorf("test %d: Missing key %q", i, k)
-				continue
-			}
-			if len(vs) != len(evs) {
-				t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
-				continue
-			}
-			for j, ev := range evs {
-				if v := vs[j]; v != ev {
-					t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
-				}
-			}
-		}
-	}
-}
-
 func TestQuery(t *testing.T) {
 	req := &Request{Method: "GET"}
 	req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar")
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index ac04033..1743911 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -891,8 +891,62 @@ func TestRequestLimit(t *testing.T) {
 		// we do support it (at least currently), so we expect a response below.
 		t.Fatalf("Do: %v", err)
 	}
-	if res.StatusCode != 400 {
-		t.Fatalf("expected 400 response status; got: %d %s", res.StatusCode, res.Status)
+	if res.StatusCode != 413 {
+		t.Fatalf("expected 413 response status; got: %d %s", res.StatusCode, res.Status)
+	}
+}
+
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (n int, err os.Error) {
+	for i := range p {
+		p[i] = byte(b)
+	}
+	return len(p), nil
+}
+
+type countReader struct {
+	r io.Reader
+	n *int64
+}
+
+func (cr countReader) Read(p []byte) (n int, err os.Error) {
+	n, err = cr.r.Read(p)
+	*cr.n += int64(n)
+	return
+}
+
+func TestRequestBodyLimit(t *testing.T) {
+	const limit = 1 << 20
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		r.Body = MaxBytesReader(w, r.Body, limit)
+		n, err := io.Copy(ioutil.Discard, r.Body)
+		if err == nil {
+			t.Errorf("expected error from io.Copy")
+		}
+		if n != limit {
+			t.Errorf("io.Copy = %d, want %d", n, limit)
+		}
+	}))
+	defer ts.Close()
+
+	nWritten := int64(0)
+	req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), &nWritten}, limit*200))
+
+	// Send the POST, but don't care it succeeds or not.  The
+	// remote side is going to reply and then close the TCP
+	// connection, and HTTP doesn't really define if that's
+	// allowed or not.  Some HTTP clients will get the response
+	// and some (like ours, currently) will complain that the
+	// request write failed, without reading the response.
+	//
+	// But that's okay, since what we're really testing is that
+	// the remote side hung up on us before we wrote too much.
+	_, _ = DefaultClient.Do(req)
+
+	if nWritten > limit*100 {
+		t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d",
+			limit, nWritten)
 	}
 }
 
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index b634e27..654af37 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -122,6 +122,25 @@ type response struct {
 	// "Connection: keep-alive" response header and a
 	// Content-Length.
 	closeAfterReply bool
+
+	// requestBodyLimitHit is set by requestTooLarge when
+	// maxBytesReader hits its max size. It is checked in
+	// WriteHeader, to make sure we don't consume the the
+	// remaining request body to try to advance to the next HTTP
+	// request. Instead, when this is set, we stop doing
+	// subsequent requests on this connection and stop reading
+	// input from it.
+	requestBodyLimitHit bool
+}
+
+// requestTooLarge is called by maxBytesReader when too much input has
+// been read from the client.
+func (r *response) requestTooLarge() {
+	r.closeAfterReply = true
+	r.requestBodyLimitHit = true
+	if !r.wroteHeader {
+		r.Header().Set("Connection", "close")
+	}
 }
 
 type writerOnly struct {
@@ -257,7 +276,7 @@ func (w *response) WriteHeader(code int) {
 
 	// Per RFC 2616, we should consume the request body before
 	// replying, if the handler hasn't already done so.
-	if w.req.ContentLength != 0 {
+	if w.req.ContentLength != 0 && !w.requestBodyLimitHit {
 		ecr, isExpecter := w.req.Body.(*expectContinueReader)
 		if !isExpecter || ecr.resp.wroteContinue {
 			w.req.Body.Close()
@@ -472,55 +491,6 @@ func (w *response) Write(data []byte) (n int, err os.Error) {
 	return m + n, err
 }
 
-// If this is an error reply (4xx or 5xx)
-// and the handler wrote some data explaining the error,
-// some browsers (i.e., Chrome, Internet Explorer)
-// will show their own error instead unless the error is
-// long enough.  The minimum lengths used in those
-// browsers are in the 256-512 range.
-// Pad to 1024 bytes.
-func errorKludge(w *response) {
-	const min = 1024
-
-	// Is this an error?
-	if kind := w.status / 100; kind != 4 && kind != 5 {
-		return
-	}
-
-	// Did the handler supply any info?  Enough?
-	if w.written == 0 || w.written >= min {
-		return
-	}
-
-	// Is it a broken browser?
-	var msg string
-	switch agent := w.req.UserAgent(); {
-	case strings.Contains(agent, "MSIE"):
-		msg = "Internet Explorer"
-	case strings.Contains(agent, "Chrome/"):
-		msg = "Chrome"
-	default:
-		return
-	}
-	msg += " would ignore this error page if this text weren't here.\n"
-
-	// Is it text?  ("Content-Type" is always in the map)
-	baseType := strings.SplitN(w.header.Get("Content-Type"), ";", 2)[0]
-	switch baseType {
-	case "text/html":
-		io.WriteString(w, "<!-- ")
-		for w.written < min {
-			io.WriteString(w, msg)
-		}
-		io.WriteString(w, " -->")
-	case "text/plain":
-		io.WriteString(w, "\n")
-		for w.written < min {
-			io.WriteString(w, msg)
-		}
-	}
-}
-
 func (w *response) finishRequest() {
 	// If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length
 	// back, we can make this a keep-alive response ...
@@ -536,14 +506,17 @@ func (w *response) finishRequest() {
 	if w.needSniff {
 		w.sniff()
 	}
-	errorKludge(w)
 	if w.chunking {
 		io.WriteString(w.conn.buf, "0\r\n")
 		// trailer key/value pairs, followed by blank line
 		io.WriteString(w.conn.buf, "\r\n")
 	}
 	w.conn.buf.Flush()
-	w.req.Body.Close()
+	// Close the body, unless we're about to close the whole TCP connection
+	// anyway.
+	if !w.closeAfterReply {
+		w.req.Body.Close()
+	}
 	if w.req.MultipartForm != nil {
 		w.req.MultipartForm.RemoveAll()
 	}
@@ -592,14 +565,18 @@ func (c *conn) serve() {
 	for {
 		w, err := c.readRequest()
 		if err != nil {
+			msg := "400 Bad Request"
 			if err == errTooLarge {
 				// Their HTTP client may or may not be
 				// able to read this if we're
 				// responding to them and hanging up
 				// while they're still writing their
 				// request.  Undefined behavior.
-				fmt.Fprintf(c.rwc, "HTTP/1.1 400 Request Too Large\r\n\r\n")
+				msg = "413 Request Entity Too Large"
+			} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
+				break // Don't reply
 			}
+			fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg)
 			break
 		}
 
diff --git a/src/pkg/http/spdy/read.go b/src/pkg/http/spdy/read.go
index c6b6ab3..2b1fd3d 100644
--- a/src/pkg/http/spdy/read.go
+++ b/src/pkg/http/spdy/read.go
@@ -214,7 +214,7 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame)
 	}
 	// Remove this condition when we bump Version to 3.
 	if Version >= 3 {
-		for h, _ := range frame.Headers {
+		for h := range frame.Headers {
 			if invalidReqHeaders[h] {
 				return &Error{InvalidHeaderPresent, frame.StreamId}
 			}
@@ -247,7 +247,7 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) o
 	}
 	// Remove this condition when we bump Version to 3.
 	if Version >= 3 {
-		for h, _ := range frame.Headers {
+		for h := range frame.Headers {
 			if invalidRespHeaders[h] {
 				return &Error{InvalidHeaderPresent, frame.StreamId}
 			}
@@ -287,7 +287,7 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) os.
 		} else {
 			invalidHeaders = invalidRespHeaders
 		}
-		for h, _ := range frame.Headers {
+		for h := range frame.Headers {
 			if invalidHeaders[h] {
 				return &Error{InvalidHeaderPresent, frame.StreamId}
 			}
diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go
index b65d99a..0a754d2 100644
--- a/src/pkg/http/transfer.go
+++ b/src/pkg/http/transfer.go
@@ -478,6 +478,8 @@ type body struct {
 	r       *bufio.Reader // underlying wire-format reader for the trailer
 	closing bool          // is the connection to be closed after reading body?
 	closed  bool
+
+	res *response // response writer for server requests, else nil
 }
 
 // ErrBodyReadAfterClose is returned when reading a Request Body after
@@ -506,6 +508,15 @@ func (b *body) Close() os.Error {
 		return nil
 	}
 
+	// In a server request, don't continue reading from the client
+	// if we've already hit the maximum body size set by the
+	// handler. If this is set, that also means the TCP connection
+	// is about to be closed, so getting to the next HTTP request
+	// in the stream is not necessary.
+	if b.res != nil && b.res.requestBodyLimitHit {
+		return nil
+	}
+
 	if _, err := io.Copy(ioutil.Discard, b); err != nil {
 		return err
 	}
diff --git a/src/pkg/http/transport.go b/src/pkg/http/transport.go
index 4302ffa..8ac7832 100644
--- a/src/pkg/http/transport.go
+++ b/src/pkg/http/transport.go
@@ -54,6 +54,10 @@ type Transport struct {
 	// If Dial is nil, net.Dial is used.
 	Dial func(net, addr string) (c net.Conn, err os.Error)
 
+	// TLSClientConfig specifies the TLS configuration to use with
+	// tls.Client. If nil, the default configuration is used.
+	TLSClientConfig *tls.Config
+
 	DisableKeepAlives  bool
 	DisableCompression bool
 
@@ -338,7 +342,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) {
 
 	if cm.targetScheme == "https" {
 		// Initiate TLS and check remote host name against certificate.
-		conn = tls.Client(conn, nil)
+		conn = tls.Client(conn, t.TLSClientConfig)
 		if err = conn.(*tls.Conn).Handshake(); err != nil {
 			return nil, err
 		}
diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go
index 11def94..a01cda8 100644
--- a/src/pkg/image/image.go
+++ b/src/pkg/image/image.go
@@ -24,6 +24,17 @@ type Image interface {
 	At(x, y int) Color
 }
 
+// PalettedImage is an image whose colors may come from a limited palette.
+// If m is a PalettedImage and m.ColorModel() returns a PalettedColorModel p,
+// then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's
+// color model is not a PalettedColorModel, then ColorIndexAt's behavior is
+// undefined.
+type PalettedImage interface {
+	// ColorIndexAt returns the palette index of the pixel at (x, y).
+	ColorIndexAt(x, y int) uint8
+	Image
+}
+
 // RGBA is an in-memory image of RGBAColor values.
 type RGBA struct {
 	// Pix holds the image's pixels, in R, G, B, A order. The pixel at
@@ -741,6 +752,9 @@ func (p PalettedColorModel) Index(c Color) int {
 		dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb)
 		ssd := (dr * dr) + (dg * dg) + (db * db)
 		if ssd < bestSSD {
+			if ssd == 0 {
+				return i
+			}
 			ret, bestSSD = i, ssd
 		}
 	}
diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go
index 8c76afa..aa02374 100644
--- a/src/pkg/image/png/reader.go
+++ b/src/pkg/image/png/reader.go
@@ -489,6 +489,16 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
 		// The current row for y is the previous row for y+1.
 		pr, cr = cr, pr
 	}
+
+	// Check for EOF, to verify the zlib checksum.
+	n, err := r.Read(pr[:1])
+	if err != os.EOF {
+		return nil, FormatError(err.String())
+	}
+	if n != 0 {
+		return nil, FormatError("too much pixel data")
+	}
+
 	return img, nil
 }
 
diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go
index bcc1a3d..2088431 100644
--- a/src/pkg/image/png/reader_test.go
+++ b/src/pkg/image/png/reader_test.go
@@ -10,6 +10,7 @@ import (
 	"image"
 	"io"
 	"os"
+	"strings"
 	"testing"
 )
 
@@ -41,7 +42,7 @@ var filenamesShort = []string{
 	"basn6a16",
 }
 
-func readPng(filename string) (image.Image, os.Error) {
+func readPNG(filename string) (image.Image, os.Error) {
 	f, err := os.Open(filename)
 	if err != nil {
 		return nil, err
@@ -183,7 +184,7 @@ func TestReader(t *testing.T) {
 	}
 	for _, fn := range names {
 		// Read the .png file.
-		img, err := readPng("testdata/pngsuite/" + fn + ".png")
+		img, err := readPNG("testdata/pngsuite/" + fn + ".png")
 		if err != nil {
 			t.Error(fn, err)
 			continue
@@ -239,3 +240,29 @@ func TestReader(t *testing.T) {
 		}
 	}
 }
+
+var readerErrors = []struct {
+	file string
+	err  string
+}{
+	{"invalid-zlib.png", "zlib checksum error"},
+	{"invalid-crc32.png", "invalid checksum"},
+	{"invalid-noend.png", "unexpected EOF"},
+	{"invalid-trunc.png", "unexpected EOF"},
+}
+
+func TestReaderError(t *testing.T) {
+	for _, tt := range readerErrors {
+		img, err := readPNG("testdata/" + tt.file)
+		if err == nil {
+			t.Errorf("decoding %s: missing error", tt.file)
+			continue
+		}
+		if !strings.Contains(err.String(), tt.err) {
+			t.Errorf("decoding %s: %s, want %s", tt.file, err, tt.err)
+		}
+		if img != nil {
+			t.Errorf("decoding %s: have image + error")
+		}
+	}
+}
diff --git a/src/pkg/image/png/testdata/invalid-crc32.png b/src/pkg/image/png/testdata/invalid-crc32.png
new file mode 100644
index 0000000..e5be408
Binary files /dev/null and b/src/pkg/image/png/testdata/invalid-crc32.png differ
diff --git a/src/pkg/image/png/testdata/invalid-noend.png b/src/pkg/image/png/testdata/invalid-noend.png
new file mode 100644
index 0000000..9137270
Binary files /dev/null and b/src/pkg/image/png/testdata/invalid-noend.png differ
diff --git a/src/pkg/image/png/testdata/invalid-trunc.png b/src/pkg/image/png/testdata/invalid-trunc.png
new file mode 100644
index 0000000..d0748cf
Binary files /dev/null and b/src/pkg/image/png/testdata/invalid-trunc.png differ
diff --git a/src/pkg/image/png/testdata/invalid-zlib.png b/src/pkg/image/png/testdata/invalid-zlib.png
new file mode 100644
index 0000000..c6d051c
Binary files /dev/null and b/src/pkg/image/png/testdata/invalid-zlib.png differ
diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go
index 55ca97e..f9556a0 100644
--- a/src/pkg/image/png/writer.go
+++ b/src/pkg/image/png/writer.go
@@ -336,9 +336,16 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
 				}
 			}
 		case cbP8:
-			paletted := m.(*image.Paletted)
-			offset := (y - b.Min.Y) * paletted.Stride
-			copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
+			if p, _ := m.(*image.Paletted); p != nil {
+				offset := (y - b.Min.Y) * p.Stride
+				copy(cr[0][1:], p.Pix[offset:offset+b.Dx()])
+			} else {
+				pi := m.(image.PalettedImage)
+				for x := b.Min.X; x < b.Max.X; x++ {
+					cr[0][i] = pi.ColorIndexAt(x, y)
+					i += 1
+				}
+			}
 		case cbTCA8:
 			// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
 			for x := b.Min.X; x < b.Max.X; x++ {
@@ -432,7 +439,12 @@ func Encode(w io.Writer, m image.Image) os.Error {
 	var e encoder
 	e.w = w
 	e.m = m
-	pal, _ := m.(*image.Paletted)
+
+	var pal image.PalettedColorModel
+	// cbP8 encoding needs PalettedImage's ColorIndexAt method.
+	if _, ok := m.(image.PalettedImage); ok {
+		pal, _ = m.ColorModel().(image.PalettedColorModel)
+	}
 	if pal != nil {
 		e.cb = cbP8
 	} else {
@@ -459,8 +471,8 @@ func Encode(w io.Writer, m image.Image) os.Error {
 	_, e.err = io.WriteString(w, pngHeader)
 	e.writeIHDR()
 	if pal != nil {
-		e.writePLTE(pal.Palette)
-		e.maybeWritetRNS(pal.Palette)
+		e.writePLTE(pal)
+		e.maybeWritetRNS(pal)
 	}
 	e.writeIDATs()
 	e.writeIEND()
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index 1599791..046aad9 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -56,13 +56,13 @@ func TestWriter(t *testing.T) {
 	for _, fn := range names {
 		qfn := "testdata/pngsuite/" + fn + ".png"
 		// Read the image.
-		m0, err := readPng(qfn)
+		m0, err := readPNG(qfn)
 		if err != nil {
 			t.Error(fn, err)
 			continue
 		}
 		// Read the image again, encode it, and decode it.
-		m1, err := readPng(qfn)
+		m1, err := readPNG(qfn)
 		if err != nil {
 			t.Error(fn, err)
 			return
diff --git a/src/pkg/image/tiff/reader.go b/src/pkg/image/tiff/reader.go
index f565266..c963992 100644
--- a/src/pkg/image/tiff/reader.go
+++ b/src/pkg/image/tiff/reader.go
@@ -180,22 +180,21 @@ func (d *decoder) flushBits() {
 // decode decodes the raw data of an image.
 // It reads from d.buf and writes the strip with ymin <= y < ymax into dst.
 func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error {
-	spp := len(d.features[tBitsPerSample]) // samples per pixel
 	d.off = 0
-	width := dst.Bounds().Dx()
 
 	// Apply horizontal predictor if necessary.
 	// In this case, p contains the color difference to the preceding pixel.
 	// See page 64-65 of the spec.
 	if d.firstVal(tPredictor) == prHorizontal && d.firstVal(tBitsPerSample) == 8 {
+		var off int
+		spp := len(d.features[tBitsPerSample]) // samples per pixel
 		for y := ymin; y < ymax; y++ {
-			d.off += spp
-			for x := 0; x < (width-1)*spp; x++ {
-				d.buf[d.off] += d.buf[d.off-spp]
-				d.off++
+			off += spp
+			for x := 0; x < (dst.Bounds().Dx()-1)*spp; x++ {
+				d.buf[off] += d.buf[off-spp]
+				off++
 			}
 		}
-		d.off = 0
 	}
 
 	switch d.mode {
@@ -224,28 +223,32 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error {
 		}
 	case mRGB:
 		img := dst.(*image.RGBA)
-		for y := ymin; y < ymax; y++ {
-			for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
-				img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], 0xff})
-				d.off += spp
-			}
+		min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		var off int
+		for i := min; i < max; i += 4 {
+			img.Pix[i+0] = d.buf[off+0]
+			img.Pix[i+1] = d.buf[off+1]
+			img.Pix[i+2] = d.buf[off+2]
+			img.Pix[i+3] = 0xff
+			off += 3
 		}
 	case mNRGBA:
 		img := dst.(*image.NRGBA)
-		for y := ymin; y < ymax; y++ {
-			for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
-				img.SetNRGBA(x, y, image.NRGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]})
-				d.off += spp
-			}
+		min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		if len(d.buf) != max-min {
+			return FormatError("short data strip")
 		}
+		copy(img.Pix[min:max], d.buf)
 	case mRGBA:
 		img := dst.(*image.RGBA)
-		for y := ymin; y < ymax; y++ {
-			for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
-				img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]})
-				d.off += spp
-			}
+		min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4
+		if len(d.buf) != max-min {
+			return FormatError("short data strip")
 		}
+		copy(img.Pix[min:max], d.buf)
 	}
 
 	return nil
@@ -309,6 +312,9 @@ func newDecoder(r io.Reader) (*decoder, os.Error) {
 		// RGB images normally have 3 samples per pixel.
 		// If there are more, ExtraSamples (p. 31-32 of the spec)
 		// gives their meaning (usually an alpha channel).
+		//
+		// This implementation does not support extra samples
+		// of an unspecified type.
 		switch len(d.features[tBitsPerSample]) {
 		case 3:
 			d.mode = mRGB
@@ -320,8 +326,7 @@ func newDecoder(r io.Reader) (*decoder, os.Error) {
 				d.mode = mNRGBA
 				d.config.ColorModel = image.NRGBAColorModel
 			default:
-				// The extra sample is discarded.
-				d.mode = mRGB
+				return nil, FormatError("wrong number of samples for RGB")
 			}
 		default:
 			return nil, FormatError("wrong number of samples for RGB")
diff --git a/src/pkg/image/tiff/reader_test.go b/src/pkg/image/tiff/reader_test.go
index f2122c4..1eb2bcd 100644
--- a/src/pkg/image/tiff/reader_test.go
+++ b/src/pkg/image/tiff/reader_test.go
@@ -5,10 +5,16 @@
 package tiff
 
 import (
+	"io/ioutil"
 	"os"
 	"testing"
 )
 
+// Read makes *buffer implements io.Reader, so that we can pass one to Decode.
+func (*buffer) Read([]byte) (int, os.Error) {
+	panic("unimplemented")
+}
+
 // TestNoRPS tries to decode an image that has no RowsPerStrip tag.
 // The tag is mandatory according to the spec but some software omits
 // it in the case of a single strip.
@@ -23,3 +29,22 @@ func TestNoRPS(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+const filename = "testdata/video-001-uncompressed.tiff"
+
+// BenchmarkDecode benchmarks the decoding of an image.
+func BenchmarkDecode(b *testing.B) {
+	b.StopTimer()
+	contents, err := ioutil.ReadFile(filename)
+	if err != nil {
+		panic(err)
+	}
+	r := &buffer{buf: contents}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		_, err := Decode(r)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
diff --git a/src/pkg/image/tiff/testdata/video-001-uncompressed.tiff b/src/pkg/image/tiff/testdata/video-001-uncompressed.tiff
new file mode 100644
index 0000000..fad1471
Binary files /dev/null and b/src/pkg/image/tiff/testdata/video-001-uncompressed.tiff differ
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index b879fe5..1fdf347 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -437,3 +437,27 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
 
 // Size returns the size of the section in bytes.
 func (s *SectionReader) Size() int64 { return s.limit - s.base }
+
+// TeeReader returns a Reader that writes to w what it reads from r.
+// All reads from r performed through it are matched with
+// corresponding writes to w.  There is no internal buffering -
+// the write must complete before the read completes.
+// Any error encountered while writing is reported as a read error.
+func TeeReader(r Reader, w Writer) Reader {
+	return &teeReader{r, w}
+}
+
+type teeReader struct {
+	r Reader
+	w Writer
+}
+
+func (t *teeReader) Read(p []byte) (n int, err os.Error) {
+	n, err = t.r.Read(p)
+	if n > 0 {
+		if n, err := t.w.Write(p[:n]); err != nil {
+			return n, err
+		}
+	}
+	return
+}
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index bc4f354..7449dcf 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -177,3 +177,30 @@ func testReadAtLeast(t *testing.T, rb ReadWriter) {
 		t.Errorf("expected to have read 1 bytes, got %v", n)
 	}
 }
+
+func TestTeeReader(t *testing.T) {
+	src := []byte("hello, world")
+	dst := make([]byte, len(src))
+	rb := bytes.NewBuffer(src)
+	wb := new(bytes.Buffer)
+	r := TeeReader(rb, wb)
+	if n, err := ReadFull(r, dst); err != nil || n != len(src) {
+		t.Fatalf("ReadFull(r, dst) = %d, %v; want %d, nil", n, err, len(src))
+	}
+	if !bytes.Equal(dst, src) {
+		t.Errorf("bytes read = %q want %q", dst, src)
+	}
+	if !bytes.Equal(wb.Bytes(), src) {
+		t.Errorf("bytes written = %q want %q", wb.Bytes(), src)
+	}
+	if n, err := r.Read(dst); n != 0 || err != os.EOF {
+		t.Errorf("r.Read at EOF = %d, %v want 0, EOF", n, err)
+	}
+	rb = bytes.NewBuffer(src)
+	pr, pw := Pipe()
+	pr.Close()
+	r = TeeReader(rb, pw)
+	if n, err := ReadFull(r, dst); n != 0 || err != os.EPIPE {
+		t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
+	}
+}
diff --git a/src/pkg/json/Makefile b/src/pkg/json/Makefile
index 4e5a8a1..28ed62b 100644
--- a/src/pkg/json/Makefile
+++ b/src/pkg/json/Makefile
@@ -11,5 +11,6 @@ GOFILES=\
 	indent.go\
 	scanner.go\
 	stream.go\
+	tags.go\
 
 include ../../Make.pkg
diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go
index 4f6562b..b7129f9 100644
--- a/src/pkg/json/decode.go
+++ b/src/pkg/json/decode.go
@@ -140,6 +140,7 @@ type decodeState struct {
 	scan       scanner
 	nextscan   scanner // for calls to nextValue
 	savedError os.Error
+	tempstr    string // scratch space to avoid some allocations
 }
 
 // errPhase is used for errors that should not happen unless
@@ -470,6 +471,8 @@ func (d *decodeState) object(v reflect.Value) {
 
 		// Figure out field corresponding to key.
 		var subv reflect.Value
+		destring := false // whether the value is wrapped in a string to be decoded first
+
 		if mv.IsValid() {
 			elemType := mv.Type().Elem()
 			if !mapElem.IsValid() {
@@ -486,7 +489,8 @@ func (d *decodeState) object(v reflect.Value) {
 			if isValidTag(key) {
 				for i := 0; i < sv.NumField(); i++ {
 					f = st.Field(i)
-					if f.Tag.Get("json") == key {
+					tagName, _ := parseTag(f.Tag.Get("json"))
+					if tagName == key {
 						ok = true
 						break
 					}
@@ -508,6 +512,8 @@ func (d *decodeState) object(v reflect.Value) {
 				} else {
 					subv = sv.FieldByIndex(f.Index)
 				}
+				_, opts := parseTag(f.Tag.Get("json"))
+				destring = opts.Contains("string")
 			}
 		}
 
@@ -520,8 +526,12 @@ func (d *decodeState) object(v reflect.Value) {
 		}
 
 		// Read value.
-		d.value(subv)
-
+		if destring {
+			d.value(reflect.ValueOf(&d.tempstr))
+			d.literalStore([]byte(d.tempstr), subv)
+		} else {
+			d.value(subv)
+		}
 		// Write value back to map;
 		// if using struct, subv points into struct already.
 		if mv.IsValid() {
@@ -550,8 +560,12 @@ func (d *decodeState) literal(v reflect.Value) {
 	// Scan read one byte too far; back up.
 	d.off--
 	d.scan.undo(op)
-	item := d.data[start:d.off]
 
+	d.literalStore(d.data[start:d.off], v)
+}
+
+// literalStore decodes a literal stored in item into v.
+func (d *decodeState) literalStore(item []byte, v reflect.Value) {
 	// Check for unmarshaler.
 	wantptr := item[0] == 'n' // null
 	unmarshaler, pv := d.indirect(v, wantptr)
diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go
index a855d60..c6d4fa0 100644
--- a/src/pkg/json/decode_test.go
+++ b/src/pkg/json/decode_test.go
@@ -42,7 +42,7 @@ var (
 	um0, um1 unmarshaler // target2 of unmarshaling
 	ump      = &um1
 	umtrue   = unmarshaler{true}
-	umslice  = []unmarshaler{unmarshaler{true}}
+	umslice  = []unmarshaler{{true}}
 	umslicep = new([]unmarshaler)
 	umstruct = ustruct{unmarshaler{true}}
 )
@@ -262,7 +262,10 @@ type All struct {
 	Float32 float32
 	Float64 float64
 
-	Foo string `json:"bar"`
+	Foo  string `json:"bar"`
+	Foo2 string `json:"bar2,dummyopt"`
+
+	IntStr int64 `json:",string"`
 
 	PBool    *bool
 	PInt     *int
@@ -331,6 +334,8 @@ var allValue = All{
 	Float32: 14.1,
 	Float64: 15.1,
 	Foo:     "foo",
+	Foo2:    "foo2",
+	IntStr:  42,
 	String:  "16",
 	Map: map[string]Small{
 		"17": {Tag: "tag17"},
@@ -391,6 +396,8 @@ var allValueIndent = `{
 	"Float32": 14.1,
 	"Float64": 15.1,
 	"bar": "foo",
+	"bar2": "foo2",
+	"IntStr": "42",
 	"PBool": null,
 	"PInt": null,
 	"PInt8": null,
@@ -481,6 +488,8 @@ var pallValueIndent = `{
 	"Float32": 0,
 	"Float64": 0,
 	"bar": "",
+	"bar2": "",
+        "IntStr": "0",
 	"PBool": true,
 	"PInt": 2,
 	"PInt8": 3,
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
index 3e593fe..5b4e616 100644
--- a/src/pkg/json/encode.go
+++ b/src/pkg/json/encode.go
@@ -14,7 +14,6 @@ import (
 	"runtime"
 	"sort"
 	"strconv"
-	"strings"
 	"unicode"
 	"utf8"
 )
@@ -59,6 +58,12 @@ import (
 //   // Note the leading comma.
 //   Field int `json:",omitempty"`
 //
+// The "string" option signals that a field is stored as JSON inside a
+// JSON-encoded string.  This extra level of encoding is sometimes
+// used when communicating with JavaScript programs:
+//
+//    Int64String int64 `json:",string"`
+//
 // The key name will be used if it's a non-empty string consisting of
 // only Unicode letters, digits, dollar signs, hyphens, and underscores.
 //
@@ -221,6 +226,12 @@ func isEmptyValue(v reflect.Value) bool {
 }
 
 func (e *encodeState) reflectValue(v reflect.Value) {
+	e.reflectValueQuoted(v, false)
+}
+
+// reflectValueQuoted writes the value in v to the output.
+// If quoted is true, the serialization is wrapped in a JSON string.
+func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
 	if !v.IsValid() {
 		e.WriteString("null")
 		return
@@ -238,26 +249,39 @@ func (e *encodeState) reflectValue(v reflect.Value) {
 		return
 	}
 
+	writeString := (*encodeState).WriteString
+	if quoted {
+		writeString = (*encodeState).string
+	}
+
 	switch v.Kind() {
 	case reflect.Bool:
 		x := v.Bool()
 		if x {
-			e.WriteString("true")
+			writeString(e, "true")
 		} else {
-			e.WriteString("false")
+			writeString(e, "false")
 		}
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		e.WriteString(strconv.Itoa64(v.Int()))
+		writeString(e, strconv.Itoa64(v.Int()))
 
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-		e.WriteString(strconv.Uitoa64(v.Uint()))
+		writeString(e, strconv.Uitoa64(v.Uint()))
 
 	case reflect.Float32, reflect.Float64:
-		e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
+		writeString(e, strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
 
 	case reflect.String:
-		e.string(v.String())
+		if quoted {
+			sb, err := Marshal(v.String())
+			if err != nil {
+				e.error(err)
+			}
+			e.string(string(sb))
+		} else {
+			e.string(v.String())
+		}
 
 	case reflect.Struct:
 		e.WriteByte('{')
@@ -269,17 +293,14 @@ func (e *encodeState) reflectValue(v reflect.Value) {
 			if f.PkgPath != "" {
 				continue
 			}
-			tag, omitEmpty := f.Name, false
+			tag, omitEmpty, quoted := f.Name, false, false
 			if tv := f.Tag.Get("json"); tv != "" {
-				ss := strings.SplitN(tv, ",", 2)
-				if isValidTag(ss[0]) {
-					tag = ss[0]
-				}
-				if len(ss) > 1 {
-					// Currently the only option is omitempty,
-					// so parsing is trivial.
-					omitEmpty = ss[1] == "omitempty"
+				name, opts := parseTag(tv)
+				if isValidTag(name) {
+					tag = name
 				}
+				omitEmpty = opts.Contains("omitempty")
+				quoted = opts.Contains("string")
 			}
 			fieldValue := v.Field(i)
 			if omitEmpty && isEmptyValue(fieldValue) {
@@ -292,7 +313,7 @@ func (e *encodeState) reflectValue(v reflect.Value) {
 			}
 			e.string(tag)
 			e.WriteByte(':')
-			e.reflectValue(fieldValue)
+			e.reflectValueQuoted(fieldValue, quoted)
 		}
 		e.WriteByte('}')
 
@@ -380,7 +401,8 @@ func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
 func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
 func (sv stringValues) get(i int) string   { return sv[i].String() }
 
-func (e *encodeState) string(s string) {
+func (e *encodeState) string(s string) (int, os.Error) {
+	len0 := e.Len()
 	e.WriteByte('"')
 	start := 0
 	for i := 0; i < len(s); {
@@ -425,4 +447,5 @@ func (e *encodeState) string(s string) {
 		e.WriteString(s[start:])
 	}
 	e.WriteByte('"')
+	return e.Len() - len0, nil
 }
diff --git a/src/pkg/json/encode_test.go b/src/pkg/json/encode_test.go
index 0e4b637..012e9f1 100644
--- a/src/pkg/json/encode_test.go
+++ b/src/pkg/json/encode_test.go
@@ -5,6 +5,8 @@
 package json
 
 import (
+	"bytes"
+	"reflect"
 	"testing"
 )
 
@@ -42,3 +44,39 @@ func TestOmitEmpty(t *testing.T) {
 		t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
 	}
 }
+
+type StringTag struct {
+	BoolStr bool   `json:",string"`
+	IntStr  int64  `json:",string"`
+	StrStr  string `json:",string"`
+}
+
+var stringTagExpected = `{
+ "BoolStr": "true",
+ "IntStr": "42",
+ "StrStr": "\"xzbit\""
+}`
+
+func TestStringTag(t *testing.T) {
+	var s StringTag
+	s.BoolStr = true
+	s.IntStr = 42
+	s.StrStr = "xzbit"
+	got, err := MarshalIndent(&s, "", " ")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if got := string(got); got != stringTagExpected {
+		t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected)
+	}
+
+	// Verify that it round-trips.
+	var s2 StringTag
+	err = NewDecoder(bytes.NewBuffer(got)).Decode(&s2)
+	if err != nil {
+		t.Fatalf("Decode: %v", err)
+	}
+	if !reflect.DeepEqual(s, s2) {
+		t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2)
+	}
+}
diff --git a/src/pkg/json/indent.go b/src/pkg/json/indent.go
index 000da42..2a75303 100644
--- a/src/pkg/json/indent.go
+++ b/src/pkg/json/indent.go
@@ -59,6 +59,7 @@ func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) os.Error {
 	needIndent := false
 	depth := 0
 	for _, c := range src {
+		scan.bytes++
 		v := scan.step(&scan, int(c))
 		if v == scanSkipSpace {
 			continue
diff --git a/src/pkg/json/scanner_test.go b/src/pkg/json/scanner_test.go
index 023e7c8..67d4a28 100644
--- a/src/pkg/json/scanner_test.go
+++ b/src/pkg/json/scanner_test.go
@@ -7,7 +7,9 @@ package json
 import (
 	"bytes"
 	"math"
+	"os"
 	"rand"
+	"reflect"
 	"testing"
 )
 
@@ -136,6 +138,29 @@ func TestIndentBig(t *testing.T) {
 	}
 }
 
+type indentErrorTest struct {
+	in  string
+	err os.Error
+}
+
+var indentErrorTests = []indentErrorTest{
+	{`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
+	{`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
+}
+
+func TestIdentErrors(t *testing.T) {
+	for i, tt := range indentErrorTests {
+		slice := make([]uint8, 0)
+		buf := bytes.NewBuffer(slice)
+		if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
+			if !reflect.DeepEqual(err, tt.err) {
+				t.Errorf("#%d: Indent: %#v", i, err)
+				continue
+			}
+		}
+	}
+}
+
 func TestNextValueBig(t *testing.T) {
 	initBig()
 	var scan scanner
diff --git a/src/pkg/json/tags.go b/src/pkg/json/tags.go
new file mode 100644
index 0000000..58cda20
--- /dev/null
+++ b/src/pkg/json/tags.go
@@ -0,0 +1,44 @@
+// 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 json
+
+import (
+	"strings"
+)
+
+// tagOptions is the string following a comma in a struct field's "json"
+// tag, or the empty string. It does not include the leading comma.
+type tagOptions string
+
+// parseTag splits a struct field's json tag into its name and
+// comma-separated options.
+func parseTag(tag string) (string, tagOptions) {
+	if idx := strings.Index(tag, ","); idx != -1 {
+		return tag[:idx], tagOptions(tag[idx+1:])
+	}
+	return tag, tagOptions("")
+}
+
+// Contains returns whether checks that a comma-separated list of options
+// contains a particular substr flag. substr must be surrounded by a
+// string boundary or commas.
+func (o tagOptions) Contains(optionName string) bool {
+	if len(o) == 0 {
+		return false
+	}
+	s := string(o)
+	for s != "" {
+		var next string
+		i := strings.Index(s, ",")
+		if i >= 0 {
+			s, next = s[:i], s[i+1:]
+		}
+		if s == optionName {
+			return true
+		}
+		s = next
+	}
+	return false
+}
diff --git a/src/pkg/json/tags_test.go b/src/pkg/json/tags_test.go
new file mode 100644
index 0000000..91fb188
--- /dev/null
+++ b/src/pkg/json/tags_test.go
@@ -0,0 +1,28 @@
+// 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 json
+
+import (
+	"testing"
+)
+
+func TestTagParsing(t *testing.T) {
+	name, opts := parseTag("field,foobar,foo")
+	if name != "field" {
+		t.Fatalf("name = %q, want field", name)
+	}
+	for _, tt := range []struct {
+		opt  string
+		want bool
+	}{
+		{"foobar", true},
+		{"foo", true},
+		{"bar", false},
+	} {
+		if opts.Contains(tt.opt) != tt.want {
+			t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
+		}
+	}
+}
diff --git a/src/pkg/math/all_test.go b/src/pkg/math/all_test.go
index d2a7d41..1fe4513 100644
--- a/src/pkg/math/all_test.go
+++ b/src/pkg/math/all_test.go
@@ -1359,6 +1359,20 @@ var powSC = []float64{
 	NaN(),           // pow(NaN, NaN)
 }
 
+var vfpow10SC = []int{
+	MinInt32,
+	MaxInt32,
+	-325,
+	309,
+}
+
+var pow10SC = []float64{
+	0,      // pow10(MinInt32)
+	Inf(1), // pow10(MaxInt32)
+	0,      // pow10(-325)
+	Inf(1), // pow10(309)
+}
+
 var vfsignbitSC = []float64{
 	Inf(-1),
 	Copysign(0, -1),
@@ -2143,6 +2157,14 @@ func TestPow(t *testing.T) {
 	}
 }
 
+func TestPow10(t *testing.T) {
+	for i := 0; i < len(vfpow10SC); i++ {
+		if f := Pow10(vfpow10SC[i]); !alike(pow10SC[i], f) {
+			t.Errorf("Pow10(%d) = %g, want %g", vfpow10SC[i], f, pow10SC[i])
+		}
+	}
+}
+
 func TestRemainder(t *testing.T) {
 	for i := 0; i < len(vf); i++ {
 		if f := Remainder(10, vf[i]); remainder[i] != f {
@@ -2659,6 +2681,18 @@ func BenchmarkPowFrac(b *testing.B) {
 	}
 }
 
+func BenchmarkPow10Pos(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		Pow10(300)
+	}
+}
+
+func BenchmarkPow10Neg(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		Pow10(-300)
+	}
+}
+
 func BenchmarkRemainder(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		Remainder(10, 3)
diff --git a/src/pkg/math/atan2_decl.go b/src/pkg/math/atan2_decl.go
old mode 100755
new mode 100644
diff --git a/src/pkg/math/pow10.go b/src/pkg/math/pow10.go
index bda2e82..20f91bc 100644
--- a/src/pkg/math/pow10.go
+++ b/src/pkg/math/pow10.go
@@ -10,6 +10,12 @@ var pow10tab [70]float64
 
 // Pow10 returns 10**e, the base-10 exponential of e.
 func Pow10(e int) float64 {
+	if e <= -325 {
+		return 0
+	} else if e > 309 {
+		return Inf(1)
+	}
+
 	if e < 0 {
 		return 1 / Pow10(-e)
 	}
diff --git a/src/pkg/mime/grammar.go b/src/pkg/mime/grammar.go
index 6e319ff..70a94cd 100644
--- a/src/pkg/mime/grammar.go
+++ b/src/pkg/mime/grammar.go
@@ -22,6 +22,15 @@ func IsTokenChar(rune int) bool {
 	return rune > 0x20 && rune < 0x7f && !isTSpecial(rune)
 }
 
+// IsToken returns true if s is a 'token' as as defined by RFC 1521
+// and RFC 2045.
+func IsToken(s string) bool {
+	if s == "" {
+		return false
+	}
+	return strings.IndexFunc(s, isNotTokenChar) < 0
+}
+
 // IsQText returns true if rune is in 'qtext' as defined by RFC 822.
 func IsQText(rune int) bool {
 	// CHAR        =  <any ASCII character>        ; (  0-177,  0.-127.)
diff --git a/src/pkg/mime/mediatype.go b/src/pkg/mime/mediatype.go
index 40c735c..b0d3933 100644
--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -12,40 +12,89 @@ import (
 	"unicode"
 )
 
-func validMediaTypeOrDisposition(s string) bool {
+// FormatMediaType serializes type t, subtype sub and the paramaters
+// param as a media type conform RFC 2045 and RFC 2616.
+// The type, subtype, and parameter names are written in lower-case.
+// When any of the arguments result in a standard violation then
+// FormatMediaType returns the empty string.
+func FormatMediaType(t, sub string, param map[string]string) string {
+	if !(IsToken(t) && IsToken(sub)) {
+		return ""
+	}
+	var b bytes.Buffer
+	b.WriteString(strings.ToLower(t))
+	b.WriteByte('/')
+	b.WriteString(strings.ToLower(sub))
+
+	for attribute, value := range param {
+		b.WriteByte(';')
+		b.WriteByte(' ')
+		if !IsToken(attribute) {
+			return ""
+		}
+		b.WriteString(strings.ToLower(attribute))
+		b.WriteByte('=')
+		if IsToken(value) {
+			b.WriteString(value)
+			continue
+		}
+
+		b.WriteByte('"')
+		offset := 0
+		for index, character := range value {
+			if character == '"' || character == '\r' {
+				b.WriteString(value[offset:index])
+				offset = index
+				b.WriteByte('\\')
+			}
+			if character&0x80 != 0 {
+				return ""
+			}
+		}
+		b.WriteString(value[offset:])
+		b.WriteByte('"')
+	}
+	return b.String()
+}
+
+func checkMediaTypeDisposition(s string) os.Error {
 	typ, rest := consumeToken(s)
 	if typ == "" {
-		return false
+		return os.NewError("mime: no media type")
 	}
 	if rest == "" {
-		return true
+		return nil
 	}
 	if !strings.HasPrefix(rest, "/") {
-		return false
+		return os.NewError("mime: expected slash after first token")
 	}
 	subtype, rest := consumeToken(rest[1:])
 	if subtype == "" {
-		return false
+		return os.NewError("mime: expected token after slash")
 	}
-	return rest == ""
+	if rest != "" {
+		return os.NewError("mime: unexpected content after media subtype")
+	}
+	return nil
 }
 
 // ParseMediaType parses a media type value and any optional
 // parameters, per RFC 1521.  Media types are the values in
 // Content-Type and Content-Disposition headers (RFC 2183).
 // On success, ParseMediaType returns the media type converted
-// to lowercase and trimmed of white space. The returned params
-// is always a non-nil map. Params maps from the lowercase
+// to lowercase and trimmed of white space and a non-nil map.
+// The returned map, params, maps from the lowercase
 // attribute to the attribute value with its case preserved.
-// On error, it returns an empty string and a nil params.
-func ParseMediaType(v string) (mediatype string, params map[string]string) {
+func ParseMediaType(v string) (mediatype string, params map[string]string, err os.Error) {
 	i := strings.Index(v, ";")
 	if i == -1 {
 		i = len(v)
 	}
 	mediatype = strings.TrimSpace(strings.ToLower(v[0:i]))
-	if !validMediaTypeOrDisposition(mediatype) {
-		return "", nil
+
+	err = checkMediaTypeDisposition(mediatype)
+	if err != nil {
+		return
 	}
 
 	params = make(map[string]string)
@@ -69,7 +118,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
 				return
 			}
 			// Parse error.
-			return "", nil
+			return "", nil, os.NewError("mime: invalid media parameter")
 		}
 
 		pmap := params
@@ -86,7 +135,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
 		}
 		if _, exists := pmap[key]; exists {
 			// Duplicate parameter name is bogus.
-			return "", nil
+			return "", nil, os.NewError("mime: duplicate parameter name")
 		}
 		pmap[key] = value
 		v = rest
diff --git a/src/pkg/mime/mediatype_test.go b/src/pkg/mime/mediatype_test.go
index 93264bd..884573e 100644
--- a/src/pkg/mime/mediatype_test.go
+++ b/src/pkg/mime/mediatype_test.go
@@ -219,7 +219,14 @@ func TestParseMediaType(t *testing.T) {
 			m("firstname", "Брэд", "lastname", "Фицпатрик")},
 	}
 	for _, test := range tests {
-		mt, params := ParseMediaType(test.in)
+		mt, params, err := ParseMediaType(test.in)
+		if err != nil {
+			if test.t != "" {
+				t.Errorf("for input %q, unexpected error: %v", test.in, err)
+				continue
+			}
+			continue
+		}
 		if g, e := mt, test.t; g != e {
 			t.Errorf("for input %q, expected type %q, got %q",
 				test.in, e, g)
@@ -238,11 +245,11 @@ func TestParseMediaType(t *testing.T) {
 }
 
 func TestParseMediaTypeBogus(t *testing.T) {
-	mt, params := ParseMediaType("bogus ;=========")
-	if mt != "" {
-		t.Error("expected empty type")
+	mt, params, err := ParseMediaType("bogus ;=========")
+	if err == nil {
+		t.Fatalf("expected an error parsing invalid media type; got type %q, params %#v", mt, params)
 	}
-	if params != nil {
-		t.Error("expected nil params")
+	if err.String() != "mime: invalid media parameter" {
+		t.Errorf("expected invalid media parameter; got error %q", err)
 	}
 }
diff --git a/src/pkg/mime/mime_test.go b/src/pkg/mime/mime_test.go
deleted file mode 100644
index 17e6104..0000000
--- a/src/pkg/mime/mime_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.
-
-// Tests for type.go
-
-package mime
-
-import "testing"
-
-var typeTests = map[string]string{
-	".t1":  "application/test",
-	".t2":  "text/test; charset=utf-8",
-	".png": "image/png",
-}
-
-func TestType(t *testing.T) {
-	typeFiles = []string{"test.types"}
-
-	for ext, want := range typeTests {
-		val := TypeByExtension(ext)
-		if val != want {
-			t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
-		}
-
-	}
-}
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index 2533bd3..f2b5072 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -69,8 +69,9 @@ func (p *Part) FileName() string {
 
 func (p *Part) parseContentDisposition() {
 	v := p.Header.Get("Content-Disposition")
-	p.disposition, p.dispositionParams = mime.ParseMediaType(v)
-	if p.dispositionParams == nil {
+	var err os.Error
+	p.disposition, p.dispositionParams, err = mime.ParseMediaType(v)
+	if err != nil {
 		p.dispositionParams = emptyParams
 	}
 }
diff --git a/src/pkg/mime/type.go b/src/pkg/mime/type.go
index 8ecfe9a..39bf40e 100644
--- a/src/pkg/mime/type.go
+++ b/src/pkg/mime/type.go
@@ -7,6 +7,7 @@ package mime
 
 import (
 	"bufio"
+	"fmt"
 	"os"
 	"strings"
 	"sync"
@@ -49,15 +50,12 @@ func loadMimeFile(filename string) {
 		if len(fields) <= 1 || fields[0][0] == '#' {
 			continue
 		}
-		typename := fields[0]
-		if strings.HasPrefix(typename, "text/") {
-			typename += "; charset=utf-8"
-		}
+		mimeType := fields[0]
 		for _, ext := range fields[1:] {
 			if ext[0] == '#' {
 				break
 			}
-			mimeTypes["."+ext] = typename
+			setExtensionType("."+ext, mimeType)
 		}
 	}
 }
@@ -81,6 +79,8 @@ var once sync.Once
 //   /etc/mime.types
 //   /etc/apache2/mime.types
 //   /etc/apache/mime.types
+//
+// Text types have the charset parameter set to "utf-8" by default.
 func TypeByExtension(ext string) string {
 	once.Do(initMime)
 	mimeLock.RLock()
@@ -93,12 +93,31 @@ func TypeByExtension(ext string) string {
 // the extension ext to typ.  The extension should begin with
 // a leading dot, as in ".html".
 func AddExtensionType(ext, typ string) os.Error {
+	if ext == "" || ext[0] != '.' {
+		return fmt.Errorf(`mime: extension "%s" misses dot`, ext)
+	}
 	once.Do(initMime)
-	if len(ext) < 1 || ext[0] != '.' {
-		return os.EINVAL
+	return setExtensionType(ext, typ)
+}
+
+func setExtensionType(extension, mimeType string) os.Error {
+	full, param, err := ParseMediaType(mimeType)
+	if err != nil {
+		return err
+	}
+	if split := strings.Index(full, "/"); split < 0 {
+		return fmt.Errorf(`mime: malformed MIME type "%s"`, mimeType)
+	} else {
+		main := full[:split]
+		sub := full[split+1:]
+		if main == "text" && param["charset"] == "" {
+			param["charset"] = "utf-8"
+		}
+		mimeType = FormatMediaType(main, sub, param)
 	}
+
 	mimeLock.Lock()
-	mimeTypes[ext] = typ
+	mimeTypes[extension] = mimeType
 	mimeLock.Unlock()
 	return nil
 }
diff --git a/src/pkg/mime/type_test.go b/src/pkg/mime/type_test.go
new file mode 100644
index 0000000..976f853
--- /dev/null
+++ b/src/pkg/mime/type_test.go
@@ -0,0 +1,35 @@
+// 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 mime
+
+import "testing"
+
+var typeTests = map[string]string{
+	".t1":  "application/test",
+	".t2":  "text/test; charset=utf-8",
+	".png": "image/png",
+}
+
+func TestTypeByExtension(t *testing.T) {
+	typeFiles = []string{"test.types"}
+
+	for ext, want := range typeTests {
+		val := TypeByExtension(ext)
+		if val != want {
+			t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
+		}
+
+	}
+}
+
+func TestCustomExtension(t *testing.T) {
+	custom := "text/xml; charset=iso-8859-1"
+	if error := AddExtensionType(".xml", custom); error != nil {
+		t.Fatalf("error %s for AddExtension(%s)", error, custom)
+	}
+	if registered := TypeByExtension(".xml"); registered != custom {
+		t.Fatalf("registered %s instead of %s", registered, custom)
+	}
+}
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index f407b17..cb46455 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -113,7 +113,7 @@ func convertRR_A(records []dnsRR) []IP {
 func convertRR_AAAA(records []dnsRR) []IP {
 	addrs := make([]IP, len(records))
 	for i, rr := range records {
-		a := make(IP, 16)
+		a := make(IP, IPv6len)
 		copy(a, rr.(*dnsRR_AAAA).AAAA[:])
 		addrs[i] = a
 	}
diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go
index 8a14cb2..2696b7f 100644
--- a/src/pkg/net/interface.go
+++ b/src/pkg/net/interface.go
@@ -26,6 +26,63 @@ func (a HardwareAddr) String() string {
 	return buf.String()
 }
 
+// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
+// following formats:
+//   01:23:45:67:89:ab
+//   01:23:45:67:89:ab:cd:ef
+//   01-23-45-67-89-ab
+//   01-23-45-67-89-ab-cd-ef
+//   0123.4567.89ab
+//   0123.4567.89ab.cdef
+func ParseMAC(s string) (hw HardwareAddr, err os.Error) {
+	if len(s) < 14 {
+		goto error
+	}
+
+	if s[2] == ':' || s[2] == '-' {
+		if (len(s)+1)%3 != 0 {
+			goto error
+		}
+		n := (len(s) + 1) / 3
+		if n != 6 && n != 8 {
+			goto error
+		}
+		hw = make(HardwareAddr, n)
+		for x, i := 0, 0; i < n; i++ {
+			var ok bool
+			if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
+				goto error
+			}
+			x += 3
+		}
+	} else if s[4] == '.' {
+		if (len(s)+1)%5 != 0 {
+			goto error
+		}
+		n := 2 * (len(s) + 1) / 5
+		if n != 6 && n != 8 {
+			goto error
+		}
+		hw = make(HardwareAddr, n)
+		for x, i := 0, 0; i < n; i += 2 {
+			var ok bool
+			if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
+				goto error
+			}
+			if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
+				goto error
+			}
+			x += 5
+		}
+	} else {
+		goto error
+	}
+	return hw, nil
+
+error:
+	return nil, os.NewError("invalid MAC address: " + s)
+}
+
 // Interface represents a mapping between network interface name
 // and index.  It also represents network interface facility
 // information.
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
index 0e4089a..c918f24 100644
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -6,6 +6,9 @@ package net
 
 import (
 	"bytes"
+	"os"
+	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -71,3 +74,46 @@ func TestInterfaceAddrs(t *testing.T) {
 		t.Logf("interface address %q\n", ifa.String())
 	}
 }
+
+var mactests = []struct {
+	in  string
+	out HardwareAddr
+	err string
+}{
+	{"01:23:45:67:89:AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+	{"01-23-45-67-89-AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+	{"0123.4567.89AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+	{"ab:cd:ef:AB:CD:EF", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, ""},
+	{"01.02.03.04.05.06", nil, "invalid MAC address"},
+	{"01:02:03:04:05:06:", nil, "invalid MAC address"},
+	{"x1:02:03:04:05:06", nil, "invalid MAC address"},
+	{"01002:03:04:05:06", nil, "invalid MAC address"},
+	{"01:02003:04:05:06", nil, "invalid MAC address"},
+	{"01:02:03004:05:06", nil, "invalid MAC address"},
+	{"01:02:03:04005:06", nil, "invalid MAC address"},
+	{"01:02:03:04:05006", nil, "invalid MAC address"},
+	{"01-02:03:04:05:06", nil, "invalid MAC address"},
+	{"01:02-03-04-05-06", nil, "invalid MAC address"},
+	{"0123:4567:89AF", nil, "invalid MAC address"},
+	{"0123-4567-89AF", nil, "invalid MAC address"},
+	{"01:23:45:67:89:AB:CD:EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+	{"01-23-45-67-89-AB-CD-EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+	{"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+}
+
+func match(err os.Error, s string) bool {
+	if s == "" {
+		return err == nil
+	}
+	return err != nil && strings.Contains(err.String(), s)
+}
+
+func TestParseMAC(t *testing.T) {
+	for _, tt := range mactests {
+		out, err := ParseMAC(tt.in)
+		if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
+			t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
+				tt.err)
+		}
+	}
+}
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
index b0e2c42..61dc3be 100644
--- a/src/pkg/net/ip.go
+++ b/src/pkg/net/ip.go
@@ -21,11 +21,8 @@ const (
 )
 
 // An IP is a single IP address, an array of bytes.
-// Functions in this package accept either 4-byte (IP v4)
-// or 16-byte (IP v6) arrays as input.  Unless otherwise
-// specified, functions in this package always return
-// IP addresses in 16-byte form using the canonical
-// embedding.
+// Functions in this package accept either 4-byte (IPv4)
+// or 16-byte (IPv6) arrays as input.
 //
 // Note that in this documentation, referring to an
 // IP address as an IPv4 address or an IPv6 address
@@ -37,6 +34,12 @@ type IP []byte
 // An IP mask is an IP address.
 type IPMask []byte
 
+// An IPNet represents an IP network.
+type IPNet struct {
+	IP   IP     // network number
+	Mask IPMask // network mask
+}
+
 // IPv4 returns the IP address (in 16-byte form) of the
 // IPv4 address a.b.c.d.
 func IPv4(a, b, c, d byte) IP {
@@ -51,20 +54,42 @@ func IPv4(a, b, c, d byte) IP {
 
 var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
 
-// IPv4Mask returns the IP mask (in 16-byte form) of the
+// IPv4Mask returns the IP mask (in 4-byte form) of the
 // IPv4 mask a.b.c.d.
 func IPv4Mask(a, b, c, d byte) IPMask {
-	p := make(IPMask, IPv6len)
-	for i := 0; i < 12; i++ {
-		p[i] = 0xff
-	}
-	p[12] = a
-	p[13] = b
-	p[14] = c
-	p[15] = d
+	p := make(IPMask, IPv4len)
+	p[0] = a
+	p[1] = b
+	p[2] = c
+	p[3] = d
 	return p
 }
 
+// CIDRMask returns an IPMask consisting of `ones' 1 bits
+// followed by 0s up to a total length of `bits' bits.
+// For a mask of this form, CIDRMask is the inverse of IPMask.Size.
+func CIDRMask(ones, bits int) IPMask {
+	if bits != 8*IPv4len && bits != 8*IPv6len {
+		return nil
+	}
+	if ones < 0 || ones > bits {
+		return nil
+	}
+	l := bits / 8
+	m := make(IPMask, l)
+	n := uint(ones)
+	for i := 0; i < l; i++ {
+		if n >= 8 {
+			m[i] = 0xff
+			n -= 8
+			continue
+		}
+		m[i] = ^byte(0xff >> n)
+		n = 0
+	}
+	return m
+}
+
 // Well-known IPv4 addresses
 var (
 	IPv4bcast     = IPv4(255, 255, 255, 255) // broadcast
@@ -213,13 +238,13 @@ func allFF(b []byte) bool {
 
 // Mask returns the result of masking the IP address ip with mask.
 func (ip IP) Mask(mask IPMask) IP {
-	n := len(ip)
-	if len(mask) == 16 && len(ip) == 4 && allFF(mask[:12]) {
+	if len(mask) == IPv6len && len(ip) == IPv4len && allFF(mask[:12]) {
 		mask = mask[12:]
 	}
-	if len(mask) == 4 && len(ip) == 16 && bytesEqual(ip[:12], v4InV6Prefix) {
+	if len(mask) == IPv4len && len(ip) == IPv6len && bytesEqual(ip[:12], v4InV6Prefix) {
 		ip = ip[12:]
 	}
+	n := len(ip)
 	if n != len(mask) {
 		return nil
 	}
@@ -230,40 +255,6 @@ func (ip IP) Mask(mask IPMask) IP {
 	return out
 }
 
-// Convert i to decimal string.
-func itod(i uint) string {
-	if i == 0 {
-		return "0"
-	}
-
-	// Assemble decimal in reverse order.
-	var b [32]byte
-	bp := len(b)
-	for ; i > 0; i /= 10 {
-		bp--
-		b[bp] = byte(i%10) + '0'
-	}
-
-	return string(b[bp:])
-}
-
-// Convert i to hexadecimal string.
-func itox(i uint) string {
-	if i == 0 {
-		return "0"
-	}
-
-	// Assemble hexadecimal in reverse order.
-	var b [32]byte
-	bp := len(b)
-	for ; i > 0; i /= 16 {
-		bp--
-		b[bp] = "0123456789abcdef"[byte(i%16)]
-	}
-
-	return string(b[bp:])
-}
-
 // String returns the string form of the IP address ip.
 // If the address is an IPv4 address, the string representation
 // is dotted decimal ("74.125.19.99").  Otherwise the representation
@@ -272,11 +263,11 @@ func (ip IP) String() string {
 	p := ip
 
 	if len(ip) == 0 {
-		return ""
+		return "<nil>"
 	}
 
 	// If IPv4, use dotted notation.
-	if p4 := p.To4(); len(p4) == 4 {
+	if p4 := p.To4(); len(p4) == IPv4len {
 		return itod(uint(p4[0])) + "." +
 			itod(uint(p4[1])) + "." +
 			itod(uint(p4[2])) + "." +
@@ -289,9 +280,9 @@ func (ip IP) String() string {
 	// Find longest run of zeros.
 	e0 := -1
 	e1 := -1
-	for i := 0; i < 16; i += 2 {
+	for i := 0; i < IPv6len; i += 2 {
 		j := i
-		for j < 16 && p[j] == 0 && p[j+1] == 0 {
+		for j < IPv6len && p[j] == 0 && p[j+1] == 0 {
 			j += 2
 		}
 		if j > i && j-i > e1-e0 {
@@ -307,17 +298,17 @@ func (ip IP) String() string {
 
 	// Print with possible :: in place of run of zeros
 	var s string
-	for i := 0; i < 16; i += 2 {
+	for i := 0; i < IPv6len; i += 2 {
 		if i == e0 {
 			s += "::"
 			i = e1
-			if i >= 16 {
+			if i >= IPv6len {
 				break
 			}
 		} else if i > 0 {
 			s += ":"
 		}
-		s += itox((uint(p[i]) << 8) | uint(p[i+1]))
+		s += itox((uint(p[i])<<8)|uint(p[i+1]), 1)
 	}
 	return s
 }
@@ -329,10 +320,10 @@ func (ip IP) Equal(x IP) bool {
 	if len(ip) == len(x) {
 		return bytesEqual(ip, x)
 	}
-	if len(ip) == 4 && len(x) == 16 {
+	if len(ip) == IPv4len && len(x) == IPv6len {
 		return bytesEqual(x[0:12], v4InV6Prefix) && bytesEqual(ip, x[12:])
 	}
-	if len(ip) == 16 && len(x) == 4 {
+	if len(ip) == IPv6len && len(x) == IPv4len {
 		return bytesEqual(ip[0:12], v4InV6Prefix) && bytesEqual(ip[12:], x)
 	}
 	return false
@@ -379,25 +370,86 @@ func simpleMaskLength(mask IPMask) int {
 	return n
 }
 
-// String returns the string representation of mask.
-// If the mask is in the canonical form--ones followed by zeros--the
-// string representation is just the decimal number of ones.
-// If the mask is in a non-canonical form, it is formatted
-// as an IP address.
-func (mask IPMask) String() string {
-	switch len(mask) {
-	case 4:
-		n := simpleMaskLength(mask)
-		if n >= 0 {
-			return itod(uint(n + (IPv6len-IPv4len)*8))
+// Size returns the number of leading ones and total bits in the mask.
+// If the mask is not in the canonical form--ones followed by zeros--then
+// Size returns 0, 0.
+func (m IPMask) Size() (ones, bits int) {
+	ones, bits = simpleMaskLength(m), len(m)*8
+	if ones == -1 {
+		return 0, 0
+	}
+	return
+}
+
+// String returns the hexadecimal form of m, with no punctuation.
+func (m IPMask) String() string {
+	s := ""
+	for _, b := range m {
+		s += itox(uint(b), 2)
+	}
+	if len(s) == 0 {
+		return "<nil>"
+	}
+	return s
+}
+
+func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
+	if ip = n.IP.To4(); ip == nil {
+		ip = n.IP
+		if len(ip) != IPv6len {
+			return nil, nil
 		}
-	case 16:
-		n := simpleMaskLength(mask)
-		if n >= 12*8 {
-			return itod(uint(n - 12*8))
+	}
+	m = n.Mask
+	switch len(m) {
+	case IPv4len:
+		if len(ip) != IPv4len {
+			return nil, nil
+		}
+	case IPv6len:
+		if len(ip) == IPv4len {
+			m = m[12:]
+		}
+	default:
+		return nil, nil
+	}
+	return
+}
+
+// Contains reports whether the network includes ip.
+func (n *IPNet) Contains(ip IP) bool {
+	nn, m := networkNumberAndMask(n)
+	if x := ip.To4(); x != nil {
+		ip = x
+	}
+	l := len(ip)
+	if l != len(nn) {
+		return false
+	}
+	for i := 0; i < l; i++ {
+		if nn[i]&m[i] != ip[i]&m[i] {
+			return false
 		}
 	}
-	return IP(mask).String()
+	return true
+}
+
+// String returns the CIDR notation of n like "192.168.100.1/24"
+// or "2001:DB8::/48" as defined in RFC 4632 and RFC 4291.
+// If the mask is not in the canonical form, it returns the
+// string which consists of an IP address, followed by a slash
+// character and a mask expressed as hexadecimal form with no
+// punctuation like "192.168.100.1/c000ff00".
+func (n *IPNet) String() string {
+	nn, m := networkNumberAndMask(n)
+	if nn == nil || m == nil {
+		return "<nil>"
+	}
+	l := simpleMaskLength(m)
+	if l == -1 {
+		return nn.String() + "/" + m.String()
+	}
+	return nn.String() + "/" + itod(uint(l))
 }
 
 // Parse IPv4 address (d.d.d.d).
@@ -440,7 +492,7 @@ func parseIPv4(s string) IP {
 //	* The last 32 bits can be in IPv4 form.
 // Thus, ::ffff:1.2.3.4 is the IPv4 address 1.2.3.4.
 func parseIPv6(s string) IP {
-	p := make(IP, 16)
+	p := make(IP, IPv6len)
 	ellipsis := -1 // position of ellipsis in p
 	i := 0         // index in string s
 
@@ -482,7 +534,7 @@ func parseIPv6(s string) IP {
 			p[j+2] = p4[14]
 			p[j+3] = p4[15]
 			i = len(s)
-			j += 4
+			j += IPv4len
 			break
 		}
 
@@ -569,46 +621,28 @@ func ParseIP(s string) IP {
 }
 
 // ParseCIDR parses s as a CIDR notation IP address and mask,
-// like "192.168.100.1/24", "2001:DB8::/48", as defined in
+// like "192.168.100.1/24" or "2001:DB8::/48", as defined in
 // RFC 4632 and RFC 4291.
-func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
+//
+// It returns the IP address and the network implied by the IP
+// and mask.  For example, ParseCIDR("192.168.100.1/16") returns
+// the IP address 192.168.100.1 and the network 192.168.0.0/16.
+func ParseCIDR(s string) (IP, *IPNet, os.Error) {
 	i := byteIndex(s, '/')
 	if i < 0 {
 		return nil, nil, &ParseError{"CIDR address", s}
 	}
 	ipstr, maskstr := s[:i], s[i+1:]
-	iplen := 4
-	ip = parseIPv4(ipstr)
+	iplen := IPv4len
+	ip := parseIPv4(ipstr)
 	if ip == nil {
-		iplen = 16
+		iplen = IPv6len
 		ip = parseIPv6(ipstr)
 	}
-	nn, i, ok := dtoi(maskstr, 0)
-	if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
+	n, i, ok := dtoi(maskstr, 0)
+	if ip == nil || !ok || i != len(maskstr) || n < 0 || n > 8*iplen {
 		return nil, nil, &ParseError{"CIDR address", s}
 	}
-	n := uint(nn)
-	if iplen == 4 {
-		v4mask := ^uint32(0xffffffff >> n)
-		mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
-	} else {
-		mask = make(IPMask, 16)
-		for i := 0; i < 16; i++ {
-			if n >= 8 {
-				mask[i] = 0xff
-				n -= 8
-				continue
-			}
-			mask[i] = ^byte(0xff >> n)
-			n = 0
-
-		}
-	}
-	// address must not have any bits not in mask
-	for i := range ip {
-		if ip[i]&^mask[i] != 0 {
-			return nil, nil, &ParseError{"CIDR address", s}
-		}
-	}
-	return ip, mask, nil
+	m := CIDRMask(n, 8*iplen)
+	return ip, &IPNet{ip.Mask(m), m}, nil
 }
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
index b189b10..07e627a 100644
--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -34,12 +34,13 @@ var parseiptests = []struct {
 	{"::ffff:127.0.0.1", IPv4(127, 0, 0, 1)},
 	{"2001:4860:0:2001::68", IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}},
 	{"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)},
+	{"", nil},
 }
 
 func TestParseIP(t *testing.T) {
 	for _, tt := range parseiptests {
 		if out := ParseIP(tt.in); !isEqual(out, tt.out) {
-			t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
+			t.Errorf("ParseIP(%q) = %v, want %v", tt.in, out, tt.out)
 		}
 	}
 }
@@ -49,60 +50,213 @@ var ipstringtests = []struct {
 	out string
 }{
 	// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
-		"2001:db8::123:12:1"},
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1},
-		"2001:db8::1"},
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1},
-		"2001:db8:0:1:0:1:0:1"},
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0},
-		"2001:db8:1:0:1:0:1:0"},
-	{IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
-		"2001::1:0:0:1"},
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0},
-		"2001:db8:0:0:1::"},
-	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
-		"2001:db8::1:0:0:1"},
-	{IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD},
-		"2001:db8::a:b:c:d"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, "2001:db8::123:12:1"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, "2001:db8:0:1:0:1:0:1"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, "2001:db8:1:0:1:0:1:0"},
+	{IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001::1:0:0:1"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"},
+	{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"},
+	{IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"},
+	{nil, "<nil>"},
 }
 
 func TestIPString(t *testing.T) {
 	for _, tt := range ipstringtests {
 		if out := tt.in.String(); out != tt.out {
-			t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
+			t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out)
 		}
 	}
 }
 
-var parsecidrtests = []struct {
-	in   string
-	ip   IP
+var ipmasktests = []struct {
+	in   IP
 	mask IPMask
-	err  os.Error
+	out  IP
+}{
+	{IPv4(192, 168, 1, 127), IPv4Mask(255, 255, 255, 128), IPv4(192, 168, 1, 0)},
+	{IPv4(192, 168, 1, 127), IPMask(ParseIP("255.255.255.192")), IPv4(192, 168, 1, 64)},
+	{IPv4(192, 168, 1, 127), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0")), IPv4(192, 168, 1, 96)},
+	{IPv4(192, 168, 1, 127), IPv4Mask(255, 0, 255, 0), IPv4(192, 0, 1, 0)},
+	{ParseIP("2001:db8::1"), IPMask(ParseIP("ffff:ff80::")), ParseIP("2001:d80::")},
+	{ParseIP("2001:db8::1"), IPMask(ParseIP("f0f0:0f0f::")), ParseIP("2000:d08::")},
+}
+
+func TestIPMask(t *testing.T) {
+	for _, tt := range ipmasktests {
+		if out := tt.in.Mask(tt.mask); out == nil || !tt.out.Equal(out) {
+			t.Errorf("IP(%v).Mask(%v) = %v, want %v", tt.in, tt.mask, out, tt.out)
+		}
+	}
+}
+
+var ipmaskstringtests = []struct {
+	in  IPMask
+	out string
+}{
+	{IPv4Mask(255, 255, 255, 240), "fffffff0"},
+	{IPv4Mask(255, 0, 128, 0), "ff008000"},
+	{IPMask(ParseIP("ffff:ff80::")), "ffffff80000000000000000000000000"},
+	{IPMask(ParseIP("ef00:ff80::cafe:0")), "ef00ff800000000000000000cafe0000"},
+	{nil, "<nil>"},
+}
+
+func TestIPMaskString(t *testing.T) {
+	for _, tt := range ipmaskstringtests {
+		if out := tt.in.String(); out != tt.out {
+			t.Errorf("IPMask.String(%v) = %q, want %q", tt.in, out, tt.out)
+		}
+	}
+}
+
+var parsecidrtests = []struct {
+	in  string
+	ip  IP
+	net *IPNet
+	err os.Error
 }{
-	{"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
-	{"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
-	{"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
-	{"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
-	{"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
-	{"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
-	{"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
-	{"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
-	{"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
-	{"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
-	{"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
-	{"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
-	{"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
-	{"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
-	{"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
-	{"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
+	{"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil},
+	{"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+	{"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+	{"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil},
+	{"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+	{"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
+	{"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
+	{"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
+	{"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
+	{"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
+	{"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
+	{"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil},
+	{"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil},
+	{"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
+	{"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
+	{"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+	{"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+	{"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}},
+	{"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}},
+	{"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}},
+	{"", nil, nil, &ParseError{"CIDR address", ""}},
 }
 
 func TestParseCIDR(t *testing.T) {
 	for _, tt := range parsecidrtests {
-		if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
-			t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
+		ip, net, err := ParseCIDR(tt.in)
+		if !reflect.DeepEqual(err, tt.err) {
+			t.Errorf("ParseCIDR(%q) = %v, %v; want %v, %v", tt.in, ip, net, tt.ip, tt.net)
+		}
+		if err == nil && (!tt.ip.Equal(ip) || !tt.net.IP.Equal(net.IP) || !isEqual(net.Mask, tt.net.Mask)) {
+			t.Errorf("ParseCIDR(%q) = %v, {%v, %v}; want %v {%v, %v}", tt.in, ip, net.IP, net.Mask, tt.ip, tt.net.IP, tt.net.Mask)
+		}
+	}
+}
+
+var ipnetcontainstests = []struct {
+	ip  IP
+	net *IPNet
+	ok  bool
+}{
+	{IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true},
+	{IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false},
+	{IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true},
+	{IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false},
+	{ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true},
+	{ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false},
+	{ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true},
+	{ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false},
+}
+
+func TestIPNetContains(t *testing.T) {
+	for _, tt := range ipnetcontainstests {
+		if ok := tt.net.Contains(tt.ip); ok != tt.ok {
+			t.Errorf("IPNet(%v).Contains(%v) = %v, want %v", tt.net, tt.ip, ok, tt.ok)
+		}
+	}
+}
+
+var ipnetstringtests = []struct {
+	in  *IPNet
+	out string
+}{
+	{&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"},
+	{&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
+	{&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"},
+	{&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
+}
+
+func TestIPNetString(t *testing.T) {
+	for _, tt := range ipnetstringtests {
+		if out := tt.in.String(); out != tt.out {
+			t.Errorf("IPNet.String(%v) = %q, want %q", tt.in, out, tt.out)
+		}
+	}
+}
+
+var cidrmasktests = []struct {
+	ones int
+	bits int
+	out  IPMask
+}{
+	{0, 32, IPv4Mask(0, 0, 0, 0)},
+	{12, 32, IPv4Mask(255, 240, 0, 0)},
+	{24, 32, IPv4Mask(255, 255, 255, 0)},
+	{32, 32, IPv4Mask(255, 255, 255, 255)},
+	{0, 128, IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{4, 128, IPMask{0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{48, 128, IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{128, 128, IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+	{33, 32, nil},
+	{32, 33, nil},
+	{-1, 128, nil},
+	{128, -1, nil},
+}
+
+func TestCIDRMask(t *testing.T) {
+	for _, tt := range cidrmasktests {
+		if out := CIDRMask(tt.ones, tt.bits); !isEqual(out, tt.out) {
+			t.Errorf("CIDRMask(%v, %v) = %v, want %v", tt.ones, tt.bits, out, tt.out)
+		}
+	}
+}
+
+var (
+	v4addr         = IP{192, 168, 0, 1}
+	v4mappedv6addr = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 0, 1}
+	v6addr         = IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}
+	v4mask         = IPMask{255, 255, 255, 0}
+	v4mappedv6mask = IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 255, 255, 255, 0}
+	v6mask         = IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}
+	badaddr        = IP{192, 168, 0}
+	badmask        = IPMask{255, 255, 0}
+	v4maskzero     = IPMask{0, 0, 0, 0}
+)
+
+var networknumberandmasktests = []struct {
+	in  IPNet
+	out IPNet
+}{
+	{IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}},
+	{IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
+	{IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
+	{IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}},
+	{IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}},
+	{IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}},
+	{IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}},
+	{in: IPNet{v6addr, v4mask}},
+	{in: IPNet{v4addr, badmask}},
+	{in: IPNet{v4mappedv6addr, badmask}},
+	{in: IPNet{v6addr, badmask}},
+	{in: IPNet{badaddr, v4mask}},
+	{in: IPNet{badaddr, v4mappedv6mask}},
+	{in: IPNet{badaddr, v6mask}},
+	{in: IPNet{badaddr, badmask}},
+}
+
+func TestNetworkNumberAndMask(t *testing.T) {
+	for _, tt := range networknumberandmasktests {
+		ip, m := networkNumberAndMask(&tt.in)
+		out := &IPNet{ip, m}
+		if !reflect.DeepEqual(&tt.out, out) {
+			t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out)
 		}
 	}
 }
@@ -158,10 +312,10 @@ var ipaftests = []struct {
 func TestIPAddrFamily(t *testing.T) {
 	for _, tt := range ipaftests {
 		if af := tt.in.To4() != nil; af != tt.af4 {
-			t.Errorf("verifying IPv4 address family for %#q = %v, want %v", tt.in, af, tt.af4)
+			t.Errorf("verifying IPv4 address family for %q = %v, want %v", tt.in, af, tt.af4)
 		}
 		if af := len(tt.in) == IPv6len && tt.in.To4() == nil; af != tt.af6 {
-			t.Errorf("verifying IPv6 address family for %#q = %v, want %v", tt.in, af, tt.af6)
+			t.Errorf("verifying IPv6 address family for %q = %v, want %v", tt.in, af, tt.af6)
 		}
 	}
 }
@@ -209,7 +363,7 @@ func name(f interface{}) string {
 func TestIPAddrScope(t *testing.T) {
 	for _, tt := range ipscopetests {
 		if ok := tt.scope(tt.in); ok != tt.ok {
-			t.Errorf("%s(%#q) = %v, want %v", name(tt.scope), tt.in, ok, tt.ok)
+			t.Errorf("%s(%q) = %v, want %v", name(tt.scope), tt.in, ok, tt.ok)
 		}
 	}
 }
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index 4e11518..5cbc588 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -25,7 +25,7 @@ func sockaddrToIP(sa syscall.Sockaddr) Addr {
 }
 
 func (a *IPAddr) family() int {
-	if a == nil || len(a.IP) <= 4 {
+	if a == nil || len(a.IP) <= IPv4len {
 		return syscall.AF_INET
 	}
 	if a.IP.To4() != nil {
@@ -158,7 +158,7 @@ func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) {
 	switch sa := sa.(type) {
 	case *syscall.SockaddrInet4:
 		addr = &IPAddr{sa.Addr[0:]}
-		if len(b) >= 4 { // discard ipv4 header
+		if len(b) >= IPv4len { // discard ipv4 header
 			hsize := (int(b[0]) & 0xf) * 4
 			copy(b, b[hsize:])
 			n -= hsize
diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
index be6dbf2..a66250c 100644
--- a/src/pkg/net/multicast_test.go
+++ b/src/pkg/net/multicast_test.go
@@ -6,13 +6,33 @@ package net
 
 import (
 	"flag"
+	"os"
 	"runtime"
 	"testing"
 )
 
 var multicast = flag.Bool("multicast", false, "enable multicast tests")
 
-func TestMulticastJoinAndLeave(t *testing.T) {
+var joinAndLeaveGroupUDPTests = []struct {
+	net   string
+	laddr IP
+	gaddr IP
+	flags Flags
+	ipv6  bool
+}{
+	// cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
+	{"udp", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
+	{"udp4", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
+	{"udp", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff01::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff02::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff04::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff05::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff08::114"), (FlagUp | FlagLoopback), true},
+	{"udp6", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
+}
+
+func TestJoinAndLeaveGroupUDP(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		return
 	}
@@ -21,53 +41,51 @@ func TestMulticastJoinAndLeave(t *testing.T) {
 		return
 	}
 
-	addr := &UDPAddr{
-		IP:   IPv4zero,
-		Port: 0,
-	}
-	// open a UDPConn
-	conn, err := ListenUDP("udp4", addr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer conn.Close()
-
-	// try to join group
-	mcast := IPv4(224, 0, 0, 254)
-	err = conn.JoinGroup(mcast)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// try to leave group
-	err = conn.LeaveGroup(mcast)
-	if err != nil {
-		t.Fatal(err)
-	}
-}
-
-func TestJoinFailureWithIPv6Address(t *testing.T) {
-	if !*multicast {
-		t.Logf("test disabled; use --multicast to enable")
-		return
-	}
-	addr := &UDPAddr{
-		IP:   IPv4zero,
-		Port: 0,
-	}
-
-	// open a UDPConn
-	conn, err := ListenUDP("udp4", addr)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer conn.Close()
-
-	// try to join group
-	mcast := ParseIP("ff02::1")
-	err = conn.JoinGroup(mcast)
-	if err == nil {
-		t.Fatal("JoinGroup succeeded, should fail")
+	for _, tt := range joinAndLeaveGroupUDPTests {
+		var (
+			ifi   *Interface
+			found bool
+		)
+		if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
+			continue
+		}
+		ift, err := Interfaces()
+		if err != nil {
+			t.Fatalf("Interfaces() failed: %v", err)
+		}
+		for _, x := range ift {
+			if x.Flags&tt.flags == tt.flags {
+				ifi = &x
+				break
+			}
+		}
+		if ifi == nil {
+			t.Logf("an appropriate multicast interface not found")
+			return
+		}
+		c, err := ListenUDP(tt.net, &UDPAddr{IP: tt.laddr})
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer c.Close()
+		if err := c.JoinGroup(ifi, tt.gaddr); err != nil {
+			t.Fatal(err)
+		}
+		ifmat, err := ifi.MulticastAddrs()
+		if err != nil {
+			t.Fatalf("MulticastAddrs() failed: %v", err)
+		}
+		for _, ifma := range ifmat {
+			if ifma.(*IPAddr).IP.Equal(tt.gaddr) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			t.Fatalf("%q not found in RIB", tt.gaddr.String())
+		}
+		if err := c.LeaveGroup(ifi, tt.gaddr); err != nil {
+			t.Fatal(err)
+		}
 	}
-	t.Logf("%s", err)
 }
diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go
index de46830..0d30a7a 100644
--- a/src/pkg/net/parse.go
+++ b/src/pkg/net/parse.go
@@ -159,6 +159,18 @@ func xtoi(s string, i0 int) (n int, i int, ok bool) {
 	return n, i, true
 }
 
+// xtoi2 converts the next two hex digits of s into a byte.
+// If s is longer than 2 bytes then the third byte must be e.
+// If the first two bytes of s are not hex digits or the third byte
+// does not match e, false is returned.
+func xtoi2(s string, e byte) (byte, bool) {
+	if len(s) > 2 && s[2] != e {
+		return 0, false
+	}
+	n, ei, ok := xtoi(s[:2], 0)
+	return byte(n), ok && ei == 2
+}
+
 // Integer to decimal.
 func itoa(i int) string {
 	var buf [30]byte
@@ -181,6 +193,37 @@ func itoa(i int) string {
 	return string(buf[n:])
 }
 
+// Convert i to decimal string.
+func itod(i uint) string {
+	if i == 0 {
+		return "0"
+	}
+
+	// Assemble decimal in reverse order.
+	var b [32]byte
+	bp := len(b)
+	for ; i > 0; i /= 10 {
+		bp--
+		b[bp] = byte(i%10) + '0'
+	}
+
+	return string(b[bp:])
+}
+
+// Convert i to hexadecimal string.
+func itox(i uint, min int) string {
+	// Assemble hexadecimal in reverse order.
+	var b [32]byte
+	bp := len(b)
+	for ; i > 0 || min > 0; i /= 16 {
+		bp--
+		b[bp] = "0123456789abcdef"[byte(i%16)]
+		min--
+	}
+
+	return string(b[bp:])
+}
+
 // Number of occurrences of b in s.
 func count(s string, b byte) int {
 	n := 0
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index 7d7f7fc..a2ff218 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -115,7 +115,9 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
 }
 
 func TestTCPServer(t *testing.T) {
-	doTest(t, "tcp", "", "127.0.0.1")
+	if syscall.OS != "openbsd" {
+		doTest(t, "tcp", "", "127.0.0.1")
+	}
 	doTest(t, "tcp", "0.0.0.0", "127.0.0.1")
 	doTest(t, "tcp", "127.0.0.1", "127.0.0.1")
 	doTest(t, "tcp4", "", "127.0.0.1")
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index 5560301..f2e9197 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -12,6 +12,11 @@ import (
 	"syscall"
 )
 
+// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
+// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
+// will not be routed to an IPv6 socket - two separate sockets are required
+// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
+
 func sockaddrToTCP(sa syscall.Sockaddr) Addr {
 	switch sa := sa.(type) {
 	case *syscall.SockaddrInet4:
@@ -23,7 +28,7 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr {
 }
 
 func (a *TCPAddr) family() int {
-	if a == nil || len(a.IP) <= 4 {
+	if a == nil || len(a.IP) <= IPv4len {
 		return syscall.AF_INET
 	}
 	if a.IP.To4() != nil {
diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go
index bb71960..d5c6ccb 100644
--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -169,17 +169,18 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) {
 	return &UDPConn{*l.plan9Conn()}, nil
 }
 
-// JoinGroup joins the IPv4 multicast group named by addr.
-// The UDPConn must use the "udp4" network.
-func (c *UDPConn) JoinGroup(addr IP) os.Error {
+// JoinGroup joins the IP multicast group named by addr on ifi,
+// which specifies the interface to join.  JoinGroup uses the
+// default multicast interface if ifi is nil.
+func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) os.Error {
 	if !c.ok() {
 		return os.EINVAL
 	}
 	return os.EPLAN9
 }
 
-// LeaveGroup exits the IPv4 multicast group named by addr.
-func (c *UDPConn) LeaveGroup(addr IP) os.Error {
+// LeaveGroup exits the IP multicast group named by addr on ifi.
+func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) os.Error {
 	if !c.ok() {
 		return os.EINVAL
 	}
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index d4ea056..1dc79f7 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -7,6 +7,7 @@
 package net
 
 import (
+	"bytes"
 	"os"
 	"syscall"
 )
@@ -22,7 +23,7 @@ func sockaddrToUDP(sa syscall.Sockaddr) Addr {
 }
 
 func (a *UDPAddr) family() int {
-	if a == nil || len(a.IP) <= 4 {
+	if a == nil || len(a.IP) <= IPv4len {
 		return syscall.AF_INET
 	}
 	if a.IP.To4() != nil {
@@ -252,43 +253,94 @@ func (c *UDPConn) BindToDevice(device string) os.Error {
 // Closing c does not affect f, and closing f does not affect c.
 func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
 
-var errInvalidMulticast = os.NewError("invalid IPv4 multicast address")
+// JoinGroup joins the IP multicast group named by addr on ifi,
+// which specifies the interface to join.  JoinGroup uses the
+// default multicast interface if ifi is nil.
+func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) os.Error {
+	if !c.ok() {
+		return os.EINVAL
+	}
+	ip := addr.To4()
+	if ip != nil {
+		return joinIPv4GroupUDP(c, ifi, ip)
+	}
+	return joinIPv6GroupUDP(c, ifi, addr)
+}
 
-// JoinGroup joins the IPv4 multicast group named by addr.
-// The UDPConn must use the "udp4" network.
-func (c *UDPConn) JoinGroup(addr IP) os.Error {
+// LeaveGroup exits the IP multicast group named by addr on ifi.
+func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) os.Error {
 	if !c.ok() {
 		return os.EINVAL
 	}
 	ip := addr.To4()
-	if ip == nil {
-		return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+	if ip != nil {
+		return leaveIPv4GroupUDP(c, ifi, ip)
 	}
-	mreq := &syscall.IPMreq{
-		Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+	return leaveIPv6GroupUDP(c, ifi, addr)
+}
+
+func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+	mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+	if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
+		return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
 	}
-	err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
-	if err != nil {
-		return &OpError{"joingroup", "udp", &IPAddr{ip}, err}
+	if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)); err != nil {
+		return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
 	}
 	return nil
 }
 
-// LeaveGroup exits the IPv4 multicast group named by addr.
-func (c *UDPConn) LeaveGroup(addr IP) os.Error {
-	if !c.ok() {
-		return os.EINVAL
+func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+	mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+	if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
+		return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
 	}
-	ip := addr.To4()
-	if ip == nil {
-		return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+	if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)); err != nil {
+		return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
 	}
-	mreq := &syscall.IPMreq{
-		Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+	return nil
+}
+
+func setIPv4InterfaceToJoin(mreq *syscall.IPMreq, ifi *Interface) os.Error {
+	if ifi == nil {
+		return nil
 	}
-	err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
+	ifat, err := ifi.Addrs()
 	if err != nil {
-		return &OpError{"leavegroup", "udp", &IPAddr{ip}, err}
+		return err
+	}
+	for _, ifa := range ifat {
+		if x := ifa.(*IPAddr).IP.To4(); x != nil {
+			copy(mreq.Interface[:], x)
+			break
+		}
+	}
+	if bytes.Equal(mreq.Multiaddr[:], IPv4zero) {
+		return os.EINVAL
+	}
+	return nil
+}
+
+func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+	mreq := &syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], ip)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)); err != nil {
+		return &OpError{"joinipv6group", "udp", &IPAddr{ip}, err}
+	}
+	return nil
+}
+
+func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+	mreq := &syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], ip)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)); err != nil {
+		return &OpError{"leaveipv6group", "udp", &IPAddr{ip}, err}
 	}
 	return nil
 }
diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go
index cacfc15..91ace6d 100644
--- a/src/pkg/os/error_plan9.go
+++ b/src/pkg/os/error_plan9.go
@@ -48,6 +48,7 @@ var (
 	EPERM   = Eperm
 	EISDIR  = syscall.EISDIR
 
+	EBADF        = NewError("bad file descriptor")
 	ENAMETOOLONG = NewError("file name too long")
 	ERANGE       = NewError("math result not representable")
 	EPIPE        = NewError("Broken Pipe")
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
index 8a4b2e1..41e7723 100644
--- a/src/pkg/os/exec_unix.go
+++ b/src/pkg/os/exec_unix.go
@@ -38,7 +38,8 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
 	if e != 0 {
 		return nil, NewSyscallError("wait", e)
 	}
-	if options&WSTOPPED == 0 {
+	// With WNOHANG pid is 0 if child has not exited.
+	if pid1 != 0 && options&WSTOPPED == 0 {
 		p.done = true
 	}
 	w = new(Waitmsg)
diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index 0791a0d..14ddd92 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -21,39 +21,6 @@ func epipecheck(file *File, e int) {
 	}
 }
 
-// Stat returns a FileInfo structure describing the named file and an error, if any.
-// If name names a valid symbolic link, the returned FileInfo describes
-// the file pointed at by the link and has fi.FollowedSymlink set to true.
-// If name names an invalid symbolic link, the returned FileInfo describes
-// the link itself and has fi.FollowedSymlink set to false.
-func Stat(name string) (fi *FileInfo, err Error) {
-	var lstat, stat syscall.Stat_t
-	e := syscall.Lstat(name, &lstat)
-	if iserror(e) {
-		return nil, &PathError{"stat", name, Errno(e)}
-	}
-	statp := &lstat
-	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
-		e := syscall.Stat(name, &stat)
-		if !iserror(e) {
-			statp = &stat
-		}
-	}
-	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
-}
-
-// Lstat returns the FileInfo structure describing the named file and an
-// error, if any.  If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link.  Lstat makes no attempt to follow the link.
-func Lstat(name string) (fi *FileInfo, err Error) {
-	var stat syscall.Stat_t
-	e := syscall.Lstat(name, &stat)
-	if iserror(e) {
-		return nil, &PathError{"lstat", name, Errno(e)}
-	}
-	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
-}
-
 // Remove removes the named file or directory.
 func Remove(name string) Error {
 	// System call interface forces us to know
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index 301c2f4..ab32ce9 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -94,6 +94,39 @@ func (file *File) Stat() (fi *FileInfo, err Error) {
 	return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil
 }
 
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+	var lstat, stat syscall.Stat_t
+	e := syscall.Lstat(name, &lstat)
+	if iserror(e) {
+		return nil, &PathError{"stat", name, Errno(e)}
+	}
+	statp := &lstat
+	if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
+		e := syscall.Stat(name, &stat)
+		if !iserror(e) {
+			statp = &stat
+		}
+	}
+	return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
+}
+
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any.  If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link.  Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+	var stat syscall.Stat_t
+	e := syscall.Lstat(name, &stat)
+	if iserror(e) {
+		return nil, &PathError{"lstat", name, Errno(e)}
+	}
+	return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
+}
+
 // Readdir reads the contents of the directory associated with file and
 // returns an array of up to n FileInfo structures, as would be returned
 // by Lstat, in directory order. Subsequent calls on the same file will yield
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 70dd6e2..0cdd2fd 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -39,8 +39,8 @@ func NewFile(fd syscall.Handle, name string) *File {
 
 // Auxiliary information if the File describes a directory
 type dirInfo struct {
-	stat         syscall.Stat_t
-	usefirststat bool
+	data     syscall.Win32finddata
+	needdata bool
 }
 
 const DevNull = "NUL"
@@ -64,12 +64,11 @@ func openFile(name string, flag int, perm uint32) (file *File, err Error) {
 
 func openDir(name string) (file *File, err Error) {
 	d := new(dirInfo)
-	r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+"\\*"), &d.stat.Windata)
+	r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
 	if e != 0 {
 		return nil, &PathError{"open", name, Errno(e)}
 	}
 	f := NewFile(r, name)
-	d.usefirststat = true
 	f.dirinfo = d
 	return f, nil
 }
@@ -128,28 +127,6 @@ func (file *File) Close() Error {
 	return err
 }
 
-func (file *File) statFile(name string) (fi *FileInfo, err Error) {
-	var stat syscall.ByHandleFileInformation
-	e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat)
-	if e != 0 {
-		return nil, &PathError{"stat", file.name, Errno(e)}
-	}
-	return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
-}
-
-// Stat returns the FileInfo structure describing file.
-// It returns the FileInfo and an error, if any.
-func (file *File) Stat() (fi *FileInfo, err Error) {
-	if file == nil || file.fd < 0 {
-		return nil, EINVAL
-	}
-	if file.isdir() {
-		// I don't know any better way to do that for directory
-		return Stat(file.name)
-	}
-	return file.statFile(file.name)
-}
-
 // Readdir reads the contents of the directory associated with file and
 // returns an array of up to n FileInfo structures, as would be returned
 // by Lstat, in directory order. Subsequent calls on the same file will yield
@@ -172,7 +149,6 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
 	if !file.isdir() {
 		return nil, &PathError{"Readdir", file.name, ENOTDIR}
 	}
-	di := file.dirinfo
 	wantAll := n <= 0
 	size := n
 	if wantAll {
@@ -180,11 +156,10 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
 		size = 100
 	}
 	fi = make([]FileInfo, 0, size) // Empty with room to grow.
+	d := &file.dirinfo.data
 	for n != 0 {
-		if di.usefirststat {
-			di.usefirststat = false
-		} else {
-			e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata)
+		if file.dirinfo.needdata {
+			e := syscall.FindNextFile(syscall.Handle(file.fd), d)
 			if e != 0 {
 				if e == syscall.ERROR_NO_MORE_FILES {
 					break
@@ -198,7 +173,8 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
 			}
 		}
 		var f FileInfo
-		fileInfoFromWin32finddata(&f, &di.stat.Windata)
+		setFileInfo(&f, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+		file.dirinfo.needdata = true
 		if f.Name == "." || f.Name == ".." { // Useless names
 			continue
 		}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 4d60333..f9baceb 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -920,6 +920,12 @@ func TestHostname(t *testing.T) {
 	if syscall.OS == "windows" || syscall.OS == "plan9" {
 		return
 	}
+
+	// TODO(jsing): Fix nametomib() on OpenBSD
+	if syscall.OS == "openbsd" {
+		return
+	}
+
 	// Check internal Hostname() against the output of /bin/hostname.
 	// Allow that the internal Hostname returns a Fully Qualified Domain Name
 	// and the /bin/hostname only returns the first component
diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index 1108843..2009d1f 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -4,24 +4,76 @@
 
 package os
 
-import "syscall"
+import (
+	"unsafe"
+	"syscall"
+)
 
-func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo {
-	return fileInfoFromWin32finddata(fi, &stat.Windata)
+// Stat returns the FileInfo structure describing file.
+// It returns the FileInfo and an error, if any.
+func (file *File) Stat() (fi *FileInfo, err Error) {
+	if file == nil || file.fd < 0 {
+		return nil, EINVAL
+	}
+	if file.isdir() {
+		// I don't know any better way to do that for directory
+		return Stat(file.name)
+	}
+	var d syscall.ByHandleFileInformation
+	e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d)
+	if e != 0 {
+		return nil, &PathError{"GetFileInformationByHandle", file.name, Errno(e)}
+	}
+	return setFileInfo(new(FileInfo), basename(file.name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
 }
 
-func fileInfoFromWin32finddata(fi *FileInfo, d *syscall.Win32finddata) *FileInfo {
-	return setFileInfo(fi, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+	if len(name) == 0 {
+		return nil, &PathError{"Stat", name, Errno(syscall.ERROR_PATH_NOT_FOUND)}
+	}
+	var d syscall.Win32FileAttributeData
+	e := syscall.GetFileAttributesEx(syscall.StringToUTF16Ptr(name), syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&d)))
+	if e != 0 {
+		return nil, &PathError{"GetFileAttributesEx", name, Errno(e)}
+	}
+	return setFileInfo(new(FileInfo), basename(name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
 }
 
-func fileInfoFromByHandleInfo(fi *FileInfo, name string, d *syscall.ByHandleFileInformation) *FileInfo {
-	for i := len(name) - 1; i >= 0; i-- {
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any.  If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link.  Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+	// No links on Windows
+	return Stat(name)
+}
+
+// basename removes trailing slashes and the leading
+// directory name and drive letter from path name.
+func basename(name string) string {
+	// Remove drive letter
+	if len(name) == 2 && name[1] == ':' {
+		name = "."
+	} else if len(name) > 2 && name[1] == ':' {
+		name = name[2:]
+	}
+	i := len(name) - 1
+	// Remove trailing slashes
+	for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
+		name = name[:i]
+	}
+	// Remove leading directory name
+	for i--; i >= 0; i-- {
 		if name[i] == '/' || name[i] == '\\' {
 			name = name[i+1:]
 			break
 		}
 	}
-	return setFileInfo(fi, name, d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+	return name
 }
 
 func setFileInfo(fi *FileInfo, name string, fa, sizehi, sizelo uint32, ctime, atime, wtime syscall.Filetime) *FileInfo {
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index d2a1069..395b127 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -81,7 +81,7 @@ var wincleantests = []PathTest{
 func TestClean(t *testing.T) {
 	tests := cleantests
 	if runtime.GOOS == "windows" {
-		for i, _ := range tests {
+		for i := range tests {
 			tests[i].result = filepath.FromSlash(tests[i].result)
 		}
 		tests = append(tests, wincleantests...)
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index 257278e..610ba4b 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -6,7 +6,7 @@ package reflect_test
 
 import (
 	"bytes"
-	"container/vector"
+	"encoding/base64"
 	"fmt"
 	"io"
 	"os"
@@ -1322,8 +1322,8 @@ func TestFieldByName(t *testing.T) {
 }
 
 func TestImportPath(t *testing.T) {
-	if path := TypeOf(vector.Vector{}).PkgPath(); path != "container/vector" {
-		t.Errorf("TypeOf(vector.Vector{}).PkgPath() = %q, want \"container/vector\"", path)
+	if path := TypeOf(&base64.Encoding{}).Elem().PkgPath(); path != "encoding/base64" {
+		t.Errorf(`TypeOf(&base64.Encoding{}).Elem().PkgPath() = %q, want "encoding/base64"`, path)
 	}
 }
 
@@ -1562,3 +1562,28 @@ func TestTagGet(t *testing.T) {
 		}
 	}
 }
+
+func TestBytes(t *testing.T) {
+	type B []byte
+	x := B{1, 2, 3, 4}
+	y := ValueOf(x).Bytes()
+	if !bytes.Equal(x, y) {
+		t.Fatalf("ValueOf(%v).Bytes() = %v", x, y)
+	}
+	if &x[0] != &y[0] {
+		t.Errorf("ValueOf(%p).Bytes() = %p", &x[0], &y[0])
+	}
+}
+
+func TestSetBytes(t *testing.T) {
+	type B []byte
+	var x B
+	y := []byte{1, 2, 3, 4}
+	ValueOf(&x).Elem().SetBytes(y)
+	if !bytes.Equal(x, y) {
+		t.Fatalf("ValueOf(%v).Bytes() = %v", x, y)
+	}
+	if &x[0] != &y[0] {
+		t.Errorf("ValueOf(%p).Bytes() = %p", &x[0], &y[0])
+	}
+}
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 4c377e1..c9a9edc 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -65,7 +65,7 @@ type Type interface {
 	Name() string
 
 	// PkgPath returns the type's package path.
-	// The package path is a full package import path like "container/vector".
+	// The package path is a full package import path like "encoding/base64".
 	// PkgPath returns an empty string for unnamed types.
 	PkgPath() string
 
@@ -75,7 +75,7 @@ type Type interface {
 
 	// String returns a string representation of the type.
 	// The string representation may use shortened package names
-	// (e.g., vector instead of "container/vector") and is not
+	// (e.g., base64 instead of "encoding/base64") and is not
 	// guaranteed to be unique among types.  To test for equality,
 	// compare the Types directly.
 	String() string
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index d3c510a..8e5a628 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -322,8 +322,31 @@ func packValue(flag uint32, typ *runtime.Type, word iword) Value {
 	return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))}
 }
 
+var dummy struct {
+	b bool
+	x interface{}
+}
+
+// Dummy annotation marking that the value x escapes,
+// for use in cases where the reflect code is so clever that
+// the compiler cannot follow.
+func escapes(x interface{}) {
+	if dummy.b {
+		dummy.x = x
+	}
+}
+
 // valueFromAddr returns a Value using the given type and address.
 func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value {
+	// TODO(rsc): Eliminate this terrible hack.
+	// The escape analysis knows that addr is a pointer
+	// but it doesn't see addr get passed to anything
+	// that keeps it.  packValue keeps it, but packValue
+	// takes a uintptr (iword(addr)), and integers (non-pointers)
+	// are assumed not to matter.  The escapes function works
+	// because return values always escape (for now).
+	escapes(addr)
+
 	if flag&flagAddr != 0 {
 		// Addressable, so the internal value is
 		// an interface containing a pointer to the real value.
@@ -398,6 +421,18 @@ func (v Value) Bool() bool {
 	return *(*bool)(unsafe.Pointer(&iv.word))
 }
 
+// Bytes returns v's underlying value.
+// It panics if v's underlying value is not a slice of bytes.
+func (v Value) Bytes() []byte {
+	iv := v.internal()
+	iv.mustBe(Slice)
+	typ := iv.typ.toType()
+	if typ.Elem().Kind() != Uint8 {
+		panic("reflect.Value.Bytes of non-byte slice")
+	}
+	return *(*[]byte)(iv.addr)
+}
+
 // CanAddr returns true if the value's address can be obtained with Addr.
 // Such values are called addressable.  A value is addressable if it is
 // an element of a slice, an element of an addressable array,
@@ -1224,6 +1259,19 @@ func (v Value) SetBool(x bool) {
 	*(*bool)(iv.addr) = x
 }
 
+// SetBytes sets v's underlying value.
+// It panics if v's underlying value is not a slice of bytes.
+func (v Value) SetBytes(x []byte) {
+	iv := v.internal()
+	iv.mustBeAssignable()
+	iv.mustBe(Slice)
+	typ := iv.typ.toType()
+	if typ.Elem().Kind() != Uint8 {
+		panic("reflect.Value.SetBytes of non-byte slice")
+	}
+	*(*[]byte)(iv.addr) = x
+}
+
 // SetComplex sets v's underlying value to x.
 // It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false.
 func (v Value) SetComplex(x complex128) {
@@ -1653,6 +1701,14 @@ func ValueOf(i interface{}) Value {
 	if i == nil {
 		return Value{}
 	}
+
+	// TODO(rsc): Eliminate this terrible hack.
+	// In the call to packValue, eface.typ doesn't escape,
+	// and eface.word is an integer.  So it looks like
+	// i (= eface) doesn't escape.  But really it does,
+	// because eface.word is actually a pointer.
+	escapes(i)
+
 	// For an interface value with the noAddr bit set,
 	// the representation is identical to an empty interface.
 	eface := *(*emptyInterface)(unsafe.Pointer(&i))
diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go
index e7bbfbe..cb2db2a 100644
--- a/src/pkg/rpc/server_test.go
+++ b/src/pkg/rpc/server_test.go
@@ -14,6 +14,7 @@ import (
 	"runtime"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 )
@@ -477,19 +478,79 @@ func benchmarkEndToEnd(dial func() (*Client, os.Error), b *testing.B) {
 
 	// Synchronous calls
 	args := &Args{7, 8}
-	reply := new(Reply)
+	procs := runtime.GOMAXPROCS(-1)
+	N := int32(b.N)
+	var wg sync.WaitGroup
+	wg.Add(procs)
 	b.StartTimer()
-	for i := 0; i < b.N; i++ {
-		err = client.Call("Arith.Add", args, reply)
-		if err != nil {
-			fmt.Printf("Add: expected no error but got string %q", err.String())
-			break
-		}
-		if reply.C != args.A+args.B {
-			fmt.Printf("Add: expected %d got %d", reply.C, args.A+args.B)
-			break
-		}
+
+	for p := 0; p < procs; p++ {
+		go func() {
+			reply := new(Reply)
+			for atomic.AddInt32(&N, -1) >= 0 {
+				err = client.Call("Arith.Add", args, reply)
+				if err != nil {
+					fmt.Printf("Add: expected no error but got string %q", err.String())
+					panic("rpc error")
+				}
+				if reply.C != args.A+args.B {
+					fmt.Printf("Add: expected %d got %d", reply.C, args.A+args.B)
+					panic("rpc error")
+				}
+			}
+			wg.Done()
+		}()
+	}
+	wg.Wait()
+}
+
+func benchmarkEndToEndAsync(dial func() (*Client, os.Error), b *testing.B) {
+	const MaxConcurrentCalls = 100
+	b.StopTimer()
+	once.Do(startServer)
+	client, err := dial()
+	if err != nil {
+		fmt.Println("error dialing", err)
+		return
 	}
+
+	// Asynchronous calls
+	args := &Args{7, 8}
+	procs := 4 * runtime.GOMAXPROCS(-1)
+	send := int32(b.N)
+	recv := int32(b.N)
+	var wg sync.WaitGroup
+	wg.Add(procs)
+	gate := make(chan bool, MaxConcurrentCalls)
+	res := make(chan *Call, MaxConcurrentCalls)
+	b.StartTimer()
+
+	for p := 0; p < procs; p++ {
+		go func() {
+			for atomic.AddInt32(&send, -1) >= 0 {
+				gate <- true
+				reply := new(Reply)
+				client.Go("Arith.Add", args, reply, res)
+			}
+		}()
+		go func() {
+			for call := range res {
+				a := call.Args.(*Args).A
+				b := call.Args.(*Args).B
+				c := call.Reply.(*Reply).C
+				if a+b != c {
+					fmt.Printf("Add: expected %d got %d", a+b, c)
+					panic("incorrect reply")
+				}
+				<-gate
+				if atomic.AddInt32(&recv, -1) == 0 {
+					close(res)
+				}
+			}
+			wg.Done()
+		}()
+	}
+	wg.Wait()
 }
 
 func BenchmarkEndToEnd(b *testing.B) {
@@ -499,3 +560,11 @@ func BenchmarkEndToEnd(b *testing.B) {
 func BenchmarkEndToEndHTTP(b *testing.B) {
 	benchmarkEndToEnd(dialHTTP, b)
 }
+
+func BenchmarkEndToEndAsync(b *testing.B) {
+	benchmarkEndToEndAsync(dialDirect, b)
+}
+
+func BenchmarkEndToEndAsyncHTTP(b *testing.B) {
+	benchmarkEndToEndAsync(dialHTTP, b)
+}
diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s
index a145188..c64e78f 100644
--- a/src/pkg/runtime/386/asm.s
+++ b/src/pkg/runtime/386/asm.s
@@ -432,17 +432,17 @@ TEXT runtime·cgocallback(SB),7,$12
 	PUSHL	(g_sched+gobuf_sp)(SI)
 	MOVL	SP, (g_sched+gobuf_sp)(SI)
 
-	// Switch to m->curg stack and call runtime.cgocallback
+	// Switch to m->curg stack and call runtime.cgocallbackg
 	// with the three arguments.  Because we are taking over
 	// the execution of m->curg but *not* resuming what had
 	// been running, we need to save that information (m->curg->gobuf)
 	// so that we can restore it when we're done. 
 	// We can restore m->curg->gobuf.sp easily, because calling
-	// runtime.cgocallback leaves SP unchanged upon return.
+	// runtime.cgocallbackg leaves SP unchanged upon return.
 	// To save m->curg->gobuf.pc, we push it onto the stack.
 	// This has the added benefit that it looks to the traceback
-	// routine like cgocallback is going to return to that
-	// PC (because we defined cgocallback to have
+	// routine like cgocallbackg is going to return to that
+	// PC (because we defined cgocallbackg to have
 	// a frame size of 12, the same amount that we use below),
 	// so that the traceback will seamlessly trace back into
 	// the earlier calls.
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index 64bd2b7..725c2b0 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -13,7 +13,7 @@ SIZE_arm=32
 SIZE=$(SIZE_$(GOARCH))
 
 CFLAGS_windows=-D__WINDOWS__
-CFLAGS=-I$(GOOS) -I$(GOARCH) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS))
+CFLAGS=-I$(GOOS) -I$(GOARCH) -I$(GOOS)/$(GOARCH) -FVw $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS))
 
 GOFILES=\
 	debug.go\
@@ -31,6 +31,7 @@ GOFILES=\
 CLEANFILES+=version.go version_*.go
 
 OFILES_windows=\
+	callback.$O\
 	syscall.$O\
 
 # 386-specific object files
diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s
index 3e3818c..a881e3b 100644
--- a/src/pkg/runtime/amd64/asm.s
+++ b/src/pkg/runtime/amd64/asm.s
@@ -448,19 +448,19 @@ TEXT runtime·asmcgocall(SB),7,$0
 	MOVQ	(g_sched+gobuf_sp)(SI), SP
 
 	// Now on a scheduling stack (a pthread-created stack).
-	SUBQ	$32, SP
+	SUBQ	$48, SP
 	ANDQ	$~15, SP	// alignment for gcc ABI
-	MOVQ	DI, 16(SP)	// save g
-	MOVQ	DX, 8(SP)	// save SP
+	MOVQ	DI, 32(SP)	// save g
+	MOVQ	DX, 24(SP)	// save SP
 	MOVQ	BX, DI		// DI = first argument in AMD64 ABI
 	MOVQ	BX, CX		// CX = first argument in Win64
 	CALL	AX
 
 	// Restore registers, g, stack pointer.
 	get_tls(CX)
-	MOVQ	16(SP), DI
+	MOVQ	32(SP), DI
 	MOVQ	DI, g(CX)
-	MOVQ	8(SP), SP
+	MOVQ	24(SP), SP
 	RET
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
@@ -477,17 +477,17 @@ TEXT runtime·cgocallback(SB),7,$24
 	PUSHQ	(g_sched+gobuf_sp)(SI)
 	MOVQ	SP, (g_sched+gobuf_sp)(SI)
 
-	// Switch to m->curg stack and call runtime.cgocallback
+	// Switch to m->curg stack and call runtime.cgocallbackg
 	// with the three arguments.  Because we are taking over
 	// the execution of m->curg but *not* resuming what had
 	// been running, we need to save that information (m->curg->gobuf)
 	// so that we can restore it when we're done. 
 	// We can restore m->curg->gobuf.sp easily, because calling
-	// runtime.cgocallback leaves SP unchanged upon return.
+	// runtime.cgocallbackg leaves SP unchanged upon return.
 	// To save m->curg->gobuf.pc, we push it onto the stack.
 	// This has the added benefit that it looks to the traceback
-	// routine like cgocallback is going to return to that
-	// PC (because we defined cgocallback to have
+	// routine like cgocallbackg is going to return to that
+	// PC (because we defined cgocallbackg to have
 	// a frame size of 24, the same amount that we use below),
 	// so that the traceback will seamlessly trace back into
 	// the earlier calls.
diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c
index 3e85d36..c03a6f7 100644
--- a/src/pkg/runtime/amd64/traceback.c
+++ b/src/pkg/runtime/amd64/traceback.c
@@ -123,27 +123,30 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
 		else if(pcbuf != nil)
 			pcbuf[n++] = pc;
 		else {
-			// Print during crash.
-			//	main+0xf /home/rsc/go/src/runtime/x.go:23
-			//		main(0x1, 0x2, 0x3)
-			runtime·printf("%S", f->name);
-			if(pc > f->entry)
-				runtime·printf("+%p", (uintptr)(pc - f->entry));
-			tracepc = pc;	// back up to CALL instruction for funcline.
-			if(n > 0 && pc > f->entry && !waspanic)
-				tracepc--;
-			runtime·printf(" %S:%d\n", f->src, runtime·funcline(f, tracepc));
-			runtime·printf("\t%S(", f->name);
-			for(i = 0; i < f->args; i++) {
-				if(i != 0)
-					runtime·prints(", ");
-				runtime·printhex(((uintptr*)fp)[i]);
-				if(i >= 4) {
-					runtime·prints(", ...");
-					break;
+			if(runtime·showframe(f)) {
+				// Print during crash.
+				//	main(0x1, 0x2, 0x3)
+				//		/home/rsc/go/src/runtime/x.go:23 +0xf
+				//		
+				tracepc = pc;	// back up to CALL instruction for funcline.
+				if(n > 0 && pc > f->entry && !waspanic)
+					tracepc--;
+				runtime·printf("%S(", f->name);
+				for(i = 0; i < f->args; i++) {
+					if(i != 0)
+						runtime·prints(", ");
+					runtime·printhex(((uintptr*)fp)[i]);
+					if(i >= 4) {
+						runtime·prints(", ...");
+						break;
+					}
 				}
+				runtime·prints(")\n");
+				runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+				if(pc > f->entry)
+					runtime·printf(" +%p", (uintptr)(pc - f->entry));
+				runtime·printf("\n");
 			}
-			runtime·prints(")\n");
 			n++;
 		}
 		
@@ -189,14 +192,16 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
 		fp = nil;
 	}
 	
-	if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil) {
-		runtime·printf("----- goroutine created by -----\n%S", f->name);
-		if(pc > f->entry)
-			runtime·printf("+%p", (uintptr)(pc - f->entry));
+	// Show what created goroutine, except main goroutine (goid 1).
+	if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) {
+		runtime·printf("created by %S\n", f->name);
 		tracepc = pc;	// back up to CALL instruction for funcline.
 		if(n > 0 && pc > f->entry)
 			tracepc--;
-		runtime·printf(" %S:%d\n", f->src, runtime·funcline(f, tracepc));
+		runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+		if(pc > f->entry)
+			runtime·printf(" +%p", (uintptr)(pc - f->entry));
+		runtime·printf("\n");
 	}
 		
 	return n;
diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c
index 5628b83..6352810 100644
--- a/src/pkg/runtime/arm/traceback.c
+++ b/src/pkg/runtime/arm/traceback.c
@@ -117,27 +117,29 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
 		else if(pcbuf != nil)
 			pcbuf[n++] = pc;
 		else {
-			// Print during crash.
-			//	main+0xf /home/rsc/go/src/runtime/x.go:23
-			//		main(0x1, 0x2, 0x3)
-			runtime·printf("[%p] %S", fp, f->name);
-			if(pc > f->entry)
-				runtime·printf("+%p", (uintptr)(pc - f->entry));
-			tracepc = pc;	// back up to CALL instruction for funcline.
-			if(n > 0 && pc > f->entry && !waspanic)
-				tracepc -= sizeof(uintptr);
-			runtime·printf(" %S:%d\n", f->src, runtime·funcline(f, tracepc));
-			runtime·printf("\t%S(", f->name);
-			for(i = 0; i < f->args; i++) {
-				if(i != 0)
-					runtime·prints(", ");
-				runtime·printhex(((uintptr*)fp)[1+i]);
-				if(i >= 4) {
-					runtime·prints(", ...");
-					break;
+			if(runtime·showframe(f)) {
+				// Print during crash.
+				//	main(0x1, 0x2, 0x3)
+				//		/home/rsc/go/src/runtime/x.go:23 +0xf
+				tracepc = pc;	// back up to CALL instruction for funcline.
+				if(n > 0 && pc > f->entry && !waspanic)
+					tracepc -= sizeof(uintptr);
+				runtime·printf("%S(", f->name);
+				for(i = 0; i < f->args; i++) {
+					if(i != 0)
+						runtime·prints(", ");
+					runtime·printhex(((uintptr*)fp)[1+i]);
+					if(i >= 4) {
+						runtime·prints(", ...");
+						break;
+					}
 				}
+				runtime·prints(")\n");
+				runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+				if(pc > f->entry)
+					runtime·printf(" +%p", (uintptr)(pc - f->entry));
+				runtime·printf("\n");
 			}
-			runtime·prints(")\n");
 			n++;
 		}
 		
@@ -181,14 +183,15 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
 			sp += 12;
 	}
 	
-	if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil) {
-		runtime·printf("----- goroutine created by -----\n%S", f->name);
-		if(pc > f->entry)
-			runtime·printf("+%p", (uintptr)(pc - f->entry));
+	if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) {
+		runtime·printf("created by %S\n", f->name);
 		tracepc = pc;	// back up to CALL instruction for funcline.
 		if(n > 0 && pc > f->entry)
 			tracepc -= sizeof(uintptr);
-		runtime·printf(" %S:%d\n", f->src, runtime·funcline(f, tracepc));
+		runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+		if(pc > f->entry)
+			runtime·printf(" +%p", (uintptr)(pc - f->entry));
+		runtime·printf("\n");
 	}
 
 	return n;		
diff --git a/src/pkg/runtime/cgo/amd64.S b/src/pkg/runtime/cgo/amd64.S
index 083c2bc..6c1a4c8 100644
--- a/src/pkg/runtime/cgo/amd64.S
+++ b/src/pkg/runtime/cgo/amd64.S
@@ -32,7 +32,11 @@ EXT(crosscall_amd64):
 	pushq %r14
 	pushq %r15
 
+#if defined(_WIN64)
+	call *%rcx	/* fn */
+#else
 	call *%rdi	/* fn */
+#endif
 
 	popq %r15
 	popq %r14
@@ -58,10 +62,21 @@ EXT(crosscall2):
 	movq  %r14, 0x30(%rsp)
 	movq  %r15, 0x38(%rsp)
 
+#if defined(_WIN64)
+	// Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15
+	movq	%rdi, 0x40(%rsp)
+	movq	%rsi, 0x48(%rsp)
+
+	movq  %rdx, 0(%rsp)	/* arg */
+	movq  %r8, 8(%rsp)	/* argsize (includes padding) */
+	
+	call %rcx	/* fn */
+#else
 	movq  %rsi, 0(%rsp)	/* arg */
 	movq  %rdx, 8(%rsp)	/* argsize (includes padding) */
 
 	call *%rdi	/* fn */
+#endif
 
 	movq  0x10(%rsp), %rbx
 	movq  0x18(%rsp), %rbp
@@ -69,5 +84,9 @@ EXT(crosscall2):
 	movq  0x28(%rsp), %r13
 	movq  0x30(%rsp), %r14
 	movq  0x38(%rsp), %r15
+#if defined(__WIN64)
+	movq	0x40(%rsp), %rdi
+	movq	0x48(%rsp), %rsi
+#endif	
 	addq  $0x58, %rsp
 	ret
diff --git a/src/pkg/runtime/cgo/util.c b/src/pkg/runtime/cgo/util.c
index 9d96521..e06b6f6 100644
--- a/src/pkg/runtime/cgo/util.c
+++ b/src/pkg/runtime/cgo/util.c
@@ -18,7 +18,7 @@ x_cgo_malloc(void *p)
 
 void (*_cgo_malloc)(void*) = x_cgo_malloc;
 
-/* Stub for calling from Go */
+/* Stub for calling free from Go */
 static void
 x_cgo_free(void *p)
 {
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 829448b..4fdb912 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -68,7 +68,7 @@
 // stack (not an m->g0 stack).  First it calls runtime.exitsyscall, which will
 // block until the $GOMAXPROCS limit allows running this goroutine.
 // Once exitsyscall has returned, it is safe to do things like call the memory
-// allocator or invoke the Go callback function p.GoF.  runtime.cgocallback
+// allocator or invoke the Go callback function p.GoF.  runtime.cgocallbackg
 // first defers a function to unwind m->g0.sched.sp, so that if p.GoF
 // panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack
 // and the m->curg stack will be unwound in lock step.
@@ -92,9 +92,9 @@ static void unwindm(void);
 void
 runtime·cgocall(void (*fn)(void*), void *arg)
 {
-	Defer *d;
+	Defer d;
 
-	if(!runtime·iscgo)
+	if(!runtime·iscgo && !Windows)
 		runtime·throw("cgocall unavailable");
 
 	if(fn == 0)
@@ -106,18 +106,18 @@ runtime·cgocall(void (*fn)(void*), void *arg)
 	 * Lock g to m to ensure we stay on the same stack if we do a
 	 * cgo callback.
 	 */
-	d = nil;
+	d.nofree = false;
 	if(m->lockedg == nil) {
 		m->lockedg = g;
 		g->lockedm = m;
 
 		// Add entry to defer stack in case of panic.
-		d = runtime·malloc(sizeof(*d));
-		d->fn = (byte*)unlockm;
-		d->siz = 0;
-		d->link = g->defer;
-		d->argp = (void*)-1;  // unused because unwindm never recovers
-		g->defer = d;
+		d.fn = (byte*)unlockm;
+		d.siz = 0;
+		d.link = g->defer;
+		d.argp = (void*)-1;  // unused because unlockm never recovers
+		d.nofree = true;
+		g->defer = &d;
 	}
 
 	/*
@@ -135,11 +135,10 @@ runtime·cgocall(void (*fn)(void*), void *arg)
 	runtime·asmcgocall(fn, arg);
 	runtime·exitsyscall();
 
-	if(d != nil) {
-		if(g->defer != d || d->fn != (byte*)unlockm)
+	if(d.nofree) {
+		if(g->defer != &d || d.fn != (byte*)unlockm)
 			runtime·throw("runtime: bad defer entry in cgocallback");
-		g->defer = d->link;
-		runtime·free(d);
+		g->defer = d.link;
 		unlockm();
 	}
 }
@@ -192,7 +191,7 @@ runtime·cfree(void *p)
 void
 runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
 {
-	Defer *d;
+	Defer d;
 
 	if(g != m->curg)
 		runtime·throw("runtime: bad g in cgocallback");
@@ -200,12 +199,12 @@ runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
 	runtime·exitsyscall();	// coming out of cgo call
 
 	// Add entry to defer stack in case of panic.
-	d = runtime·malloc(sizeof(*d));
-	d->fn = (byte*)unwindm;
-	d->siz = 0;
-	d->link = g->defer;
-	d->argp = (void*)-1;  // unused because unwindm never recovers
-	g->defer = d;
+	d.fn = (byte*)unwindm;
+	d.siz = 0;
+	d.link = g->defer;
+	d.argp = (void*)-1;  // unused because unwindm never recovers
+	d.nofree = true;
+	g->defer = &d;
 
 	// Invoke callback.
 	reflect·call((byte*)fn, arg, argsize);
@@ -213,10 +212,9 @@ runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
 	// Pop defer.
 	// Do not unwind m->g0->sched.sp.
 	// Our caller, cgocallback, will do that.
-	if(g->defer != d || d->fn != (byte*)unwindm)
+	if(g->defer != &d || d.fn != (byte*)unwindm)
 		runtime·throw("runtime: bad defer entry in cgocallback");
-	g->defer = d->link;
-	runtime·free(d);
+	g->defer = d.link;
 
 	runtime·entersyscall();	// going back to cgo call
 }
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c
index ef53423..eac2098 100644
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -105,7 +105,7 @@ runtime·makechan_c(ChanType *t, int64 hint)
 	// allocate memory in one call
 	c = (Hchan*)runtime·mal(n + hint*elem->size);
 	if(runtime·destroylock)
-		runtime·addfinalizer(c, destroychan, 0);
+		runtime·addfinalizer(c, (void*)destroychan, 0);
 
 	c->elemsize = elem->size;
 	c->elemalg = &runtime·algarray[elem->alg];
@@ -171,6 +171,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
 			return;
 		}
 		g->status = Gwaiting;
+		g->waitreason = "chan send (nil chan)";
 		runtime·gosched();
 		return;  // not reached
 	}
@@ -217,6 +218,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
 	mysg.selgen = NOSELGEN;
 	g->param = nil;
 	g->status = Gwaiting;
+	g->waitreason = "chan send";
 	enqueue(&c->sendq, &mysg);
 	runtime·unlock(c);
 	runtime·gosched();
@@ -244,6 +246,7 @@ asynch:
 		mysg.elem = nil;
 		mysg.selgen = NOSELGEN;
 		g->status = Gwaiting;
+		g->waitreason = "chan send";
 		enqueue(&c->sendq, &mysg);
 		runtime·unlock(c);
 		runtime·gosched();
@@ -293,6 +296,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
 			return;
 		}
 		g->status = Gwaiting;
+		g->waitreason = "chan receive (nil chan)";
 		runtime·gosched();
 		return;  // not reached
 	}
@@ -332,6 +336,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
 	mysg.selgen = NOSELGEN;
 	g->param = nil;
 	g->status = Gwaiting;
+	g->waitreason = "chan receive";
 	enqueue(&c->recvq, &mysg);
 	runtime·unlock(c);
 	runtime·gosched();
@@ -363,6 +368,7 @@ asynch:
 		mysg.elem = nil;
 		mysg.selgen = NOSELGEN;
 		g->status = Gwaiting;
+		g->waitreason = "chan receive";
 		enqueue(&c->recvq, &mysg);
 		runtime·unlock(c);
 		runtime·gosched();
@@ -780,6 +786,7 @@ void
 runtime·block(void)
 {
 	g->status = Gwaiting;	// forever
+	g->waitreason = "select (no cases)";
 	runtime·gosched();
 }
 
@@ -912,6 +919,7 @@ loop:
 
 	g->param = nil;
 	g->status = Gwaiting;
+	g->waitreason = "select";
 	selunlock(sel);
 	runtime·gosched();
 
diff --git a/src/pkg/runtime/chan_test.go b/src/pkg/runtime/chan_test.go
index 46ddfd7..7cea906 100644
--- a/src/pkg/runtime/chan_test.go
+++ b/src/pkg/runtime/chan_test.go
@@ -59,6 +59,57 @@ func TestPseudoRandomSend(t *testing.T) {
 	t.Errorf("Want pseudo random, got %d zeros and %d ones", n0, n1)
 }
 
+func TestMultiConsumer(t *testing.T) {
+	const nwork = 23
+	const niter = 271828
+
+	pn := []int{2, 3, 7, 11, 13, 17, 19, 23, 27, 31}
+
+	q := make(chan int, nwork*3)
+	r := make(chan int, nwork*3)
+
+	// workers
+	var wg sync.WaitGroup
+	for i := 0; i < nwork; i++ {
+		wg.Add(1)
+		go func(w int) {
+			for v := range q {
+				// mess with the fifo-ish nature of range
+				if pn[w%len(pn)] == v {
+					runtime.Gosched()
+				}
+				r <- v
+			}
+			wg.Done()
+		}(i)
+	}
+
+	// feeder & closer
+	expect := 0
+	go func() {
+		for i := 0; i < niter; i++ {
+			v := pn[i%len(pn)]
+			expect += v
+			q <- v
+		}
+		close(q)  // no more work
+		wg.Wait() // workers done
+		close(r)  // ... so there can be no more results
+	}()
+
+	// consume & check
+	n := 0
+	s := 0
+	for v := range r {
+		n++
+		s += v
+	}
+	if n != niter || s != expect {
+		t.Errorf("Expected sum %d (got %d) from %d iter (saw %d)",
+			expect, s, niter, n)
+	}
+}
+
 func BenchmarkSelectUncontended(b *testing.B) {
 	const CallsPerSched = 1000
 	procs := runtime.GOMAXPROCS(-1)
diff --git a/src/pkg/runtime/goc2c.c b/src/pkg/runtime/goc2c.c
index 61236e2..fcac9c0 100644
--- a/src/pkg/runtime/goc2c.c
+++ b/src/pkg/runtime/goc2c.c
@@ -196,13 +196,14 @@ getchar_skipping_comments(void)
 }
 
 /*
- * Read and return a token.  Tokens are delimited by whitespace or by
- * [(),{}].  The latter are all returned as single characters.
+ * Read and return a token.  Tokens are string or character literals
+ * or else delimited by whitespace or by [(),{}].
+ * The latter are all returned as single characters.
  */
 static char *
 read_token(void)
 {
-	int c;
+	int c, q;
 	char *buf;
 	unsigned int alc, off;
 	const char* delims = "(),{}";
@@ -217,7 +218,26 @@ read_token(void)
 	alc = 16;
 	buf = xmalloc(alc + 1);
 	off = 0;
-	if (strchr(delims, c) != NULL) {
+	if(c == '"' || c == '\'') {
+		q = c;
+		buf[off] = c;
+		++off;
+		while (1) {
+			if (off+2 >= alc) { // room for c and maybe next char
+				alc *= 2;
+				buf = xrealloc(buf, alc + 1);
+			}
+			c = getchar_no_eof();
+			buf[off] = c;
+			++off;
+			if(c == q)
+				break;
+			if(c == '\\') {
+				buf[off] = getchar_no_eof();
+				++off;
+			}
+		}
+	} else if (strchr(delims, c) != NULL) {
 		buf[off] = c;
 		++off;
 	} else {
diff --git a/src/pkg/runtime/linux/386/rt0.s b/src/pkg/runtime/linux/386/rt0.s
index 223e6d2..8314954 100644
--- a/src/pkg/runtime/linux/386/rt0.s
+++ b/src/pkg/runtime/linux/386/rt0.s
@@ -5,5 +5,13 @@
 // Darwin and Linux use the same linkage to main
 
 TEXT _rt0_386_linux(SB),7,$0
-	JMP	_rt0_386(SB)
+	CALL	runtime·linux_setup_vdso(SB)
+	JMP		_rt0_386(SB)
+
+TEXT _fallback_vdso(SB),7,$0
+	INT	$0x80
+	RET
+
+DATA	runtime·_vdso(SB)/4, $_fallback_vdso(SB)
+GLOBL	runtime·_vdso(SB), $4
 
diff --git a/src/pkg/runtime/linux/386/signal.c b/src/pkg/runtime/linux/386/signal.c
index 8916e10..4045b2e 100644
--- a/src/pkg/runtime/linux/386/signal.c
+++ b/src/pkg/runtime/linux/386/signal.c
@@ -182,3 +182,27 @@ os·sigpipe(void)
 	sigaction(SIGPIPE, SIG_DFL, false);
 	runtime·raisesigpipe();
 }
+
+#define AT_NULL		0
+#define AT_SYSINFO	32
+extern uint32 runtime·_vdso;
+
+#pragma textflag 7
+void runtime·linux_setup_vdso(int32 argc, void *argv_list)
+{
+	byte **argv = &argv_list;
+	byte **envp;
+	uint32 *auxv;
+
+	// skip envp to get to ELF auxiliary vector.
+	for(envp = &argv[argc+1]; *envp != nil; envp++)
+		;
+	envp++;
+	
+	for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) {
+		if(auxv[0] == AT_SYSINFO) {
+			runtime·_vdso = auxv[1];
+			break;
+		}
+	}		
+}
diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s
index 0b4a349..f87420f 100644
--- a/src/pkg/runtime/linux/386/sys.s
+++ b/src/pkg/runtime/linux/386/sys.s
@@ -11,14 +11,14 @@
 TEXT runtime·exit(SB),7,$0
 	MOVL	$252, AX	// syscall number
 	MOVL	4(SP), BX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	INT $3	// not reached
 	RET
 
 TEXT runtime·exit1(SB),7,$0
 	MOVL	$1, AX	// exit - exit the current os thread
 	MOVL	4(SP), BX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	INT $3	// not reached
 	RET
 
@@ -27,13 +27,13 @@ TEXT runtime·open(SB),7,$0
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·close(SB),7,$0
 	MOVL	$6, AX		// syscall - close
 	MOVL	4(SP), BX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·write(SB),7,$0
@@ -41,7 +41,7 @@ TEXT runtime·write(SB),7,$0
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·read(SB),7,$0
@@ -49,16 +49,16 @@ TEXT runtime·read(SB),7,$0
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·raisesigpipe(SB),7,$12
 	MOVL	$224, AX	// syscall - gettid
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	MOVL	AX, 0(SP)	// arg 1 tid
 	MOVL	$13, 4(SP)	// arg 2 SIGPIPE
 	MOVL	$238, AX	// syscall - tkill
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·setitimer(SB),7,$0-24
@@ -66,7 +66,7 @@ TEXT runtime·setitimer(SB),7,$0-24
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·mincore(SB),7,$0-24
@@ -74,7 +74,7 @@ TEXT runtime·mincore(SB),7,$0-24
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·gettime(SB), 7, $32
@@ -82,7 +82,7 @@ TEXT runtime·gettime(SB), 7, $32
 	LEAL	8(SP), BX
 	MOVL	$0, CX
 	MOVL	$0, DX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 
 	MOVL	8(SP), BX	// sec
 	MOVL	sec+0(FP), DI
@@ -100,7 +100,7 @@ TEXT runtime·rt_sigaction(SB),7,$0
 	MOVL	8(SP), CX
 	MOVL	12(SP), DX
 	MOVL	16(SP), SI
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 TEXT runtime·sigtramp(SB),7,$44
@@ -138,7 +138,9 @@ TEXT runtime·sigignore(SB),7,$0
 
 TEXT runtime·sigreturn(SB),7,$0
 	MOVL	$173, AX	// rt_sigreturn
-	INT $0x80
+	// Sigreturn expects same SP as signal handler,
+	// so cannot CALL *runtime._vsdo(SB) here.
+	INT	$0x80
 	INT $3	// not reached
 	RET
 
@@ -151,7 +153,7 @@ TEXT runtime·mmap(SB),7,$0
 	MOVL	20(SP), DI
 	MOVL	24(SP), BP
 	SHRL	$12, BP
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	3(PC)
 	NOTL	AX
@@ -162,7 +164,7 @@ TEXT runtime·munmap(SB),7,$0
 	MOVL	$91, AX	// munmap
 	MOVL	4(SP), BX
 	MOVL	8(SP), CX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	2(PC)
 	INT $3
@@ -178,7 +180,7 @@ TEXT runtime·futex(SB),7,$0
 	MOVL	16(SP), SI
 	MOVL	20(SP), DI
 	MOVL	24(SP), BP
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
 
 // int32 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
@@ -199,6 +201,10 @@ TEXT runtime·clone(SB),7,$0
 	MOVL	SI, 8(CX)
 	MOVL	$1234, 12(CX)
 
+	// cannot use CALL *runtime·_vdso(SB) here, because
+	// the stack changes during the system call (after 
+	// CALL *runtime·_vdso(SB), the child is still using
+	// the parent's stack when executing its RET instruction).
 	INT	$0x80
 
 	// In parent, return.
@@ -214,7 +220,7 @@ TEXT runtime·clone(SB),7,$0
 
 	// Initialize AX to Linux tid
 	MOVL	$224, AX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 
 	// In child on new stack.  Reload registers (paranoia).
 	MOVL	0(SP), BX	// m
@@ -262,7 +268,7 @@ TEXT runtime·sigaltstack(SB),7,$-8
 	MOVL	$186, AX	// sigaltstack
 	MOVL	new+4(SP), BX
 	MOVL	old+8(SP), CX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	2(PC)
 	INT	$3
@@ -323,7 +329,7 @@ TEXT runtime·setldt(SB),7,$32
 	MOVL	AX, CX	// user_desc
 	MOVL	$16, DX	// sizeof(user_desc)
 	MOVL	$123, AX	// syscall - modify_ldt
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 
 	// breakpoint on error
 	CMPL AX, $0xfffff001
@@ -340,5 +346,5 @@ TEXT runtime·setldt(SB),7,$32
 
 TEXT runtime·osyield(SB),7,$0
 	MOVL	$158, AX
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	RET
diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c
index ad0fac6..6c5c908 100644
--- a/src/pkg/runtime/linux/mem.c
+++ b/src/pkg/runtime/linux/mem.c
@@ -6,23 +6,26 @@
 enum
 {
 	ENOMEM = 12,
+	_PAGE_SIZE = 4096,
 };
 
 static int32
 addrspace_free(void *v, uintptr n)
 {
-	uintptr page_size = 4096;
+	int32 errval;
+	uintptr chunk;
 	uintptr off;
-	int8 one_byte;
+	static byte vec[4096];
 
-	for(off = 0; off < n; off += page_size) {
-		int32 errval = runtime·mincore((int8 *)v + off, page_size, (void *)&one_byte);
+	for(off = 0; off < n; off += chunk) {
+		chunk = _PAGE_SIZE * sizeof vec;
+		if(chunk > (n - off))
+			chunk = n - off;
+		errval = runtime·mincore((int8*)v + off, chunk, vec);
 		// errval is 0 if success, or -(error_code) if error.
 		if (errval == 0 || errval != -ENOMEM)
 			return 0;
 	}
-	USED(v);
-	USED(n);
 	return 1;
 }
 
@@ -72,7 +75,7 @@ runtime·SysReserve(void *v, uintptr n)
 		return v;
 	
 	p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
-	if(p < (void*)4096) {
+	if((uintptr)p < 4096 || -(uintptr)p < 4096) {
 		return nil;
 	}
 	return p;
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index b9fe36d..84e0ac4 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -248,6 +248,7 @@ runtime·mallocinit(void)
 	byte *p;
 	uintptr arena_size, bitmap_size;
 	extern byte end[];
+	byte *want;
 
 	runtime·InitSizes();
 
@@ -307,9 +308,13 @@ runtime·mallocinit(void)
 		// not as an absolute requirement.  If we ask for the end
 		// of the data segment but the operating system requires
 		// a little more space before we can start allocating, it will
-		// give out a slightly higher pointer.  That's fine.  
-		// Run with what we get back.
-		p = runtime·SysReserve(end, bitmap_size + arena_size);
+		// give out a slightly higher pointer.  Except QEMU, which
+		// is buggy, as usual: it won't adjust the pointer upward.
+		// So adjust it upward a little bit ourselves: 1/4 MB to get
+		// away from the running binary image and then round up
+		// to a MB boundary.
+		want = (byte*)(((uintptr)end + (1<<18) + (1<<20) - 1)&~((1<<20)-1));
+		p = runtime·SysReserve(want, bitmap_size + arena_size);
 		if(p == nil)
 			runtime·throw("runtime: cannot reserve arena virtual address space");
 	}
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index 78ea2aa..e79317b 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -697,6 +697,7 @@ runfinq(void)
 		if(f == nil) {
 			fingwait = 1;
 			g->status = Gwaiting;
+			g->waitreason = "finalizer wait";
 			runtime·gosched();
 			continue;
 		}
diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh
index 328e2d5..920e79a 100755
--- a/src/pkg/runtime/mkasmh.sh
+++ b/src/pkg/runtime/mkasmh.sh
@@ -96,6 +96,7 @@ awk '
 /^aggr G$/ { aggr="g" }
 /^aggr M$/ { aggr = "m" }
 /^aggr Gobuf$/ { aggr = "gobuf" }
+/^aggr WinCall$/ { aggr = "wincall" }
 /^}/ { aggr = "" }
 
 #	Gobuf 24 sched;
diff --git a/src/pkg/runtime/openbsd/386/defs.h b/src/pkg/runtime/openbsd/386/defs.h
new file mode 100644
index 0000000..d61462c
--- /dev/null
+++ b/src/pkg/runtime/openbsd/386/defs.h
@@ -0,0 +1,140 @@
+// godefs -f -m32 defs.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+// Constants
+enum {
+	PROT_NONE = 0,
+	PROT_READ = 0x1,
+	PROT_WRITE = 0x2,
+	PROT_EXEC = 0x4,
+	MAP_ANON = 0x1000,
+	MAP_PRIVATE = 0x2,
+	MAP_FIXED = 0x10,
+	SA_SIGINFO = 0x40,
+	SA_RESTART = 0x2,
+	SA_ONSTACK = 0x1,
+	EINTR = 0x4,
+	SIGHUP = 0x1,
+	SIGINT = 0x2,
+	SIGQUIT = 0x3,
+	SIGILL = 0x4,
+	SIGTRAP = 0x5,
+	SIGABRT = 0x6,
+	SIGEMT = 0x7,
+	SIGFPE = 0x8,
+	SIGKILL = 0x9,
+	SIGBUS = 0xa,
+	SIGSEGV = 0xb,
+	SIGSYS = 0xc,
+	SIGPIPE = 0xd,
+	SIGALRM = 0xe,
+	SIGTERM = 0xf,
+	SIGURG = 0x10,
+	SIGSTOP = 0x11,
+	SIGTSTP = 0x12,
+	SIGCONT = 0x13,
+	SIGCHLD = 0x14,
+	SIGTTIN = 0x15,
+	SIGTTOU = 0x16,
+	SIGIO = 0x17,
+	SIGXCPU = 0x18,
+	SIGXFSZ = 0x19,
+	SIGVTALRM = 0x1a,
+	SIGPROF = 0x1b,
+	SIGWINCH = 0x1c,
+	SIGINFO = 0x1d,
+	SIGUSR1 = 0x1e,
+	SIGUSR2 = 0x1f,
+	FPE_INTDIV = 0x1,
+	FPE_INTOVF = 0x2,
+	FPE_FLTDIV = 0x3,
+	FPE_FLTOVF = 0x4,
+	FPE_FLTUND = 0x5,
+	FPE_FLTRES = 0x6,
+	FPE_FLTINV = 0x7,
+	FPE_FLTSUB = 0x8,
+	BUS_ADRALN = 0x1,
+	BUS_ADRERR = 0x2,
+	BUS_OBJERR = 0x3,
+	SEGV_MAPERR = 0x1,
+	SEGV_ACCERR = 0x2,
+	ITIMER_REAL = 0,
+	ITIMER_VIRTUAL = 0x1,
+	ITIMER_PROF = 0x2,
+};
+
+// Types
+#pragma pack on
+
+typedef struct Sigaltstack Sigaltstack;
+struct Sigaltstack {
+	void *ss_sp;
+	uint32 ss_size;
+	int32 ss_flags;
+};
+
+typedef uint32 Sigset;
+
+typedef struct Siginfo Siginfo;
+struct Siginfo {
+	int32 si_signo;
+	int32 si_code;
+	int32 si_errno;
+	byte _data[116];
+};
+
+typedef union Sigval Sigval;
+union Sigval {
+	int32 sival_int;
+	void *sival_ptr;
+};
+
+typedef struct StackT StackT;
+struct StackT {
+	void *ss_sp;
+	uint32 ss_size;
+	int32 ss_flags;
+};
+
+typedef struct Timeval Timeval;
+struct Timeval {
+	int32 tv_sec;
+	int32 tv_usec;
+};
+
+typedef struct Itimerval Itimerval;
+struct Itimerval {
+	Timeval it_interval;
+	Timeval it_value;
+};
+
+typedef void sfxsave64;
+
+typedef void usavefpu;
+
+typedef struct Sigcontext Sigcontext;
+struct Sigcontext {
+	int32 sc_gs;
+	int32 sc_fs;
+	int32 sc_es;
+	int32 sc_ds;
+	int32 sc_edi;
+	int32 sc_esi;
+	int32 sc_ebp;
+	int32 sc_ebx;
+	int32 sc_edx;
+	int32 sc_ecx;
+	int32 sc_eax;
+	int32 sc_eip;
+	int32 sc_cs;
+	int32 sc_eflags;
+	int32 sc_esp;
+	int32 sc_ss;
+	int32 sc_onstack;
+	int32 sc_mask;
+	int32 sc_trapno;
+	int32 sc_err;
+	usavefpu *sc_fpstate;
+};
+#pragma pack off
diff --git a/src/pkg/runtime/openbsd/386/rt0.s b/src/pkg/runtime/openbsd/386/rt0.s
new file mode 100644
index 0000000..e7e0da7
--- /dev/null
+++ b/src/pkg/runtime/openbsd/386/rt0.s
@@ -0,0 +1,6 @@
+// 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.
+
+TEXT _rt0_386_openbsd(SB),7,$0
+	JMP     _rt0_386(SB)
diff --git a/src/pkg/runtime/openbsd/386/signal.c b/src/pkg/runtime/openbsd/386/signal.c
new file mode 100644
index 0000000..8b0d4ac
--- /dev/null
+++ b/src/pkg/runtime/openbsd/386/signal.c
@@ -0,0 +1,189 @@
+// 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.
+
+#include "runtime.h"
+#include "defs.h"
+#include "signals.h"
+#include "os.h"
+
+extern void runtime·sigtramp(void);
+
+typedef struct sigaction {
+	union {
+		void    (*__sa_handler)(int32);
+		void    (*__sa_sigaction)(int32, Siginfo*, void *);
+	} __sigaction_u;		/* signal handler */
+	uint32	sa_mask;		/* signal mask to apply */
+	int32	sa_flags;		/* see signal options below */
+} Sigaction;
+
+void
+runtime·dumpregs(Sigcontext *r)
+{
+	runtime·printf("eax     %x\n", r->sc_eax);
+	runtime·printf("ebx     %x\n", r->sc_ebx);
+	runtime·printf("ecx     %x\n", r->sc_ecx);
+	runtime·printf("edx     %x\n", r->sc_edx);
+	runtime·printf("edi     %x\n", r->sc_edi);
+	runtime·printf("esi     %x\n", r->sc_esi);
+	runtime·printf("ebp     %x\n", r->sc_ebp);
+	runtime·printf("esp     %x\n", r->sc_esp);
+	runtime·printf("eip     %x\n", r->sc_eip);
+	runtime·printf("eflags  %x\n", r->sc_eflags);
+	runtime·printf("cs      %x\n", r->sc_cs);
+	runtime·printf("fs      %x\n", r->sc_fs);
+	runtime·printf("gs      %x\n", r->sc_gs);
+}
+
+String
+runtime·signame(int32 sig)
+{
+	if(sig < 0 || sig >= NSIG)
+		return runtime·emptystring;
+	return runtime·gostringnocopy((byte*)runtime·sigtab[sig].name);
+}
+
+void
+runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
+{
+	Sigcontext *r = context;
+	uintptr *sp;
+
+	if(sig == SIGPROF) {
+		runtime·sigprof((uint8*)r->sc_eip, (uint8*)r->sc_esp, nil, gp);
+		return;
+	}
+
+	if(gp != nil && (runtime·sigtab[sig].flags & SigPanic)) {
+		// Make it look like a call to the signal func.
+		// Have to pass arguments out of band since
+		// augmenting the stack frame would break
+		// the unwinding code.
+		gp->sig = sig;
+		gp->sigcode0 = info->si_code;
+		gp->sigcode1 = *(uintptr*)((byte*)info + 12); /* si_addr */
+		gp->sigpc = r->sc_eip;
+
+		// Only push runtime·sigpanic if r->sc_eip != 0.
+		// If r->sc_eip == 0, probably panicked because of a
+		// call to a nil func.  Not pushing that onto sp will
+		// make the trace look like a call to runtime·sigpanic instead.
+		// (Otherwise the trace will end at runtime·sigpanic and we
+		// won't get to see who faulted.)
+		if(r->sc_eip != 0) {
+			sp = (uintptr*)r->sc_esp;
+			*--sp = r->sc_eip;
+			r->sc_esp = (uintptr)sp;
+		}
+		r->sc_eip = (uintptr)runtime·sigpanic;
+		return;
+	}
+
+	if(runtime·sigtab[sig].flags & SigQueue) {
+		if(runtime·sigsend(sig) || (runtime·sigtab[sig].flags & SigIgnore))
+			return;
+		runtime·exit(2);	// SIGINT, SIGTERM, etc
+	}
+
+	if(runtime·panicking)	// traceback already printed
+		runtime·exit(2);
+	runtime·panicking = 1;
+
+	if(sig < 0 || sig >= NSIG)
+		runtime·printf("Signal %d\n", sig);
+	else
+		runtime·printf("%s\n", runtime·sigtab[sig].name);
+
+	runtime·printf("PC=%X\n", r->sc_eip);
+	runtime·printf("\n");
+
+	if(runtime·gotraceback()){
+		runtime·traceback((void*)r->sc_eip, (void*)r->sc_esp, 0, gp);
+		runtime·tracebackothers(gp);
+		runtime·dumpregs(r);
+	}
+
+	runtime·exit(2);
+}
+
+// Called from kernel on signal stack, so no stack split.
+#pragma textflag 7
+void
+runtime·sigignore(void)
+{
+}
+
+void
+runtime·signalstack(byte *p, int32 n)
+{
+	Sigaltstack st;
+
+	st.ss_sp = (int8*)p;
+	st.ss_size = n;
+	st.ss_flags = 0;
+	runtime·sigaltstack(&st, nil);
+}
+
+static void
+sigaction(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+	Sigaction sa;
+
+	runtime·memclr((byte*)&sa, sizeof sa);
+	sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+	if(restart)
+		sa.sa_flags |= SA_RESTART;
+	sa.sa_mask = ~0ULL;
+	if (fn == runtime·sighandler)
+		fn = (void*)runtime·sigtramp;
+	sa.__sigaction_u.__sa_sigaction = (void*)fn;
+	runtime·sigaction(i, &sa, nil);
+}
+
+void
+runtime·initsig(int32 queue)
+{
+	int32 i;
+	void *fn;
+
+	runtime·siginit();
+
+	for(i = 0; i<NSIG; i++) {
+		if(runtime·sigtab[i].flags) {
+			if((runtime·sigtab[i].flags & SigQueue) != queue)
+				continue;
+			if(runtime·sigtab[i].flags & (SigCatch | SigQueue))
+				fn = runtime·sighandler;
+			else
+				fn = runtime·sigignore;
+			sigaction(i, fn, (runtime·sigtab[i].flags & SigRestart) != 0);
+		}
+	}
+}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+	Itimerval it;
+	
+	runtime·memclr((byte*)&it, sizeof it);
+	if(hz == 0) {
+		runtime·setitimer(ITIMER_PROF, &it, nil);
+		sigaction(SIGPROF, SIG_IGN, true);
+	} else {
+		sigaction(SIGPROF, runtime·sighandler, true);
+		it.it_interval.tv_sec = 0;
+		it.it_interval.tv_usec = 1000000 / hz;
+		it.it_value = it.it_interval;
+		runtime·setitimer(ITIMER_PROF, &it, nil);
+	}
+	m->profilehz = hz;
+}
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/openbsd/386/sys.s b/src/pkg/runtime/openbsd/386/sys.s
new file mode 100644
index 0000000..3a78679
--- /dev/null
+++ b/src/pkg/runtime/openbsd/386/sys.s
@@ -0,0 +1,254 @@
+// 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.
+//
+// System calls and other sys.stuff for 386, OpenBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "386/asm.h"
+
+// Exit the entire program (like C exit)
+TEXT runtime·exit(SB),7,$-4
+	MOVL	$1, AX
+	INT	$0x80
+	CALL	runtime·notok(SB)
+	RET
+
+TEXT runtime·exit1(SB),7,$-4
+	MOVL	$302, AX		// sys_threxit
+	INT	$0x80
+	JAE	2(PC)
+	CALL	runtime·notok(SB)
+	RET
+
+TEXT runtime·write(SB),7,$-4
+	MOVL	$4, AX			// sys_write
+	INT	$0x80
+	RET
+
+TEXT runtime·raisesigpipe(SB),7,$12
+	MOVL	$299, AX		// sys_getthrid
+	INT	$0x80
+	MOVL	$0, 0(SP)
+	MOVL	AX, 4(SP)		// arg 1 - pid
+	MOVL	$13, 8(SP)		// arg 2 - signum == SIGPIPE
+	MOVL	$37, AX			// sys_kill
+	INT	$0x80
+	RET
+
+TEXT runtime·notok(SB),7,$0
+	MOVL	$0xf1, 0xf1
+	RET
+
+TEXT runtime·mmap(SB),7,$36
+	LEAL	arg0+0(FP), SI
+	LEAL	4(SP), DI
+	CLD
+	MOVSL				// arg 1 - addr
+	MOVSL				// arg 2 - len
+	MOVSL				// arg 3 - prot
+	MOVSL				// arg 4 - flags
+	MOVSL				// arg 5 - fd
+	MOVL	$0, AX
+	STOSL				// arg 6 - pad
+	MOVSL				// arg 7 - offset
+	MOVL	$0, AX			// top 64 bits of file offset
+	STOSL
+	MOVL	$197, AX		// sys_mmap
+	INT	$0x80
+	JCC	2(PC)
+	NEGL	AX
+	RET
+
+TEXT runtime·munmap(SB),7,$-4
+	MOVL	$73, AX			// sys_munmap
+	INT	$0x80
+	JAE	2(PC)
+	CALL	runtime·notok(SB)
+	RET
+
+TEXT runtime·setitimer(SB),7,$-4
+	MOVL	$83, AX
+	INT	$0x80
+	RET
+
+TEXT runtime·gettime(SB),7,$32
+	MOVL	$116, AX
+	LEAL	12(SP), BX
+	MOVL	BX, 4(SP)
+	MOVL	$0, 8(SP)
+	INT	$0x80
+
+	MOVL	12(SP), BX		// sec
+	MOVL	sec+0(FP), DI
+	MOVL	BX, (DI)
+	MOVL	$0, 4(DI)		// zero extend 32 -> 64 bits
+
+	MOVL	16(SP), BX		// usec
+	MOVL	usec+4(FP), DI
+	MOVL	BX, (DI)
+	RET
+
+TEXT runtime·sigaction(SB),7,$-4
+	MOVL	$46, AX			// sys_sigaction
+	INT	$0x80
+	JAE	2(PC)
+	CALL	runtime·notok(SB)
+	RET
+
+TEXT runtime·sigtramp(SB),7,$44
+	get_tls(CX)
+
+	// save g
+	MOVL	g(CX), DI
+	MOVL	DI, 20(SP)
+	
+	// g = m->gsignal
+	MOVL	m(CX), BX
+	MOVL	m_gsignal(BX), BX
+	MOVL	BX, g(CX)
+
+	// copy arguments for call to sighandler
+	MOVL	signo+0(FP), BX
+	MOVL	BX, 0(SP)
+	MOVL	info+4(FP), BX
+	MOVL	BX, 4(SP)
+	MOVL	context+8(FP), BX
+	MOVL	BX, 8(SP)
+	MOVL	DI, 12(SP)
+
+	CALL	runtime·sighandler(SB)
+
+	// restore g
+	get_tls(CX)
+	MOVL	20(SP), BX
+	MOVL	BX, g(CX)
+	
+	// call sigreturn
+	MOVL	context+8(FP), AX
+	MOVL	$0, 0(SP)		// syscall gap
+	MOVL	AX, 4(SP)		// arg 1 - sigcontext
+	MOVL	$103, AX		// sys_sigreturn
+	INT	$0x80
+	CALL	runtime·notok(SB)
+	RET
+
+// int32 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
+TEXT runtime·rfork_thread(SB),7,$8
+	MOVL	flags+8(SP), AX
+	MOVL	stack+12(SP), CX
+
+	// Copy m, g, fn off parent stack for use by child.
+	SUBL	$16, CX
+	MOVL	mm+16(SP), SI
+	MOVL	SI, 0(CX)
+	MOVL	gg+20(SP), SI
+	MOVL	SI, 4(CX)
+	MOVL	fn+24(SP), SI
+	MOVL	SI, 8(CX)
+	MOVL	$1234, 12(CX)
+	MOVL	CX, SI
+
+	MOVL	$0, 0(SP)		// syscall gap
+	MOVL	AX, 4(SP)		// arg 1 - flags
+	MOVL	$251, AX		// sys_rfork
+	INT	$0x80
+
+	// Return if rfork syscall failed
+	JCC	4(PC)
+	NEGL	AX
+	MOVL	AX, 48(SP)
+	RET
+
+	// In parent, return.
+	CMPL	AX, $0
+	JEQ	3(PC)
+	MOVL	AX, 48(SP)
+	RET
+
+	// In child, on new stack.
+	MOVL    SI, SP
+
+	// Paranoia: check that SP is as we expect.
+	MOVL	12(SP), BP
+	CMPL	BP, $1234
+	JEQ	2(PC)
+	INT	$3
+
+	// Reload registers
+	MOVL	0(SP), BX		// m
+	MOVL	4(SP), DX		// g
+	MOVL	8(SP), SI		// fn
+
+	// Initialize m->procid to thread ID
+	MOVL	$299, AX		// sys_getthrid
+	INT	$0x80
+	MOVL	AX, m_procid(BX)
+
+	// Set FS to point at m->tls
+	LEAL	m_tls(BX), BP
+	PUSHAL				// save registers
+	PUSHL	BP
+	CALL	runtime·settls(SB)
+	POPL	AX
+	POPAL
+	
+	// Now segment is established.  Initialize m, g.
+	get_tls(AX)
+	MOVL	DX, g(AX)
+	MOVL	BX, m(AX)
+
+	CALL	runtime·stackcheck(SB)	// smashes AX, CX
+	MOVL	0(DX), DX		// paranoia; check they are not nil
+	MOVL	0(BX), BX
+
+	// more paranoia; check that stack splitting code works
+	PUSHAL
+	CALL	runtime·emptyfunc(SB)
+	POPAL
+
+	// Call fn
+	CALL	SI
+
+	CALL	runtime·exit1(SB)
+	MOVL	$0x1234, 0x1005
+	RET
+
+TEXT runtime·sigaltstack(SB),7,$-8
+	MOVL	$288, AX		// sys_sigaltstack
+	MOVL	new+4(SP), BX
+	MOVL	old+8(SP), CX
+	INT	$0x80
+	CMPL	AX, $0xfffff001
+	JLS	2(PC)
+	INT	$3
+	RET
+
+TEXT runtime·setldt(SB),7,$8
+	// Under OpenBSD we set the GS base instead of messing with the LDT.
+	MOVL	16(SP), AX		// tls0
+	MOVL	AX, 0(SP)
+	CALL	runtime·settls(SB)
+	RET
+
+TEXT runtime·settls(SB),7,$16
+	// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
+	MOVL	20(SP), CX
+	ADDL	$8, CX
+	MOVL	CX, 0(CX)
+	MOVL	$0, 0(SP)		// syscall gap
+	MOVL	$9, 4(SP)		// I386_SET_GSBASE (machine/sysarch.h)
+	MOVL	CX, 8(SP)		// pointer to base
+	MOVL	$165, AX		// sys_sysarch
+	INT	$0x80
+	JCC	2(PC)
+	CALL	runtime·notok(SB)
+	RET
+
+TEXT runtime·osyield(SB),7,$-4
+	MOVL	$298, AX		// sys_sched_yield
+	INT	$0x80
+	RET
+
+GLOBL runtime·tlsoffset(SB),$4
diff --git a/src/pkg/runtime/openbsd/amd64/sys.s b/src/pkg/runtime/openbsd/amd64/sys.s
index 2a238df..38b3dbc 100644
--- a/src/pkg/runtime/openbsd/amd64/sys.s
+++ b/src/pkg/runtime/openbsd/amd64/sys.s
@@ -55,10 +55,10 @@ TEXT runtime·rfork_thread(SB),7,$0
 	// It shouldn't return.  If it does, exit
 	MOVL	$302, AX		// sys_threxit
 	SYSCALL
-	JMP	-3(PC)  // keep exiting
+	JMP	-3(PC)			// keep exiting
 
-TEXT runtime·sys_sched_yield(SB),7,$0
-	MOVL $298, AX
+TEXT runtime·osyield(SB),7,$0
+	MOVL $298, AX			// sys_sched_yield
 	SYSCALL
 	RET
 
@@ -123,11 +123,11 @@ TEXT runtime·gettime(SB),7,$32
 	MOVL	$116, AX		// sys_gettimeofday
 	SYSCALL
 
-	MOVQ	8(SP), BX	// sec
+	MOVQ	8(SP), BX		// sec
 	MOVQ	sec+0(FP), DI
 	MOVQ	BX, (DI)
 
-	MOVL	16(SP), BX	// usec
+	MOVL	16(SP), BX		// usec
 	MOVQ	usec+8(FP), DI
 	MOVL	BX, (DI)
 	RET
diff --git a/src/pkg/runtime/openbsd/defs.c b/src/pkg/runtime/openbsd/defs.c
index d8adec9..d0e0a19 100644
--- a/src/pkg/runtime/openbsd/defs.c
+++ b/src/pkg/runtime/openbsd/defs.c
@@ -14,7 +14,6 @@
 #include <sys/time.h>
 #include <sys/unistd.h>
 #include <sys/signal.h>
-#include <machine/mcontext.h>
 #include <errno.h>
 #include <signal.h>
 
@@ -97,7 +96,8 @@ typedef stack_t $StackT;
 typedef struct timeval $Timeval;
 typedef struct itimerval $Itimerval;
 
-// This is a hack to avoid pulling in machine/fpu.h and struct fxsave64.
+// This is a hack to avoid pulling in machine/fpu.h.
 typedef void $sfxsave64;
+typedef void $usavefpu;
 
 typedef struct sigcontext $Sigcontext;
diff --git a/src/pkg/runtime/openbsd/mem.c b/src/pkg/runtime/openbsd/mem.c
index 07abf2c..46b6b07 100644
--- a/src/pkg/runtime/openbsd/mem.c
+++ b/src/pkg/runtime/openbsd/mem.c
@@ -3,6 +3,11 @@
 #include "os.h"
 #include "malloc.h"
 
+enum
+{
+	ENOMEM = 12,
+};
+
 void*
 runtime·SysAlloc(uintptr n)
 {
@@ -33,19 +38,20 @@ runtime·SysFree(void *v, uintptr n)
 void*
 runtime·SysReserve(void *v, uintptr n)
 {
+	void *p;
+
 	// On 64-bit, people with ulimit -v set complain if we reserve too
 	// much address space.  Instead, assume that the reservation is okay
 	// and check the assumption in SysMap.
 	if(sizeof(void*) == 8)
 		return v;
-	
-	return runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
-}
 
-enum
-{
-	ENOMEM = 12,
-};
+	p = runtime·mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+	if (p == ((void *)-ENOMEM))
+		return nil;
+	else
+		return p;
+}
 
 void
 runtime·SysMap(void *v, uintptr n)
diff --git a/src/pkg/runtime/openbsd/thread.c b/src/pkg/runtime/openbsd/thread.c
index 7e9ba5d..909db8c 100644
--- a/src/pkg/runtime/openbsd/thread.c
+++ b/src/pkg/runtime/openbsd/thread.c
@@ -9,19 +9,20 @@
 extern SigTab runtime·sigtab[];
 
 extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-extern void runtime·sys_sched_yield(void);
+
+enum
+{
+	ENOTSUP = 91,
+};
 
 // Basic spinlocks using CAS. We can improve on these later.
 static void
 lock(Lock *l)
 {
-	uint32 v;
-	int32 ret;
-
 	for(;;) {
 		if(runtime·cas(&l->key, 0, 1))
 			return;
-		runtime·sys_sched_yield();
+		runtime·osyield();
 	}
 }
 
@@ -29,7 +30,6 @@ static void
 unlock(Lock *l)
 {
 	uint32 v;
-	int32 ret;
 
 	for (;;) {
 		v = l->key;
@@ -103,8 +103,8 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
 
 	if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) {
 		runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret);
-		runtime·printf("runtime: is kern.rthreads disabled?\n");
-
+		if (ret == -ENOTSUP)
+			runtime·printf("runtime: is kern.rthreads disabled?\n");
 		runtime·throw("runtime.newosproc");
 	}
 }
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 5f396b4..cc07574 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -128,7 +128,7 @@ Sched runtime·sched;
 int32 runtime·gomaxprocs;
 bool runtime·singleproc;
 
-// An m that is waiting for notewakeup(&m->havenextg).  This may be
+// An m that is waiting for notewakeup(&m->havenextg).  This may
 // only be accessed while the scheduler lock is held.  This is used to
 // minimize the number of times we call notewakeup while the scheduler
 // lock is held, since the m will normally move quickly to lock the
@@ -250,6 +250,40 @@ runtime·goexit(void)
 }
 
 void
+runtime·goroutineheader(G *g)
+{
+	int8 *status;
+
+	switch(g->status) {
+	case Gidle:
+		status = "idle";
+		break;
+	case Grunnable:
+		status = "runnable";
+		break;
+	case Grunning:
+		status = "running";
+		break;
+	case Gsyscall:
+		status = "syscall";
+		break;
+	case Gwaiting:
+		if(g->waitreason)
+			status = g->waitreason;
+		else
+			status = "waiting";
+		break;
+	case Gmoribund:
+		status = "moribund";
+		break;
+	default:
+		status = "???";
+		break;
+	}
+	runtime·printf("goroutine %d [%s]:\n", g->goid, status);
+}
+
+void
 runtime·tracebackothers(G *me)
 {
 	G *g;
@@ -257,7 +291,8 @@ runtime·tracebackothers(G *me)
 	for(g = runtime·allg; g != nil; g = g->alllink) {
 		if(g == me || g->status == Gdead)
 			continue;
-		runtime·printf("\ngoroutine %d [%d]:\n", g->goid, g->status);
+		runtime·printf("\n");
+		runtime·goroutineheader(g);
 		runtime·traceback(g->sched.pc, g->sched.sp, 0, g);
 	}
 }
@@ -1073,15 +1108,18 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
 	schedlock();
 
 	if((newg = gfget()) != nil){
-		newg->status = Gwaiting;
 		if(newg->stackguard - StackGuard != newg->stack0)
 			runtime·throw("invalid stack in newg");
 	} else {
 		newg = runtime·malg(StackMin);
-		newg->status = Gwaiting;
-		newg->alllink = runtime·allg;
-		runtime·allg = newg;
+		if(runtime·lastg == nil)
+			runtime·allg = newg;
+		else
+			runtime·lastg->alllink = newg;
+		runtime·lastg = newg;
 	}
+	newg->status = Gwaiting;
+	newg->waitreason = "new goroutine";
 
 	sp = newg->stackbase;
 	sp -= siz;
@@ -1153,7 +1191,8 @@ runtime·deferreturn(uintptr arg0)
 	runtime·memmove(argp, d->args, d->siz);
 	g->defer = d->link;
 	fn = d->fn;
-	runtime·free(d);
+	if(!d->nofree)
+		runtime·free(d);
 	runtime·jmpdefer(fn, argp);
 }
 
@@ -1165,7 +1204,8 @@ rundefer(void)
 	while((d = g->defer) != nil) {
 		g->defer = d->link;
 		reflect·call(d->fn, d->args, d->siz);
-		runtime·free(d);
+		if(!d->nofree)
+			runtime·free(d);
 	}
 }
 
@@ -1245,7 +1285,8 @@ runtime·panic(Eface e)
 			runtime·mcall(recovery);
 			runtime·throw("recovery failed"); // mcall should not return
 		}
-		runtime·free(d);
+		if(!d->nofree)
+			runtime·free(d);
 	}
 
 	// ran out of deferred calls - old-school panic now
@@ -1280,7 +1321,8 @@ recovery(G *gp)
 	else
 		gp->sched.sp = (byte*)d->argp - 2*sizeof(uintptr);
 	gp->sched.pc = d->pc;
-	runtime·free(d);
+	if(!d->nofree)
+		runtime·free(d);
 	runtime·gogo(&gp->sched, 1);
 }
 
@@ -1562,7 +1604,7 @@ os·setenv_c(String k, String v)
 	runtime·memmove(arg[1], v.str, v.len);
 	arg[1][v.len] = 0;
 
-	runtime·asmcgocall(libcgo_setenv, arg);
+	runtime·asmcgocall((void*)libcgo_setenv, arg);
 	runtime·free(arg[0]);
 	runtime·free(arg[1]);
 }
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index 49aba7d..ae6fd87 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -51,12 +51,15 @@ runtime·dopanic(int32 unused)
 	static bool didothers;
 
 	if(g->sig != 0)
-		runtime·printf("\n[signal %x code=%p addr=%p pc=%p]\n",
+		runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
 			g->sig, g->sigcode0, g->sigcode1, g->sigpc);
 
-	runtime·printf("\n");
 	if(runtime·gotraceback()){
-		runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
+		if(g != m->g0) {
+			runtime·printf("\n");
+			runtime·goroutineheader(g);
+			runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
+		}
 		if(!didothers) {
 			didothers = true;
 			runtime·tracebackothers(g);
@@ -635,20 +638,20 @@ runtime·algarray[] =
 {
 [AMEM]	{ memhash, memequal, memprint, memcopy },
 [ANOEQ]	{ runtime·nohash, runtime·noequal, memprint, memcopy },
-[ASTRING]	{ strhash, strequal, strprint, strcopy },
-[AINTER]		{ interhash, interequal, interprint, intercopy },
-[ANILINTER]	{ nilinterhash, nilinterequal, nilinterprint, nilintercopy },
-[ASLICE]	{ runtime·nohash, runtime·noequal, memprint, slicecopy },
-[AMEM8]		{ memhash, memequal8, memprint, memcopy8 },
-[AMEM16]	{ memhash, memequal16, memprint, memcopy16 },
-[AMEM32]	{ memhash, memequal32, memprint, memcopy32 },
-[AMEM64]	{ memhash, memequal64, memprint, memcopy64 },
-[AMEM128]	{ memhash, memequal128, memprint, memcopy128 },
-[ANOEQ8]	{ runtime·nohash, runtime·noequal, memprint, memcopy8 },
-[ANOEQ16]	{ runtime·nohash, runtime·noequal, memprint, memcopy16 },
-[ANOEQ32]	{ runtime·nohash, runtime·noequal, memprint, memcopy32 },
-[ANOEQ64]	{ runtime·nohash, runtime·noequal, memprint, memcopy64 },
-[ANOEQ128]	{ runtime·nohash, runtime·noequal, memprint, memcopy128 },
+[ASTRING]	{ (void*)strhash, (void*)strequal, (void*)strprint, (void*)strcopy },
+[AINTER]		{ (void*)interhash, (void*)interequal, (void*)interprint, (void*)intercopy },
+[ANILINTER]	{ (void*)nilinterhash, (void*)nilinterequal, (void*)nilinterprint, (void*)nilintercopy },
+[ASLICE]	{ (void*)runtime·nohash, (void*)runtime·noequal, (void*)memprint, (void*)slicecopy },
+[AMEM8]		{ memhash, (void*)memequal8, memprint, (void*)memcopy8 },
+[AMEM16]	{ memhash, (void*)memequal16, memprint, (void*)memcopy16 },
+[AMEM32]	{ memhash, (void*)memequal32, memprint, (void*)memcopy32 },
+[AMEM64]	{ memhash, (void*)memequal64, memprint, (void*)memcopy64 },
+[AMEM128]	{ memhash, (void*)memequal128, memprint, (void*)memcopy128 },
+[ANOEQ8]	{ runtime·nohash, runtime·noequal, memprint, (void*)memcopy8 },
+[ANOEQ16]	{ runtime·nohash, runtime·noequal, memprint, (void*)memcopy16 },
+[ANOEQ32]	{ runtime·nohash, runtime·noequal, memprint, (void*)memcopy32 },
+[ANOEQ64]	{ runtime·nohash, runtime·noequal, memprint, (void*)memcopy64 },
+[ANOEQ128]	{ runtime·nohash, runtime·noequal, memprint, (void*)memcopy128 },
 };
 
 int64
@@ -703,7 +706,13 @@ runtime·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool r
 void
 runtime·Callers(int32 skip, Slice pc, int32 retn)
 {
-	retn = runtime·callers(skip, (uintptr*)pc.array, pc.len);
+	// runtime.callers uses pc.array==nil as a signal
+	// to print a stack trace.  Pick off 0-length pc here
+	// so that we don't let a nil pc slice get to it.
+	if(pc.len == 0)
+		retn = 0;
+	else
+		retn = runtime·callers(skip, (uintptr*)pc.array, pc.len);
 	FLUSH(&retn);
 }
 
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 526a320..6feedcb 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -70,6 +70,7 @@ typedef	struct	Hmap		Hmap;
 typedef	struct	Hchan		Hchan;
 typedef	struct	Complex64	Complex64;
 typedef	struct	Complex128	Complex128;
+typedef	struct	WinCall		WinCall;
 
 /*
  * per-cpu declaration.
@@ -199,6 +200,7 @@ struct	G
 	int16	status;
 	int32	goid;
 	uint32	selgen;		// valid sudog pointer
+	int8*	waitreason;	// if status==Gwaiting
 	G*	schedlink;
 	bool	readyonstop;
 	bool	ispanic;
@@ -249,11 +251,6 @@ struct	M
 	uint32	freglo[16];	// D[i] lsb and F[i]
 	uint32	freghi[16];	// D[i] msb and F[i+16]
 	uint32	fflag;		// floating point compare flags
-#ifdef __WINDOWS__
-#ifdef _64BIT	
-	void*	gostack;
-#endif
-#endif
 };
 
 struct	Stktop
@@ -305,6 +302,15 @@ struct	Func
 	int32	locals;	// number of 32-bit locals
 };
 
+struct	WinCall
+{
+	void	(*fn)(void*);
+	uintptr	n;	// number of parameters
+	void*	args;	// parameters
+	uintptr	r;	// return value
+	uintptr	err;	// error number
+};
+
 #ifdef __WINDOWS__
 enum {
    Windows = 1
@@ -359,6 +365,7 @@ enum {
 struct Defer
 {
 	int32	siz;
+	bool	nofree;
 	byte*	argp;  // where args were copied from
 	byte*	pc;
 	byte*	fn;
@@ -383,6 +390,7 @@ struct Panic
 extern	Alg	runtime·algarray[Amax];
 extern	String	runtime·emptystring;
 G*	runtime·allg;
+G*	runtime·lastg;
 M*	runtime·allm;
 extern	int32	runtime·gomaxprocs;
 extern	bool	runtime·singleproc;
@@ -433,6 +441,7 @@ String	runtime·gostringnocopy(byte*);
 String	runtime·gostringw(uint16*);
 void	runtime·initsig(int32);
 int32	runtime·gotraceback(void);
+void	runtime·goroutineheader(G*);
 void	runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp);
 void	runtime·tracebackothers(G*);
 int32	runtime·write(int32, void*, int32);
@@ -626,10 +635,11 @@ void	runtime·mapiterkeyvalue(struct hash_iter*, void*, void*);
 Hmap*	runtime·makemap_c(MapType*, int64);
 
 Hchan*	runtime·makechan_c(ChanType*, int64);
-void	runtime·chansend(ChanType*, Hchan*, void*, bool*);
-void	runtime·chanrecv(ChanType*, Hchan*, void*, bool*, bool*);
+void	runtime·chansend(ChanType*, Hchan*, byte*, bool*);
+void	runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*);
 int32	runtime·chanlen(Hchan*);
 int32	runtime·chancap(Hchan*);
+bool	runtime·showframe(Func*);
 
 void	runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*);
 
diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc
index ae84351..d202a9d 100644
--- a/src/pkg/runtime/sema.goc
+++ b/src/pkg/runtime/sema.goc
@@ -129,6 +129,7 @@ runtime·semacquire(uint32 volatile *addr)
 		// (we set nwait above), so go to sleep.
 		semqueue(root, addr, &s);
 		g->status = Gwaiting;
+		g->waitreason = "semacquire";
 		runtime·unlock(root);
 		runtime·gosched();
 		if(cansemacquire(addr))
diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc
index 48bf318..322706c 100644
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -80,6 +80,8 @@ runtime·gobytes(byte *p, int32 n)
 	Slice sl;
 
 	sl.array = runtime·mallocgc(n, FlagNoPointers, 1, 0);
+	sl.len = n;
+	sl.cap = n;
 	runtime·memmove(sl.array, p, n);
 	return sl;
 }
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
index d2ebf9b..d0468c6 100644
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -464,3 +464,40 @@ runtime·findfunc(uintptr addr)
 	runtime·prints("findfunc unreachable\n");
 	return nil;
 }
+
+static bool
+hasprefix(String s, int8 *p)
+{
+	int32 i;
+	
+	for(i=0; i<s.len; i++) {
+		if(p[i] == 0)
+			return 1;
+		if(p[i] != s.str[i])
+			return 0;
+	}
+	return p[i] == 0;
+}
+
+static bool
+contains(String s, int8 *p)
+{
+	int32 i;
+	
+	if(p[0] == 0)
+		return 1;
+	for(i=0; i<s.len; i++) {
+		if(s.str[i] != p[0])
+			continue;
+		if(hasprefix((String){s.str + i, s.len - i}, p))
+			return 1;
+	}
+	return 0;
+}
+
+bool
+runtime·showframe(Func *f)
+{
+	// return 1;  // for debugging - show all frames
+	return contains(f->name, ".") && !hasprefix(f->name, "runtime.");
+}
diff --git a/src/pkg/runtime/syscall_windows_test.go b/src/pkg/runtime/syscall_windows_test.go
new file mode 100644
index 0000000..c270607
--- /dev/null
+++ b/src/pkg/runtime/syscall_windows_test.go
@@ -0,0 +1,99 @@
+// 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 runtime_test
+
+import (
+	"syscall"
+	"unsafe"
+	"testing"
+)
+
+func TestStdCall(t *testing.T) {
+	type Rect struct {
+		left, top, right, bottom int32
+	}
+
+	h, e := syscall.LoadLibrary("user32.dll")
+	if e != 0 {
+		t.Fatal("LoadLibrary(USER32)")
+	}
+	p, e := syscall.GetProcAddress(h, "UnionRect")
+	if e != 0 {
+		t.Fatal("GetProcAddress(USER32.UnionRect)")
+	}
+
+	res := Rect{}
+	expected := Rect{1, 1, 40, 60}
+	a, _, _ := syscall.Syscall(uintptr(p),
+		3,
+		uintptr(unsafe.Pointer(&res)),
+		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
+		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
+	if a != 1 || res.left != expected.left ||
+		res.top != expected.top ||
+		res.right != expected.right ||
+		res.bottom != expected.bottom {
+		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
+	}
+}
+
+func TestCDecl(t *testing.T) {
+	h, e := syscall.LoadLibrary("user32.dll")
+	if e != 0 {
+		t.Fatal("LoadLibrary(USER32)")
+	}
+	p, e := syscall.GetProcAddress(h, "wsprintfA")
+	if e != 0 {
+		t.Fatal("GetProcAddress(USER32.wsprintfA)")
+	}
+
+	var buf [50]byte
+	a, _, _ := syscall.Syscall6(uintptr(p),
+		5,
+		uintptr(unsafe.Pointer(&buf[0])),
+		uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))),
+		1000, 2000, 3000, 0)
+	if string(buf[:a]) != "1000 2000 3000" {
+		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
+	}
+}
+
+func TestCallback(t *testing.T) {
+	h, e := syscall.LoadLibrary("user32.dll")
+	if e != 0 {
+		t.Fatal("LoadLibrary(USER32)")
+	}
+	pEnumWindows, e := syscall.GetProcAddress(h, "EnumWindows")
+	if e != 0 {
+		t.Fatal("GetProcAddress(USER32.EnumWindows)")
+	}
+	pIsWindow, e := syscall.GetProcAddress(h, "IsWindow")
+	if e != 0 {
+		t.Fatal("GetProcAddress(USER32.IsWindow)")
+	}
+	counter := 0
+	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
+		if lparam != 888 {
+			t.Error("lparam was not passed to callback")
+		}
+		b, _, _ := syscall.Syscall(uintptr(pIsWindow), 1, uintptr(hwnd), 0, 0)
+		if b == 0 {
+			t.Error("USER32.IsWindow returns FALSE")
+		}
+		counter++
+		return 1 // continue enumeration
+	})
+	a, _, _ := syscall.Syscall(uintptr(pEnumWindows), 2, cb, 888, 0)
+	if a == 0 {
+		t.Error("USER32.EnumWindows returns FALSE")
+	}
+	if counter == 0 {
+		t.Error("Callback has been never called or your have no windows")
+	}
+}
+
+func TestCallbackInAnotherThread(t *testing.T) {
+	// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
+}
diff --git a/src/pkg/runtime/windows/386/callback.c b/src/pkg/runtime/windows/386/callback.c
new file mode 100644
index 0000000..11b3d29
--- /dev/null
+++ b/src/pkg/runtime/windows/386/callback.c
@@ -0,0 +1,107 @@
+// 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.
+
+#include "runtime.h"
+#include "type.h"
+#include "defs.h"
+#include "os.h"
+
+// Will keep all callbacks in a linked list, so they don't get garbage collected.
+typedef	struct	Callback	Callback;
+struct	Callback {
+	Callback*	link;
+	void*		gobody;
+	byte		asmbody;
+};
+
+typedef	struct	Callbacks	Callbacks;
+struct	Callbacks {
+	Lock;
+	Callback*	link;
+	int32		n;
+};
+
+static	Callbacks	cbs;
+
+// Call back from windows dll into go.
+byte *
+runtime·compilecallback(Eface fn, bool cleanstack)
+{
+	FuncType *ft;
+	Type *t;
+	int32 argsize, i, n;
+	byte *p;
+	Callback *c;
+
+	if(fn.type == nil || fn.type->kind != KindFunc)
+		runtime·panicstring("compilecallback: not a function");
+	ft = (FuncType*)fn.type;
+	if(ft->out.len != 1)
+		runtime·panicstring("compilecallback: function must have one output parameter");
+	if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
+		runtime·panicstring("compilecallback: output parameter size is wrong");
+	argsize = 0;
+	for(i=0; i<ft->in.len; i++) {
+		t = ((Type**)ft->in.array)[i];
+		if(t->size > sizeof(uintptr))
+			runtime·panicstring("compilecallback: input parameter size is wrong");
+		argsize += sizeof(uintptr);
+	}
+
+	// compute size of new fn.
+	// must match code laid out below.
+	n = 1+4;		// MOVL fn, AX
+	n += 1+4;		// MOVL argsize, DX
+	n += 1+4;		// MOVL callbackasm, CX
+	n += 2;			// CALL CX
+	n += 1;			// RET
+	if(cleanstack && argsize!=0)
+		n += 2;		// ... argsize
+
+	runtime·lock(&cbs);
+	for(c = cbs.link; c != nil; c = c->link) {
+		if(c->gobody == fn.data) {
+			runtime·unlock(&cbs);
+			return &c->asmbody;
+		}
+	}
+	if(cbs.n >= 2000)
+		runtime·throw("too many callback functions");
+	c = runtime·mal(sizeof *c + n);
+	c->gobody = fn.data;
+	c->link = cbs.link;
+	cbs.link = c;
+	cbs.n++;
+	runtime·unlock(&cbs);
+
+	p = &c->asmbody;
+
+	// MOVL fn, AX
+	*p++ = 0xb8;
+	*(uint32*)p = (uint32)fn.data;
+	p += 4;
+
+	// MOVL argsize, DX
+	*p++ = 0xba;
+	*(uint32*)p = argsize;
+	p += 4;
+
+	// MOVL callbackasm, CX
+	*p++ = 0xb9;
+	*(uint32*)p = (uint32)runtime·callbackasm;
+	p += 4;
+
+	// CALL CX
+	*p++ = 0xff;
+	*p++ = 0xd1;
+
+	// RET argsize?
+	if(cleanstack && argsize!=0) {
+		*p++ = 0xc2;
+		*(uint16*)p = argsize;
+	} else
+		*p = 0xc3;
+
+	return &c->asmbody;
+}
diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s
index 703f77d..94aed83 100644
--- a/src/pkg/runtime/windows/386/sys.s
+++ b/src/pkg/runtime/windows/386/sys.s
@@ -4,44 +4,37 @@
 
 #include "386/asm.h"
 
-// void *stdcall_raw(void *fn, int32 count, uintptr *args)
-TEXT runtime·stdcall_raw(SB),7,$0
-	// Copy arguments from stack.
-	MOVL	fn+0(FP), AX
-	MOVL	count+4(FP), CX		// words
-	MOVL	args+8(FP), BP
-
-	// Switch to m->g0 if needed.
-	get_tls(DI)
-	MOVL	m(DI), DX
-	MOVL	m_g0(DX), SI
-	CMPL	g(DI), SI
-	MOVL	SP, BX
-	JEQ	2(PC)
-	MOVL	(g_sched+gobuf_sp)(SI), SP
-	PUSHL	BX
-	PUSHL	g(DI)
-	MOVL	SI, g(DI)
+// void runtime·asmstdcall(void *c);
+TEXT runtime·asmstdcall(SB),7,$0
+	MOVL	c+0(FP), DX
+
+	// SetLastError(0).
+	MOVL	$0, 0x34(FS)
 
-	// Copy args to new stack.
+	// Copy args to the stack.
+	MOVL	SP, BP
+	MOVL	wincall_n(DX), CX	// words
 	MOVL	CX, BX
 	SALL	$2, BX
 	SUBL	BX, SP			// room for args
 	MOVL	SP, DI
-	MOVL	BP, SI
+	MOVL	wincall_args(DX), SI
 	CLD
 	REP; MOVSL
 
-	// Call stdcall function.
+	// Call stdcall or cdecl function.
+	// DI SI BP BX are preserved, SP is not
+	MOVL	wincall_fn(DX), AX
 	CALL	AX
+	MOVL	BP, SP
 
-	// Restore original SP, g.
-	get_tls(DI)
-	POPL	g(DI)
-	POPL	SP
+	// Return result.
+	MOVL	c+0(FP), DX
+	MOVL	AX, wincall_r(DX)
 
-	// Someday the convention will be D is always cleared.
-	CLD
+	// GetLastError().
+	MOVL	0x34(FS), BX
+	MOVL	BX, wincall_err(DX)
 
 	RET
 
diff --git a/src/pkg/runtime/windows/amd64/callback.c b/src/pkg/runtime/windows/amd64/callback.c
new file mode 100644
index 0000000..d53822e
--- /dev/null
+++ b/src/pkg/runtime/windows/amd64/callback.c
@@ -0,0 +1,104 @@
+// 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.
+
+#include "runtime.h"
+#include "type.h"
+#include "defs.h"
+#include "os.h"
+
+// Will keep all callbacks in a linked list, so they don't get garbage collected.
+typedef	struct	Callback	Callback;
+struct	Callback {
+	Callback*	link;
+	void*		gobody;
+	byte		asmbody;
+};
+
+typedef	struct	Callbacks	Callbacks;
+struct	Callbacks {
+	Lock;
+	Callback*	link;
+	int32		n;
+};
+
+static	Callbacks	cbs;
+
+// Call back from windows dll into go.
+byte *
+runtime·compilecallback(Eface fn, bool /*cleanstack*/)
+{
+	FuncType *ft;
+	Type *t;
+	int32 argsize, i, n;
+	byte *p;
+	Callback *c;
+
+	if(fn.type == nil || fn.type->kind != KindFunc)
+		runtime·panicstring("compilecallback: not a function");
+	ft = (FuncType*)fn.type;
+	if(ft->out.len != 1)
+		runtime·panicstring("compilecallback: function must have one output parameter");
+	if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
+		runtime·panicstring("compilecallback: output parameter size is wrong");
+	argsize = 0;
+	for(i=0; i<ft->in.len; i++) {
+		t = ((Type**)ft->in.array)[i];
+		if(t->size > sizeof(uintptr))
+			runtime·panicstring("compilecallback: input parameter size is wrong");
+		argsize += sizeof(uintptr);
+	}
+
+	// compute size of new fn.
+	// must match code laid out below.
+	n  = 2+8+1; // MOVQ fn, AX           / PUSHQ AX
+	n += 2+8+1; // MOVQ argsize, AX      / PUSHQ AX
+	n += 2+8;   // MOVQ callbackasm, AX
+	n += 2;     // JMP  AX
+
+	runtime·lock(&cbs);
+	for(c = cbs.link; c != nil; c = c->link) {
+		if(c->gobody == fn.data) {
+			runtime·unlock(&cbs);
+			return &c->asmbody;
+		}
+	}
+	if(cbs.n >= 2000)
+		runtime·throw("too many callback functions");
+	c = runtime·mal(sizeof *c + n);
+	c->gobody = fn.data;
+	c->link = cbs.link;
+	cbs.link = c;
+	cbs.n++;
+	runtime·unlock(&cbs);
+
+	p = &c->asmbody;
+
+	// MOVQ fn, AX
+	*p++ = 0x48;
+	*p++ = 0xb8;
+	*(uint64*)p = (uint64)fn.data;
+	p += 8;
+	// PUSH AX
+	*p++ = 0x50;
+
+	// MOVQ argsize, AX
+	*p++ = 0x48;
+	*p++ = 0xb8;
+	*(uint64*)p = argsize;
+	p += 8;
+	// PUSH AX
+	*p++ = 0x50;
+
+	// MOVQ callbackasm, AX
+	*p++ = 0x48;
+	*p++ = 0xb8;
+	*(uint64*)p = (uint64)runtime·callbackasm;
+	p += 8;
+
+	// JMP AX
+	*p++ = 0xFF;
+	*p++ = 0xE0;
+
+	return &c->asmbody;
+}
diff --git a/src/pkg/runtime/windows/amd64/defs.h b/src/pkg/runtime/windows/amd64/defs.h
index 830c6a8..30c66df 100644
--- a/src/pkg/runtime/windows/amd64/defs.h
+++ b/src/pkg/runtime/windows/amd64/defs.h
@@ -1,4 +1,4 @@
-// g:\opensource\go\bin\godefs.exe -f -m64 defs.c
+// c:\go\bin\godefs.exe -f -m64 defs.c
 
 // MACHINE GENERATED - DO NOT EDIT.
 
@@ -37,4 +37,60 @@ struct ExceptionRecord {
 	byte pad_godefs_0[4];
 	uint64 ExceptionInformation[15];
 };
+
+typedef struct M128a M128a;
+struct M128a {
+	uint64 Low;
+	int64 High;
+};
+
+typedef struct Context Context;
+struct Context {
+	uint64 P1Home;
+	uint64 P2Home;
+	uint64 P3Home;
+	uint64 P4Home;
+	uint64 P5Home;
+	uint64 P6Home;
+	uint32 ContextFlags;
+	uint32 MxCsr;
+	uint16 SegCs;
+	uint16 SegDs;
+	uint16 SegEs;
+	uint16 SegFs;
+	uint16 SegGs;
+	uint16 SegSs;
+	uint32 EFlags;
+	uint64 Dr0;
+	uint64 Dr1;
+	uint64 Dr2;
+	uint64 Dr3;
+	uint64 Dr6;
+	uint64 Dr7;
+	uint64 Rax;
+	uint64 Rcx;
+	uint64 Rdx;
+	uint64 Rbx;
+	uint64 Rsp;
+	uint64 Rbp;
+	uint64 Rsi;
+	uint64 Rdi;
+	uint64 R8;
+	uint64 R9;
+	uint64 R10;
+	uint64 R11;
+	uint64 R12;
+	uint64 R13;
+	uint64 R14;
+	uint64 R15;
+	uint64 Rip;
+	byte Pad_godefs_0[512];
+	M128a VectorRegister[26];
+	uint64 VectorControl;
+	uint64 DebugControl;
+	uint64 LastBranchToRip;
+	uint64 LastBranchFromRip;
+	uint64 LastExceptionToRip;
+	uint64 LastExceptionFromRip;
+};
 #pragma pack off
diff --git a/src/pkg/runtime/windows/amd64/signal.c b/src/pkg/runtime/windows/amd64/signal.c
index 1fc3eb0..1e621b7 100644
--- a/src/pkg/runtime/windows/amd64/signal.c
+++ b/src/pkg/runtime/windows/amd64/signal.c
@@ -6,9 +6,97 @@
 #include "defs.h"
 #include "os.h"
 
+extern void *runtime·sigtramp;
+
+void
+runtime·dumpregs(Context *r)
+{
+	runtime·printf("rax     %X\n", r->Rax);
+	runtime·printf("rbx     %X\n", r->Rbx);
+	runtime·printf("rcx     %X\n", r->Rcx);
+	runtime·printf("rdx     %X\n", r->Rdx);
+	runtime·printf("rdi     %X\n", r->Rdi);
+	runtime·printf("rsi     %X\n", r->Rsi);
+	runtime·printf("rbp     %X\n", r->Rbp);
+	runtime·printf("rsp     %X\n", r->Rsp);
+	runtime·printf("r8      %X\n", r->R8 );
+	runtime·printf("r9      %X\n", r->R9 );
+	runtime·printf("r10     %X\n", r->R10);
+	runtime·printf("r11     %X\n", r->R11);
+	runtime·printf("r12     %X\n", r->R12);
+	runtime·printf("r13     %X\n", r->R13);
+	runtime·printf("r14     %X\n", r->R14);
+	runtime·printf("r15     %X\n", r->R15);
+	runtime·printf("rip     %X\n", r->Rip);
+	runtime·printf("rflags  %X\n", r->EFlags);
+	runtime·printf("cs      %X\n", (uint64)r->SegCs);
+	runtime·printf("fs      %X\n", (uint64)r->SegFs);
+	runtime·printf("gs      %X\n", (uint64)r->SegGs);
+}
+
 void
-runtime·initsig(int32 queue)
+runtime·initsig(int32)
+{
+	runtime·siginit();
+	// following line keeps sigtramp alive at link stage
+	// if there's a better way please write it here
+	void *p = runtime·sigtramp;
+	USED(p);
+}
+
+uint32
+runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
 {
+	uintptr *sp;
+
+	switch(info->ExceptionCode) {
+	case EXCEPTION_BREAKPOINT:
+		return 1;
+	}
+
+	if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
+		// Make it look like a call to the signal func.
+		// Have to pass arguments out of band since
+		// augmenting the stack frame would break
+		// the unwinding code.
+		gp->sig = info->ExceptionCode;
+		gp->sigcode0 = info->ExceptionInformation[0];
+		gp->sigcode1 = info->ExceptionInformation[1];
+		gp->sigpc = r->Rip;
+
+		// Only push runtime·sigpanic if r->rip != 0.
+		// If r->rip == 0, probably panicked because of a
+		// call to a nil func.  Not pushing that onto sp will
+		// make the trace look like a call to runtime·sigpanic instead.
+		// (Otherwise the trace will end at runtime·sigpanic and we
+		// won't get to see who faulted.)
+		if(r->Rip != 0) {
+			sp = (uintptr*)r->Rsp;
+			*--sp = r->Rip;
+			r->Rsp = (uintptr)sp;
+		}
+		r->Rip = (uintptr)runtime·sigpanic;
+		return 0;
+	}
+
+	if(runtime·panicking)	// traceback already printed
+		runtime·exit(2);
+	runtime·panicking = 1;
+
+	runtime·printf("Exception %x %p %p\n", info->ExceptionCode,
+		info->ExceptionInformation[0], info->ExceptionInformation[1]);
+
+	runtime·printf("PC=%X\n", r->Rip);
+	runtime·printf("\n");
+
+	if(runtime·gotraceback()){
+		runtime·traceback((void*)r->Rip, (void*)r->Rsp, 0, gp);
+		runtime·tracebackothers(gp);
+		runtime·dumpregs(r);
+	}
+
+	runtime·exit(2);
+	return 0;
 }
 
 void
diff --git a/src/pkg/runtime/windows/amd64/sys.s b/src/pkg/runtime/windows/amd64/sys.s
index 2009d16..9b4a17e 100644
--- a/src/pkg/runtime/windows/amd64/sys.s
+++ b/src/pkg/runtime/windows/amd64/sys.s
@@ -4,46 +4,57 @@
 
 #include "amd64/asm.h"
 
-// void *stdcall_raw(void *fn, uintptr nargs, void *args)
-TEXT runtime·stdcall_raw(SB),7,$8
-	MOVQ	fn+0(FP), AX
-	MOVQ	nargs+8(FP), CX
-	MOVQ	args+16(FP), R11
-
-	// Switch to m->g0 if needed.
-	get_tls(DI)
-	MOVQ	m(DI), DX
-	MOVQ	g(DI), SI
-	MOVQ	SI, 0(SP)		// save g
-	MOVQ	SP, m_gostack(DX)	// save SP
-	MOVQ	m_g0(DX), SI
-	CMPQ	g(DI), SI
-	JEQ 3(PC)
-	MOVQ	(g_sched+gobuf_sp)(SI), SP
-	ANDQ	$~15, SP
-	MOVQ	SI, g(DI)
-	
-	SUBQ	$0x60, SP
-	
-	// Copy args to new stack.
+#define maxargs 12
+
+// void runtime·asmstdcall(void *c);
+TEXT runtime·asmstdcall(SB),7,$0
+	// asmcgocall will put first argument into CX.
+	PUSHQ	CX			// save for later
+	MOVQ	wincall_fn(CX), AX
+	MOVQ	wincall_args(CX), SI
+	MOVQ	wincall_n(CX), CX
+
+	// SetLastError(0).
+	MOVQ	0x30(GS), DI
+	MOVL	$0, 0x68(DI)
+
+	SUBQ	$(maxargs*8), SP	// room for args
+
+	// Fast version, do not store args on the stack.
+	CMPL	CX, $4
+	JLE	loadregs
+
+	// Check we have enough room for args.
+	CMPL	CX, $maxargs
+	JLE	2(PC)
+	INT	$3			// not enough room -> crash
+
+	// Copy args to the stack.
 	MOVQ	SP, DI
-	MOVQ	R11, SI
 	CLD
 	REP; MOVSQ
-	MOVQ	0(R11), CX
-	MOVQ	8(R11), DX
-	MOVQ	16(R11), R8
-	MOVQ	24(R11), R9
+	MOVQ	SP, SI
+
+loadregs:
+	// Load first 4 args into correspondent registers.
+	MOVQ	0(SI), CX
+	MOVQ	8(SI), DX
+	MOVQ	16(SI), R8
+	MOVQ	24(SI), R9
 
 	// Call stdcall function.
 	CALL	AX
-	
-	// Restore original SP, g.
-	get_tls(DI)
-	MOVQ	m(DI), DX
-	MOVQ	m_gostack(DX), SP	// restore SP
-	MOVQ	0(SP), SI		// restore g
-	MOVQ	SI, g(DI)
+
+	ADDQ	$(maxargs*8), SP
+
+	// Return result.
+	POPQ	CX
+	MOVQ	AX, wincall_r(CX)
+
+	// GetLastError().
+	MOVQ	0x30(GS), DI
+	MOVL	0x68(DI), AX
+	MOVQ	AX, wincall_err(CX)
 
 	RET
 
@@ -59,26 +70,164 @@ TEXT runtime·setlasterror(SB),7,$0
 	MOVL	AX, 0x68(CX)
 	RET
 
+TEXT runtime·sigtramp(SB),7,$56
+	// CX: exception record
+	// R8: context
+
+	// unwinding?
+	TESTL	$6, 4(CX)		// exception flags
+	MOVL	$1, AX
+	JNZ	sigdone
+
+	// copy arguments for call to sighandler
+	MOVQ	CX, 0(SP)
+	MOVQ	R8, 8(SP)
+	get_tls(CX)
+	MOVQ	g(CX), CX
+	MOVQ	CX, 16(SP)
+
+	MOVQ	BX, 24(SP)
+	MOVQ	BP, 32(SP)
+	MOVQ	SI, 40(SP)
+	MOVQ	DI, 48(SP)
+
+	CALL	runtime·sighandler(SB)
+
+	MOVQ	24(SP), BX
+	MOVQ	32(SP), BP
+	MOVQ	40(SP), SI
+	MOVQ	48(SP), DI
+sigdone:
+	RET
+
 // Windows runs the ctrl handler in a new thread.
 TEXT runtime·ctrlhandler(SB),7,$0
-	// TODO
+	PUSHQ	BP
+	MOVQ	SP, BP
+	PUSHQ	BX
+	PUSHQ	SI
+	PUSHQ	DI
+	PUSHQ	0x58(GS)
+	MOVQ	SP, BX
+
+	// setup dummy m, g
+	SUBQ	$(m_fflag+4), SP	// at least space for m_fflag
+	LEAQ	m_tls(SP), CX
+	MOVQ	CX, 0x58(GS)
+	MOVQ	SP, m(CX)
+	MOVQ	SP, DX
+	SUBQ	$16, SP			// space for g_stack{guard,base}
+	MOVQ	SP, g(CX)
+	MOVQ	SP, m_g0(DX)
+	LEAQ	-8192(SP), CX
+	MOVQ	CX, g_stackguard(SP)
+	MOVQ	BX, g_stackbase(SP)
+
+	PUSHQ	16(BP)
+	CALL	runtime·ctrlhandler1(SB)
+	POPQ	CX
+
+	get_tls(CX)
+	MOVQ	g(CX), CX
+	MOVQ	g_stackbase(CX), SP
+	POPQ	0x58(GS)
+	POPQ	DI
+	POPQ	SI
+	POPQ	BX
+	POPQ	BP
 	RET
-	
+
+// Continuation of thunk function created for each callback by ../thread.c compilecallback,
+// runs on Windows stack (not Go stack).
+// Thunk code designed to have minimal size for it is copied many (up to thousands) times.
+//
+// thunk:
+//	MOVQ	$fn, AX
+//	PUSHQ	AX
+//	MOVQ	$argsize, AX
+//	PUSHQ	AX
+//	MOVQ	$runtime·callbackasm, AX
+//	JMP	AX
 TEXT runtime·callbackasm(SB),7,$0
-	// TODO
+	// Construct args vector for cgocallback().
+	// By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
+	// args from the 5th on are on the stack.
+	// In any case, even if function has 0,1,2,3,4 args, there is reserved
+	// but uninitialized "shadow space" for the first 4 args.
+	// The values are in registers.
+  	MOVQ	CX, (24+0)(SP)
+  	MOVQ	DX, (24+8)(SP)
+  	MOVQ	R8, (24+16)(SP)
+  	MOVQ	R9, (24+24)(SP)
+	// 6l does not accept writing POPQs here issuing a warning "unbalanced PUSH/POP"
+  	MOVQ	0(SP), DX	// POPQ DX
+  	MOVQ	8(SP), AX	// POPQ AX
+	ADDQ	$16, SP
+
+	// preserve whatever's at the memory location that
+	// the callback will use to store the return value
+	LEAQ	8(SP), CX       // args vector, skip return address
+	PUSHQ	0(CX)(DX*1)     // store 8 bytes from just after the args array
+	ADDQ	$8, DX          // extend argsize by size of return value
+
+	// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
+	// as required by windows callback convention.
+	// 6l does not allow writing many PUSHQs here issuing a warning "nosplit stack overflow"
+	// the warning has no sense as this code uses os thread stack
+	PUSHFQ
+	SUBQ	$64, SP
+	MOVQ	DI, 56(SP)
+	MOVQ	SI, 48(SP)
+	MOVQ	BP, 40(SP)
+	MOVQ	BX, 32(SP)
+	MOVQ	R12, 24(SP)
+	MOVQ	R13, 16(SP)
+	MOVQ	R14, 8(SP)
+	MOVQ	R15, 0(SP)
+
+	// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+	PUSHQ	DX    // uintptr framesize
+	PUSHQ	CX    // void *frame
+	PUSHQ	AX    // void (*fn)(void*)
+	CLD
+	CALL  runtime·cgocallback(SB)
+	POPQ	AX
+	POPQ	CX
+	POPQ	DX
+
+	// restore registers as required for windows callback
+	// 6l does not allow writing many POPs here issuing a warning "nosplit stack overflow"
+	MOVQ	0(SP), R15
+	MOVQ	8(SP), R14
+	MOVQ	16(SP), R13
+	MOVQ	24(SP), R12
+	MOVQ	32(SP), BX
+	MOVQ	40(SP), BP
+	MOVQ	48(SP), SI
+	MOVQ	56(SP), DI
+	ADDQ	$64, SP
+	POPFQ
+
+	MOVL	-8(CX)(DX*1), AX  // return value
+	POPQ	-8(CX)(DX*1)      // restore bytes just after the args
 	RET
 
-// void tstart(M *newm);
-TEXT runtime·tstart(SB),7,$0
-	MOVQ	newm+8(SP), CX		// m
-	MOVQ	m_g0(CX), DX		// g
+TEXT runtime·setstacklimits(SB),7,$0
+	MOVQ	0x30(GS), CX
+	MOVQ	$0, 0x10(CX)
+	MOVQ	$0xffffffffffff, AX
+	MOVQ	AX, 0x08(CX)
+	RET
 
-	MOVQ	SP, DI			// remember stack
+// uint32 tstart_stdcall(M *newm);
+TEXT runtime·tstart_stdcall(SB),7,$0
+	// CX contains first arg newm
+	MOVQ	m_g0(CX), DX		// g
 
 	// Layout new m scheduler stack on os stack.
 	MOVQ	SP, AX
 	MOVQ	AX, g_stackbase(DX)
-	SUBQ	$(64*1024), AX	// stack size
+	SUBQ	$(64*1024), AX		// stack size
 	MOVQ	AX, g_stackguard(DX)
 
 	// Set up tls.
@@ -90,32 +239,11 @@ TEXT runtime·tstart(SB),7,$0
 	// Someday the convention will be D is always cleared.
 	CLD
 
-	PUSHQ	DI			// original stack
-
-	CALL	runtime·stackcheck(SB)		// clobbers AX,CX
-
+	CALL	runtime·setstacklimits(SB)
+	CALL	runtime·stackcheck(SB)	// clobbers AX,CX
 	CALL	runtime·mstart(SB)
 
-	POPQ	DI			// original stack
-	MOVQ	DI, SP
-	
-	RET
-
-// uint32 tstart_stdcall(M *newm);
-TEXT runtime·tstart_stdcall(SB),7,$0
-	MOVQ CX, BX // stdcall first arg in RCX
-
-	PUSHQ	BX
-	CALL	runtime·tstart+0(SB)
-	POPQ	BX
-
-	// Adjust stack for stdcall to return properly.
-	MOVQ	(SP), AX		// save return address
-	ADDQ	$8, SP			// remove single parameter
-	MOVQ	AX, (SP)		// restore return address
-
 	XORL	AX, AX			// return 0 == success
-
 	RET
 
 TEXT runtime·notok(SB),7,$0
@@ -125,6 +253,6 @@ TEXT runtime·notok(SB),7,$0
 
 // set tls base to DI
 TEXT runtime·settls(SB),7,$0
+	CALL	runtime·setstacklimits(SB)
 	MOVQ	DI, 0x58(GS)
 	RET
-
diff --git a/src/pkg/runtime/windows/defs.c b/src/pkg/runtime/windows/defs.c
index 3b28249..b076afd 100644
--- a/src/pkg/runtime/windows/defs.c
+++ b/src/pkg/runtime/windows/defs.c
@@ -33,5 +33,10 @@ enum {
 };
 
 typedef EXCEPTION_RECORD $ExceptionRecord;
+#ifdef _X86_
 typedef FLOATING_SAVE_AREA $FloatingSaveArea;
+#endif
+#ifdef _AMD64_
+typedef M128A $M128a;
+#endif
 typedef CONTEXT $Context;
diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h
index bc96787..a8cc299 100644
--- a/src/pkg/runtime/windows/os.h
+++ b/src/pkg/runtime/windows/os.h
@@ -10,7 +10,7 @@ extern void *runtime·GetProcAddress;
 #pragma	varargck	countpos	runtime·stdcall	2
 #pragma	varargck	type		runtime·stdcall	void*
 #pragma	varargck	type		runtime·stdcall	uintptr
-void *runtime·stdcall_raw(void *fn, uintptr nargs, void *args);
+void runtime·asmstdcall(void *c);
 void *runtime·stdcall(void *fn, int32 count, ...);
 uintptr runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err);
 
diff --git a/src/pkg/runtime/windows/syscall.goc b/src/pkg/runtime/windows/syscall.goc
index 85071e0..4777a61 100644
--- a/src/pkg/runtime/windows/syscall.goc
+++ b/src/pkg/runtime/windows/syscall.goc
@@ -20,6 +20,10 @@ func NewCallback(fn Eface) (code uintptr) {
 	code = (uintptr)runtime·compilecallback(fn, true);
 }
 
+func NewCallbackCDecl(fn Eface) (code uintptr) {
+	code = (uintptr)runtime·compilecallback(fn, false);
+}
+
 func Syscall(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
 	USED(a2);
 	USED(a3);
diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c
index e08d1b6..b76eaac 100644
--- a/src/pkg/runtime/windows/thread.c
+++ b/src/pkg/runtime/windows/thread.c
@@ -6,6 +6,7 @@
 #include "type.h"
 #include "defs.h"
 #include "os.h"
+#include "cgocall.h"
 
 #pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
 #pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll"
@@ -221,34 +222,27 @@ runtime·gettime(int64 *sec, int32 *usec)
 void *
 runtime·stdcall(void *fn, int32 count, ...)
 {
-	return runtime·stdcall_raw(fn, count, (uintptr*)&count + 1);
+	WinCall c;
+
+	c.fn = fn;
+	c.n = count;
+	c.args = (uintptr*)&count + 1;
+	runtime·asmcgocall(runtime·asmstdcall, &c);
+	return (void*)c.r;
 }
 
 uintptr
 runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err)
 {
-	G *oldlock;
-	uintptr ret;
-
-	/*
-	 * Lock g to m to ensure we stay on the same stack if we do a callback.
-	 */
-	oldlock = m->lockedg;
-	m->lockedg = g;
-	g->lockedm = m;
-
-	runtime·entersyscall();
-	runtime·setlasterror(0);
-	ret = (uintptr)runtime·stdcall_raw(fn, nargs, args);
-	if(err)
-		*err = runtime·getlasterror();
-	runtime·exitsyscall();
-
-	m->lockedg = oldlock;
-	if(oldlock == nil)
-		g->lockedm = nil;
+	WinCall c;
 
-	return ret;
+	c.fn = fn;
+	c.n = nargs;
+	c.args = args;
+	runtime·cgocall(runtime·asmstdcall, &c);
+	if(err)
+		*err = c.err;
+	return c.r;
 }
 
 uint32
@@ -326,105 +320,6 @@ runtime·ctrlhandler1(uint32 type)
 	return 0;
 }
 
-// Will keep all callbacks in a linked list, so they don't get garbage collected.
-typedef	struct	Callback	Callback;
-struct	Callback {
-	Callback*	link;
-	void*		gobody;
-	byte		asmbody;
-};
-
-typedef	struct	Callbacks	Callbacks;
-struct	Callbacks {
-	Lock;
-	Callback*	link;
-	int32		n;
-};
-
-static	Callbacks	cbs;
-
-// Call back from windows dll into go.
-byte *
-runtime·compilecallback(Eface fn, bool cleanstack)
-{
-	FuncType *ft;
-	Type *t;
-	int32 argsize, i, n;
-	byte *p;
-	Callback *c;
-
-	if(fn.type == nil || fn.type->kind != KindFunc)
-		runtime·panicstring("compilecallback: not a function");
-	ft = (FuncType*)fn.type;
-	if(ft->out.len != 1)
-		runtime·panicstring("compilecallback: function must have one output parameter");
-	if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
-		runtime·panicstring("compilecallback: output parameter size is wrong");
-	argsize = 0;
-	for(i=0; i<ft->in.len; i++) {
-		t = ((Type**)ft->in.array)[i];
-		if(t->size != sizeof(uintptr))
-			runtime·panicstring("compilecallback: input parameter size is wrong");
-		argsize += t->size;
-	}
-
-	// compute size of new fn.
-	// must match code laid out below.
-	n = 1+4;		// MOVL fn, AX
-	n += 1+4;		// MOVL argsize, DX
-	n += 1+4;		// MOVL callbackasm, CX
-	n += 2;			// CALL CX
-	n += 1;			// RET
-	if(cleanstack)
-		n += 2;		// ... argsize
-
-	runtime·lock(&cbs);
-	for(c = cbs.link; c != nil; c = c->link) {
-		if(c->gobody == fn.data) {
-			runtime·unlock(&cbs);
-			return &c->asmbody;
-		}
-	}
-	if(cbs.n >= 2000)
-		runtime·throw("too many callback functions");
-	c = runtime·mal(sizeof *c + n);
-	c->gobody = fn.data;
-	c->link = cbs.link;
-	cbs.link = c;
-	cbs.n++;
-	runtime·unlock(&cbs);
-
-	p = &c->asmbody;
-
-	// MOVL fn, AX
-	*p++ = 0xb8;
-	*(uint32*)p = (uint32)fn.data;
-	p += 4;
-
-	// MOVL argsize, DX
-	*p++ = 0xba;
-	*(uint32*)p = argsize;
-	p += 4;
-
-	// MOVL callbackasm, CX
-	*p++ = 0xb9;
-	*(uint32*)p = (uint32)runtime·callbackasm;
-	p += 4;
-
-	// CALL CX
-	*p++ = 0xff;
-	*p++ = 0xd1;
-
-	// RET argsize?
-	if(cleanstack) {
-		*p++ = 0xc2;
-		*(uint16*)p = argsize;
-	} else
-		*p = 0xc3;
-
-	return &c->asmbody;
-}
-
 void
 os·sigpipe(void)
 {
diff --git a/src/pkg/sort/export_test.go b/src/pkg/sort/export_test.go
new file mode 100644
index 0000000..b6e30ce
--- /dev/null
+++ b/src/pkg/sort/export_test.go
@@ -0,0 +1,9 @@
+// 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 sort
+
+func Heapsort(data Interface) {
+	heapSort(data, 0, data.Len())
+}
diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go
index 0a4a437..83ee170 100644
--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -37,10 +37,47 @@ func insertionSort(data Interface, a, b int) {
 	}
 }
 
+// siftDown implements the heap property on data[lo, hi).
+// first is an offset into the array where the root of the heap lies.
+func siftDown(data Interface, lo, hi, first int) {
+	root := lo
+	for {
+		child := 2*root + 1
+		if child >= hi {
+			break
+		}
+		if child+1 < hi && data.Less(first+child, first+child+1) {
+			child++
+		}
+		if !data.Less(first+root, first+child) {
+			return
+		}
+		data.Swap(first+root, first+child)
+		root = child
+	}
+}
+
+func heapSort(data Interface, a, b int) {
+	first := a
+	lo := 0
+	hi := b - a
+
+	// Build heap with greatest element at top.
+	for i := (hi - 1) / 2; i >= 0; i-- {
+		siftDown(data, i, hi, first)
+	}
+
+	// Pop elements, largest first, into end of data.
+	for i := hi - 1; i >= 0; i-- {
+		data.Swap(first, first+i)
+		siftDown(data, lo, i, first)
+	}
+}
+
 // Quicksort, following Bentley and McIlroy,
 // ``Engineering a Sort Function,'' SP&E November 1993.
 
-// Move the median of the three values data[a], data[b], data[c] into data[a].
+// medianOfThree moves the median of the three values data[a], data[b], data[c] into data[a].
 func medianOfThree(data Interface, a, b, c int) {
 	m0 := b
 	m1 := a
@@ -123,16 +160,21 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
 	return lo + b - a, hi - (d - c)
 }
 
-func quickSort(data Interface, a, b int) {
+func quickSort(data Interface, a, b, maxDepth int) {
 	for b-a > 7 {
+		if maxDepth == 0 {
+			heapSort(data, a, b)
+			return
+		}
+		maxDepth--
 		mlo, mhi := doPivot(data, a, b)
 		// Avoiding recursion on the larger subproblem guarantees
 		// a stack depth of at most lg(b-a).
 		if mlo-a < b-mhi {
-			quickSort(data, a, mlo)
+			quickSort(data, a, mlo, maxDepth)
 			a = mhi // i.e., quickSort(data, mhi, b)
 		} else {
-			quickSort(data, mhi, b)
+			quickSort(data, mhi, b, maxDepth)
 			b = mlo // i.e., quickSort(data, a, mlo)
 		}
 	}
@@ -141,7 +183,16 @@ func quickSort(data Interface, a, b int) {
 	}
 }
 
-func Sort(data Interface) { quickSort(data, 0, data.Len()) }
+func Sort(data Interface) {
+	// Switch to heapsort if depth of 2*ceil(lg(n)) is reached.
+	n := data.Len()
+	maxDepth := 0
+	for 1<<uint(maxDepth) < n {
+		maxDepth++
+	}
+	maxDepth *= 2
+	quickSort(data, 0, data.Len(), maxDepth)
+}
 
 func IsSorted(data Interface) bool {
 	n := data.Len()
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index 5007a92..a564015 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -169,6 +169,13 @@ func (d *testingData) Swap(i, j int) {
 	d.data[i], d.data[j] = d.data[j], d.data[i]
 }
 
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
 func lg(n int) int {
 	i := 0
 	for 1<<uint(i) < n {
@@ -177,7 +184,7 @@ func lg(n int) int {
 	return i
 }
 
-func TestBentleyMcIlroy(t *testing.T) {
+func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
 	sizes := []int{100, 1023, 1024, 1025}
 	if testing.Short() {
 		sizes = []int{100, 127, 128, 129}
@@ -253,7 +260,7 @@ func TestBentleyMcIlroy(t *testing.T) {
 
 					desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode])
 					d := &testingData{desc, t, mdata[0:n], n * lg(n) * 12 / 10, 0}
-					Sort(d)
+					sort(d)
 
 					// If we were testing C qsort, we'd have to make a copy
 					// of the slice and sort it ourselves and then compare
@@ -274,9 +281,58 @@ func TestBentleyMcIlroy(t *testing.T) {
 	}
 }
 
-func min(a, b int) int {
-	if a < b {
-		return a
+func TestSortBM(t *testing.T) {
+	testBentleyMcIlroy(t, Sort)
+}
+
+func TestHeapsortBM(t *testing.T) {
+	testBentleyMcIlroy(t, Heapsort)
+}
+
+// This is based on the "antiquicksort" implementation by M. Douglas McIlroy.
+// See http://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info.
+type adversaryTestingData struct {
+	data      []int
+	keys      map[int]int
+	candidate int
+}
+
+func (d *adversaryTestingData) Len() int { return len(d.data) }
+
+func (d *adversaryTestingData) Less(i, j int) bool {
+	if _, present := d.keys[i]; !present {
+		if _, present := d.keys[j]; !present {
+			if i == d.candidate {
+				d.keys[i] = len(d.keys)
+			} else {
+				d.keys[j] = len(d.keys)
+			}
+		}
 	}
-	return b
+
+	if _, present := d.keys[i]; !present {
+		d.candidate = i
+		return false
+	}
+	if _, present := d.keys[j]; !present {
+		d.candidate = j
+		return true
+	}
+
+	return d.keys[i] >= d.keys[j]
+}
+
+func (d *adversaryTestingData) Swap(i, j int) {
+	d.data[i], d.data[j] = d.data[j], d.data[i]
+}
+
+func TestAdversary(t *testing.T) {
+	const size = 100
+	data := make([]int, size)
+	for i := 0; i < size; i++ {
+		data[i] = i
+	}
+
+	d := &adversaryTestingData{data, make(map[int]int), 0}
+	Sort(d) // This should degenerate to heapsort.
 }
diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go
index 38b3805..86c56f7 100644
--- a/src/pkg/strconv/atof.go
+++ b/src/pkg/strconv/atof.go
@@ -56,8 +56,9 @@ func special(s string) (f float64, ok bool) {
 }
 
 // TODO(rsc): Better truncation handling.
-func stringToDecimal(s string) (neg bool, d *decimal, trunc bool, ok bool) {
+func (b *decimal) set(s string) (ok bool) {
 	i := 0
+	b.neg = false
 
 	// optional sign
 	if i >= len(s) {
@@ -67,12 +68,11 @@ func stringToDecimal(s string) (neg bool, d *decimal, trunc bool, ok bool) {
 	case s[i] == '+':
 		i++
 	case s[i] == '-':
-		neg = true
+		b.neg = true
 		i++
 	}
 
 	// digits
-	b := new(decimal)
 	sawdot := false
 	sawdigits := false
 	for ; i < len(s); i++ {
@@ -137,7 +137,6 @@ func stringToDecimal(s string) (neg bool, d *decimal, trunc bool, ok bool) {
 		return
 	}
 
-	d = b
 	ok = true
 	return
 }
@@ -145,7 +144,7 @@ func stringToDecimal(s string) (neg bool, d *decimal, trunc bool, ok bool) {
 // decimal power of ten to binary power of two.
 var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26}
 
-func decimalToFloatBits(neg bool, d *decimal, trunc bool, flt *floatInfo) (b uint64, overflow bool) {
+func (d *decimal) floatBits(flt *floatInfo) (b uint64, overflow bool) {
 	var exp int
 	var mant uint64
 
@@ -209,7 +208,8 @@ func decimalToFloatBits(neg bool, d *decimal, trunc bool, flt *floatInfo) (b uin
 	}
 
 	// Extract 1+flt.mantbits bits.
-	mant = d.Shift(int(1 + flt.mantbits)).RoundedInteger()
+	d.Shift(int(1 + flt.mantbits))
+	mant = d.RoundedInteger()
 
 	// Rounding might have added a bit; shift down.
 	if mant == 2<<flt.mantbits {
@@ -236,7 +236,7 @@ out:
 	// Assemble bits.
 	bits := mant & (uint64(1)<<flt.mantbits - 1)
 	bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
-	if neg {
+	if d.neg {
 		bits |= 1 << flt.mantbits << flt.expbits
 	}
 	return bits, overflow
@@ -244,24 +244,24 @@ out:
 
 // Compute exact floating-point integer from d's digits.
 // Caller is responsible for avoiding overflow.
-func decimalAtof64Int(neg bool, d *decimal) float64 {
+func (d *decimal) atof64int() float64 {
 	f := 0.0
 	for i := 0; i < d.nd; i++ {
 		f = f*10 + float64(d.d[i]-'0')
 	}
-	if neg {
-		f *= -1 // BUG work around 6g f = -f.
+	if d.neg {
+		f = -f
 	}
 	return f
 }
 
-func decimalAtof32Int(neg bool, d *decimal) float32 {
+func (d *decimal) atof32int() float32 {
 	f := float32(0)
 	for i := 0; i < d.nd; i++ {
 		f = f*10 + float32(d.d[i]-'0')
 	}
-	if neg {
-		f *= -1 // BUG work around 6g f = -f.
+	if d.neg {
+		f = -f
 	}
 	return f
 }
@@ -281,7 +281,7 @@ var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1
 //	value is exact integer * exact power of ten
 //	value is exact integer / exact power of ten
 // These all produce potentially inexact but correctly rounded answers.
-func decimalAtof64(neg bool, d *decimal, trunc bool) (f float64, ok bool) {
+func (d *decimal) atof64() (f float64, ok bool) {
 	// Exact integers are <= 10^15.
 	// Exact powers of ten are <= 10^22.
 	if d.nd > 15 {
@@ -289,11 +289,11 @@ func decimalAtof64(neg bool, d *decimal, trunc bool) (f float64, ok bool) {
 	}
 	switch {
 	case d.dp == d.nd: // int
-		f := decimalAtof64Int(neg, d)
+		f := d.atof64int()
 		return f, true
 
 	case d.dp > d.nd && d.dp <= 15+22: // int * 10^k
-		f := decimalAtof64Int(neg, d)
+		f := d.atof64int()
 		k := d.dp - d.nd
 		// If exponent is big but number of digits is not,
 		// can move a few zeros into the integer part.
@@ -304,7 +304,7 @@ func decimalAtof64(neg bool, d *decimal, trunc bool) (f float64, ok bool) {
 		return f * float64pow10[k], true
 
 	case d.dp < d.nd && d.nd-d.dp <= 22: // int / 10^k
-		f := decimalAtof64Int(neg, d)
+		f := d.atof64int()
 		return f / float64pow10[d.nd-d.dp], true
 	}
 	return
@@ -312,7 +312,7 @@ func decimalAtof64(neg bool, d *decimal, trunc bool) (f float64, ok bool) {
 
 // If possible to convert decimal d to 32-bit float f exactly,
 // entirely in floating-point math, do so, avoiding the machinery above.
-func decimalAtof32(neg bool, d *decimal, trunc bool) (f float32, ok bool) {
+func (d *decimal) atof32() (f float32, ok bool) {
 	// Exact integers are <= 10^7.
 	// Exact powers of ten are <= 10^10.
 	if d.nd > 7 {
@@ -320,11 +320,11 @@ func decimalAtof32(neg bool, d *decimal, trunc bool) (f float32, ok bool) {
 	}
 	switch {
 	case d.dp == d.nd: // int
-		f := decimalAtof32Int(neg, d)
+		f := d.atof32int()
 		return f, true
 
 	case d.dp > d.nd && d.dp <= 7+10: // int * 10^k
-		f := decimalAtof32Int(neg, d)
+		f := d.atof32int()
 		k := d.dp - d.nd
 		// If exponent is big but number of digits is not,
 		// can move a few zeros into the integer part.
@@ -335,7 +335,7 @@ func decimalAtof32(neg bool, d *decimal, trunc bool) (f float32, ok bool) {
 		return f * float32pow10[k], true
 
 	case d.dp < d.nd && d.nd-d.dp <= 10: // int / 10^k
-		f := decimalAtof32Int(neg, d)
+		f := d.atof32int()
 		return f / float32pow10[d.nd-d.dp], true
 	}
 	return
@@ -360,16 +360,16 @@ func Atof32(s string) (f float32, err os.Error) {
 		return float32(val), nil
 	}
 
-	neg, d, trunc, ok := stringToDecimal(s)
-	if !ok {
+	var d decimal
+	if !d.set(s) {
 		return 0, &NumError{s, os.EINVAL}
 	}
 	if optimize {
-		if f, ok := decimalAtof32(neg, d, trunc); ok {
+		if f, ok := d.atof32(); ok {
 			return f, nil
 		}
 	}
-	b, ovf := decimalToFloatBits(neg, d, trunc, &float32info)
+	b, ovf := d.floatBits(&float32info)
 	f = math.Float32frombits(uint32(b))
 	if ovf {
 		err = &NumError{s, os.ERANGE}
@@ -385,16 +385,16 @@ func Atof64(s string) (f float64, err os.Error) {
 		return val, nil
 	}
 
-	neg, d, trunc, ok := stringToDecimal(s)
-	if !ok {
+	var d decimal
+	if !d.set(s) {
 		return 0, &NumError{s, os.EINVAL}
 	}
 	if optimize {
-		if f, ok := decimalAtof64(neg, d, trunc); ok {
+		if f, ok := d.atof64(); ok {
 			return f, nil
 		}
 	}
-	b, ovf := decimalToFloatBits(neg, d, trunc, &float64info)
+	b, ovf := d.floatBits(&float64info)
 	f = math.Float64frombits(b)
 	if ovf {
 		err = &NumError{s, os.ERANGE}
diff --git a/src/pkg/strconv/atof_test.go b/src/pkg/strconv/atof_test.go
index 0fdd0ea..23aafc1 100644
--- a/src/pkg/strconv/atof_test.go
+++ b/src/pkg/strconv/atof_test.go
@@ -34,6 +34,7 @@ var atoftests = []atofTest{
 	{"100000000000000016777215", "1.0000000000000001e+23", nil},
 	{"100000000000000016777216", "1.0000000000000003e+23", nil},
 	{"-1", "-1", nil},
+	{"-0.1", "-0.1", nil},
 	{"-0", "-0", nil},
 	{"1e-20", "1e-20", nil},
 	{"625e-3", "0.625", nil},
diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go
index 783065b..f572ea4 100644
--- a/src/pkg/strconv/decimal.go
+++ b/src/pkg/strconv/decimal.go
@@ -14,9 +14,10 @@ package strconv
 type decimal struct {
 	// TODO(rsc): Can make d[] a bit smaller and add
 	// truncated bool;
-	d  [2000]byte // digits
-	nd int        // number of digits used
-	dp int        // decimal point
+	d   [2000]byte // digits
+	nd  int        // number of digits used
+	dp  int        // decimal point
+	neg bool
 }
 
 func (a *decimal) String() string {
@@ -266,8 +267,7 @@ func leftShift(a *decimal, k uint) {
 }
 
 // Binary shift left (k > 0) or right (k < 0).
-// Returns receiver for convenience.
-func (a *decimal) Shift(k int) *decimal {
+func (a *decimal) Shift(k int) {
 	switch {
 	case a.nd == 0:
 		// nothing to do: a == 0
@@ -284,7 +284,6 @@ func (a *decimal) Shift(k int) *decimal {
 		}
 		rightShift(a, uint(-k))
 	}
-	return a
 }
 
 // If we chop a at nd digits, should we round up?
diff --git a/src/pkg/strconv/decimal_test.go b/src/pkg/strconv/decimal_test.go
index 9b79035..deb2e02 100644
--- a/src/pkg/strconv/decimal_test.go
+++ b/src/pkg/strconv/decimal_test.go
@@ -32,7 +32,9 @@ var shifttests = []shiftTest{
 func TestDecimalShift(t *testing.T) {
 	for i := 0; i < len(shifttests); i++ {
 		test := &shifttests[i]
-		s := NewDecimal(test.i).Shift(test.shift).String()
+		d := NewDecimal(test.i)
+		d.Shift(test.shift)
+		s := d.String()
 		if s != test.out {
 			t.Errorf("Decimal %v << %v = %v, want %v",
 				test.i, test.shift, s, test.out)
@@ -108,7 +110,9 @@ var roundinttests = []roundIntTest{
 func TestDecimalRoundedInteger(t *testing.T) {
 	for i := 0; i < len(roundinttests); i++ {
 		test := roundinttests[i]
-		int := NewDecimal(test.i).Shift(test.shift).RoundedInteger()
+		d := NewDecimal(test.i)
+		d.Shift(test.shift)
+		int := d.RoundedInteger()
 		if int != test.int {
 			t.Errorf("Decimal %v >> %v RoundedInteger = %v, want %v",
 				test.i, test.shift, int, test.int)
diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go
index b6049c5..07fe806 100644
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -98,7 +98,8 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
 	// The shift is exp - flt.mantbits because mant is a 1-bit integer
 	// followed by a flt.mantbits fraction, and we are treating it as
 	// a 1+flt.mantbits-bit integer.
-	d := newDecimal(mant).Shift(exp - int(flt.mantbits))
+	d := newDecimal(mant)
+	d.Shift(exp - int(flt.mantbits))
 
 	// Round appropriately.
 	// Negative precision means "only as much as needed to be exact."
@@ -183,7 +184,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
 	// d = mant << (exp - mantbits)
 	// Next highest floating point number is mant+1 << exp-mantbits.
 	// Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
-	upper := newDecimal(mant*2 + 1).Shift(exp - int(flt.mantbits) - 1)
+	upper := newDecimal(mant*2 + 1)
+	upper.Shift(exp - int(flt.mantbits) - 1)
 
 	// d = mant << (exp - mantbits)
 	// Next lowest floating point number is mant-1 << exp-mantbits,
@@ -201,7 +203,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
 		mantlo = mant*2 - 1
 		explo = exp - 1
 	}
-	lower := newDecimal(mantlo*2 + 1).Shift(explo - int(flt.mantbits) - 1)
+	lower := newDecimal(mantlo*2 + 1)
+	lower.Shift(explo - int(flt.mantbits) - 1)
 
 	// The upper and lower bounds are possible outputs only if
 	// the original mantissa is even, so that IEEE round-to-even
diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s
index 914d2fe..4cab426 100644
--- a/src/pkg/sync/atomic/asm_386.s
+++ b/src/pkg/sync/atomic/asm_386.s
@@ -18,6 +18,9 @@ TEXT ·CompareAndSwapUint32(SB),7,$0
 TEXT ·CompareAndSwapUintptr(SB),7,$0
 	JMP	·CompareAndSwapUint32(SB)
 
+TEXT ·CompareAndSwapPointer(SB),7,$0
+	JMP	·CompareAndSwapUint32(SB)
+
 TEXT ·CompareAndSwapInt64(SB),7,$0
 	JMP	·CompareAndSwapUint64(SB)
 
@@ -94,3 +97,24 @@ TEXT ·LoadUint32(SB),7,$0
 	MOVL	0(AX), AX
 	MOVL	AX, ret+4(FP)
 	RET
+
+TEXT ·LoadUintptr(SB),7,$0
+	JMP	·LoadUint32(SB)
+
+TEXT ·LoadPointer(SB),7,$0
+	JMP	·LoadUint32(SB)
+
+TEXT ·StoreInt32(SB),7,$0
+	JMP	·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),7,$0
+	MOVL	addrptr+0(FP), BP
+	MOVL	val+4(FP), AX
+	XCHGL	AX, 0(BP)
+	RET
+
+TEXT ·StoreUintptr(SB),7,$0
+	JMP	·StoreUint32(SB)
+
+TEXT ·StorePointer(SB),7,$0
+	JMP	·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s
index 4282950..d903f36 100644
--- a/src/pkg/sync/atomic/asm_amd64.s
+++ b/src/pkg/sync/atomic/asm_amd64.s
@@ -17,6 +17,9 @@ TEXT ·CompareAndSwapUint32(SB),7,$0
 TEXT ·CompareAndSwapUintptr(SB),7,$0
 	JMP	·CompareAndSwapUint64(SB)
 
+TEXT ·CompareAndSwapPointer(SB),7,$0
+	JMP	·CompareAndSwapUint64(SB)
+
 TEXT ·CompareAndSwapInt64(SB),7,$0
 	JMP	·CompareAndSwapUint64(SB)
 
@@ -67,3 +70,29 @@ TEXT ·LoadUint32(SB),7,$0
 	MOVL	AX, ret+8(FP)
 	RET
 
+TEXT ·LoadUintptr(SB),7,$0
+	JMP	·LoadPointer(SB)
+
+TEXT ·LoadPointer(SB),7,$0
+	MOVQ	addrptr+0(FP), AX
+	MOVQ	0(AX), AX
+	MOVQ	AX, ret+8(FP)
+	RET
+
+TEXT ·StoreInt32(SB),7,$0
+	JMP	·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),7,$0
+	MOVQ	addrptr+0(FP), BP
+	MOVL	val+8(FP), AX
+	XCHGL	AX, 0(BP)
+	RET
+
+TEXT ·StoreUintptr(SB),7,$0
+	JMP	·StorePointer(SB)
+
+TEXT ·StorePointer(SB),7,$0
+	MOVQ	addrptr+0(FP), BP
+	MOVQ	val+8(FP), AX
+	XCHGQ	AX, 0(BP)
+	RET
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 9ac4119..ff44191 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -50,6 +50,9 @@ cascheck:
 TEXT ·CompareAndSwapUintptr(SB),7,$0
 	B	·CompareAndSwapUint32(SB)
 
+TEXT ·CompareAndSwapPointer(SB),7,$0
+	B	·CompareAndSwapUint32(SB)
+
 TEXT ·AddInt32(SB),7,$0
 	B	·AddUint32(SB)
 
@@ -96,3 +99,27 @@ loadloop1:
 	BCC	loadloop1
 	MOVW	R1, val+4(FP)
 	RET
+
+TEXT ·LoadUintptr(SB),7,$0
+	B	·LoadUint32(SB)
+
+TEXT ·LoadPointer(SB),7,$0
+	B	·LoadUint32(SB)
+
+TEXT ·StoreInt32(SB),7,$0
+	B	·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),7,$0
+	MOVW	addrptr+0(FP), R2
+	MOVW	val+4(FP), R1
+storeloop1:
+	MOVW	0(R2), R0
+	BL	cas<>(SB)
+	BCC	storeloop1
+	RET
+
+TEXT ·StoreUintptr(SB),7,$0
+	B	·StoreUint32(SB)
+
+TEXT ·StorePointer(SB),7,$0
+	B	·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go
index 2229e58..d3fc138 100644
--- a/src/pkg/sync/atomic/atomic_test.go
+++ b/src/pkg/sync/atomic/atomic_test.go
@@ -164,17 +164,17 @@ func TestCompareAndSwapInt32(t *testing.T) {
 	for val := int32(1); val+val > val; val += val {
 		x.i = val
 		if !CompareAndSwapInt32(&x.i, val, val+1) {
-			t.Errorf("should have swapped %#x %#x", val, val+1)
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 		x.i = val + 1
 		if CompareAndSwapInt32(&x.i, val, val+2) {
-			t.Errorf("should not have swapped %#x %#x", val, val+2)
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 	}
 	if x.before != magic32 || x.after != magic32 {
@@ -193,17 +193,17 @@ func TestCompareAndSwapUint32(t *testing.T) {
 	for val := uint32(1); val+val > val; val += val {
 		x.i = val
 		if !CompareAndSwapUint32(&x.i, val, val+1) {
-			t.Errorf("should have swapped %#x %#x", val, val+1)
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 		x.i = val + 1
 		if CompareAndSwapUint32(&x.i, val, val+2) {
-			t.Errorf("should not have swapped %#x %#x", val, val+2)
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 	}
 	if x.before != magic32 || x.after != magic32 {
@@ -226,17 +226,17 @@ func TestCompareAndSwapInt64(t *testing.T) {
 	for val := int64(1); val+val > val; val += val {
 		x.i = val
 		if !CompareAndSwapInt64(&x.i, val, val+1) {
-			t.Errorf("should have swapped %#x %#x", val, val+1)
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 		x.i = val + 1
 		if CompareAndSwapInt64(&x.i, val, val+2) {
-			t.Errorf("should not have swapped %#x %#x", val, val+2)
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 	}
 	if x.before != magic64 || x.after != magic64 {
@@ -259,17 +259,17 @@ func TestCompareAndSwapUint64(t *testing.T) {
 	for val := uint64(1); val+val > val; val += val {
 		x.i = val
 		if !CompareAndSwapUint64(&x.i, val, val+1) {
-			t.Errorf("should have swapped %#x %#x", val, val+1)
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 		x.i = val + 1
 		if CompareAndSwapUint64(&x.i, val, val+2) {
-			t.Errorf("should not have swapped %#x %#x", val, val+2)
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 	}
 	if x.before != magic64 || x.after != magic64 {
@@ -290,17 +290,48 @@ func TestCompareAndSwapUintptr(t *testing.T) {
 	for val := uintptr(1); val+val > val; val += val {
 		x.i = val
 		if !CompareAndSwapUintptr(&x.i, val, val+1) {
-			t.Errorf("should have swapped %#x %#x", val, val+1)
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 		x.i = val + 1
 		if CompareAndSwapUintptr(&x.i, val, val+2) {
-			t.Errorf("should not have swapped %#x %#x", val, val+2)
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
 		}
 		if x.i != val+1 {
-			t.Errorf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+		}
+	}
+	if x.before != magicptr || x.after != magicptr {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+	}
+}
+
+func TestCompareAndSwapPointer(t *testing.T) {
+	var x struct {
+		before uintptr
+		i      unsafe.Pointer
+		after  uintptr
+	}
+	var m uint64 = magic64
+	magicptr := uintptr(m)
+	x.before = magicptr
+	x.after = magicptr
+	for val := uintptr(1); val+val > val; val += val {
+		x.i = unsafe.Pointer(val)
+		if !CompareAndSwapPointer(&x.i, unsafe.Pointer(val), unsafe.Pointer(val+1)) {
+			t.Fatalf("should have swapped %#x %#x", val, val+1)
+		}
+		if x.i != unsafe.Pointer(val+1) {
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
+		}
+		x.i = unsafe.Pointer(val + 1)
+		if CompareAndSwapPointer(&x.i, unsafe.Pointer(val), unsafe.Pointer(val+2)) {
+			t.Fatalf("should not have swapped %#x %#x", val, val+2)
+		}
+		if x.i != unsafe.Pointer(val+1) {
+			t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1)
 		}
 	}
 	if x.before != magicptr || x.after != magicptr {
@@ -348,6 +379,138 @@ func TestLoadUint32(t *testing.T) {
 	}
 }
 
+func TestLoadUintptr(t *testing.T) {
+	var x struct {
+		before uintptr
+		i      uintptr
+		after  uintptr
+	}
+	var m uint64 = magic64
+	magicptr := uintptr(m)
+	x.before = magicptr
+	x.after = magicptr
+	for delta := uintptr(1); delta+delta > delta; delta += delta {
+		k := LoadUintptr(&x.i)
+		if k != x.i {
+			t.Fatalf("delta=%d i=%d k=%d", delta, x.i, k)
+		}
+		x.i += delta
+	}
+	if x.before != magicptr || x.after != magicptr {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+	}
+}
+
+func TestLoadPointer(t *testing.T) {
+	var x struct {
+		before uintptr
+		i      unsafe.Pointer
+		after  uintptr
+	}
+	var m uint64 = magic64
+	magicptr := uintptr(m)
+	x.before = magicptr
+	x.after = magicptr
+	for delta := uintptr(1); delta+delta > delta; delta += delta {
+		k := LoadPointer(&x.i)
+		if k != x.i {
+			t.Fatalf("delta=%d i=%d k=%d", delta, x.i, k)
+		}
+		x.i = unsafe.Pointer(uintptr(x.i) + delta)
+	}
+	if x.before != magicptr || x.after != magicptr {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+	}
+}
+
+func TestStoreInt32(t *testing.T) {
+	var x struct {
+		before int32
+		i      int32
+		after  int32
+	}
+	x.before = magic32
+	x.after = magic32
+	v := int32(0)
+	for delta := int32(1); delta+delta > delta; delta += delta {
+		StoreInt32(&x.i, v)
+		if x.i != v {
+			t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v)
+		}
+		v += delta
+	}
+	if x.before != magic32 || x.after != magic32 {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32)
+	}
+}
+
+func TestStoreUint32(t *testing.T) {
+	var x struct {
+		before uint32
+		i      uint32
+		after  uint32
+	}
+	x.before = magic32
+	x.after = magic32
+	v := uint32(0)
+	for delta := uint32(1); delta+delta > delta; delta += delta {
+		StoreUint32(&x.i, v)
+		if x.i != v {
+			t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v)
+		}
+		v += delta
+	}
+	if x.before != magic32 || x.after != magic32 {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32)
+	}
+}
+
+func TestStoreUintptr(t *testing.T) {
+	var x struct {
+		before uintptr
+		i      uintptr
+		after  uintptr
+	}
+	var m uint64 = magic64
+	magicptr := uintptr(m)
+	x.before = magicptr
+	x.after = magicptr
+	v := uintptr(0)
+	for delta := uintptr(1); delta+delta > delta; delta += delta {
+		StoreUintptr(&x.i, v)
+		if x.i != v {
+			t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v)
+		}
+		v += delta
+	}
+	if x.before != magicptr || x.after != magicptr {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+	}
+}
+
+func TestStorePointer(t *testing.T) {
+	var x struct {
+		before uintptr
+		i      unsafe.Pointer
+		after  uintptr
+	}
+	var m uint64 = magic64
+	magicptr := uintptr(m)
+	x.before = magicptr
+	x.after = magicptr
+	v := unsafe.Pointer(uintptr(0))
+	for delta := uintptr(1); delta+delta > delta; delta += delta {
+		StorePointer(&x.i, unsafe.Pointer(v))
+		if x.i != v {
+			t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v)
+		}
+		v = unsafe.Pointer(uintptr(v) + delta)
+	}
+	if x.before != magicptr || x.after != magicptr {
+		t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr)
+	}
+}
+
 // Tests of correct behavior, with contention.
 // (Is the function atomic?)
 //
@@ -366,6 +529,7 @@ var hammer32 = []struct {
 	{"CompareAndSwapInt32", hammerCompareAndSwapInt32},
 	{"CompareAndSwapUint32", hammerCompareAndSwapUint32},
 	{"CompareAndSwapUintptr", hammerCompareAndSwapUintptr32},
+	{"CompareAndSwapPointer", hammerCompareAndSwapPointer32},
 }
 
 func init() {
@@ -436,6 +600,20 @@ func hammerCompareAndSwapUintptr32(uval *uint32, count int) {
 	}
 }
 
+func hammerCompareAndSwapPointer32(uval *uint32, count int) {
+	// only safe when uintptr is 32-bit.
+	// not called on 64-bit systems.
+	val := (*unsafe.Pointer)(unsafe.Pointer(uval))
+	for i := 0; i < count; i++ {
+		for {
+			v := *val
+			if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) {
+				break
+			}
+		}
+	}
+}
+
 func TestHammer32(t *testing.T) {
 	const p = 4
 	n := 100000
@@ -460,7 +638,7 @@ func TestHammer32(t *testing.T) {
 			<-c
 		}
 		if val != uint32(n)*p {
-			t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
+			t.Fatalf("%s: val=%d want %d", tt.name, val, n*p)
 		}
 	}
 }
@@ -475,6 +653,7 @@ var hammer64 = []struct {
 	{"CompareAndSwapInt64", hammerCompareAndSwapInt64},
 	{"CompareAndSwapUint64", hammerCompareAndSwapUint64},
 	{"CompareAndSwapUintptr", hammerCompareAndSwapUintptr64},
+	{"CompareAndSwapPointer", hammerCompareAndSwapPointer64},
 }
 
 func init() {
@@ -545,6 +724,20 @@ func hammerCompareAndSwapUintptr64(uval *uint64, count int) {
 	}
 }
 
+func hammerCompareAndSwapPointer64(uval *uint64, count int) {
+	// only safe when uintptr is 64-bit.
+	// not called on 32-bit systems.
+	val := (*unsafe.Pointer)(unsafe.Pointer(uval))
+	for i := 0; i < count; i++ {
+		for {
+			v := *val
+			if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) {
+				break
+			}
+		}
+	}
+}
+
 func TestHammer64(t *testing.T) {
 	if test64err != nil {
 		t.Logf("Skipping 64-bit tests: %v", test64err)
@@ -573,63 +766,113 @@ func TestHammer64(t *testing.T) {
 			<-c
 		}
 		if val != uint64(n)*p {
-			t.Errorf("%s: val=%d want %d", tt.name, val, n*p)
+			t.Fatalf("%s: val=%d want %d", tt.name, val, n*p)
 		}
 	}
 }
 
-func hammerLoadInt32(t *testing.T, uval *uint32) {
-	val := (*int32)(unsafe.Pointer(uval))
-	for {
-		v := LoadInt32(val)
+func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) {
+	val := (*int32)(valp)
+	v := LoadInt32(val)
+	vlo := v & ((1 << 16) - 1)
+	vhi := v >> 16
+	if vlo != vhi {
+		t.Fatalf("LoadInt32: %#x != %#x", vlo, vhi)
+	}
+	new := v + 1 + 1<<16
+	if vlo == 1e4 {
+		new = 0
+	}
+	StoreInt32(val, new)
+}
+
+func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) {
+	val := (*uint32)(valp)
+	v := LoadUint32(val)
+	vlo := v & ((1 << 16) - 1)
+	vhi := v >> 16
+	if vlo != vhi {
+		t.Fatalf("LoadUint32: %#x != %#x", vlo, vhi)
+	}
+	new := v + 1 + 1<<16
+	if vlo == 1e4 {
+		new = 0
+	}
+	StoreUint32(val, new)
+}
+
+func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) {
+	val := (*uintptr)(valp)
+	var test64 uint64 = 1 << 50
+	arch32 := uintptr(test64) == 0
+	v := LoadUintptr(val)
+	new := v
+	if arch32 {
 		vlo := v & ((1 << 16) - 1)
 		vhi := v >> 16
 		if vlo != vhi {
-			t.Fatalf("LoadInt32: %#x != %#x", vlo, vhi)
+			t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi)
 		}
-		new := v + 1 + 1<<16
+		new = v + 1 + 1<<16
 		if vlo == 1e4 {
 			new = 0
 		}
-		if CompareAndSwapInt32(val, v, new) {
-			break
+	} else {
+		vlo := v & ((1 << 32) - 1)
+		vhi := v >> 32
+		if vlo != vhi {
+			t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi)
 		}
+		inc := uint64(1 + 1<<32)
+		new = v + uintptr(inc)
 	}
+	StoreUintptr(val, new)
 }
 
-func hammerLoadUint32(t *testing.T, val *uint32) {
-	for {
-		v := LoadUint32(val)
+func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) {
+	val := (*unsafe.Pointer)(valp)
+	var test64 uint64 = 1 << 50
+	arch32 := uintptr(test64) == 0
+	v := uintptr(LoadPointer(val))
+	new := v
+	if arch32 {
 		vlo := v & ((1 << 16) - 1)
 		vhi := v >> 16
 		if vlo != vhi {
-			t.Fatalf("LoadUint32: %#x != %#x", vlo, vhi)
+			t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi)
 		}
-		new := v + 1 + 1<<16
+		new = v + 1 + 1<<16
 		if vlo == 1e4 {
 			new = 0
 		}
-		if CompareAndSwapUint32(val, v, new) {
-			break
+	} else {
+		vlo := v & ((1 << 32) - 1)
+		vhi := v >> 32
+		if vlo != vhi {
+			t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi)
 		}
+		inc := uint64(1 + 1<<32)
+		new = v + uintptr(inc)
 	}
+	StorePointer(val, unsafe.Pointer(new))
 }
 
-func TestHammerLoad(t *testing.T) {
-	tests := [...]func(*testing.T, *uint32){hammerLoadInt32, hammerLoadUint32}
-	n := 100000
+func TestHammerStoreLoad(t *testing.T) {
+	tests := [...]func(*testing.T, unsafe.Pointer){hammerStoreLoadInt32, hammerStoreLoadUint32,
+		hammerStoreLoadUintptr, hammerStoreLoadPointer}
+	n := int(1e6)
 	if testing.Short() {
-		n = 10000
+		n = int(1e4)
 	}
 	const procs = 8
 	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(procs))
 	for _, tt := range tests {
 		c := make(chan int)
-		var val uint32
+		var val uint64
 		for p := 0; p < procs; p++ {
 			go func() {
 				for i := 0; i < n; i++ {
-					tt(t, &val)
+					tt(t, unsafe.Pointer(&val))
 				}
 				c <- 1
 			}()
@@ -639,3 +882,40 @@ func TestHammerLoad(t *testing.T) {
 		}
 	}
 }
+
+func TestStoreLoadSeqCst(t *testing.T) {
+	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+	N := int32(1e6)
+	if testing.Short() {
+		N = int32(1e5)
+	}
+	c := make(chan bool, 2)
+	X := [2]int32{}
+	ack := [2][3]int32{{-1, -1, -1}, {-1, -1, -1}}
+	for p := 0; p < 2; p++ {
+		go func(me int) {
+			he := 1 - me
+			for i := int32(1); i < N; i++ {
+				StoreInt32(&X[me], i)
+				my := LoadInt32(&X[he])
+				ack[me][i%3] = my
+				for w := 1; ack[he][i%3] == -1; w++ {
+					if w%1000 == 0 {
+						runtime.Gosched()
+					}
+				}
+				his := ack[he][i%3]
+				if (my != i && my != i-1) || (his != i && his != i-1) {
+					t.Fatalf("invalid values: %d/%d (%d)", my, his, i)
+				}
+				if my != i && his != i {
+					t.Fatalf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i)
+				}
+				ack[me][(i-1)%3] = -1
+			}
+			c <- true
+		}(p)
+	}
+	<-c
+	<-c
+}
diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index b35eb53..987f8c9 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -22,6 +22,10 @@
 //
 package atomic
 
+import (
+	"unsafe"
+)
+
 // BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11.
 //
 // On x86-32, the 64-bit functions use instructions unavailable before the Pentium.
@@ -41,6 +45,9 @@ func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool)
 // CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
 func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool)
 
+// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.
+func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
+
 // AddInt32 atomically adds delta to *val and returns the new value.
 func AddInt32(val *int32, delta int32) (new int32)
 
@@ -62,6 +69,24 @@ func LoadInt32(addr *int32) (val int32)
 // LoadUint32 atomically loads *addr.
 func LoadUint32(addr *uint32) (val uint32)
 
+// LoadUintptr atomically loads *addr.
+func LoadUintptr(addr *uintptr) (val uintptr)
+
+// LoadPointer atomically loads *addr.
+func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
+
+// StoreInt32 atomically stores val into *addr.
+func StoreInt32(addr *int32, val int32)
+
+// StoreUint32 atomically stores val into *addr.
+func StoreUint32(addr *uint32, val uint32)
+
+// StoreUintptr atomically stores val into *addr.
+func StoreUintptr(addr *uintptr, val uintptr)
+
+// StorePointer atomically stores val into *addr.
+func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
+
 // Helper for ARM.  Linker will discard on other systems
 func panic64() {
 	panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)")
diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile
index f626d09..d4dbc38 100644
--- a/src/pkg/syscall/Makefile
+++ b/src/pkg/syscall/Makefile
@@ -42,6 +42,15 @@ GOFILES_linux=\
 	sockcmsg_unix.go\
 	syscall_unix.go\
 
+GOFILES_openbsd=\
+	bpf_bsd.go\
+	exec_unix.go\
+	route_bsd.go\
+	route_openbsd.go\
+	sockcmsg_unix.go\
+	syscall_bsd.go\
+	syscall_unix.go\
+
 GOFILES_windows=\
 	exec_windows.go\
 	zerrors_windows.go\
diff --git a/src/pkg/syscall/asm_linux_386.s b/src/pkg/syscall/asm_linux_386.s
index 82f170b..22e00e4 100644
--- a/src/pkg/syscall/asm_linux_386.s
+++ b/src/pkg/syscall/asm_linux_386.s
@@ -17,7 +17,7 @@ TEXT	·Syscall(SB),7,$0
 	MOVL	16(SP), DX
 	MOVL	$0, SI
 	MOVL	$0,  DI
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	ok
 	MOVL	$-1, 20(SP)	// r1
@@ -43,7 +43,7 @@ TEXT	·Syscall6(SB),7,$0
 	MOVL	20(SP), SI
 	MOVL	24(SP), DI
 	MOVL	28(SP), BP
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	ok6
 	MOVL	$-1, 32(SP)	// r1
@@ -67,7 +67,7 @@ TEXT ·RawSyscall(SB),7,$0
 	MOVL	16(SP), DX
 	MOVL	$0, SI
 	MOVL	$0,  DI
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	ok1
 	MOVL	$-1, 20(SP)	// r1
@@ -90,7 +90,7 @@ TEXT	·RawSyscall6(SB),7,$0
 	MOVL	20(SP), SI
 	MOVL	24(SP), DI
 	MOVL	28(SP), BP
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	ok2
 	MOVL	$-1, 32(SP)	// r1
@@ -116,7 +116,7 @@ TEXT ·socketcall(SB),7,$0
 	MOVL	$0, DX
 	MOVL	$0, SI
 	MOVL	$0,  DI
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	oksock
 	MOVL	$-1, 32(SP)	// n
@@ -139,7 +139,7 @@ TEXT ·rawsocketcall(SB),7,$0
 	MOVL	$0, DX
 	MOVL	$0, SI
 	MOVL	$0,  DI
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	oksock1
 	MOVL	$-1, 32(SP)	// n
@@ -165,7 +165,7 @@ TEXT ·Seek(SB),7,$0
 	MOVL	8(SP), DX	// offset-low
 	LEAL	20(SP), SI	// result pointer
 	MOVL	16(SP),  DI	// whence
-	INT	$0x80
+	CALL	*runtime·_vdso(SB)
 	CMPL	AX, $0xfffff001
 	JLS	okseek
 	MOVL	$-1, 20(SP)	// newoffset low
diff --git a/src/pkg/syscall/asm_openbsd_386.s b/src/pkg/syscall/asm_openbsd_386.s
new file mode 100644
index 0000000..daa115f
--- /dev/null
+++ b/src/pkg/syscall/asm_openbsd_386.s
@@ -0,0 +1,137 @@
+// 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.
+
+//
+// System call support for 386, OpenBSD
+//
+
+// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
+// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
+// Trap # in AX, args on stack above caller pc.
+
+TEXT	·Syscall(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVL	4(SP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok
+	MOVL	$-1, 20(SP)	// r1
+	MOVL	$-1, 24(SP)	// r2
+	MOVL	AX, 28(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok:
+	MOVL	AX, 20(SP)	// r1
+	MOVL	DX, 24(SP)	// r2
+	MOVL	$0, 28(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall6(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVL	4(SP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok6
+	MOVL	$-1, 32(SP)	// r1
+	MOVL	$-1, 36(SP)	// r2
+	MOVL	AX, 40(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok6:
+	MOVL	AX, 32(SP)	// r1
+	MOVL	DX, 36(SP)	// r2
+	MOVL	$0, 40(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall9(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVL	4(SP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok9
+	MOVL	$-1, 44(SP)	// r1
+	MOVL	$-1, 48(SP)	// r2
+	MOVL	AX, 52(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	MOVL	AX, 44(SP)	// r1
+	MOVL	DX, 48(SP)	// r2
+	MOVL	$0, 52(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT ·RawSyscall(SB),7,$0
+	MOVL	4(SP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok1
+	MOVL	$-1, 20(SP)	// r1
+	MOVL	$-1, 24(SP)	// r2
+	MOVL	AX, 28(SP)		// errno
+	RET
+ok1:
+	MOVL	AX, 20(SP)	// r1
+	MOVL	DX, 24(SP)	// r2
+	MOVL	$0, 28(SP)	// errno
+	RET
+
+TEXT	·RawSyscall6(SB),7,$0
+	MOVL	4(SP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok2
+	MOVL	$-1, 32(SP)	// r1
+	MOVL	$-1, 36(SP)	// r2
+	MOVL	AX, 40(SP)		// errno
+	RET
+ok2:
+	MOVL	AX, 32(SP)	// r1
+	MOVL	DX, 36(SP)	// r2
+	MOVL	$0, 40(SP)	// errno
+	RET
diff --git a/src/pkg/syscall/asm_openbsd_amd64.s b/src/pkg/syscall/asm_openbsd_amd64.s
new file mode 100644
index 0000000..e19c6a9
--- /dev/null
+++ b/src/pkg/syscall/asm_openbsd_amd64.s
@@ -0,0 +1,130 @@
+// 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.
+
+//
+// System call support for AMD64, OpenBSD
+//
+
+// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
+// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
+// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
+// Trap # in AX, args in DI SI DX, return in AX DX
+
+TEXT	·Syscall(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVQ	8(SP), AX	// syscall entry
+	MOVQ	16(SP), DI
+	MOVQ	24(SP), SI
+	MOVQ	32(SP), DX
+	MOVQ	$0, R10
+	MOVQ	$0, R8
+	MOVQ	$0, R9
+	SYSCALL
+	JCC	ok
+	MOVQ	$-1, 40(SP)	// r1
+	MOVQ	$0, 48(SP)	// r2
+	MOVQ	AX, 56(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok:
+	MOVQ	AX, 40(SP)	// r1
+	MOVQ	DX, 48(SP)	// r2
+	MOVQ	$0, 56(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall6(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVQ	8(SP), AX	// syscall entry
+	MOVQ	16(SP), DI
+	MOVQ	24(SP), SI
+	MOVQ	32(SP), DX
+	MOVQ	40(SP), R10
+	MOVQ	48(SP), R8
+	MOVQ	56(SP), R9
+	SYSCALL
+	JCC	ok6
+	MOVQ	$-1, 64(SP)	// r1
+	MOVQ	$0, 72(SP)	// r2
+	MOVQ	AX, 80(SP)  	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok6:
+	MOVQ	AX, 64(SP)	// r1
+	MOVQ	DX, 72(SP)	// r2
+	MOVQ	$0, 80(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall9(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVQ	8(SP), AX	// syscall entry
+	MOVQ	16(SP), DI
+	MOVQ	24(SP), SI
+	MOVQ	32(SP), DX
+	MOVQ	40(SP), R10
+	MOVQ	48(SP), R8
+	MOVQ	56(SP), R9
+	MOVQ	64(SP), R11
+	MOVQ	72(SP), R12
+	MOVQ	80(SP), R13
+	SUBQ    $32, SP
+	MOVQ	R11, 8(SP)	// arg 7
+	MOVQ	R12, 16(SP)	// arg 8
+	MOVQ	R13, 24(SP)	// arg 9
+	SYSCALL
+	JCC	ok9
+	ADDQ    $32, SP
+	MOVQ	$-1, 64(SP)	// r1
+	MOVQ	$0, 72(SP)	// r2
+	MOVQ	AX, 80(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	ADDQ    $32, SP
+	MOVQ	AX, 64(SP)	// r1
+	MOVQ	DX, 72(SP)	// r2
+	MOVQ	$0, 80(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·RawSyscall(SB),7,$0
+	MOVQ	16(SP), DI
+	MOVQ	24(SP), SI
+	MOVQ	32(SP), DX
+	MOVQ	$0, R10
+	MOVQ	$0, R8
+	MOVQ	$0, R9
+	MOVQ	8(SP), AX	// syscall entry
+	SYSCALL
+	JCC	ok1
+	MOVQ	$-1, 40(SP)	// r1
+	MOVQ	$0, 48(SP)	// r2
+	MOVQ	AX, 56(SP)	// errno
+	RET
+ok1:
+	MOVQ	AX, 40(SP)	// r1
+	MOVQ	DX, 48(SP)	// r2
+	MOVQ	$0, 56(SP)	// errno
+	RET
+
+TEXT	·RawSyscall6(SB),7,$0
+	MOVQ	16(SP), DI
+	MOVQ	24(SP), SI
+	MOVQ	32(SP), DX
+	MOVQ	40(SP), R10
+	MOVQ	48(SP), R8
+	MOVQ	56(SP), R9
+	MOVQ	8(SP), AX	// syscall entry
+	SYSCALL
+	JCC	ok2
+	MOVQ	$-1, 64(SP)	// r1
+	MOVQ	$0, 72(SP)	// r2
+	MOVQ	AX, 80(SP)	// errno
+	RET
+ok2:
+	MOVQ	AX, 64(SP)	// r1
+	MOVQ	DX, 72(SP)	// r2
+	MOVQ	$0, 80(SP)	// errno
+	RET
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index 7d0c1ac..8d1d639 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -166,6 +166,18 @@ plan9_386)
 	mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
 	mktypes="godefs -gsyscall -f -m32"
 	;;
+openbsd_386)
+	mkerrors="$mkerrors -f -m32"
+	mksyscall="./mksyscall.pl -l32 -openbsd"
+	mksysnum="curl -s 'http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
+	mktypes="godefs -gsyscall -f-m32"
+	;;
+openbsd_amd64)
+	mkerrors="$mkerrors -f -m64"
+	mksyscall="./mksyscall.pl -openbsd"
+	mksysnum="curl -s 'http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
+	mktypes="godefs -gsyscall -f-m64"
+	;;
 *)
 	echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
 	exit 1
@@ -176,7 +188,7 @@ esac
 	if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
 	syscall_goos="syscall_$GOOS.go"
 	case "$GOOS" in
-	darwin | freebsd)
+	darwin | freebsd | openbsd)
 		syscall_goos="syscall_bsd.go $syscall_goos"
 		;;
 	esac
diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh
index c90cd1c..9384e9b 100755
--- a/src/pkg/syscall/mkerrors.sh
+++ b/src/pkg/syscall/mkerrors.sh
@@ -83,6 +83,28 @@ includes_FreeBSD='
 #include <netinet/ip_mroute.h>
 '
 
+includes_OpenBSD='
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/termios.h>
+#include <sys/ttycom.h>
+#include <sys/wait.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/if_ether.h>
+#include <net/if_bridge.h>
+'
+
 includes='
 #include <sys/types.h>
 #include <fcntl.h>
@@ -120,7 +142,7 @@ done
 	# it encounters while processing the input
 	echo "${!indirect} $includes" | $GCC -x c - -E -dM $ccflags |
 	awk '
-		$1 != "#define" || $2 ~ /\(/ {next}
+		$1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
 
 		$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next}  # 386 registers
 		$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
diff --git a/src/pkg/syscall/mksyscall.pl b/src/pkg/syscall/mksyscall.pl
index ed65259..213526a 100755
--- a/src/pkg/syscall/mksyscall.pl
+++ b/src/pkg/syscall/mksyscall.pl
@@ -25,6 +25,7 @@ my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
 my $errors = 0;
 my $_32bit = "";
 my $plan9 = 0;
+my $openbsd = 0;
 
 if($ARGV[0] eq "-b32") {
 	$_32bit = "big-endian";
@@ -37,6 +38,10 @@ if($ARGV[0] eq "-plan9") {
 	$plan9 = 1;
 	shift;
 }
+if($ARGV[0] eq "-openbsd") {
+	$openbsd = 1;
+	shift;
+}
 
 if($ARGV[0] =~ /^-/) {
 	print STDERR "usage: mksyscall.pl [-b32 | -l32] [file ...]\n";
@@ -115,6 +120,15 @@ while(<>) {
 			$text .= "\n";
 			push @args, "uintptr(_p$n)", "uintptr(len($name))";
 			$n++;
+		} elsif($type eq "int64" && $openbsd) {
+			push @args, "0";
+			if($_32bit eq "big-endian") {
+				push @args, "uintptr($name>>32)", "uintptr($name)";
+			} elsif($_32bit eq "little-endian") {
+				push @args, "uintptr($name)", "uintptr($name>>32)";
+			} else {
+				push @args, "uintptr($name)";
+			}
 		} elsif($type eq "int64" && $_32bit ne "") {
 			if($_32bit eq "big-endian") {
 				push @args, "uintptr($name>>32)", "uintptr($name)";
diff --git a/src/pkg/syscall/mksysnum_openbsd.pl b/src/pkg/syscall/mksysnum_openbsd.pl
new file mode 100755
index 0000000..e041888
--- /dev/null
+++ b/src/pkg/syscall/mksysnum_openbsd.pl
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+# 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.
+#
+# Generate system call table for OpenBSD from master list
+# (for example, /usr/src/sys/kern/syscalls.master).
+
+use strict;
+
+my $command = "mksysnum_openbsd.pl " . join(' ', @ARGV);
+
+print <<EOF;
+// $command
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+EOF
+
+while(<>){
+	if(/^([0-9]+)\s+STD\s+(NOLOCK\s+)?({ \S+\s+\*?(\w+).*)$/){
+		my $num = $1;
+		my $proto = $3;
+		my $name = $4;
+		$name =~ y/a-z/A-Z/;
+
+		# There are multiple entries for enosys and nosys, so comment them out.
+		if($name =~ /^SYS_E?NOSYS$/){
+			$name = "// $name";
+		}
+		if($name eq 'SYS_SYS_EXIT'){
+			$name = 'SYS_EXIT';
+		}
+
+		print "	$name = $num;  // $proto\n";
+	}
+}
+
+print <<EOF;
+)
+EOF
diff --git a/src/pkg/syscall/netlink_linux.go b/src/pkg/syscall/netlink_linux.go
index 681027a..62d9e15 100644
--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -195,10 +195,12 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) {
 	)
 
 	switch msg.Header.Type {
-	case RTM_NEWLINK:
+	case RTM_NEWLINK, RTM_DELLINK:
 		buf = msg.Data[SizeofIfInfomsg:]
-	case RTM_NEWADDR:
+	case RTM_NEWADDR, RTM_DELADDR:
 		buf = msg.Data[SizeofIfAddrmsg:]
+	case RTM_NEWROUTE, RTM_DELROUTE:
+		buf = msg.Data[SizeofRtMsg:]
 	default:
 		return nil, EINVAL
 	}
diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go
index 93e345d..22a0a4f 100644
--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -72,9 +72,52 @@ type RouteMessage struct {
 	Data   []byte
 }
 
-func (m *RouteMessage) sockaddr() (sas []Sockaddr) {
-	// TODO: implement this in the near future
-	return nil
+const rtaRtMask = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK
+
+func (m *RouteMessage) sockaddr() []Sockaddr {
+	var (
+		af  int
+		sas [4]Sockaddr
+	)
+
+	buf := m.Data[:]
+	for i := uint(0); i < RTAX_MAX; i++ {
+		if m.Header.Addrs&rtaRtMask&(1<<i) == 0 {
+			continue
+		}
+		rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
+		switch i {
+		case RTAX_DST, RTAX_GATEWAY:
+			sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
+			if e != 0 {
+				return nil
+			}
+			if i == RTAX_DST {
+				af = int(rsa.Family)
+			}
+			sas[i] = sa
+		case RTAX_NETMASK, RTAX_GENMASK:
+			switch af {
+			case AF_INET:
+				rsa4 := (*RawSockaddrInet4)(unsafe.Pointer(&buf[0]))
+				sa := new(SockaddrInet4)
+				for j := 0; rsa4.Len > 0 && j < int(rsa4.Len)-int(unsafe.Offsetof(rsa4.Addr)); j++ {
+					sa.Addr[j] = rsa4.Addr[j]
+				}
+				sas[i] = sa
+			case AF_INET6:
+				rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&buf[0]))
+				sa := new(SockaddrInet6)
+				for j := 0; rsa6.Len > 0 && j < int(rsa6.Len)-int(unsafe.Offsetof(rsa6.Addr)); j++ {
+					sa.Addr[j] = rsa6.Addr[j]
+				}
+				sas[i] = sa
+			}
+		}
+		buf = buf[rsaAlignOf(int(rsa.Len)):]
+	}
+
+	return sas[:]
 }
 
 // InterfaceMessage represents a routing message containing
diff --git a/src/pkg/syscall/route_openbsd.go b/src/pkg/syscall/route_openbsd.go
new file mode 100644
index 0000000..30e1cac
--- /dev/null
+++ b/src/pkg/syscall/route_openbsd.go
@@ -0,0 +1,35 @@
+// 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.
+
+// Routing sockets and messages for OpenBSD
+
+package syscall
+
+import (
+	"unsafe"
+)
+
+func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage {
+	switch any.Type {
+	case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
+		p := (*RouteMessage)(unsafe.Pointer(any))
+		rtm := &RouteMessage{}
+		rtm.Header = p.Header
+		rtm.Data = buf[SizeofRtMsghdr:any.Msglen]
+		return rtm
+	case RTM_IFINFO:
+		p := (*InterfaceMessage)(unsafe.Pointer(any))
+		ifm := &InterfaceMessage{}
+		ifm.Header = p.Header
+		ifm.Data = buf[SizeofIfMsghdr:any.Msglen]
+		return ifm
+	case RTM_NEWADDR, RTM_DELADDR:
+		p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
+		ifam := &InterfaceAddrMessage{}
+		ifam.Header = p.Header
+		ifam.Data = buf[SizeofIfaMsghdr:any.Msglen]
+		return ifam
+	}
+	return nil
+}
diff --git a/src/pkg/syscall/syscall_openbsd.go b/src/pkg/syscall/syscall_openbsd.go
new file mode 100644
index 0000000..da1bd4d
--- /dev/null
+++ b/src/pkg/syscall/syscall_openbsd.go
@@ -0,0 +1,261 @@
+// Copyright 2009,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.
+
+// OpenBSD system calls.
+// This file is compiled as ordinary Go code,
+// but it is also input to mksyscall,
+// which parses the //sys lines and generates system call stubs.
+// Note that sometimes we use a lowercase //sys name and wrap
+// it in our own nicer implementation, either here or in
+// syscall_bsd.go or syscall_unix.go.
+
+package syscall
+
+import "unsafe"
+
+const OS = "openbsd"
+
+type SockaddrDatalink struct {
+	Len    uint8
+	Family uint8
+	Index  uint16
+	Type   uint8
+	Nlen   uint8
+	Alen   uint8
+	Slen   uint8
+	Data   [24]int8
+	raw    RawSockaddrDatalink
+}
+
+func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
+
+// ParseDirent parses up to max directory entries in buf,
+// appending the names to names. It returns the number
+// bytes consumed from buf, the number of entries added
+// to names, and the new names slice.
+func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
+	origlen := len(buf)
+	for max != 0 && len(buf) > 0 {
+		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
+		if dirent.Reclen == 0 {
+			buf = nil
+			break
+		}
+		buf = buf[dirent.Reclen:]
+		if dirent.Fileno == 0 { // File absent in directory.
+			continue
+		}
+		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
+		var name = string(bytes[0:dirent.Namlen])
+		if name == "." || name == ".." { // Useless names
+			continue
+		}
+		max--
+		count++
+		names = append(names, name)
+	}
+	return origlen - len(buf), count, names
+}
+
+//sysnb pipe(p *[2]_C_int) (errno int)
+func Pipe(p []int) (errno int) {
+	if len(p) != 2 {
+		return EINVAL
+	}
+	var pp [2]_C_int
+	errno = pipe(&pp)
+	p[0] = int(pp[0])
+	p[1] = int(pp[1])
+	return
+}
+
+// TODO
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) {
+	return -1, ENOSYS
+}
+
+/*
+ * Exposed directly
+ */
+//sys	Access(path string, mode uint32) (errno int)
+//sys	Adjtime(delta *Timeval, olddelta *Timeval) (errno int)
+//sys	Chdir(path string) (errno int)
+//sys	Chflags(path string, flags int) (errno int)
+//sys	Chmod(path string, mode uint32) (errno int)
+//sys	Chown(path string, uid int, gid int) (errno int)
+//sys	Chroot(path string) (errno int)
+//sys	Close(fd int) (errno int)
+//sysnb	Dup(fd int) (nfd int, errno int)
+//sysnb	Dup2(from int, to int) (errno int)
+//sys	Exit(code int)
+//sys	Fchdir(fd int) (errno int)
+//sys	Fchflags(path string, flags int) (errno int)
+//sys	Fchmod(fd int, mode uint32) (errno int)
+//sys	Fchown(fd int, uid int, gid int) (errno int)
+//sys	Flock(fd int, how int) (errno int)
+//sys	Fpathconf(fd int, name int) (val int, errno int)
+//sys	Fstat(fd int, stat *Stat_t) (errno int)
+//sys	Fstatfs(fd int, stat *Statfs_t) (errno int)
+//sys	Fsync(fd int) (errno int)
+//sys	Ftruncate(fd int, length int64) (errno int)
+//sys	Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int)
+//sysnb	Getegid() (egid int)
+//sysnb	Geteuid() (uid int)
+//sys	Getfsstat(buf []Statfs_t, flags int) (n int, errno int)
+//sysnb	Getgid() (gid int)
+//sysnb	Getpgid(pid int) (pgid int, errno int)
+//sysnb	Getpgrp() (pgrp int)
+//sysnb	Getpid() (pid int)
+//sysnb	Getppid() (ppid int)
+//sys	Getpriority(which int, who int) (prio int, errno int)
+//sysnb	Getrlimit(which int, lim *Rlimit) (errno int)
+//sysnb	Getrusage(who int, rusage *Rusage) (errno int)
+//sysnb	Getsid(pid int) (sid int, errno int)
+//sysnb	Gettimeofday(tv *Timeval) (errno int)
+//sysnb	Getuid() (uid int)
+//sys	Issetugid() (tainted bool)
+//sys	Kill(pid int, signum int) (errno int)
+//sys	Kqueue() (fd int, errno int)
+//sys	Lchown(path string, uid int, gid int) (errno int)
+//sys	Link(path string, link string) (errno int)
+//sys	Listen(s int, backlog int) (errno int)
+//sys	Lstat(path string, stat *Stat_t) (errno int)
+//sys	Mkdir(path string, mode uint32) (errno int)
+//sys	Mkfifo(path string, mode uint32) (errno int)
+//sys	Mknod(path string, mode uint32, dev int) (errno int)
+//sys	Nanosleep(time *Timespec, leftover *Timespec) (errno int)
+//sys	Open(path string, mode int, perm uint32) (fd int, errno int)
+//sys	Pathconf(path string, name int) (val int, errno int)
+//sys	Pread(fd int, p []byte, offset int64) (n int, errno int)
+//sys	Pwrite(fd int, p []byte, offset int64) (n int, errno int)
+//sys	Read(fd int, p []byte) (n int, errno int)
+//sys	Readlink(path string, buf []byte) (n int, errno int)
+//sys	Rename(from string, to string) (errno int)
+//sys	Revoke(path string) (errno int)
+//sys	Rmdir(path string) (errno int)
+//sys	Seek(fd int, offset int64, whence int) (newoffset int64, errno int) = SYS_LSEEK
+//sys	Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int)
+//sysnb	Setegid(egid int) (errno int)
+//sysnb	Seteuid(euid int) (errno int)
+//sysnb	Setgid(gid int) (errno int)
+//sys	Setlogin(name string) (errno int)
+//sysnb	Setpgid(pid int, pgid int) (errno int)
+//sys	Setpriority(which int, who int, prio int) (errno int)
+//sysnb	Setregid(rgid int, egid int) (errno int)
+//sysnb	Setreuid(ruid int, euid int) (errno int)
+//sysnb	Setrlimit(which int, lim *Rlimit) (errno int)
+//sysnb	Setsid() (pid int, errno int)
+//sysnb	Settimeofday(tp *Timeval) (errno int)
+//sysnb	Setuid(uid int) (errno int)
+//sys	Stat(path string, stat *Stat_t) (errno int)
+//sys	Statfs(path string, stat *Statfs_t) (errno int)
+//sys	Symlink(path string, link string) (errno int)
+//sys	Sync() (errno int)
+//sys	Truncate(path string, length int64) (errno int)
+//sys	Umask(newmask int) (oldmask int)
+//sys	Unlink(path string) (errno int)
+//sys	Unmount(path string, flags int) (errno int)
+//sys	Write(fd int, p []byte) (n int, errno int)
+//sys	mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int)
+//sys	munmap(addr uintptr, length uintptr) (errno int)
+//sys	read(fd int, buf *byte, nbuf int) (n int, errno int)
+//sys	write(fd int, buf *byte, nbuf int) (n int, errno int)
+
+/*
+ * Unimplemented
+ */
+// __getcwd
+// __semctl
+// __syscall
+// __sysctl
+// adjfreq
+// break
+// clock_getres
+// clock_gettime
+// clock_settime
+// closefrom
+// execve
+// faccessat
+// fchmodat
+// fchownat
+// fcntl
+// fhopen
+// fhstat
+// fhstatfs
+// fork
+// fstatat
+// futimens
+// getfh
+// getgid
+// getitimer
+// getlogin
+// getresgid
+// getresuid
+// getrtable
+// getthrid
+// ioctl
+// ktrace
+// lfs_bmapv
+// lfs_markv
+// lfs_segclean
+// lfs_segwait
+// linkat
+// mincore
+// minherit
+// mkdirat
+// mkfifoat
+// mknodat
+// mlock
+// mlockall
+// mount
+// mquery
+// msgctl
+// msgget
+// msgrcv
+// msgsnd
+// munlock
+// munlockall
+// nfssvc
+// nnpfspioctl
+// openat
+// poll
+// preadv
+// profil
+// pwritev
+// quotactl
+// readlinkat
+// readv
+// reboot
+// renameat
+// rfork
+// sched_yield
+// semget
+// semop
+// setgroups
+// setitimer
+// setresgid
+// setresuid
+// setrtable
+// setsockopt
+// shmat
+// shmctl
+// shmdt
+// shmget
+// sigaction
+// sigaltstack
+// sigpending
+// sigprocmask
+// sigreturn
+// sigsuspend
+// symlinkat
+// sysarch
+// syscall
+// threxit
+// thrsigdivert
+// thrsleep
+// thrwakeup
+// unlinkat
+// utimensat
+// vfork
+// writev
diff --git a/src/pkg/syscall/syscall_openbsd_386.go b/src/pkg/syscall/syscall_openbsd_386.go
new file mode 100644
index 0000000..3c4c693
--- /dev/null
+++ b/src/pkg/syscall/syscall_openbsd_386.go
@@ -0,0 +1,42 @@
+// 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 syscall
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+	ts.Sec = int32(nsec / 1e9)
+	ts.Nsec = int32(nsec % 1e9)
+	return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+	nsec += 999 // round up to microsecond
+	tv.Usec = int32(nsec % 1e9 / 1e3)
+	tv.Sec = int32(nsec / 1e9)
+	return
+}
+
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+	k.Ident = uint32(fd)
+	k.Filter = int16(mode)
+	k.Flags = uint16(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+	iov.Len = uint32(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+	msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+	cmsg.Len = uint32(length)
+}
diff --git a/src/pkg/syscall/syscall_openbsd_amd64.go b/src/pkg/syscall/syscall_openbsd_amd64.go
new file mode 100644
index 0000000..c356ad4
--- /dev/null
+++ b/src/pkg/syscall/syscall_openbsd_amd64.go
@@ -0,0 +1,42 @@
+// 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 syscall
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+	ts.Sec = int32(nsec / 1e9)
+	ts.Nsec = nsec % 1e9
+	return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+	nsec += 999 // round up to microsecond
+	tv.Usec = nsec % 1e9 / 1e3
+	tv.Sec = int64(nsec / 1e9)
+	return
+}
+
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+	k.Ident = uint32(fd)
+	k.Filter = int16(mode)
+	k.Flags = uint16(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+	iov.Len = uint64(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+	msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+	cmsg.Len = uint32(length)
+}
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 19c6587..7bc26d3 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -158,7 +158,7 @@ func NewCallback(fn interface{}) uintptr
 //sys	GetLastError() (lasterrno int)
 //sys	LoadLibrary(libname string) (handle Handle, errno int) = LoadLibraryW
 //sys	FreeLibrary(handle Handle) (errno int)
-//sys	GetProcAddress(module Handle, procname string) (proc Handle, errno int)
+//sys	GetProcAddress(module Handle, procname string) (proc uintptr, errno int)
 //sys	GetVersion() (ver uint32, errno int)
 //sys	FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) = FormatMessageW
 //sys	ExitProcess(exitcode uint32)
@@ -207,6 +207,7 @@ func NewCallback(fn interface{}) uintptr
 //sys	SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int)
 //sys	GetFileAttributes(name *uint16) (attrs uint32, errno int) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
 //sys	SetFileAttributes(name *uint16, attrs uint32) (errno int) = kernel32.SetFileAttributesW
+//sys	GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) = kernel32.GetFileAttributesExW
 //sys	GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW
 //sys	CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) [failretval==nil] = shell32.CommandLineToArgvW
 //sys	LocalFree(hmem Handle) (handle Handle, errno int) [failretval!=0]
@@ -354,39 +355,6 @@ func getStdHandle(h int) (fd Handle) {
 	return r
 }
 
-func Stat(path string, stat *Stat_t) (errno int) {
-	if len(path) == 0 {
-		return ERROR_PATH_NOT_FOUND
-	}
-	// Remove trailing slash.
-	if path[len(path)-1] == '/' || path[len(path)-1] == '\\' {
-		// Check if we're given root directory ("\" or "c:\").
-		if len(path) == 1 || (len(path) == 3 && path[1] == ':') {
-			// TODO(brainman): Perhaps should fetch other fields, not just FileAttributes.
-			stat.Windata = Win32finddata{}
-			a, e := GetFileAttributes(StringToUTF16Ptr(path))
-			if e != 0 {
-				return e
-			}
-			stat.Windata.FileAttributes = a
-			return 0
-		}
-		path = path[:len(path)-1]
-	}
-	h, e := FindFirstFile(StringToUTF16Ptr(path), &stat.Windata)
-	if e != 0 {
-		return e
-	}
-	defer FindClose(h)
-	stat.Mode = 0
-	return 0
-}
-
-func Lstat(path string, stat *Stat_t) (errno int) {
-	// no links on windows, just call Stat
-	return Stat(path, stat)
-}
-
 const ImplementsGetwd = true
 
 func Getwd() (wd string, errno int) {
diff --git a/src/pkg/syscall/types_linux.c b/src/pkg/syscall/types_linux.c
index abb2a91..68d64a3 100644
--- a/src/pkg/syscall/types_linux.c
+++ b/src/pkg/syscall/types_linux.c
@@ -212,7 +212,7 @@ enum {
 	$SizeofRtAttr = sizeof(struct rtattr),
 	$SizeofIfInfomsg = sizeof(struct ifinfomsg),
 	$SizeofIfAddrmsg = sizeof(struct ifaddrmsg),
-	$SizeofRtmsg = sizeof(struct rtmsg),
+	$SizeofRtMsg = sizeof(struct rtmsg),
 	$SizeofRtNexthop = sizeof(struct rtnexthop),
 };
 
diff --git a/src/pkg/syscall/types_openbsd.c b/src/pkg/syscall/types_openbsd.c
new file mode 100644
index 0000000..e51c64e
--- /dev/null
+++ b/src/pkg/syscall/types_openbsd.c
@@ -0,0 +1,182 @@
+// 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.
+
+/*
+Input to godefs.  See also mkerrors.sh and mkall.sh
+ */
+
+#define KERNEL
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+// Machine characteristics; for internal use.
+
+enum {
+	$sizeofPtr = sizeof(void*),
+	$sizeofShort = sizeof(short),
+	$sizeofInt = sizeof(int),
+	$sizeofLong = sizeof(long),
+	$sizeofLongLong = sizeof(long long),
+};
+
+// Basic types
+
+typedef short $_C_short;
+typedef int $_C_int;
+typedef long $_C_long;
+typedef long long $_C_long_long;
+
+// Time
+
+typedef struct timespec $Timespec;
+typedef struct timeval $Timeval;
+
+// Processes
+
+typedef struct rusage $Rusage;
+typedef struct rlimit $Rlimit;
+
+typedef gid_t $_Gid_t;
+
+// Files
+
+enum {	// Directory mode bits
+	$S_IFMT = S_IFMT,
+	$S_IFIFO = S_IFIFO,
+	$S_IFCHR = S_IFCHR,
+	$S_IFDIR = S_IFDIR,
+	$S_IFBLK = S_IFBLK,
+	$S_IFREG = S_IFREG,
+	$S_IFLNK = S_IFLNK,
+	$S_IFSOCK = S_IFSOCK,
+	$S_ISUID = S_ISUID,
+	$S_ISGID = S_ISGID,
+	$S_ISVTX = S_ISVTX,
+	$S_IRUSR = S_IRUSR,
+	$S_IWUSR = S_IWUSR,
+	$S_IXUSR = S_IXUSR,
+};
+
+typedef struct stat $Stat_t;
+typedef struct statfs $Statfs_t;
+typedef struct flock $Flock_t;
+
+typedef struct dirent $Dirent;
+
+// Sockets
+
+union sockaddr_all {
+	struct sockaddr s1;	// this one gets used for fields
+	struct sockaddr_in s2;	// these pad it out
+	struct sockaddr_in6 s3;
+	struct sockaddr_un s4;
+	struct sockaddr_dl s5;
+};
+
+struct sockaddr_any {
+	struct sockaddr addr;
+	char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
+};
+
+typedef struct sockaddr_in $RawSockaddrInet4;
+typedef struct sockaddr_in6 $RawSockaddrInet6;
+typedef struct sockaddr_un $RawSockaddrUnix;
+typedef struct sockaddr_dl $RawSockaddrDatalink;
+typedef struct sockaddr $RawSockaddr;
+typedef struct sockaddr_any $RawSockaddrAny;
+typedef socklen_t $_Socklen;
+typedef struct linger $Linger;
+typedef struct iovec $Iovec;
+typedef struct ip_mreq $IPMreq;
+typedef struct ipv6_mreq $IPv6Mreq;
+typedef struct msghdr $Msghdr;
+typedef struct cmsghdr $Cmsghdr;
+typedef struct in6_pktinfo $Inet6Pktinfo;
+
+enum {
+	$SizeofSockaddrInet4 = sizeof(struct sockaddr_in),
+	$SizeofSockaddrInet6 = sizeof(struct sockaddr_in6),
+	$SizeofSockaddrAny = sizeof(struct sockaddr_any),
+	$SizeofSockaddrUnix = sizeof(struct sockaddr_un),
+	$SizeofSockaddrDatalink = sizeof(struct sockaddr_dl),
+	$SizeofLinger = sizeof(struct linger),
+	$SizeofIPMreq = sizeof(struct ip_mreq),
+	$SizeofIPv6Mreq = sizeof(struct ipv6_mreq),
+	$SizeofMsghdr = sizeof(struct msghdr),
+	$SizeofCmsghdr = sizeof(struct cmsghdr),
+	$SizeofInet6Pktinfo = sizeof(struct in6_pktinfo),
+};
+
+// Ptrace requests
+
+enum {
+	$PTRACE_TRACEME = PT_TRACE_ME,
+	$PTRACE_CONT = PT_CONTINUE,
+	$PTRACE_KILL = PT_KILL,
+};
+
+// Events (kqueue, kevent)
+
+typedef struct kevent $Kevent_t;
+
+// Select
+
+typedef fd_set $FdSet;
+
+// Routing and interface messages
+
+enum {
+	$SizeofIfMsghdr = sizeof(struct if_msghdr),
+	$SizeofIfData = sizeof(struct if_data),
+	$SizeofIfaMsghdr = sizeof(struct ifa_msghdr),
+	$SizeofRtMsghdr = sizeof(struct rt_msghdr),
+	$SizeofRtMetrics = sizeof(struct rt_metrics),
+};
+
+typedef struct if_msghdr $IfMsghdr;
+typedef struct if_data $IfData;
+typedef struct ifa_msghdr $IfaMsghdr;
+typedef struct rt_msghdr $RtMsghdr;
+typedef struct rt_metrics $RtMetrics;
+
+// Berkeley packet filter
+
+enum {
+	$SizeofBpfVersion = sizeof(struct bpf_version),
+	$SizeofBpfStat = sizeof(struct bpf_stat),
+	$SizeofBpfProgram = sizeof(struct bpf_program),
+	$SizeofBpfInsn = sizeof(struct bpf_insn),
+	$SizeofBpfHdr = sizeof(struct bpf_hdr),
+};
+
+typedef struct bpf_version $BpfVersion;
+typedef struct bpf_stat $BpfStat;
+typedef struct bpf_program $BpfProgram;
+typedef struct bpf_insn $BpfInsn;
+typedef struct bpf_hdr $BpfHdr;
+typedef struct bpf_timeval $BpfTimeval;
diff --git a/src/pkg/syscall/zerrors_openbsd_386.go b/src/pkg/syscall/zerrors_openbsd_386.go
new file mode 100644
index 0000000..e09d9de
--- /dev/null
+++ b/src/pkg/syscall/zerrors_openbsd_386.go
@@ -0,0 +1,1367 @@
+// mkerrors.sh -f -m32
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// godefs -c gcc -f -m32 -gsyscall -f -m32 _const.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+	AF_APPLETALK                      = 0x10
+	AF_BLUETOOTH                      = 0x20
+	AF_CCITT                          = 0xa
+	AF_CHAOS                          = 0x5
+	AF_CNT                            = 0x15
+	AF_COIP                           = 0x14
+	AF_DATAKIT                        = 0x9
+	AF_DECnet                         = 0xc
+	AF_DLI                            = 0xd
+	AF_E164                           = 0x1a
+	AF_ECMA                           = 0x8
+	AF_ENCAP                          = 0x1c
+	AF_HYLINK                         = 0xf
+	AF_IMPLINK                        = 0x3
+	AF_INET                           = 0x2
+	AF_INET6                          = 0x18
+	AF_IPX                            = 0x17
+	AF_ISDN                           = 0x1a
+	AF_ISO                            = 0x7
+	AF_KEY                            = 0x1e
+	AF_LAT                            = 0xe
+	AF_LINK                           = 0x12
+	AF_LOCAL                          = 0x1
+	AF_MAX                            = 0x24
+	AF_MPLS                           = 0x21
+	AF_NATM                           = 0x1b
+	AF_NS                             = 0x6
+	AF_OSI                            = 0x7
+	AF_PUP                            = 0x4
+	AF_ROUTE                          = 0x11
+	AF_SIP                            = 0x1d
+	AF_SNA                            = 0xb
+	AF_UNIX                           = 0x1
+	AF_UNSPEC                         = 0
+	ARPHRD_ETHER                      = 0x1
+	ARPHRD_FRELAY                     = 0xf
+	ARPHRD_IEEE1394                   = 0x18
+	ARPHRD_IEEE802                    = 0x6
+	BIOCFLUSH                         = 0x20004268
+	BIOCGBLEN                         = 0x40044266
+	BIOCGDIRFILT                      = 0x4004427c
+	BIOCGDLT                          = 0x4004426a
+	BIOCGDLTLIST                      = 0xc008427b
+	BIOCGETIF                         = 0x4020426b
+	BIOCGFILDROP                      = 0x40044278
+	BIOCGHDRCMPLT                     = 0x40044274
+	BIOCGRSIG                         = 0x40044273
+	BIOCGRTIMEOUT                     = 0x4008426e
+	BIOCGSTATS                        = 0x4008426f
+	BIOCIMMEDIATE                     = 0x80044270
+	BIOCLOCK                          = 0x20004276
+	BIOCPROMISC                       = 0x20004269
+	BIOCSBLEN                         = 0xc0044266
+	BIOCSDIRFILT                      = 0x8004427d
+	BIOCSDLT                          = 0x8004427a
+	BIOCSETF                          = 0x80084267
+	BIOCSETIF                         = 0x8020426c
+	BIOCSETWF                         = 0x80084277
+	BIOCSFILDROP                      = 0x80044279
+	BIOCSHDRCMPLT                     = 0x80044275
+	BIOCSRSIG                         = 0x80044272
+	BIOCSRTIMEOUT                     = 0x8008426d
+	BIOCVERSION                       = 0x40044271
+	BPF_A                             = 0x10
+	BPF_ABS                           = 0x20
+	BPF_ADD                           = 0
+	BPF_ALIGNMENT                     = 0x4
+	BPF_ALU                           = 0x4
+	BPF_AND                           = 0x50
+	BPF_B                             = 0x10
+	BPF_DIRECTION_IN                  = 0x1
+	BPF_DIRECTION_OUT                 = 0x2
+	BPF_DIV                           = 0x30
+	BPF_H                             = 0x8
+	BPF_IMM                           = 0
+	BPF_IND                           = 0x40
+	BPF_JA                            = 0
+	BPF_JEQ                           = 0x10
+	BPF_JGE                           = 0x30
+	BPF_JGT                           = 0x20
+	BPF_JMP                           = 0x5
+	BPF_JSET                          = 0x40
+	BPF_K                             = 0
+	BPF_LD                            = 0
+	BPF_LDX                           = 0x1
+	BPF_LEN                           = 0x80
+	BPF_LSH                           = 0x60
+	BPF_MAJOR_VERSION                 = 0x1
+	BPF_MAXBUFSIZE                    = 0x200000
+	BPF_MAXINSNS                      = 0x200
+	BPF_MEM                           = 0x60
+	BPF_MEMWORDS                      = 0x10
+	BPF_MINBUFSIZE                    = 0x20
+	BPF_MINOR_VERSION                 = 0x1
+	BPF_MISC                          = 0x7
+	BPF_MSH                           = 0xa0
+	BPF_MUL                           = 0x20
+	BPF_NEG                           = 0x80
+	BPF_OR                            = 0x40
+	BPF_RELEASE                       = 0x30bb6
+	BPF_RET                           = 0x6
+	BPF_RSH                           = 0x70
+	BPF_ST                            = 0x2
+	BPF_STX                           = 0x3
+	BPF_SUB                           = 0x10
+	BPF_TAX                           = 0
+	BPF_TXA                           = 0x80
+	BPF_W                             = 0
+	BPF_X                             = 0x8
+	CTL_MAXNAME                       = 0xc
+	CTL_NET                           = 0x4
+	DLT_ARCNET                        = 0x7
+	DLT_ATM_RFC1483                   = 0xb
+	DLT_AX25                          = 0x3
+	DLT_CHAOS                         = 0x5
+	DLT_EN10MB                        = 0x1
+	DLT_EN3MB                         = 0x2
+	DLT_ENC                           = 0xd
+	DLT_FDDI                          = 0xa
+	DLT_IEEE802                       = 0x6
+	DLT_IEEE802_11                    = 0x69
+	DLT_IEEE802_11_RADIO              = 0x7f
+	DLT_LOOP                          = 0xc
+	DLT_MPLS                          = 0xdb
+	DLT_NULL                          = 0
+	DLT_PFLOG                         = 0x75
+	DLT_PFSYNC                        = 0x12
+	DLT_PPP                           = 0x9
+	DLT_PPP_BSDOS                     = 0x10
+	DLT_PPP_ETHER                     = 0x33
+	DLT_PRONET                        = 0x4
+	DLT_RAW                           = 0xe
+	DLT_SLIP                          = 0x8
+	DLT_SLIP_BSDOS                    = 0xf
+	DT_BLK                            = 0x6
+	DT_CHR                            = 0x2
+	DT_DIR                            = 0x4
+	DT_FIFO                           = 0x1
+	DT_LNK                            = 0xa
+	DT_REG                            = 0x8
+	DT_SOCK                           = 0xc
+	DT_UNKNOWN                        = 0
+	E2BIG                             = 0x7
+	EACCES                            = 0xd
+	EADDRINUSE                        = 0x30
+	EADDRNOTAVAIL                     = 0x31
+	EAFNOSUPPORT                      = 0x2f
+	EAGAIN                            = 0x23
+	EALREADY                          = 0x25
+	EAUTH                             = 0x50
+	EBADF                             = 0x9
+	EBADRPC                           = 0x48
+	EBUSY                             = 0x10
+	ECANCELED                         = 0x58
+	ECHILD                            = 0xa
+	ECHO                              = 0x8
+	ECHOCTL                           = 0x40
+	ECHOE                             = 0x2
+	ECHOK                             = 0x4
+	ECHOKE                            = 0x1
+	ECHONL                            = 0x10
+	ECHOPRT                           = 0x20
+	ECONNABORTED                      = 0x35
+	ECONNREFUSED                      = 0x3d
+	ECONNRESET                        = 0x36
+	EDEADLK                           = 0xb
+	EDESTADDRREQ                      = 0x27
+	EDOM                              = 0x21
+	EDQUOT                            = 0x45
+	EEXIST                            = 0x11
+	EFAULT                            = 0xe
+	EFBIG                             = 0x1b
+	EFER_LMA                          = 0x400
+	EFER_LME                          = 0x100
+	EFER_NXE                          = 0x800
+	EFER_SCE                          = 0x1
+	EFTYPE                            = 0x4f
+	EHOSTDOWN                         = 0x40
+	EHOSTUNREACH                      = 0x41
+	EIDRM                             = 0x59
+	EILSEQ                            = 0x54
+	EINPROGRESS                       = 0x24
+	EINTR                             = 0x4
+	EINVAL                            = 0x16
+	EIO                               = 0x5
+	EIPSEC                            = 0x52
+	EISCONN                           = 0x38
+	EISDIR                            = 0x15
+	ELAST                             = 0x5b
+	ELOOP                             = 0x3e
+	EMEDIUMTYPE                       = 0x56
+	EMFILE                            = 0x18
+	EMLINK                            = 0x1f
+	EMSGSIZE                          = 0x28
+	EMT_TAGOVF                        = 0x1
+	EMUL_ENABLED                      = 0x1
+	EMUL_NATIVE                       = 0x2
+	ENAMETOOLONG                      = 0x3f
+	ENDRUNDISC                        = 0x9
+	ENEEDAUTH                         = 0x51
+	ENETDOWN                          = 0x32
+	ENETRESET                         = 0x34
+	ENETUNREACH                       = 0x33
+	ENFILE                            = 0x17
+	ENOATTR                           = 0x53
+	ENOBUFS                           = 0x37
+	ENODEV                            = 0x13
+	ENOENT                            = 0x2
+	ENOEXEC                           = 0x8
+	ENOLCK                            = 0x4d
+	ENOMEDIUM                         = 0x55
+	ENOMEM                            = 0xc
+	ENOMSG                            = 0x5a
+	ENOPROTOOPT                       = 0x2a
+	ENOSPC                            = 0x1c
+	ENOSYS                            = 0x4e
+	ENOTBLK                           = 0xf
+	ENOTCONN                          = 0x39
+	ENOTDIR                           = 0x14
+	ENOTEMPTY                         = 0x42
+	ENOTSOCK                          = 0x26
+	ENOTSUP                           = 0x5b
+	ENOTTY                            = 0x19
+	ENXIO                             = 0x6
+	EOPNOTSUPP                        = 0x2d
+	EOVERFLOW                         = 0x57
+	EPERM                             = 0x1
+	EPFNOSUPPORT                      = 0x2e
+	EPIPE                             = 0x20
+	EPROCLIM                          = 0x43
+	EPROCUNAVAIL                      = 0x4c
+	EPROGMISMATCH                     = 0x4b
+	EPROGUNAVAIL                      = 0x4a
+	EPROTONOSUPPORT                   = 0x2b
+	EPROTOTYPE                        = 0x29
+	ERANGE                            = 0x22
+	EREMOTE                           = 0x47
+	EROFS                             = 0x1e
+	ERPCMISMATCH                      = 0x49
+	ESHUTDOWN                         = 0x3a
+	ESOCKTNOSUPPORT                   = 0x2c
+	ESPIPE                            = 0x1d
+	ESRCH                             = 0x3
+	ESTALE                            = 0x46
+	ETHERMIN                          = 0x2e
+	ETHERMTU                          = 0x5dc
+	ETHERTYPE_8023                    = 0x4
+	ETHERTYPE_AARP                    = 0x80f3
+	ETHERTYPE_ACCTON                  = 0x8390
+	ETHERTYPE_AEONIC                  = 0x8036
+	ETHERTYPE_ALPHA                   = 0x814a
+	ETHERTYPE_AMBER                   = 0x6008
+	ETHERTYPE_AMOEBA                  = 0x8145
+	ETHERTYPE_AOE                     = 0x88a2
+	ETHERTYPE_APOLLO                  = 0x80f7
+	ETHERTYPE_APOLLODOMAIN            = 0x8019
+	ETHERTYPE_APPLETALK               = 0x809b
+	ETHERTYPE_APPLITEK                = 0x80c7
+	ETHERTYPE_ARGONAUT                = 0x803a
+	ETHERTYPE_ARP                     = 0x806
+	ETHERTYPE_AT                      = 0x809b
+	ETHERTYPE_ATALK                   = 0x809b
+	ETHERTYPE_ATOMIC                  = 0x86df
+	ETHERTYPE_ATT                     = 0x8069
+	ETHERTYPE_ATTSTANFORD             = 0x8008
+	ETHERTYPE_AUTOPHON                = 0x806a
+	ETHERTYPE_AXIS                    = 0x8856
+	ETHERTYPE_BCLOOP                  = 0x9003
+	ETHERTYPE_BOFL                    = 0x8102
+	ETHERTYPE_CABLETRON               = 0x7034
+	ETHERTYPE_CHAOS                   = 0x804
+	ETHERTYPE_COMDESIGN               = 0x806c
+	ETHERTYPE_COMPUGRAPHIC            = 0x806d
+	ETHERTYPE_COUNTERPOINT            = 0x8062
+	ETHERTYPE_CRONUS                  = 0x8004
+	ETHERTYPE_CRONUSVLN               = 0x8003
+	ETHERTYPE_DCA                     = 0x1234
+	ETHERTYPE_DDE                     = 0x807b
+	ETHERTYPE_DEBNI                   = 0xaaaa
+	ETHERTYPE_DECAM                   = 0x8048
+	ETHERTYPE_DECCUST                 = 0x6006
+	ETHERTYPE_DECDIAG                 = 0x6005
+	ETHERTYPE_DECDNS                  = 0x803c
+	ETHERTYPE_DECDTS                  = 0x803e
+	ETHERTYPE_DECEXPER                = 0x6000
+	ETHERTYPE_DECLAST                 = 0x8041
+	ETHERTYPE_DECLTM                  = 0x803f
+	ETHERTYPE_DECMUMPS                = 0x6009
+	ETHERTYPE_DECNETBIOS              = 0x8040
+	ETHERTYPE_DELTACON                = 0x86de
+	ETHERTYPE_DIDDLE                  = 0x4321
+	ETHERTYPE_DLOG1                   = 0x660
+	ETHERTYPE_DLOG2                   = 0x661
+	ETHERTYPE_DN                      = 0x6003
+	ETHERTYPE_DOGFIGHT                = 0x1989
+	ETHERTYPE_DSMD                    = 0x8039
+	ETHERTYPE_ECMA                    = 0x803
+	ETHERTYPE_ENCRYPT                 = 0x803d
+	ETHERTYPE_ES                      = 0x805d
+	ETHERTYPE_EXCELAN                 = 0x8010
+	ETHERTYPE_EXPERDATA               = 0x8049
+	ETHERTYPE_FLIP                    = 0x8146
+	ETHERTYPE_FLOWCONTROL             = 0x8808
+	ETHERTYPE_FRARP                   = 0x808
+	ETHERTYPE_GENDYN                  = 0x8068
+	ETHERTYPE_HAYES                   = 0x8130
+	ETHERTYPE_HIPPI_FP                = 0x8180
+	ETHERTYPE_HITACHI                 = 0x8820
+	ETHERTYPE_HP                      = 0x8005
+	ETHERTYPE_IEEEPUP                 = 0xa00
+	ETHERTYPE_IEEEPUPAT               = 0xa01
+	ETHERTYPE_IMLBL                   = 0x4c42
+	ETHERTYPE_IMLBLDIAG               = 0x424c
+	ETHERTYPE_IP                      = 0x800
+	ETHERTYPE_IPAS                    = 0x876c
+	ETHERTYPE_IPV6                    = 0x86dd
+	ETHERTYPE_IPX                     = 0x8137
+	ETHERTYPE_IPXNEW                  = 0x8037
+	ETHERTYPE_KALPANA                 = 0x8582
+	ETHERTYPE_LANBRIDGE               = 0x8038
+	ETHERTYPE_LANPROBE                = 0x8888
+	ETHERTYPE_LAT                     = 0x6004
+	ETHERTYPE_LBACK                   = 0x9000
+	ETHERTYPE_LITTLE                  = 0x8060
+	ETHERTYPE_LLDP                    = 0x88cc
+	ETHERTYPE_LOGICRAFT               = 0x8148
+	ETHERTYPE_LOOPBACK                = 0x9000
+	ETHERTYPE_MATRA                   = 0x807a
+	ETHERTYPE_MAX                     = 0xffff
+	ETHERTYPE_MERIT                   = 0x807c
+	ETHERTYPE_MICP                    = 0x873a
+	ETHERTYPE_MOPDL                   = 0x6001
+	ETHERTYPE_MOPRC                   = 0x6002
+	ETHERTYPE_MOTOROLA                = 0x818d
+	ETHERTYPE_MPLS                    = 0x8847
+	ETHERTYPE_MPLS_MCAST              = 0x8848
+	ETHERTYPE_MUMPS                   = 0x813f
+	ETHERTYPE_NBPCC                   = 0x3c04
+	ETHERTYPE_NBPCLAIM                = 0x3c09
+	ETHERTYPE_NBPCLREQ                = 0x3c05
+	ETHERTYPE_NBPCLRSP                = 0x3c06
+	ETHERTYPE_NBPCREQ                 = 0x3c02
+	ETHERTYPE_NBPCRSP                 = 0x3c03
+	ETHERTYPE_NBPDG                   = 0x3c07
+	ETHERTYPE_NBPDGB                  = 0x3c08
+	ETHERTYPE_NBPDLTE                 = 0x3c0a
+	ETHERTYPE_NBPRAR                  = 0x3c0c
+	ETHERTYPE_NBPRAS                  = 0x3c0b
+	ETHERTYPE_NBPRST                  = 0x3c0d
+	ETHERTYPE_NBPSCD                  = 0x3c01
+	ETHERTYPE_NBPVCD                  = 0x3c00
+	ETHERTYPE_NBS                     = 0x802
+	ETHERTYPE_NCD                     = 0x8149
+	ETHERTYPE_NESTAR                  = 0x8006
+	ETHERTYPE_NETBEUI                 = 0x8191
+	ETHERTYPE_NOVELL                  = 0x8138
+	ETHERTYPE_NS                      = 0x600
+	ETHERTYPE_NSAT                    = 0x601
+	ETHERTYPE_NSCOMPAT                = 0x807
+	ETHERTYPE_NTRAILER                = 0x10
+	ETHERTYPE_OS9                     = 0x7007
+	ETHERTYPE_OS9NET                  = 0x7009
+	ETHERTYPE_PACER                   = 0x80c6
+	ETHERTYPE_PAE                     = 0x888e
+	ETHERTYPE_PCS                     = 0x4242
+	ETHERTYPE_PLANNING                = 0x8044
+	ETHERTYPE_PPP                     = 0x880b
+	ETHERTYPE_PPPOE                   = 0x8864
+	ETHERTYPE_PPPOEDISC               = 0x8863
+	ETHERTYPE_PRIMENTS                = 0x7031
+	ETHERTYPE_PUP                     = 0x200
+	ETHERTYPE_PUPAT                   = 0x200
+	ETHERTYPE_QINQ                    = 0x88a8
+	ETHERTYPE_RACAL                   = 0x7030
+	ETHERTYPE_RATIONAL                = 0x8150
+	ETHERTYPE_RAWFR                   = 0x6559
+	ETHERTYPE_RCL                     = 0x1995
+	ETHERTYPE_RDP                     = 0x8739
+	ETHERTYPE_RETIX                   = 0x80f2
+	ETHERTYPE_REVARP                  = 0x8035
+	ETHERTYPE_SCA                     = 0x6007
+	ETHERTYPE_SECTRA                  = 0x86db
+	ETHERTYPE_SECUREDATA              = 0x876d
+	ETHERTYPE_SGITW                   = 0x817e
+	ETHERTYPE_SG_BOUNCE               = 0x8016
+	ETHERTYPE_SG_DIAG                 = 0x8013
+	ETHERTYPE_SG_NETGAMES             = 0x8014
+	ETHERTYPE_SG_RESV                 = 0x8015
+	ETHERTYPE_SIMNET                  = 0x5208
+	ETHERTYPE_SLOW                    = 0x8809
+	ETHERTYPE_SNA                     = 0x80d5
+	ETHERTYPE_SNMP                    = 0x814c
+	ETHERTYPE_SONIX                   = 0xfaf5
+	ETHERTYPE_SPIDER                  = 0x809f
+	ETHERTYPE_SPRITE                  = 0x500
+	ETHERTYPE_STP                     = 0x8181
+	ETHERTYPE_TALARIS                 = 0x812b
+	ETHERTYPE_TALARISMC               = 0x852b
+	ETHERTYPE_TCPCOMP                 = 0x876b
+	ETHERTYPE_TCPSM                   = 0x9002
+	ETHERTYPE_TEC                     = 0x814f
+	ETHERTYPE_TIGAN                   = 0x802f
+	ETHERTYPE_TRAIL                   = 0x1000
+	ETHERTYPE_TRANSETHER              = 0x6558
+	ETHERTYPE_TYMSHARE                = 0x802e
+	ETHERTYPE_UBBST                   = 0x7005
+	ETHERTYPE_UBDEBUG                 = 0x900
+	ETHERTYPE_UBDIAGLOOP              = 0x7002
+	ETHERTYPE_UBDL                    = 0x7000
+	ETHERTYPE_UBNIU                   = 0x7001
+	ETHERTYPE_UBNMC                   = 0x7003
+	ETHERTYPE_VALID                   = 0x1600
+	ETHERTYPE_VARIAN                  = 0x80dd
+	ETHERTYPE_VAXELN                  = 0x803b
+	ETHERTYPE_VEECO                   = 0x8067
+	ETHERTYPE_VEXP                    = 0x805b
+	ETHERTYPE_VGLAB                   = 0x8131
+	ETHERTYPE_VINES                   = 0xbad
+	ETHERTYPE_VINESECHO               = 0xbaf
+	ETHERTYPE_VINESLOOP               = 0xbae
+	ETHERTYPE_VITAL                   = 0xff00
+	ETHERTYPE_VLAN                    = 0x8100
+	ETHERTYPE_VLTLMAN                 = 0x8080
+	ETHERTYPE_VPROD                   = 0x805c
+	ETHERTYPE_VURESERVED              = 0x8147
+	ETHERTYPE_WATERLOO                = 0x8130
+	ETHERTYPE_WELLFLEET               = 0x8103
+	ETHERTYPE_X25                     = 0x805
+	ETHERTYPE_X75                     = 0x801
+	ETHERTYPE_XNSSM                   = 0x9001
+	ETHERTYPE_XTP                     = 0x817d
+	ETHER_ADDR_LEN                    = 0x6
+	ETHER_ALIGN                       = 0x2
+	ETHER_CRC_LEN                     = 0x4
+	ETHER_CRC_POLY_BE                 = 0x4c11db6
+	ETHER_CRC_POLY_LE                 = 0xedb88320
+	ETHER_HDR_LEN                     = 0xe
+	ETHER_MAX_DIX_LEN                 = 0x600
+	ETHER_MAX_LEN                     = 0x5ee
+	ETHER_MIN_LEN                     = 0x40
+	ETHER_TYPE_LEN                    = 0x2
+	ETHER_VLAN_ENCAP_LEN              = 0x4
+	ETIMEDOUT                         = 0x3c
+	ETOOMANYREFS                      = 0x3b
+	ETXTBSY                           = 0x1a
+	EUSERS                            = 0x44
+	EVFILT_AIO                        = -0x3
+	EVFILT_PROC                       = -0x5
+	EVFILT_READ                       = -0x1
+	EVFILT_SIGNAL                     = -0x6
+	EVFILT_SYSCOUNT                   = 0x7
+	EVFILT_TIMER                      = -0x7
+	EVFILT_VNODE                      = -0x4
+	EVFILT_WRITE                      = -0x2
+	EV_ADD                            = 0x1
+	EV_CLEAR                          = 0x20
+	EV_DELETE                         = 0x2
+	EV_DISABLE                        = 0x8
+	EV_ENABLE                         = 0x4
+	EV_EOF                            = 0x8000
+	EV_ERROR                          = 0x4000
+	EV_FLAG1                          = 0x2000
+	EV_ONESHOT                        = 0x10
+	EV_SYSFLAGS                       = 0xf000
+	EWOULDBLOCK                       = 0x23
+	EXDEV                             = 0x12
+	EXTA                              = 0x4b00
+	EXTB                              = 0x9600
+	EXTPROC                           = 0x800
+	FD_CLOEXEC                        = 0x1
+	FD_SETSIZE                        = 0x400
+	F_DUPFD                           = 0
+	F_DUPFD_CLOEXEC                   = 0xa
+	F_GETFD                           = 0x1
+	F_GETFL                           = 0x3
+	F_GETLK                           = 0x7
+	F_GETOWN                          = 0x5
+	F_RDLCK                           = 0x1
+	F_SETFD                           = 0x2
+	F_SETFL                           = 0x4
+	F_SETLK                           = 0x8
+	F_SETLKW                          = 0x9
+	F_SETOWN                          = 0x6
+	F_UNLCK                           = 0x2
+	F_WRLCK                           = 0x3
+	IFA_ROUTE                         = 0x1
+	IFF_ALLMULTI                      = 0x200
+	IFF_BROADCAST                     = 0x2
+	IFF_CANTCHANGE                    = 0x8e52
+	IFF_DEBUG                         = 0x4
+	IFF_LINK0                         = 0x1000
+	IFF_LINK1                         = 0x2000
+	IFF_LINK2                         = 0x4000
+	IFF_LOOPBACK                      = 0x8
+	IFF_MULTICAST                     = 0x8000
+	IFF_NOARP                         = 0x80
+	IFF_NOTRAILERS                    = 0x20
+	IFF_OACTIVE                       = 0x400
+	IFF_POINTOPOINT                   = 0x10
+	IFF_PROMISC                       = 0x100
+	IFF_RUNNING                       = 0x40
+	IFF_SIMPLEX                       = 0x800
+	IFF_UP                            = 0x1
+	IFNAMSIZ                          = 0x10
+	IFT_1822                          = 0x2
+	IFT_A12MPPSWITCH                  = 0x82
+	IFT_AAL2                          = 0xbb
+	IFT_AAL5                          = 0x31
+	IFT_ADSL                          = 0x5e
+	IFT_AFLANE8023                    = 0x3b
+	IFT_AFLANE8025                    = 0x3c
+	IFT_ARAP                          = 0x58
+	IFT_ARCNET                        = 0x23
+	IFT_ARCNETPLUS                    = 0x24
+	IFT_ASYNC                         = 0x54
+	IFT_ATM                           = 0x25
+	IFT_ATMDXI                        = 0x69
+	IFT_ATMFUNI                       = 0x6a
+	IFT_ATMIMA                        = 0x6b
+	IFT_ATMLOGICAL                    = 0x50
+	IFT_ATMRADIO                      = 0xbd
+	IFT_ATMSUBINTERFACE               = 0x86
+	IFT_ATMVCIENDPT                   = 0xc2
+	IFT_ATMVIRTUAL                    = 0x95
+	IFT_BGPPOLICYACCOUNTING           = 0xa2
+	IFT_BLUETOOTH                     = 0xf8
+	IFT_BRIDGE                        = 0xd1
+	IFT_BSC                           = 0x53
+	IFT_CARP                          = 0xf7
+	IFT_CCTEMUL                       = 0x3d
+	IFT_CEPT                          = 0x13
+	IFT_CES                           = 0x85
+	IFT_CHANNEL                       = 0x46
+	IFT_CNR                           = 0x55
+	IFT_COFFEE                        = 0x84
+	IFT_COMPOSITELINK                 = 0x9b
+	IFT_DCN                           = 0x8d
+	IFT_DIGITALPOWERLINE              = 0x8a
+	IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+	IFT_DLSW                          = 0x4a
+	IFT_DOCSCABLEDOWNSTREAM           = 0x80
+	IFT_DOCSCABLEMACLAYER             = 0x7f
+	IFT_DOCSCABLEUPSTREAM             = 0x81
+	IFT_DOCSCABLEUPSTREAMCHANNEL      = 0xcd
+	IFT_DS0                           = 0x51
+	IFT_DS0BUNDLE                     = 0x52
+	IFT_DS1FDL                        = 0xaa
+	IFT_DS3                           = 0x1e
+	IFT_DTM                           = 0x8c
+	IFT_DUMMY                         = 0xf1
+	IFT_DVBASILN                      = 0xac
+	IFT_DVBASIOUT                     = 0xad
+	IFT_DVBRCCDOWNSTREAM              = 0x93
+	IFT_DVBRCCMACLAYER                = 0x92
+	IFT_DVBRCCUPSTREAM                = 0x94
+	IFT_ECONET                        = 0xce
+	IFT_ENC                           = 0xf4
+	IFT_EON                           = 0x19
+	IFT_EPLRS                         = 0x57
+	IFT_ESCON                         = 0x49
+	IFT_ETHER                         = 0x6
+	IFT_FAITH                         = 0xf3
+	IFT_FAST                          = 0x7d
+	IFT_FASTETHER                     = 0x3e
+	IFT_FASTETHERFX                   = 0x45
+	IFT_FDDI                          = 0xf
+	IFT_FIBRECHANNEL                  = 0x38
+	IFT_FRAMERELAYINTERCONNECT        = 0x3a
+	IFT_FRAMERELAYMPI                 = 0x5c
+	IFT_FRDLCIENDPT                   = 0xc1
+	IFT_FRELAY                        = 0x20
+	IFT_FRELAYDCE                     = 0x2c
+	IFT_FRF16MFRBUNDLE                = 0xa3
+	IFT_FRFORWARD                     = 0x9e
+	IFT_G703AT2MB                     = 0x43
+	IFT_G703AT64K                     = 0x42
+	IFT_GIF                           = 0xf0
+	IFT_GIGABITETHERNET               = 0x75
+	IFT_GR303IDT                      = 0xb2
+	IFT_GR303RDT                      = 0xb1
+	IFT_H323GATEKEEPER                = 0xa4
+	IFT_H323PROXY                     = 0xa5
+	IFT_HDH1822                       = 0x3
+	IFT_HDLC                          = 0x76
+	IFT_HDSL2                         = 0xa8
+	IFT_HIPERLAN2                     = 0xb7
+	IFT_HIPPI                         = 0x2f
+	IFT_HIPPIINTERFACE                = 0x39
+	IFT_HOSTPAD                       = 0x5a
+	IFT_HSSI                          = 0x2e
+	IFT_HY                            = 0xe
+	IFT_IBM370PARCHAN                 = 0x48
+	IFT_IDSL                          = 0x9a
+	IFT_IEEE1394                      = 0x90
+	IFT_IEEE80211                     = 0x47
+	IFT_IEEE80212                     = 0x37
+	IFT_IEEE8023ADLAG                 = 0xa1
+	IFT_IFGSN                         = 0x91
+	IFT_IMT                           = 0xbe
+	IFT_INFINIBAND                    = 0xc7
+	IFT_INTERLEAVE                    = 0x7c
+	IFT_IP                            = 0x7e
+	IFT_IPFORWARD                     = 0x8e
+	IFT_IPOVERATM                     = 0x72
+	IFT_IPOVERCDLC                    = 0x6d
+	IFT_IPOVERCLAW                    = 0x6e
+	IFT_IPSWITCH                      = 0x4e
+	IFT_ISDN                          = 0x3f
+	IFT_ISDNBASIC                     = 0x14
+	IFT_ISDNPRIMARY                   = 0x15
+	IFT_ISDNS                         = 0x4b
+	IFT_ISDNU                         = 0x4c
+	IFT_ISO88022LLC                   = 0x29
+	IFT_ISO88023                      = 0x7
+	IFT_ISO88024                      = 0x8
+	IFT_ISO88025                      = 0x9
+	IFT_ISO88025CRFPINT               = 0x62
+	IFT_ISO88025DTR                   = 0x56
+	IFT_ISO88025FIBER                 = 0x73
+	IFT_ISO88026                      = 0xa
+	IFT_ISUP                          = 0xb3
+	IFT_L2VLAN                        = 0x87
+	IFT_L3IPVLAN                      = 0x88
+	IFT_L3IPXVLAN                     = 0x89
+	IFT_LAPB                          = 0x10
+	IFT_LAPD                          = 0x4d
+	IFT_LAPF                          = 0x77
+	IFT_LINEGROUP                     = 0xd2
+	IFT_LOCALTALK                     = 0x2a
+	IFT_LOOP                          = 0x18
+	IFT_MEDIAMAILOVERIP               = 0x8b
+	IFT_MFSIGLINK                     = 0xa7
+	IFT_MIOX25                        = 0x26
+	IFT_MODEM                         = 0x30
+	IFT_MPC                           = 0x71
+	IFT_MPLS                          = 0xa6
+	IFT_MPLSTUNNEL                    = 0x96
+	IFT_MSDSL                         = 0x8f
+	IFT_MVL                           = 0xbf
+	IFT_MYRINET                       = 0x63
+	IFT_NFAS                          = 0xaf
+	IFT_NSIP                          = 0x1b
+	IFT_OPTICALCHANNEL                = 0xc3
+	IFT_OPTICALTRANSPORT              = 0xc4
+	IFT_OTHER                         = 0x1
+	IFT_P10                           = 0xc
+	IFT_P80                           = 0xd
+	IFT_PARA                          = 0x22
+	IFT_PFLOG                         = 0xf5
+	IFT_PFLOW                         = 0xf9
+	IFT_PFSYNC                        = 0xf6
+	IFT_PLC                           = 0xae
+	IFT_PON155                        = 0xcf
+	IFT_PON622                        = 0xd0
+	IFT_POS                           = 0xab
+	IFT_PPP                           = 0x17
+	IFT_PPPMULTILINKBUNDLE            = 0x6c
+	IFT_PROPATM                       = 0xc5
+	IFT_PROPBWAP2MP                   = 0xb8
+	IFT_PROPCNLS                      = 0x59
+	IFT_PROPDOCSWIRELESSDOWNSTREAM    = 0xb5
+	IFT_PROPDOCSWIRELESSMACLAYER      = 0xb4
+	IFT_PROPDOCSWIRELESSUPSTREAM      = 0xb6
+	IFT_PROPMUX                       = 0x36
+	IFT_PROPVIRTUAL                   = 0x35
+	IFT_PROPWIRELESSP2P               = 0x9d
+	IFT_PTPSERIAL                     = 0x16
+	IFT_PVC                           = 0xf2
+	IFT_Q2931                         = 0xc9
+	IFT_QLLC                          = 0x44
+	IFT_RADIOMAC                      = 0xbc
+	IFT_RADSL                         = 0x5f
+	IFT_REACHDSL                      = 0xc0
+	IFT_RFC1483                       = 0x9f
+	IFT_RS232                         = 0x21
+	IFT_RSRB                          = 0x4f
+	IFT_SDLC                          = 0x11
+	IFT_SDSL                          = 0x60
+	IFT_SHDSL                         = 0xa9
+	IFT_SIP                           = 0x1f
+	IFT_SIPSIG                        = 0xcc
+	IFT_SIPTG                         = 0xcb
+	IFT_SLIP                          = 0x1c
+	IFT_SMDSDXI                       = 0x2b
+	IFT_SMDSICIP                      = 0x34
+	IFT_SONET                         = 0x27
+	IFT_SONETOVERHEADCHANNEL          = 0xb9
+	IFT_SONETPATH                     = 0x32
+	IFT_SONETVT                       = 0x33
+	IFT_SRP                           = 0x97
+	IFT_SS7SIGLINK                    = 0x9c
+	IFT_STACKTOSTACK                  = 0x6f
+	IFT_STARLAN                       = 0xb
+	IFT_T1                            = 0x12
+	IFT_TDLC                          = 0x74
+	IFT_TELINK                        = 0xc8
+	IFT_TERMPAD                       = 0x5b
+	IFT_TR008                         = 0xb0
+	IFT_TRANSPHDLC                    = 0x7b
+	IFT_TUNNEL                        = 0x83
+	IFT_ULTRA                         = 0x1d
+	IFT_USB                           = 0xa0
+	IFT_V11                           = 0x40
+	IFT_V35                           = 0x2d
+	IFT_V36                           = 0x41
+	IFT_V37                           = 0x78
+	IFT_VDSL                          = 0x61
+	IFT_VIRTUALIPADDRESS              = 0x70
+	IFT_VIRTUALTG                     = 0xca
+	IFT_VOICEDID                      = 0xd5
+	IFT_VOICEEM                       = 0x64
+	IFT_VOICEEMFGD                    = 0xd3
+	IFT_VOICEENCAP                    = 0x67
+	IFT_VOICEFGDEANA                  = 0xd4
+	IFT_VOICEFXO                      = 0x65
+	IFT_VOICEFXS                      = 0x66
+	IFT_VOICEOVERATM                  = 0x98
+	IFT_VOICEOVERCABLE                = 0xc6
+	IFT_VOICEOVERFRAMERELAY           = 0x99
+	IFT_VOICEOVERIP                   = 0x68
+	IFT_X213                          = 0x5d
+	IFT_X25                           = 0x5
+	IFT_X25DDN                        = 0x4
+	IFT_X25HUNTGROUP                  = 0x7a
+	IFT_X25MLP                        = 0x79
+	IFT_X25PLE                        = 0x28
+	IFT_XETHER                        = 0x1a
+	IN_CLASSA_HOST                    = 0xffffff
+	IN_CLASSA_MAX                     = 0x80
+	IN_CLASSA_NET                     = 0xff000000
+	IN_CLASSA_NSHIFT                  = 0x18
+	IN_CLASSB_HOST                    = 0xffff
+	IN_CLASSB_MAX                     = 0x10000
+	IN_CLASSB_NET                     = 0xffff0000
+	IN_CLASSB_NSHIFT                  = 0x10
+	IN_CLASSC_HOST                    = 0xff
+	IN_CLASSC_NET                     = 0xffffff00
+	IN_CLASSC_NSHIFT                  = 0x8
+	IN_CLASSD_HOST                    = 0xfffffff
+	IN_CLASSD_NET                     = 0xf0000000
+	IN_CLASSD_NSHIFT                  = 0x1c
+	IN_LOOPBACKNET                    = 0x7f
+	IN_RFC3021_HOST                   = 0x1
+	IN_RFC3021_NET                    = 0xfffffffe
+	IN_RFC3021_NSHIFT                 = 0x1f
+	IPPROTO_AH                        = 0x33
+	IPPROTO_CARP                      = 0x70
+	IPPROTO_DIVERT                    = 0x102
+	IPPROTO_DONE                      = 0x101
+	IPPROTO_DSTOPTS                   = 0x3c
+	IPPROTO_EGP                       = 0x8
+	IPPROTO_ENCAP                     = 0x62
+	IPPROTO_EON                       = 0x50
+	IPPROTO_ESP                       = 0x32
+	IPPROTO_ETHERIP                   = 0x61
+	IPPROTO_FRAGMENT                  = 0x2c
+	IPPROTO_GGP                       = 0x3
+	IPPROTO_GRE                       = 0x2f
+	IPPROTO_HOPOPTS                   = 0
+	IPPROTO_ICMP                      = 0x1
+	IPPROTO_ICMPV6                    = 0x3a
+	IPPROTO_IDP                       = 0x16
+	IPPROTO_IGMP                      = 0x2
+	IPPROTO_IP                        = 0
+	IPPROTO_IPCOMP                    = 0x6c
+	IPPROTO_IPIP                      = 0x4
+	IPPROTO_IPV4                      = 0x4
+	IPPROTO_IPV6                      = 0x29
+	IPPROTO_MAX                       = 0x100
+	IPPROTO_MAXID                     = 0x103
+	IPPROTO_MOBILE                    = 0x37
+	IPPROTO_MPLS                      = 0x89
+	IPPROTO_NONE                      = 0x3b
+	IPPROTO_PFSYNC                    = 0xf0
+	IPPROTO_PIM                       = 0x67
+	IPPROTO_PUP                       = 0xc
+	IPPROTO_RAW                       = 0xff
+	IPPROTO_ROUTING                   = 0x2b
+	IPPROTO_RSVP                      = 0x2e
+	IPPROTO_TCP                       = 0x6
+	IPPROTO_TP                        = 0x1d
+	IPPROTO_UDP                       = 0x11
+	IPV6_AUTH_LEVEL                   = 0x35
+	IPV6_AUTOFLOWLABEL                = 0x3b
+	IPV6_CHECKSUM                     = 0x1a
+	IPV6_DEFAULT_MULTICAST_HOPS       = 0x1
+	IPV6_DEFAULT_MULTICAST_LOOP       = 0x1
+	IPV6_DEFHLIM                      = 0x40
+	IPV6_DONTFRAG                     = 0x3e
+	IPV6_DSTOPTS                      = 0x32
+	IPV6_ESP_NETWORK_LEVEL            = 0x37
+	IPV6_ESP_TRANS_LEVEL              = 0x36
+	IPV6_FAITH                        = 0x1d
+	IPV6_FLOWINFO_MASK                = 0xffffff0f
+	IPV6_FLOWLABEL_MASK               = 0xffff0f00
+	IPV6_FRAGTTL                      = 0x78
+	IPV6_HLIMDEC                      = 0x1
+	IPV6_HOPLIMIT                     = 0x2f
+	IPV6_HOPOPTS                      = 0x31
+	IPV6_IPCOMP_LEVEL                 = 0x3c
+	IPV6_JOIN_GROUP                   = 0xc
+	IPV6_LEAVE_GROUP                  = 0xd
+	IPV6_MAXHLIM                      = 0xff
+	IPV6_MAXPACKET                    = 0xffff
+	IPV6_MMTU                         = 0x500
+	IPV6_MULTICAST_HOPS               = 0xa
+	IPV6_MULTICAST_IF                 = 0x9
+	IPV6_MULTICAST_LOOP               = 0xb
+	IPV6_NEXTHOP                      = 0x30
+	IPV6_OPTIONS                      = 0x1
+	IPV6_PATHMTU                      = 0x2c
+	IPV6_PIPEX                        = 0x3f
+	IPV6_PKTINFO                      = 0x2e
+	IPV6_PORTRANGE                    = 0xe
+	IPV6_PORTRANGE_DEFAULT            = 0
+	IPV6_PORTRANGE_HIGH               = 0x1
+	IPV6_PORTRANGE_LOW                = 0x2
+	IPV6_RECVDSTOPTS                  = 0x28
+	IPV6_RECVHOPLIMIT                 = 0x25
+	IPV6_RECVHOPOPTS                  = 0x27
+	IPV6_RECVPATHMTU                  = 0x2b
+	IPV6_RECVPKTINFO                  = 0x24
+	IPV6_RECVRTHDR                    = 0x26
+	IPV6_RECVTCLASS                   = 0x39
+	IPV6_RTABLE                       = 0x1021
+	IPV6_RTHDR                        = 0x33
+	IPV6_RTHDRDSTOPTS                 = 0x23
+	IPV6_RTHDR_LOOSE                  = 0
+	IPV6_RTHDR_STRICT                 = 0x1
+	IPV6_RTHDR_TYPE_0                 = 0
+	IPV6_SOCKOPT_RESERVED1            = 0x3
+	IPV6_TCLASS                       = 0x3d
+	IPV6_UNICAST_HOPS                 = 0x4
+	IPV6_USE_MIN_MTU                  = 0x2a
+	IPV6_V6ONLY                       = 0x1b
+	IPV6_VERSION                      = 0x60
+	IPV6_VERSION_MASK                 = 0xf0
+	IP_ADD_MEMBERSHIP                 = 0xc
+	IP_AUTH_LEVEL                     = 0x14
+	IP_DEFAULT_MULTICAST_LOOP         = 0x1
+	IP_DEFAULT_MULTICAST_TTL          = 0x1
+	IP_DF                             = 0x4000
+	IP_DROP_MEMBERSHIP                = 0xd
+	IP_ESP_NETWORK_LEVEL              = 0x16
+	IP_ESP_TRANS_LEVEL                = 0x15
+	IP_HDRINCL                        = 0x2
+	IP_IPCOMP_LEVEL                   = 0x1d
+	IP_IPSEC_LOCAL_AUTH               = 0x1b
+	IP_IPSEC_LOCAL_CRED               = 0x19
+	IP_IPSEC_LOCAL_ID                 = 0x17
+	IP_IPSEC_REMOTE_AUTH              = 0x1c
+	IP_IPSEC_REMOTE_CRED              = 0x1a
+	IP_IPSEC_REMOTE_ID                = 0x18
+	IP_MAXPACKET                      = 0xffff
+	IP_MAX_MEMBERSHIPS                = 0xfff
+	IP_MF                             = 0x2000
+	IP_MINTTL                         = 0x20
+	IP_MIN_MEMBERSHIPS                = 0xf
+	IP_MSS                            = 0x240
+	IP_MULTICAST_IF                   = 0x9
+	IP_MULTICAST_LOOP                 = 0xb
+	IP_MULTICAST_TTL                  = 0xa
+	IP_OFFMASK                        = 0x1fff
+	IP_OPTIONS                        = 0x1
+	IP_PIPEX                          = 0x22
+	IP_PORTRANGE                      = 0x13
+	IP_PORTRANGE_DEFAULT              = 0
+	IP_PORTRANGE_HIGH                 = 0x1
+	IP_PORTRANGE_LOW                  = 0x2
+	IP_RECVDSTADDR                    = 0x7
+	IP_RECVDSTPORT                    = 0x21
+	IP_RECVIF                         = 0x1e
+	IP_RECVOPTS                       = 0x5
+	IP_RECVRETOPTS                    = 0x6
+	IP_RECVRTABLE                     = 0x23
+	IP_RECVTTL                        = 0x1f
+	IP_RETOPTS                        = 0x8
+	IP_RF                             = 0x8000
+	IP_RTABLE                         = 0x1021
+	IP_TOS                            = 0x3
+	IP_TTL                            = 0x4
+	MAP_ET_KVAGUARD                   = 0x10
+	MSG_BCAST                         = 0x100
+	MSG_CTRUNC                        = 0x20
+	MSG_DONTROUTE                     = 0x4
+	MSG_DONTWAIT                      = 0x80
+	MSG_EOR                           = 0x8
+	MSG_MCAST                         = 0x200
+	MSG_OOB                           = 0x1
+	MSG_PEEK                          = 0x2
+	MSG_TRUNC                         = 0x10
+	MSG_WAITALL                       = 0x40
+	NAME_MAX                          = 0xff
+	NET_RT_DUMP                       = 0x1
+	NET_RT_FLAGS                      = 0x2
+	NET_RT_IFLIST                     = 0x3
+	NET_RT_MAXID                      = 0x6
+	NET_RT_STATS                      = 0x4
+	NET_RT_TABLE                      = 0x5
+	O_ACCMODE                         = 0x3
+	O_APPEND                          = 0x8
+	O_ASYNC                           = 0x40
+	O_CLOEXEC                         = 0x10000
+	O_CREAT                           = 0x200
+	O_DIRECTORY                       = 0x20000
+	O_DSYNC                           = 0x80
+	O_EXCL                            = 0x800
+	O_EXLOCK                          = 0x20
+	O_FSYNC                           = 0x80
+	O_NDELAY                          = 0x4
+	O_NOCTTY                          = 0x8000
+	O_NOFOLLOW                        = 0x100
+	O_NONBLOCK                        = 0x4
+	O_RDONLY                          = 0
+	O_RDWR                            = 0x2
+	O_RSYNC                           = 0x80
+	O_SHLOCK                          = 0x10
+	O_SYNC                            = 0x80
+	O_TRUNC                           = 0x400
+	O_WRONLY                          = 0x1
+	RTAX_AUTHOR                       = 0x6
+	RTAX_BRD                          = 0x7
+	RTAX_DST                          = 0
+	RTAX_GATEWAY                      = 0x1
+	RTAX_GENMASK                      = 0x3
+	RTAX_IFA                          = 0x5
+	RTAX_IFP                          = 0x4
+	RTAX_LABEL                        = 0xa
+	RTAX_MAX                          = 0xb
+	RTAX_NETMASK                      = 0x2
+	RTAX_SRC                          = 0x8
+	RTAX_SRCMASK                      = 0x9
+	RTA_AUTHOR                        = 0x40
+	RTA_BRD                           = 0x80
+	RTA_DST                           = 0x1
+	RTA_GATEWAY                       = 0x2
+	RTA_GENMASK                       = 0x8
+	RTA_IFA                           = 0x20
+	RTA_IFP                           = 0x10
+	RTA_LABEL                         = 0x400
+	RTA_NETMASK                       = 0x4
+	RTA_SRC                           = 0x100
+	RTA_SRCMASK                       = 0x200
+	RTF_ANNOUNCE                      = 0x4000
+	RTF_BLACKHOLE                     = 0x1000
+	RTF_CLONED                        = 0x10000
+	RTF_CLONING                       = 0x100
+	RTF_DONE                          = 0x40
+	RTF_DYNAMIC                       = 0x10
+	RTF_FMASK                         = 0x8f808
+	RTF_GATEWAY                       = 0x2
+	RTF_HOST                          = 0x4
+	RTF_JUMBO                         = 0x80000
+	RTF_LLINFO                        = 0x400
+	RTF_MASK                          = 0x80
+	RTF_MODIFIED                      = 0x20
+	RTF_MPATH                         = 0x40000
+	RTF_MPLS                          = 0x100000
+	RTF_PERMANENT_ARP                 = 0x2000
+	RTF_PROTO1                        = 0x8000
+	RTF_PROTO2                        = 0x4000
+	RTF_PROTO3                        = 0x2000
+	RTF_REJECT                        = 0x8
+	RTF_SOURCE                        = 0x20000
+	RTF_STATIC                        = 0x800
+	RTF_TUNNEL                        = 0x100000
+	RTF_UP                            = 0x1
+	RTF_USETRAILERS                   = 0x8000
+	RTF_XRESOLVE                      = 0x200
+	RTM_ADD                           = 0x1
+	RTM_CHANGE                        = 0x3
+	RTM_DELADDR                       = 0xd
+	RTM_DELETE                        = 0x2
+	RTM_DESYNC                        = 0x10
+	RTM_GET                           = 0x4
+	RTM_IFANNOUNCE                    = 0xf
+	RTM_IFINFO                        = 0xe
+	RTM_LOCK                          = 0x8
+	RTM_LOSING                        = 0x5
+	RTM_MAXSIZE                       = 0x800
+	RTM_MISS                          = 0x7
+	RTM_NEWADDR                       = 0xc
+	RTM_REDIRECT                      = 0x6
+	RTM_RESOLVE                       = 0xb
+	RTM_RTTUNIT                       = 0xf4240
+	RTM_VERSION                       = 0x4
+	RTV_EXPIRE                        = 0x4
+	RTV_HOPCOUNT                      = 0x2
+	RTV_MTU                           = 0x1
+	RTV_RPIPE                         = 0x8
+	RTV_RTT                           = 0x40
+	RTV_RTTVAR                        = 0x80
+	RTV_SPIPE                         = 0x10
+	RTV_SSTHRESH                      = 0x20
+	SCM_CREDS                         = 0x2
+	SCM_RIGHTS                        = 0x1
+	SCM_TIMESTAMP                     = 0x4
+	SHUT_RD                           = 0
+	SHUT_RDWR                         = 0x2
+	SHUT_WR                           = 0x1
+	SIGABRT                           = 0x6
+	SIGALRM                           = 0xe
+	SIGBUS                            = 0xa
+	SIGCHLD                           = 0x14
+	SIGCONT                           = 0x13
+	SIGEMT                            = 0x7
+	SIGFPE                            = 0x8
+	SIGHUP                            = 0x1
+	SIGILL                            = 0x4
+	SIGINFO                           = 0x1d
+	SIGINT                            = 0x2
+	SIGIO                             = 0x17
+	SIGIOT                            = 0x6
+	SIGKILL                           = 0x9
+	SIGPIPE                           = 0xd
+	SIGPROF                           = 0x1b
+	SIGQUIT                           = 0x3
+	SIGSEGV                           = 0xb
+	SIGSTOP                           = 0x11
+	SIGSYS                            = 0xc
+	SIGTERM                           = 0xf
+	SIGTHR                            = 0x20
+	SIGTRAP                           = 0x5
+	SIGTSTP                           = 0x12
+	SIGTTIN                           = 0x15
+	SIGTTOU                           = 0x16
+	SIGURG                            = 0x10
+	SIGUSR1                           = 0x1e
+	SIGUSR2                           = 0x1f
+	SIGVTALRM                         = 0x1a
+	SIGWINCH                          = 0x1c
+	SIGXCPU                           = 0x18
+	SIGXFSZ                           = 0x19
+	SIOCADDMULTI                      = 0x80206931
+	SIOCAIFADDR                       = 0x8040691a
+	SIOCAIFGROUP                      = 0x80246987
+	SIOCALIFADDR                      = 0x8218691c
+	SIOCATMARK                        = 0x40047307
+	SIOCBRDGADD                       = 0x8054693c
+	SIOCBRDGADDS                      = 0x80546941
+	SIOCBRDGARL                       = 0x806e694d
+	SIOCBRDGDADDR                     = 0x80286947
+	SIOCBRDGDEL                       = 0x8054693d
+	SIOCBRDGDELS                      = 0x80546942
+	SIOCBRDGFLUSH                     = 0x80546948
+	SIOCBRDGFRL                       = 0x806e694e
+	SIOCBRDGGCACHE                    = 0xc0146941
+	SIOCBRDGGFD                       = 0xc0146952
+	SIOCBRDGGHT                       = 0xc0146951
+	SIOCBRDGGIFFLGS                   = 0xc054693e
+	SIOCBRDGGMA                       = 0xc0146953
+	SIOCBRDGGPARAM                    = 0xc0386958
+	SIOCBRDGGPRI                      = 0xc0146950
+	SIOCBRDGGRL                       = 0xc028694f
+	SIOCBRDGGSIFS                     = 0xc054693c
+	SIOCBRDGGTO                       = 0xc0146946
+	SIOCBRDGIFS                       = 0xc0546942
+	SIOCBRDGRTS                       = 0xc0186943
+	SIOCBRDGSADDR                     = 0xc0286944
+	SIOCBRDGSCACHE                    = 0x80146940
+	SIOCBRDGSFD                       = 0x80146952
+	SIOCBRDGSHT                       = 0x80146951
+	SIOCBRDGSIFCOST                   = 0x80546955
+	SIOCBRDGSIFFLGS                   = 0x8054693f
+	SIOCBRDGSIFPRIO                   = 0x80546954
+	SIOCBRDGSMA                       = 0x80146953
+	SIOCBRDGSPRI                      = 0x80146950
+	SIOCBRDGSPROTO                    = 0x8014695a
+	SIOCBRDGSTO                       = 0x80146945
+	SIOCBRDGSTXHC                     = 0x80146959
+	SIOCDELMULTI                      = 0x80206932
+	SIOCDIFADDR                       = 0x80206919
+	SIOCDIFGROUP                      = 0x80246989
+	SIOCDIFPHYADDR                    = 0x80206949
+	SIOCDLIFADDR                      = 0x8218691e
+	SIOCGETKALIVE                     = 0xc01869a4
+	SIOCGETLABEL                      = 0x8020699a
+	SIOCGETPFLOW                      = 0xc02069fe
+	SIOCGETPFSYNC                     = 0xc02069f8
+	SIOCGETSGCNT                      = 0xc0147534
+	SIOCGETVIFCNT                     = 0xc0147533
+	SIOCGETVLANPRIO                   = 0xc0206990
+	SIOCGHIWAT                        = 0x40047301
+	SIOCGIFADDR                       = 0xc0206921
+	SIOCGIFASYNCMAP                   = 0xc020697c
+	SIOCGIFBRDADDR                    = 0xc0206923
+	SIOCGIFCONF                       = 0xc0086924
+	SIOCGIFDATA                       = 0xc020691b
+	SIOCGIFDESCR                      = 0xc0206981
+	SIOCGIFDSTADDR                    = 0xc0206922
+	SIOCGIFFLAGS                      = 0xc0206911
+	SIOCGIFGATTR                      = 0xc024698b
+	SIOCGIFGENERIC                    = 0xc020693a
+	SIOCGIFGMEMB                      = 0xc024698a
+	SIOCGIFGROUP                      = 0xc0246988
+	SIOCGIFMEDIA                      = 0xc0286936
+	SIOCGIFMETRIC                     = 0xc0206917
+	SIOCGIFMTU                        = 0xc020697e
+	SIOCGIFNETMASK                    = 0xc0206925
+	SIOCGIFPDSTADDR                   = 0xc0206948
+	SIOCGIFPRIORITY                   = 0xc020699c
+	SIOCGIFPSRCADDR                   = 0xc0206947
+	SIOCGIFRDOMAIN                    = 0xc02069a0
+	SIOCGIFRTLABEL                    = 0xc0206983
+	SIOCGIFTIMESLOT                   = 0xc0206986
+	SIOCGIFXFLAGS                     = 0xc020699e
+	SIOCGLIFADDR                      = 0xc218691d
+	SIOCGLIFPHYADDR                   = 0xc218694b
+	SIOCGLIFPHYRTABLE                 = 0xc02069a2
+	SIOCGLOWAT                        = 0x40047303
+	SIOCGPGRP                         = 0x40047309
+	SIOCGVH                           = 0xc02069f6
+	SIOCIFCREATE                      = 0x8020697a
+	SIOCIFDESTROY                     = 0x80206979
+	SIOCIFGCLONERS                    = 0xc00c6978
+	SIOCSETKALIVE                     = 0x801869a3
+	SIOCSETLABEL                      = 0x80206999
+	SIOCSETPFLOW                      = 0x802069fd
+	SIOCSETPFSYNC                     = 0x802069f7
+	SIOCSETVLANPRIO                   = 0x8020698f
+	SIOCSHIWAT                        = 0x80047300
+	SIOCSIFADDR                       = 0x8020690c
+	SIOCSIFASYNCMAP                   = 0x8020697d
+	SIOCSIFBRDADDR                    = 0x80206913
+	SIOCSIFDESCR                      = 0x80206980
+	SIOCSIFDSTADDR                    = 0x8020690e
+	SIOCSIFFLAGS                      = 0x80206910
+	SIOCSIFGATTR                      = 0x8024698c
+	SIOCSIFGENERIC                    = 0x80206939
+	SIOCSIFLLADDR                     = 0x8020691f
+	SIOCSIFMEDIA                      = 0xc0206935
+	SIOCSIFMETRIC                     = 0x80206918
+	SIOCSIFMTU                        = 0x8020697f
+	SIOCSIFNETMASK                    = 0x80206916
+	SIOCSIFPHYADDR                    = 0x80406946
+	SIOCSIFPRIORITY                   = 0x8020699b
+	SIOCSIFRDOMAIN                    = 0x8020699f
+	SIOCSIFRTLABEL                    = 0x80206982
+	SIOCSIFTIMESLOT                   = 0x80206985
+	SIOCSIFXFLAGS                     = 0x8020699d
+	SIOCSLIFPHYADDR                   = 0x8218694a
+	SIOCSLIFPHYRTABLE                 = 0x802069a1
+	SIOCSLOWAT                        = 0x80047302
+	SIOCSPGRP                         = 0x80047308
+	SIOCSVH                           = 0xc02069f5
+	SOCK_DGRAM                        = 0x2
+	SOCK_RAW                          = 0x3
+	SOCK_RDM                          = 0x4
+	SOCK_SEQPACKET                    = 0x5
+	SOCK_STREAM                       = 0x1
+	SOL_SOCKET                        = 0xffff
+	SOMAXCONN                         = 0x80
+	SO_ACCEPTCONN                     = 0x2
+	SO_BINDANY                        = 0x1000
+	SO_BROADCAST                      = 0x20
+	SO_DEBUG                          = 0x1
+	SO_DONTROUTE                      = 0x10
+	SO_ERROR                          = 0x1007
+	SO_JUMBO                          = 0x400
+	SO_KEEPALIVE                      = 0x8
+	SO_LINGER                         = 0x80
+	SO_NETPROC                        = 0x1020
+	SO_OOBINLINE                      = 0x100
+	SO_PEERCRED                       = 0x1022
+	SO_RCVBUF                         = 0x1002
+	SO_RCVLOWAT                       = 0x1004
+	SO_RCVTIMEO                       = 0x1006
+	SO_REUSEADDR                      = 0x4
+	SO_REUSEPORT                      = 0x200
+	SO_RTABLE                         = 0x1021
+	SO_SNDBUF                         = 0x1001
+	SO_SNDLOWAT                       = 0x1003
+	SO_SNDTIMEO                       = 0x1005
+	SO_SPLICE                         = 0x1023
+	SO_TIMESTAMP                      = 0x800
+	SO_TYPE                           = 0x1008
+	SO_USELOOPBACK                    = 0x40
+	TCP_MAXBURST                      = 0x4
+	TCP_MAXSEG                        = 0x2
+	TCP_MAXWIN                        = 0xffff
+	TCP_MAX_SACK                      = 0x3
+	TCP_MAX_WINSHIFT                  = 0xe
+	TCP_MD5SIG                        = 0x4
+	TCP_MSS                           = 0x200
+	TCP_NODELAY                       = 0x1
+	TCP_NSTATES                       = 0xb
+	TCP_SACK_ENABLE                   = 0x8
+	TIOCCBRK                          = 0x2000747a
+	TIOCCDTR                          = 0x20007478
+	TIOCCONS                          = 0x80047462
+	TIOCDRAIN                         = 0x2000745e
+	TIOCEXCL                          = 0x2000740d
+	TIOCEXT                           = 0x80047460
+	TIOCFLAG_CLOCAL                   = 0x2
+	TIOCFLAG_CRTSCTS                  = 0x4
+	TIOCFLAG_MDMBUF                   = 0x8
+	TIOCFLAG_PPS                      = 0x10
+	TIOCFLAG_SOFTCAR                  = 0x1
+	TIOCFLUSH                         = 0x80047410
+	TIOCGETA                          = 0x402c7413
+	TIOCGETD                          = 0x4004741a
+	TIOCGFLAGS                        = 0x4004745d
+	TIOCGPGRP                         = 0x40047477
+	TIOCGTSTAMP                       = 0x4008745b
+	TIOCGWINSZ                        = 0x40087468
+	TIOCMBIC                          = 0x8004746b
+	TIOCMBIS                          = 0x8004746c
+	TIOCMGET                          = 0x4004746a
+	TIOCMODG                          = 0x4004746a
+	TIOCMODS                          = 0x8004746d
+	TIOCMSET                          = 0x8004746d
+	TIOCM_CAR                         = 0x40
+	TIOCM_CD                          = 0x40
+	TIOCM_CTS                         = 0x20
+	TIOCM_DSR                         = 0x100
+	TIOCM_DTR                         = 0x2
+	TIOCM_LE                          = 0x1
+	TIOCM_RI                          = 0x80
+	TIOCM_RNG                         = 0x80
+	TIOCM_RTS                         = 0x4
+	TIOCM_SR                          = 0x10
+	TIOCM_ST                          = 0x8
+	TIOCNOTTY                         = 0x20007471
+	TIOCNXCL                          = 0x2000740e
+	TIOCOUTQ                          = 0x40047473
+	TIOCPKT                           = 0x80047470
+	TIOCPKT_DATA                      = 0
+	TIOCPKT_DOSTOP                    = 0x20
+	TIOCPKT_FLUSHREAD                 = 0x1
+	TIOCPKT_FLUSHWRITE                = 0x2
+	TIOCPKT_IOCTL                     = 0x40
+	TIOCPKT_NOSTOP                    = 0x10
+	TIOCPKT_START                     = 0x8
+	TIOCPKT_STOP                      = 0x4
+	TIOCREMOTE                        = 0x80047469
+	TIOCSBRK                          = 0x2000747b
+	TIOCSCTTY                         = 0x20007461
+	TIOCSDTR                          = 0x20007479
+	TIOCSETA                          = 0x802c7414
+	TIOCSETAF                         = 0x802c7416
+	TIOCSETAW                         = 0x802c7415
+	TIOCSETD                          = 0x8004741b
+	TIOCSFLAGS                        = 0x8004745c
+	TIOCSIG                           = 0x8004745f
+	TIOCSPGRP                         = 0x80047476
+	TIOCSTART                         = 0x2000746e
+	TIOCSTAT                          = 0x80047465
+	TIOCSTI                           = 0x80017472
+	TIOCSTOP                          = 0x2000746f
+	TIOCSTSTAMP                       = 0x8008745a
+	TIOCSWINSZ                        = 0x80087467
+	TIOCUCNTL                         = 0x80047466
+	WALTSIG                           = 0x4
+	WCONTINUED                        = 0x8
+	WCOREFLAG                         = 0x80
+	WNOHANG                           = 0x1
+	WSTOPPED                          = 0x7f
+	WUNTRACED                         = 0x2
+)
+
+// Types
+
+// Error table
+var errors = [...]string{
+	1:  "operation not permitted",
+	2:  "no such file or directory",
+	3:  "no such process",
+	4:  "interrupted system call",
+	5:  "input/output error",
+	6:  "device not configured",
+	7:  "argument list too long",
+	8:  "exec format error",
+	9:  "bad file descriptor",
+	10: "no child processes",
+	11: "resource deadlock avoided",
+	12: "cannot allocate memory",
+	13: "permission denied",
+	14: "bad address",
+	15: "block device required",
+	16: "device busy",
+	17: "file exists",
+	18: "cross-device link",
+	19: "operation not supported by device",
+	20: "not a directory",
+	21: "is a directory",
+	22: "invalid argument",
+	23: "too many open files in system",
+	24: "too many open files",
+	25: "inappropriate ioctl for device",
+	26: "text file busy",
+	27: "file too large",
+	28: "no space left on device",
+	29: "illegal seek",
+	30: "read-only file system",
+	31: "too many links",
+	32: "broken pipe",
+	33: "numerical argument out of domain",
+	34: "result too large",
+	35: "resource temporarily unavailable",
+	36: "operation now in progress",
+	37: "operation already in progress",
+	38: "socket operation on non-socket",
+	39: "destination address required",
+	40: "message too long",
+	41: "protocol wrong type for socket",
+	42: "protocol not available",
+	43: "protocol not supported",
+	44: "socket type not supported",
+	45: "operation not supported",
+	46: "protocol family not supported",
+	47: "address family not supported by protocol family",
+	48: "address already in use",
+	49: "can't assign requested address",
+	50: "network is down",
+	51: "network is unreachable",
+	52: "network dropped connection on reset",
+	53: "software caused connection abort",
+	54: "connection reset by peer",
+	55: "no buffer space available",
+	56: "socket is already connected",
+	57: "socket is not connected",
+	58: "can't send after socket shutdown",
+	59: "too many references: can't splice",
+	60: "connection timed out",
+	61: "connection refused",
+	62: "too many levels of symbolic links",
+	63: "file name too long",
+	64: "host is down",
+	65: "no route to host",
+	66: "directory not empty",
+	67: "too many processes",
+	68: "too many users",
+	69: "disc quota exceeded",
+	70: "stale NFS file handle",
+	71: "too many levels of remote in path",
+	72: "RPC struct is bad",
+	73: "RPC version wrong",
+	74: "RPC prog. not avail",
+	75: "program version wrong",
+	76: "bad procedure for program",
+	77: "no locks available",
+	78: "function not implemented",
+	79: "inappropriate file type or format",
+	80: "authentication error",
+	81: "need authenticator",
+	82: "IPsec processing failure",
+	83: "attribute not found",
+	84: "illegal byte sequence",
+	85: "no medium found",
+	86: "wrong medium type",
+	87: "value too large to be stored in data type",
+	88: "operation canceled",
+	89: "identifier removed",
+	90: "no message of desired type",
+	91: "not supported",
+}
diff --git a/src/pkg/syscall/zerrors_openbsd_amd64.go b/src/pkg/syscall/zerrors_openbsd_amd64.go
new file mode 100644
index 0000000..10fa5e4
--- /dev/null
+++ b/src/pkg/syscall/zerrors_openbsd_amd64.go
@@ -0,0 +1,1367 @@
+// mkerrors.sh -f -m64
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// godefs -c gcc -f -m64 -gsyscall -f -m64 _const.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+	AF_APPLETALK                      = 0x10
+	AF_BLUETOOTH                      = 0x20
+	AF_CCITT                          = 0xa
+	AF_CHAOS                          = 0x5
+	AF_CNT                            = 0x15
+	AF_COIP                           = 0x14
+	AF_DATAKIT                        = 0x9
+	AF_DECnet                         = 0xc
+	AF_DLI                            = 0xd
+	AF_E164                           = 0x1a
+	AF_ECMA                           = 0x8
+	AF_ENCAP                          = 0x1c
+	AF_HYLINK                         = 0xf
+	AF_IMPLINK                        = 0x3
+	AF_INET                           = 0x2
+	AF_INET6                          = 0x18
+	AF_IPX                            = 0x17
+	AF_ISDN                           = 0x1a
+	AF_ISO                            = 0x7
+	AF_KEY                            = 0x1e
+	AF_LAT                            = 0xe
+	AF_LINK                           = 0x12
+	AF_LOCAL                          = 0x1
+	AF_MAX                            = 0x24
+	AF_MPLS                           = 0x21
+	AF_NATM                           = 0x1b
+	AF_NS                             = 0x6
+	AF_OSI                            = 0x7
+	AF_PUP                            = 0x4
+	AF_ROUTE                          = 0x11
+	AF_SIP                            = 0x1d
+	AF_SNA                            = 0xb
+	AF_UNIX                           = 0x1
+	AF_UNSPEC                         = 0
+	ARPHRD_ETHER                      = 0x1
+	ARPHRD_FRELAY                     = 0xf
+	ARPHRD_IEEE1394                   = 0x18
+	ARPHRD_IEEE802                    = 0x6
+	BIOCFLUSH                         = 0x20004268
+	BIOCGBLEN                         = 0x40044266
+	BIOCGDIRFILT                      = 0x4004427c
+	BIOCGDLT                          = 0x4004426a
+	BIOCGDLTLIST                      = 0xc010427b
+	BIOCGETIF                         = 0x4020426b
+	BIOCGFILDROP                      = 0x40044278
+	BIOCGHDRCMPLT                     = 0x40044274
+	BIOCGRSIG                         = 0x40044273
+	BIOCGRTIMEOUT                     = 0x4010426e
+	BIOCGSTATS                        = 0x4008426f
+	BIOCIMMEDIATE                     = 0x80044270
+	BIOCLOCK                          = 0x20004276
+	BIOCPROMISC                       = 0x20004269
+	BIOCSBLEN                         = 0xc0044266
+	BIOCSDIRFILT                      = 0x8004427d
+	BIOCSDLT                          = 0x8004427a
+	BIOCSETF                          = 0x80104267
+	BIOCSETIF                         = 0x8020426c
+	BIOCSETWF                         = 0x80104277
+	BIOCSFILDROP                      = 0x80044279
+	BIOCSHDRCMPLT                     = 0x80044275
+	BIOCSRSIG                         = 0x80044272
+	BIOCSRTIMEOUT                     = 0x8010426d
+	BIOCVERSION                       = 0x40044271
+	BPF_A                             = 0x10
+	BPF_ABS                           = 0x20
+	BPF_ADD                           = 0
+	BPF_ALIGNMENT                     = 0x4
+	BPF_ALU                           = 0x4
+	BPF_AND                           = 0x50
+	BPF_B                             = 0x10
+	BPF_DIRECTION_IN                  = 0x1
+	BPF_DIRECTION_OUT                 = 0x2
+	BPF_DIV                           = 0x30
+	BPF_H                             = 0x8
+	BPF_IMM                           = 0
+	BPF_IND                           = 0x40
+	BPF_JA                            = 0
+	BPF_JEQ                           = 0x10
+	BPF_JGE                           = 0x30
+	BPF_JGT                           = 0x20
+	BPF_JMP                           = 0x5
+	BPF_JSET                          = 0x40
+	BPF_K                             = 0
+	BPF_LD                            = 0
+	BPF_LDX                           = 0x1
+	BPF_LEN                           = 0x80
+	BPF_LSH                           = 0x60
+	BPF_MAJOR_VERSION                 = 0x1
+	BPF_MAXBUFSIZE                    = 0x200000
+	BPF_MAXINSNS                      = 0x200
+	BPF_MEM                           = 0x60
+	BPF_MEMWORDS                      = 0x10
+	BPF_MINBUFSIZE                    = 0x20
+	BPF_MINOR_VERSION                 = 0x1
+	BPF_MISC                          = 0x7
+	BPF_MSH                           = 0xa0
+	BPF_MUL                           = 0x20
+	BPF_NEG                           = 0x80
+	BPF_OR                            = 0x40
+	BPF_RELEASE                       = 0x30bb6
+	BPF_RET                           = 0x6
+	BPF_RSH                           = 0x70
+	BPF_ST                            = 0x2
+	BPF_STX                           = 0x3
+	BPF_SUB                           = 0x10
+	BPF_TAX                           = 0
+	BPF_TXA                           = 0x80
+	BPF_W                             = 0
+	BPF_X                             = 0x8
+	CTL_MAXNAME                       = 0xc
+	CTL_NET                           = 0x4
+	DLT_ARCNET                        = 0x7
+	DLT_ATM_RFC1483                   = 0xb
+	DLT_AX25                          = 0x3
+	DLT_CHAOS                         = 0x5
+	DLT_EN10MB                        = 0x1
+	DLT_EN3MB                         = 0x2
+	DLT_ENC                           = 0xd
+	DLT_FDDI                          = 0xa
+	DLT_IEEE802                       = 0x6
+	DLT_IEEE802_11                    = 0x69
+	DLT_IEEE802_11_RADIO              = 0x7f
+	DLT_LOOP                          = 0xc
+	DLT_MPLS                          = 0xdb
+	DLT_NULL                          = 0
+	DLT_PFLOG                         = 0x75
+	DLT_PFSYNC                        = 0x12
+	DLT_PPP                           = 0x9
+	DLT_PPP_BSDOS                     = 0x10
+	DLT_PPP_ETHER                     = 0x33
+	DLT_PRONET                        = 0x4
+	DLT_RAW                           = 0xe
+	DLT_SLIP                          = 0x8
+	DLT_SLIP_BSDOS                    = 0xf
+	DT_BLK                            = 0x6
+	DT_CHR                            = 0x2
+	DT_DIR                            = 0x4
+	DT_FIFO                           = 0x1
+	DT_LNK                            = 0xa
+	DT_REG                            = 0x8
+	DT_SOCK                           = 0xc
+	DT_UNKNOWN                        = 0
+	E2BIG                             = 0x7
+	EACCES                            = 0xd
+	EADDRINUSE                        = 0x30
+	EADDRNOTAVAIL                     = 0x31
+	EAFNOSUPPORT                      = 0x2f
+	EAGAIN                            = 0x23
+	EALREADY                          = 0x25
+	EAUTH                             = 0x50
+	EBADF                             = 0x9
+	EBADRPC                           = 0x48
+	EBUSY                             = 0x10
+	ECANCELED                         = 0x58
+	ECHILD                            = 0xa
+	ECHO                              = 0x8
+	ECHOCTL                           = 0x40
+	ECHOE                             = 0x2
+	ECHOK                             = 0x4
+	ECHOKE                            = 0x1
+	ECHONL                            = 0x10
+	ECHOPRT                           = 0x20
+	ECONNABORTED                      = 0x35
+	ECONNREFUSED                      = 0x3d
+	ECONNRESET                        = 0x36
+	EDEADLK                           = 0xb
+	EDESTADDRREQ                      = 0x27
+	EDOM                              = 0x21
+	EDQUOT                            = 0x45
+	EEXIST                            = 0x11
+	EFAULT                            = 0xe
+	EFBIG                             = 0x1b
+	EFER_LMA                          = 0x400
+	EFER_LME                          = 0x100
+	EFER_NXE                          = 0x800
+	EFER_SCE                          = 0x1
+	EFTYPE                            = 0x4f
+	EHOSTDOWN                         = 0x40
+	EHOSTUNREACH                      = 0x41
+	EIDRM                             = 0x59
+	EILSEQ                            = 0x54
+	EINPROGRESS                       = 0x24
+	EINTR                             = 0x4
+	EINVAL                            = 0x16
+	EIO                               = 0x5
+	EIPSEC                            = 0x52
+	EISCONN                           = 0x38
+	EISDIR                            = 0x15
+	ELAST                             = 0x5b
+	ELOOP                             = 0x3e
+	EMEDIUMTYPE                       = 0x56
+	EMFILE                            = 0x18
+	EMLINK                            = 0x1f
+	EMSGSIZE                          = 0x28
+	EMT_TAGOVF                        = 0x1
+	EMUL_ENABLED                      = 0x1
+	EMUL_NATIVE                       = 0x2
+	ENAMETOOLONG                      = 0x3f
+	ENDRUNDISC                        = 0x9
+	ENEEDAUTH                         = 0x51
+	ENETDOWN                          = 0x32
+	ENETRESET                         = 0x34
+	ENETUNREACH                       = 0x33
+	ENFILE                            = 0x17
+	ENOATTR                           = 0x53
+	ENOBUFS                           = 0x37
+	ENODEV                            = 0x13
+	ENOENT                            = 0x2
+	ENOEXEC                           = 0x8
+	ENOLCK                            = 0x4d
+	ENOMEDIUM                         = 0x55
+	ENOMEM                            = 0xc
+	ENOMSG                            = 0x5a
+	ENOPROTOOPT                       = 0x2a
+	ENOSPC                            = 0x1c
+	ENOSYS                            = 0x4e
+	ENOTBLK                           = 0xf
+	ENOTCONN                          = 0x39
+	ENOTDIR                           = 0x14
+	ENOTEMPTY                         = 0x42
+	ENOTSOCK                          = 0x26
+	ENOTSUP                           = 0x5b
+	ENOTTY                            = 0x19
+	ENXIO                             = 0x6
+	EOPNOTSUPP                        = 0x2d
+	EOVERFLOW                         = 0x57
+	EPERM                             = 0x1
+	EPFNOSUPPORT                      = 0x2e
+	EPIPE                             = 0x20
+	EPROCLIM                          = 0x43
+	EPROCUNAVAIL                      = 0x4c
+	EPROGMISMATCH                     = 0x4b
+	EPROGUNAVAIL                      = 0x4a
+	EPROTONOSUPPORT                   = 0x2b
+	EPROTOTYPE                        = 0x29
+	ERANGE                            = 0x22
+	EREMOTE                           = 0x47
+	EROFS                             = 0x1e
+	ERPCMISMATCH                      = 0x49
+	ESHUTDOWN                         = 0x3a
+	ESOCKTNOSUPPORT                   = 0x2c
+	ESPIPE                            = 0x1d
+	ESRCH                             = 0x3
+	ESTALE                            = 0x46
+	ETHERMIN                          = 0x2e
+	ETHERMTU                          = 0x5dc
+	ETHERTYPE_8023                    = 0x4
+	ETHERTYPE_AARP                    = 0x80f3
+	ETHERTYPE_ACCTON                  = 0x8390
+	ETHERTYPE_AEONIC                  = 0x8036
+	ETHERTYPE_ALPHA                   = 0x814a
+	ETHERTYPE_AMBER                   = 0x6008
+	ETHERTYPE_AMOEBA                  = 0x8145
+	ETHERTYPE_AOE                     = 0x88a2
+	ETHERTYPE_APOLLO                  = 0x80f7
+	ETHERTYPE_APOLLODOMAIN            = 0x8019
+	ETHERTYPE_APPLETALK               = 0x809b
+	ETHERTYPE_APPLITEK                = 0x80c7
+	ETHERTYPE_ARGONAUT                = 0x803a
+	ETHERTYPE_ARP                     = 0x806
+	ETHERTYPE_AT                      = 0x809b
+	ETHERTYPE_ATALK                   = 0x809b
+	ETHERTYPE_ATOMIC                  = 0x86df
+	ETHERTYPE_ATT                     = 0x8069
+	ETHERTYPE_ATTSTANFORD             = 0x8008
+	ETHERTYPE_AUTOPHON                = 0x806a
+	ETHERTYPE_AXIS                    = 0x8856
+	ETHERTYPE_BCLOOP                  = 0x9003
+	ETHERTYPE_BOFL                    = 0x8102
+	ETHERTYPE_CABLETRON               = 0x7034
+	ETHERTYPE_CHAOS                   = 0x804
+	ETHERTYPE_COMDESIGN               = 0x806c
+	ETHERTYPE_COMPUGRAPHIC            = 0x806d
+	ETHERTYPE_COUNTERPOINT            = 0x8062
+	ETHERTYPE_CRONUS                  = 0x8004
+	ETHERTYPE_CRONUSVLN               = 0x8003
+	ETHERTYPE_DCA                     = 0x1234
+	ETHERTYPE_DDE                     = 0x807b
+	ETHERTYPE_DEBNI                   = 0xaaaa
+	ETHERTYPE_DECAM                   = 0x8048
+	ETHERTYPE_DECCUST                 = 0x6006
+	ETHERTYPE_DECDIAG                 = 0x6005
+	ETHERTYPE_DECDNS                  = 0x803c
+	ETHERTYPE_DECDTS                  = 0x803e
+	ETHERTYPE_DECEXPER                = 0x6000
+	ETHERTYPE_DECLAST                 = 0x8041
+	ETHERTYPE_DECLTM                  = 0x803f
+	ETHERTYPE_DECMUMPS                = 0x6009
+	ETHERTYPE_DECNETBIOS              = 0x8040
+	ETHERTYPE_DELTACON                = 0x86de
+	ETHERTYPE_DIDDLE                  = 0x4321
+	ETHERTYPE_DLOG1                   = 0x660
+	ETHERTYPE_DLOG2                   = 0x661
+	ETHERTYPE_DN                      = 0x6003
+	ETHERTYPE_DOGFIGHT                = 0x1989
+	ETHERTYPE_DSMD                    = 0x8039
+	ETHERTYPE_ECMA                    = 0x803
+	ETHERTYPE_ENCRYPT                 = 0x803d
+	ETHERTYPE_ES                      = 0x805d
+	ETHERTYPE_EXCELAN                 = 0x8010
+	ETHERTYPE_EXPERDATA               = 0x8049
+	ETHERTYPE_FLIP                    = 0x8146
+	ETHERTYPE_FLOWCONTROL             = 0x8808
+	ETHERTYPE_FRARP                   = 0x808
+	ETHERTYPE_GENDYN                  = 0x8068
+	ETHERTYPE_HAYES                   = 0x8130
+	ETHERTYPE_HIPPI_FP                = 0x8180
+	ETHERTYPE_HITACHI                 = 0x8820
+	ETHERTYPE_HP                      = 0x8005
+	ETHERTYPE_IEEEPUP                 = 0xa00
+	ETHERTYPE_IEEEPUPAT               = 0xa01
+	ETHERTYPE_IMLBL                   = 0x4c42
+	ETHERTYPE_IMLBLDIAG               = 0x424c
+	ETHERTYPE_IP                      = 0x800
+	ETHERTYPE_IPAS                    = 0x876c
+	ETHERTYPE_IPV6                    = 0x86dd
+	ETHERTYPE_IPX                     = 0x8137
+	ETHERTYPE_IPXNEW                  = 0x8037
+	ETHERTYPE_KALPANA                 = 0x8582
+	ETHERTYPE_LANBRIDGE               = 0x8038
+	ETHERTYPE_LANPROBE                = 0x8888
+	ETHERTYPE_LAT                     = 0x6004
+	ETHERTYPE_LBACK                   = 0x9000
+	ETHERTYPE_LITTLE                  = 0x8060
+	ETHERTYPE_LLDP                    = 0x88cc
+	ETHERTYPE_LOGICRAFT               = 0x8148
+	ETHERTYPE_LOOPBACK                = 0x9000
+	ETHERTYPE_MATRA                   = 0x807a
+	ETHERTYPE_MAX                     = 0xffff
+	ETHERTYPE_MERIT                   = 0x807c
+	ETHERTYPE_MICP                    = 0x873a
+	ETHERTYPE_MOPDL                   = 0x6001
+	ETHERTYPE_MOPRC                   = 0x6002
+	ETHERTYPE_MOTOROLA                = 0x818d
+	ETHERTYPE_MPLS                    = 0x8847
+	ETHERTYPE_MPLS_MCAST              = 0x8848
+	ETHERTYPE_MUMPS                   = 0x813f
+	ETHERTYPE_NBPCC                   = 0x3c04
+	ETHERTYPE_NBPCLAIM                = 0x3c09
+	ETHERTYPE_NBPCLREQ                = 0x3c05
+	ETHERTYPE_NBPCLRSP                = 0x3c06
+	ETHERTYPE_NBPCREQ                 = 0x3c02
+	ETHERTYPE_NBPCRSP                 = 0x3c03
+	ETHERTYPE_NBPDG                   = 0x3c07
+	ETHERTYPE_NBPDGB                  = 0x3c08
+	ETHERTYPE_NBPDLTE                 = 0x3c0a
+	ETHERTYPE_NBPRAR                  = 0x3c0c
+	ETHERTYPE_NBPRAS                  = 0x3c0b
+	ETHERTYPE_NBPRST                  = 0x3c0d
+	ETHERTYPE_NBPSCD                  = 0x3c01
+	ETHERTYPE_NBPVCD                  = 0x3c00
+	ETHERTYPE_NBS                     = 0x802
+	ETHERTYPE_NCD                     = 0x8149
+	ETHERTYPE_NESTAR                  = 0x8006
+	ETHERTYPE_NETBEUI                 = 0x8191
+	ETHERTYPE_NOVELL                  = 0x8138
+	ETHERTYPE_NS                      = 0x600
+	ETHERTYPE_NSAT                    = 0x601
+	ETHERTYPE_NSCOMPAT                = 0x807
+	ETHERTYPE_NTRAILER                = 0x10
+	ETHERTYPE_OS9                     = 0x7007
+	ETHERTYPE_OS9NET                  = 0x7009
+	ETHERTYPE_PACER                   = 0x80c6
+	ETHERTYPE_PAE                     = 0x888e
+	ETHERTYPE_PCS                     = 0x4242
+	ETHERTYPE_PLANNING                = 0x8044
+	ETHERTYPE_PPP                     = 0x880b
+	ETHERTYPE_PPPOE                   = 0x8864
+	ETHERTYPE_PPPOEDISC               = 0x8863
+	ETHERTYPE_PRIMENTS                = 0x7031
+	ETHERTYPE_PUP                     = 0x200
+	ETHERTYPE_PUPAT                   = 0x200
+	ETHERTYPE_QINQ                    = 0x88a8
+	ETHERTYPE_RACAL                   = 0x7030
+	ETHERTYPE_RATIONAL                = 0x8150
+	ETHERTYPE_RAWFR                   = 0x6559
+	ETHERTYPE_RCL                     = 0x1995
+	ETHERTYPE_RDP                     = 0x8739
+	ETHERTYPE_RETIX                   = 0x80f2
+	ETHERTYPE_REVARP                  = 0x8035
+	ETHERTYPE_SCA                     = 0x6007
+	ETHERTYPE_SECTRA                  = 0x86db
+	ETHERTYPE_SECUREDATA              = 0x876d
+	ETHERTYPE_SGITW                   = 0x817e
+	ETHERTYPE_SG_BOUNCE               = 0x8016
+	ETHERTYPE_SG_DIAG                 = 0x8013
+	ETHERTYPE_SG_NETGAMES             = 0x8014
+	ETHERTYPE_SG_RESV                 = 0x8015
+	ETHERTYPE_SIMNET                  = 0x5208
+	ETHERTYPE_SLOW                    = 0x8809
+	ETHERTYPE_SNA                     = 0x80d5
+	ETHERTYPE_SNMP                    = 0x814c
+	ETHERTYPE_SONIX                   = 0xfaf5
+	ETHERTYPE_SPIDER                  = 0x809f
+	ETHERTYPE_SPRITE                  = 0x500
+	ETHERTYPE_STP                     = 0x8181
+	ETHERTYPE_TALARIS                 = 0x812b
+	ETHERTYPE_TALARISMC               = 0x852b
+	ETHERTYPE_TCPCOMP                 = 0x876b
+	ETHERTYPE_TCPSM                   = 0x9002
+	ETHERTYPE_TEC                     = 0x814f
+	ETHERTYPE_TIGAN                   = 0x802f
+	ETHERTYPE_TRAIL                   = 0x1000
+	ETHERTYPE_TRANSETHER              = 0x6558
+	ETHERTYPE_TYMSHARE                = 0x802e
+	ETHERTYPE_UBBST                   = 0x7005
+	ETHERTYPE_UBDEBUG                 = 0x900
+	ETHERTYPE_UBDIAGLOOP              = 0x7002
+	ETHERTYPE_UBDL                    = 0x7000
+	ETHERTYPE_UBNIU                   = 0x7001
+	ETHERTYPE_UBNMC                   = 0x7003
+	ETHERTYPE_VALID                   = 0x1600
+	ETHERTYPE_VARIAN                  = 0x80dd
+	ETHERTYPE_VAXELN                  = 0x803b
+	ETHERTYPE_VEECO                   = 0x8067
+	ETHERTYPE_VEXP                    = 0x805b
+	ETHERTYPE_VGLAB                   = 0x8131
+	ETHERTYPE_VINES                   = 0xbad
+	ETHERTYPE_VINESECHO               = 0xbaf
+	ETHERTYPE_VINESLOOP               = 0xbae
+	ETHERTYPE_VITAL                   = 0xff00
+	ETHERTYPE_VLAN                    = 0x8100
+	ETHERTYPE_VLTLMAN                 = 0x8080
+	ETHERTYPE_VPROD                   = 0x805c
+	ETHERTYPE_VURESERVED              = 0x8147
+	ETHERTYPE_WATERLOO                = 0x8130
+	ETHERTYPE_WELLFLEET               = 0x8103
+	ETHERTYPE_X25                     = 0x805
+	ETHERTYPE_X75                     = 0x801
+	ETHERTYPE_XNSSM                   = 0x9001
+	ETHERTYPE_XTP                     = 0x817d
+	ETHER_ADDR_LEN                    = 0x6
+	ETHER_ALIGN                       = 0x2
+	ETHER_CRC_LEN                     = 0x4
+	ETHER_CRC_POLY_BE                 = 0x4c11db6
+	ETHER_CRC_POLY_LE                 = 0xedb88320
+	ETHER_HDR_LEN                     = 0xe
+	ETHER_MAX_DIX_LEN                 = 0x600
+	ETHER_MAX_LEN                     = 0x5ee
+	ETHER_MIN_LEN                     = 0x40
+	ETHER_TYPE_LEN                    = 0x2
+	ETHER_VLAN_ENCAP_LEN              = 0x4
+	ETIMEDOUT                         = 0x3c
+	ETOOMANYREFS                      = 0x3b
+	ETXTBSY                           = 0x1a
+	EUSERS                            = 0x44
+	EVFILT_AIO                        = -0x3
+	EVFILT_PROC                       = -0x5
+	EVFILT_READ                       = -0x1
+	EVFILT_SIGNAL                     = -0x6
+	EVFILT_SYSCOUNT                   = 0x7
+	EVFILT_TIMER                      = -0x7
+	EVFILT_VNODE                      = -0x4
+	EVFILT_WRITE                      = -0x2
+	EV_ADD                            = 0x1
+	EV_CLEAR                          = 0x20
+	EV_DELETE                         = 0x2
+	EV_DISABLE                        = 0x8
+	EV_ENABLE                         = 0x4
+	EV_EOF                            = 0x8000
+	EV_ERROR                          = 0x4000
+	EV_FLAG1                          = 0x2000
+	EV_ONESHOT                        = 0x10
+	EV_SYSFLAGS                       = 0xf000
+	EWOULDBLOCK                       = 0x23
+	EXDEV                             = 0x12
+	EXTA                              = 0x4b00
+	EXTB                              = 0x9600
+	EXTPROC                           = 0x800
+	FD_CLOEXEC                        = 0x1
+	FD_SETSIZE                        = 0x400
+	F_DUPFD                           = 0
+	F_DUPFD_CLOEXEC                   = 0xa
+	F_GETFD                           = 0x1
+	F_GETFL                           = 0x3
+	F_GETLK                           = 0x7
+	F_GETOWN                          = 0x5
+	F_RDLCK                           = 0x1
+	F_SETFD                           = 0x2
+	F_SETFL                           = 0x4
+	F_SETLK                           = 0x8
+	F_SETLKW                          = 0x9
+	F_SETOWN                          = 0x6
+	F_UNLCK                           = 0x2
+	F_WRLCK                           = 0x3
+	IFA_ROUTE                         = 0x1
+	IFF_ALLMULTI                      = 0x200
+	IFF_BROADCAST                     = 0x2
+	IFF_CANTCHANGE                    = 0x8e52
+	IFF_DEBUG                         = 0x4
+	IFF_LINK0                         = 0x1000
+	IFF_LINK1                         = 0x2000
+	IFF_LINK2                         = 0x4000
+	IFF_LOOPBACK                      = 0x8
+	IFF_MULTICAST                     = 0x8000
+	IFF_NOARP                         = 0x80
+	IFF_NOTRAILERS                    = 0x20
+	IFF_OACTIVE                       = 0x400
+	IFF_POINTOPOINT                   = 0x10
+	IFF_PROMISC                       = 0x100
+	IFF_RUNNING                       = 0x40
+	IFF_SIMPLEX                       = 0x800
+	IFF_UP                            = 0x1
+	IFNAMSIZ                          = 0x10
+	IFT_1822                          = 0x2
+	IFT_A12MPPSWITCH                  = 0x82
+	IFT_AAL2                          = 0xbb
+	IFT_AAL5                          = 0x31
+	IFT_ADSL                          = 0x5e
+	IFT_AFLANE8023                    = 0x3b
+	IFT_AFLANE8025                    = 0x3c
+	IFT_ARAP                          = 0x58
+	IFT_ARCNET                        = 0x23
+	IFT_ARCNETPLUS                    = 0x24
+	IFT_ASYNC                         = 0x54
+	IFT_ATM                           = 0x25
+	IFT_ATMDXI                        = 0x69
+	IFT_ATMFUNI                       = 0x6a
+	IFT_ATMIMA                        = 0x6b
+	IFT_ATMLOGICAL                    = 0x50
+	IFT_ATMRADIO                      = 0xbd
+	IFT_ATMSUBINTERFACE               = 0x86
+	IFT_ATMVCIENDPT                   = 0xc2
+	IFT_ATMVIRTUAL                    = 0x95
+	IFT_BGPPOLICYACCOUNTING           = 0xa2
+	IFT_BLUETOOTH                     = 0xf8
+	IFT_BRIDGE                        = 0xd1
+	IFT_BSC                           = 0x53
+	IFT_CARP                          = 0xf7
+	IFT_CCTEMUL                       = 0x3d
+	IFT_CEPT                          = 0x13
+	IFT_CES                           = 0x85
+	IFT_CHANNEL                       = 0x46
+	IFT_CNR                           = 0x55
+	IFT_COFFEE                        = 0x84
+	IFT_COMPOSITELINK                 = 0x9b
+	IFT_DCN                           = 0x8d
+	IFT_DIGITALPOWERLINE              = 0x8a
+	IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+	IFT_DLSW                          = 0x4a
+	IFT_DOCSCABLEDOWNSTREAM           = 0x80
+	IFT_DOCSCABLEMACLAYER             = 0x7f
+	IFT_DOCSCABLEUPSTREAM             = 0x81
+	IFT_DOCSCABLEUPSTREAMCHANNEL      = 0xcd
+	IFT_DS0                           = 0x51
+	IFT_DS0BUNDLE                     = 0x52
+	IFT_DS1FDL                        = 0xaa
+	IFT_DS3                           = 0x1e
+	IFT_DTM                           = 0x8c
+	IFT_DUMMY                         = 0xf1
+	IFT_DVBASILN                      = 0xac
+	IFT_DVBASIOUT                     = 0xad
+	IFT_DVBRCCDOWNSTREAM              = 0x93
+	IFT_DVBRCCMACLAYER                = 0x92
+	IFT_DVBRCCUPSTREAM                = 0x94
+	IFT_ECONET                        = 0xce
+	IFT_ENC                           = 0xf4
+	IFT_EON                           = 0x19
+	IFT_EPLRS                         = 0x57
+	IFT_ESCON                         = 0x49
+	IFT_ETHER                         = 0x6
+	IFT_FAITH                         = 0xf3
+	IFT_FAST                          = 0x7d
+	IFT_FASTETHER                     = 0x3e
+	IFT_FASTETHERFX                   = 0x45
+	IFT_FDDI                          = 0xf
+	IFT_FIBRECHANNEL                  = 0x38
+	IFT_FRAMERELAYINTERCONNECT        = 0x3a
+	IFT_FRAMERELAYMPI                 = 0x5c
+	IFT_FRDLCIENDPT                   = 0xc1
+	IFT_FRELAY                        = 0x20
+	IFT_FRELAYDCE                     = 0x2c
+	IFT_FRF16MFRBUNDLE                = 0xa3
+	IFT_FRFORWARD                     = 0x9e
+	IFT_G703AT2MB                     = 0x43
+	IFT_G703AT64K                     = 0x42
+	IFT_GIF                           = 0xf0
+	IFT_GIGABITETHERNET               = 0x75
+	IFT_GR303IDT                      = 0xb2
+	IFT_GR303RDT                      = 0xb1
+	IFT_H323GATEKEEPER                = 0xa4
+	IFT_H323PROXY                     = 0xa5
+	IFT_HDH1822                       = 0x3
+	IFT_HDLC                          = 0x76
+	IFT_HDSL2                         = 0xa8
+	IFT_HIPERLAN2                     = 0xb7
+	IFT_HIPPI                         = 0x2f
+	IFT_HIPPIINTERFACE                = 0x39
+	IFT_HOSTPAD                       = 0x5a
+	IFT_HSSI                          = 0x2e
+	IFT_HY                            = 0xe
+	IFT_IBM370PARCHAN                 = 0x48
+	IFT_IDSL                          = 0x9a
+	IFT_IEEE1394                      = 0x90
+	IFT_IEEE80211                     = 0x47
+	IFT_IEEE80212                     = 0x37
+	IFT_IEEE8023ADLAG                 = 0xa1
+	IFT_IFGSN                         = 0x91
+	IFT_IMT                           = 0xbe
+	IFT_INFINIBAND                    = 0xc7
+	IFT_INTERLEAVE                    = 0x7c
+	IFT_IP                            = 0x7e
+	IFT_IPFORWARD                     = 0x8e
+	IFT_IPOVERATM                     = 0x72
+	IFT_IPOVERCDLC                    = 0x6d
+	IFT_IPOVERCLAW                    = 0x6e
+	IFT_IPSWITCH                      = 0x4e
+	IFT_ISDN                          = 0x3f
+	IFT_ISDNBASIC                     = 0x14
+	IFT_ISDNPRIMARY                   = 0x15
+	IFT_ISDNS                         = 0x4b
+	IFT_ISDNU                         = 0x4c
+	IFT_ISO88022LLC                   = 0x29
+	IFT_ISO88023                      = 0x7
+	IFT_ISO88024                      = 0x8
+	IFT_ISO88025                      = 0x9
+	IFT_ISO88025CRFPINT               = 0x62
+	IFT_ISO88025DTR                   = 0x56
+	IFT_ISO88025FIBER                 = 0x73
+	IFT_ISO88026                      = 0xa
+	IFT_ISUP                          = 0xb3
+	IFT_L2VLAN                        = 0x87
+	IFT_L3IPVLAN                      = 0x88
+	IFT_L3IPXVLAN                     = 0x89
+	IFT_LAPB                          = 0x10
+	IFT_LAPD                          = 0x4d
+	IFT_LAPF                          = 0x77
+	IFT_LINEGROUP                     = 0xd2
+	IFT_LOCALTALK                     = 0x2a
+	IFT_LOOP                          = 0x18
+	IFT_MEDIAMAILOVERIP               = 0x8b
+	IFT_MFSIGLINK                     = 0xa7
+	IFT_MIOX25                        = 0x26
+	IFT_MODEM                         = 0x30
+	IFT_MPC                           = 0x71
+	IFT_MPLS                          = 0xa6
+	IFT_MPLSTUNNEL                    = 0x96
+	IFT_MSDSL                         = 0x8f
+	IFT_MVL                           = 0xbf
+	IFT_MYRINET                       = 0x63
+	IFT_NFAS                          = 0xaf
+	IFT_NSIP                          = 0x1b
+	IFT_OPTICALCHANNEL                = 0xc3
+	IFT_OPTICALTRANSPORT              = 0xc4
+	IFT_OTHER                         = 0x1
+	IFT_P10                           = 0xc
+	IFT_P80                           = 0xd
+	IFT_PARA                          = 0x22
+	IFT_PFLOG                         = 0xf5
+	IFT_PFLOW                         = 0xf9
+	IFT_PFSYNC                        = 0xf6
+	IFT_PLC                           = 0xae
+	IFT_PON155                        = 0xcf
+	IFT_PON622                        = 0xd0
+	IFT_POS                           = 0xab
+	IFT_PPP                           = 0x17
+	IFT_PPPMULTILINKBUNDLE            = 0x6c
+	IFT_PROPATM                       = 0xc5
+	IFT_PROPBWAP2MP                   = 0xb8
+	IFT_PROPCNLS                      = 0x59
+	IFT_PROPDOCSWIRELESSDOWNSTREAM    = 0xb5
+	IFT_PROPDOCSWIRELESSMACLAYER      = 0xb4
+	IFT_PROPDOCSWIRELESSUPSTREAM      = 0xb6
+	IFT_PROPMUX                       = 0x36
+	IFT_PROPVIRTUAL                   = 0x35
+	IFT_PROPWIRELESSP2P               = 0x9d
+	IFT_PTPSERIAL                     = 0x16
+	IFT_PVC                           = 0xf2
+	IFT_Q2931                         = 0xc9
+	IFT_QLLC                          = 0x44
+	IFT_RADIOMAC                      = 0xbc
+	IFT_RADSL                         = 0x5f
+	IFT_REACHDSL                      = 0xc0
+	IFT_RFC1483                       = 0x9f
+	IFT_RS232                         = 0x21
+	IFT_RSRB                          = 0x4f
+	IFT_SDLC                          = 0x11
+	IFT_SDSL                          = 0x60
+	IFT_SHDSL                         = 0xa9
+	IFT_SIP                           = 0x1f
+	IFT_SIPSIG                        = 0xcc
+	IFT_SIPTG                         = 0xcb
+	IFT_SLIP                          = 0x1c
+	IFT_SMDSDXI                       = 0x2b
+	IFT_SMDSICIP                      = 0x34
+	IFT_SONET                         = 0x27
+	IFT_SONETOVERHEADCHANNEL          = 0xb9
+	IFT_SONETPATH                     = 0x32
+	IFT_SONETVT                       = 0x33
+	IFT_SRP                           = 0x97
+	IFT_SS7SIGLINK                    = 0x9c
+	IFT_STACKTOSTACK                  = 0x6f
+	IFT_STARLAN                       = 0xb
+	IFT_T1                            = 0x12
+	IFT_TDLC                          = 0x74
+	IFT_TELINK                        = 0xc8
+	IFT_TERMPAD                       = 0x5b
+	IFT_TR008                         = 0xb0
+	IFT_TRANSPHDLC                    = 0x7b
+	IFT_TUNNEL                        = 0x83
+	IFT_ULTRA                         = 0x1d
+	IFT_USB                           = 0xa0
+	IFT_V11                           = 0x40
+	IFT_V35                           = 0x2d
+	IFT_V36                           = 0x41
+	IFT_V37                           = 0x78
+	IFT_VDSL                          = 0x61
+	IFT_VIRTUALIPADDRESS              = 0x70
+	IFT_VIRTUALTG                     = 0xca
+	IFT_VOICEDID                      = 0xd5
+	IFT_VOICEEM                       = 0x64
+	IFT_VOICEEMFGD                    = 0xd3
+	IFT_VOICEENCAP                    = 0x67
+	IFT_VOICEFGDEANA                  = 0xd4
+	IFT_VOICEFXO                      = 0x65
+	IFT_VOICEFXS                      = 0x66
+	IFT_VOICEOVERATM                  = 0x98
+	IFT_VOICEOVERCABLE                = 0xc6
+	IFT_VOICEOVERFRAMERELAY           = 0x99
+	IFT_VOICEOVERIP                   = 0x68
+	IFT_X213                          = 0x5d
+	IFT_X25                           = 0x5
+	IFT_X25DDN                        = 0x4
+	IFT_X25HUNTGROUP                  = 0x7a
+	IFT_X25MLP                        = 0x79
+	IFT_X25PLE                        = 0x28
+	IFT_XETHER                        = 0x1a
+	IN_CLASSA_HOST                    = 0xffffff
+	IN_CLASSA_MAX                     = 0x80
+	IN_CLASSA_NET                     = 0xff000000
+	IN_CLASSA_NSHIFT                  = 0x18
+	IN_CLASSB_HOST                    = 0xffff
+	IN_CLASSB_MAX                     = 0x10000
+	IN_CLASSB_NET                     = 0xffff0000
+	IN_CLASSB_NSHIFT                  = 0x10
+	IN_CLASSC_HOST                    = 0xff
+	IN_CLASSC_NET                     = 0xffffff00
+	IN_CLASSC_NSHIFT                  = 0x8
+	IN_CLASSD_HOST                    = 0xfffffff
+	IN_CLASSD_NET                     = 0xf0000000
+	IN_CLASSD_NSHIFT                  = 0x1c
+	IN_LOOPBACKNET                    = 0x7f
+	IN_RFC3021_HOST                   = 0x1
+	IN_RFC3021_NET                    = 0xfffffffe
+	IN_RFC3021_NSHIFT                 = 0x1f
+	IPPROTO_AH                        = 0x33
+	IPPROTO_CARP                      = 0x70
+	IPPROTO_DIVERT                    = 0x102
+	IPPROTO_DONE                      = 0x101
+	IPPROTO_DSTOPTS                   = 0x3c
+	IPPROTO_EGP                       = 0x8
+	IPPROTO_ENCAP                     = 0x62
+	IPPROTO_EON                       = 0x50
+	IPPROTO_ESP                       = 0x32
+	IPPROTO_ETHERIP                   = 0x61
+	IPPROTO_FRAGMENT                  = 0x2c
+	IPPROTO_GGP                       = 0x3
+	IPPROTO_GRE                       = 0x2f
+	IPPROTO_HOPOPTS                   = 0
+	IPPROTO_ICMP                      = 0x1
+	IPPROTO_ICMPV6                    = 0x3a
+	IPPROTO_IDP                       = 0x16
+	IPPROTO_IGMP                      = 0x2
+	IPPROTO_IP                        = 0
+	IPPROTO_IPCOMP                    = 0x6c
+	IPPROTO_IPIP                      = 0x4
+	IPPROTO_IPV4                      = 0x4
+	IPPROTO_IPV6                      = 0x29
+	IPPROTO_MAX                       = 0x100
+	IPPROTO_MAXID                     = 0x103
+	IPPROTO_MOBILE                    = 0x37
+	IPPROTO_MPLS                      = 0x89
+	IPPROTO_NONE                      = 0x3b
+	IPPROTO_PFSYNC                    = 0xf0
+	IPPROTO_PIM                       = 0x67
+	IPPROTO_PUP                       = 0xc
+	IPPROTO_RAW                       = 0xff
+	IPPROTO_ROUTING                   = 0x2b
+	IPPROTO_RSVP                      = 0x2e
+	IPPROTO_TCP                       = 0x6
+	IPPROTO_TP                        = 0x1d
+	IPPROTO_UDP                       = 0x11
+	IPV6_AUTH_LEVEL                   = 0x35
+	IPV6_AUTOFLOWLABEL                = 0x3b
+	IPV6_CHECKSUM                     = 0x1a
+	IPV6_DEFAULT_MULTICAST_HOPS       = 0x1
+	IPV6_DEFAULT_MULTICAST_LOOP       = 0x1
+	IPV6_DEFHLIM                      = 0x40
+	IPV6_DONTFRAG                     = 0x3e
+	IPV6_DSTOPTS                      = 0x32
+	IPV6_ESP_NETWORK_LEVEL            = 0x37
+	IPV6_ESP_TRANS_LEVEL              = 0x36
+	IPV6_FAITH                        = 0x1d
+	IPV6_FLOWINFO_MASK                = 0xffffff0f
+	IPV6_FLOWLABEL_MASK               = 0xffff0f00
+	IPV6_FRAGTTL                      = 0x78
+	IPV6_HLIMDEC                      = 0x1
+	IPV6_HOPLIMIT                     = 0x2f
+	IPV6_HOPOPTS                      = 0x31
+	IPV6_IPCOMP_LEVEL                 = 0x3c
+	IPV6_JOIN_GROUP                   = 0xc
+	IPV6_LEAVE_GROUP                  = 0xd
+	IPV6_MAXHLIM                      = 0xff
+	IPV6_MAXPACKET                    = 0xffff
+	IPV6_MMTU                         = 0x500
+	IPV6_MULTICAST_HOPS               = 0xa
+	IPV6_MULTICAST_IF                 = 0x9
+	IPV6_MULTICAST_LOOP               = 0xb
+	IPV6_NEXTHOP                      = 0x30
+	IPV6_OPTIONS                      = 0x1
+	IPV6_PATHMTU                      = 0x2c
+	IPV6_PIPEX                        = 0x3f
+	IPV6_PKTINFO                      = 0x2e
+	IPV6_PORTRANGE                    = 0xe
+	IPV6_PORTRANGE_DEFAULT            = 0
+	IPV6_PORTRANGE_HIGH               = 0x1
+	IPV6_PORTRANGE_LOW                = 0x2
+	IPV6_RECVDSTOPTS                  = 0x28
+	IPV6_RECVHOPLIMIT                 = 0x25
+	IPV6_RECVHOPOPTS                  = 0x27
+	IPV6_RECVPATHMTU                  = 0x2b
+	IPV6_RECVPKTINFO                  = 0x24
+	IPV6_RECVRTHDR                    = 0x26
+	IPV6_RECVTCLASS                   = 0x39
+	IPV6_RTABLE                       = 0x1021
+	IPV6_RTHDR                        = 0x33
+	IPV6_RTHDRDSTOPTS                 = 0x23
+	IPV6_RTHDR_LOOSE                  = 0
+	IPV6_RTHDR_STRICT                 = 0x1
+	IPV6_RTHDR_TYPE_0                 = 0
+	IPV6_SOCKOPT_RESERVED1            = 0x3
+	IPV6_TCLASS                       = 0x3d
+	IPV6_UNICAST_HOPS                 = 0x4
+	IPV6_USE_MIN_MTU                  = 0x2a
+	IPV6_V6ONLY                       = 0x1b
+	IPV6_VERSION                      = 0x60
+	IPV6_VERSION_MASK                 = 0xf0
+	IP_ADD_MEMBERSHIP                 = 0xc
+	IP_AUTH_LEVEL                     = 0x14
+	IP_DEFAULT_MULTICAST_LOOP         = 0x1
+	IP_DEFAULT_MULTICAST_TTL          = 0x1
+	IP_DF                             = 0x4000
+	IP_DROP_MEMBERSHIP                = 0xd
+	IP_ESP_NETWORK_LEVEL              = 0x16
+	IP_ESP_TRANS_LEVEL                = 0x15
+	IP_HDRINCL                        = 0x2
+	IP_IPCOMP_LEVEL                   = 0x1d
+	IP_IPSEC_LOCAL_AUTH               = 0x1b
+	IP_IPSEC_LOCAL_CRED               = 0x19
+	IP_IPSEC_LOCAL_ID                 = 0x17
+	IP_IPSEC_REMOTE_AUTH              = 0x1c
+	IP_IPSEC_REMOTE_CRED              = 0x1a
+	IP_IPSEC_REMOTE_ID                = 0x18
+	IP_MAXPACKET                      = 0xffff
+	IP_MAX_MEMBERSHIPS                = 0xfff
+	IP_MF                             = 0x2000
+	IP_MINTTL                         = 0x20
+	IP_MIN_MEMBERSHIPS                = 0xf
+	IP_MSS                            = 0x240
+	IP_MULTICAST_IF                   = 0x9
+	IP_MULTICAST_LOOP                 = 0xb
+	IP_MULTICAST_TTL                  = 0xa
+	IP_OFFMASK                        = 0x1fff
+	IP_OPTIONS                        = 0x1
+	IP_PIPEX                          = 0x22
+	IP_PORTRANGE                      = 0x13
+	IP_PORTRANGE_DEFAULT              = 0
+	IP_PORTRANGE_HIGH                 = 0x1
+	IP_PORTRANGE_LOW                  = 0x2
+	IP_RECVDSTADDR                    = 0x7
+	IP_RECVDSTPORT                    = 0x21
+	IP_RECVIF                         = 0x1e
+	IP_RECVOPTS                       = 0x5
+	IP_RECVRETOPTS                    = 0x6
+	IP_RECVRTABLE                     = 0x23
+	IP_RECVTTL                        = 0x1f
+	IP_RETOPTS                        = 0x8
+	IP_RF                             = 0x8000
+	IP_RTABLE                         = 0x1021
+	IP_TOS                            = 0x3
+	IP_TTL                            = 0x4
+	MAP_ET_KVAGUARD                   = 0x10
+	MSG_BCAST                         = 0x100
+	MSG_CTRUNC                        = 0x20
+	MSG_DONTROUTE                     = 0x4
+	MSG_DONTWAIT                      = 0x80
+	MSG_EOR                           = 0x8
+	MSG_MCAST                         = 0x200
+	MSG_OOB                           = 0x1
+	MSG_PEEK                          = 0x2
+	MSG_TRUNC                         = 0x10
+	MSG_WAITALL                       = 0x40
+	NAME_MAX                          = 0xff
+	NET_RT_DUMP                       = 0x1
+	NET_RT_FLAGS                      = 0x2
+	NET_RT_IFLIST                     = 0x3
+	NET_RT_MAXID                      = 0x6
+	NET_RT_STATS                      = 0x4
+	NET_RT_TABLE                      = 0x5
+	O_ACCMODE                         = 0x3
+	O_APPEND                          = 0x8
+	O_ASYNC                           = 0x40
+	O_CLOEXEC                         = 0x10000
+	O_CREAT                           = 0x200
+	O_DIRECTORY                       = 0x20000
+	O_DSYNC                           = 0x80
+	O_EXCL                            = 0x800
+	O_EXLOCK                          = 0x20
+	O_FSYNC                           = 0x80
+	O_NDELAY                          = 0x4
+	O_NOCTTY                          = 0x8000
+	O_NOFOLLOW                        = 0x100
+	O_NONBLOCK                        = 0x4
+	O_RDONLY                          = 0
+	O_RDWR                            = 0x2
+	O_RSYNC                           = 0x80
+	O_SHLOCK                          = 0x10
+	O_SYNC                            = 0x80
+	O_TRUNC                           = 0x400
+	O_WRONLY                          = 0x1
+	RTAX_AUTHOR                       = 0x6
+	RTAX_BRD                          = 0x7
+	RTAX_DST                          = 0
+	RTAX_GATEWAY                      = 0x1
+	RTAX_GENMASK                      = 0x3
+	RTAX_IFA                          = 0x5
+	RTAX_IFP                          = 0x4
+	RTAX_LABEL                        = 0xa
+	RTAX_MAX                          = 0xb
+	RTAX_NETMASK                      = 0x2
+	RTAX_SRC                          = 0x8
+	RTAX_SRCMASK                      = 0x9
+	RTA_AUTHOR                        = 0x40
+	RTA_BRD                           = 0x80
+	RTA_DST                           = 0x1
+	RTA_GATEWAY                       = 0x2
+	RTA_GENMASK                       = 0x8
+	RTA_IFA                           = 0x20
+	RTA_IFP                           = 0x10
+	RTA_LABEL                         = 0x400
+	RTA_NETMASK                       = 0x4
+	RTA_SRC                           = 0x100
+	RTA_SRCMASK                       = 0x200
+	RTF_ANNOUNCE                      = 0x4000
+	RTF_BLACKHOLE                     = 0x1000
+	RTF_CLONED                        = 0x10000
+	RTF_CLONING                       = 0x100
+	RTF_DONE                          = 0x40
+	RTF_DYNAMIC                       = 0x10
+	RTF_FMASK                         = 0x8f808
+	RTF_GATEWAY                       = 0x2
+	RTF_HOST                          = 0x4
+	RTF_JUMBO                         = 0x80000
+	RTF_LLINFO                        = 0x400
+	RTF_MASK                          = 0x80
+	RTF_MODIFIED                      = 0x20
+	RTF_MPATH                         = 0x40000
+	RTF_MPLS                          = 0x100000
+	RTF_PERMANENT_ARP                 = 0x2000
+	RTF_PROTO1                        = 0x8000
+	RTF_PROTO2                        = 0x4000
+	RTF_PROTO3                        = 0x2000
+	RTF_REJECT                        = 0x8
+	RTF_SOURCE                        = 0x20000
+	RTF_STATIC                        = 0x800
+	RTF_TUNNEL                        = 0x100000
+	RTF_UP                            = 0x1
+	RTF_USETRAILERS                   = 0x8000
+	RTF_XRESOLVE                      = 0x200
+	RTM_ADD                           = 0x1
+	RTM_CHANGE                        = 0x3
+	RTM_DELADDR                       = 0xd
+	RTM_DELETE                        = 0x2
+	RTM_DESYNC                        = 0x10
+	RTM_GET                           = 0x4
+	RTM_IFANNOUNCE                    = 0xf
+	RTM_IFINFO                        = 0xe
+	RTM_LOCK                          = 0x8
+	RTM_LOSING                        = 0x5
+	RTM_MAXSIZE                       = 0x800
+	RTM_MISS                          = 0x7
+	RTM_NEWADDR                       = 0xc
+	RTM_REDIRECT                      = 0x6
+	RTM_RESOLVE                       = 0xb
+	RTM_RTTUNIT                       = 0xf4240
+	RTM_VERSION                       = 0x4
+	RTV_EXPIRE                        = 0x4
+	RTV_HOPCOUNT                      = 0x2
+	RTV_MTU                           = 0x1
+	RTV_RPIPE                         = 0x8
+	RTV_RTT                           = 0x40
+	RTV_RTTVAR                        = 0x80
+	RTV_SPIPE                         = 0x10
+	RTV_SSTHRESH                      = 0x20
+	SCM_CREDS                         = 0x2
+	SCM_RIGHTS                        = 0x1
+	SCM_TIMESTAMP                     = 0x4
+	SHUT_RD                           = 0
+	SHUT_RDWR                         = 0x2
+	SHUT_WR                           = 0x1
+	SIGABRT                           = 0x6
+	SIGALRM                           = 0xe
+	SIGBUS                            = 0xa
+	SIGCHLD                           = 0x14
+	SIGCONT                           = 0x13
+	SIGEMT                            = 0x7
+	SIGFPE                            = 0x8
+	SIGHUP                            = 0x1
+	SIGILL                            = 0x4
+	SIGINFO                           = 0x1d
+	SIGINT                            = 0x2
+	SIGIO                             = 0x17
+	SIGIOT                            = 0x6
+	SIGKILL                           = 0x9
+	SIGPIPE                           = 0xd
+	SIGPROF                           = 0x1b
+	SIGQUIT                           = 0x3
+	SIGSEGV                           = 0xb
+	SIGSTOP                           = 0x11
+	SIGSYS                            = 0xc
+	SIGTERM                           = 0xf
+	SIGTHR                            = 0x20
+	SIGTRAP                           = 0x5
+	SIGTSTP                           = 0x12
+	SIGTTIN                           = 0x15
+	SIGTTOU                           = 0x16
+	SIGURG                            = 0x10
+	SIGUSR1                           = 0x1e
+	SIGUSR2                           = 0x1f
+	SIGVTALRM                         = 0x1a
+	SIGWINCH                          = 0x1c
+	SIGXCPU                           = 0x18
+	SIGXFSZ                           = 0x19
+	SIOCADDMULTI                      = 0x80206931
+	SIOCAIFADDR                       = 0x8040691a
+	SIOCAIFGROUP                      = 0x80286987
+	SIOCALIFADDR                      = 0x8218691c
+	SIOCATMARK                        = 0x40047307
+	SIOCBRDGADD                       = 0x8058693c
+	SIOCBRDGADDS                      = 0x80586941
+	SIOCBRDGARL                       = 0x806e694d
+	SIOCBRDGDADDR                     = 0x80286947
+	SIOCBRDGDEL                       = 0x8058693d
+	SIOCBRDGDELS                      = 0x80586942
+	SIOCBRDGFLUSH                     = 0x80586948
+	SIOCBRDGFRL                       = 0x806e694e
+	SIOCBRDGGCACHE                    = 0xc0146941
+	SIOCBRDGGFD                       = 0xc0146952
+	SIOCBRDGGHT                       = 0xc0146951
+	SIOCBRDGGIFFLGS                   = 0xc058693e
+	SIOCBRDGGMA                       = 0xc0146953
+	SIOCBRDGGPARAM                    = 0xc0406958
+	SIOCBRDGGPRI                      = 0xc0146950
+	SIOCBRDGGRL                       = 0xc030694f
+	SIOCBRDGGSIFS                     = 0xc058693c
+	SIOCBRDGGTO                       = 0xc0146946
+	SIOCBRDGIFS                       = 0xc0586942
+	SIOCBRDGRTS                       = 0xc0206943
+	SIOCBRDGSADDR                     = 0xc0286944
+	SIOCBRDGSCACHE                    = 0x80146940
+	SIOCBRDGSFD                       = 0x80146952
+	SIOCBRDGSHT                       = 0x80146951
+	SIOCBRDGSIFCOST                   = 0x80586955
+	SIOCBRDGSIFFLGS                   = 0x8058693f
+	SIOCBRDGSIFPRIO                   = 0x80586954
+	SIOCBRDGSMA                       = 0x80146953
+	SIOCBRDGSPRI                      = 0x80146950
+	SIOCBRDGSPROTO                    = 0x8014695a
+	SIOCBRDGSTO                       = 0x80146945
+	SIOCBRDGSTXHC                     = 0x80146959
+	SIOCDELMULTI                      = 0x80206932
+	SIOCDIFADDR                       = 0x80206919
+	SIOCDIFGROUP                      = 0x80286989
+	SIOCDIFPHYADDR                    = 0x80206949
+	SIOCDLIFADDR                      = 0x8218691e
+	SIOCGETKALIVE                     = 0xc01869a4
+	SIOCGETLABEL                      = 0x8020699a
+	SIOCGETPFLOW                      = 0xc02069fe
+	SIOCGETPFSYNC                     = 0xc02069f8
+	SIOCGETSGCNT                      = 0xc0207534
+	SIOCGETVIFCNT                     = 0xc0287533
+	SIOCGETVLANPRIO                   = 0xc0206990
+	SIOCGHIWAT                        = 0x40047301
+	SIOCGIFADDR                       = 0xc0206921
+	SIOCGIFASYNCMAP                   = 0xc020697c
+	SIOCGIFBRDADDR                    = 0xc0206923
+	SIOCGIFCONF                       = 0xc0106924
+	SIOCGIFDATA                       = 0xc020691b
+	SIOCGIFDESCR                      = 0xc0206981
+	SIOCGIFDSTADDR                    = 0xc0206922
+	SIOCGIFFLAGS                      = 0xc0206911
+	SIOCGIFGATTR                      = 0xc028698b
+	SIOCGIFGENERIC                    = 0xc020693a
+	SIOCGIFGMEMB                      = 0xc028698a
+	SIOCGIFGROUP                      = 0xc0286988
+	SIOCGIFMEDIA                      = 0xc0306936
+	SIOCGIFMETRIC                     = 0xc0206917
+	SIOCGIFMTU                        = 0xc020697e
+	SIOCGIFNETMASK                    = 0xc0206925
+	SIOCGIFPDSTADDR                   = 0xc0206948
+	SIOCGIFPRIORITY                   = 0xc020699c
+	SIOCGIFPSRCADDR                   = 0xc0206947
+	SIOCGIFRDOMAIN                    = 0xc02069a0
+	SIOCGIFRTLABEL                    = 0xc0206983
+	SIOCGIFTIMESLOT                   = 0xc0206986
+	SIOCGIFXFLAGS                     = 0xc020699e
+	SIOCGLIFADDR                      = 0xc218691d
+	SIOCGLIFPHYADDR                   = 0xc218694b
+	SIOCGLIFPHYRTABLE                 = 0xc02069a2
+	SIOCGLOWAT                        = 0x40047303
+	SIOCGPGRP                         = 0x40047309
+	SIOCGVH                           = 0xc02069f6
+	SIOCIFCREATE                      = 0x8020697a
+	SIOCIFDESTROY                     = 0x80206979
+	SIOCIFGCLONERS                    = 0xc0106978
+	SIOCSETKALIVE                     = 0x801869a3
+	SIOCSETLABEL                      = 0x80206999
+	SIOCSETPFLOW                      = 0x802069fd
+	SIOCSETPFSYNC                     = 0x802069f7
+	SIOCSETVLANPRIO                   = 0x8020698f
+	SIOCSHIWAT                        = 0x80047300
+	SIOCSIFADDR                       = 0x8020690c
+	SIOCSIFASYNCMAP                   = 0x8020697d
+	SIOCSIFBRDADDR                    = 0x80206913
+	SIOCSIFDESCR                      = 0x80206980
+	SIOCSIFDSTADDR                    = 0x8020690e
+	SIOCSIFFLAGS                      = 0x80206910
+	SIOCSIFGATTR                      = 0x8028698c
+	SIOCSIFGENERIC                    = 0x80206939
+	SIOCSIFLLADDR                     = 0x8020691f
+	SIOCSIFMEDIA                      = 0xc0206935
+	SIOCSIFMETRIC                     = 0x80206918
+	SIOCSIFMTU                        = 0x8020697f
+	SIOCSIFNETMASK                    = 0x80206916
+	SIOCSIFPHYADDR                    = 0x80406946
+	SIOCSIFPRIORITY                   = 0x8020699b
+	SIOCSIFRDOMAIN                    = 0x8020699f
+	SIOCSIFRTLABEL                    = 0x80206982
+	SIOCSIFTIMESLOT                   = 0x80206985
+	SIOCSIFXFLAGS                     = 0x8020699d
+	SIOCSLIFPHYADDR                   = 0x8218694a
+	SIOCSLIFPHYRTABLE                 = 0x802069a1
+	SIOCSLOWAT                        = 0x80047302
+	SIOCSPGRP                         = 0x80047308
+	SIOCSVH                           = 0xc02069f5
+	SOCK_DGRAM                        = 0x2
+	SOCK_RAW                          = 0x3
+	SOCK_RDM                          = 0x4
+	SOCK_SEQPACKET                    = 0x5
+	SOCK_STREAM                       = 0x1
+	SOL_SOCKET                        = 0xffff
+	SOMAXCONN                         = 0x80
+	SO_ACCEPTCONN                     = 0x2
+	SO_BINDANY                        = 0x1000
+	SO_BROADCAST                      = 0x20
+	SO_DEBUG                          = 0x1
+	SO_DONTROUTE                      = 0x10
+	SO_ERROR                          = 0x1007
+	SO_JUMBO                          = 0x400
+	SO_KEEPALIVE                      = 0x8
+	SO_LINGER                         = 0x80
+	SO_NETPROC                        = 0x1020
+	SO_OOBINLINE                      = 0x100
+	SO_PEERCRED                       = 0x1022
+	SO_RCVBUF                         = 0x1002
+	SO_RCVLOWAT                       = 0x1004
+	SO_RCVTIMEO                       = 0x1006
+	SO_REUSEADDR                      = 0x4
+	SO_REUSEPORT                      = 0x200
+	SO_RTABLE                         = 0x1021
+	SO_SNDBUF                         = 0x1001
+	SO_SNDLOWAT                       = 0x1003
+	SO_SNDTIMEO                       = 0x1005
+	SO_SPLICE                         = 0x1023
+	SO_TIMESTAMP                      = 0x800
+	SO_TYPE                           = 0x1008
+	SO_USELOOPBACK                    = 0x40
+	TCP_MAXBURST                      = 0x4
+	TCP_MAXSEG                        = 0x2
+	TCP_MAXWIN                        = 0xffff
+	TCP_MAX_SACK                      = 0x3
+	TCP_MAX_WINSHIFT                  = 0xe
+	TCP_MD5SIG                        = 0x4
+	TCP_MSS                           = 0x200
+	TCP_NODELAY                       = 0x1
+	TCP_NSTATES                       = 0xb
+	TCP_SACK_ENABLE                   = 0x8
+	TIOCCBRK                          = 0x2000747a
+	TIOCCDTR                          = 0x20007478
+	TIOCCONS                          = 0x80047462
+	TIOCDRAIN                         = 0x2000745e
+	TIOCEXCL                          = 0x2000740d
+	TIOCEXT                           = 0x80047460
+	TIOCFLAG_CLOCAL                   = 0x2
+	TIOCFLAG_CRTSCTS                  = 0x4
+	TIOCFLAG_MDMBUF                   = 0x8
+	TIOCFLAG_PPS                      = 0x10
+	TIOCFLAG_SOFTCAR                  = 0x1
+	TIOCFLUSH                         = 0x80047410
+	TIOCGETA                          = 0x402c7413
+	TIOCGETD                          = 0x4004741a
+	TIOCGFLAGS                        = 0x4004745d
+	TIOCGPGRP                         = 0x40047477
+	TIOCGTSTAMP                       = 0x4010745b
+	TIOCGWINSZ                        = 0x40087468
+	TIOCMBIC                          = 0x8004746b
+	TIOCMBIS                          = 0x8004746c
+	TIOCMGET                          = 0x4004746a
+	TIOCMODG                          = 0x4004746a
+	TIOCMODS                          = 0x8004746d
+	TIOCMSET                          = 0x8004746d
+	TIOCM_CAR                         = 0x40
+	TIOCM_CD                          = 0x40
+	TIOCM_CTS                         = 0x20
+	TIOCM_DSR                         = 0x100
+	TIOCM_DTR                         = 0x2
+	TIOCM_LE                          = 0x1
+	TIOCM_RI                          = 0x80
+	TIOCM_RNG                         = 0x80
+	TIOCM_RTS                         = 0x4
+	TIOCM_SR                          = 0x10
+	TIOCM_ST                          = 0x8
+	TIOCNOTTY                         = 0x20007471
+	TIOCNXCL                          = 0x2000740e
+	TIOCOUTQ                          = 0x40047473
+	TIOCPKT                           = 0x80047470
+	TIOCPKT_DATA                      = 0
+	TIOCPKT_DOSTOP                    = 0x20
+	TIOCPKT_FLUSHREAD                 = 0x1
+	TIOCPKT_FLUSHWRITE                = 0x2
+	TIOCPKT_IOCTL                     = 0x40
+	TIOCPKT_NOSTOP                    = 0x10
+	TIOCPKT_START                     = 0x8
+	TIOCPKT_STOP                      = 0x4
+	TIOCREMOTE                        = 0x80047469
+	TIOCSBRK                          = 0x2000747b
+	TIOCSCTTY                         = 0x20007461
+	TIOCSDTR                          = 0x20007479
+	TIOCSETA                          = 0x802c7414
+	TIOCSETAF                         = 0x802c7416
+	TIOCSETAW                         = 0x802c7415
+	TIOCSETD                          = 0x8004741b
+	TIOCSFLAGS                        = 0x8004745c
+	TIOCSIG                           = 0x8004745f
+	TIOCSPGRP                         = 0x80047476
+	TIOCSTART                         = 0x2000746e
+	TIOCSTAT                          = 0x80047465
+	TIOCSTI                           = 0x80017472
+	TIOCSTOP                          = 0x2000746f
+	TIOCSTSTAMP                       = 0x8008745a
+	TIOCSWINSZ                        = 0x80087467
+	TIOCUCNTL                         = 0x80047466
+	WALTSIG                           = 0x4
+	WCONTINUED                        = 0x8
+	WCOREFLAG                         = 0x80
+	WNOHANG                           = 0x1
+	WSTOPPED                          = 0x7f
+	WUNTRACED                         = 0x2
+)
+
+// Types
+
+// Error table
+var errors = [...]string{
+	1:  "operation not permitted",
+	2:  "no such file or directory",
+	3:  "no such process",
+	4:  "interrupted system call",
+	5:  "input/output error",
+	6:  "device not configured",
+	7:  "argument list too long",
+	8:  "exec format error",
+	9:  "bad file descriptor",
+	10: "no child processes",
+	11: "resource deadlock avoided",
+	12: "cannot allocate memory",
+	13: "permission denied",
+	14: "bad address",
+	15: "block device required",
+	16: "device busy",
+	17: "file exists",
+	18: "cross-device link",
+	19: "operation not supported by device",
+	20: "not a directory",
+	21: "is a directory",
+	22: "invalid argument",
+	23: "too many open files in system",
+	24: "too many open files",
+	25: "inappropriate ioctl for device",
+	26: "text file busy",
+	27: "file too large",
+	28: "no space left on device",
+	29: "illegal seek",
+	30: "read-only file system",
+	31: "too many links",
+	32: "broken pipe",
+	33: "numerical argument out of domain",
+	34: "result too large",
+	35: "resource temporarily unavailable",
+	36: "operation now in progress",
+	37: "operation already in progress",
+	38: "socket operation on non-socket",
+	39: "destination address required",
+	40: "message too long",
+	41: "protocol wrong type for socket",
+	42: "protocol not available",
+	43: "protocol not supported",
+	44: "socket type not supported",
+	45: "operation not supported",
+	46: "protocol family not supported",
+	47: "address family not supported by protocol family",
+	48: "address already in use",
+	49: "can't assign requested address",
+	50: "network is down",
+	51: "network is unreachable",
+	52: "network dropped connection on reset",
+	53: "software caused connection abort",
+	54: "connection reset by peer",
+	55: "no buffer space available",
+	56: "socket is already connected",
+	57: "socket is not connected",
+	58: "can't send after socket shutdown",
+	59: "too many references: can't splice",
+	60: "connection timed out",
+	61: "connection refused",
+	62: "too many levels of symbolic links",
+	63: "file name too long",
+	64: "host is down",
+	65: "no route to host",
+	66: "directory not empty",
+	67: "too many processes",
+	68: "too many users",
+	69: "disc quota exceeded",
+	70: "stale NFS file handle",
+	71: "too many levels of remote in path",
+	72: "RPC struct is bad",
+	73: "RPC version wrong",
+	74: "RPC prog. not avail",
+	75: "program version wrong",
+	76: "bad procedure for program",
+	77: "no locks available",
+	78: "function not implemented",
+	79: "inappropriate file type or format",
+	80: "authentication error",
+	81: "need authenticator",
+	82: "IPsec processing failure",
+	83: "attribute not found",
+	84: "illegal byte sequence",
+	85: "no medium found",
+	86: "wrong medium type",
+	87: "value too large to be stored in data type",
+	88: "operation canceled",
+	89: "identifier removed",
+	90: "no message of desired type",
+	91: "not supported",
+}
diff --git a/src/pkg/syscall/zsyscall_openbsd_386.go b/src/pkg/syscall/zsyscall_openbsd_386.go
new file mode 100644
index 0000000..ca390cc
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_openbsd_386.go
@@ -0,0 +1,941 @@
+// mksyscall.pl -l32 -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_386.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) {
+	r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+	wpid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (errno int) {
+	_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
+	_, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+	_, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) {
+	_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (errno int) {
+	_, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
+	_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) {
+	r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (errno int) {
+	var _p0 unsafe.Pointer
+	if len(mib) > 0 {
+		_p0 = unsafe.Pointer(&mib[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]_C_int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+	nfd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+	Syscall(SYS_EXIT, uintptr(code), 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (errno int) {
+	_, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstatfs(fd int, stat *Statfs_t) (errno int) {
+	_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (errno int) {
+	_, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+	r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+	egid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+	r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+	gid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+	pgid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+	r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+	pgrp = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+	r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+	pid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+	r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+	ppid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, errno int) {
+	r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+	prio = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+	sid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+	r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+	tainted = bool(r0 != 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum int) (errno int) {
+	_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (errno int) {
+	_, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (errno int) {
+	_, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (errno int) {
+	_, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (errno int) {
+	_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Read(fd int, p []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (errno int) {
+	_, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
+	r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0)
+	newoffset = int64(int64(r1)<<32 | int64(r0))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) {
+	_, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setlogin(name string) (errno int) {
+	_, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (errno int) {
+	_, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+	pid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Statfs(path string, stat *Statfs_t) (errno int) {
+	_, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (errno int) {
+	_, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (errno int) {
+	_, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (errno int) {
+	_, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+	r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+	oldmask = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Write(fd int, p []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+	r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0)
+	ret = uintptr(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+	_, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, buf *byte, nbuf int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, buf *byte, nbuf int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
diff --git a/src/pkg/syscall/zsyscall_openbsd_amd64.go b/src/pkg/syscall/zsyscall_openbsd_amd64.go
new file mode 100644
index 0000000..94ce290
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_openbsd_amd64.go
@@ -0,0 +1,941 @@
+// mksyscall.pl -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_amd64.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(ngid int, gid *_Gid_t) (n int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) {
+	r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+	wpid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (errno int) {
+	_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
+	_, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) {
+	_, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) {
+	_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (errno int) {
+	_, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) {
+	_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) {
+	r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (errno int) {
+	var _p0 unsafe.Pointer
+	if len(mib) > 0 {
+		_p0 = unsafe.Pointer(&mib[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]_C_int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (errno int) {
+	_, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+	nfd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+	Syscall(SYS_EXIT, uintptr(code), 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (errno int) {
+	_, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstatfs(fd int, stat *Statfs_t) (errno int) {
+	_, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (errno int) {
+	_, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (errno int) {
+	_, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+	r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+	egid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+	r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+	gid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+	pgid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+	r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+	pgrp = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+	r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+	pid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+	r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+	ppid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, errno int) {
+	r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+	prio = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+	sid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (errno int) {
+	_, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+	r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+	tainted = bool(r0 != 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum int) (errno int) {
+	_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (errno int) {
+	_, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (errno int) {
+	_, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (errno int) {
+	_, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (errno int) {
+	_, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (errno int) {
+	_, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (errno int) {
+	_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, errno int) {
+	r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+	fd = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, errno int) {
+	r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+	val = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), 0)
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Read(fd int, p []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (errno int) {
+	_, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
+	r0, _, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(whence), 0, 0)
+	newoffset = int64(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) {
+	_, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setlogin(name string) (errno int) {
+	_, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (errno int) {
+	_, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, errno int) {
+	r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+	pid = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (errno int) {
+	_, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (errno int) {
+	_, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Statfs(path string, stat *Statfs_t) (errno int) {
+	_, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (errno int) {
+	_, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (errno int) {
+	_, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (errno int) {
+	_, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length))
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+	r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+	oldmask = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (errno int) {
+	_, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (errno int) {
+	_, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Write(fd int, p []byte) (n int, errno int) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, errno int) {
+	r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), 0, 0)
+	ret = uintptr(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (errno int) {
+	_, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, buf *byte, nbuf int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, buf *byte, nbuf int) (n int, errno int) {
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+	n = int(r0)
+	errno = int(e1)
+	return
+}
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index b46493f..24c82a4 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -66,6 +66,7 @@ var (
 	procSetFileTime                = modkernel32.NewProc("SetFileTime")
 	procGetFileAttributesW         = modkernel32.NewProc("GetFileAttributesW")
 	procSetFileAttributesW         = modkernel32.NewProc("SetFileAttributesW")
+	procGetFileAttributesExW       = modkernel32.NewProc("GetFileAttributesExW")
 	procGetCommandLineW            = modkernel32.NewProc("GetCommandLineW")
 	procCommandLineToArgvW         = modshell32.NewProc("CommandLineToArgvW")
 	procLocalFree                  = modkernel32.NewProc("LocalFree")
@@ -141,9 +142,9 @@ func FreeLibrary(handle Handle) (errno int) {
 	return
 }
 
-func GetProcAddress(module Handle, procname string) (proc Handle, errno int) {
+func GetProcAddress(module Handle, procname string) (proc uintptr, errno int) {
 	r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
-	proc = Handle(r0)
+	proc = uintptr(r0)
 	if proc == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -848,6 +849,20 @@ func SetFileAttributes(name *uint16, attrs uint32) (errno int) {
 	return
 }
 
+func GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) {
+	r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
+	if int(r1) == 0 {
+		if e1 != 0 {
+			errno = int(e1)
+		} else {
+			errno = EINVAL
+		}
+	} else {
+		errno = 0
+	}
+	return
+}
+
 func GetCommandLine() (cmd *uint16) {
 	r0, _, _ := Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0)
 	cmd = (*uint16)(unsafe.Pointer(r0))
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
index ca087bb..06bb114 100644
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -66,6 +66,7 @@ var (
 	procSetFileTime                = modkernel32.NewProc("SetFileTime")
 	procGetFileAttributesW         = modkernel32.NewProc("GetFileAttributesW")
 	procSetFileAttributesW         = modkernel32.NewProc("SetFileAttributesW")
+	procGetFileAttributesExW       = modkernel32.NewProc("GetFileAttributesExW")
 	procGetCommandLineW            = modkernel32.NewProc("GetCommandLineW")
 	procCommandLineToArgvW         = modshell32.NewProc("CommandLineToArgvW")
 	procLocalFree                  = modkernel32.NewProc("LocalFree")
@@ -141,9 +142,9 @@ func FreeLibrary(handle Handle) (errno int) {
 	return
 }
 
-func GetProcAddress(module Handle, procname string) (proc Handle, errno int) {
+func GetProcAddress(module Handle, procname string) (proc uintptr, errno int) {
 	r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
-	proc = Handle(r0)
+	proc = uintptr(r0)
 	if proc == 0 {
 		if e1 != 0 {
 			errno = int(e1)
@@ -848,6 +849,20 @@ func SetFileAttributes(name *uint16, attrs uint32) (errno int) {
 	return
 }
 
+func GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) {
+	r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
+	if int(r1) == 0 {
+		if e1 != 0 {
+			errno = int(e1)
+		} else {
+			errno = EINVAL
+		}
+	} else {
+		errno = 0
+	}
+	return
+}
+
 func GetCommandLine() (cmd *uint16) {
 	r0, _, _ := Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0)
 	cmd = (*uint16)(unsafe.Pointer(r0))
diff --git a/src/pkg/syscall/zsysnum_openbsd_386.go b/src/pkg/syscall/zsysnum_openbsd_386.go
new file mode 100644
index 0000000..71310d8
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_openbsd_386.go
@@ -0,0 +1,207 @@
+// mksysnum_openbsd.pl 
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+	SYS_EXIT           = 1   // { void sys_exit(int rval); }
+	SYS_FORK           = 2   // { int sys_fork(void); }
+	SYS_READ           = 3   // { ssize_t sys_read(int fd, void *buf, size_t nbyte); }
+	SYS_WRITE          = 4   // { ssize_t sys_write(int fd, const void *buf, \
+	SYS_OPEN           = 5   // { int sys_open(const char *path, \
+	SYS_CLOSE          = 6   // { int sys_close(int fd); }
+	SYS_WAIT4          = 7   // { pid_t sys_wait4(pid_t pid, int *status, int options, \
+	SYS_LINK           = 9   // { int sys_link(const char *path, const char *link); }
+	SYS_UNLINK         = 10  // { int sys_unlink(const char *path); }
+	SYS_CHDIR          = 12  // { int sys_chdir(const char *path); }
+	SYS_FCHDIR         = 13  // { int sys_fchdir(int fd); }
+	SYS_MKNOD          = 14  // { int sys_mknod(const char *path, mode_t mode, \
+	SYS_CHMOD          = 15  // { int sys_chmod(const char *path, mode_t mode); }
+	SYS_CHOWN          = 16  // { int sys_chown(const char *path, uid_t uid, \
+	SYS_OBREAK         = 17  // { int sys_obreak(char *nsize); } break
+	SYS_GETPID         = 20  // { pid_t sys_getpid(void); }
+	SYS_MOUNT          = 21  // { int sys_mount(const char *type, const char *path, \
+	SYS_UNMOUNT        = 22  // { int sys_unmount(const char *path, int flags); }
+	SYS_SETUID         = 23  // { int sys_setuid(uid_t uid); }
+	SYS_GETUID         = 24  // { uid_t sys_getuid(void); }
+	SYS_GETEUID        = 25  // { uid_t sys_geteuid(void); }
+	SYS_PTRACE         = 26  // { int sys_ptrace(int req, pid_t pid, caddr_t addr, \
+	SYS_RECVMSG        = 27  // { ssize_t sys_recvmsg(int s, struct msghdr *msg, \
+	SYS_SENDMSG        = 28  // { ssize_t sys_sendmsg(int s, \
+	SYS_RECVFROM       = 29  // { ssize_t sys_recvfrom(int s, void *buf, size_t len, \
+	SYS_ACCEPT         = 30  // { int sys_accept(int s, struct sockaddr *name, \
+	SYS_GETPEERNAME    = 31  // { int sys_getpeername(int fdes, struct sockaddr *asa, \
+	SYS_GETSOCKNAME    = 32  // { int sys_getsockname(int fdes, struct sockaddr *asa, \
+	SYS_ACCESS         = 33  // { int sys_access(const char *path, int flags); }
+	SYS_CHFLAGS        = 34  // { int sys_chflags(const char *path, u_int flags); }
+	SYS_FCHFLAGS       = 35  // { int sys_fchflags(int fd, u_int flags); }
+	SYS_SYNC           = 36  // { void sys_sync(void); }
+	SYS_KILL           = 37  // { int sys_kill(int pid, int signum); }
+	SYS_GETPPID        = 39  // { pid_t sys_getppid(void); }
+	SYS_DUP            = 41  // { int sys_dup(int fd); }
+	SYS_GETEGID        = 43  // { gid_t sys_getegid(void); }
+	SYS_PROFIL         = 44  // { int sys_profil(caddr_t samples, size_t size, \
+	SYS_KTRACE         = 45  // { int sys_ktrace(const char *fname, int ops, \
+	SYS_SIGACTION      = 46  // { int sys_sigaction(int signum, \
+	SYS_GETGID         = 47  // { gid_t sys_getgid(void); }
+	SYS_SIGPROCMASK    = 48  // { int sys_sigprocmask(int how, sigset_t mask); }
+	SYS_GETLOGIN       = 49  // { int sys_getlogin(char *namebuf, u_int namelen); }
+	SYS_SETLOGIN       = 50  // { int sys_setlogin(const char *namebuf); }
+	SYS_ACCT           = 51  // { int sys_acct(const char *path); }
+	SYS_SIGPENDING     = 52  // { int sys_sigpending(void); }
+	SYS_OSIGALTSTACK   = 53  // { int sys_osigaltstack(const struct osigaltstack *nss, \
+	SYS_IOCTL          = 54  // { int sys_ioctl(int fd, \
+	SYS_REBOOT         = 55  // { int sys_reboot(int opt); }
+	SYS_REVOKE         = 56  // { int sys_revoke(const char *path); }
+	SYS_SYMLINK        = 57  // { int sys_symlink(const char *path, \
+	SYS_READLINK       = 58  // { int sys_readlink(const char *path, char *buf, \
+	SYS_EXECVE         = 59  // { int sys_execve(const char *path, \
+	SYS_UMASK          = 60  // { mode_t sys_umask(mode_t newmask); }
+	SYS_CHROOT         = 61  // { int sys_chroot(const char *path); }
+	SYS_VFORK          = 66  // { int sys_vfork(void); }
+	SYS_SBRK           = 69  // { int sys_sbrk(int incr); }
+	SYS_SSTK           = 70  // { int sys_sstk(int incr); }
+	SYS_MUNMAP         = 73  // { int sys_munmap(void *addr, size_t len); }
+	SYS_MPROTECT       = 74  // { int sys_mprotect(void *addr, size_t len, \
+	SYS_MADVISE        = 75  // { int sys_madvise(void *addr, size_t len, \
+	SYS_MINCORE        = 78  // { int sys_mincore(void *addr, size_t len, \
+	SYS_GETGROUPS      = 79  // { int sys_getgroups(int gidsetsize, \
+	SYS_SETGROUPS      = 80  // { int sys_setgroups(int gidsetsize, \
+	SYS_GETPGRP        = 81  // { int sys_getpgrp(void); }
+	SYS_SETPGID        = 82  // { int sys_setpgid(pid_t pid, int pgid); }
+	SYS_SETITIMER      = 83  // { int sys_setitimer(int which, \
+	SYS_GETITIMER      = 86  // { int sys_getitimer(int which, \
+	SYS_DUP2           = 90  // { int sys_dup2(int from, int to); }
+	SYS_FCNTL          = 92  // { int sys_fcntl(int fd, int cmd, ... void *arg); }
+	SYS_SELECT         = 93  // { int sys_select(int nd, fd_set *in, fd_set *ou, \
+	SYS_FSYNC          = 95  // { int sys_fsync(int fd); }
+	SYS_SETPRIORITY    = 96  // { int sys_setpriority(int which, id_t who, int prio); }
+	SYS_SOCKET         = 97  // { int sys_socket(int domain, int type, int protocol); }
+	SYS_CONNECT        = 98  // { int sys_connect(int s, const struct sockaddr *name, \
+	SYS_GETPRIORITY    = 100 // { int sys_getpriority(int which, id_t who); }
+	SYS_SIGRETURN      = 103 // { int sys_sigreturn(struct sigcontext *sigcntxp); }
+	SYS_BIND           = 104 // { int sys_bind(int s, const struct sockaddr *name, \
+	SYS_SETSOCKOPT     = 105 // { int sys_setsockopt(int s, int level, int name, \
+	SYS_LISTEN         = 106 // { int sys_listen(int s, int backlog); }
+	SYS_SIGSUSPEND     = 111 // { int sys_sigsuspend(int mask); }
+	SYS_GETTIMEOFDAY   = 116 // { int sys_gettimeofday(struct timeval *tp, \
+	SYS_GETRUSAGE      = 117 // { int sys_getrusage(int who, struct rusage *rusage); }
+	SYS_GETSOCKOPT     = 118 // { int sys_getsockopt(int s, int level, int name, \
+	SYS_READV          = 120 // { ssize_t sys_readv(int fd, \
+	SYS_WRITEV         = 121 // { ssize_t sys_writev(int fd, \
+	SYS_SETTIMEOFDAY   = 122 // { int sys_settimeofday(const struct timeval *tv, \
+	SYS_FCHOWN         = 123 // { int sys_fchown(int fd, uid_t uid, gid_t gid); }
+	SYS_FCHMOD         = 124 // { int sys_fchmod(int fd, mode_t mode); }
+	SYS_SETREUID       = 126 // { int sys_setreuid(uid_t ruid, uid_t euid); }
+	SYS_SETREGID       = 127 // { int sys_setregid(gid_t rgid, gid_t egid); }
+	SYS_RENAME         = 128 // { int sys_rename(const char *from, const char *to); }
+	SYS_FLOCK          = 131 // { int sys_flock(int fd, int how); }
+	SYS_MKFIFO         = 132 // { int sys_mkfifo(const char *path, mode_t mode); }
+	SYS_SENDTO         = 133 // { ssize_t sys_sendto(int s, const void *buf, \
+	SYS_SHUTDOWN       = 134 // { int sys_shutdown(int s, int how); }
+	SYS_SOCKETPAIR     = 135 // { int sys_socketpair(int domain, int type, \
+	SYS_MKDIR          = 136 // { int sys_mkdir(const char *path, mode_t mode); }
+	SYS_RMDIR          = 137 // { int sys_rmdir(const char *path); }
+	SYS_UTIMES         = 138 // { int sys_utimes(const char *path, \
+	SYS_ADJTIME        = 140 // { int sys_adjtime(const struct timeval *delta, \
+	SYS_SETSID         = 147 // { int sys_setsid(void); }
+	SYS_QUOTACTL       = 148 // { int sys_quotactl(const char *path, int cmd, \
+	SYS_NFSSVC         = 155 // { int sys_nfssvc(int flag, void *argp); }
+	SYS_GETFH          = 161 // { int sys_getfh(const char *fname, fhandle_t *fhp); }
+	SYS_SYSARCH        = 165 // { int sys_sysarch(int op, void *parms); }
+	SYS_PREAD          = 173 // { ssize_t sys_pread(int fd, void *buf, \
+	SYS_PWRITE         = 174 // { ssize_t sys_pwrite(int fd, const void *buf, \
+	SYS_SETGID         = 181 // { int sys_setgid(gid_t gid); }
+	SYS_SETEGID        = 182 // { int sys_setegid(gid_t egid); }
+	SYS_SETEUID        = 183 // { int sys_seteuid(uid_t euid); }
+	LFS_BMAPV          = 184 // { int lfs_bmapv(fsid_t *fsidp, \
+	LFS_MARKV          = 185 // { int lfs_markv(fsid_t *fsidp, \
+	LFS_SEGCLEAN       = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); }
+	LFS_SEGWAIT        = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); }
+	SYS_PATHCONF       = 191 // { long sys_pathconf(const char *path, int name); }
+	SYS_FPATHCONF      = 192 // { long sys_fpathconf(int fd, int name); }
+	SYS_SWAPCTL        = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); }
+	SYS_GETRLIMIT      = 194 // { int sys_getrlimit(int which, \
+	SYS_SETRLIMIT      = 195 // { int sys_setrlimit(int which, \
+	SYS_OGETDIRENTRIES = 196 // { int sys_ogetdirentries(int fd, char *buf, \
+	SYS_MMAP           = 197 // { void *sys_mmap(void *addr, size_t len, int prot, \
+	SYS_LSEEK          = 199 // { off_t sys_lseek(int fd, int pad, off_t offset, \
+	SYS_TRUNCATE       = 200 // { int sys_truncate(const char *path, int pad, \
+	SYS_FTRUNCATE      = 201 // { int sys_ftruncate(int fd, int pad, off_t length); }
+	SYS___SYSCTL       = 202 // { int sys___sysctl(int *name, u_int namelen, \
+	SYS_MLOCK          = 203 // { int sys_mlock(const void *addr, size_t len); }
+	SYS_MUNLOCK        = 204 // { int sys_munlock(const void *addr, size_t len); }
+	SYS_FUTIMES        = 206 // { int sys_futimes(int fd, \
+	SYS_GETPGID        = 207 // { pid_t sys_getpgid(pid_t pid); }
+	SYS_NNPFSPIOCTL    = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \
+	SYS_SEMGET         = 221 // { int sys_semget(key_t key, int nsems, int semflg); }
+	SYS_MSGGET         = 225 // { int sys_msgget(key_t key, int msgflg); }
+	SYS_MSGSND         = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \
+	SYS_MSGRCV         = 227 // { int sys_msgrcv(int msqid, void *msgp, size_t msgsz, \
+	SYS_SHMAT          = 228 // { void *sys_shmat(int shmid, const void *shmaddr, \
+	SYS_SHMDT          = 230 // { int sys_shmdt(const void *shmaddr); }
+	SYS_CLOCK_GETTIME  = 232 // { int sys_clock_gettime(clockid_t clock_id, \
+	SYS_CLOCK_SETTIME  = 233 // { int sys_clock_settime(clockid_t clock_id, \
+	SYS_CLOCK_GETRES   = 234 // { int sys_clock_getres(clockid_t clock_id, \
+	SYS_NANOSLEEP      = 240 // { int sys_nanosleep(const struct timespec *rqtp, \
+	SYS_MINHERIT       = 250 // { int sys_minherit(void *addr, size_t len, \
+	SYS_RFORK          = 251 // { int sys_rfork(int flags); }
+	SYS_POLL           = 252 // { int sys_poll(struct pollfd *fds, \
+	SYS_ISSETUGID      = 253 // { int sys_issetugid(void); }
+	SYS_LCHOWN         = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); }
+	SYS_GETSID         = 255 // { pid_t sys_getsid(pid_t pid); }
+	SYS_MSYNC          = 256 // { int sys_msync(void *addr, size_t len, int flags); }
+	SYS_PIPE           = 263 // { int sys_pipe(int *fdp); }
+	SYS_FHOPEN         = 264 // { int sys_fhopen(const fhandle_t *fhp, int flags); }
+	SYS_PREADV         = 267 // { ssize_t sys_preadv(int fd, \
+	SYS_PWRITEV        = 268 // { ssize_t sys_pwritev(int fd, \
+	SYS_KQUEUE         = 269 // { int sys_kqueue(void); }
+	SYS_KEVENT         = 270 // { int sys_kevent(int fd, \
+	SYS_MLOCKALL       = 271 // { int sys_mlockall(int flags); }
+	SYS_MUNLOCKALL     = 272 // { int sys_munlockall(void); }
+	SYS_GETRESUID      = 281 // { int sys_getresuid(uid_t *ruid, uid_t *euid, \
+	SYS_SETRESUID      = 282 // { int sys_setresuid(uid_t ruid, uid_t euid, \
+	SYS_GETRESGID      = 283 // { int sys_getresgid(gid_t *rgid, gid_t *egid, \
+	SYS_SETRESGID      = 284 // { int sys_setresgid(gid_t rgid, gid_t egid, \
+	SYS_MQUERY         = 286 // { void *sys_mquery(void *addr, size_t len, int prot, \
+	SYS_CLOSEFROM      = 287 // { int sys_closefrom(int fd); }
+	SYS_SIGALTSTACK    = 288 // { int sys_sigaltstack(const struct sigaltstack *nss, \
+	SYS_SHMGET         = 289 // { int sys_shmget(key_t key, size_t size, int shmflg); }
+	SYS_SEMOP          = 290 // { int sys_semop(int semid, struct sembuf *sops, \
+	SYS_STAT           = 291 // { int sys_stat(const char *path, struct stat *ub); }
+	SYS_FSTAT          = 292 // { int sys_fstat(int fd, struct stat *sb); }
+	SYS_LSTAT          = 293 // { int sys_lstat(const char *path, struct stat *ub); }
+	SYS_FHSTAT         = 294 // { int sys_fhstat(const fhandle_t *fhp, \
+	SYS___SEMCTL       = 295 // { int sys___semctl(int semid, int semnum, int cmd, \
+	SYS_SHMCTL         = 296 // { int sys_shmctl(int shmid, int cmd, \
+	SYS_MSGCTL         = 297 // { int sys_msgctl(int msqid, int cmd, \
+	SYS_SCHED_YIELD    = 298 // { int sys_sched_yield(void); }
+	SYS_GETTHRID       = 299 // { pid_t sys_getthrid(void); }
+	SYS_THRSLEEP       = 300 // { int sys_thrsleep(void *ident, clockid_t clock_id, \
+	SYS_THRWAKEUP      = 301 // { int sys_thrwakeup(void *ident, int n); }
+	SYS_THREXIT        = 302 // { void sys_threxit(pid_t *notdead); }
+	SYS_THRSIGDIVERT   = 303 // { int sys_thrsigdivert(sigset_t sigmask, \
+	SYS___GETCWD       = 304 // { int sys___getcwd(char *buf, size_t len); }
+	SYS_ADJFREQ        = 305 // { int sys_adjfreq(const int64_t *freq, \
+	SYS_GETFSSTAT      = 306 // { int sys_getfsstat(struct statfs *buf, size_t bufsize, \
+	SYS_STATFS         = 307 // { int sys_statfs(const char *path, \
+	SYS_FSTATFS        = 308 // { int sys_fstatfs(int fd, struct statfs *buf); }
+	SYS_FHSTATFS       = 309 // { int sys_fhstatfs(const fhandle_t *fhp, \
+	SYS_SETRTABLE      = 310 // { int sys_setrtable(int rtableid); }
+	SYS_GETRTABLE      = 311 // { int sys_getrtable(void); }
+	SYS_GETDIRENTRIES  = 312 // { int sys_getdirentries(int fd, char *buf, \
+	SYS_FACCESSAT      = 313 // { int sys_faccessat(int fd, const char *path, \
+	SYS_FCHMODAT       = 314 // { int sys_fchmodat(int fd, const char *path, \
+	SYS_FCHOWNAT       = 315 // { int sys_fchownat(int fd, const char *path, \
+	SYS_FSTATAT        = 316 // { int sys_fstatat(int fd, const char *path, \
+	SYS_LINKAT         = 317 // { int sys_linkat(int fd1, const char *path1, int fd2, \
+	SYS_MKDIRAT        = 318 // { int sys_mkdirat(int fd, const char *path, \
+	SYS_MKFIFOAT       = 319 // { int sys_mkfifoat(int fd, const char *path, \
+	SYS_MKNODAT        = 320 // { int sys_mknodat(int fd, const char *path, \
+	SYS_OPENAT         = 321 // { int sys_openat(int fd, const char *path, int flags, \
+	SYS_READLINKAT     = 322 // { ssize_t sys_readlinkat(int fd, const char *path, \
+	SYS_RENAMEAT       = 323 // { int sys_renameat(int fromfd, const char *from, \
+	SYS_SYMLINKAT      = 324 // { int sys_symlinkat(const char *path, int fd, \
+	SYS_UNLINKAT       = 325 // { int sys_unlinkat(int fd, const char *path, \
+	SYS_UTIMENSAT      = 326 // { int sys_utimensat(int fd, const char *path, \
+)
diff --git a/src/pkg/syscall/zsysnum_openbsd_amd64.go b/src/pkg/syscall/zsysnum_openbsd_amd64.go
new file mode 100644
index 0000000..dc47750
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_openbsd_amd64.go
@@ -0,0 +1,204 @@
+// mksysnum_openbsd.pl 
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+	SYS_EXIT          = 1   // { void sys_exit(int rval); }
+	SYS_FORK          = 2   // { int sys_fork(void); }
+	SYS_READ          = 3   // { ssize_t sys_read(int fd, void *buf, size_t nbyte); }
+	SYS_WRITE         = 4   // { ssize_t sys_write(int fd, const void *buf, \
+	SYS_OPEN          = 5   // { int sys_open(const char *path, \
+	SYS_CLOSE         = 6   // { int sys_close(int fd); }
+	SYS_WAIT4         = 7   // { pid_t sys_wait4(pid_t pid, int *status, int options, \
+	SYS_LINK          = 9   // { int sys_link(const char *path, const char *link); }
+	SYS_UNLINK        = 10  // { int sys_unlink(const char *path); }
+	SYS_CHDIR         = 12  // { int sys_chdir(const char *path); }
+	SYS_FCHDIR        = 13  // { int sys_fchdir(int fd); }
+	SYS_MKNOD         = 14  // { int sys_mknod(const char *path, mode_t mode, \
+	SYS_CHMOD         = 15  // { int sys_chmod(const char *path, mode_t mode); }
+	SYS_CHOWN         = 16  // { int sys_chown(const char *path, uid_t uid, \
+	SYS_OBREAK        = 17  // { int sys_obreak(char *nsize); } break
+	SYS_GETPID        = 20  // { pid_t sys_getpid(void); }
+	SYS_MOUNT         = 21  // { int sys_mount(const char *type, const char *path, \
+	SYS_UNMOUNT       = 22  // { int sys_unmount(const char *path, int flags); }
+	SYS_SETUID        = 23  // { int sys_setuid(uid_t uid); }
+	SYS_GETUID        = 24  // { uid_t sys_getuid(void); }
+	SYS_GETEUID       = 25  // { uid_t sys_geteuid(void); }
+	SYS_PTRACE        = 26  // { int sys_ptrace(int req, pid_t pid, caddr_t addr, \
+	SYS_RECVMSG       = 27  // { ssize_t sys_recvmsg(int s, struct msghdr *msg, \
+	SYS_SENDMSG       = 28  // { ssize_t sys_sendmsg(int s, \
+	SYS_RECVFROM      = 29  // { ssize_t sys_recvfrom(int s, void *buf, size_t len, \
+	SYS_ACCEPT        = 30  // { int sys_accept(int s, struct sockaddr *name, \
+	SYS_GETPEERNAME   = 31  // { int sys_getpeername(int fdes, struct sockaddr *asa, \
+	SYS_GETSOCKNAME   = 32  // { int sys_getsockname(int fdes, struct sockaddr *asa, \
+	SYS_ACCESS        = 33  // { int sys_access(const char *path, int flags); }
+	SYS_CHFLAGS       = 34  // { int sys_chflags(const char *path, u_int flags); }
+	SYS_FCHFLAGS      = 35  // { int sys_fchflags(int fd, u_int flags); }
+	SYS_SYNC          = 36  // { void sys_sync(void); }
+	SYS_KILL          = 37  // { int sys_kill(int pid, int signum); }
+	SYS_GETPPID       = 39  // { pid_t sys_getppid(void); }
+	SYS_DUP           = 41  // { int sys_dup(int fd); }
+	SYS_GETEGID       = 43  // { gid_t sys_getegid(void); }
+	SYS_PROFIL        = 44  // { int sys_profil(caddr_t samples, size_t size, \
+	SYS_KTRACE        = 45  // { int sys_ktrace(const char *fname, int ops, \
+	SYS_SIGACTION     = 46  // { int sys_sigaction(int signum, \
+	SYS_GETGID        = 47  // { gid_t sys_getgid(void); }
+	SYS_SIGPROCMASK   = 48  // { int sys_sigprocmask(int how, sigset_t mask); }
+	SYS_GETLOGIN      = 49  // { int sys_getlogin(char *namebuf, u_int namelen); }
+	SYS_SETLOGIN      = 50  // { int sys_setlogin(const char *namebuf); }
+	SYS_ACCT          = 51  // { int sys_acct(const char *path); }
+	SYS_SIGPENDING    = 52  // { int sys_sigpending(void); }
+	SYS_IOCTL         = 54  // { int sys_ioctl(int fd, \
+	SYS_REBOOT        = 55  // { int sys_reboot(int opt); }
+	SYS_REVOKE        = 56  // { int sys_revoke(const char *path); }
+	SYS_SYMLINK       = 57  // { int sys_symlink(const char *path, \
+	SYS_READLINK      = 58  // { int sys_readlink(const char *path, char *buf, \
+	SYS_EXECVE        = 59  // { int sys_execve(const char *path, \
+	SYS_UMASK         = 60  // { mode_t sys_umask(mode_t newmask); }
+	SYS_CHROOT        = 61  // { int sys_chroot(const char *path); }
+	SYS_VFORK         = 66  // { int sys_vfork(void); }
+	SYS_MUNMAP        = 73  // { int sys_munmap(void *addr, size_t len); }
+	SYS_MPROTECT      = 74  // { int sys_mprotect(void *addr, size_t len, \
+	SYS_MADVISE       = 75  // { int sys_madvise(void *addr, size_t len, \
+	SYS_MINCORE       = 78  // { int sys_mincore(void *addr, size_t len, \
+	SYS_GETGROUPS     = 79  // { int sys_getgroups(int gidsetsize, \
+	SYS_SETGROUPS     = 80  // { int sys_setgroups(int gidsetsize, \
+	SYS_GETPGRP       = 81  // { int sys_getpgrp(void); }
+	SYS_SETPGID       = 82  // { int sys_setpgid(pid_t pid, int pgid); }
+	SYS_SETITIMER     = 83  // { int sys_setitimer(int which, \
+	SYS_GETITIMER     = 86  // { int sys_getitimer(int which, \
+	SYS_DUP2          = 90  // { int sys_dup2(int from, int to); }
+	SYS_FCNTL         = 92  // { int sys_fcntl(int fd, int cmd, ... void *arg); }
+	SYS_SELECT        = 93  // { int sys_select(int nd, fd_set *in, fd_set *ou, \
+	SYS_FSYNC         = 95  // { int sys_fsync(int fd); }
+	SYS_SETPRIORITY   = 96  // { int sys_setpriority(int which, id_t who, int prio); }
+	SYS_SOCKET        = 97  // { int sys_socket(int domain, int type, int protocol); }
+	SYS_CONNECT       = 98  // { int sys_connect(int s, const struct sockaddr *name, \
+	SYS_GETPRIORITY   = 100 // { int sys_getpriority(int which, id_t who); }
+	SYS_SIGRETURN     = 103 // { int sys_sigreturn(struct sigcontext *sigcntxp); }
+	SYS_BIND          = 104 // { int sys_bind(int s, const struct sockaddr *name, \
+	SYS_SETSOCKOPT    = 105 // { int sys_setsockopt(int s, int level, int name, \
+	SYS_LISTEN        = 106 // { int sys_listen(int s, int backlog); }
+	SYS_SIGSUSPEND    = 111 // { int sys_sigsuspend(int mask); }
+	SYS_GETTIMEOFDAY  = 116 // { int sys_gettimeofday(struct timeval *tp, \
+	SYS_GETRUSAGE     = 117 // { int sys_getrusage(int who, struct rusage *rusage); }
+	SYS_GETSOCKOPT    = 118 // { int sys_getsockopt(int s, int level, int name, \
+	SYS_READV         = 120 // { ssize_t sys_readv(int fd, \
+	SYS_WRITEV        = 121 // { ssize_t sys_writev(int fd, \
+	SYS_SETTIMEOFDAY  = 122 // { int sys_settimeofday(const struct timeval *tv, \
+	SYS_FCHOWN        = 123 // { int sys_fchown(int fd, uid_t uid, gid_t gid); }
+	SYS_FCHMOD        = 124 // { int sys_fchmod(int fd, mode_t mode); }
+	SYS_SETREUID      = 126 // { int sys_setreuid(uid_t ruid, uid_t euid); }
+	SYS_SETREGID      = 127 // { int sys_setregid(gid_t rgid, gid_t egid); }
+	SYS_RENAME        = 128 // { int sys_rename(const char *from, const char *to); }
+	SYS_FLOCK         = 131 // { int sys_flock(int fd, int how); }
+	SYS_MKFIFO        = 132 // { int sys_mkfifo(const char *path, mode_t mode); }
+	SYS_SENDTO        = 133 // { ssize_t sys_sendto(int s, const void *buf, \
+	SYS_SHUTDOWN      = 134 // { int sys_shutdown(int s, int how); }
+	SYS_SOCKETPAIR    = 135 // { int sys_socketpair(int domain, int type, \
+	SYS_MKDIR         = 136 // { int sys_mkdir(const char *path, mode_t mode); }
+	SYS_RMDIR         = 137 // { int sys_rmdir(const char *path); }
+	SYS_UTIMES        = 138 // { int sys_utimes(const char *path, \
+	SYS_ADJTIME       = 140 // { int sys_adjtime(const struct timeval *delta, \
+	SYS_SETSID        = 147 // { int sys_setsid(void); }
+	SYS_QUOTACTL      = 148 // { int sys_quotactl(const char *path, int cmd, \
+	SYS_NFSSVC        = 155 // { int sys_nfssvc(int flag, void *argp); }
+	SYS_GETFH         = 161 // { int sys_getfh(const char *fname, fhandle_t *fhp); }
+	SYS_SYSARCH       = 165 // { int sys_sysarch(int op, void *parms); }
+	SYS_PREAD         = 173 // { ssize_t sys_pread(int fd, void *buf, \
+	SYS_PWRITE        = 174 // { ssize_t sys_pwrite(int fd, const void *buf, \
+	SYS_SETGID        = 181 // { int sys_setgid(gid_t gid); }
+	SYS_SETEGID       = 182 // { int sys_setegid(gid_t egid); }
+	SYS_SETEUID       = 183 // { int sys_seteuid(uid_t euid); }
+	LFS_BMAPV         = 184 // { int lfs_bmapv(fsid_t *fsidp, \
+	LFS_MARKV         = 185 // { int lfs_markv(fsid_t *fsidp, \
+	LFS_SEGCLEAN      = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); }
+	LFS_SEGWAIT       = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); }
+	SYS_PATHCONF      = 191 // { long sys_pathconf(const char *path, int name); }
+	SYS_FPATHCONF     = 192 // { long sys_fpathconf(int fd, int name); }
+	SYS_SWAPCTL       = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); }
+	SYS_GETRLIMIT     = 194 // { int sys_getrlimit(int which, \
+	SYS_SETRLIMIT     = 195 // { int sys_setrlimit(int which, \
+	SYS_MMAP          = 197 // { void *sys_mmap(void *addr, size_t len, int prot, \
+	SYS_LSEEK         = 199 // { off_t sys_lseek(int fd, int pad, off_t offset, \
+	SYS_TRUNCATE      = 200 // { int sys_truncate(const char *path, int pad, \
+	SYS_FTRUNCATE     = 201 // { int sys_ftruncate(int fd, int pad, off_t length); }
+	SYS___SYSCTL      = 202 // { int sys___sysctl(int *name, u_int namelen, \
+	SYS_MLOCK         = 203 // { int sys_mlock(const void *addr, size_t len); }
+	SYS_MUNLOCK       = 204 // { int sys_munlock(const void *addr, size_t len); }
+	SYS_FUTIMES       = 206 // { int sys_futimes(int fd, \
+	SYS_GETPGID       = 207 // { pid_t sys_getpgid(pid_t pid); }
+	SYS_NNPFSPIOCTL   = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \
+	SYS_SEMGET        = 221 // { int sys_semget(key_t key, int nsems, int semflg); }
+	SYS_MSGGET        = 225 // { int sys_msgget(key_t key, int msgflg); }
+	SYS_MSGSND        = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \
+	SYS_MSGRCV        = 227 // { int sys_msgrcv(int msqid, void *msgp, size_t msgsz, \
+	SYS_SHMAT         = 228 // { void *sys_shmat(int shmid, const void *shmaddr, \
+	SYS_SHMDT         = 230 // { int sys_shmdt(const void *shmaddr); }
+	SYS_CLOCK_GETTIME = 232 // { int sys_clock_gettime(clockid_t clock_id, \
+	SYS_CLOCK_SETTIME = 233 // { int sys_clock_settime(clockid_t clock_id, \
+	SYS_CLOCK_GETRES  = 234 // { int sys_clock_getres(clockid_t clock_id, \
+	SYS_NANOSLEEP     = 240 // { int sys_nanosleep(const struct timespec *rqtp, \
+	SYS_MINHERIT      = 250 // { int sys_minherit(void *addr, size_t len, \
+	SYS_RFORK         = 251 // { int sys_rfork(int flags); }
+	SYS_POLL          = 252 // { int sys_poll(struct pollfd *fds, \
+	SYS_ISSETUGID     = 253 // { int sys_issetugid(void); }
+	SYS_LCHOWN        = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); }
+	SYS_GETSID        = 255 // { pid_t sys_getsid(pid_t pid); }
+	SYS_MSYNC         = 256 // { int sys_msync(void *addr, size_t len, int flags); }
+	SYS_PIPE          = 263 // { int sys_pipe(int *fdp); }
+	SYS_FHOPEN        = 264 // { int sys_fhopen(const fhandle_t *fhp, int flags); }
+	SYS_PREADV        = 267 // { ssize_t sys_preadv(int fd, \
+	SYS_PWRITEV       = 268 // { ssize_t sys_pwritev(int fd, \
+	SYS_KQUEUE        = 269 // { int sys_kqueue(void); }
+	SYS_KEVENT        = 270 // { int sys_kevent(int fd, \
+	SYS_MLOCKALL      = 271 // { int sys_mlockall(int flags); }
+	SYS_MUNLOCKALL    = 272 // { int sys_munlockall(void); }
+	SYS_GETRESUID     = 281 // { int sys_getresuid(uid_t *ruid, uid_t *euid, \
+	SYS_SETRESUID     = 282 // { int sys_setresuid(uid_t ruid, uid_t euid, \
+	SYS_GETRESGID     = 283 // { int sys_getresgid(gid_t *rgid, gid_t *egid, \
+	SYS_SETRESGID     = 284 // { int sys_setresgid(gid_t rgid, gid_t egid, \
+	SYS_MQUERY        = 286 // { void *sys_mquery(void *addr, size_t len, int prot, \
+	SYS_CLOSEFROM     = 287 // { int sys_closefrom(int fd); }
+	SYS_SIGALTSTACK   = 288 // { int sys_sigaltstack(const struct sigaltstack *nss, \
+	SYS_SHMGET        = 289 // { int sys_shmget(key_t key, size_t size, int shmflg); }
+	SYS_SEMOP         = 290 // { int sys_semop(int semid, struct sembuf *sops, \
+	SYS_STAT          = 291 // { int sys_stat(const char *path, struct stat *ub); }
+	SYS_FSTAT         = 292 // { int sys_fstat(int fd, struct stat *sb); }
+	SYS_LSTAT         = 293 // { int sys_lstat(const char *path, struct stat *ub); }
+	SYS_FHSTAT        = 294 // { int sys_fhstat(const fhandle_t *fhp, \
+	SYS___SEMCTL      = 295 // { int sys___semctl(int semid, int semnum, int cmd, \
+	SYS_SHMCTL        = 296 // { int sys_shmctl(int shmid, int cmd, \
+	SYS_MSGCTL        = 297 // { int sys_msgctl(int msqid, int cmd, \
+	SYS_SCHED_YIELD   = 298 // { int sys_sched_yield(void); }
+	SYS_GETTHRID      = 299 // { pid_t sys_getthrid(void); }
+	SYS_THRSLEEP      = 300 // { int sys_thrsleep(void *ident, clockid_t clock_id, \
+	SYS_THRWAKEUP     = 301 // { int sys_thrwakeup(void *ident, int n); }
+	SYS_THREXIT       = 302 // { void sys_threxit(pid_t *notdead); }
+	SYS_THRSIGDIVERT  = 303 // { int sys_thrsigdivert(sigset_t sigmask, \
+	SYS___GETCWD      = 304 // { int sys___getcwd(char *buf, size_t len); }
+	SYS_ADJFREQ       = 305 // { int sys_adjfreq(const int64_t *freq, \
+	SYS_GETFSSTAT     = 306 // { int sys_getfsstat(struct statfs *buf, size_t bufsize, \
+	SYS_STATFS        = 307 // { int sys_statfs(const char *path, \
+	SYS_FSTATFS       = 308 // { int sys_fstatfs(int fd, struct statfs *buf); }
+	SYS_FHSTATFS      = 309 // { int sys_fhstatfs(const fhandle_t *fhp, \
+	SYS_SETRTABLE     = 310 // { int sys_setrtable(int rtableid); }
+	SYS_GETRTABLE     = 311 // { int sys_getrtable(void); }
+	SYS_GETDIRENTRIES = 312 // { int sys_getdirentries(int fd, char *buf, \
+	SYS_FACCESSAT     = 313 // { int sys_faccessat(int fd, const char *path, \
+	SYS_FCHMODAT      = 314 // { int sys_fchmodat(int fd, const char *path, \
+	SYS_FCHOWNAT      = 315 // { int sys_fchownat(int fd, const char *path, \
+	SYS_FSTATAT       = 316 // { int sys_fstatat(int fd, const char *path, \
+	SYS_LINKAT        = 317 // { int sys_linkat(int fd1, const char *path1, int fd2, \
+	SYS_MKDIRAT       = 318 // { int sys_mkdirat(int fd, const char *path, \
+	SYS_MKFIFOAT      = 319 // { int sys_mkfifoat(int fd, const char *path, \
+	SYS_MKNODAT       = 320 // { int sys_mknodat(int fd, const char *path, \
+	SYS_OPENAT        = 321 // { int sys_openat(int fd, const char *path, int flags, \
+	SYS_READLINKAT    = 322 // { ssize_t sys_readlinkat(int fd, const char *path, \
+	SYS_RENAMEAT      = 323 // { int sys_renameat(int fromfd, const char *from, \
+	SYS_SYMLINKAT     = 324 // { int sys_symlinkat(const char *path, int fd, \
+	SYS_UNLINKAT      = 325 // { int sys_unlinkat(int fd, const char *path, \
+	SYS_UTIMENSAT     = 326 // { int sys_utimensat(int fd, const char *path, \
+	SYS_FUTIMENS      = 327 // { int sys_futimens(int fd, \
+)
diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go
index 252fbff..0335eb5 100644
--- a/src/pkg/syscall/ztypes_linux_386.go
+++ b/src/pkg/syscall/ztypes_linux_386.go
@@ -98,7 +98,7 @@ const (
 	SizeofRtAttr            = 0x4
 	SizeofIfInfomsg         = 0x10
 	SizeofIfAddrmsg         = 0x8
-	SizeofRtmsg             = 0xc
+	SizeofRtMsg             = 0xc
 	SizeofRtNexthop         = 0x8
 	SizeofSockFilter        = 0x8
 	SizeofSockFprog         = 0x8
diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go
index 520ba96..c97b7f6 100644
--- a/src/pkg/syscall/ztypes_linux_amd64.go
+++ b/src/pkg/syscall/ztypes_linux_amd64.go
@@ -98,7 +98,7 @@ const (
 	SizeofRtAttr            = 0x4
 	SizeofIfInfomsg         = 0x10
 	SizeofIfAddrmsg         = 0x8
-	SizeofRtmsg             = 0xc
+	SizeofRtMsg             = 0xc
 	SizeofRtNexthop         = 0x8
 	SizeofSockFilter        = 0x8
 	SizeofSockFprog         = 0x10
diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go
index 2421df0..3d4aded 100644
--- a/src/pkg/syscall/ztypes_linux_arm.go
+++ b/src/pkg/syscall/ztypes_linux_arm.go
@@ -103,7 +103,7 @@ const (
 	SizeofRtAttr            = 0x4
 	SizeofIfInfomsg         = 0x10
 	SizeofIfAddrmsg         = 0x8
-	SizeofRtmsg             = 0xc
+	SizeofRtMsg             = 0xc
 	SizeofRtNexthop         = 0x8
 	SizeofSockFilter        = 0x8
 	SizeofSockFprog         = 0x8
diff --git a/src/pkg/syscall/ztypes_openbsd_386.go b/src/pkg/syscall/ztypes_openbsd_386.go
new file mode 100644
index 0000000..af6824a
--- /dev/null
+++ b/src/pkg/syscall/ztypes_openbsd_386.go
@@ -0,0 +1,383 @@
+// godefs -gsyscall -f-m32 types_openbsd.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+	sizeofPtr              = 0x4
+	sizeofShort            = 0x2
+	sizeofInt              = 0x4
+	sizeofLong             = 0x4
+	sizeofLongLong         = 0x8
+	S_IFMT                 = 0xf000
+	S_IFIFO                = 0x1000
+	S_IFCHR                = 0x2000
+	S_IFDIR                = 0x4000
+	S_IFBLK                = 0x6000
+	S_IFREG                = 0x8000
+	S_IFLNK                = 0xa000
+	S_IFSOCK               = 0xc000
+	S_ISUID                = 0x800
+	S_ISGID                = 0x400
+	S_ISVTX                = 0x200
+	S_IRUSR                = 0x100
+	S_IWUSR                = 0x80
+	S_IXUSR                = 0x40
+	SizeofSockaddrInet4    = 0x10
+	SizeofSockaddrInet6    = 0x1c
+	SizeofSockaddrAny      = 0x6c
+	SizeofSockaddrUnix     = 0x6a
+	SizeofSockaddrDatalink = 0x20
+	SizeofLinger           = 0x8
+	SizeofIPMreq           = 0x8
+	SizeofIPv6Mreq         = 0x14
+	SizeofMsghdr           = 0x1c
+	SizeofCmsghdr          = 0xc
+	SizeofInet6Pktinfo     = 0x14
+	PTRACE_TRACEME         = 0
+	PTRACE_CONT            = 0x7
+	PTRACE_KILL            = 0x8
+	SizeofIfMsghdr         = 0xe4
+	SizeofIfData           = 0xcc
+	SizeofIfaMsghdr        = 0x18
+	SizeofRtMsghdr         = 0x58
+	SizeofRtMetrics        = 0x30
+	SizeofBpfVersion       = 0x4
+	SizeofBpfStat          = 0x8
+	SizeofBpfProgram       = 0x8
+	SizeofBpfInsn          = 0x8
+	SizeofBpfHdr           = 0x14
+)
+
+// Types
+
+type _C_short int16
+
+type _C_int int32
+
+type _C_long int32
+
+type _C_long_long int64
+
+type Timespec struct {
+	Sec  int32
+	Nsec int32
+}
+
+type Timeval struct {
+	Sec  int32
+	Usec int32
+}
+
+type Rusage struct {
+	Utime    Timeval
+	Stime    Timeval
+	Maxrss   int32
+	Ixrss    int32
+	Idrss    int32
+	Isrss    int32
+	Minflt   int32
+	Majflt   int32
+	Nswap    int32
+	Inblock  int32
+	Oublock  int32
+	Msgsnd   int32
+	Msgrcv   int32
+	Nsignals int32
+	Nvcsw    int32
+	Nivcsw   int32
+}
+
+type Rlimit struct {
+	Cur uint64
+	Max uint64
+}
+
+type _Gid_t uint32
+
+type Stat_t struct {
+	Dev            int32
+	Ino            uint32
+	Mode           uint32
+	Nlink          uint32
+	Uid            uint32
+	Gid            uint32
+	Rdev           int32
+	Lspare0        int32
+	Atim           Timespec
+	Mtim           Timespec
+	Ctim           Timespec
+	Size           int64
+	Blocks         int64
+	Blksize        uint32
+	Flags          uint32
+	Gen            uint32
+	Lspare1        int32
+	X__st_birthtim Timespec
+	Qspare         [2]int64
+}
+
+type Statfs_t struct {
+	F_flags       uint32
+	F_bsize       uint32
+	F_iosize      uint32
+	F_blocks      uint64
+	F_bfree       uint64
+	F_bavail      int64
+	F_files       uint64
+	F_ffree       uint64
+	F_favail      int64
+	F_syncwrites  uint64
+	F_syncreads   uint64
+	F_asyncwrites uint64
+	F_asyncreads  uint64
+	F_fsid        [8]byte /* fsid_t */
+	F_namemax     uint32
+	F_owner       uint32
+	F_ctime       uint32
+	F_spare       [3]uint32
+	F_fstypename  [16]int8
+	F_mntonname   [90]int8
+	F_mntfromname [90]int8
+	Mount_info    [160]byte /* mount_info */
+}
+
+type Flock_t struct {
+	Start  int64
+	Len    int64
+	Pid    int32
+	Type   int16
+	Whence int16
+}
+
+type Dirent struct {
+	Fileno uint32
+	Reclen uint16
+	Type   uint8
+	Namlen uint8
+	Name   [256]int8
+}
+
+type RawSockaddrInet4 struct {
+	Len    uint8
+	Family uint8
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]int8
+}
+
+type RawSockaddrInet6 struct {
+	Len      uint8
+	Family   uint8
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte /* in6_addr */
+	Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+	Len    uint8
+	Family uint8
+	Path   [104]int8
+}
+
+type RawSockaddrDatalink struct {
+	Len    uint8
+	Family uint8
+	Index  uint16
+	Type   uint8
+	Nlen   uint8
+	Alen   uint8
+	Slen   uint8
+	Data   [24]int8
+}
+
+type RawSockaddr struct {
+	Len    uint8
+	Family uint8
+	Data   [14]int8
+}
+
+type RawSockaddrAny struct {
+	Addr RawSockaddr
+	Pad  [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+	Onoff  int32
+	Linger int32
+}
+
+type Iovec struct {
+	Base *byte
+	Len  uint32
+}
+
+type IPMreq struct {
+	Multiaddr [4]byte /* in_addr */
+	Interface [4]byte /* in_addr */
+}
+
+type IPv6Mreq struct {
+	Multiaddr [16]byte /* in6_addr */
+	Interface uint32
+}
+
+type Msghdr struct {
+	Name       *byte
+	Namelen    uint32
+	Iov        *Iovec
+	Iovlen     uint32
+	Control    *byte
+	Controllen uint32
+	Flags      int32
+}
+
+type Cmsghdr struct {
+	Len   uint32
+	Level int32
+	Type  int32
+}
+
+type Inet6Pktinfo struct {
+	Addr    [16]byte /* in6_addr */
+	Ifindex uint32
+}
+
+type Kevent_t struct {
+	Ident  uint32
+	Filter int16
+	Flags  uint16
+	Fflags uint32
+	Data   int32
+	Udata  *byte
+}
+
+type FdSet struct {
+	Bits [32]int32
+}
+
+type IfMsghdr struct {
+	Msglen  uint16
+	Version uint8
+	Type    uint8
+	Hdrlen  uint16
+	Index   uint16
+	Tableid uint16
+	Pad1    uint8
+	Pad2    uint8
+	Addrs   int32
+	Flags   int32
+	Xflags  int32
+	Data    IfData
+}
+
+type IfData struct {
+	Type       uint8
+	Addrlen    uint8
+	Hdrlen     uint8
+	Link_state uint8
+	Mtu        uint32
+	Metric     uint32
+	Pad        uint32
+	Baudrate   uint64
+	Ipackets   uint64
+	Ierrors    uint64
+	Opackets   uint64
+	Oerrors    uint64
+	Collisions uint64
+	Ibytes     uint64
+	Obytes     uint64
+	Imcasts    uint64
+	Omcasts    uint64
+	Iqdrops    uint64
+	Noproto    uint64
+	Lastchange Timeval
+	Mclpool    [7][12]byte /* mclpool */
+}
+
+type IfaMsghdr struct {
+	Msglen  uint16
+	Version uint8
+	Type    uint8
+	Hdrlen  uint16
+	Index   uint16
+	Tableid uint16
+	Pad1    uint8
+	Pad2    uint8
+	Addrs   int32
+	Flags   int32
+	Metric  int32
+}
+
+type RtMsghdr struct {
+	Msglen   uint16
+	Version  uint8
+	Type     uint8
+	Hdrlen   uint16
+	Index    uint16
+	Tableid  uint16
+	Priority uint8
+	Mpls     uint8
+	Addrs    int32
+	Flags    int32
+	Fmask    int32
+	Pid      int32
+	Seq      int32
+	Errno    int32
+	Inits    uint32
+	Rmx      RtMetrics
+}
+
+type RtMetrics struct {
+	Pksent   uint64
+	Locks    uint32
+	Mtu      uint32
+	Expire   uint32
+	Refcnt   uint32
+	Hopcount uint32
+	Recvpipe uint32
+	Sendpipe uint32
+	Ssthresh uint32
+	Rtt      uint32
+	Rttvar   uint32
+}
+
+type BpfVersion struct {
+	Major uint16
+	Minor uint16
+}
+
+type BpfStat struct {
+	Recv uint32
+	Drop uint32
+}
+
+type BpfProgram struct {
+	Len   uint32
+	Insns *BpfInsn
+}
+
+type BpfInsn struct {
+	Code uint16
+	Jt   uint8
+	Jf   uint8
+	K    uint32
+}
+
+type BpfHdr struct {
+	Tstamp       BpfTimeval
+	Caplen       uint32
+	Datalen      uint32
+	Hdrlen       uint16
+	Pad_godefs_0 [2]byte
+}
+
+type BpfTimeval struct {
+	Sec  uint32
+	Usec uint32
+}
diff --git a/src/pkg/syscall/ztypes_openbsd_amd64.go b/src/pkg/syscall/ztypes_openbsd_amd64.go
new file mode 100644
index 0000000..01df7e7
--- /dev/null
+++ b/src/pkg/syscall/ztypes_openbsd_amd64.go
@@ -0,0 +1,390 @@
+// godefs -gsyscall -f-m64 types_openbsd.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+	sizeofPtr              = 0x8
+	sizeofShort            = 0x2
+	sizeofInt              = 0x4
+	sizeofLong             = 0x8
+	sizeofLongLong         = 0x8
+	S_IFMT                 = 0xf000
+	S_IFIFO                = 0x1000
+	S_IFCHR                = 0x2000
+	S_IFDIR                = 0x4000
+	S_IFBLK                = 0x6000
+	S_IFREG                = 0x8000
+	S_IFLNK                = 0xa000
+	S_IFSOCK               = 0xc000
+	S_ISUID                = 0x800
+	S_ISGID                = 0x400
+	S_ISVTX                = 0x200
+	S_IRUSR                = 0x100
+	S_IWUSR                = 0x80
+	S_IXUSR                = 0x40
+	SizeofSockaddrInet4    = 0x10
+	SizeofSockaddrInet6    = 0x1c
+	SizeofSockaddrAny      = 0x6c
+	SizeofSockaddrUnix     = 0x6a
+	SizeofSockaddrDatalink = 0x20
+	SizeofLinger           = 0x8
+	SizeofIPMreq           = 0x8
+	SizeofIPv6Mreq         = 0x14
+	SizeofMsghdr           = 0x30
+	SizeofCmsghdr          = 0xc
+	SizeofInet6Pktinfo     = 0x14
+	PTRACE_TRACEME         = 0
+	PTRACE_CONT            = 0x7
+	PTRACE_KILL            = 0x8
+	SizeofIfMsghdr         = 0xf0
+	SizeofIfData           = 0xd8
+	SizeofIfaMsghdr        = 0x18
+	SizeofRtMsghdr         = 0x58
+	SizeofRtMetrics        = 0x30
+	SizeofBpfVersion       = 0x4
+	SizeofBpfStat          = 0x8
+	SizeofBpfProgram       = 0x10
+	SizeofBpfInsn          = 0x8
+	SizeofBpfHdr           = 0x14
+)
+
+// Types
+
+type _C_short int16
+
+type _C_int int32
+
+type _C_long int64
+
+type _C_long_long int64
+
+type Timespec struct {
+	Sec          int32
+	Pad_godefs_0 [4]byte
+	Nsec         int64
+}
+
+type Timeval struct {
+	Sec  int64
+	Usec int64
+}
+
+type Rusage struct {
+	Utime    Timeval
+	Stime    Timeval
+	Maxrss   int64
+	Ixrss    int64
+	Idrss    int64
+	Isrss    int64
+	Minflt   int64
+	Majflt   int64
+	Nswap    int64
+	Inblock  int64
+	Oublock  int64
+	Msgsnd   int64
+	Msgrcv   int64
+	Nsignals int64
+	Nvcsw    int64
+	Nivcsw   int64
+}
+
+type Rlimit struct {
+	Cur uint64
+	Max uint64
+}
+
+type _Gid_t uint32
+
+type Stat_t struct {
+	Dev            int32
+	Ino            uint32
+	Mode           uint32
+	Nlink          uint32
+	Uid            uint32
+	Gid            uint32
+	Rdev           int32
+	Lspare0        int32
+	Atim           Timespec
+	Mtim           Timespec
+	Ctim           Timespec
+	Size           int64
+	Blocks         int64
+	Blksize        uint32
+	Flags          uint32
+	Gen            uint32
+	Lspare1        int32
+	X__st_birthtim Timespec
+	Qspare         [2]int64
+}
+
+type Statfs_t struct {
+	F_flags       uint32
+	F_bsize       uint32
+	F_iosize      uint32
+	Pad_godefs_0  [4]byte
+	F_blocks      uint64
+	F_bfree       uint64
+	F_bavail      int64
+	F_files       uint64
+	F_ffree       uint64
+	F_favail      int64
+	F_syncwrites  uint64
+	F_syncreads   uint64
+	F_asyncwrites uint64
+	F_asyncreads  uint64
+	F_fsid        [8]byte /* fsid_t */
+	F_namemax     uint32
+	F_owner       uint32
+	F_ctime       uint32
+	F_spare       [3]uint32
+	F_fstypename  [16]int8
+	F_mntonname   [90]int8
+	F_mntfromname [90]int8
+	Pad_godefs_1  [4]byte
+	Mount_info    [160]byte /* mount_info */
+}
+
+type Flock_t struct {
+	Start  int64
+	Len    int64
+	Pid    int32
+	Type   int16
+	Whence int16
+}
+
+type Dirent struct {
+	Fileno uint32
+	Reclen uint16
+	Type   uint8
+	Namlen uint8
+	Name   [256]int8
+}
+
+type RawSockaddrInet4 struct {
+	Len    uint8
+	Family uint8
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]int8
+}
+
+type RawSockaddrInet6 struct {
+	Len      uint8
+	Family   uint8
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte /* in6_addr */
+	Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+	Len    uint8
+	Family uint8
+	Path   [104]int8
+}
+
+type RawSockaddrDatalink struct {
+	Len    uint8
+	Family uint8
+	Index  uint16
+	Type   uint8
+	Nlen   uint8
+	Alen   uint8
+	Slen   uint8
+	Data   [24]int8
+}
+
+type RawSockaddr struct {
+	Len    uint8
+	Family uint8
+	Data   [14]int8
+}
+
+type RawSockaddrAny struct {
+	Addr RawSockaddr
+	Pad  [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+	Onoff  int32
+	Linger int32
+}
+
+type Iovec struct {
+	Base *byte
+	Len  uint64
+}
+
+type IPMreq struct {
+	Multiaddr [4]byte /* in_addr */
+	Interface [4]byte /* in_addr */
+}
+
+type IPv6Mreq struct {
+	Multiaddr [16]byte /* in6_addr */
+	Interface uint32
+}
+
+type Msghdr struct {
+	Name         *byte
+	Namelen      uint32
+	Pad_godefs_0 [4]byte
+	Iov          *Iovec
+	Iovlen       uint32
+	Pad_godefs_1 [4]byte
+	Control      *byte
+	Controllen   uint32
+	Flags        int32
+}
+
+type Cmsghdr struct {
+	Len   uint32
+	Level int32
+	Type  int32
+}
+
+type Inet6Pktinfo struct {
+	Addr    [16]byte /* in6_addr */
+	Ifindex uint32
+}
+
+type Kevent_t struct {
+	Ident  uint32
+	Filter int16
+	Flags  uint16
+	Fflags uint32
+	Data   int32
+	Udata  *byte
+}
+
+type FdSet struct {
+	Bits [32]int32
+}
+
+type IfMsghdr struct {
+	Msglen  uint16
+	Version uint8
+	Type    uint8
+	Hdrlen  uint16
+	Index   uint16
+	Tableid uint16
+	Pad1    uint8
+	Pad2    uint8
+	Addrs   int32
+	Flags   int32
+	Xflags  int32
+	Data    IfData
+}
+
+type IfData struct {
+	Type         uint8
+	Addrlen      uint8
+	Hdrlen       uint8
+	Link_state   uint8
+	Mtu          uint32
+	Metric       uint32
+	Pad          uint32
+	Baudrate     uint64
+	Ipackets     uint64
+	Ierrors      uint64
+	Opackets     uint64
+	Oerrors      uint64
+	Collisions   uint64
+	Ibytes       uint64
+	Obytes       uint64
+	Imcasts      uint64
+	Omcasts      uint64
+	Iqdrops      uint64
+	Noproto      uint64
+	Lastchange   Timeval
+	Mclpool      [7][12]byte /* mclpool */
+	Pad_godefs_0 [4]byte
+}
+
+type IfaMsghdr struct {
+	Msglen  uint16
+	Version uint8
+	Type    uint8
+	Hdrlen  uint16
+	Index   uint16
+	Tableid uint16
+	Pad1    uint8
+	Pad2    uint8
+	Addrs   int32
+	Flags   int32
+	Metric  int32
+}
+
+type RtMsghdr struct {
+	Msglen   uint16
+	Version  uint8
+	Type     uint8
+	Hdrlen   uint16
+	Index    uint16
+	Tableid  uint16
+	Priority uint8
+	Mpls     uint8
+	Addrs    int32
+	Flags    int32
+	Fmask    int32
+	Pid      int32
+	Seq      int32
+	Errno    int32
+	Inits    uint32
+	Rmx      RtMetrics
+}
+
+type RtMetrics struct {
+	Pksent   uint64
+	Locks    uint32
+	Mtu      uint32
+	Expire   uint32
+	Refcnt   uint32
+	Hopcount uint32
+	Recvpipe uint32
+	Sendpipe uint32
+	Ssthresh uint32
+	Rtt      uint32
+	Rttvar   uint32
+}
+
+type BpfVersion struct {
+	Major uint16
+	Minor uint16
+}
+
+type BpfStat struct {
+	Recv uint32
+	Drop uint32
+}
+
+type BpfProgram struct {
+	Len          uint32
+	Pad_godefs_0 [4]byte
+	Insns        *BpfInsn
+}
+
+type BpfInsn struct {
+	Code uint16
+	Jt   uint8
+	Jf   uint8
+	K    uint32
+}
+
+type BpfHdr struct {
+	Tstamp       BpfTimeval
+	Caplen       uint32
+	Datalen      uint32
+	Hdrlen       uint16
+	Pad_godefs_0 [2]byte
+}
+
+type BpfTimeval struct {
+	Sec  uint32
+	Usec uint32
+}
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index 07f2b85..5b8c33a 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -244,6 +244,20 @@ type ByHandleFileInformation struct {
 	FileIndexLow       uint32
 }
 
+const (
+	GetFileExInfoStandard = 0
+	GetFileExMaxInfoLevel = 1
+)
+
+type Win32FileAttributeData struct {
+	FileAttributes uint32
+	CreationTime   Filetime
+	LastAccessTime Filetime
+	LastWriteTime  Filetime
+	FileSizeHigh   uint32
+	FileSizeLow    uint32
+}
+
 // ShowWindow constants
 const (
 	// winuser.h
@@ -291,12 +305,6 @@ type ProcessInformation struct {
 	ThreadId  uint32
 }
 
-// Invented values to support what package os expects.
-type Stat_t struct {
-	Windata Win32finddata
-	Mode    uint32
-}
-
 type Systemtime struct {
 	Year         uint16
 	Month        uint16
@@ -349,7 +357,7 @@ const (
 	IPPROTO_IPV6 = 0x29
 	IPV6_V6ONLY  = 0x1b
 
-	SOMAXCONN = 5
+	SOMAXCONN = 0x7fffffff
 
 	TCP_NODELAY = 1
 
@@ -359,6 +367,9 @@ const (
 
 	WSADESCRIPTION_LEN = 256
 	WSASYS_STATUS_LEN  = 128
+
+	IPV6_JOIN_GROUP  = 12
+	IPV6_LEAVE_GROUP = 13
 )
 
 type WSABuf struct {
diff --git a/src/pkg/template/doc.go b/src/pkg/template/doc.go
index f65946a..a52f32d 100644
--- a/src/pkg/template/doc.go
+++ b/src/pkg/template/doc.go
@@ -294,7 +294,7 @@ This defines two templates, T1 and T2, and a third T3 that invokes the other two
 when it is executed.
 
 The second way to build a template set is to use Set's Add method to add a
-parsed template to a set.  A template may be bound at most one set.  If it's
+parsed template to a set.  A template may be bound to at most one set.  If it's
 necessary to have a template in multiple sets, the template definition must be
 parsed multiple times to create distinct *Template values.
 
diff --git a/src/pkg/template/exec.go b/src/pkg/template/exec.go
index eaa57ae..e7fad72 100644
--- a/src/pkg/template/exec.go
+++ b/src/pkg/template/exec.go
@@ -196,23 +196,25 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
 	val, _ := indirect(s.evalPipeline(dot, r.Pipe))
 	// mark top of stack before any variables in the body are pushed.
 	mark := s.mark()
+	oneIteration := func(index, elem reflect.Value) {
+		// Set top var (lexically the second if there are two) to the element.
+		if len(r.Pipe.Decl) > 0 {
+			s.setVar(1, elem)
+		}
+		// Set next var (lexically the first if there are two) to the index.
+		if len(r.Pipe.Decl) > 1 {
+			s.setVar(2, index)
+		}
+		s.walk(elem, r.List)
+		s.pop(mark)
+	}
 	switch val.Kind() {
 	case reflect.Array, reflect.Slice:
 		if val.Len() == 0 {
 			break
 		}
 		for i := 0; i < val.Len(); i++ {
-			elem := val.Index(i)
-			// Set top var (lexically the second if there are two) to the element.
-			if len(r.Pipe.Decl) > 0 {
-				s.setVar(1, elem)
-			}
-			// Set next var (lexically the first if there are two) to the index.
-			if len(r.Pipe.Decl) > 1 {
-				s.setVar(2, reflect.ValueOf(i))
-			}
-			s.walk(elem, r.List)
-			s.pop(mark)
+			oneIteration(reflect.ValueOf(i), val.Index(i))
 		}
 		return
 	case reflect.Map:
@@ -220,17 +222,23 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
 			break
 		}
 		for _, key := range val.MapKeys() {
-			elem := val.MapIndex(key)
-			// Set top var (lexically the second if there are two) to the element.
-			if len(r.Pipe.Decl) > 0 {
-				s.setVar(1, elem)
-			}
-			// Set next var (lexically the first if there are two) to the key.
-			if len(r.Pipe.Decl) > 1 {
-				s.setVar(2, key)
+			oneIteration(key, val.MapIndex(key))
+		}
+		return
+	case reflect.Chan:
+		if val.IsNil() {
+			break
+		}
+		i := 0
+		for ; ; i++ {
+			elem, ok := val.Recv()
+			if !ok {
+				break
 			}
-			s.walk(elem, r.List)
-			s.pop(mark)
+			oneIteration(reflect.ValueOf(i), elem)
+		}
+		if i == 0 {
+			break
 		}
 		return
 	case reflect.Invalid:
@@ -498,7 +506,18 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
 		s.errorf("invalid value; expected %s", typ)
 	}
 	if !value.Type().AssignableTo(typ) {
-		s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+		// Does one dereference or indirection work? We could do more, as we
+		// do with method receivers, but that gets messy and method receivers
+		// are much more constrained, so it makes more sense there than here.
+		// Besides, one is almost always all you need.
+		switch {
+		case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
+			value = value.Elem()
+		case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
+			value = value.Addr()
+		default:
+			s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+		}
 	}
 	return value
 }
diff --git a/src/pkg/template/exec_test.go b/src/pkg/template/exec_test.go
index 82f56e1..8e1894e 100644
--- a/src/pkg/template/exec_test.go
+++ b/src/pkg/template/exec_test.go
@@ -391,6 +391,8 @@ var execTests = []execTest{
 	{"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true},
 	{"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
 	{"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
+	{"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
+	{"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
 
 	// Cute examples.
 	{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
@@ -414,6 +416,11 @@ var execTests = []execTest{
 	{"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true},
 	// Stringer.
 	{"bug5", "{{.Str}}", "foozle", tVal, true},
+	// Args need to be indirected and dereferenced sometimes.
+	{"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true},
+	{"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true},
+	{"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true},
+	{"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true},
 }
 
 func zeroArgs() string {
@@ -424,9 +431,35 @@ func oneArg(a string) string {
 	return "oneArg=" + a
 }
 
+// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
+func count(n int) chan string {
+	if n == 0 {
+		return nil
+	}
+	c := make(chan string)
+	go func() {
+		for i := 0; i < n; i++ {
+			c <- "abcdefghijklmnop"[i : i+1]
+		}
+		close(c)
+	}()
+	return c
+}
+
+// vfunc takes a *V and a V
+func vfunc(V, *V) string {
+	return "vfunc"
+}
+
 func testExecute(execTests []execTest, set *Set, t *testing.T) {
 	b := new(bytes.Buffer)
-	funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf}
+	funcs := FuncMap{
+		"count":    count,
+		"oneArg":   oneArg,
+		"typeOf":   typeOf,
+		"vfunc":    vfunc,
+		"zeroArgs": zeroArgs,
+	}
 	for _, test := range execTests {
 		tmpl := New(test.name).Funcs(funcs)
 		_, err := tmpl.ParseInSet(test.input, set)
diff --git a/src/pkg/template/parse/lex.go b/src/pkg/template/parse/lex.go
index 7ec4e92..83ad6c6 100644
--- a/src/pkg/template/parse/lex.go
+++ b/src/pkg/template/parse/lex.go
@@ -278,53 +278,51 @@ func lexInsideAction(l *lexer) stateFn {
 	// Either number, quoted string, or identifier.
 	// Spaces separate and are ignored.
 	// Pipe symbols separate and are emitted.
-	for {
-		if strings.HasPrefix(l.input[l.pos:], rightDelim) {
-			return lexRightDelim
+	if strings.HasPrefix(l.input[l.pos:], rightDelim) {
+		return lexRightDelim
+	}
+	switch r := l.next(); {
+	case r == eof || r == '\n':
+		return l.errorf("unclosed action")
+	case isSpace(r):
+		l.ignore()
+	case r == ':':
+		if l.next() != '=' {
+			return l.errorf("expected :=")
 		}
-		switch r := l.next(); {
-		case r == eof || r == '\n':
-			return l.errorf("unclosed action")
-		case isSpace(r):
-			l.ignore()
-		case r == ':':
-			if l.next() != '=' {
-				return l.errorf("expected :=")
+		l.emit(itemColonEquals)
+	case r == '|':
+		l.emit(itemPipe)
+	case r == '"':
+		return lexQuote
+	case r == '`':
+		return lexRawQuote
+	case r == '$':
+		return lexIdentifier
+	case r == '\'':
+		return lexChar
+	case r == '.':
+		// special look-ahead for ".field" so we don't break l.backup().
+		if l.pos < len(l.input) {
+			r := l.input[l.pos]
+			if r < '0' || '9' < r {
+				return lexIdentifier // itemDot comes from the keyword table.
 			}
-			l.emit(itemColonEquals)
-		case r == '|':
-			l.emit(itemPipe)
-		case r == '"':
-			return lexQuote
-		case r == '`':
-			return lexRawQuote
-		case r == '$':
-			return lexIdentifier
-		case r == '\'':
-			return lexChar
-		case r == '.':
-			// special look-ahead for ".field" so we don't break l.backup().
-			if l.pos < len(l.input) {
-				r := l.input[l.pos]
-				if r < '0' || '9' < r {
-					return lexIdentifier // itemDot comes from the keyword table.
-				}
-			}
-			fallthrough // '.' can start a number.
-		case r == '+' || r == '-' || ('0' <= r && r <= '9'):
-			l.backup()
-			return lexNumber
-		case isAlphaNumeric(r):
-			l.backup()
-			return lexIdentifier
-		case r <= unicode.MaxASCII && unicode.IsPrint(r):
-			l.emit(itemChar)
-			return lexInsideAction
-		default:
-			return l.errorf("unrecognized character in action: %#U", r)
 		}
+		fallthrough // '.' can start a number.
+	case r == '+' || r == '-' || ('0' <= r && r <= '9'):
+		l.backup()
+		return lexNumber
+	case isAlphaNumeric(r):
+		l.backup()
+		return lexIdentifier
+	case r <= unicode.MaxASCII && unicode.IsPrint(r):
+		l.emit(itemChar)
+		return lexInsideAction
+	default:
+		return l.errorf("unrecognized character in action: %#U", r)
 	}
-	return nil
+	return lexInsideAction
 }
 
 // lexIdentifier scans an alphanumeric or field.
diff --git a/src/pkg/template/parse/lex_test.go b/src/pkg/template/parse/lex_test.go
index 2ad91d5..d71c8e6 100644
--- a/src/pkg/template/parse/lex_test.go
+++ b/src/pkg/template/parse/lex_test.go
@@ -184,6 +184,20 @@ var lexTests = []lexTest{
 		tLeft,
 		{itemError, `bad number syntax: "3k"`},
 	}},
+
+	// Fixed bugs
+	// Many elements in an action blew the lookahead until
+	// we made lexInsideAction not loop.
+	{"long pipeline deadlock", "{{|||||}}", []item{
+		tLeft,
+		tPipe,
+		tPipe,
+		tPipe,
+		tPipe,
+		tPipe,
+		tRight,
+		tEOF,
+	}},
 }
 
 // collect gathers the emitted items into a slice.
diff --git a/src/pkg/template/parse/node.go b/src/pkg/template/parse/node.go
index a917418..7411327 100644
--- a/src/pkg/template/parse/node.go
+++ b/src/pkg/template/parse/node.go
@@ -35,8 +35,8 @@ const (
 	NodeBool                       // A boolean constant.
 	NodeCommand                    // An element of a pipeline.
 	NodeDot                        // The cursor, dot.
-	NodeElse                       // An else action.
-	NodeEnd                        // An end action.
+	nodeElse                       // An else action. Not added to tree.
+	nodeEnd                        // An end action. Not added to tree.
 	NodeField                      // A field or method name.
 	NodeIdentifier                 // An identifier; always a function name.
 	NodeIf                         // An if action.
@@ -356,41 +356,42 @@ func (s *StringNode) String() string {
 	return fmt.Sprintf("S=%#q", s.Text)
 }
 
-// EndNode represents an {{end}} action. It is represented by a nil pointer.
-type EndNode bool
+// endNode represents an {{end}} action. It is represented by a nil pointer.
+// It does not appear in the final parse tree.
+type endNode bool
 
-func newEnd() *EndNode {
+func newEnd() *endNode {
 	return nil
 }
 
-func (e *EndNode) Type() NodeType {
-	return NodeEnd
+func (e *endNode) Type() NodeType {
+	return nodeEnd
 }
 
-func (e *EndNode) String() string {
+func (e *endNode) String() string {
 	return "{{end}}"
 }
 
-// ElseNode represents an {{else}} action.
-type ElseNode struct {
+// elseNode represents an {{else}} action. Does not appear in the final tree.
+type elseNode struct {
 	NodeType
 	Line int // The line number in the input.
 }
 
-func newElse(line int) *ElseNode {
-	return &ElseNode{NodeType: NodeElse, Line: line}
+func newElse(line int) *elseNode {
+	return &elseNode{NodeType: nodeElse, Line: line}
 }
 
-func (e *ElseNode) Type() NodeType {
-	return NodeElse
+func (e *elseNode) Type() NodeType {
+	return nodeElse
 }
 
-func (e *ElseNode) String() string {
+func (e *elseNode) String() string {
 	return "{{else}}"
 }
 
-// IfNode represents an {{if}} action and its commands.
-type IfNode struct {
+// BranchNode is the common representation of if, range, and with.
+type BranchNode struct {
 	NodeType
 	Line     int       // The line number in the input.
 	Pipe     *PipeNode // The pipeline to be evaluated.
@@ -398,35 +399,49 @@ type IfNode struct {
 	ElseList *ListNode // What to execute if the value is empty (nil if absent).
 }
 
-func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
-	return &IfNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}
+func (b *BranchNode) String() string {
+	name := ""
+	switch b.NodeType {
+	case NodeIf:
+		name = "if"
+	case NodeRange:
+		name = "range"
+	case NodeWith:
+		name = "with"
+	default:
+		panic("unknown branch type")
+	}
+	if b.ElseList != nil {
+		return fmt.Sprintf("({{%s %s}} %s {{else}} %s)", name, b.Pipe, b.List, b.ElseList)
+	}
+	return fmt.Sprintf("({{%s %s}} %s)", name, b.Pipe, b.List)
 }
 
-func (i *IfNode) String() string {
-	if i.ElseList != nil {
-		return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.Pipe, i.List, i.ElseList)
-	}
-	return fmt.Sprintf("({{if %s}} %s)", i.Pipe, i.List)
+// IfNode represents an {{if}} action and its commands.
+type IfNode struct {
+	BranchNode
+}
+
+func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
+	return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
 }
 
 // RangeNode represents a {{range}} action and its commands.
 type RangeNode struct {
-	NodeType
-	Line     int       // The line number in the input.
-	Pipe     *PipeNode // The pipeline to be evaluated.
-	List     *ListNode // What to execute if the value is non-empty.
-	ElseList *ListNode // What to execute if the value is empty (nil if absent).
+	BranchNode
 }
 
 func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
-	return &RangeNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}
+	return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
 }
 
-func (r *RangeNode) String() string {
-	if r.ElseList != nil {
-		return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.Pipe, r.List, r.ElseList)
-	}
-	return fmt.Sprintf("({{range %s}} %s)", r.Pipe, r.List)
+// WithNode represents a {{with}} action and its commands.
+type WithNode struct {
+	BranchNode
+}
+
+func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
+	return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
 }
 
 // TemplateNode represents a {{template}} action.
@@ -447,23 +462,3 @@ func (t *TemplateNode) String() string {
 	}
 	return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
 }
-
-// WithNode represents a {{with}} action and its commands.
-type WithNode struct {
-	NodeType
-	Line     int       // The line number in the input.
-	Pipe     *PipeNode // The pipeline to be evaluated.
-	List     *ListNode // What to execute if the value is non-empty.
-	ElseList *ListNode // What to execute if the value is empty (nil if absent).
-}
-
-func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
-	return &WithNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}
-}
-
-func (w *WithNode) String() string {
-	if w.ElseList != nil {
-		return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.Pipe, w.List, w.ElseList)
-	}
-	return fmt.Sprintf("({{with %s}} %s)", w.Pipe, w.List)
-}
diff --git a/src/pkg/template/parse/parse.go b/src/pkg/template/parse/parse.go
index 9a411a3..6918074 100644
--- a/src/pkg/template/parse/parse.go
+++ b/src/pkg/template/parse/parse.go
@@ -173,7 +173,7 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
 	for t.peek().typ != itemEOF {
 		n := t.textOrAction()
 		switch n.Type() {
-		case NodeEnd, NodeElse:
+		case nodeEnd, nodeElse:
 			return list, n
 		}
 		list.append(n)
@@ -278,10 +278,10 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list,
 	var next Node
 	list, next = t.itemList(false)
 	switch next.Type() {
-	case NodeEnd: //done
-	case NodeElse:
+	case nodeEnd: //done
+	case nodeElse:
 		elseList, next = t.itemList(false)
-		if next.Type() != NodeEnd {
+		if next.Type() != nodeEnd {
 			t.errorf("expected end; found %s", next)
 		}
 		elseList = elseList
diff --git a/src/pkg/template/parse/set.go b/src/pkg/template/parse/set.go
index 4820da9..dca41ea 100644
--- a/src/pkg/template/parse/set.go
+++ b/src/pkg/template/parse/set.go
@@ -37,7 +37,7 @@ func Set(text string, funcs ...map[string]interface{}) (tree map[string]*Tree, e
 		if end == nil {
 			t.errorf("unexpected EOF in %s", context)
 		}
-		if end.Type() != NodeEnd {
+		if end.Type() != nodeEnd {
 			t.errorf("unexpected %s in %s", end, context)
 		}
 		t.stopParse()
diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile
index a6fce3f..473e7ea 100644
--- a/src/pkg/time/Makefile
+++ b/src/pkg/time/Makefile
@@ -13,27 +13,27 @@ GOFILES=\
 	time.go\
 
 GOFILES_freebsd=\
-	sys_posix.go\
+	sys_unix.go\
 	zoneinfo_posix.go\
 	zoneinfo_unix.go\
 
 GOFILES_darwin=\
-	sys_posix.go\
+	sys_unix.go\
 	zoneinfo_posix.go\
 	zoneinfo_unix.go\
 
 GOFILES_linux=\
-	sys_posix.go\
+	sys_unix.go\
 	zoneinfo_posix.go\
 	zoneinfo_unix.go\
 
 GOFILES_openbsd=\
-	sys_posix.go\
+	sys_unix.go\
 	zoneinfo_posix.go\
 	zoneinfo_unix.go\
 
 GOFILES_windows=\
-	sys_posix.go\
+	sys_windows.go\
 	zoneinfo_windows.go\
 
 GOFILES_plan9=\
diff --git a/src/pkg/time/internal_test.go b/src/pkg/time/internal_test.go
new file mode 100644
index 0000000..d7e7076
--- /dev/null
+++ b/src/pkg/time/internal_test.go
@@ -0,0 +1,12 @@
+// 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 time
+
+func init() {
+	// force US/Pacific for time zone tests
+	onceSetupZone.Do(setupTestingZone)
+}
+
+var Interrupt = interrupt
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index a4a1a42..b6b88f6 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -7,7 +7,6 @@ package time_test
 import (
 	"fmt"
 	"os"
-	"syscall"
 	"testing"
 	"sort"
 	. "time"
@@ -17,7 +16,7 @@ func TestSleep(t *testing.T) {
 	const delay = int64(100e6)
 	go func() {
 		Sleep(delay / 2)
-		syscall.Kill(os.Getpid(), syscall.SIGCHLD)
+		Interrupt()
 	}()
 	start := Nanoseconds()
 	Sleep(delay)
diff --git a/src/pkg/time/sys_plan9.go b/src/pkg/time/sys_plan9.go
index abe8649..9ae0161 100644
--- a/src/pkg/time/sys_plan9.go
+++ b/src/pkg/time/sys_plan9.go
@@ -16,3 +16,8 @@ func sysSleep(t int64) os.Error {
 	}
 	return nil
 }
+
+// for testing: whatever interrupts a sleep
+func interrupt() {
+	// cannot predict pid, don't want to kill group
+}
diff --git a/src/pkg/time/sys_posix.go b/src/pkg/time/sys_posix.go
deleted file mode 100644
index 0d1eb72..0000000
--- a/src/pkg/time/sys_posix.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 time
-
-import (
-	"os"
-	"syscall"
-)
-
-func sysSleep(t int64) os.Error {
-	errno := syscall.Sleep(t)
-	if errno != 0 && errno != syscall.EINTR {
-		return os.NewSyscallError("sleep", errno)
-	}
-	return nil
-}
diff --git a/src/pkg/time/sys_unix.go b/src/pkg/time/sys_unix.go
new file mode 100644
index 0000000..0f9128e
--- /dev/null
+++ b/src/pkg/time/sys_unix.go
@@ -0,0 +1,23 @@
+// 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 time
+
+import (
+	"os"
+	"syscall"
+)
+
+func sysSleep(t int64) os.Error {
+	errno := syscall.Sleep(t)
+	if errno != 0 && errno != syscall.EINTR {
+		return os.NewSyscallError("sleep", errno)
+	}
+	return nil
+}
+
+// for testing: whatever interrupts a sleep
+func interrupt() {
+	syscall.Kill(os.Getpid(), syscall.SIGCHLD)
+}
diff --git a/src/pkg/time/sys_windows.go b/src/pkg/time/sys_windows.go
new file mode 100644
index 0000000..feff90b
--- /dev/null
+++ b/src/pkg/time/sys_windows.go
@@ -0,0 +1,22 @@
+// 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 time
+
+import (
+	"os"
+	"syscall"
+)
+
+func sysSleep(t int64) os.Error {
+	errno := syscall.Sleep(t)
+	if errno != 0 && errno != syscall.EINTR {
+		return os.NewSyscallError("sleep", errno)
+	}
+	return nil
+}
+
+// for testing: whatever interrupts a sleep
+func interrupt() {
+}
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index dceed49..07d7598 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -5,7 +5,6 @@
 package time_test
 
 import (
-	"os"
 	"strconv"
 	"strings"
 	"testing"
@@ -13,13 +12,6 @@ import (
 	. "time"
 )
 
-func init() {
-	// Force US Pacific time for daylight-savings
-	// tests below (localtests).  Needs to be set
-	// before the first call into the time library.
-	os.Setenv("TZ", "America/Los_Angeles")
-}
-
 // We should be in PST/PDT, but if the time zone files are missing we
 // won't be. The purpose of this test is to at least explain why some of
 // the subsequent tests fail.
diff --git a/src/pkg/time/zoneinfo_plan9.go b/src/pkg/time/zoneinfo_plan9.go
index 3c3e7c4..577ef85 100644
--- a/src/pkg/time/zoneinfo_plan9.go
+++ b/src/pkg/time/zoneinfo_plan9.go
@@ -57,3 +57,19 @@ func setupZone() {
 	}
 	zones = parseZones(t)
 }
+
+func setupTestingZone() {
+	f, err := os.Open("/adm/timezone/US_Pacific")
+	if err != nil {
+		return
+	}
+	defer f.Close()
+	l, _ := f.Seek(0, 2)
+	f.Seek(0, 0)
+	buf := make([]byte, l)
+	_, err = f.Read(buf)
+	if err != nil {
+		return
+	}
+	zones = parseZones(string(buf))
+}
diff --git a/src/pkg/time/zoneinfo_unix.go b/src/pkg/time/zoneinfo_unix.go
index f3ea7b6..ce4d9f1 100644
--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.go
@@ -185,6 +185,11 @@ func readinfofile(name string) ([]zonetime, bool) {
 	return parseinfo(buf)
 }
 
+func setupTestingZone() {
+	os.Setenv("TZ", "America/Los_Angeles")
+	setupZone()
+}
+
 func setupZone() {
 	// consult $TZ to find the time zone to use.
 	// no $TZ means use the system default /etc/localtime.
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go
index fabc006..ab3e7df 100644
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -27,9 +27,30 @@ type zone struct {
 	prev                  *zone
 }
 
+// BUG(rsc): On Windows, time zone abbreviations are unavailable.
+// This package constructs them using the capital letters from a longer
+// time zone description.
+
 // Populate zone struct with Windows supplied information. Returns true, if data is valid.
 func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) {
-	z.name = syscall.UTF16ToString(name)
+	// name is 'Pacific Standard Time' but we want 'PST'.
+	// Extract just capital letters.  It's not perfect but the
+	// information we need is not available from the kernel.
+	// Because time zone abbreviations are not unique,
+	// Windows refuses to expose them.
+	//
+	// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
+	// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
+	short := make([]uint16, len(name))
+	w := 0
+	for _, c := range name {
+		if 'A' <= c && c <= 'Z' {
+			short[w] = c
+			w++
+		}
+	}
+	z.name = syscall.UTF16ToString(short[:w])
+
 	z.offset = int(bias)
 	z.year = int64(d.Year)
 	z.month = int(d.Month)
@@ -129,6 +150,10 @@ func setupZone() {
 		initError = os.NewSyscallError("GetTimeZoneInformation", e)
 		return
 	}
+	setupZoneFromTZI(&i)
+}
+
+func setupZoneFromTZI(i *syscall.Timezoneinformation) {
 	if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) {
 		tz.disabled = true
 		tz.offsetIfDisabled = tz.std.offset
@@ -144,6 +169,23 @@ func setupZone() {
 	tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year)
 }
 
+var usPacific = syscall.Timezoneinformation{
+	Bias: 8 * 60,
+	StandardName: [32]uint16{
+		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
+	},
+	StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
+	DaylightName: [32]uint16{
+		'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
+	},
+	DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
+	DaylightBias: -60,
+}
+
+func setupTestingZone() {
+	setupZoneFromTZI(&usPacific)
+}
+
 // Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
 func lookupTimezone(sec int64) (zone string, offset int) {
 	onceSetupZone.Do(setupZone)
diff --git a/src/pkg/unsafe/unsafe.go b/src/pkg/unsafe/unsafe.go
index a125706..c49f54d 100644
--- a/src/pkg/unsafe/unsafe.go
+++ b/src/pkg/unsafe/unsafe.go
@@ -24,17 +24,17 @@ type Pointer *ArbitraryType
 // Sizeof returns the size in bytes occupied by the value v.  The size is that of the
 // "top level" of the value only.  For instance, if v is a slice, it returns the size of
 // the slice descriptor, not the size of the memory referenced by the slice.
-func Sizeof(v ArbitraryType) int
+func Sizeof(v ArbitraryType) uintptr
 
 // Offsetof returns the offset within the struct of the field represented by v,
 // which must be of the form struct_value.field.  In other words, it returns the
 // number of bytes between the start of the struct and the start of the field.
-func Offsetof(v ArbitraryType) int
+func Offsetof(v ArbitraryType) uintptr
 
 // Alignof returns the alignment of the value v.  It is the maximum value m such
 // that the address of a variable with the type of v will always always be zero mod m.
 // If v is of the form obj.f, it returns the alignment of field f within struct object obj.
-func Alignof(v ArbitraryType) int
+func Alignof(v ArbitraryType) uintptr
 
 // Typeof returns the type of an interface value, a runtime.Type.
 func Typeof(i interface{}) (typ interface{})
diff --git a/src/pkg/url/url.go b/src/pkg/url/url.go
index d07b016..9d19348 100644
--- a/src/pkg/url/url.go
+++ b/src/pkg/url/url.go
@@ -532,20 +532,28 @@ func ParseQuery(query string) (m Values, err os.Error) {
 }
 
 func parseQuery(m Values, query string) (err os.Error) {
-	for _, kv := range strings.Split(query, "&") {
-		if len(kv) == 0 {
+	for query != "" {
+		key := query
+		if i := strings.IndexAny(key, "&;"); i >= 0 {
+			key, query = key[:i], key[i+1:]
+		} else {
+			query = ""
+		}
+		if key == "" {
 			continue
 		}
-		kvPair := strings.SplitN(kv, "=", 2)
-
-		var key, value string
-		var e os.Error
-		key, e = QueryUnescape(kvPair[0])
-		if e == nil && len(kvPair) > 1 {
-			value, e = QueryUnescape(kvPair[1])
+		value := ""
+		if i := strings.Index(key, "="); i >= 0 {
+			key, value = key[:i], key[i+1:]
+		}
+		key, err1 := QueryUnescape(key)
+		if err1 != nil {
+			err = err1
+			continue
 		}
-		if e != nil {
-			err = e
+		value, err1 = QueryUnescape(value)
+		if err1 != nil {
+			err = err1
 			continue
 		}
 		m[key] = append(m[key], value)
diff --git a/src/pkg/url/url_test.go b/src/pkg/url/url_test.go
index af394d4..8c27e18 100644
--- a/src/pkg/url/url_test.go
+++ b/src/pkg/url/url_test.go
@@ -11,11 +11,6 @@ import (
 	"testing"
 )
 
-// TODO(rsc):
-//	test Unescape
-//	test Escape
-//	test Parse
-
 type URLTest struct {
 	in        string
 	out       *URL
@@ -696,3 +691,60 @@ func TestQueryValues(t *testing.T) {
 		t.Errorf("second Get(bar) = %q, want %q", g, e)
 	}
 }
+
+type parseTest struct {
+	query string
+	out   Values
+}
+
+var parseTests = []parseTest{
+	{
+		query: "a=1&b=2",
+		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
+	},
+	{
+		query: "a=1&a=2&a=banana",
+		out:   Values{"a": []string{"1", "2", "banana"}},
+	},
+	{
+		query: "ascii=%3Ckey%3A+0x90%3E",
+		out:   Values{"ascii": []string{"<key: 0x90>"}},
+	},
+	{
+		query: "a=1;b=2",
+		out:   Values{"a": []string{"1"}, "b": []string{"2"}},
+	},
+	{
+		query: "a=1&a=2;a=banana",
+		out:   Values{"a": []string{"1", "2", "banana"}},
+	},
+}
+
+func TestParseQuery(t *testing.T) {
+	for i, test := range parseTests {
+		form, err := ParseQuery(test.query)
+		if err != nil {
+			t.Errorf("test %d: Unexpected error: %v", i, err)
+			continue
+		}
+		if len(form) != len(test.out) {
+			t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out))
+		}
+		for k, evs := range test.out {
+			vs, ok := form[k]
+			if !ok {
+				t.Errorf("test %d: Missing key %q", i, k)
+				continue
+			}
+			if len(vs) != len(evs) {
+				t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs))
+				continue
+			}
+			for j, ev := range evs {
+				if v := vs[j]; v != ev {
+					t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev)
+				}
+			}
+		}
+	}
+}
diff --git a/src/pkg/websocket/Makefile b/src/pkg/websocket/Makefile
index 6d3c9cb..25131de 100644
--- a/src/pkg/websocket/Makefile
+++ b/src/pkg/websocket/Makefile
@@ -5,5 +5,7 @@ GOFILES=\
 	client.go\
 	server.go\
 	websocket.go\
+	hixie.go\
+	hybi.go\
 
 include ../../Make.pkg
diff --git a/src/pkg/websocket/client.go b/src/pkg/websocket/client.go
index 74bede4..7497ac6 100644
--- a/src/pkg/websocket/client.go
+++ b/src/pkg/websocket/client.go
@@ -6,114 +6,119 @@ package websocket
 
 import (
 	"bufio"
-	"bytes"
 	"crypto/tls"
-	"fmt"
-	"http"
 	"io"
 	"net"
 	"os"
-	"rand"
-	"strings"
 	"url"
 )
 
-type ProtocolError struct {
-	ErrorString string
-}
-
-func (err *ProtocolError) String() string { return string(err.ErrorString) }
-
-var (
-	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"}
-	ErrChallengeResponse    = &ProtocolError{"mismatch challenge/response"}
-	secKeyRandomChars       [0x30 - 0x21 + 0x7F - 0x3A]byte
-)
-
+// DialError is an error that occurs while dialling a websocket server.
 type DialError struct {
-	URL      string
-	Protocol string
-	Origin   string
-	Error    os.Error
+	*Config
+	Error os.Error
 }
 
 func (e *DialError) String() string {
-	return "websocket.Dial " + e.URL + ": " + e.Error.String()
+	return "websocket.Dial " + e.Config.Location.String() + ": " + e.Error.String()
 }
 
-func init() {
-	i := 0
-	for ch := byte(0x21); ch < 0x30; ch++ {
-		secKeyRandomChars[i] = ch
-		i++
+// NewConfig creates a new WebSocket config for client connection.
+func NewConfig(server, origin string) (config *Config, err os.Error) {
+	config = new(Config)
+	config.Version = ProtocolVersionHybi
+	config.Location, err = url.ParseRequest(server)
+	if err != nil {
+		return
 	}
-	for ch := byte(0x3a); ch < 0x7F; ch++ {
-		secKeyRandomChars[i] = ch
-		i++
+	config.Origin, err = url.ParseRequest(origin)
+	if err != nil {
+		return
 	}
+	return
 }
 
-type handshaker func(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) os.Error
-
-// newClient creates a new Web Socket client connection.
-func newClient(resourceName, host, origin, location, protocol string, rwc io.ReadWriteCloser, handshake handshaker) (ws *Conn, err os.Error) {
+// NewClient creates a new WebSocket client connection over rwc.
+func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err os.Error) {
 	br := bufio.NewReader(rwc)
 	bw := bufio.NewWriter(rwc)
-	err = handshake(resourceName, host, origin, location, protocol, br, bw)
+	switch config.Version {
+	case ProtocolVersionHixie75:
+		err = hixie75ClientHandshake(config, br, bw)
+	case ProtocolVersionHixie76, ProtocolVersionHybi00:
+		err = hixie76ClientHandshake(config, br, bw)
+	case ProtocolVersionHybi:
+		err = hybiClientHandshake(config, br, bw)
+	default:
+		err = ErrBadProtocolVersion
+	}
 	if err != nil {
 		return
 	}
 	buf := bufio.NewReadWriter(br, bw)
-	ws = newConn(origin, location, protocol, buf, rwc)
+	switch config.Version {
+	case ProtocolVersionHixie75, ProtocolVersionHixie76, ProtocolVersionHybi00:
+		ws = newHixieClientConn(config, buf, rwc)
+	case ProtocolVersionHybi:
+		ws = newHybiClientConn(config, buf, rwc)
+	}
 	return
 }
 
 /*
-Dial opens a new client connection to a Web Socket.
+Dial opens a new client connection to a WebSocket.
 
 A trivial example client:
 
 	package main
 
 	import (
-		"websocket"
+		"http"
+		"log"
 		"strings"
+		"websocket"
 	)
 
 	func main() {
-	 	ws, err := websocket.Dial("ws://localhost/ws", "", "http://localhost/");
-	 	if err != nil {
-			panic("Dial: " + err.String())
+		origin := "http://localhost/"
+		url := "ws://localhost/ws" 
+		ws, err := websocket.Dial(url, "", origin)
+		if err != nil {
+			log.Fatal(err)
 		}
 		if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
-			panic("Write: " + err.String())
+			log.Fatal(err)
 		}
 		var msg = make([]byte, 512);
 		if n, err := ws.Read(msg); err != nil {
-			panic("Read: " + err.String())
+			log.Fatal(err)
 		}
 		// use msg[0:n]
 	}
 */
 func Dial(url_, protocol, origin string) (ws *Conn, err os.Error) {
-	var client net.Conn
-
-	parsedUrl, err := url.Parse(url_)
+	config, err := NewConfig(url_, origin)
 	if err != nil {
-		goto Error
+		return nil, err
 	}
+	return DialConfig(config)
+}
 
-	switch parsedUrl.Scheme {
+// DialConfig opens a new client connection to a WebSocket with a config.
+func DialConfig(config *Config) (ws *Conn, err os.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", parsedUrl.Host)
+		client, err = net.Dial("tcp", config.Location.Host)
 
 	case "wss":
-		client, err = tls.Dial("tcp", parsedUrl.Host, nil)
+		client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig)
 
 	default:
 		err = ErrBadScheme
@@ -122,202 +127,12 @@ func Dial(url_, protocol, origin string) (ws *Conn, err os.Error) {
 		goto Error
 	}
 
-	ws, err = newClient(parsedUrl.RawPath, parsedUrl.Host, origin, url_, protocol, client, handshake)
+	ws, err = NewClient(config, client)
 	if err != nil {
 		goto Error
 	}
 	return
 
 Error:
-	return nil, &DialError{url_, protocol, origin, err}
-}
-
-/*
-Generates handshake key as described in 4.1 Opening handshake step 16 to 22.
-cf. http://www.whatwg.org/specs/web-socket-protocol/
-*/
-func generateKeyNumber() (key string, number uint32) {
-	// 16.  Let /spaces_n/ be a random integer from 1 to 12 inclusive.
-	spaces := rand.Intn(12) + 1
-
-	// 17. Let /max_n/ be the largest integer not greater than
-	//     4,294,967,295 divided by /spaces_n/
-	max := int(4294967295 / uint32(spaces))
-
-	// 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive.
-	number = uint32(rand.Intn(max + 1))
-
-	// 19. Let /product_n/ be the result of multiplying /number_n/ and
-	//     /spaces_n/ together.
-	product := number * uint32(spaces)
-
-	// 20. Let /key_n/ be a string consisting of /product_n/, expressed
-	// in base ten using the numerals in the range U+0030 DIGIT ZERO (0)
-	// to U+0039 DIGIT NINE (9).
-	key = fmt.Sprintf("%d", product)
-
-	// 21. Insert between one and twelve random characters from the ranges
-	//     U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
-	//     positions.
-	n := rand.Intn(12) + 1
-	for i := 0; i < n; i++ {
-		pos := rand.Intn(len(key)) + 1
-		ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))]
-		key = key[0:pos] + string(ch) + key[pos:]
-	}
-
-	// 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random
-	//     positions other than the start or end of the string.
-	for i := 0; i < spaces; i++ {
-		pos := rand.Intn(len(key)-1) + 1
-		key = key[0:pos] + " " + key[pos:]
-	}
-
-	return
-}
-
-/*
-Generates handshake key_3 as described in 4.1 Opening handshake step 26.
-cf. http://www.whatwg.org/specs/web-socket-protocol/
-*/
-func generateKey3() (key []byte) {
-	// 26. Let /key3/ be a string consisting of eight random bytes (or
-	//  equivalently, a random 64 bit integer encoded in big-endian order).
-	key = make([]byte, 8)
-	for i := 0; i < 8; i++ {
-		key[i] = byte(rand.Intn(256))
-	}
-	return
-}
-
-/*
-Web Socket protocol handshake based on
-http://www.whatwg.org/specs/web-socket-protocol/
-(draft of http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol)
-*/
-func handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
-	// 4.1. Opening handshake.
-	// Step 5.  send a request line.
-	bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n")
-
-	// Step 6-14. push request headers in fields.
-	var fields []string
-	fields = append(fields, "Upgrade: WebSocket\r\n")
-	fields = append(fields, "Connection: Upgrade\r\n")
-	fields = append(fields, "Host: "+host+"\r\n")
-	fields = append(fields, "Origin: "+origin+"\r\n")
-	if protocol != "" {
-		fields = append(fields, "Sec-WebSocket-Protocol: "+protocol+"\r\n")
-	}
-	// TODO(ukai): Step 15. send cookie if any.
-
-	// Step 16-23. generate keys and push Sec-WebSocket-Key<n> in fields.
-	key1, number1 := generateKeyNumber()
-	key2, number2 := generateKeyNumber()
-	fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n")
-	fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n")
-
-	// Step 24. shuffle fields and send them out.
-	for i := 1; i < len(fields); i++ {
-		j := rand.Intn(i)
-		fields[i], fields[j] = fields[j], fields[i]
-	}
-	for i := 0; i < len(fields); i++ {
-		bw.WriteString(fields[i])
-	}
-	// Step 25. send CRLF.
-	bw.WriteString("\r\n")
-
-	// Step 26. generate 8 bytes random key.
-	key3 := generateKey3()
-	// Step 27. send it out.
-	bw.Write(key3)
-	if err = bw.Flush(); err != nil {
-		return
-	}
-
-	// Step 28-29, 32-40. read response from server.
-	resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
-	if err != nil {
-		return err
-	}
-	// Step 30. check response code is 101.
-	if resp.StatusCode != 101 {
-		return ErrBadStatus
-	}
-
-	// Step 41. check websocket headers.
-	if resp.Header.Get("Upgrade") != "WebSocket" ||
-		strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
-		return ErrBadUpgrade
-	}
-
-	if resp.Header.Get("Sec-Websocket-Origin") != origin {
-		return ErrBadWebSocketOrigin
-	}
-
-	if resp.Header.Get("Sec-Websocket-Location") != location {
-		return ErrBadWebSocketLocation
-	}
-
-	if protocol != "" && resp.Header.Get("Sec-Websocket-Protocol") != protocol {
-		return ErrBadWebSocketProtocol
-	}
-
-	// Step 42-43. get expected data from challenge data.
-	expected, err := getChallengeResponse(number1, number2, key3)
-	if err != nil {
-		return err
-	}
-
-	// Step 44. read 16 bytes from server.
-	reply := make([]byte, 16)
-	if _, err = io.ReadFull(br, reply); err != nil {
-		return err
-	}
-
-	// Step 45. check the reply equals to expected data.
-	if !bytes.Equal(expected, reply) {
-		return ErrChallengeResponse
-	}
-	// WebSocket connection is established.
-	return
-}
-
-/*
-Handshake described in (soon obsolete)
-draft-hixie-thewebsocket-protocol-75.
-*/
-func draft75handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
-	bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n")
-	bw.WriteString("Upgrade: WebSocket\r\n")
-	bw.WriteString("Connection: Upgrade\r\n")
-	bw.WriteString("Host: " + host + "\r\n")
-	bw.WriteString("Origin: " + origin + "\r\n")
-	if protocol != "" {
-		bw.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
-	}
-	bw.WriteString("\r\n")
-	bw.Flush()
-	resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
-	if err != nil {
-		return
-	}
-	if resp.Status != "101 Web Socket Protocol Handshake" {
-		return ErrBadStatus
-	}
-	if resp.Header.Get("Upgrade") != "WebSocket" ||
-		resp.Header.Get("Connection") != "Upgrade" {
-		return ErrBadUpgrade
-	}
-	if resp.Header.Get("Websocket-Origin") != origin {
-		return ErrBadWebSocketOrigin
-	}
-	if resp.Header.Get("Websocket-Location") != location {
-		return ErrBadWebSocketLocation
-	}
-	if protocol != "" && resp.Header.Get("Websocket-Protocol") != protocol {
-		return ErrBadWebSocketProtocol
-	}
-	return
+	return nil, &DialError{config, err}
 }
diff --git a/src/pkg/websocket/hixie.go b/src/pkg/websocket/hixie.go
new file mode 100644
index 0000000..b755a5c
--- /dev/null
+++ b/src/pkg/websocket/hixie.go
@@ -0,0 +1,694 @@
+// 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
+
+// This file implements a protocol of Hixie draft version 75 and 76
+// (draft 76 equals to hybi 00)
+
+import (
+	"bufio"
+	"bytes"
+	"container/vector"
+	"crypto/md5"
+	"encoding/binary"
+	"fmt"
+	"http"
+	"io"
+	"io/ioutil"
+	"os"
+	"rand"
+	"strconv"
+	"strings"
+	"url"
+)
+
+// An aray of characters to be randomly inserted to construct Sec-WebSocket-Key
+// value. It holds characters from ranges U+0021 to U+002F and U+003A to U+007E.
+// See Step 21 in Section 4.1 Opening handshake.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#page-22
+var secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte
+
+func init() {
+	i := 0
+	for ch := byte(0x21); ch < 0x30; ch++ {
+		secKeyRandomChars[i] = ch
+		i++
+	}
+	for ch := byte(0x3a); ch < 0x7F; ch++ {
+		secKeyRandomChars[i] = ch
+		i++
+	}
+}
+
+type byteReader interface {
+	ReadByte() (byte, os.Error)
+}
+
+// readHixieLength reads frame length for frame type 0x80-0xFF
+// as defined in Hixie draft.
+// See section 4.2 Data framing.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-4.2
+func readHixieLength(r byteReader) (length int64, lengthFields []byte, err os.Error) {
+	for {
+		c, err := r.ReadByte()
+		if err != nil {
+			return 0, nil, err
+		}
+		lengthFields = append(lengthFields, c)
+		length = length*128 + int64(c&0x7f)
+		if c&0x80 == 0 {
+			break
+		}
+	}
+	return
+}
+
+// A hixieLengthFrameReader is a reader for frame type 0x80-0xFF
+// as defined in hixie draft.
+type hixieLengthFrameReader struct {
+	reader    io.Reader
+	FrameType byte
+	Length    int64
+	header    *bytes.Buffer
+	length    int
+}
+
+func (frame *hixieLengthFrameReader) Read(msg []byte) (n int, err os.Error) {
+	return frame.reader.Read(msg)
+}
+
+func (frame *hixieLengthFrameReader) PayloadType() byte {
+	if frame.FrameType == '\xff' && frame.Length == 0 {
+		return CloseFrame
+	}
+	return UnknownFrame
+}
+
+func (frame *hixieLengthFrameReader) HeaderReader() io.Reader {
+	if frame.header == nil {
+		return nil
+	}
+	if frame.header.Len() == 0 {
+		frame.header = nil
+		return nil
+	}
+	return frame.header
+}
+
+func (frame *hixieLengthFrameReader) TrailerReader() io.Reader { return nil }
+
+func (frame *hixieLengthFrameReader) Len() (n int) { return frame.length }
+
+// A HixieSentinelFrameReader is a reader for frame type 0x00-0x7F
+// as defined in hixie draft.
+type hixieSentinelFrameReader struct {
+	reader      *bufio.Reader
+	FrameType   byte
+	header      *bytes.Buffer
+	data        []byte
+	seenTrailer bool
+	trailer     *bytes.Buffer
+}
+
+func (frame *hixieSentinelFrameReader) Read(msg []byte) (n int, err os.Error) {
+	if len(frame.data) == 0 {
+		if frame.seenTrailer {
+			return 0, os.EOF
+		}
+		frame.data, err = frame.reader.ReadSlice('\xff')
+		if err == nil {
+			frame.seenTrailer = true
+			frame.data = frame.data[:len(frame.data)-1] // trim \xff
+			frame.trailer = bytes.NewBuffer([]byte{0xff})
+		}
+	}
+	n = copy(msg, frame.data)
+	frame.data = frame.data[n:]
+	return n, err
+}
+
+func (frame *hixieSentinelFrameReader) PayloadType() byte {
+	if frame.FrameType == 0 {
+		return TextFrame
+	}
+	return UnknownFrame
+}
+
+func (frame *hixieSentinelFrameReader) HeaderReader() io.Reader {
+	if frame.header == nil {
+		return nil
+	}
+	if frame.header.Len() == 0 {
+		frame.header = nil
+		return nil
+	}
+	return frame.header
+}
+
+func (frame *hixieSentinelFrameReader) TrailerReader() io.Reader {
+	if frame.trailer == nil {
+		return nil
+	}
+	if frame.trailer.Len() == 0 {
+		frame.trailer = nil
+		return nil
+	}
+	return frame.trailer
+}
+
+func (frame *hixieSentinelFrameReader) Len() int { return -1 }
+
+// A HixieFrameReaderFactory creates new frame reader based on its frame type.
+type hixieFrameReaderFactory struct {
+	*bufio.Reader
+}
+
+func (buf hixieFrameReaderFactory) NewFrameReader() (r frameReader, err os.Error) {
+	var header []byte
+	var b byte
+	b, err = buf.ReadByte()
+	if err != nil {
+		return
+	}
+	header = append(header, b)
+	if b&0x80 == 0x80 {
+		length, lengthFields, err := readHixieLength(buf.Reader)
+		if err != nil {
+			return nil, err
+		}
+		if length == 0 {
+			return nil, os.EOF
+		}
+		header = append(header, lengthFields...)
+		return &hixieLengthFrameReader{
+			reader:    io.LimitReader(buf.Reader, length),
+			FrameType: b,
+			Length:    length,
+			header:    bytes.NewBuffer(header)}, err
+	}
+	return &hixieSentinelFrameReader{
+		reader:    buf.Reader,
+		FrameType: b,
+		header:    bytes.NewBuffer(header)}, err
+}
+
+type hixiFrameWriter struct {
+	writer *bufio.Writer
+}
+
+func (frame *hixiFrameWriter) Write(msg []byte) (n int, err os.Error) {
+	frame.writer.WriteByte(0)
+	frame.writer.Write(msg)
+	frame.writer.WriteByte(0xff)
+	err = frame.writer.Flush()
+	return len(msg), err
+}
+
+func (frame *hixiFrameWriter) Close() os.Error { return nil }
+
+type hixiFrameWriterFactory struct {
+	*bufio.Writer
+}
+
+func (buf hixiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err os.Error) {
+	if payloadType != TextFrame {
+		return nil, ErrNotSupported
+	}
+	return &hixiFrameWriter{writer: buf.Writer}, nil
+}
+
+type hixiFrameHandler struct {
+	conn *Conn
+}
+
+func (handler *hixiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err os.Error) {
+	if header := frame.HeaderReader(); header != nil {
+		io.Copy(ioutil.Discard, header)
+	}
+	if frame.PayloadType() != TextFrame {
+		io.Copy(ioutil.Discard, frame)
+		return nil, nil
+	}
+	return frame, nil
+}
+
+func (handler *hixiFrameHandler) WriteClose(_ int) (err os.Error) {
+	closingFrame := []byte{'\xff', '\x00'}
+	handler.conn.buf.Write(closingFrame)
+	return handler.conn.buf.Flush()
+}
+
+// newHixiConn creates a new WebSocket connection speaking hixie draft protocol.
+func newHixieConn(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: hixieFrameReaderFactory{buf.Reader},
+		frameWriterFactory: hixiFrameWriterFactory{buf.Writer},
+		PayloadType:        TextFrame}
+	ws.frameHandler = &hixiFrameHandler{ws}
+	return ws
+}
+
+// getChallengeResponse computes the expected response from the
+// challenge as described in section 5.1 Opening Handshake steps 42 to
+// 43 of http://www.whatwg.org/specs/web-socket-protocol/
+func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err os.Error) {
+	// 41. Let /challenge/ be the concatenation of /number_1/, expressed
+	// a big-endian 32 bit integer, /number_2/, expressed in a big-
+	// endian 32 bit integer, and the eight bytes of /key_3/ in the
+	// order they were sent to the wire.
+	challenge := make([]byte, 16)
+	binary.BigEndian.PutUint32(challenge[0:], number1)
+	binary.BigEndian.PutUint32(challenge[4:], number2)
+	copy(challenge[8:], key3)
+
+	// 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big-
+	// endian 128 bit string.
+	h := md5.New()
+	if _, err = h.Write(challenge); err != nil {
+		return
+	}
+	expected = h.Sum()
+	return
+}
+
+// Generates handshake key as described in 4.1 Opening handshake step 16 to 22.
+// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+func generateKeyNumber() (key string, number uint32) {
+	// 16.  Let /spaces_n/ be a random integer from 1 to 12 inclusive.
+	spaces := rand.Intn(12) + 1
+
+	// 17. Let /max_n/ be the largest integer not greater than
+	//     4,294,967,295 divided by /spaces_n/
+	max := int(4294967295 / uint32(spaces))
+
+	// 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive.
+	number = uint32(rand.Intn(max + 1))
+
+	// 19. Let /product_n/ be the result of multiplying /number_n/ and
+	//     /spaces_n/ together.
+	product := number * uint32(spaces)
+
+	// 20. Let /key_n/ be a string consisting of /product_n/, expressed
+	// in base ten using the numerals in the range U+0030 DIGIT ZERO (0)
+	// to U+0039 DIGIT NINE (9).
+	key = fmt.Sprintf("%d", product)
+
+	// 21. Insert between one and twelve random characters from the ranges
+	//     U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
+	//     positions.
+	n := rand.Intn(12) + 1
+	for i := 0; i < n; i++ {
+		pos := rand.Intn(len(key)) + 1
+		ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))]
+		key = key[0:pos] + string(ch) + key[pos:]
+	}
+
+	// 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random
+	//     positions other than the start or end of the string.
+	for i := 0; i < spaces; i++ {
+		pos := rand.Intn(len(key)-1) + 1
+		key = key[0:pos] + " " + key[pos:]
+	}
+
+	return
+}
+
+// Generates handshake key_3 as described in 4.1 Opening handshake step 26.
+// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+func generateKey3() (key []byte) {
+	// 26. Let /key3/ be a string consisting of eight random bytes (or
+	//  equivalently, a random 64 bit integer encoded in big-endian order).
+	key = make([]byte, 8)
+	for i := 0; i < 8; i++ {
+		key[i] = byte(rand.Intn(256))
+	}
+	return
+}
+
+// Cilent handhake described in (soon obsolete)
+// draft-ietf-hybi-thewebsocket-protocol-00
+// (draft-hixie-thewebsocket-protocol-76) 
+func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
+	switch config.Version {
+	case ProtocolVersionHixie76, ProtocolVersionHybi00:
+	default:
+		panic("wrong protocol version.")
+	}
+	// 4.1. Opening handshake.
+	// Step 5.  send a request line.
+	bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
+
+	// Step 6-14. push request headers in fields.
+	var fields vector.StringVector
+	fields.Push("Upgrade: WebSocket\r\n")
+	fields.Push("Connection: Upgrade\r\n")
+	fields.Push("Host: " + config.Location.Host + "\r\n")
+	fields.Push("Origin: " + config.Origin.String() + "\r\n")
+	if len(config.Protocol) > 0 {
+		if len(config.Protocol) != 1 {
+			return ErrBadWebSocketProtocol
+		}
+		fields.Push("Sec-WebSocket-Protocol: " + config.Protocol[0] + "\r\n")
+	}
+	// TODO(ukai): Step 15. send cookie if any.
+
+	// Step 16-23. generate keys and push Sec-WebSocket-Key<n> in fields.
+	key1, number1 := generateKeyNumber()
+	key2, number2 := generateKeyNumber()
+	if config.handshakeData != nil {
+		key1 = config.handshakeData["key1"]
+		n, err := strconv.Atoui(config.handshakeData["number1"])
+		if err != nil {
+			panic(err)
+		}
+		number1 = uint32(n)
+		key2 = config.handshakeData["key2"]
+		n, err = strconv.Atoui(config.handshakeData["number2"])
+		if err != nil {
+			panic(err)
+		}
+		number2 = uint32(n)
+	}
+	fields.Push("Sec-WebSocket-Key1: " + key1 + "\r\n")
+	fields.Push("Sec-WebSocket-Key2: " + key2 + "\r\n")
+
+	// Step 24. shuffle fields and send them out.
+	for i := 1; i < len(fields); i++ {
+		j := rand.Intn(i)
+		fields[i], fields[j] = fields[j], fields[i]
+	}
+	for i := 0; i < len(fields); i++ {
+		bw.WriteString(fields[i])
+	}
+	// Step 25. send CRLF.
+	bw.WriteString("\r\n")
+
+	// Step 26. generate 8 bytes random key.
+	key3 := generateKey3()
+	if config.handshakeData != nil {
+		key3 = []byte(config.handshakeData["key3"])
+	}
+	// Step 27. send it out.
+	bw.Write(key3)
+	if err = bw.Flush(); err != nil {
+		return
+	}
+
+	// Step 28-29, 32-40. read response from server.
+	resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
+	if err != nil {
+		return err
+	}
+	// Step 30. check response code is 101.
+	if resp.StatusCode != 101 {
+		return ErrBadStatus
+	}
+
+	// Step 41. check websocket headers.
+	if resp.Header.Get("Upgrade") != "WebSocket" ||
+		strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
+		return ErrBadUpgrade
+	}
+
+	if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() {
+		return ErrBadWebSocketOrigin
+	}
+
+	if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() {
+		return ErrBadWebSocketLocation
+	}
+
+	if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] {
+		return ErrBadWebSocketProtocol
+	}
+
+	// Step 42-43. get expected data from challenge data.
+	expected, err := getChallengeResponse(number1, number2, key3)
+	if err != nil {
+		return err
+	}
+
+	// Step 44. read 16 bytes from server.
+	reply := make([]byte, 16)
+	if _, err = io.ReadFull(br, reply); err != nil {
+		return err
+	}
+
+	// Step 45. check the reply equals to expected data.
+	if !bytes.Equal(expected, reply) {
+		return ErrChallengeResponse
+	}
+	// WebSocket connection is established.
+	return
+}
+
+// Client Handshake described in (soon obsolete)
+// draft-hixie-thewebsocket-protocol-75.
+func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
+	if config.Version != ProtocolVersionHixie75 {
+		panic("wrong protocol version.")
+	}
+	bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
+	bw.WriteString("Upgrade: WebSocket\r\n")
+	bw.WriteString("Connection: Upgrade\r\n")
+	bw.WriteString("Host: " + config.Location.Host + "\r\n")
+	bw.WriteString("Origin: " + config.Origin.String() + "\r\n")
+	if len(config.Protocol) > 0 {
+		if len(config.Protocol) != 1 {
+			return ErrBadWebSocketProtocol
+		}
+		bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n")
+	}
+	bw.WriteString("\r\n")
+	bw.Flush()
+	resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
+	if err != nil {
+		return
+	}
+	if resp.Status != "101 Web Socket Protocol Handshake" {
+		return ErrBadStatus
+	}
+	if resp.Header.Get("Upgrade") != "WebSocket" ||
+		resp.Header.Get("Connection") != "Upgrade" {
+		return ErrBadUpgrade
+	}
+	if resp.Header.Get("Websocket-Origin") != config.Origin.String() {
+		return ErrBadWebSocketOrigin
+	}
+	if resp.Header.Get("Websocket-Location") != config.Location.String() {
+		return ErrBadWebSocketLocation
+	}
+	if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] {
+		return ErrBadWebSocketProtocol
+	}
+	return
+}
+
+// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol.
+func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
+	return newHixieConn(config, buf, rwc, nil)
+}
+
+// Gets key number from Sec-WebSocket-Key<n>: field as described
+// in 5.2 Sending the server's opening handshake, 4.
+func getKeyNumber(s string) (r uint32) {
+	// 4. Let /key-number_n/ be the digits (characters in the range
+	// U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/,
+	// interpreted as a base ten integer, ignoring all other characters
+	// in /key_n/.
+	r = 0
+	for i := 0; i < len(s); i++ {
+		if s[i] >= '0' && s[i] <= '9' {
+			r = r*10 + uint32(s[i]) - '0'
+		}
+	}
+	return
+}
+
+// A Hixie76ServerHandshaker performs a server handshake using
+// hixie draft 76 protocol.
+type hixie76ServerHandshaker struct {
+	*Config
+	challengeResponse []byte
+}
+
+func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) {
+	c.Version = ProtocolVersionHybi00
+	if req.Method != "GET" {
+		return http.StatusMethodNotAllowed, ErrBadRequestMethod
+	}
+	// HTTP version can be safely ignored.
+
+	if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
+		strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
+		return http.StatusBadRequest, ErrNotWebSocket
+	}
+
+	// TODO(ukai): check Host
+	c.Origin, err = url.ParseRequest(req.Header.Get("Origin"))
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+
+	key1 := req.Header.Get("Sec-Websocket-Key1")
+	if key1 == "" {
+		return http.StatusBadRequest, ErrChallengeResponse
+	}
+	key2 := req.Header.Get("Sec-Websocket-Key2")
+	if key2 == "" {
+		return http.StatusBadRequest, ErrChallengeResponse
+	}
+	key3 := make([]byte, 8)
+	if _, err := io.ReadFull(buf, key3); err != nil {
+		return http.StatusBadRequest, ErrChallengeResponse
+	}
+
+	var scheme string
+	if req.TLS != nil {
+		scheme = "wss"
+	} else {
+		scheme = "ws"
+	}
+	c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+
+	// Step 4. get key number in Sec-WebSocket-Key<n> fields.
+	keyNumber1 := getKeyNumber(key1)
+	keyNumber2 := getKeyNumber(key2)
+
+	// Step 5. get number of spaces in Sec-WebSocket-Key<n> fields.
+	space1 := uint32(strings.Count(key1, " "))
+	space2 := uint32(strings.Count(key2, " "))
+	if space1 == 0 || space2 == 0 {
+		return http.StatusBadRequest, ErrChallengeResponse
+	}
+
+	// Step 6. key number must be an integral multiple of spaces.
+	if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 {
+		return http.StatusBadRequest, ErrChallengeResponse
+	}
+
+	// Step 7. let part be key number divided by spaces.
+	part1 := keyNumber1 / space1
+	part2 := keyNumber2 / space2
+
+	// Step 8. let challenge be concatenation of part1, part2 and key3.
+	// Step 9. get MD5 fingerprint of challenge.
+	c.challengeResponse, err = getChallengeResponse(part1, part2, key3)
+	if err != nil {
+		return http.StatusInternalServerError, err
+	}
+	protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
+	protocols := strings.Split(protocol, ",")
+	for i := 0; i < len(protocols); i++ {
+		c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
+	}
+
+	return http.StatusSwitchingProtocols, nil
+}
+
+func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err os.Error) {
+	if len(c.Protocol) > 0 {
+		if len(c.Protocol) != 1 {
+			return ErrBadWebSocketProtocol
+		}
+	}
+
+	// Step 10. send response status line.
+	buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n")
+	// Step 11. send response headers.
+	buf.WriteString("Upgrade: WebSocket\r\n")
+	buf.WriteString("Connection: Upgrade\r\n")
+	buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n")
+	buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n")
+	if len(c.Protocol) > 0 {
+		buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
+	}
+	// Step 12. send CRLF.
+	buf.WriteString("\r\n")
+	// Step 13. send response data.
+	buf.Write(c.challengeResponse)
+	return buf.Flush()
+}
+
+func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
+	return newHixieServerConn(c.Config, buf, rwc, request)
+}
+
+// A hixie75ServerHandshaker performs a server handshake using
+// hixie draft 75 protocol.
+type hixie75ServerHandshaker struct {
+	*Config
+}
+
+func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) {
+	c.Version = ProtocolVersionHixie75
+	if req.Method != "GET" || req.Proto != "HTTP/1.1" {
+		return http.StatusMethodNotAllowed, ErrBadRequestMethod
+	}
+	if req.Header.Get("Upgrade") != "WebSocket" {
+		return http.StatusBadRequest, ErrNotWebSocket
+	}
+	if req.Header.Get("Connection") != "Upgrade" {
+		return http.StatusBadRequest, ErrNotWebSocket
+	}
+	c.Origin, err = url.ParseRequest(strings.TrimSpace(req.Header.Get("Origin")))
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+
+	var scheme string
+	if req.TLS != nil {
+		scheme = "wss"
+	} else {
+		scheme = "ws"
+	}
+	c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+	protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol"))
+	protocols := strings.Split(protocol, ",")
+	for i := 0; i < len(protocols); i++ {
+		c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
+	}
+
+	return http.StatusSwitchingProtocols, nil
+}
+
+func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err os.Error) {
+	if len(c.Protocol) > 0 {
+		if len(c.Protocol) != 1 {
+			return ErrBadWebSocketProtocol
+		}
+	}
+
+	buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
+	buf.WriteString("Upgrade: WebSocket\r\n")
+	buf.WriteString("Connection: Upgrade\r\n")
+	buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n")
+	buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n")
+	if len(c.Protocol) > 0 {
+		buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
+	}
+	buf.WriteString("\r\n")
+	return buf.Flush()
+}
+
+func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
+	return newHixieServerConn(c.Config, buf, rwc, request)
+}
+
+// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol.
+func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+	return newHixieConn(config, buf, rwc, request)
+}
diff --git a/src/pkg/websocket/hixie_test.go b/src/pkg/websocket/hixie_test.go
new file mode 100644
index 0000000..a480b66
--- /dev/null
+++ b/src/pkg/websocket/hixie_test.go
@@ -0,0 +1,201 @@
+// 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"
+	"http"
+	"os"
+	"strings"
+	"testing"
+	"url"
+)
+
+// Test the getChallengeResponse function with values from section
+// 5.1 of the specification steps 18, 26, and 43 from
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+func TestHixie76Challenge(t *testing.T) {
+	var part1 uint32 = 777007543
+	var part2 uint32 = 114997259
+	key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58}
+	expected := []byte("0st3Rl&q-2ZU^weu")
+
+	response, err := getChallengeResponse(part1, part2, key3)
+	if err != nil {
+		t.Errorf("getChallengeResponse: returned error %v", err)
+		return
+	}
+	if !bytes.Equal(expected, response) {
+		t.Errorf("getChallengeResponse: expected %q got %q", expected, response)
+	}
+}
+
+func TestHixie76ClientHandshake(t *testing.T) {
+	b := bytes.NewBuffer([]byte{})
+	bw := bufio.NewWriter(b)
+	br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 WebSocket Protocol Handshake
+Upgrade: WebSocket
+Connection: Upgrade
+Sec-WebSocket-Origin: http://example.com
+Sec-WebSocket-Location: ws://example.com/demo
+Sec-WebSocket-Protocol: sample
+
+8jKS'y:G*Co,Wxa-`))
+
+	var err os.Error
+	config := new(Config)
+	config.Location, err = url.ParseRequest("ws://example.com/demo")
+	if err != nil {
+		t.Fatal("location url", err)
+	}
+	config.Origin, err = url.ParseRequest("http://example.com")
+	if err != nil {
+		t.Fatal("origin url", err)
+	}
+	config.Protocol = append(config.Protocol, "sample")
+	config.Version = ProtocolVersionHixie76
+
+	config.handshakeData = map[string]string{
+		"key1":    "4 @1  46546xW%0l 1 5",
+		"number1": "829309203",
+		"key2":    "12998 5 Y3 1  .P00",
+		"number2": "259970620",
+		"key3":    "^n:ds[4U",
+	}
+	err = hixie76ClientHandshake(config, br, bw)
+	if err != nil {
+		t.Errorf("handshake failed: %v", err)
+	}
+	req, err := http.ReadRequest(bufio.NewReader(b))
+	if err != nil {
+		t.Errorf("read request: %v", err)
+	}
+	if req.Method != "GET" {
+		t.Errorf("request method expected GET, but got %q", req.Method)
+	}
+	if req.RawURL != "/demo" {
+		t.Errorf("request path expected /demo, but got %q", req.RawURL)
+	}
+	if req.Proto != "HTTP/1.1" {
+		t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto)
+	}
+	if req.Host != "example.com" {
+		t.Errorf("request Host expected example.com, but got %v", req.Host)
+	}
+	var expectedHeader = map[string]string{
+		"Connection":             "Upgrade",
+		"Upgrade":                "WebSocket",
+		"Origin":                 "http://example.com",
+		"Sec-Websocket-Key1":     config.handshakeData["key1"],
+		"Sec-Websocket-Key2":     config.handshakeData["key2"],
+		"Sec-WebSocket-Protocol": config.Protocol[0],
+	}
+	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 TestHixie76ServerHandshake(t *testing.T) {
+	config := new(Config)
+	handshaker := &hixie76ServerHandshaker{Config: config}
+	br := bufio.NewReader(strings.NewReader(`GET /demo HTTP/1.1
+Host: example.com
+Connection: Upgrade
+Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
+Sec-WebSocket-Protocol: sample
+Upgrade: WebSocket
+Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
+Origin: http://example.com
+
+^n:ds[4U`))
+	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)
+
+	err = handshaker.AcceptHandshake(bw)
+	if err != nil {
+		t.Errorf("handshake response failed: %v", err)
+	}
+	expectedResponse := strings.Join([]string{
+		"HTTP/1.1 101 WebSocket Protocol Handshake",
+		"Upgrade: WebSocket",
+		"Connection: Upgrade",
+		"Sec-WebSocket-Origin: http://example.com",
+		"Sec-WebSocket-Location: ws://example.com/demo",
+		"Sec-WebSocket-Protocol: sample",
+		"", ""}, "\r\n") + "8jKS'y:G*Co,Wxa-"
+	if b.String() != expectedResponse {
+		t.Errorf("handshake expected %q but got %q", expectedResponse, b.String())
+	}
+}
+
+func TestHixie76SkipLengthFrame(t *testing.T) {
+	b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
+	buf := bytes.NewBuffer(b)
+	br := bufio.NewReader(buf)
+	bw := bufio.NewWriter(buf)
+	config := newConfig(t, "/")
+	ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil)
+	msg := make([]byte, 5)
+	n, err := ws.Read(msg)
+	if err != nil {
+		t.Errorf("Read: %v", err)
+	}
+	if !bytes.Equal(b[4:9], msg[0:n]) {
+		t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n])
+	}
+}
+
+func TestHixie76SkipNoUTF8Frame(t *testing.T) {
+	b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
+	buf := bytes.NewBuffer(b)
+	br := bufio.NewReader(buf)
+	bw := bufio.NewWriter(buf)
+	config := newConfig(t, "/")
+	ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil)
+	msg := make([]byte, 5)
+	n, err := ws.Read(msg)
+	if err != nil {
+		t.Errorf("Read: %v", err)
+	}
+	if !bytes.Equal(b[4:9], msg[0:n]) {
+		t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n])
+	}
+}
+
+func TestHixie76ClosingFrame(t *testing.T) {
+	b := []byte{0, 'h', 'e', 'l', 'l', 'o', '\xff'}
+	buf := bytes.NewBuffer(b)
+	br := bufio.NewReader(buf)
+	bw := bufio.NewWriter(buf)
+	config := newConfig(t, "/")
+	ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil)
+	msg := make([]byte, 5)
+	n, err := ws.Read(msg)
+	if err != nil {
+		t.Errorf("read: %v", err)
+	}
+	if !bytes.Equal(b[1:6], msg[0:n]) {
+		t.Errorf("Read: expected %q got %q", b[1:6], msg[0:n])
+	}
+	n, err = ws.Read(msg)
+	if err != os.EOF {
+		t.Errorf("read: %v", err)
+	}
+}
diff --git a/src/pkg/websocket/hybi.go b/src/pkg/websocket/hybi.go
new file mode 100644
index 0000000..cad4718
--- /dev/null
+++ b/src/pkg/websocket/hybi.go
@@ -0,0 +1,515 @@
+// 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-10
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/rand"
+	"crypto/sha1"
+	"encoding/base64"
+	"encoding/binary"
+	"fmt"
+	"http"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+	"url"
+)
+
+const (
+	websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+	closeStatusNormal          = 1000
+	closeStatusGoingAway       = 1001
+	closeStatusProtocolError   = 1002
+	closeStatusUnsupportedData = 1003
+	closeStatusFrameTooLarge   = 1004
+	closeStatusNoStatusRcvd    = 1005
+	closeStatusAbnormalClosure = 1006
+
+	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"}
+)
+
+// 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 os.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 4.2 Base Frameing protocol for detail.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4.2
+func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err os.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
+		}
+		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 os.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)
+		var data []byte
+
+		for i := 0; i < length; i++ {
+			data = append(data, 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() os.Error { return nil }
+
+type hybiFrameWriterFactory struct {
+	*bufio.Writer
+	needMaskingKey bool
+}
+
+func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err os.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) (r frameReader, err os.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, os.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, os.EOF
+	case PingFrame:
+		pingMsg := make([]byte, maxControlFramePayloadLength)
+		n, err := io.ReadFull(frame, pingMsg)
+		if err != nil && err != io.ErrUnexpectedEOF {
+			return nil, err
+		}
+		io.Copy(ioutil.Discard, frame)
+		n, err = handler.WritePong(pingMsg[:n])
+		if err != nil {
+			return nil, err
+		}
+		return nil, nil
+	case PongFrame:
+		return nil, ErrNotImplemented
+	}
+	return frame, nil
+}
+
+func (handler *hybiFrameHandler) WriteClose(status int) (err os.Error) {
+	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 os.Error) {
+	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 os.Error) {
+	maskingKey = make([]byte, 4)
+	if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
+		return
+	}
+	return
+}
+
+// genetateNonce geneates 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
+}
+
+// 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 os.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())
+	return
+}
+
+// Client handhake described in draft-ietf-hybi-thewebsocket-protocol-09
+func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
+	if config.Version != ProtocolVersionHybi {
+		panic("wrong protocol version.")
+	}
+
+	bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
+
+	bw.WriteString("Host: " + 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("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
+	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 extensions.
+	// TODO(ukai): send cookie if any.
+
+	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 os.Error) {
+	c.Version = ProtocolVersionHybi
+	if req.Method != "GET" {
+		return http.StatusMethodNotAllowed, ErrBadRequestMethod
+	}
+	// HTTP version can be safely ignored.
+
+	if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
+		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")
+	if version != fmt.Sprintf("%d", c.Version) {
+		return http.StatusBadRequest, ErrBadWebSocketVersion
+	}
+	c.Origin, err = url.ParseRequest(req.Header.Get("Sec-Websocket-Origin"))
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+	var scheme string
+	if req.TLS != nil {
+		scheme = "wss"
+	} else {
+		scheme = "ws"
+	}
+	c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
+	if err != nil {
+		return http.StatusBadRequest, err
+	}
+	protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-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
+}
+
+func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err os.Error) {
+	if len(c.Protocol) > 0 {
+		if len(c.Protocol) != 1 {
+			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): support extensions
+	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/src/pkg/websocket/hybi_test.go b/src/pkg/websocket/hybi_test.go
new file mode 100644
index 0000000..c437819
--- /dev/null
+++ b/src/pkg/websocket/hybi_test.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 websocket
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"http"
+	"os"
+	"strings"
+	"testing"
+	"url"
+)
+
+// Test the getNonceAccept function with values in
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-09
+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) {
+	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 os.Error
+	config := new(Config)
+	config.Location, err = url.ParseRequest("ws://server.example.com/chat")
+	if err != nil {
+		t.Fatal("location url", err)
+	}
+	config.Origin, err = url.ParseRequest("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 = ProtocolVersionHybi
+
+	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.Errorf("read request: %v", err)
+	}
+	if req.Method != "GET" {
+		t.Errorf("request method expected GET, but got %q", req.Method)
+	}
+	if req.RawURL != "/chat" {
+		t.Errorf("request path expected /demo, but got %q", req.RawURL)
+	}
+	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 example.com, but got %v", req.Host)
+	}
+	var expectedHeader = map[string]string{
+		"Connection":             "Upgrade",
+		"Upgrade":                "websocket",
+		"Sec-Websocket-Key":      config.handshakeData["key"],
+		"Sec-Websocket-Origin":   config.Origin.String(),
+		"Sec-Websocket-Protocol": "chat, superchat",
+		"Sec-Websocket-Version":  fmt.Sprintf("%d", ProtocolVersionHybi),
+	}
+	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==
+Sec-WebSocket-Origin: http://example.com
+Sec-WebSocket-Protocol: chat, superchat
+Sec-WebSocket-Version: 8
+
+`))
+	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())
+	}
+}
+
+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 {
+		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) {
+	frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame}
+	payload := []byte("hello")
+	testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader)
+
+	frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame}
+	testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, 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 TestHybiRead(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 TestHybiReadWithMasking(t *testing.T) {
+	wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20,
+		0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
+		0x89, 0x05, 'h', 'e', 'l', 'l', 'o',
+		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, nil)
+
+	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)
+	}
+}
diff --git a/src/pkg/websocket/server.go b/src/pkg/websocket/server.go
index e0e7c87..8f6a6a9 100644
--- a/src/pkg/websocket/server.go
+++ b/src/pkg/websocket/server.go
@@ -5,11 +5,41 @@
 package websocket
 
 import (
+	"bufio"
+	"fmt"
 	"http"
 	"io"
-	"strings"
+	"os"
 )
 
+func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request) (conn *Conn, err os.Error) {
+	config := new(Config)
+	var hs serverHandshaker = &hybiServerHandshaker{Config: config}
+	code, err := hs.ReadHandshake(buf.Reader, req)
+	if err != nil {
+		hs = &hixie76ServerHandshaker{Config: config}
+		code, err = hs.ReadHandshake(buf.Reader, req)
+	}
+	if err != nil {
+		hs = &hixie75ServerHandshaker{Config: config}
+		code, err = hs.ReadHandshake(buf.Reader, req)
+	}
+	if err != nil {
+		fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
+		buf.WriteString("\r\n")
+		buf.WriteString(err.String())
+		return
+	}
+	config.Protocol = nil
+
+	err = hs.AcceptHandshake(buf.Writer)
+	if err != nil {
+		return
+	}
+	conn = hs.NewServerConn(buf, rwc, req)
+	return
+}
+
 /*
 Handler is an interface to a WebSocket.
 
@@ -23,7 +53,7 @@ A trivial example server:
 		"websocket"
 	)
 
-	// Echo the data received on the Web Socket.
+	// Echo the data received on the WebSocket.
 	func EchoServer(ws *websocket.Conn) {
 		io.Copy(ws, ws);
 	}
@@ -38,26 +68,8 @@ A trivial example server:
 */
 type Handler func(*Conn)
 
-/*
-Gets key number from Sec-WebSocket-Key<n>: field as described
-in 5.2 Sending the server's opening handshake, 4.
-*/
-func getKeyNumber(s string) (r uint32) {
-	// 4. Let /key-number_n/ be the digits (characters in the range
-	// U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/,
-	// interpreted as a base ten integer, ignoring all other characters
-	// in /key_n/.
-	r = 0
-	for i := 0; i < len(s); i++ {
-		if s[i] >= '0' && s[i] <= '9' {
-			r = r*10 + uint32(s[i]) - '0'
-		}
-	}
-	return
-}
-
 // ServeHTTP implements the http.Handler interface for a Web Socket
-func (f Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	rwc, buf, err := w.(http.Hijacker).Hijack()
 	if err != nil {
 		panic("Hijack failed: " + err.String())
@@ -67,153 +79,12 @@ func (f Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	// the client did not send a handshake that matches with protocol
 	// specification.
 	defer rwc.Close()
-
-	if req.Method != "GET" {
-		return
-	}
-	// HTTP version can be safely ignored.
-
-	if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
-		strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
-		return
-	}
-
-	// TODO(ukai): check Host
-	origin := req.Header.Get("Origin")
-	if origin == "" {
-		return
-	}
-
-	key1 := req.Header.Get("Sec-Websocket-Key1")
-	if key1 == "" {
-		return
-	}
-	key2 := req.Header.Get("Sec-Websocket-Key2")
-	if key2 == "" {
-		return
-	}
-	key3 := make([]byte, 8)
-	if _, err := io.ReadFull(buf, key3); err != nil {
-		return
-	}
-
-	var location string
-	if req.TLS != nil {
-		location = "wss://" + req.Host + req.URL.RawPath
-	} else {
-		location = "ws://" + req.Host + req.URL.RawPath
-	}
-
-	// Step 4. get key number in Sec-WebSocket-Key<n> fields.
-	keyNumber1 := getKeyNumber(key1)
-	keyNumber2 := getKeyNumber(key2)
-
-	// Step 5. get number of spaces in Sec-WebSocket-Key<n> fields.
-	space1 := uint32(strings.Count(key1, " "))
-	space2 := uint32(strings.Count(key2, " "))
-	if space1 == 0 || space2 == 0 {
-		return
-	}
-
-	// Step 6. key number must be an integral multiple of spaces.
-	if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 {
-		return
-	}
-
-	// Step 7. let part be key number divided by spaces.
-	part1 := keyNumber1 / space1
-	part2 := keyNumber2 / space2
-
-	// Step 8. let challenge be concatenation of part1, part2 and key3.
-	// Step 9. get MD5 fingerprint of challenge.
-	response, err := getChallengeResponse(part1, part2, key3)
+	conn, err := newServerConn(rwc, buf, req)
 	if err != nil {
 		return
 	}
-
-	// Step 10. send response status line.
-	buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n")
-	// Step 11. send response headers.
-	buf.WriteString("Upgrade: WebSocket\r\n")
-	buf.WriteString("Connection: Upgrade\r\n")
-	buf.WriteString("Sec-WebSocket-Location: " + location + "\r\n")
-	buf.WriteString("Sec-WebSocket-Origin: " + origin + "\r\n")
-	protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
-	if protocol != "" {
-		buf.WriteString("Sec-WebSocket-Protocol: " + protocol + "\r\n")
-	}
-	// Step 12. send CRLF.
-	buf.WriteString("\r\n")
-	// Step 13. send response data.
-	buf.Write(response)
-	if err := buf.Flush(); err != nil {
-		return
-	}
-	ws := newConn(origin, location, protocol, buf, rwc)
-	ws.Request = req
-	f(ws)
-}
-
-/*
-Draft75Handler is an interface to a WebSocket based on the
-(soon obsolete) draft-hixie-thewebsocketprotocol-75.
-*/
-type Draft75Handler func(*Conn)
-
-// ServeHTTP implements the http.Handler interface for a Web Socket.
-func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	if req.Method != "GET" || req.Proto != "HTTP/1.1" {
-		w.WriteHeader(http.StatusBadRequest)
-		io.WriteString(w, "Unexpected request")
-		return
-	}
-	if req.Header.Get("Upgrade") != "WebSocket" {
-		w.WriteHeader(http.StatusBadRequest)
-		io.WriteString(w, "missing Upgrade: WebSocket header")
-		return
-	}
-	if req.Header.Get("Connection") != "Upgrade" {
-		w.WriteHeader(http.StatusBadRequest)
-		io.WriteString(w, "missing Connection: Upgrade header")
-		return
-	}
-	origin := strings.TrimSpace(req.Header.Get("Origin"))
-	if origin == "" {
-		w.WriteHeader(http.StatusBadRequest)
-		io.WriteString(w, "missing Origin header")
-		return
-	}
-
-	rwc, buf, err := w.(http.Hijacker).Hijack()
-	if err != nil {
-		panic("Hijack failed: " + err.String())
-		return
-	}
-	defer rwc.Close()
-
-	var location string
-	if req.TLS != nil {
-		location = "wss://" + req.Host + req.URL.RawPath
-	} else {
-		location = "ws://" + req.Host + req.URL.RawPath
-	}
-
-	// TODO(ukai): verify origin,location,protocol.
-
-	buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
-	buf.WriteString("Upgrade: WebSocket\r\n")
-	buf.WriteString("Connection: Upgrade\r\n")
-	buf.WriteString("WebSocket-Origin: " + origin + "\r\n")
-	buf.WriteString("WebSocket-Location: " + location + "\r\n")
-	protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol"))
-	// canonical header key of WebSocket-Protocol.
-	if protocol != "" {
-		buf.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
-	}
-	buf.WriteString("\r\n")
-	if err := buf.Flush(); err != nil {
-		return
+	if conn == nil {
+		panic("unepxected nil conn")
 	}
-	ws := newConn(origin, location, protocol, buf, rwc)
-	f(ws)
+	h(conn)
 }
diff --git a/src/pkg/websocket/websocket.go b/src/pkg/websocket/websocket.go
index 7447cf8..1d063c3 100644
--- a/src/pkg/websocket/websocket.go
+++ b/src/pkg/websocket/websocket.go
@@ -2,145 +2,239 @@
 // 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 Web Socket protocol.
-// The protocol is defined at http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
+// Package websocket implements a client and server for the WebSocket protocol.
+// The protocol is defined at http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol
 package websocket
 
-// TODO(ukai):
-//   better logging.
-
 import (
 	"bufio"
-	"crypto/md5"
-	"encoding/binary"
+	"crypto/tls"
 	"http"
 	"io"
+	"io/ioutil"
+	"json"
 	"net"
 	"os"
+	"url"
 )
 
-// WebSocketAddr is an implementation of net.Addr for Web Sockets.
-type WebSocketAddr string
+const (
+	ProtocolVersionHixie75 = -75
+	ProtocolVersionHixie76 = -76
+	ProtocolVersionHybi00  = 0
+	ProtocolVersionHybi    = 8
 
-// Network returns the network type for a Web Socket, "websocket".
-func (addr WebSocketAddr) Network() string { return "websocket" }
+	ContinuationFrame = 0
+	TextFrame         = 1
+	BinaryFrame       = 2
+	CloseFrame        = 8
+	PingFrame         = 9
+	PongFrame         = 10
+	UnknownFrame      = 255
+)
 
-// String returns the network address for a Web Socket.
-func (addr WebSocketAddr) String() string { return string(addr) }
+// WebSocket protocol errors.
+type ProtocolError struct {
+	ErrorString string
+}
 
-const (
-	stateFrameByte = iota
-	stateFrameLength
-	stateFrameData
-	stateFrameTextData
+func (err ProtocolError) String() 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"}
 )
 
-// Conn is a channel to communicate to a Web Socket.
-// It implements the net.Conn interface.
+// WebSocketAddr is an implementation of net.Addr for WebSocket.
+type WebSocketAddr struct {
+	*url.URL
+}
+
+// Network returns the network type for a WebSocket, "websocket".
+func (addr WebSocketAddr) Network() string { return "websocket" }
+
+// String returns the network address for a WebSocket.
+func (addr WebSocketAddr) String() string { return addr.String() }
+
+// 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
+
+	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 os.Error)
+
+	// AcceptHandshake accepts the client handshake request and sends
+	// handshake response back to client.
+	AcceptHandshake(buf *bufio.Writer) (err os.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 os.Error)
+}
+
+// frameWriter is an interface to write a WebSocket frame.
+type frameWriter interface {
+	// Writer is to write playload of the frame.
+	io.WriteCloser
+}
+
+// frameWriterFactory is an interface to create new frame writer.
+type frameWriterFactory interface {
+	NewFrameWriter(payloadType byte) (w frameWriter, err os.Error)
+}
+
+type frameHandler interface {
+	HandleFrame(frame frameReader) (r frameReader, err os.Error)
+	WriteClose(status int) (err os.Error)
+}
+
+// Conn represents a WebSocket connection.
 type Conn struct {
-	// The origin URI for the Web Socket.
-	Origin string
-	// The location URI for the Web Socket.
-	Location string
-	// The subprotocol for the Web Socket.
-	Protocol string
-	// The initial http Request (for the Server side only).
-	Request *http.Request
+	config  *Config
+	request *http.Request
 
 	buf *bufio.ReadWriter
 	rwc io.ReadWriteCloser
 
-	// It holds text data in previous Read() that failed with small buffer.
-	data    []byte
-	reading bool
-}
+	frameReaderFactory
+	frameReader
 
-// newConn creates a new Web Socket.
-func newConn(origin, location, protocol string, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
-	if buf == nil {
-		br := bufio.NewReader(rwc)
-		bw := bufio.NewWriter(rwc)
-		buf = bufio.NewReadWriter(br, bw)
-	}
-	ws := &Conn{Origin: origin, Location: location, Protocol: protocol, buf: buf, rwc: rwc}
-	return ws
+	frameWriterFactory
+
+	frameHandler
+	PayloadType        byte
+	defaultCloseStatus int
 }
 
-// Read implements the io.Reader interface for a Conn.
+// 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 os.Error) {
-Frame:
-	for !ws.reading && len(ws.data) == 0 {
-		// Beginning of frame, possibly.
-		b, err := ws.buf.ReadByte()
+again:
+	if ws.frameReader == nil {
+		frame, err := ws.frameReaderFactory.NewFrameReader()
 		if err != nil {
 			return 0, err
 		}
-		if b&0x80 == 0x80 {
-			// Skip length frame.
-			length := 0
-			for {
-				c, err := ws.buf.ReadByte()
-				if err != nil {
-					return 0, err
-				}
-				length = length*128 + int(c&0x7f)
-				if c&0x80 == 0 {
-					break
-				}
-			}
-			for length > 0 {
-				_, err := ws.buf.ReadByte()
-				if err != nil {
-					return 0, err
-				}
-			}
-			continue Frame
+		ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
+		if err != nil {
+			return 0, err
 		}
-		// In text mode
-		if b != 0 {
-			// Skip this frame
-			for {
-				c, err := ws.buf.ReadByte()
-				if err != nil {
-					return 0, err
-				}
-				if c == '\xff' {
-					break
-				}
-			}
-			continue Frame
+		if ws.frameReader == nil {
+			goto again
 		}
-		ws.reading = true
 	}
-	if len(ws.data) == 0 {
-		ws.data, err = ws.buf.ReadSlice('\xff')
-		if err == nil {
-			ws.reading = false
-			ws.data = ws.data[:len(ws.data)-1] // trim \xff
+	n, err = ws.frameReader.Read(msg)
+	if err == os.EOF {
+		if trailer := ws.frameReader.TrailerReader(); trailer != nil {
+			io.Copy(ioutil.Discard, trailer)
 		}
+		ws.frameReader = nil
+		goto again
 	}
-	n = copy(msg, ws.data)
-	ws.data = ws.data[n:]
 	return n, err
 }
 
-// Write implements the io.Writer interface for a Conn.
+// 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 os.Error) {
-	ws.buf.WriteByte(0)
-	ws.buf.Write(msg)
-	ws.buf.WriteByte(0xff)
-	err = ws.buf.Flush()
-	return len(msg), err
+	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 for a Conn.
-func (ws *Conn) Close() os.Error { return ws.rwc.Close() }
+// Close implements the io.Closer interface.
+func (ws *Conn) Close() os.Error {
+	err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
+	if err != nil {
+		return err
+	}
+	return ws.rwc.Close()
+}
 
-// LocalAddr returns the WebSocket Origin for the connection.
-func (ws *Conn) LocalAddr() net.Addr { return WebSocketAddr(ws.Origin) }
+func (ws *Conn) IsClientConn() bool { return ws.request == nil }
+func (ws *Conn) IsServerConn() bool { return ws.request != nil }
 
-// RemoteAddr returns the WebSocket locations for the connection.
-func (ws *Conn) RemoteAddr() net.Addr { return WebSocketAddr(ws.Location) }
+// 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 WebSocketAddr{ws.config.Origin}
+	}
+	return WebSocketAddr{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 WebSocketAddr{ws.config.Location}
+	}
+	return WebSocketAddr{ws.config.Origin}
+}
 
 // SetTimeout sets the connection's network timeout in nanoseconds.
 func (ws *Conn) SetTimeout(nsec int64) os.Error {
@@ -166,27 +260,139 @@ func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
 	return os.EINVAL
 }
 
-// getChallengeResponse computes the expected response from the
-// challenge as described in section 5.1 Opening Handshake steps 42 to
-// 43 of http://www.whatwg.org/specs/web-socket-protocol/
-func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err os.Error) {
-	// 41. Let /challenge/ be the concatenation of /number_1/, expressed
-	// a big-endian 32 bit integer, /number_2/, expressed in a big-
-	// endian 32 bit integer, and the eight bytes of /key_3/ in the
-	// order they were sent to the wire.
-	challenge := make([]byte, 16)
-	binary.BigEndian.PutUint32(challenge[0:], number1)
-	binary.BigEndian.PutUint32(challenge[4:], number2)
-	copy(challenge[8:], key3)
+// 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 os.Error)
+	Unmarshal func(data []byte, payloadType byte, v interface{}) (err os.Error)
+}
+
+// Send sends v marshaled by cd.Marshal as single frame to ws.
+func (cd Codec) Send(ws *Conn, v interface{}) (err os.Error) {
+	if err != nil {
+		return err
+	}
+	data, payloadType, err := cd.Marshal(v)
+	if err != nil {
+		return err
+	}
+	w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
+	_, 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 os.Error) {
+	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 os.Error) {
+	switch data := v.(type) {
+	case string:
+		return []byte(data), TextFrame, nil
+	case []byte:
+		return data, BinaryFrame, nil
+	}
+	return nil, UnknownFrame, ErrNotSupported
+}
 
-	// 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big-
-	// endian 128 bit string.
-	h := md5.New()
-	if _, err = h.Write(challenge); err != nil {
-		return
+func unmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
+	switch data := v.(type) {
+	case *string:
+		*data = string(msg)
+		return nil
+	case *[]byte:
+		*data = msg
+		return nil
 	}
-	expected = h.Sum()
-	return
+	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 os.Error) {
+	msg, err = json.Marshal(v)
+	return msg, TextFrame, err
 }
 
-var _ net.Conn = (*Conn)(nil) // compile-time check that *Conn implements net.Conn.
+func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
+	return json.Unmarshal(msg, v)
+}
+
+/*
+JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
+
+Trival 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/src/pkg/websocket/websocket_test.go b/src/pkg/websocket/websocket_test.go
index 71c3c85..058f38c 100644
--- a/src/pkg/websocket/websocket_test.go
+++ b/src/pkg/websocket/websocket_test.go
@@ -5,7 +5,6 @@
 package websocket
 
 import (
-	"bufio"
 	"bytes"
 	"fmt"
 	"http"
@@ -13,6 +12,7 @@ import (
 	"io"
 	"log"
 	"net"
+	"strings"
 	"sync"
 	"testing"
 	"url"
@@ -23,31 +23,38 @@ var once sync.Once
 
 func echoServer(ws *Conn) { io.Copy(ws, ws) }
 
+type Count struct {
+	S string
+	N int
+}
+
+func countServer(ws *Conn) {
+	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
+		}
+	}
+}
+
 func startServer() {
 	http.Handle("/echo", Handler(echoServer))
-	http.Handle("/echoDraft75", Draft75Handler(echoServer))
+	http.Handle("/count", Handler(countServer))
 	server := httptest.NewServer(nil)
 	serverAddr = server.Listener.Addr().String()
 	log.Print("Test WebSocket server listening on ", serverAddr)
 }
 
-// Test the getChallengeResponse function with values from section
-// 5.1 of the specification steps 18, 26, and 43 from
-// http://www.whatwg.org/specs/web-socket-protocol/
-func TestChallenge(t *testing.T) {
-	var part1 uint32 = 777007543
-	var part2 uint32 = 114997259
-	key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58}
-	expected := []byte("0st3Rl&q-2ZU^weu")
-
-	response, err := getChallengeResponse(part1, part2, key3)
-	if err != nil {
-		t.Errorf("getChallengeResponse: returned error %v", err)
-		return
-	}
-	if !bytes.Equal(expected, response) {
-		t.Errorf("getChallengeResponse: expected %q got %q", expected, response)
-	}
+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) {
@@ -58,19 +65,18 @@ func TestEcho(t *testing.T) {
 	if err != nil {
 		t.Fatal("dialing", err)
 	}
-	ws, err := newClient("/echo", "localhost", "http://localhost",
-		"ws://localhost/echo", "", client, handshake)
+	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 := ws.Write(msg); err != nil {
+	if _, err := conn.Write(msg); err != nil {
 		t.Errorf("Write: %v", err)
 	}
 	var actual_msg = make([]byte, 512)
-	n, err := ws.Read(actual_msg)
+	n, err := conn.Read(actual_msg)
 	if err != nil {
 		t.Errorf("Read: %v", err)
 	}
@@ -78,10 +84,10 @@ func TestEcho(t *testing.T) {
 	if !bytes.Equal(msg, actual_msg) {
 		t.Errorf("Echo: expected %q got %q", msg, actual_msg)
 	}
-	ws.Close()
+	conn.Close()
 }
 
-func TestEchoDraft75(t *testing.T) {
+func TestCount(t *testing.T) {
 	once.Do(startServer)
 
 	// websocket.Dial()
@@ -89,27 +95,39 @@ func TestEchoDraft75(t *testing.T) {
 	if err != nil {
 		t.Fatal("dialing", err)
 	}
-	ws, err := newClient("/echoDraft75", "localhost", "http://localhost",
-		"ws://localhost/echoDraft75", "", client, draft75handshake)
+	conn, err := NewClient(newConfig(t, "/count"), client)
 	if err != nil {
-		t.Errorf("WebSocket handshake: %v", err)
+		t.Errorf("WebSocket handshake error: %v", err)
 		return
 	}
 
-	msg := []byte("hello, world\n")
-	if _, err := ws.Write(msg); err != nil {
-		t.Errorf("Write: error %v", err)
+	var count Count
+	count.S = "hello"
+	if err := JSON.Send(conn, count); err != nil {
+		t.Errorf("Write: %v", err)
 	}
-	var actual_msg = make([]byte, 512)
-	n, err := ws.Read(actual_msg)
-	if err != nil {
-		t.Errorf("Read: error %v", err)
+	if err := JSON.Receive(conn, &count); 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)
+	if count.N != 1 {
+		t.Errorf("count: expected %d got %d", 1, count.N)
 	}
-	ws.Close()
+	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) {
@@ -120,8 +138,13 @@ func TestWithQuery(t *testing.T) {
 		t.Fatal("dialing", err)
 	}
 
-	ws, err := newClient("/echo?q=v", "localhost", "http://localhost",
-		"ws://localhost/echo?q=v", "", client, handshake)
+	config := newConfig(t, "/echo")
+	config.Location, err = url.ParseRequest(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
@@ -137,8 +160,10 @@ func TestWithProtocol(t *testing.T) {
 		t.Fatal("dialing", err)
 	}
 
-	ws, err := newClient("/echo", "localhost", "http://localhost",
-		"ws://localhost/echo", "test", client, handshake)
+	config := newConfig(t, "/echo")
+	config.Protocol = append(config.Protocol, "test")
+
+	ws, err := NewClient(config, client)
 	if err != nil {
 		t.Errorf("WebSocket handshake: %v", err)
 		return
@@ -167,27 +192,15 @@ func TestHTTP(t *testing.T) {
 	}
 }
 
-func TestHTTPDraft75(t *testing.T) {
-	once.Do(startServer)
-
-	r, err := http.Get(fmt.Sprintf("http://%s/echoDraft75", serverAddr))
-	if err != nil {
-		t.Errorf("Get: error %#v", err)
-		return
-	}
-	if r.StatusCode != http.StatusBadRequest {
-		t.Errorf("Get: got status %d", r.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 := Dial(fmt.Sprintf("ws://%s/echo", serverAddr), "", "http://localhost/")
+		ws, err := DialConfig(config)
 		if err != nil {
 			t.Error("Dial failed:", err.String())
 			break
@@ -206,19 +219,18 @@ func TestSmallBuffer(t *testing.T) {
 	if err != nil {
 		t.Fatal("dialing", err)
 	}
-	ws, err := newClient("/echo", "localhost", "http://localhost",
-		"ws://localhost/echo", "", client, handshake)
+	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 := ws.Write(msg); err != nil {
+	if _, err := conn.Write(msg); err != nil {
 		t.Errorf("Write: %v", err)
 	}
 	var small_msg = make([]byte, 8)
-	n, err := ws.Read(small_msg)
+	n, err := conn.Read(small_msg)
 	if err != nil {
 		t.Errorf("Read: %v", err)
 	}
@@ -226,7 +238,7 @@ func TestSmallBuffer(t *testing.T) {
 		t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg)
 	}
 	var second_msg = make([]byte, len(msg))
-	n, err = ws.Read(second_msg)
+	n, err = conn.Read(second_msg)
 	if err != nil {
 		t.Errorf("Read: %v", err)
 	}
@@ -234,38 +246,5 @@ func TestSmallBuffer(t *testing.T) {
 	if !bytes.Equal(msg[len(small_msg):], second_msg) {
 		t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg)
 	}
-	ws.Close()
-
-}
-
-func testSkipLengthFrame(t *testing.T) {
-	b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
-	buf := bytes.NewBuffer(b)
-	br := bufio.NewReader(buf)
-	bw := bufio.NewWriter(buf)
-	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
-	msg := make([]byte, 5)
-	n, err := ws.Read(msg)
-	if err != nil {
-		t.Errorf("Read: %v", err)
-	}
-	if !bytes.Equal(b[4:8], msg[0:n]) {
-		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
-	}
-}
-
-func testSkipNoUTF8Frame(t *testing.T) {
-	b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
-	buf := bytes.NewBuffer(b)
-	br := bufio.NewReader(buf)
-	bw := bufio.NewWriter(buf)
-	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
-	msg := make([]byte, 5)
-	n, err := ws.Read(msg)
-	if err != nil {
-		t.Errorf("Read: %v", err)
-	}
-	if !bytes.Equal(b[4:8], msg[0:n]) {
-		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
-	}
+	conn.Close()
 }
diff --git a/src/pkg/xml/marshal.go b/src/pkg/xml/marshal.go
index ea421c1..8396dba 100644
--- a/src/pkg/xml/marshal.go
+++ b/src/pkg/xml/marshal.go
@@ -39,10 +39,10 @@ type printer struct {
 // 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 a single XML
-// element containing the data.
+// writing nothing.  Marshal handles all other data by writing one or more XML
+// elements containing the data.
 //
-// The name of that XML element is taken from, in order of preference:
+// The name for the XML elements is taken from, in order of preference:
 //     - the tag on an XMLName field, if the data is a struct
 //     - the value of an XMLName field of type xml.Name
 //     - the tag of the struct field used to obtain the data
@@ -58,6 +58,31 @@ type printer struct {
 //     - a field with tag "innerxml" is written verbatim,
 //        not subject to the usual marshalling procedure.
 //
+// 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.  For example:
+//
+//	type Result struct {
+//		XMLName   xml.Name `xml:"result"`
+//		FirstName string   `xml:"person>name>first"`
+//		LastName  string   `xml:"person>name>last"`
+//		Age       int      `xml:"person>age"`
+//	}
+//
+//	xml.Marshal(w, &Result{FirstName: "John", LastName: "Doe", Age: 42})
+//
+// would be marshalled as:
+//
+//	<result>
+//		<person>
+//			<name>
+//				<first>John</first>
+//				<last>Doe</last>
+//			</name>
+//			<age>42</age>
+//		</person>
+//	</result>
+//
 // Marshal will return an error if asked to marshal a channel, function, or map.
 func Marshal(w io.Writer, v interface{}) (err os.Error) {
 	p := &printer{bufio.NewWriter(w)}
@@ -170,22 +195,25 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
 		bytes := val.Interface().([]byte)
 		Escape(p, bytes)
 	case reflect.Struct:
+		s := parentStack{printer: p}
 		for i, n := 0, val.NumField(); i < n; i++ {
 			if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
 				name := f.Name
+				vf := val.Field(i)
 				switch tag := f.Tag.Get("xml"); tag {
 				case "":
+					s.trim(nil)
 				case "chardata":
 					if tk := f.Type.Kind(); tk == reflect.String {
-						Escape(p, []byte(val.Field(i).String()))
+						Escape(p, []byte(vf.String()))
 					} else if tk == reflect.Slice {
-						if elem, ok := val.Field(i).Interface().([]byte); ok {
+						if elem, ok := vf.Interface().([]byte); ok {
 							Escape(p, elem)
 						}
 					}
 					continue
 				case "innerxml":
-					iface := val.Field(i).Interface()
+					iface := vf.Interface()
 					switch raw := iface.(type) {
 					case []byte:
 						p.Write(raw)
@@ -197,14 +225,28 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
 				case "attr":
 					continue
 				default:
-					name = tag
+					parents := strings.Split(tag, ">")
+					if len(parents) == 1 {
+						parents, name = nil, tag
+					} else {
+						parents, name = parents[:len(parents)-1], parents[len(parents)-1]
+						if parents[0] == "" {
+							parents[0] = f.Name
+						}
+					}
+
+					s.trim(parents)
+					if !(vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) || !vf.IsNil() {
+						s.push(parents[len(s.stack):])
+					}
 				}
 
-				if err := p.marshalValue(val.Field(i), name); err != nil {
+				if err := p.marshalValue(vf, name); err != nil {
 					return err
 				}
 			}
 		}
+		s.trim(nil)
 	default:
 		return &UnsupportedTypeError{typ}
 	}
@@ -217,6 +259,41 @@ func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
 	return nil
 }
 
+type parentStack struct {
+	*printer
+	stack []string
+}
+
+// trim updates the XML context to match the longest common prefix of the stack
+// and the given parents.  A closing tag will be written for every parent
+// popped.  Passing a zero slice or nil will close all the elements.
+func (s *parentStack) trim(parents []string) {
+	split := 0
+	for ; split < len(parents) && split < len(s.stack); split++ {
+		if parents[split] != s.stack[split] {
+			break
+		}
+	}
+
+	for i := len(s.stack) - 1; i >= split; i-- {
+		s.WriteString("</")
+		s.WriteString(s.stack[i])
+		s.WriteByte('>')
+	}
+
+	s.stack = parents[:split]
+}
+
+// push adds parent elements to the stack and writes open tags.
+func (s *parentStack) push(parents []string) {
+	for i := 0; i < len(parents); i++ {
+		s.WriteString("<")
+		s.WriteString(parents[i])
+		s.WriteByte('>')
+	}
+	s.stack = append(s.stack, parents...)
+}
+
 // A MarshalXMLError is returned when Marshal or MarshalIndent encounter a type
 // that cannot be converted into XML.
 type UnsupportedTypeError struct {
diff --git a/src/pkg/xml/marshal_test.go b/src/pkg/xml/marshal_test.go
index 5b972fa..ad3aa97 100644
--- a/src/pkg/xml/marshal_test.go
+++ b/src/pkg/xml/marshal_test.go
@@ -69,6 +69,41 @@ type SecretAgent struct {
 	Obfuscate string `xml:"innerxml"`
 }
 
+type NestedItems struct {
+	XMLName Name     `xml:"result"`
+	Items   []string `xml:">item"`
+	Item1   []string `xml:"Items>item1"`
+}
+
+type NestedOrder struct {
+	XMLName Name   `xml:"result"`
+	Field1  string `xml:"parent>c"`
+	Field2  string `xml:"parent>b"`
+	Field3  string `xml:"parent>a"`
+}
+
+type MixedNested struct {
+	XMLName Name   `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 Name    `xml:"service"`
+	Domain  *Domain `xml:"host>domain"`
+	Port    *Port   `xml:"host>port"`
+	Extra1  interface{}
+	Extra2  interface{} `xml:"host>extra2"`
+}
+
 var nilStruct *Ship
 
 var marshalTests = []struct {
@@ -170,6 +205,94 @@ var marshalTests = []struct {
 			`</passenger>` +
 			`</spaceship>`,
 	},
+	// Test a>b
+	{
+		Value: NestedItems{Items: []string{}, Item1: []string{}},
+		ExpectXML: `<result>` +
+			`<Items>` +
+			`</Items>` +
+			`</result>`,
+	},
+	{
+		Value: NestedItems{Items: []string{}, Item1: []string{"A"}},
+		ExpectXML: `<result>` +
+			`<Items>` +
+			`<item1>A</item1>` +
+			`</Items>` +
+			`</result>`,
+	},
+	{
+		Value: NestedItems{Items: []string{"A", "B"}, Item1: []string{}},
+		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: `<???>` +
+			`<parent1>` +
+			`<parent2><a>A</a></parent2>` +
+			`<parent2><c>C</c></parent2>` +
+			`</parent1>` +
+			`</???>`,
+	},
+	{
+		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>`,
+	},
+	{
+		Value: Service{Port: &Port{Number: "80"}, Extra2: "example"},
+		ExpectXML: `<service>` +
+			`<host><port>80</port></host>` +
+			`<host><extra2>example</extra2></host>` +
+			`</service>`,
+	},
 }
 
 func TestMarshal(t *testing.T) {
diff --git a/src/sudo.bash b/src/sudo.bash
index 147e58f..3322a18 100755
--- a/src/sudo.bash
+++ b/src/sudo.bash
@@ -13,6 +13,11 @@ Darwin)
 	exit 0
 esac
 
+if [[ ! -d /usr/local/bin ]]; then
+	echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.'
+	exit 2
+fi
+
 for i in prof cov
 do
 	sudo cp "$GOROOT"/src/cmd/$i/6$i /usr/local/bin/6$i
diff --git a/src/version.bash b/src/version.bash
index fc899e2..792814b 100755
--- a/src/version.bash
+++ b/src/version.bash
@@ -6,7 +6,7 @@
 GOROOT=$(dirname $0)/..
 
 # If a version file created by -save is available, use it
-if [ -f "$GOROOT/VERSION" ]; then
+if [ -f "$GOROOT/VERSION" -a "$1" != "-save" ]; then
 	cat $GOROOT/VERSION
 	exit 0
 fi
diff --git a/test/chan/sieve2.go b/test/chan/sieve2.go
index 7f2ed91..9a7ab15 100644
--- a/test/chan/sieve2.go
+++ b/test/chan/sieve2.go
@@ -13,7 +13,6 @@ package main
 import (
 	"container/heap"
 	"container/ring"
-	"container/vector"
 )
 
 // Return a chan of odd numbers, starting from 5.
@@ -47,13 +46,28 @@ type PeekCh struct {
 	ch   chan int
 }
 
-// Heap of PeekCh, sorting by head values.
-type PeekChHeap struct {
-	*vector.Vector
-}
+// Heap of PeekCh, sorting by head values, satisfies Heap interface.
+type PeekChHeap []*PeekCh
 
 func (h *PeekChHeap) Less(i, j int) bool {
-	return h.At(i).(*PeekCh).head < h.At(j).(*PeekCh).head
+	return (*h)[i].head < (*h)[j].head
+}
+
+func (h *PeekChHeap) Swap(i, j int) {
+	(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
+}
+
+func (h *PeekChHeap) Len() int {
+	return len(*h)
+}
+
+func (h *PeekChHeap) Pop() (v interface{}) {
+	*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
+	return
+}
+
+func (h *PeekChHeap) Push(v interface{}) {
+	*h = append(*h, v.(*PeekCh))
 }
 
 // Return a channel to serve as a sending proxy to 'out'.
@@ -108,26 +122,26 @@ func Sieve() chan int {
 
 	// Merge channels of multiples of 'primes' into 'composites'.
 	go func() {
-		h := &PeekChHeap{new(vector.Vector)}
+		var h PeekChHeap
 		min := 15
 		for {
 			m := multiples(<-primes)
 			head := <-m
 			for min < head {
 				composites <- min
-				minchan := heap.Pop(h).(*PeekCh)
+				minchan := heap.Pop(&h).(*PeekCh)
 				min = minchan.head
 				minchan.head = <-minchan.ch
-				heap.Push(h, minchan)
+				heap.Push(&h, minchan)
 			}
 			for min == head {
-				minchan := heap.Pop(h).(*PeekCh)
+				minchan := heap.Pop(&h).(*PeekCh)
 				min = minchan.head
 				minchan.head = <-minchan.ch
-				heap.Push(h, minchan)
+				heap.Push(&h, minchan)
 			}
 			composites <- head
-			heap.Push(h, &PeekCh{<-m, m})
+			heap.Push(&h, &PeekCh{<-m, m})
 		}
 	}()
 
diff --git a/test/escape2.go b/test/escape2.go
new file mode 100644
index 0000000..dbe0c69
--- /dev/null
+++ b/test/escape2.go
@@ -0,0 +1,995 @@
+// errchk -0 $G -m $D/$F.go
+
+// 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 foo
+
+import "unsafe"
+
+var gxx *int
+
+func foo1(x int) { // ERROR "moved to heap: NAME-x"
+	gxx = &x  // ERROR "&x escapes to heap"
+}
+
+func foo2(yy *int) { // ERROR "leaking param: NAME-yy"
+	gxx = yy
+}
+
+func foo3(x int) *int { // ERROR "moved to heap: NAME-x"
+	return &x  // ERROR "&x escapes to heap"
+}
+
+type T *T
+
+func foo3b(t T) { // ERROR "leaking param: NAME-t"
+	*t = t
+}
+
+// xx isn't going anywhere, so use of yy is ok
+func foo4(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape"
+	xx = yy
+}
+
+// xx isn't going anywhere, so taking address of yy is ok
+func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape"
+	xx = &yy  // ERROR "&yy does not escape"
+}
+
+func foo6(xx **int, yy *int) { // ERROR "xx does not escape" "leaking param: NAME-yy"
+	*xx = yy
+}
+
+func foo7(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape"
+	**xx = *yy
+}
+
+func foo8(xx, yy *int) int { // ERROR "xx does not escape" "yy does not escape"
+	xx = yy
+	return *xx
+}
+
+func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy"
+	xx = yy
+	return xx
+}
+
+func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape"
+	*xx = *yy
+}
+
+func foo11() int {
+	x, y := 0, 42
+	xx := &x  // ERROR "&x does not escape"
+	yy := &y  // ERROR "&y does not escape"
+	*xx = *yy
+	return x
+}
+
+var xxx **int
+
+func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy"
+	xxx = yyy
+}
+
+func foo13(yyy **int) { // ERROR "yyy does not escape"
+	*xxx = *yyy
+}
+
+func foo14(yyy **int) { // ERROR "yyy does not escape"
+	**xxx = **yyy
+}
+
+func foo15(yy *int) { // ERROR "moved to heap: NAME-yy"
+	xxx = &yy  // ERROR "&yy escapes to heap"
+}
+
+func foo16(yy *int) { // ERROR "leaking param: NAME-yy"
+	*xxx = yy
+}
+
+func foo17(yy *int) { // ERROR "yy does not escape"
+	**xxx = *yy
+}
+
+func foo18(y int) { // ERROR "moved to heap: "NAME-y"
+	*xxx = &y  // ERROR "&y escapes to heap"
+}
+
+func foo19(y int) {
+	**xxx = y
+}
+
+type Bar struct {
+	i  int
+	ii *int
+}
+
+func NewBar() *Bar {
+	return &Bar{42, nil} // ERROR "&struct literal escapes to heap"
+}
+
+func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x"
+	return &Bar{42, x} // ERROR "&struct literal escapes to heap"
+}
+
+func NewBarp2(x *int) *Bar { // ERROR "x does not escape"
+	return &Bar{*x, nil} // ERROR "&struct literal escapes to heap"
+}
+
+func (b *Bar) NoLeak() int { // ERROR "b does not escape"
+	return *(b.ii)
+}
+
+func (b *Bar) AlsoNoLeak() *int { // ERROR "b does not escape"
+	return b.ii
+}
+
+func goLeak(b *Bar) {  // ERROR "leaking param: NAME-b"
+	go b.NoLeak()
+}
+
+type Bar2 struct {
+	i  [12]int
+	ii []int
+}
+
+func NewBar2() *Bar2 {
+	return &Bar2{[12]int{42}, nil} // ERROR "&struct literal escapes to heap"
+}
+
+func (b *Bar2) NoLeak() int { // ERROR "b does not escape"
+	return b.i[0]
+}
+
+func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b"
+	return b.i[:]  // ERROR "&b.i escapes to heap"
+}
+
+func (b *Bar2) AlsoNoLeak() []int { // ERROR "b does not escape"
+	return b.ii[0:1]
+}
+
+func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b"
+	b.ii = b.i[0:4]  // ERROR "&b.i escapes to heap"
+}
+
+func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b"
+	var buf []int
+	buf = b.i[0:]  // ERROR "&b.i escapes to heap"
+	b.ii = buf
+}
+
+func foo21() func() int {
+	x := 42 // ERROR "moved to heap: NAME-x"
+	return func() int {  // ERROR "func literal escapes to heap"
+		return x  // ERROR "&x escapes to heap"
+	}
+}
+
+func foo22() int {
+	x := 42
+	return func() int {  // ERROR "func literal does not escape"
+		return x
+	}()
+}
+
+func foo23(x int) func() int { // ERROR "moved to heap: NAME-x"
+	return func() int {  // ERROR "func literal escapes to heap"
+		return x  // ERROR "&x escapes to heap"
+	}
+}
+
+func foo23a(x int) func() int { // ERROR "moved to heap: NAME-x"
+	f := func() int {  // ERROR "func literal escapes to heap"
+		return x  // ERROR "&x escapes to heap"
+	}
+	return f
+}
+
+func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x"
+	f := func() int { return x } // ERROR "moved to heap: NAME-f" "func literal escapes to heap" "&x escapes to heap"
+	return &f  // ERROR "&f escapes to heap"
+}
+
+func foo24(x int) int {
+	return func() int {  // ERROR "func literal does not escape"
+		return x
+	}()
+}
+
+var x *int
+
+func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
+	x = xx
+	return *x
+}
+
+func foonoleak(xx *int) int { // ERROR "xx does not escape"
+	return *x + *xx
+}
+
+func foo31(x int) int { // ERROR "moved to heap: NAME-x"
+	return fooleak(&x)  // ERROR "&x escapes to heap"
+}
+
+func foo32(x int) int {
+	return foonoleak(&x)  // ERROR "&x does not escape"
+}
+
+type Foo struct {
+	xx *int
+	x  int
+}
+
+var F Foo
+var pf *Foo
+
+func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f"
+	pf = f
+}
+
+func (f *Foo) foonoleak() { // ERROR "f does not escape"
+	F.x = f.x
+}
+
+func (f *Foo) Leak() { // ERROR "leaking param: NAME-f"
+	f.fooleak()
+}
+
+func (f *Foo) NoLeak() { // ERROR "f does not escape"
+	f.foonoleak()
+}
+
+func foo41(x int) { // ERROR "moved to heap: NAME-x"
+	F.xx = &x  // ERROR "&x escapes to heap"
+}
+
+func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
+	f.xx = &x  // ERROR "&x escapes to heap"
+}
+
+func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
+	f.xx = &x  // ERROR "&x escapes to heap"
+}
+
+func foo44(yy *int) { // ERROR "leaking param: NAME-yy"
+	F.xx = yy
+}
+
+func (f *Foo) foo45() { // ERROR "f does not escape"
+	F.x = f.x
+}
+
+func (f *Foo) foo46() { // ERROR "f does not escape"
+	F.xx = f.xx
+}
+
+func (f *Foo) foo47() { // ERROR "leaking param: NAME-f"
+	f.xx = &f.x  // ERROR "&f.x escapes to heap"
+}
+
+var ptrSlice []*int
+
+func foo50(i *int) { // ERROR "leaking param: NAME-i"
+	ptrSlice[0] = i
+}
+
+var ptrMap map[*int]*int
+
+func foo51(i *int) { // ERROR "leaking param: NAME-i"
+	ptrMap[i] = i
+}
+
+func indaddr1(x int) *int { // ERROR "moved to heap: NAME-x"
+	return &x  // ERROR "&x escapes to heap"
+}
+
+func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x"
+	return *&x  // ERROR "&x does not escape"
+}
+
+func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x"
+	return *(**int)(unsafe.Pointer(&x))  // ERROR "&x does not escape"
+}
+
+// From package math:
+
+func Float32bits(f float32) uint32 {
+	return *(*uint32)(unsafe.Pointer(&f))  // ERROR "&f does not escape"
+}
+
+func Float32frombits(b uint32) float32 {
+	return *(*float32)(unsafe.Pointer(&b))  // ERROR "&b does not escape"
+}
+
+func Float64bits(f float64) uint64 {
+	return *(*uint64)(unsafe.Pointer(&f))  // ERROR "&f does not escape"
+}
+
+func Float64frombits(b uint64) float64 {
+	return *(*float64)(unsafe.Pointer(&b))  // ERROR "&b does not escape"
+}
+
+// contrast with
+func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f"
+	return (*uint64)(unsafe.Pointer(&f))  // ERROR "&f escapes to heap"
+}
+
+func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: NAME-f"
+	return (*uint64)(unsafe.Pointer(f))
+}
+
+func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i"
+	switch val := i.(type) {
+	case *int:
+		return val
+	case *int8:
+		v := int(*val) // ERROR "moved to heap: NAME-v"
+		return &v  // ERROR "&v escapes to heap"
+	}
+	return nil
+}
+
+func exprsw(i *int) *int { // ERROR "leaking param: NAME-i"
+	switch j := i; *j + 110 {
+	case 12:
+		return j
+	case 42:
+		return nil
+	}
+	return nil
+
+}
+
+// assigning to an array element is like assigning to the array
+func foo60(i *int) *int { // ERROR "leaking param: NAME-i"
+	var a [12]*int
+	a[0] = i
+	return a[1]
+}
+
+func foo60a(i *int) *int { // ERROR "i does not escape"
+	var a [12]*int
+	a[0] = i
+	return nil
+}
+
+// assigning to a struct field  is like assigning to the struct
+func foo61(i *int) *int { // ERROR "leaking param: NAME-i"
+	type S struct {
+		a, b *int
+	}
+	var s S
+	s.a = i
+	return s.b
+}
+
+func foo61a(i *int) *int { // ERROR "i does not escape"
+	type S struct {
+		a, b *int
+	}
+	var s S
+	s.a = i
+	return nil
+}
+
+// assigning to a struct field is like assigning to the struct but
+// here this subtlety is lost, since s.a counts as an assignment to a
+// track-losing dereference.
+func foo62(i *int) *int { // ERROR "leaking param: NAME-i"
+	type S struct {
+		a, b *int
+	}
+	s := new(S) // ERROR "new[(]S[)] does not escape"
+	s.a = i
+	return nil // s.b
+}
+
+type M interface {
+	M()
+}
+
+func foo63(m M) { // ERROR "m does not escape"
+}
+
+func foo64(m M) { // ERROR "leaking param: NAME-m"
+	m.M()
+}
+
+func foo64b(m M) { // ERROR "leaking param: NAME-m"
+	defer m.M()
+}
+
+type MV int
+
+func (MV) M() {}
+
+func foo65() {
+	var mv MV
+	foo63(&mv)  // ERROR "&mv does not escape"
+}
+
+func foo66() {
+	var mv MV // ERROR "moved to heap: NAME-mv"
+	foo64(&mv)  // ERROR "&mv escapes to heap"
+}
+
+func foo67() {
+	var mv MV
+	foo63(mv)
+}
+
+func foo68() {
+	var mv MV
+	foo64(mv) // escapes but it's an int so irrelevant
+}
+
+func foo69(m M) { // ERROR "leaking param: NAME-m"
+	foo64(m)
+}
+
+func foo70(mv1 *MV, m M) { // ERROR "leaking param: NAME-mv1" "leaking param: NAME-m"
+	m = mv1
+	foo64(m)
+}
+
+func foo71(x *int) []*int { // ERROR "leaking param: NAME-x"
+	var y []*int
+	y = append(y, x)
+	return y
+}
+
+func foo71a(x int) []*int { // ERROR "moved to heap: NAME-x"
+	var y []*int
+	y = append(y, &x)  // ERROR "&x escapes to heap"
+	return y
+}
+
+func foo72() {
+	var x int
+	var y [1]*int
+	y[0] = &x  // ERROR "&x does not escape"
+}
+
+func foo72aa() [10]*int {
+	var x int // ERROR "moved to heap: NAME-x"
+	var y [10]*int
+	y[0] = &x  // ERROR "&x escapes to heap"
+	return y
+}
+
+func foo72a() {
+	var y [10]*int
+	for i := 0; i < 10; i++ {
+		// escapes its scope
+		x := i // ERROR "moved to heap: NAME-x"
+		y[i] = &x // ERROR "&x escapes to heap"
+	}
+	return
+}
+
+func foo72b() [10]*int {
+	var y [10]*int
+	for i := 0; i < 10; i++ {
+		x := i // ERROR "moved to heap: NAME-x"
+		y[i] = &x  // ERROR "&x escapes to heap"
+	}
+	return y
+}
+
+// issue 2145
+func foo73() {
+	s := []int{3, 2, 1} // ERROR "slice literal does not escape"
+	for _, v := range s {
+		vv := v        // ERROR "moved to heap: NAME-vv"
+		// actually just escapes its scope
+		defer func() { // ERROR "func literal escapes to heap"
+			println(vv)  // ERROR "&vv escapes to heap"
+		}()
+	}
+}
+
+func foo74() {
+	s := []int{3, 2, 1} // ERROR "slice literal does not escape"
+	for _, v := range s {
+		vv := v        // ERROR "moved to heap: NAME-vv"
+		// actually just escapes its scope
+		fn := func() { // ERROR "func literal escapes to heap"
+			println(vv)  // ERROR "&vv escapes to heap"
+		}
+		defer fn()
+	}
+}
+
+func myprint(y *int, x ...interface{}) *int { // ERROR "x does not escape" "leaking param: NAME-y"
+	return y
+}
+
+func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: NAME-x"
+	return &x[0]  // ERROR "&x.0. escapes to heap"
+}
+
+func foo75(z *int) { // ERROR "leaking param: NAME-z"
+	myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo75a(z *int) { // ERROR "z does not escape"
+	myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+}
+
+func foo76(z *int) { // ERROR "leaking param: NAME-z"
+	myprint(nil, z) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo76a(z *int) { // ERROR "leaking param: NAME-z"
+	myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
+}
+
+func foo76b() {
+	myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo76c() {
+	myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+}
+
+func foo76d() {
+	defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo76e() {
+	defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+}
+
+func foo76f() {
+	for {
+		// TODO: This one really only escapes its scope, but we don't distinguish yet.
+		defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+	}
+}
+
+func foo76g() {
+	for {
+		defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+	}
+}
+
+func foo77(z []interface{}) { // ERROR "z does not escape"
+	myprint(nil, z...) // z does not escape
+}
+
+func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z"
+	myprint1(nil, z...)
+}
+
+func foo78(z int) *int { // ERROR "moved to heap: NAME-z"
+	return &z  // ERROR "&z escapes to heap"
+}
+
+func foo78a(z int) *int { // ERROR "moved to heap: NAME-z"
+	y := &z  // ERROR "&z escapes to heap"
+	x := &y  // ERROR "&y does not escape"
+	return *x // really return y
+}
+
+func foo79() *int {
+	return new(int) // ERROR "new[(]int[)] escapes to heap"
+}
+
+func foo80() *int {
+	var z *int
+	for {
+		// Really just escapes its scope but we don't distinguish
+		z = new(int) // ERROR "new[(]int[)] escapes to heap"
+	}
+	_ = z
+	return nil
+}
+
+func foo81() *int {
+	for {
+		z := new(int) // ERROR "new[(]int[)] does not escape"
+		_ = z
+	}
+	return nil
+}
+
+type Fooer interface {
+	Foo()
+}
+
+type LimitedFooer struct {
+	Fooer
+	N int64
+}
+
+func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r"
+	return &LimitedFooer{r, n} // ERROR "&struct literal escapes to heap"
+}
+
+func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+	return map[*int]*int{nil: x} // ERROR "map literal escapes to heap"
+}
+
+func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+	return map[*int]*int{x: nil} // ERROR "map literal escapes to heap"
+}
+
+func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x"
+	return [2]*int{x, nil}
+}
+
+// does not leak c
+func foo93(c chan *int) *int { // ERROR "c does not escape"
+	for v := range c {
+		return v
+	}
+	return nil
+}
+
+// does not leak m
+func foo94(m map[*int]*int, b bool) *int { // ERROR "m does not escape"
+	for k, v := range m {
+		if b {
+			return k
+		}
+		return v
+	}
+	return nil
+}
+
+// does leak x
+func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x"
+	m[x] = x
+}
+
+// does not leak m
+func foo96(m []*int) *int { // ERROR "m does not escape"
+	return m[0]
+}
+
+// does leak m
+func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m"
+	return m[0]
+}
+
+// does not leak m
+func foo98(m map[int]*int) *int { // ERROR "m does not escape"
+	return m[0]
+}
+
+// does leak m
+func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m"
+	return m[:]
+}
+
+// does not leak m
+func foo100(m []*int) *int { // ERROR "m does not escape"
+	for _, v := range m {
+		return v
+	}
+	return nil
+}
+
+// does leak m
+func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m"
+	for _, v := range m {
+		return v
+	}
+	return nil
+}
+
+// does not leak m
+func foo101a(m [1]*int) *int { // ERROR "m does not escape"
+	for i := range m { // ERROR "moved to heap: NAME-i"
+		return &i  // ERROR "&i escapes to heap"
+	}
+	return nil
+}
+
+// does leak x
+func foo102(m []*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x"
+	m[0] = x
+}
+
+// does not leak x
+func foo103(m [1]*int, x *int) { // ERROR "m does not escape" "x does not escape"
+	m[0] = x
+}
+
+var y []*int
+
+// does not leak x
+func foo104(x []*int) {  // ERROR "x does not escape"
+	copy(y, x)
+}
+
+// does not leak x
+func foo105(x []*int) {  // ERROR "x does not escape"
+	_ = append(y, x...)
+}
+
+// does leak x
+func foo106(x *int) { // ERROR "leaking param: NAME-x"
+	_ = append(y, x)
+}
+
+func foo107(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+	return map[*int]*int{x: nil} // ERROR "map literal escapes to heap"
+}
+
+func foo108(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+	return map[*int]*int{nil: x} // ERROR "map literal escapes to heap"
+}
+
+func foo109(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := map[*int]*int{x: nil}  // ERROR "map literal does not escape"
+	for k, _ := range m {
+		return k
+	}
+	return nil
+}
+
+func foo110(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := map[*int]*int{nil: x}  // ERROR "map literal does not escape"
+	return m[nil]
+}
+
+func foo111(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := []*int{x}  // ERROR "slice literal does not escape"
+	return m[0]
+}
+
+func foo112(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := [1]*int{x}
+	return m[0]
+}
+
+func foo113(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := Bar{ii: x}
+	return m.ii
+}
+
+func foo114(x *int) *int { // ERROR "leaking param: NAME-x"
+	m := &Bar{ii: x}  // ERROR "&struct literal does not escape"
+	return m.ii
+}
+
+func foo115(x *int) *int { // ERROR "leaking param: NAME-x"
+	return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1))
+}
+
+func foo116(b bool) *int {
+	if b {
+		x := 1  // ERROR "moved to heap: NAME-x"
+		return &x  // ERROR "&x escapes to heap"
+	} else {
+		y := 1  // ERROR "moved to heap: NAME-y"
+		return &y  // ERROR "&y escapes to heap"
+	}
+	return nil
+}
+
+func foo117(unknown func(interface{})) {  // ERROR "unknown does not escape"
+	x := 1 // ERROR "moved to heap: NAME-x"
+	unknown(&x) // ERROR "&x escapes to heap"
+}
+
+func foo118(unknown func(*int)) {  // ERROR "unknown does not escape"
+	x := 1 // ERROR "moved to heap: NAME-x"
+	unknown(&x) // ERROR "&x escapes to heap"
+}
+
+func external(*int)
+
+func foo119(x *int) {  // ERROR "leaking param: NAME-x"
+	external(x)
+}
+
+func foo120() {
+	// formerly exponential time analysis
+L1:
+L2:
+L3:
+L4:
+L5:
+L6:
+L7:
+L8:
+L9:
+L10:
+L11:
+L12:
+L13:
+L14:
+L15:
+L16:
+L17:
+L18:
+L19:
+L20:
+L21:
+L22:
+L23:
+L24:
+L25:
+L26:
+L27:
+L28:
+L29:
+L30:
+L31:
+L32:
+L33:
+L34:
+L35:
+L36:
+L37:
+L38:
+L39:
+L40:
+L41:
+L42:
+L43:
+L44:
+L45:
+L46:
+L47:
+L48:
+L49:
+L50:
+L51:
+L52:
+L53:
+L54:
+L55:
+L56:
+L57:
+L58:
+L59:
+L60:
+L61:
+L62:
+L63:
+L64:
+L65:
+L66:
+L67:
+L68:
+L69:
+L70:
+L71:
+L72:
+L73:
+L74:
+L75:
+L76:
+L77:
+L78:
+L79:
+L80:
+L81:
+L82:
+L83:
+L84:
+L85:
+L86:
+L87:
+L88:
+L89:
+L90:
+L91:
+L92:
+L93:
+L94:
+L95:
+L96:
+L97:
+L98:
+L99:
+L100:
+	// use the labels to silence compiler errors
+	goto L1
+	goto L2
+	goto L3
+	goto L4
+	goto L5
+	goto L6
+	goto L7
+	goto L8
+	goto L9
+	goto L10
+	goto L11
+	goto L12
+	goto L13
+	goto L14
+	goto L15
+	goto L16
+	goto L17
+	goto L18
+	goto L19
+	goto L20
+	goto L21
+	goto L22
+	goto L23
+	goto L24
+	goto L25
+	goto L26
+	goto L27
+	goto L28
+	goto L29
+	goto L30
+	goto L31
+	goto L32
+	goto L33
+	goto L34
+	goto L35
+	goto L36
+	goto L37
+	goto L38
+	goto L39
+	goto L40
+	goto L41
+	goto L42
+	goto L43
+	goto L44
+	goto L45
+	goto L46
+	goto L47
+	goto L48
+	goto L49
+	goto L50
+	goto L51
+	goto L52
+	goto L53
+	goto L54
+	goto L55
+	goto L56
+	goto L57
+	goto L58
+	goto L59
+	goto L60
+	goto L61
+	goto L62
+	goto L63
+	goto L64
+	goto L65
+	goto L66
+	goto L67
+	goto L68
+	goto L69
+	goto L70
+	goto L71
+	goto L72
+	goto L73
+	goto L74
+	goto L75
+	goto L76
+	goto L77
+	goto L78
+	goto L79
+	goto L80
+	goto L81
+	goto L82
+	goto L83
+	goto L84
+	goto L85
+	goto L86
+	goto L87
+	goto L88
+	goto L89
+	goto L90
+	goto L91
+	goto L92
+	goto L93
+	goto L94
+	goto L95
+	goto L96
+	goto L97
+	goto L98
+	goto L99
+	goto L100
+}
diff --git a/test/escape3.go b/test/escape3.go
new file mode 100644
index 0000000..fc2d6eb
--- /dev/null
+++ b/test/escape3.go
@@ -0,0 +1,36 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+// Test run-time behavior of escape analysis-related optimizations.
+
+package main
+
+func main() {
+	test1()
+}
+
+func test1() {
+	check1(0)
+	check1(1)
+	check1(2)
+}
+
+type T1 struct {
+	X, Y, Z int
+}
+
+func f() int {
+	return 1
+}
+
+func check1(pass int) T1 {
+	v := []T1{{X: f(), Z: f()}}
+	if v[0].Y != 0 {
+		panic("nonzero init")
+	}
+	v[0].Y = pass
+	return v[0]
+}
diff --git a/test/fixedbugs/bug366.go b/test/fixedbugs/bug366.go
new file mode 100644
index 0000000..8c000f5
--- /dev/null
+++ b/test/fixedbugs/bug366.go
@@ -0,0 +1,37 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+// Issue 2206.  Incorrect sign extension of div arguments.
+
+package main
+
+func five(x int64) {
+	if x != 5 {
+		panic(x)
+	}
+}
+
+func main() {
+       // 5
+       five(int64(5 / (5 / 3)))
+
+       // 5
+       five(int64(byte(5) / (byte(5) / byte(3))))
+
+       // 5
+       var a, b byte = 5, 3
+       five(int64(a / (a / b)))
+       
+       // integer divide by zero in golang.org sandbox
+       // 0 on windows/amd64
+       x := [3]byte{2, 3, 5}
+       five(int64(x[2] / (x[2] / x[1])))
+
+       // integer divide by zero in golang.org sandbox
+       // crash on windows/amd64
+       y := x[1:3]
+       five(int64(y[1] / (y[1] / y[0])))
+}
\ No newline at end of file
diff --git a/test/golden.out b/test/golden.out
index 655ceda..624b211 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -4,19 +4,15 @@
 =========== ./cmp2.go
 panic: runtime error: comparing uncomparable type []int
 
-
 =========== ./cmp3.go
 panic: runtime error: comparing uncomparable type []int
 
-
 =========== ./cmp4.go
 panic: runtime error: hash of unhashable type []int
 
-
 =========== ./cmp5.go
 panic: runtime error: hash of unhashable type []int
 
-
 =========== ./deferprint.go
 printing: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
@@ -46,9 +42,6 @@ hello, world
 =========== ./sigchld.go
 survived SIGCHLD
 
-=========== ./sinit.go
-FAIL
-
 =========== ./turing.go
 Hello World!
 
@@ -115,11 +108,9 @@ PASS
 =========== interface/fail.go
 panic: interface conversion: *main.S is not main.I: missing method Foo
 
-
 =========== interface/returntype.go
 panic: interface conversion: *main.S is not main.I2: missing method Name
 
-
 == nilptr/
 
 == syntax/
@@ -154,12 +145,10 @@ M
 =========== fixedbugs/bug113.go
 panic: interface conversion: interface is int, not int32
 
-
 =========== fixedbugs/bug148.go
 2 3
 panic: interface conversion: interface is main.T, not main.T
 
-
 =========== fixedbugs/bug328.go
 0x0
 
diff --git a/test/hashmap.go b/test/hashmap.go
old mode 100755
new mode 100644
diff --git a/test/run b/test/run
index bc31d2f..d6f5727 100755
--- a/test/run
+++ b/test/run
@@ -31,7 +31,7 @@ unset GREP_OPTIONS	# in case user has a non-standard set
 
 failed=0
 
-PATH=/bin:/usr/bin:/usr/local/bin:${GOBIN:-$GOROOT/bin}:`pwd`
+PATH=${GOBIN:-$GOROOT/bin}:`pwd`:/bin:/usr/bin:/usr/local/bin
 
 RUNFILE="/tmp/gorun-$$-$USER"
 TMP1FILE="/tmp/gotest1-$$-$USER"
diff --git a/test/sinit.go b/test/sinit.go
index 2adb931..5cd3a45 100644
--- a/test/sinit.go
+++ b/test/sinit.go
@@ -1,4 +1,4 @@
-// $G -S $D/$F.go | egrep initdone >/dev/null && echo FAIL || true
+// $G -S $D/$F.go | egrep initdone >/dev/null && echo BUG sinit || true
 
 // Copyright 2010 The Go Authors.  All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -9,45 +9,45 @@ package p
 // Should be no init func in the assembly.
 // All these initializations should be done at link time.
 
-type	S	struct{ a,b,c int }
-type	SS	struct{ aa,bb,cc S }
-type	SA	struct{ a,b,c [3]int }
-type	SC	struct{ a,b,c []int }
+type S struct{ a, b, c int }
+type SS struct{ aa, bb, cc S }
+type SA struct{ a, b, c [3]int }
+type SC struct{ a, b, c []int }
 
 var (
-	zero = 2
-	one = 1
-	pi = 3.14
-	slice = []byte{1,2,3}
-	sliceInt = []int{1,2,3}
-	hello = "hello, world"
-	bytes = []byte("hello, world")
-	four, five = 4, 5
-	x, y = 0.1, "hello"
-	nilslice []byte = nil
-	nilmap map[string]int = nil
-	nilfunc func() = nil
-	nilchan chan int = nil
-	nilptr *byte = nil
+	zero                      = 2
+	one                       = 1
+	pi                        = 3.14
+	slice                     = []byte{1, 2, 3}
+	sliceInt                  = []int{1, 2, 3}
+	hello                     = "hello, world"
+	bytes                     = []byte("hello, world")
+	four, five                = 4, 5
+	x, y                      = 0.1, "hello"
+	nilslice   []byte         = nil
+	nilmap     map[string]int = nil
+	nilfunc    func()         = nil
+	nilchan    chan int       = nil
+	nilptr     *byte          = nil
 )
 
-var	a	= [3]int{1001, 1002, 1003}
-var	s	= S{1101, 1102, 1103}
-var	c	= []int{1201, 1202, 1203}
+var a = [3]int{1001, 1002, 1003}
+var s = S{1101, 1102, 1103}
+var c = []int{1201, 1202, 1203}
 
-var	aa	= [3][3]int{[3]int{2001,2002,2003}, [3]int{2004,2005,2006}, [3]int{2007,2008,2009}}
-var	as	= [3]S{S{2101,2102,2103},S{2104,2105,2106},S{2107,2108,2109}}
-var	ac	= [3][]int{[]int{2201,2202,2203}, []int{2204,2205,2206}, []int{2207,2208,2209}}
+var aa = [3][3]int{[3]int{2001, 2002, 2003}, [3]int{2004, 2005, 2006}, [3]int{2007, 2008, 2009}}
+var as = [3]S{S{2101, 2102, 2103}, S{2104, 2105, 2106}, S{2107, 2108, 2109}}
+var ac = [3][]int{[]int{2201, 2202, 2203}, []int{2204, 2205, 2206}, []int{2207, 2208, 2209}}
 
-var	sa	= SA{[3]int{3001,3002,3003},[3]int{3004,3005,3006},[3]int{3007,3008,3009}}
-var	ss	= SS{S{3101,3102,3103},S{3104,3105,3106},S{3107,3108,3109}}
-var	sc	= SC{[]int{3201,3202,3203},[]int{3204,3205,3206},[]int{3207,3208,3209}}
+var sa = SA{[3]int{3001, 3002, 3003}, [3]int{3004, 3005, 3006}, [3]int{3007, 3008, 3009}}
+var ss = SS{S{3101, 3102, 3103}, S{3104, 3105, 3106}, S{3107, 3108, 3109}}
+var sc = SC{[]int{3201, 3202, 3203}, []int{3204, 3205, 3206}, []int{3207, 3208, 3209}}
 
-var	ca	= [][3]int{[3]int{4001,4002,4003}, [3]int{4004,4005,4006}, [3]int{4007,4008,4009}}
-var	cs	= []S{S{4101,4102,4103},S{4104,4105,4106},S{4107,4108,4109}}
-var	cc	= [][]int{[]int{4201,4202,4203}, []int{4204,4205,4206}, []int{4207,4208,4209}}
+var ca = [][3]int{[3]int{4001, 4002, 4003}, [3]int{4004, 4005, 4006}, [3]int{4007, 4008, 4009}}
+var cs = []S{S{4101, 4102, 4103}, S{4104, 4105, 4106}, S{4107, 4108, 4109}}
+var cc = [][]int{[]int{4201, 4202, 4203}, []int{4204, 4205, 4206}, []int{4207, 4208, 4209}}
 
-var	answers	= [...]int {
+var answers = [...]int{
 	// s
 	1101, 1102, 1103,
 
@@ -98,3 +98,158 @@ var	answers	= [...]int {
 	2008, 2208, 2308, 4008, 4208, 4308, 5008, 5208, 5308,
 	2009, 2209, 2309, 4009, 4209, 4309, 5009, 5209, 5309,
 }
+
+var (
+	copy_zero = zero
+	copy_one = one
+	copy_pi = pi
+	copy_slice = slice
+	copy_sliceInt = sliceInt
+	copy_hello = hello
+	copy_bytes = bytes
+	copy_four, copy_five = four, five
+	copy_x, copy_y = x, y
+	copy_nilslice = nilslice
+	copy_nilmap = nilmap
+	copy_nilfunc = nilfunc
+	copy_nilchan = nilchan
+	copy_nilptr = nilptr
+)
+
+var copy_a = a
+var copy_s = s
+var copy_c = c
+
+var copy_aa = aa
+var copy_as = as
+var copy_ac = ac
+
+var copy_sa = sa
+var copy_ss = ss
+var copy_sc = sc
+
+var copy_ca = ca
+var copy_cs = cs
+var copy_cc = cc
+
+var copy_answers = answers
+
+var bx bool
+var b0 = false
+var b1 = true
+
+var fx float32
+var f0 = float32(0)
+var f1 = float32(1)
+
+var gx float64
+var g0 = float64(0)
+var g1 = float64(1)
+
+var ix int
+var i0 = 0
+var i1 = 1
+
+var jx uint
+var j0 = uint(0)
+var j1 = uint(1)
+
+var cx complex64
+var c0 = complex64(0)
+var c1 = complex64(1)
+
+var dx complex128
+var d0 = complex128(0)
+var d1 = complex128(1)
+
+var sx []int
+var s0 = []int{0, 0, 0}
+var s1 = []int{1, 2, 3}
+
+func fi() int
+
+var ax [10]int
+var a0 = [10]int{0, 0, 0}
+var a1 = [10]int{1, 2, 3, 4}
+
+type T struct{ X, Y int }
+
+var tx T
+var t0 = T{}
+var t0a = T{0, 0}
+var t0b = T{X: 0}
+var t1 = T{X: 1, Y: 2}
+var t1a = T{3, 4}
+
+var psx *[]int
+var ps0 = &[]int{0, 0, 0}
+var ps1 = &[]int{1, 2, 3}
+
+var pax *[10]int
+var pa0 = &[10]int{0, 0, 0}
+var pa1 = &[10]int{1, 2, 3}
+
+var ptx *T
+var pt0 = &T{}
+var pt0a = &T{0, 0}
+var pt0b = &T{X: 0}
+var pt1 = &T{X: 1, Y: 2}
+var pt1a = &T{3, 4}
+
+var copy_bx = bx
+var copy_b0 = b0
+var copy_b1 = b1
+
+var copy_fx = fx
+var copy_f0 = f0
+var copy_f1 = f1
+
+var copy_gx = gx
+var copy_g0 = g0
+var copy_g1 = g1
+
+var copy_ix = ix
+var copy_i0 = i0
+var copy_i1 = i1
+
+var copy_jx = jx
+var copy_j0 = j0
+var copy_j1 = j1
+
+var copy_cx = cx
+var copy_c0 = c0
+var copy_c1 = c1
+
+var copy_dx = dx
+var copy_d0 = d0
+var copy_d1 = d1
+
+var copy_sx = sx
+var copy_s0 = s0
+var copy_s1 = s1
+
+var copy_ax = ax
+var copy_a0 = a0
+var copy_a1 = a1
+
+var copy_tx = tx
+var copy_t0 = t0
+var copy_t0a = t0a
+var copy_t0b = t0b
+var copy_t1 = t1
+var copy_t1a = t1a
+
+var copy_psx = psx
+var copy_ps0 = ps0
+var copy_ps1 = ps1
+
+var copy_pax = pax
+var copy_pa0 = pa0
+var copy_pa1 = pa1
+
+var copy_ptx = ptx
+var copy_pt0 = pt0
+var copy_pt0a = pt0a
+var copy_pt0b = pt0b
+var copy_pt1 = pt1
+var copy_pt1a = pt1a
diff --git a/test/struct0.go b/test/struct0.go
new file mode 100644
index 0000000..2398c41
--- /dev/null
+++ b/test/struct0.go
@@ -0,0 +1,34 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+// zero length structs.
+// used to not be evaluated.
+// issue 2232.
+
+package main
+
+func recv(c chan interface{}) struct{} {
+	return (<-c).(struct{})
+}
+
+var m = make(map[interface{}]int)
+
+func recv1(c chan interface{}) {
+	defer rec()
+	m[(<-c).(struct{})] = 0
+}
+
+func rec() {
+	recover()
+}
+
+func main() {
+	c := make(chan interface{})
+	go recv(c)
+	c <- struct{}{}
+	go recv1(c)
+	c <- struct{}{}
+}
diff --git a/test/typeswitch2.go b/test/typeswitch2.go
index f8fe396..57c5a18 100644
--- a/test/typeswitch2.go
+++ b/test/typeswitch2.go
@@ -18,11 +18,27 @@ func whatis(x interface{}) string {
 		return "Reader1"
 	case io.Reader: // ERROR "duplicate"
 		return "Reader2"
-	case interface { r(); w() }:
+	case interface {
+		r()
+		w()
+	}:
 		return "rw"
-	case interface { w(); r() }:	// ERROR "duplicate"
+	case interface {
+		w()
+		r()
+	}: // ERROR "duplicate"
 		return "wr"
-	
+
 	}
 	return ""
 }
+
+func notused(x interface{}) {
+	// The first t is in a different scope than the 2nd t; it cannot
+	// be accessed (=> declared and not used error); but it is legal
+	// to declare it.
+	switch t := 0; t := x.(type) { // ERROR "declared and not used"
+	case int:
+		_ = t // this is using the t of "t := x.(type)"
+	}
+}

-- 
Packaging for Google Go



More information about the Pkg-google-commits mailing list