]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libgo: Update to weekly.2011-12-02.
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 13 Dec 2011 19:16:27 +0000 (19:16 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 13 Dec 2011 19:16:27 +0000 (19:16 +0000)
From-SVN: r182295

234 files changed:
gcc/testsuite/go.test/test/fixedbugs/bug257.go
gcc/testsuite/go.test/test/initsyscall.go
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/archive/tar/common.go
libgo/go/archive/tar/reader.go
libgo/go/archive/tar/reader_test.go
libgo/go/archive/tar/writer.go
libgo/go/archive/tar/writer_test.go
libgo/go/archive/zip/reader.go
libgo/go/archive/zip/reader_test.go
libgo/go/archive/zip/struct.go
libgo/go/bytes/bytes_test.go
libgo/go/compress/gzip/gunzip.go
libgo/go/compress/gzip/gzip.go
libgo/go/compress/gzip/gzip_test.go
libgo/go/crypto/bcrypt/bcrypt.go
libgo/go/crypto/ecdsa/ecdsa_test.go
libgo/go/crypto/hmac/hmac.go
libgo/go/crypto/hmac/hmac_test.go
libgo/go/crypto/md4/md4.go
libgo/go/crypto/md4/md4_test.go
libgo/go/crypto/md5/md5.go
libgo/go/crypto/md5/md5_test.go
libgo/go/crypto/ocsp/ocsp.go
libgo/go/crypto/ocsp/ocsp_test.go
libgo/go/crypto/openpgp/canonical_text.go
libgo/go/crypto/openpgp/canonical_text_test.go
libgo/go/crypto/openpgp/keys.go
libgo/go/crypto/openpgp/packet/private_key.go
libgo/go/crypto/openpgp/packet/private_key_test.go
libgo/go/crypto/openpgp/packet/public_key.go
libgo/go/crypto/openpgp/packet/public_key_test.go
libgo/go/crypto/openpgp/packet/signature.go
libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go
libgo/go/crypto/openpgp/s2k/s2k.go
libgo/go/crypto/openpgp/write.go
libgo/go/crypto/openpgp/write_test.go
libgo/go/crypto/rand/rand_unix.go
libgo/go/crypto/ripemd160/ripemd160.go
libgo/go/crypto/ripemd160/ripemd160_test.go
libgo/go/crypto/rsa/pkcs1v15_test.go
libgo/go/crypto/rsa/rsa.go
libgo/go/crypto/sha1/sha1.go
libgo/go/crypto/sha1/sha1_test.go
libgo/go/crypto/sha256/sha256.go
libgo/go/crypto/sha256/sha256_test.go
libgo/go/crypto/sha512/sha512.go
libgo/go/crypto/sha512/sha512_test.go
libgo/go/crypto/tls/cipher_suites.go
libgo/go/crypto/tls/common.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/handshake_server.go
libgo/go/crypto/tls/handshake_server_test.go
libgo/go/crypto/tls/key_agreement.go
libgo/go/crypto/tls/prf.go
libgo/go/crypto/tls/root_unix.go
libgo/go/crypto/tls/root_windows.go
libgo/go/crypto/tls/tls.go
libgo/go/crypto/x509/cert_pool.go
libgo/go/crypto/x509/pkcs8.go [new file with mode: 0644]
libgo/go/crypto/x509/pkcs8_test.go [new file with mode: 0644]
libgo/go/crypto/x509/pkix/pkix.go
libgo/go/crypto/x509/verify.go
libgo/go/crypto/x509/verify_test.go
libgo/go/crypto/x509/x509.go
libgo/go/crypto/x509/x509_test.go
libgo/go/encoding/asn1/asn1.go
libgo/go/encoding/asn1/asn1_test.go
libgo/go/encoding/asn1/marshal.go
libgo/go/encoding/asn1/marshal_test.go
libgo/go/encoding/json/encode.go
libgo/go/encoding/xml/xml.go
libgo/go/encoding/xml/xml_test.go
libgo/go/exp/gotype/gotype.go
libgo/go/exp/gui/gui.go [deleted file]
libgo/go/exp/gui/x11/auth.go [deleted file]
libgo/go/exp/gui/x11/conn.go [deleted file]
libgo/go/exp/sql/driver/driver.go
libgo/go/exp/sql/fakedb_test.go
libgo/go/exp/sql/sql.go
libgo/go/exp/sql/sql_test.go
libgo/go/exp/ssh/channel.go
libgo/go/exp/ssh/client.go
libgo/go/exp/ssh/client_auth_test.go
libgo/go/exp/ssh/common.go
libgo/go/exp/ssh/common_test.go [new file with mode: 0644]
libgo/go/exp/ssh/doc.go
libgo/go/exp/ssh/server.go
libgo/go/exp/ssh/session.go
libgo/go/exp/ssh/session_test.go [new file with mode: 0644]
libgo/go/exp/ssh/tcpip.go
libgo/go/exp/ssh/tcpip_func_test.go [new file with mode: 0644]
libgo/go/exp/ssh/transport.go
libgo/go/exp/types/check_test.go
libgo/go/exp/types/gcimporter.go
libgo/go/exp/types/gcimporter_test.go
libgo/go/fmt/fmt_test.go
libgo/go/fmt/print.go
libgo/go/go/ast/resolve.go
libgo/go/go/build/build.go
libgo/go/go/build/dir.go
libgo/go/go/build/path.go
libgo/go/go/doc/comment.go
libgo/go/go/doc/comment_test.go [new file with mode: 0644]
libgo/go/go/doc/headscan.go [new file with mode: 0644]
libgo/go/go/parser/interface.go
libgo/go/go/parser/parser_test.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/performance_test.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/printer_test.go
libgo/go/hash/adler32/adler32.go
libgo/go/hash/crc32/crc32.go
libgo/go/hash/crc64/crc64.go
libgo/go/hash/fnv/fnv.go
libgo/go/hash/fnv/fnv_test.go
libgo/go/hash/hash.go
libgo/go/html/doctype.go [new file with mode: 0644]
libgo/go/html/parse.go
libgo/go/html/parse_test.go
libgo/go/html/render.go
libgo/go/html/template/clone_test.go
libgo/go/html/template/content.go
libgo/go/html/template/doc.go
libgo/go/html/template/escape.go
libgo/go/html/template/escape_test.go
libgo/go/html/template/template.go
libgo/go/html/token.go
libgo/go/html/token_test.go
libgo/go/io/ioutil/ioutil.go
libgo/go/io/ioutil/ioutil_test.go
libgo/go/io/ioutil/tempfile.go
libgo/go/io/multi_test.go
libgo/go/log/log.go
libgo/go/math/abs.go
libgo/go/math/asinh.go
libgo/go/math/big/calibrate_test.go
libgo/go/math/big/int_test.go
libgo/go/math/big/nat.go
libgo/go/math/big/nat_test.go
libgo/go/math/cbrt.go
libgo/go/math/floor.go
libgo/go/math/gamma.go
libgo/go/math/log1p.go
libgo/go/math/modf.go
libgo/go/math/sincos.go
libgo/go/net/dnsclient_unix.go
libgo/go/net/fd.go
libgo/go/net/fd_windows.go
libgo/go/net/hosts.go
libgo/go/net/http/cgi/host_test.go
libgo/go/net/http/cookie.go
libgo/go/net/http/cookie_test.go
libgo/go/net/http/export_test.go
libgo/go/net/http/fcgi/child.go
libgo/go/net/http/fs.go
libgo/go/net/http/fs_test.go
libgo/go/net/http/httptest/server.go
libgo/go/net/http/httputil/reverseproxy.go
libgo/go/net/http/pprof/pprof.go
libgo/go/net/http/serve_test.go
libgo/go/net/http/server.go
libgo/go/net/http/sniff.go
libgo/go/net/http/sniff_test.go
libgo/go/net/http/transport_test.go
libgo/go/net/mail/message.go
libgo/go/net/mail/message_test.go
libgo/go/net/timeout_test.go
libgo/go/old/netchan/common.go
libgo/go/old/netchan/export.go
libgo/go/old/netchan/import.go
libgo/go/os/exec/lp_unix.go
libgo/go/os/exec/lp_windows.go
libgo/go/os/export_test.go [new file with mode: 0644]
libgo/go/os/file.go
libgo/go/os/file_posix.go
libgo/go/os/file_unix.go
libgo/go/os/getwd.go
libgo/go/os/os_test.go
libgo/go/os/os_unix_test.go [new file with mode: 0644]
libgo/go/os/path.go
libgo/go/os/stat.go
libgo/go/os/stat_openbsd.go
libgo/go/os/types.go
libgo/go/os/user/user_test.go
libgo/go/patch/git.go
libgo/go/path/filepath/match.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/strings/strings.go
libgo/go/strings/strings_test.go
libgo/go/testing/benchmark.go
libgo/go/testing/example.go
libgo/go/testing/testing.go
libgo/go/text/template/doc.go
libgo/go/text/template/exec.go
libgo/go/text/template/exec_test.go
libgo/go/text/template/funcs.go
libgo/go/text/template/helper.go
libgo/go/text/template/multi_test.go [new file with mode: 0644]
libgo/go/text/template/parse.go [deleted file]
libgo/go/text/template/parse/parse.go
libgo/go/text/template/parse/parse_test.go
libgo/go/text/template/parse/set.go [deleted file]
libgo/go/text/template/set.go [deleted file]
libgo/go/text/template/set_test.go [deleted file]
libgo/go/text/template/template.go [new file with mode: 0644]
libgo/go/text/template/testdata/tmpl1.tmpl
libgo/go/text/template/testdata/tmpl2.tmpl
libgo/go/time/example_test.go [new file with mode: 0644]
libgo/go/time/format.go
libgo/go/time/internal_test.go
libgo/go/time/sleep.go
libgo/go/time/sleep_test.go
libgo/go/time/sys.go
libgo/go/time/sys_unix.go
libgo/go/time/tick.go
libgo/go/time/tick_test.go
libgo/go/time/time.go
libgo/go/time/time_test.go
libgo/go/time/zoneinfo.go [new file with mode: 0644]
libgo/go/time/zoneinfo_plan9.go
libgo/go/time/zoneinfo_posix.go [deleted file]
libgo/go/time/zoneinfo_unix.go
libgo/go/time/zoneinfo_windows.go
libgo/go/websocket/client.go
libgo/go/websocket/hixie.go
libgo/go/websocket/hybi.go
libgo/go/websocket/server.go
libgo/runtime/go-nanotime.c
libgo/runtime/go-now.c [new file with mode: 0644]
libgo/runtime/time.goc

index 713c42481f8442860c5cba0ea92dc05638d3ceff..1b32475003a80872c391510dfac2c1a75a8f069c 100644 (file)
@@ -20047,11 +20047,10 @@ var gettysburg = "  Four score and seven years ago our fathers brought forth on\
        "\n" +
        "Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania\n"
 
-
 func main() {
        m := md5.New()
        io.WriteString(m, data)
-       hash := fmt.Sprintf("%x", m.Sum())
+       hash := fmt.Sprintf("%x", m.Sum(nil))
        if hash != "525f06bc62a65017cd2217d7584e5920" {
                println("BUG a", hash)
                return
@@ -20059,7 +20058,7 @@ func main() {
 
        m = md5.New()
        io.WriteString(m, gettysburg)
-       hash = fmt.Sprintf("%x", m.Sum())
+       hash = fmt.Sprintf("%x", m.Sum(nil))
        if hash != "d7ec5d9d47a4d166091e8d9ebd7ea0aa" {
                println("BUG gettysburg", hash)
                println(len(gettysburg))
index b5e5812b64b3ed7bd8a84479ff044dcf67b4f76f..d0c26d2a83730909d9368c56689694ae1a48112e 100644 (file)
@@ -19,9 +19,8 @@ func f() {
 
 func init() {
        go f()
-       time.Nanoseconds()
+       time.Now()
 }
 
 func main() {
 }
-
index f62ea21e57c1d9e231bb3e113b023e7a9e95d664..9847f4715a5c4b1b08458903488fc9a86893cdc9 100644 (file)
@@ -1,4 +1,4 @@
-b4a91b693374
+0beb796b4ef8
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 9701bada7cef70e251b192c04cf3b76cd1e1fbc6..3db823e8e1a6af3e15dec70ee8899ae6e6fbf026 100644 (file)
@@ -233,7 +233,6 @@ toolexeclibgoexpdir = $(toolexeclibgodir)/exp
 
 toolexeclibgoexp_DATA = \
        exp/ebnf.gox \
-       exp/gui.gox \
        $(exp_inotify_gox) \
        exp/norm.gox \
        exp/spdy.gox \
@@ -242,11 +241,6 @@ toolexeclibgoexp_DATA = \
        exp/terminal.gox \
        exp/types.gox
 
-toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui
-
-toolexeclibgoexpgui_DATA = \
-       exp/gui/x11.gox
-
 toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql
 
 toolexeclibgoexpsql_DATA = \
@@ -447,6 +441,7 @@ runtime_files = \
        runtime/go-map-len.c \
        runtime/go-map-range.c \
        runtime/go-nanotime.c \
+       runtime/go-now.c \
        runtime/go-new-map.c \
        runtime/go-new.c \
        runtime/go-panic.c \
@@ -576,6 +571,7 @@ go_hash_files = \
 go_html_files = \
        go/html/const.go \
        go/html/doc.go \
+       go/html/doctype.go \
        go/html/entity.go \
        go/html/escape.go \
        go/html/node.go \
@@ -888,7 +884,7 @@ go_time_files = \
        go/time/sys_unix.go \
        go/time/tick.go \
        go/time/time.go \
-       go/time/zoneinfo_posix.go \
+       go/time/zoneinfo.go \
        go/time/zoneinfo_unix.go
 
 go_unicode_files = \
@@ -1038,6 +1034,7 @@ go_crypto_twofish_files = \
 go_crypto_x509_files = \
        go/crypto/x509/cert_pool.go \
        go/crypto/x509/pkcs1.go \
+       go/crypto/x509/pkcs8.go \
        go/crypto/x509/verify.go \
        go/crypto/x509/x509.go
 go_crypto_xtea_files = \
@@ -1135,8 +1132,6 @@ go_encoding_xml_files = \
 go_exp_ebnf_files = \
        go/exp/ebnf/ebnf.go \
        go/exp/ebnf/parser.go
-go_exp_gui_files = \
-       go/exp/gui/gui.go
 go_exp_inotify_files = \
        go/exp/inotify/inotify_linux.go
 go_exp_norm_files = \
@@ -1178,10 +1173,6 @@ go_exp_types_files = \
        go/exp/types/types.go \
        go/exp/types/universe.go
 
-go_exp_gui_x11_files = \
-       go/exp/gui/x11/auth.go \
-       go/exp/gui/x11/conn.go
-
 go_exp_sql_driver_files = \
        go/exp/sql/driver/driver.go \
        go/exp/sql/driver/types.go
@@ -1415,13 +1406,11 @@ go_text_template_files = \
        go/text/template/exec.go \
        go/text/template/funcs.go \
        go/text/template/helper.go \
-       go/text/template/parse.go \
-       go/text/template/set.go
+       go/text/template/template.go
 go_text_template_parse_files = \
        go/text/template/parse/lex.go \
        go/text/template/parse/node.go \
-       go/text/template/parse/parse.go \
-       go/text/template/parse/set.go
+       go/text/template/parse/parse.go
 
 go_sync_atomic_files = \
        go/sync/atomic/doc.go
@@ -1725,14 +1714,12 @@ libgo_go_objs = \
        encoding/pem.lo \
        encoding/xml.lo \
        exp/ebnf.lo \
-       exp/gui.lo \
        exp/norm.lo \
        exp/spdy.lo \
        exp/sql.lo \
        exp/ssh.lo \
        exp/terminal.lo \
        exp/types.lo \
-       exp/gui/x11.lo \
        exp/sql/driver.lo \
        html/template.lo \
        go/ast.lo \
@@ -2784,16 +2771,6 @@ exp/ebnf/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: exp/ebnf/check
 
-@go_include@ exp/gui.lo.dep
-exp/gui.lo.dep: $(go_exp_gui_files)
-       $(BUILDDEPS)
-exp/gui.lo: $(go_exp_gui_files)
-       $(BUILDPACKAGE)
-exp/gui/check: $(CHECK_DEPS)
-       @$(MKDIR_P) exp/gui
-       @$(CHECK)
-.PHONY: exp/gui/check
-
 @go_include@ exp/norm.lo.dep
 exp/norm.lo.dep: $(go_exp_norm_files)
        $(BUILDDEPS)
@@ -2854,16 +2831,6 @@ exp/types/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: exp/types/check
 
-@go_include@ exp/gui/x11.lo.dep
-exp/gui/x11.lo.dep: $(go_exp_gui_x11_files)
-       $(BUILDDEPS)
-exp/gui/x11.lo: $(go_exp_gui_x11_files)
-       $(BUILDPACKAGE)
-exp/gui/x11/check: $(CHECK_DEPS)
-       @$(MKDIR_P) exp/gui/x11
-       @$(CHECK)
-.PHONY: exp/gui/x11/check
-
 @go_include@ exp/inotify.lo.dep
 exp/inotify.lo.dep: $(go_exp_inotify_files)
        $(BUILDDEPS)
@@ -3686,8 +3653,6 @@ encoding/xml.gox: encoding/xml.lo
 
 exp/ebnf.gox: exp/ebnf.lo
        $(BUILDGOX)
-exp/gui.gox: exp/gui.lo
-       $(BUILDGOX)
 exp/inotify.gox: exp/inotify.lo
        $(BUILDGOX)
 exp/norm.gox: exp/norm.lo
@@ -3703,9 +3668,6 @@ exp/terminal.gox: exp/terminal.lo
 exp/types.gox: exp/types.lo
        $(BUILDGOX)
 
-exp/gui/x11.gox: exp/gui/x11.lo
-       $(BUILDGOX)
-
 exp/sql/driver.gox: exp/sql/driver.lo
        $(BUILDGOX)
 
@@ -3950,6 +3912,7 @@ TEST_PACKAGES = \
        html/template/check \
        go/ast/check \
        $(go_build_check_omitted_since_it_calls_6g) \
+       go/doc/check \
        go/parser/check \
        go/printer/check \
        go/scanner/check \
index 3d9ed7c1ca0d7905ceb3383532a109a1ed6fda53..6552074a3da22dd051cb0a5377b2622c413dc604 100644 (file)
@@ -102,7 +102,6 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
        "$(DESTDIR)$(toolexeclibgodebugdir)" \
        "$(DESTDIR)$(toolexeclibgoencodingdir)" \
        "$(DESTDIR)$(toolexeclibgoexpdir)" \
-       "$(DESTDIR)$(toolexeclibgoexpguidir)" \
        "$(DESTDIR)$(toolexeclibgoexpsqldir)" \
        "$(DESTDIR)$(toolexeclibgogodir)" \
        "$(DESTDIR)$(toolexeclibgohashdir)" \
@@ -161,15 +160,15 @@ am__DEPENDENCIES_2 = bufio/bufio.lo bytes/bytes.lo bytes/index.lo \
        encoding/base64.lo encoding/binary.lo encoding/csv.lo \
        encoding/git85.lo encoding/gob.lo encoding/hex.lo \
        encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \
-       exp/gui.lo exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo \
-       exp/terminal.lo exp/types.lo exp/gui/x11.lo exp/sql/driver.lo \
-       html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
-       go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
-       hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
-       net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
-       net/http/pprof.lo image/bmp.lo image/color.lo image/draw.lo \
-       image/gif.lo image/jpeg.lo image/png.lo image/tiff.lo \
-       image/ycbcr.lo index/suffixarray.lo io/ioutil.lo log/syslog.lo \
+       exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo exp/terminal.lo \
+       exp/types.lo exp/sql/driver.lo html/template.lo go/ast.lo \
+       go/build.lo go/doc.lo go/parser.lo go/printer.lo go/scanner.lo \
+       go/token.lo hash/adler32.lo hash/crc32.lo hash/crc64.lo \
+       hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \
+       net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
+       image/bmp.lo image/color.lo image/draw.lo image/gif.lo \
+       image/jpeg.lo image/png.lo image/tiff.lo image/ycbcr.lo \
+       index/suffixarray.lo io/ioutil.lo log/syslog.lo \
        log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
        mime/mime.lo mime/multipart.lo net/dict.lo net/http.lo \
        net/mail.lo net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
@@ -200,12 +199,12 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
        runtime/go-interface-val-compare.c runtime/go-make-slice.c \
        runtime/go-map-delete.c runtime/go-map-index.c \
        runtime/go-map-len.c runtime/go-map-range.c \
-       runtime/go-nanotime.c runtime/go-new-map.c runtime/go-new.c \
-       runtime/go-panic.c runtime/go-print.c runtime/go-recover.c \
-       runtime/go-reflect.c runtime/go-reflect-call.c \
-       runtime/go-reflect-map.c runtime/go-rune.c \
-       runtime/go-runtime-error.c runtime/go-setenv.c \
-       runtime/go-signal.c runtime/go-strcmp.c \
+       runtime/go-nanotime.c runtime/go-now.c runtime/go-new-map.c \
+       runtime/go-new.c runtime/go-panic.c runtime/go-print.c \
+       runtime/go-recover.c runtime/go-reflect.c \
+       runtime/go-reflect-call.c runtime/go-reflect-map.c \
+       runtime/go-rune.c runtime/go-runtime-error.c \
+       runtime/go-setenv.c runtime/go-signal.c runtime/go-strcmp.c \
        runtime/go-string-to-byte-array.c \
        runtime/go-string-to-int-array.c runtime/go-strplus.c \
        runtime/go-strslice.c runtime/go-trampoline.c \
@@ -238,20 +237,20 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \
        go-interface-compare.lo go-interface-eface-compare.lo \
        go-interface-val-compare.lo go-make-slice.lo go-map-delete.lo \
        go-map-index.lo go-map-len.lo go-map-range.lo go-nanotime.lo \
-       go-new-map.lo go-new.lo go-panic.lo go-print.lo go-recover.lo \
-       go-reflect.lo go-reflect-call.lo go-reflect-map.lo go-rune.lo \
-       go-runtime-error.lo go-setenv.lo go-signal.lo go-strcmp.lo \
-       go-string-to-byte-array.lo go-string-to-int-array.lo \
-       go-strplus.lo go-strslice.lo go-trampoline.lo go-type-eface.lo \
-       go-type-error.lo go-type-identity.lo go-type-interface.lo \
-       go-type-string.lo go-typedesc-equal.lo go-typestring.lo \
-       go-unreflect.lo go-unsafe-new.lo go-unsafe-newarray.lo \
-       go-unsafe-pointer.lo go-unwind.lo chan.lo cpuprof.lo \
-       $(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
-       mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo proc.lo \
-       runtime.lo thread.lo yield.lo $(am__objects_3) iface.lo \
-       malloc.lo map.lo mprof.lo reflect.lo runtime1.lo sema.lo \
-       sigqueue.lo string.lo time.lo
+       go-now.lo go-new-map.lo go-new.lo go-panic.lo go-print.lo \
+       go-recover.lo go-reflect.lo go-reflect-call.lo \
+       go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \
+       go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \
+       go-string-to-int-array.lo go-strplus.lo go-strslice.lo \
+       go-trampoline.lo go-type-eface.lo go-type-error.lo \
+       go-type-identity.lo go-type-interface.lo go-type-string.lo \
+       go-typedesc-equal.lo go-typestring.lo go-unreflect.lo \
+       go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \
+       go-unwind.lo chan.lo cpuprof.lo $(am__objects_1) mcache.lo \
+       mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \
+       mheap.lo msize.lo proc.lo runtime.lo thread.lo yield.lo \
+       $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \
+       runtime1.lo sema.lo sigqueue.lo string.lo time.lo
 am_libgo_la_OBJECTS = $(am__objects_4)
 libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
 libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@@ -290,18 +289,18 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
        $(toolexeclibgocrypto_DATA) $(toolexeclibgocryptoopenpgp_DATA) \
        $(toolexeclibgocryptox509_DATA) $(toolexeclibgodebug_DATA) \
        $(toolexeclibgoencoding_DATA) $(toolexeclibgoexp_DATA) \
-       $(toolexeclibgoexpgui_DATA) $(toolexeclibgoexpsql_DATA) \
-       $(toolexeclibgogo_DATA) $(toolexeclibgohash_DATA) \
-       $(toolexeclibgohtml_DATA) $(toolexeclibgoimage_DATA) \
-       $(toolexeclibgoindex_DATA) $(toolexeclibgoio_DATA) \
-       $(toolexeclibgolog_DATA) $(toolexeclibgomath_DATA) \
-       $(toolexeclibgomime_DATA) $(toolexeclibgonet_DATA) \
-       $(toolexeclibgonethttp_DATA) $(toolexeclibgonetrpc_DATA) \
-       $(toolexeclibgoold_DATA) $(toolexeclibgoos_DATA) \
-       $(toolexeclibgopath_DATA) $(toolexeclibgoregexp_DATA) \
-       $(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \
-       $(toolexeclibgotesting_DATA) $(toolexeclibgotext_DATA) \
-       $(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA)
+       $(toolexeclibgoexpsql_DATA) $(toolexeclibgogo_DATA) \
+       $(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \
+       $(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \
+       $(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \
+       $(toolexeclibgomath_DATA) $(toolexeclibgomime_DATA) \
+       $(toolexeclibgonet_DATA) $(toolexeclibgonethttp_DATA) \
+       $(toolexeclibgonetrpc_DATA) $(toolexeclibgoold_DATA) \
+       $(toolexeclibgoos_DATA) $(toolexeclibgopath_DATA) \
+       $(toolexeclibgoregexp_DATA) $(toolexeclibgoruntime_DATA) \
+       $(toolexeclibgosync_DATA) $(toolexeclibgotesting_DATA) \
+       $(toolexeclibgotext_DATA) $(toolexeclibgotexttemplate_DATA) \
+       $(toolexeclibgounicode_DATA)
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive        \
   distclean-recursive maintainer-clean-recursive
 AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
@@ -690,7 +689,6 @@ toolexeclibgoencoding_DATA = \
 toolexeclibgoexpdir = $(toolexeclibgodir)/exp
 toolexeclibgoexp_DATA = \
        exp/ebnf.gox \
-       exp/gui.gox \
        $(exp_inotify_gox) \
        exp/norm.gox \
        exp/spdy.gox \
@@ -699,10 +697,6 @@ toolexeclibgoexp_DATA = \
        exp/terminal.gox \
        exp/types.gox
 
-toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui
-toolexeclibgoexpgui_DATA = \
-       exp/gui/x11.gox
-
 toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql
 toolexeclibgoexpsql_DATA = \
        exp/sql/driver.gox
@@ -868,6 +862,7 @@ runtime_files = \
        runtime/go-map-len.c \
        runtime/go-map-range.c \
        runtime/go-nanotime.c \
+       runtime/go-now.c \
        runtime/go-new-map.c \
        runtime/go-new.c \
        runtime/go-panic.c \
@@ -960,6 +955,7 @@ go_hash_files = \
 go_html_files = \
        go/html/const.go \
        go/html/doc.go \
+       go/html/doctype.go \
        go/html/entity.go \
        go/html/escape.go \
        go/html/node.go \
@@ -1204,7 +1200,7 @@ go_time_files = \
        go/time/sys_unix.go \
        go/time/tick.go \
        go/time/time.go \
-       go/time/zoneinfo_posix.go \
+       go/time/zoneinfo.go \
        go/time/zoneinfo_unix.go
 
 go_unicode_files = \
@@ -1377,6 +1373,7 @@ go_crypto_twofish_files = \
 go_crypto_x509_files = \
        go/crypto/x509/cert_pool.go \
        go/crypto/x509/pkcs1.go \
+       go/crypto/x509/pkcs8.go \
        go/crypto/x509/verify.go \
        go/crypto/x509/x509.go
 
@@ -1495,9 +1492,6 @@ go_exp_ebnf_files = \
        go/exp/ebnf/ebnf.go \
        go/exp/ebnf/parser.go
 
-go_exp_gui_files = \
-       go/exp/gui/gui.go
-
 go_exp_inotify_files = \
        go/exp/inotify/inotify_linux.go
 
@@ -1545,10 +1539,6 @@ go_exp_types_files = \
        go/exp/types/types.go \
        go/exp/types/universe.go
 
-go_exp_gui_x11_files = \
-       go/exp/gui/x11/auth.go \
-       go/exp/gui/x11/conn.go
-
 go_exp_sql_driver_files = \
        go/exp/sql/driver/driver.go \
        go/exp/sql/driver/types.go
@@ -1805,14 +1795,12 @@ go_text_template_files = \
        go/text/template/exec.go \
        go/text/template/funcs.go \
        go/text/template/helper.go \
-       go/text/template/parse.go \
-       go/text/template/set.go
+       go/text/template/template.go
 
 go_text_template_parse_files = \
        go/text/template/parse/lex.go \
        go/text/template/parse/node.go \
-       go/text/template/parse/parse.go \
-       go/text/template/parse/set.go
+       go/text/template/parse/parse.go
 
 go_sync_atomic_files = \
        go/sync/atomic/doc.go
@@ -2016,14 +2004,12 @@ libgo_go_objs = \
        encoding/pem.lo \
        encoding/xml.lo \
        exp/ebnf.lo \
-       exp/gui.lo \
        exp/norm.lo \
        exp/spdy.lo \
        exp/sql.lo \
        exp/ssh.lo \
        exp/terminal.lo \
        exp/types.lo \
-       exp/gui/x11.lo \
        exp/sql/driver.lo \
        html/template.lo \
        go/ast.lo \
@@ -2295,6 +2281,7 @@ TEST_PACKAGES = \
        html/template/check \
        go/ast/check \
        $(go_build_check_omitted_since_it_calls_6g) \
+       go/doc/check \
        go/parser/check \
        go/printer/check \
        go/scanner/check \
@@ -2511,6 +2498,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-nanotime.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new-map.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-now.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-panic.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-print.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-recover.Plo@am__quote@
@@ -2799,6 +2787,13 @@ go-nanotime.lo: runtime/go-nanotime.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-nanotime.lo `test -f 'runtime/go-nanotime.c' || echo '$(srcdir)/'`runtime/go-nanotime.c
 
+go-now.lo: runtime/go-now.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-now.lo -MD -MP -MF $(DEPDIR)/go-now.Tpo -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/go-now.Tpo $(DEPDIR)/go-now.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='runtime/go-now.c' object='go-now.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c
+
 go-new-map.lo: runtime/go-new-map.c
 @am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-new-map.lo -MD -MP -MF $(DEPDIR)/go-new-map.Tpo -c -o go-new-map.lo `test -f 'runtime/go-new-map.c' || echo '$(srcdir)/'`runtime/go-new-map.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/go-new-map.Tpo $(DEPDIR)/go-new-map.Plo
@@ -3374,26 +3369,6 @@ uninstall-toolexeclibgoexpDATA:
        test -n "$$files" || exit 0; \
        echo " ( cd '$(DESTDIR)$(toolexeclibgoexpdir)' && rm -f" $$files ")"; \
        cd "$(DESTDIR)$(toolexeclibgoexpdir)" && rm -f $$files
-install-toolexeclibgoexpguiDATA: $(toolexeclibgoexpgui_DATA)
-       @$(NORMAL_INSTALL)
-       test -z "$(toolexeclibgoexpguidir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpguidir)"
-       @list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \
-       for p in $$list; do \
-         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-         echo "$$d$$p"; \
-       done | $(am__base_list) | \
-       while read files; do \
-         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgoexpguidir)'"; \
-         $(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexpguidir)" || exit $$?; \
-       done
-
-uninstall-toolexeclibgoexpguiDATA:
-       @$(NORMAL_UNINSTALL)
-       @list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \
-       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-       test -n "$$files" || exit 0; \
-       echo " ( cd '$(DESTDIR)$(toolexeclibgoexpguidir)' && rm -f" $$files ")"; \
-       cd "$(DESTDIR)$(toolexeclibgoexpguidir)" && rm -f $$files
 install-toolexeclibgoexpsqlDATA: $(toolexeclibgoexpsql_DATA)
        @$(NORMAL_INSTALL)
        test -z "$(toolexeclibgoexpsqldir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpsqldir)"
@@ -4171,7 +4146,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \
                config.h
 installdirs: installdirs-recursive
 installdirs-am:
-       for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpguidir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
+       for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
          test -z "$$dir" || $(MKDIR_P) "$$dir"; \
        done
 install: install-recursive
@@ -4241,7 +4216,6 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \
        install-toolexeclibgocryptox509DATA \
        install-toolexeclibgodebugDATA \
        install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
-       install-toolexeclibgoexpguiDATA \
        install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \
        install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \
        install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \
@@ -4307,7 +4281,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
        uninstall-toolexeclibgodebugDATA \
        uninstall-toolexeclibgoencodingDATA \
        uninstall-toolexeclibgoexpDATA \
-       uninstall-toolexeclibgoexpguiDATA \
        uninstall-toolexeclibgoexpsqlDATA \
        uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
        uninstall-toolexeclibgohtmlDATA \
@@ -4355,7 +4328,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
        install-toolexeclibgocryptox509DATA \
        install-toolexeclibgodebugDATA \
        install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
-       install-toolexeclibgoexpguiDATA \
        install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \
        install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \
        install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \
@@ -4385,7 +4357,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
        uninstall-toolexeclibgodebugDATA \
        uninstall-toolexeclibgoencodingDATA \
        uninstall-toolexeclibgoexpDATA \
-       uninstall-toolexeclibgoexpguiDATA \
        uninstall-toolexeclibgoexpsqlDATA \
        uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
        uninstall-toolexeclibgohtmlDATA \
@@ -5383,16 +5354,6 @@ exp/ebnf/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: exp/ebnf/check
 
-@go_include@ exp/gui.lo.dep
-exp/gui.lo.dep: $(go_exp_gui_files)
-       $(BUILDDEPS)
-exp/gui.lo: $(go_exp_gui_files)
-       $(BUILDPACKAGE)
-exp/gui/check: $(CHECK_DEPS)
-       @$(MKDIR_P) exp/gui
-       @$(CHECK)
-.PHONY: exp/gui/check
-
 @go_include@ exp/norm.lo.dep
 exp/norm.lo.dep: $(go_exp_norm_files)
        $(BUILDDEPS)
@@ -5453,16 +5414,6 @@ exp/types/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: exp/types/check
 
-@go_include@ exp/gui/x11.lo.dep
-exp/gui/x11.lo.dep: $(go_exp_gui_x11_files)
-       $(BUILDDEPS)
-exp/gui/x11.lo: $(go_exp_gui_x11_files)
-       $(BUILDPACKAGE)
-exp/gui/x11/check: $(CHECK_DEPS)
-       @$(MKDIR_P) exp/gui/x11
-       @$(CHECK)
-.PHONY: exp/gui/x11/check
-
 @go_include@ exp/inotify.lo.dep
 exp/inotify.lo.dep: $(go_exp_inotify_files)
        $(BUILDDEPS)
@@ -6280,8 +6231,6 @@ encoding/xml.gox: encoding/xml.lo
 
 exp/ebnf.gox: exp/ebnf.lo
        $(BUILDGOX)
-exp/gui.gox: exp/gui.lo
-       $(BUILDGOX)
 exp/inotify.gox: exp/inotify.lo
        $(BUILDGOX)
 exp/norm.gox: exp/norm.lo
@@ -6297,9 +6246,6 @@ exp/terminal.gox: exp/terminal.lo
 exp/types.gox: exp/types.lo
        $(BUILDGOX)
 
-exp/gui/x11.gox: exp/gui/x11.lo
-       $(BUILDGOX)
-
 exp/sql/driver.gox: exp/sql/driver.lo
        $(BUILDGOX)
 
index 67355086a639af3286692360ace5a4c727d74a58..fc7a40923cd2a918f22e116184597b96560d76b6 100644 (file)
 //   http://www.gnu.org/software/tar/manual/html_node/Standard.html
 package tar
 
+import "time"
+
 const (
        blockSize = 512
 
        // Types
-       TypeReg           = '0'    // regular file.
-       TypeRegA          = '\x00' // regular file.
-       TypeLink          = '1'    // hard link.
-       TypeSymlink       = '2'    // symbolic link.
-       TypeChar          = '3'    // character device node.
-       TypeBlock         = '4'    // block device node.
-       TypeDir           = '5'    // directory.
-       TypeFifo          = '6'    // fifo node.
-       TypeCont          = '7'    // reserved.
-       TypeXHeader       = 'x'    // extended header.
-       TypeXGlobalHeader = 'g'    // global extended header.
+       TypeReg           = '0'    // regular file
+       TypeRegA          = '\x00' // regular file
+       TypeLink          = '1'    // hard link
+       TypeSymlink       = '2'    // symbolic link
+       TypeChar          = '3'    // character device node
+       TypeBlock         = '4'    // block device node
+       TypeDir           = '5'    // directory
+       TypeFifo          = '6'    // fifo node
+       TypeCont          = '7'    // reserved
+       TypeXHeader       = 'x'    // extended header
+       TypeXGlobalHeader = 'g'    // global extended header
 )
 
 // A Header represents a single header in a tar archive.
 // Some fields may not be populated.
 type Header struct {
-       Name     string // name of header file entry.
-       Mode     int64  // permission and mode bits.
-       Uid      int    // user id of owner.
-       Gid      int    // group id of owner.
-       Size     int64  // length in bytes.
-       Mtime    int64  // modified time; seconds since epoch.
-       Typeflag byte   // type of header entry.
-       Linkname string // target name of link.
-       Uname    string // user name of owner.
-       Gname    string // group name of owner.
-       Devmajor int64  // major number of character or block device.
-       Devminor int64  // minor number of character or block device.
-       Atime    int64  // access time; seconds since epoch.
-       Ctime    int64  // status change time; seconds since epoch.
-
+       Name       string    // name of header file entry
+       Mode       int64     // permission and mode bits
+       Uid        int       // user id of owner
+       Gid        int       // group id of owner
+       Size       int64     // length in bytes
+       ModTime    time.Time // modified time
+       Typeflag   byte      // type of header entry
+       Linkname   string    // target name of link
+       Uname      string    // user name of owner
+       Gname      string    // group name of owner
+       Devmajor   int64     // major number of character or block device
+       Devminor   int64     // minor number of character or block device
+       AccessTime time.Time // access time
+       ChangeTime time.Time // status change time
 }
 
 var zeroBlock = make([]byte, blockSize)
index facba2cc7a3e6726ded3c9e5133bc0f1eb69de23..76955e2ec03af9e04758014404799627a3a4f925 100644 (file)
@@ -14,6 +14,7 @@ import (
        "io/ioutil"
        "os"
        "strconv"
+       "time"
 )
 
 var (
@@ -141,7 +142,7 @@ func (tr *Reader) readHeader() *Header {
        hdr.Uid = int(tr.octal(s.next(8)))
        hdr.Gid = int(tr.octal(s.next(8)))
        hdr.Size = tr.octal(s.next(12))
-       hdr.Mtime = tr.octal(s.next(12))
+       hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
        s.next(8) // chksum
        hdr.Typeflag = s.next(1)[0]
        hdr.Linkname = cString(s.next(100))
@@ -178,8 +179,8 @@ func (tr *Reader) readHeader() *Header {
                        prefix = cString(s.next(155))
                case "star":
                        prefix = cString(s.next(131))
-                       hdr.Atime = tr.octal(s.next(12))
-                       hdr.Ctime = tr.octal(s.next(12))
+                       hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0)
+                       hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0)
                }
                if len(prefix) > 0 {
                        hdr.Name = prefix + "/" + hdr.Name
index 00eea6b62d7fbffac00e503e596055eb87640347..5ca4212ae7b1361dc5477c4236aa96c14baf44bb 100644 (file)
@@ -12,6 +12,7 @@ import (
        "os"
        "reflect"
        "testing"
+       "time"
 )
 
 type untarTest struct {
@@ -29,7 +30,7 @@ var gnuTarTest = &untarTest{
                        Uid:      73025,
                        Gid:      5000,
                        Size:     5,
-                       Mtime:    1244428340,
+                       ModTime:  time.Unix(1244428340, 0),
                        Typeflag: '0',
                        Uname:    "dsymonds",
                        Gname:    "eng",
@@ -40,7 +41,7 @@ var gnuTarTest = &untarTest{
                        Uid:      73025,
                        Gid:      5000,
                        Size:     11,
-                       Mtime:    1244436044,
+                       ModTime:  time.Unix(1244436044, 0),
                        Typeflag: '0',
                        Uname:    "dsymonds",
                        Gname:    "eng",
@@ -58,30 +59,30 @@ var untarTests = []*untarTest{
                file: "testdata/star.tar",
                headers: []*Header{
                        &Header{
-                               Name:     "small.txt",
-                               Mode:     0640,
-                               Uid:      73025,
-                               Gid:      5000,
-                               Size:     5,
-                               Mtime:    1244592783,
-                               Typeflag: '0',
-                               Uname:    "dsymonds",
-                               Gname:    "eng",
-                               Atime:    1244592783,
-                               Ctime:    1244592783,
+                               Name:       "small.txt",
+                               Mode:       0640,
+                               Uid:        73025,
+                               Gid:        5000,
+                               Size:       5,
+                               ModTime:    time.Unix(1244592783, 0),
+                               Typeflag:   '0',
+                               Uname:      "dsymonds",
+                               Gname:      "eng",
+                               AccessTime: time.Unix(1244592783, 0),
+                               ChangeTime: time.Unix(1244592783, 0),
                        },
                        &Header{
-                               Name:     "small2.txt",
-                               Mode:     0640,
-                               Uid:      73025,
-                               Gid:      5000,
-                               Size:     11,
-                               Mtime:    1244592783,
-                               Typeflag: '0',
-                               Uname:    "dsymonds",
-                               Gname:    "eng",
-                               Atime:    1244592783,
-                               Ctime:    1244592783,
+                               Name:       "small2.txt",
+                               Mode:       0640,
+                               Uid:        73025,
+                               Gid:        5000,
+                               Size:       11,
+                               ModTime:    time.Unix(1244592783, 0),
+                               Typeflag:   '0',
+                               Uname:      "dsymonds",
+                               Gname:      "eng",
+                               AccessTime: time.Unix(1244592783, 0),
+                               ChangeTime: time.Unix(1244592783, 0),
                        },
                },
        },
@@ -94,7 +95,7 @@ var untarTests = []*untarTest{
                                Uid:      73025,
                                Gid:      5000,
                                Size:     5,
-                               Mtime:    1244593104,
+                               ModTime:  time.Unix(1244593104, 0),
                                Typeflag: '\x00',
                        },
                        &Header{
@@ -103,7 +104,7 @@ var untarTests = []*untarTest{
                                Uid:      73025,
                                Gid:      5000,
                                Size:     11,
-                               Mtime:    1244593104,
+                               ModTime:  time.Unix(1244593104, 0),
                                Typeflag: '\x00',
                        },
                },
@@ -221,7 +222,7 @@ func TestIncrementalRead(t *testing.T) {
                        h.Write(rdbuf[0:nr])
                }
                // verify checksum
-               have := fmt.Sprintf("%x", h.Sum())
+               have := fmt.Sprintf("%x", h.Sum(nil))
                want := cksums[nread]
                if want != have {
                        t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
index 222df90782ca4812783257bb6834218cf75f02f5..b9310b3f189dc080833d367727a96de97ecfdfe0 100644 (file)
@@ -127,19 +127,19 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
        // TODO(dsymonds): handle names longer than 100 chars
        copy(s.next(100), []byte(hdr.Name))
 
-       tw.octal(s.next(8), hdr.Mode)          // 100:108
-       tw.numeric(s.next(8), int64(hdr.Uid))  // 108:116
-       tw.numeric(s.next(8), int64(hdr.Gid))  // 116:124
-       tw.numeric(s.next(12), hdr.Size)       // 124:136
-       tw.numeric(s.next(12), hdr.Mtime)      // 136:148
-       s.next(8)                              // chksum (148:156)
-       s.next(1)[0] = hdr.Typeflag            // 156:157
-       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
-       tw.numeric(s.next(8), hdr.Devmajor)    // 329:337
-       tw.numeric(s.next(8), hdr.Devminor)    // 337:345
+       tw.octal(s.next(8), hdr.Mode)              // 100:108
+       tw.numeric(s.next(8), int64(hdr.Uid))      // 108:116
+       tw.numeric(s.next(8), int64(hdr.Gid))      // 116:124
+       tw.numeric(s.next(12), hdr.Size)           // 124:136
+       tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
+       s.next(8)                                  // chksum (148:156)
+       s.next(1)[0] = hdr.Typeflag                // 156:157
+       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
+       tw.numeric(s.next(8), hdr.Devmajor)        // 329:337
+       tw.numeric(s.next(8), hdr.Devminor)        // 337:345
 
        // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
        if tw.usedBinary {
index 6cc93868820eb37386d43094cd8fabbc2df6afad..8d7ed32d32e4e2862759411fffe4e4c9bd7c528b 100644 (file)
@@ -11,6 +11,7 @@ import (
        "io/ioutil"
        "testing"
        "testing/iotest"
+       "time"
 )
 
 type writerTestEntry struct {
@@ -38,7 +39,7 @@ var writerTests = []*writerTest{
                                        Uid:      73025,
                                        Gid:      5000,
                                        Size:     5,
-                                       Mtime:    1246508266,
+                                       ModTime:  time.Unix(1246508266, 0),
                                        Typeflag: '0',
                                        Uname:    "dsymonds",
                                        Gname:    "eng",
@@ -52,7 +53,7 @@ var writerTests = []*writerTest{
                                        Uid:      73025,
                                        Gid:      5000,
                                        Size:     11,
-                                       Mtime:    1245217492,
+                                       ModTime:  time.Unix(1245217492, 0),
                                        Typeflag: '0',
                                        Uname:    "dsymonds",
                                        Gname:    "eng",
@@ -66,7 +67,7 @@ var writerTests = []*writerTest{
                                        Uid:      1000,
                                        Gid:      1000,
                                        Size:     0,
-                                       Mtime:    1314603082,
+                                       ModTime:  time.Unix(1314603082, 0),
                                        Typeflag: '2',
                                        Linkname: "small.txt",
                                        Uname:    "strings",
@@ -89,7 +90,7 @@ var writerTests = []*writerTest{
                                        Uid:      73025,
                                        Gid:      5000,
                                        Size:     16 << 30,
-                                       Mtime:    1254699560,
+                                       ModTime:  time.Unix(1254699560, 0),
                                        Typeflag: '0',
                                        Uname:    "dsymonds",
                                        Gname:    "eng",
index cfbe5498a157995258cd13129bbb0b8ec113ef85..4365009a308f386a843c3eb1f6fddcdad9f1aa0f 100644 (file)
@@ -56,7 +56,7 @@ func OpenReader(name string) (*ReadCloser, error) {
                return nil, err
        }
        r := new(ReadCloser)
-       if err := r.init(f, fi.Size); err != nil {
+       if err := r.init(f, fi.Size()); err != nil {
                f.Close()
                return nil, err
        }
index ca0b04e2bba397ad64ab32913b0d446885d6ae37..8c0ecaa4386c1af86c16913e1082984f6875e947 100644 (file)
@@ -164,8 +164,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
                t.Error(err)
                return
        }
-       if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want {
-               t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
+       if ft := f.ModTime(); !ft.Equal(mtime) {
+               t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)
        }
 
        testFileMode(t, f, ft.Mode)
index b862b5a6acb4ce51448fb73ea12278a188d0d896..43c04bb27b2c4b3bee60d3de3815f62e209b5109 100644 (file)
@@ -11,8 +11,10 @@ This package does not support ZIP64 or disk spanning.
 */
 package zip
 
-import "errors"
-import "time"
+import (
+       "errors"
+       "time"
+)
 
 // Compression methods.
 const (
@@ -74,24 +76,26 @@ func recoverError(errp *error) {
 // The resolution is 2s.
 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
-       return time.Time{
+       return time.Date(
                // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
-               Year:  int64(dosDate>>9 + 1980),
-               Month: int(dosDate >> 5 & 0xf),
-               Day:   int(dosDate & 0x1f),
+               int(dosDate>>9+1980),
+               time.Month(dosDate>>5&0xf),
+               int(dosDate&0x1f),
 
                // time bits 0-4: second/2; 5-10: minute; 11-15: hour
-               Hour:   int(dosTime >> 11),
-               Minute: int(dosTime >> 5 & 0x3f),
-               Second: int(dosTime & 0x1f * 2),
-       }
+               int(dosTime>>11),
+               int(dosTime>>5&0x3f),
+               int(dosTime&0x1f*2),
+               0, // nanoseconds
+
+               time.UTC,
+       )
 }
 
-// Mtime_ns returns the modified time in ns since epoch.
+// ModTime returns the modification time.
 // The resolution is 2s.
-func (h *FileHeader) Mtime_ns() int64 {
-       t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
-       return t.Seconds() * 1e9
+func (h *FileHeader) ModTime() time.Time {
+       return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
 }
 
 // Mode returns the permission and mode bits for the FileHeader.
index 21a1a4f5808fee29a7fdc28bd19588a8ebcaa4ed..829ef05319c41a0555b2b8cf9eec068a8412f368 100644 (file)
@@ -702,7 +702,7 @@ func TestTrim(t *testing.T) {
                case "TrimRight":
                        f = TrimRight
                default:
-                       t.Error("Undefined trim function %s", name)
+                       t.Errorf("Undefined trim function %s", name)
                }
                actual := string(f([]byte(tc.in), tc.cutset))
                if actual != tc.out {
index a23e515e0e05cb7726d3b3d4c9433366f9f37b78..7c78b9e366d637dea2129c2f652c7a8c65307dc7 100644 (file)
@@ -13,6 +13,7 @@ import (
        "hash"
        "hash/crc32"
        "io"
+       "time"
 )
 
 // BUG(nigeltao): Comments and Names don't properly map UTF-8 character codes outside of
@@ -42,11 +43,11 @@ var ChecksumError = errors.New("gzip checksum error")
 // The gzip file stores a header giving metadata about the compressed file.
 // That header is exposed as the fields of the Compressor and Decompressor structs.
 type Header struct {
-       Comment string // comment
-       Extra   []byte // "extra data"
-       Mtime   uint32 // modification time (seconds since January 1, 1970)
-       Name    string // file name
-       OS      byte   // operating system type
+       Comment string    // comment
+       Extra   []byte    // "extra data"
+       ModTime time.Time // modification time
+       Name    string    // file name
+       OS      byte      // operating system type
 }
 
 // An Decompressor is an io.Reader that can be read to retrieve
@@ -130,7 +131,7 @@ func (z *Decompressor) readHeader(save bool) error {
        }
        z.flg = z.buf[3]
        if save {
-               z.Mtime = get4(z.buf[4:8])
+               z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0)
                // z.buf[8] is xfl, ignored
                z.OS = z.buf[9]
        }
index 94b0f1f85e26c5342eab359e9e304802d670dba5..07b91b66823b31b707b1d5c98511dbfe2710be97 100644 (file)
@@ -122,7 +122,7 @@ func (z *Compressor) Write(p []byte) (int, error) {
                if z.Comment != "" {
                        z.buf[3] |= 0x10
                }
-               put4(z.buf[4:8], z.Mtime)
+               put4(z.buf[4:8], uint32(z.ModTime.Unix()))
                if z.level == BestCompression {
                        z.buf[8] = 2
                } else if z.level == BestSpeed {
index 121e627e6b234f98de3ae0b867adde1fd3522aea..815825be99940e0466ac662a59315de1a91c25ea 100644 (file)
@@ -8,6 +8,7 @@ import (
        "io"
        "io/ioutil"
        "testing"
+       "time"
 )
 
 // pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the
@@ -53,7 +54,7 @@ func TestWriter(t *testing.T) {
                func(compressor *Compressor) {
                        compressor.Comment = "comment"
                        compressor.Extra = []byte("extra")
-                       compressor.Mtime = 1e8
+                       compressor.ModTime = time.Unix(1e8, 0)
                        compressor.Name = "name"
                        _, err := compressor.Write([]byte("payload"))
                        if err != nil {
@@ -74,8 +75,8 @@ func TestWriter(t *testing.T) {
                        if string(decompressor.Extra) != "extra" {
                                t.Fatalf("extra is %q, want %q", decompressor.Extra, "extra")
                        }
-                       if decompressor.Mtime != 1e8 {
-                               t.Fatalf("mtime is %d, want %d", decompressor.Mtime, uint32(1e8))
+                       if decompressor.ModTime.Unix() != 1e8 {
+                               t.Fatalf("mtime is %d, want %d", decompressor.ModTime.Unix(), uint32(1e8))
                        }
                        if decompressor.Name != "name" {
                                t.Fatalf("name is %q, want %q", decompressor.Name, "name")
index 9740135622849210e8e3db8d588bcdeec65fd9cc..362b2eb53cb23a1cee45e671a0e744549cb25d75 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package bcrypt implements Provos and Mazières's bcrypt adapative hashing
+// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
 // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
 package bcrypt
 
index 22360b5708c53e5ab34beed4a9d47c3465200271..45433e102033cd8c4f97781afb6cc6eb673b762f 100644 (file)
@@ -214,7 +214,7 @@ func TestVectors(t *testing.T) {
                msg, _ := hex.DecodeString(test.msg)
                sha.Reset()
                sha.Write(msg)
-               hashed := sha.Sum()
+               hashed := sha.Sum(nil)
                r := fromHex(test.r)
                s := fromHex(test.s)
                if Verify(&pub, hashed, r, s) != test.ok {
index 6a17bbd44fa1451f8652b483c9e5712d752836cc..deaceafb260a39f4caf04a220dd551d64582cba7 100644 (file)
@@ -48,15 +48,15 @@ func (h *hmac) tmpPad(xor byte) {
        }
 }
 
-func (h *hmac) Sum() []byte {
-       sum := h.inner.Sum()
+func (h *hmac) Sum(in []byte) []byte {
+       sum := h.inner.Sum(nil)
        h.tmpPad(0x5c)
        for i, b := range sum {
                h.tmp[padSize+i] = b
        }
        h.outer.Reset()
        h.outer.Write(h.tmp)
-       return h.outer.Sum()
+       return h.outer.Sum(in)
 }
 
 func (h *hmac) Write(p []byte) (n int, err error) {
@@ -81,7 +81,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
        if len(key) > padSize {
                // If key is too big, hash it.
                hm.outer.Write(key)
-               key = hm.outer.Sum()
+               key = hm.outer.Sum(nil)
        }
        hm.key = make([]byte, len(key))
        copy(hm.key, key)
index 03431c92f759be2393fdfc738ea8e82896b046d7..eac254b9d1910737d2dd84623bc52245d9c58669 100644 (file)
@@ -192,7 +192,7 @@ func TestHMAC(t *testing.T) {
 
                        // Repetitive Sum() calls should return the same value
                        for k := 0; k < 2; k++ {
-                               sum := fmt.Sprintf("%x", h.Sum())
+                               sum := fmt.Sprintf("%x", h.Sum(nil))
                                if sum != tt.out {
                                        t.Errorf("test %d.%d.%d: have %s want %s\n", i, j, k, sum, tt.out)
                                }
index f21cc51a21a0522d6dfc67cf37ddaa3163e7d38e..e51e8bee50cff4eb2082e1ddcf235f3a08ab9938 100644 (file)
@@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0, so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 16)
-       j := 0
        for _, s := range d.s {
-               p[j+0] = byte(s >> 0)
-               p[j+1] = byte(s >> 8)
-               p[j+2] = byte(s >> 16)
-               p[j+3] = byte(s >> 24)
-               j += 4
+               in = append(in, byte(s>>0))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>24))
        }
-       return p
+       return in
 }
index 721bd4cbcc84841a76859e3b9f962eb7c6d8b780..b56edd7875d8b6655cc07ccec0f65d8171d74680 100644 (file)
@@ -58,10 +58,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
index 20f3a1b6f7580f8167bfac1420d711f6cd03cac6..182cfb8537077383e6bdbc200032ad6262870b28 100644 (file)
@@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0 so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 16)
-       j := 0
        for _, s := range d.s {
-               p[j+0] = byte(s >> 0)
-               p[j+1] = byte(s >> 8)
-               p[j+2] = byte(s >> 16)
-               p[j+3] = byte(s >> 24)
-               j += 4
+               in = append(in, byte(s>>0))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>24))
        }
-       return p
+       return in
 }
index 857002b701334e99f8abd22b65372d6a3375f206..b15e4668c3219eb0e8ded352956afb2a71675cf3 100644 (file)
@@ -58,10 +58,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("md5[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
index a04b5bd713513892a80fff02e526da760f77c399..b9dfdf94e319a9567610c8c3868203d83e19bbf5 100644 (file)
@@ -61,7 +61,7 @@ type responseData struct {
        Version       int              `asn1:"optional,default:1,explicit,tag:0"`
        RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
        KeyHash       []byte           `asn1:"optional,explicit,tag:2"`
-       ProducedAt    *time.Time
+       ProducedAt    time.Time
        Responses     []singleResponse
 }
 
@@ -70,12 +70,12 @@ type singleResponse struct {
        Good       asn1.Flag   `asn1:"explicit,tag:0,optional"`
        Revoked    revokedInfo `asn1:"explicit,tag:1,optional"`
        Unknown    asn1.Flag   `asn1:"explicit,tag:2,optional"`
-       ThisUpdate *time.Time
-       NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
+       ThisUpdate time.Time
+       NextUpdate time.Time `asn1:"explicit,tag:0,optional"`
 }
 
 type revokedInfo struct {
-       RevocationTime *time.Time
+       RevocationTime time.Time
        Reason         int `asn1:"explicit,tag:0,optional"`
 }
 
@@ -97,7 +97,7 @@ type Response struct {
        // Status is one of {Good, Revoked, Unknown, ServerFailed}
        Status                                        int
        SerialNumber                                  []byte
-       ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time
+       ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
        RevocationReason                              int
        Certificate                                   *x509.Certificate
 }
@@ -161,7 +161,7 @@ func ParseResponse(bytes []byte) (*Response, error) {
 
        pub := ret.Certificate.PublicKey.(*rsa.PublicKey)
        h.Write(basicResp.TBSResponseData.Raw)
-       digest := h.Sum()
+       digest := h.Sum(nil)
        signature := basicResp.Signature.RightAlign()
 
        if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil {
index 7be37211c106f2ea176a288bfe1bcb989ed46bc7..bacca558b48772addcec41e96b65cc783d0f3b08 100644 (file)
@@ -15,7 +15,13 @@ func TestOCSPDecode(t *testing.T) {
                t.Error(err)
        }
 
-       expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}}
+       expected := Response{
+               Status:           0,
+               SerialNumber:     []byte{0x1, 0xd0, 0xfa},
+               RevocationReason: 0,
+               ThisUpdate:       time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC),
+               NextUpdate:       time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC),
+       }
 
        if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
                t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
index fe4557aafc19a943be46ce17400a531608acddc7..98cee5e75ae0950ad2702fb78eb24098981d42f0 100644 (file)
@@ -41,8 +41,8 @@ func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
        return len(buf), nil
 }
 
-func (cth *canonicalTextHash) Sum() []byte {
-       return cth.h.Sum()
+func (cth *canonicalTextHash) Sum(in []byte) []byte {
+       return cth.h.Sum(in)
 }
 
 func (cth *canonicalTextHash) Reset() {
index ae54f8c83ee0aeaae1bfceb6c4f22cb32eb50502..841475f80c0145c44f1a25301e439962587822f8 100644 (file)
@@ -17,8 +17,8 @@ func (r recordingHash) Write(b []byte) (n int, err error) {
        return r.buf.Write(b)
 }
 
-func (r recordingHash) Sum() []byte {
-       return r.buf.Bytes()
+func (r recordingHash) Sum(in []byte) []byte {
+       return append(in, r.buf.Bytes()...)
 }
 
 func (r recordingHash) Reset() {
@@ -33,7 +33,7 @@ func testCanonicalText(t *testing.T, input, expected string) {
        r := recordingHash{bytes.NewBuffer(nil)}
        c := NewCanonicalTextHash(r)
        c.Write([]byte(input))
-       result := c.Sum()
+       result := c.Sum(nil)
        if expected != string(result) {
                t.Errorf("input: %x got: %x want: %x", input, result, expected)
        }
index b705d226e1f047c9a444574a8e8f8de2de0c7730..df39970c0b672d176253b22dad1cbfb1c93adb97 100644 (file)
@@ -381,7 +381,7 @@ const defaultRSAKeyBits = 2048
 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
 // single identity composed of the given full name, comment and email, any of
 // which may be empty but must not contain any of "()<>\x00".
-func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, error) {
+func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email string) (*Entity, error) {
        uid := packet.NewUserId(name, comment, email)
        if uid == nil {
                return nil, error_.InvalidArgumentError("user id field contained invalid characters")
@@ -395,11 +395,9 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
                return nil, err
        }
 
-       t := uint32(currentTimeSecs)
-
        e := &Entity{
-               PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ),
-               PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ),
+               PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey, false /* not a subkey */ ),
+               PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv, false /* not a subkey */ ),
                Identities: make(map[string]*Identity),
        }
        isPrimaryId := true
@@ -407,7 +405,7 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
                Name:   uid.Name,
                UserId: uid,
                SelfSignature: &packet.Signature{
-                       CreationTime: t,
+                       CreationTime: currentTime,
                        SigType:      packet.SigTypePositiveCert,
                        PubKeyAlgo:   packet.PubKeyAlgoRSA,
                        Hash:         crypto.SHA256,
@@ -421,10 +419,10 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
 
        e.Subkeys = make([]Subkey, 1)
        e.Subkeys[0] = Subkey{
-               PublicKey:  packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ),
-               PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ),
+               PublicKey:  packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey, true /* is a subkey */ ),
+               PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv, true /* is a subkey */ ),
                Sig: &packet.Signature{
-                       CreationTime:              t,
+                       CreationTime:              currentTime,
                        SigType:                   packet.SigTypeSubkeyBinding,
                        PubKeyAlgo:                packet.PubKeyAlgoRSA,
                        Hash:                      crypto.SHA256,
@@ -533,7 +531,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity) error {
                SigType:      packet.SigTypeGenericCert,
                PubKeyAlgo:   signer.PrivateKey.PubKeyAlgo,
                Hash:         crypto.SHA256,
-               CreationTime: uint32(time.Seconds()),
+               CreationTime: time.Now(),
                IssuerKeyId:  &signer.PrivateKey.KeyId,
        }
        if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil {
index c0ff82b4135fcd107d0cfa78745a6fe4f954302c..d67e968861758a183fa60784b05ff4cb2a18f87e 100644 (file)
@@ -17,6 +17,7 @@ import (
        "io/ioutil"
        "math/big"
        "strconv"
+       "time"
 )
 
 // PrivateKey represents a possibly encrypted private key. See RFC 4880,
@@ -32,9 +33,9 @@ type PrivateKey struct {
        iv            []byte
 }
 
-func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey {
+func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey {
        pk := new(PrivateKey)
-       pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey)
+       pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey, isSubkey)
        pk.PrivateKey = priv
        return pk
 }
@@ -99,13 +100,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
 }
 
 func mod64kHash(d []byte) uint16 {
-       h := uint16(0)
-       for i := 0; i < len(d); i += 2 {
-               v := uint16(d[i]) << 8
-               if i+1 < len(d) {
-                       v += uint16(d[i+1])
-               }
-               h += v
+       var h uint16
+       for _, b := range d {
+               h += uint16(b)
        }
        return h
 }
@@ -195,7 +192,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
                }
                h := sha1.New()
                h.Write(data[:len(data)-sha1.Size])
-               sum := h.Sum()
+               sum := h.Sum(nil)
                if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
                        return error_.StructuralError("private key checksum failure")
                }
index 60eebaa6b094dd3125e05fe701c342f24c115bb0..35d8951a86b05077a3b186796e25e0f74956e686 100644 (file)
@@ -6,19 +6,20 @@ package packet
 
 import (
        "testing"
+       "time"
 )
 
 var privateKeyTests = []struct {
        privateKeyHex string
-       creationTime  uint32
+       creationTime  time.Time
 }{
        {
                privKeyRSAHex,
-               0x4cc349a8,
+               time.Unix(0x4cc349a8, 0),
        },
        {
                privKeyElGamalHex,
-               0x4df9ee1a,
+               time.Unix(0x4df9ee1a, 0),
        },
 }
 
@@ -43,7 +44,7 @@ func TestPrivateKeyRead(t *testing.T) {
                        continue
                }
 
-               if privKey.CreationTime != test.creationTime || privKey.Encrypted {
+               if !privKey.CreationTime.Equal(test.creationTime) || privKey.Encrypted {
                        t.Errorf("#%d: bad result, got: %#v", i, privKey)
                }
        }
index 7d71dc49a7b2983718a2930b46a013591675db29..9aa30e0c15f0c85ce67ca9e8bba6c30a2ae7ba8c 100644 (file)
@@ -16,11 +16,12 @@ import (
        "io"
        "math/big"
        "strconv"
+       "time"
 )
 
 // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
 type PublicKey struct {
-       CreationTime uint32 // seconds since the epoch
+       CreationTime time.Time
        PubKeyAlgo   PublicKeyAlgorithm
        PublicKey    interface{} // Either a *rsa.PublicKey or *dsa.PublicKey
        Fingerprint  [20]byte
@@ -38,9 +39,9 @@ func fromBig(n *big.Int) parsedMPI {
 }
 
 // NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
-func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey {
+func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey, isSubkey bool) *PublicKey {
        pk := &PublicKey{
-               CreationTime: creationTimeSecs,
+               CreationTime: creationTime,
                PubKeyAlgo:   PubKeyAlgoRSA,
                PublicKey:    pub,
                IsSubkey:     isSubkey,
@@ -62,7 +63,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
        if buf[0] != 4 {
                return error_.UnsupportedError("public key version")
        }
-       pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4])
+       pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
        pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
        switch pk.PubKeyAlgo {
        case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
@@ -87,7 +88,7 @@ func (pk *PublicKey) setFingerPrintAndKeyId() {
        fingerPrint := sha1.New()
        pk.SerializeSignaturePrefix(fingerPrint)
        pk.serializeWithoutHeaders(fingerPrint)
-       copy(pk.Fingerprint[:], fingerPrint.Sum())
+       copy(pk.Fingerprint[:], fingerPrint.Sum(nil))
        pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
 }
 
@@ -234,10 +235,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) {
 func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
        var buf [6]byte
        buf[0] = 4
-       buf[1] = byte(pk.CreationTime >> 24)
-       buf[2] = byte(pk.CreationTime >> 16)
-       buf[3] = byte(pk.CreationTime >> 8)
-       buf[4] = byte(pk.CreationTime)
+       t := uint32(pk.CreationTime.Unix())
+       buf[1] = byte(t >> 24)
+       buf[2] = byte(t >> 16)
+       buf[3] = byte(t >> 8)
+       buf[4] = byte(t)
        buf[5] = byte(pk.PubKeyAlgo)
 
        _, err = w.Write(buf[:])
@@ -269,7 +271,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
        }
 
        signed.Write(sig.HashSuffix)
-       hashBytes := signed.Sum()
+       hashBytes := signed.Sum(nil)
 
        if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
                return error_.SignatureError("hash tag doesn't match")
index 6e8bfbce66e732dbc109e71a8e3c1738d89e82e0..72f459f47bf028019844beb764ff4467cd6727f2 100644 (file)
@@ -8,19 +8,20 @@ import (
        "bytes"
        "encoding/hex"
        "testing"
+       "time"
 )
 
 var pubKeyTests = []struct {
        hexData        string
        hexFingerprint string
-       creationTime   uint32
+       creationTime   time.Time
        pubKeyAlgo     PublicKeyAlgorithm
        keyId          uint64
        keyIdString    string
        keyIdShort     string
 }{
-       {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
-       {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"},
+       {rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
+       {dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"},
 }
 
 func TestPublicKeyRead(t *testing.T) {
@@ -38,8 +39,8 @@ func TestPublicKeyRead(t *testing.T) {
                if pk.PubKeyAlgo != test.pubKeyAlgo {
                        t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo)
                }
-               if pk.CreationTime != test.creationTime {
-                       t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime)
+               if !pk.CreationTime.Equal(test.creationTime) {
+                       t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime)
                }
                expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint)
                if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) {
index 4ebb906cad72e02bc41e725959099846f5777f17..1cdc1ee0f0c798e9dc62dc3dfec5e94b3669071e 100644 (file)
@@ -15,6 +15,7 @@ import (
        "hash"
        "io"
        "strconv"
+       "time"
 )
 
 // Signature represents a signature. See RFC 4880, section 5.2.
@@ -28,7 +29,7 @@ type Signature struct {
        // HashTag contains the first two bytes of the hash for fast rejection
        // of bad signed data.
        HashTag      [2]byte
-       CreationTime uint32 // Unix epoch time
+       CreationTime time.Time
 
        RSASignature     parsedMPI
        DSASigR, DSASigS parsedMPI
@@ -151,7 +152,7 @@ func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool)
                }
        }
 
-       if sig.CreationTime == 0 {
+       if sig.CreationTime.IsZero() {
                err = error_.StructuralError("no creation time in signature")
        }
 
@@ -223,7 +224,12 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
                        err = error_.StructuralError("signature creation time not four bytes")
                        return
                }
-               sig.CreationTime = binary.BigEndian.Uint32(subpacket)
+               t := binary.BigEndian.Uint32(subpacket)
+               if t == 0 {
+                       sig.CreationTime = time.Time{}
+               } else {
+                       sig.CreationTime = time.Unix(int64(t), 0)
+               }
        case signatureExpirationSubpacket:
                // Signature expiration time, section 5.2.3.10
                if !isHashed {
@@ -417,7 +423,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
        }
 
        h.Write(sig.HashSuffix)
-       digest = h.Sum()
+       digest = h.Sum(nil)
        copy(sig.HashTag[:], digest)
        return
 }
@@ -541,10 +547,7 @@ type outputSubpacket struct {
 
 func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
        creationTime := make([]byte, 4)
-       creationTime[0] = byte(sig.CreationTime >> 24)
-       creationTime[1] = byte(sig.CreationTime >> 16)
-       creationTime[2] = byte(sig.CreationTime >> 8)
-       creationTime[3] = byte(sig.CreationTime)
+       binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
        subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
 
        if sig.IssuerKeyId != nil {
index 8225db6d2f6411a066179b84420089838e2e6e48..dff776e3eb2f0ceb9dfd881c0f11df1558af42cc 100644 (file)
@@ -201,7 +201,7 @@ func (ser *seMDCReader) Close() error {
        }
        ser.h.Write(ser.trailer[:2])
 
-       final := ser.h.Sum()
+       final := ser.h.Sum(nil)
        if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
                return error_.SignatureError("hash mismatch")
        }
@@ -227,7 +227,7 @@ func (w *seMDCWriter) Close() (err error) {
        buf[0] = mdcPacketTagByte
        buf[1] = sha1.Size
        w.h.Write(buf[:2])
-       digest := w.h.Sum()
+       digest := w.h.Sum(nil)
        copy(buf[2:], digest)
 
        _, err = w.w.Write(buf[:])
index 2a753db16bd8c6431f84387ee73f3efa23e1dec4..83673e173353caaf30ff69bae031d947f3c833f5 100644 (file)
@@ -34,7 +34,7 @@ func Salted(out []byte, h hash.Hash, in []byte, salt []byte) {
                }
                h.Write(salt)
                h.Write(in)
-               n := copy(out[done:], h.Sum())
+               n := copy(out[done:], h.Sum(nil))
                done += n
        }
 }
@@ -68,7 +68,7 @@ func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
                                written += len(combined)
                        }
                }
-               n := copy(out[done:], h.Sum())
+               n := copy(out[done:], h.Sum(nil))
                done += n
        }
 }
index 6f3450c9cdbc016cbc4cc642f9566944db8138fa..60dae01e64b2fa6da49942c3534b45f84f6674ef 100644 (file)
@@ -68,7 +68,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
        sig.SigType = sigType
        sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
        sig.Hash = crypto.SHA256
-       sig.CreationTime = uint32(time.Seconds())
+       sig.CreationTime = time.Now()
        sig.IssuerKeyId = &signer.PrivateKey.KeyId
 
        h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
@@ -95,8 +95,8 @@ type FileHints struct {
        // file should not be written to disk. It may be equal to "_CONSOLE" to
        // suggest the data should not be written to disk.
        FileName string
-       // EpochSeconds contains the modification time of the file, or 0 if not applicable.
-       EpochSeconds uint32
+       // ModTime contains the modification time of the file, or the zero time if not applicable.
+       ModTime time.Time
 }
 
 // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
@@ -115,7 +115,11 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi
        if err != nil {
                return
        }
-       return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
+       var epochSeconds uint32
+       if !hints.ModTime.IsZero() {
+               epochSeconds = uint32(hints.ModTime.Unix())
+       }
+       return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
 }
 
 // intersectPreferences mutates and returns a prefix of a that contains only
@@ -243,7 +247,11 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
                w = noOpCloser{encryptedData}
 
        }
-       literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
+       var epochSeconds uint32
+       if !hints.ModTime.IsZero() {
+               epochSeconds = uint32(hints.ModTime.Unix())
+       }
+       literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
        if err != nil {
                return nil, err
        }
@@ -275,7 +283,7 @@ func (s signatureWriter) Close() error {
                SigType:      packet.SigTypeBinary,
                PubKeyAlgo:   s.signer.PubKeyAlgo,
                Hash:         s.hashType,
-               CreationTime: uint32(time.Seconds()),
+               CreationTime: time.Now(),
                IssuerKeyId:  &s.signer.KeyId,
        }
 
index 3cadf4cc95a927174bc003452ab14967deaa0dd6..02fa5b75bff626300b2476ab5c1ab8cd1d83dd30 100644 (file)
@@ -54,7 +54,7 @@ func TestNewEntity(t *testing.T) {
                return
        }
 
-       e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com")
+       e, err := NewEntity(rand.Reader, time.Now(), "Test User", "test", "test@example.com")
        if err != nil {
                t.Errorf("failed to create entity: %s", err)
                return
index 09442ad283078be060a59242d027de62c391f5b2..d9cddf6d2ad2830d61cad1d6ece74a78cd06eb2f 100644 (file)
@@ -100,7 +100,7 @@ func (r *reader) Read(b []byte) (n int, err error) {
                // t = encrypt(time)
                // dst = encrypt(t^seed)
                // seed = encrypt(t^dst)
-               ns := time.Nanoseconds()
+               ns := time.Now().UnixNano()
                r.time[0] = byte(ns >> 56)
                r.time[1] = byte(ns >> 48)
                r.time[2] = byte(ns >> 40)
index 6ccfe875f5567a54d3d9ca7ef0ef5f228f04e64f..c128ee445a5af95eb6369262bebe8e4a693ddb23 100644 (file)
@@ -81,7 +81,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0 so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -107,11 +107,11 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 20)
-       j := 0
        for _, s := range d.s {
-               p[j], p[j+1], p[j+2], p[j+3] = byte(s), byte(s>>8), byte(s>>16), byte(s>>24)
-               j += 4
+               in = append(in, byte(s))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>24))
        }
-       return p
+       return in
 }
index f4135f5cf65970c2b6ad113951646952af52f3eb..5df1b2593d2c16aa5bef5a54cb8fd18d0d895fa7 100644 (file)
@@ -38,10 +38,10 @@ func TestVectors(t *testing.T) {
                                io.WriteString(md, tv.in)
                        } else {
                                io.WriteString(md, tv.in[0:len(tv.in)/2])
-                               md.Sum()
+                               md.Sum(nil)
                                io.WriteString(md, tv.in[len(tv.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", md.Sum())
+                       s := fmt.Sprintf("%x", md.Sum(nil))
                        if s != tv.out {
                                t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out)
                        }
@@ -56,7 +56,7 @@ func TestMillionA(t *testing.T) {
                io.WriteString(md, "aaaaaaaaaa")
        }
        out := "52783243c1697bdbe16d37f97f68f08325dc1528"
-       s := fmt.Sprintf("%x", md.Sum())
+       s := fmt.Sprintf("%x", md.Sum(nil))
        if s != out {
                t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out)
        }
index 66188ac10ed8fa4432f523c7c5c26e273a11dc6c..58d5fda197628e55ffcf9c671667e0a20c85ce6c 100644 (file)
@@ -168,7 +168,7 @@ func TestSignPKCS1v15(t *testing.T) {
        for i, test := range signPKCS1v15Tests {
                h := sha1.New()
                h.Write([]byte(test.in))
-               digest := h.Sum()
+               digest := h.Sum(nil)
 
                s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest)
                if err != nil {
@@ -186,7 +186,7 @@ func TestVerifyPKCS1v15(t *testing.T) {
        for i, test := range signPKCS1v15Tests {
                h := sha1.New()
                h.Write([]byte(test.in))
-               digest := h.Sum()
+               digest := h.Sum(nil)
 
                sig, _ := hex.DecodeString(test.out)
 
index 27ccf61c4fc1bf78caadac8e0bdded6405ed4a2d..f74525c103ad453e8e7537b727260862b8f55fcd 100644 (file)
@@ -194,7 +194,7 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
        for done < len(out) {
                hash.Write(seed)
                hash.Write(counter[0:4])
-               digest := hash.Sum()
+               digest := hash.Sum(nil)
                hash.Reset()
 
                for i := 0; i < len(digest) && done < len(out); i++ {
@@ -231,7 +231,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l
        }
 
        hash.Write(label)
-       lHash := hash.Sum()
+       lHash := hash.Sum(nil)
        hash.Reset()
 
        em := make([]byte, k)
@@ -428,7 +428,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext
        }
 
        hash.Write(label)
-       lHash := hash.Sum()
+       lHash := hash.Sum(nil)
        hash.Reset()
 
        // Converting the plaintext number to bytes will strip any
index 4cdf5b2e989b4c706b6b82bf31f5a6a4c50f0897..f41cdb5b0279d8be461559889fc255e226ca247a 100644 (file)
@@ -79,7 +79,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0 so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -105,14 +105,11 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 20)
-       j := 0
        for _, s := range d.h {
-               p[j+0] = byte(s >> 24)
-               p[j+1] = byte(s >> 16)
-               p[j+2] = byte(s >> 8)
-               p[j+3] = byte(s >> 0)
-               j += 4
+               in = append(in, byte(s>>24))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s))
        }
-       return p
+       return in
 }
index 2712fe35eaf795f4299e4d29b1e4bddea1aa59f7..c23df6c41e9de1794d555fe9c5cf90c1987049a2 100644 (file)
@@ -60,10 +60,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
index 14b8cfc7ecacf55a0e97dcb54649375dfb28c62b..34861f6cf49522a87de9f69b4df3b5a4b98f59a4 100644 (file)
@@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0 so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -149,17 +149,15 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 32)
-       j := 0
-       for _, s := range d.h {
-               p[j+0] = byte(s >> 24)
-               p[j+1] = byte(s >> 16)
-               p[j+2] = byte(s >> 8)
-               p[j+3] = byte(s >> 0)
-               j += 4
-       }
+       h := d.h[:]
        if d.is224 {
-               return p[0:28]
+               h = d.h[:7]
+       }
+       for _, s := range h {
+               in = append(in, byte(s>>24))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s))
        }
-       return p
+       return in
 }
index 42a3fa7a01082c2bd2c797992fe1a65e9bc81802..a6efb37545602480437cec07d54f6f402e432e32 100644 (file)
@@ -94,10 +94,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
@@ -112,10 +112,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
index 1bd27982bb70563a768bc882779e14cb4406d92c..3cf65cbe7c825e9b76e42549975efa039a3ba682 100644 (file)
@@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
        return
 }
 
-func (d0 *digest) Sum() []byte {
+func (d0 *digest) Sum(in []byte) []byte {
        // Make a copy of d0 so that caller can keep writing and summing.
        d := new(digest)
        *d = *d0
@@ -149,21 +149,19 @@ func (d0 *digest) Sum() []byte {
                panic("d.nx != 0")
        }
 
-       p := make([]byte, 64)
-       j := 0
-       for _, s := range d.h {
-               p[j+0] = byte(s >> 56)
-               p[j+1] = byte(s >> 48)
-               p[j+2] = byte(s >> 40)
-               p[j+3] = byte(s >> 32)
-               p[j+4] = byte(s >> 24)
-               p[j+5] = byte(s >> 16)
-               p[j+6] = byte(s >> 8)
-               p[j+7] = byte(s >> 0)
-               j += 8
-       }
+       h := d.h[:]
        if d.is384 {
-               return p[0:48]
+               h = d.h[:6]
+       }
+       for _, s := range h {
+               in = append(in, byte(s>>56))
+               in = append(in, byte(s>>48))
+               in = append(in, byte(s>>40))
+               in = append(in, byte(s>>32))
+               in = append(in, byte(s>>24))
+               in = append(in, byte(s>>16))
+               in = append(in, byte(s>>8))
+               in = append(in, byte(s))
        }
-       return p
+       return in
 }
index dd116dc17b3cf1a56de70d135a5c5ab6995484bc..a70f7c54e39e4e0a6f7dd4cedfa34c9ac028ed6c 100644 (file)
@@ -94,10 +94,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
@@ -112,10 +112,10 @@ func TestGolden(t *testing.T) {
                                io.WriteString(c, g.in)
                        } else {
                                io.WriteString(c, g.in[0:len(g.in)/2])
-                               c.Sum()
+                               c.Sum(nil)
                                io.WriteString(c, g.in[len(g.in)/2:])
                        }
-                       s := fmt.Sprintf("%x", c.Sum())
+                       s := fmt.Sprintf("%x", c.Sum(nil))
                        if s != g.out {
                                t.Fatalf("sha384[%d](%s) = %s want %s", j, g.in, s, g.out)
                        }
index 1134f36258374b24a642e8fe87b56c7229da34b4..c0e8656f79b534c6960ad3ef2f22af43103c24dc 100644 (file)
@@ -37,6 +37,7 @@ type keyAgreement interface {
 // A cipherSuite is a specific combination of key agreement, cipher and MAC
 // function. All cipher suites currently assume RSA key agreement.
 type cipherSuite struct {
+       id uint16
        // the lengths, in bytes, of the key material needed for each component.
        keyLen int
        macLen int
@@ -50,13 +51,13 @@ type cipherSuite struct {
        mac      func(version uint16, macKey []byte) macFunction
 }
 
-var cipherSuites = map[uint16]*cipherSuite{
-       TLS_RSA_WITH_RC4_128_SHA:            &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
-       TLS_RSA_WITH_3DES_EDE_CBC_SHA:       &cipherSuite{24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
-       TLS_RSA_WITH_AES_128_CBC_SHA:        &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1},
-       TLS_ECDHE_RSA_WITH_RC4_128_SHA:      &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
-       TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
-       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:  &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
+var cipherSuites = []*cipherSuite{
+       &cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
+       &cipherSuite{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
+       &cipherSuite{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1},
+       &cipherSuite{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
+       &cipherSuite{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
+       &cipherSuite{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
 }
 
 func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -126,13 +127,13 @@ func (s ssl30MAC) MAC(seq, record []byte) []byte {
        s.h.Write(record[:1])
        s.h.Write(record[3:5])
        s.h.Write(record[recordHeaderLen:])
-       digest := s.h.Sum()
+       digest := s.h.Sum(nil)
 
        s.h.Reset()
        s.h.Write(s.key)
        s.h.Write(ssl30Pad2[:padLength])
        s.h.Write(digest)
-       return s.h.Sum()
+       return s.h.Sum(nil)
 }
 
 // tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
@@ -148,7 +149,7 @@ func (s tls10MAC) MAC(seq, record []byte) []byte {
        s.h.Reset()
        s.h.Write(seq)
        s.h.Write(record)
-       return s.h.Sum()
+       return s.h.Sum(nil)
 }
 
 func rsaKA() keyAgreement {
@@ -159,15 +160,20 @@ func ecdheRSAKA() keyAgreement {
        return new(ecdheRSAKeyAgreement)
 }
 
-// mutualCipherSuite returns a cipherSuite and its id given a list of supported
+// mutualCipherSuite returns a cipherSuite given a list of supported
 // ciphersuites and the id requested by the peer.
-func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) {
+func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
        for _, id := range have {
                if id == want {
-                       return cipherSuites[id], id
+                       for _, suite := range cipherSuites {
+                               if suite.id == want {
+                                       return suite
+                               }
+                       }
+                       return nil
                }
        }
-       return
+       return nil
 }
 
 // A list of the possible cipher suite ids. Taken from
index ea520859b827c9267a85fd283525cdeeef76c179..f57d932a98f9a6602b51f61cf5ef032dcdb71481 100644 (file)
@@ -121,7 +121,7 @@ type Config struct {
 
        // Time returns the current time as the number of seconds since the epoch.
        // If Time is nil, TLS uses the system time.Seconds.
-       Time func() int64
+       Time func() time.Time
 
        // Certificates contains one or more certificate chains
        // to present to the other side of the connection.
@@ -175,10 +175,10 @@ func (c *Config) rand() io.Reader {
        return r
 }
 
-func (c *Config) time() int64 {
+func (c *Config) time() time.Time {
        t := c.Time
        if t == nil {
-               t = time.Seconds
+               t = time.Now
        }
        return t()
 }
@@ -315,9 +315,7 @@ var (
 
 func initDefaultCipherSuites() {
        varDefaultCipherSuites = make([]uint16, len(cipherSuites))
-       i := 0
-       for id := range cipherSuites {
-               varDefaultCipherSuites[i] = id
-               i++
+       for i, suite := range cipherSuites {
+               varDefaultCipherSuites[i] = suite.id
        }
 }
index aed991ccd1bfb00229a382557dd00d1cba0eb8d8..b4337f2aac61f915a72daee9f8fc9ab6411cb336 100644 (file)
@@ -32,7 +32,7 @@ func (c *Conn) clientHandshake() error {
                nextProtoNeg:       len(c.config.NextProtos) > 0,
        }
 
-       t := uint32(c.config.time())
+       t := uint32(c.config.time().Unix())
        hello.random[0] = byte(t >> 24)
        hello.random[1] = byte(t >> 16)
        hello.random[2] = byte(t >> 8)
@@ -72,7 +72,7 @@ func (c *Conn) clientHandshake() error {
                return errors.New("server advertised unrequested NPN")
        }
 
-       suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
+       suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
        if suite == nil {
                return c.sendAlert(alertHandshakeFailure)
        }
@@ -232,8 +232,8 @@ func (c *Conn) clientHandshake() error {
        if cert != nil {
                certVerify := new(certificateVerifyMsg)
                var digest [36]byte
-               copy(digest[0:16], finishedHash.serverMD5.Sum())
-               copy(digest[16:36], finishedHash.serverSHA1.Sum())
+               copy(digest[0:16], finishedHash.serverMD5.Sum(nil))
+               copy(digest[16:36], finishedHash.serverSHA1.Sum(nil))
                signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest[0:])
                if err != nil {
                        return c.sendAlert(alertInternalError)
@@ -292,7 +292,7 @@ func (c *Conn) clientHandshake() error {
        }
 
        c.handshakeComplete = true
-       c.cipherSuite = suiteId
+       c.cipherSuite = suite.id
        return nil
 }
 
index d5af084eda5b094c0d157e6893dae6abb31c3cd7..bbb23c0c9f6dacbaf080ee65b27fbaa5ee36a5df 100644 (file)
@@ -56,18 +56,25 @@ Curves:
        ellipticOk := supportedCurve && supportedPointFormat
 
        var suite *cipherSuite
-       var suiteId uint16
 FindCipherSuite:
        for _, id := range clientHello.cipherSuites {
                for _, supported := range config.cipherSuites() {
                        if id == supported {
-                               suite = cipherSuites[id]
+                               suite = nil
+                               for _, s := range cipherSuites {
+                                       if s.id == id {
+                                               suite = s
+                                               break
+                                       }
+                               }
+                               if suite == nil {
+                                       continue
+                               }
                                // Don't select a ciphersuite which we can't
                                // support for this client.
                                if suite.elliptic && !ellipticOk {
                                        continue
                                }
-                               suiteId = id
                                break FindCipherSuite
                        }
                }
@@ -87,8 +94,8 @@ FindCipherSuite:
        }
 
        hello.vers = vers
-       hello.cipherSuite = suiteId
-       t := uint32(config.time())
+       hello.cipherSuite = suite.id
+       t := uint32(config.time().Unix())
        hello.random = make([]byte, 32)
        hello.random[0] = byte(t >> 24)
        hello.random[1] = byte(t >> 16)
@@ -228,8 +235,8 @@ FindCipherSuite:
                }
 
                digest := make([]byte, 36)
-               copy(digest[0:16], finishedHash.serverMD5.Sum())
-               copy(digest[16:36], finishedHash.serverSHA1.Sum())
+               copy(digest[0:16], finishedHash.serverMD5.Sum(nil))
+               copy(digest[16:36], finishedHash.serverSHA1.Sum(nil))
                err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
                if err != nil {
                        c.sendAlert(alertBadCertificate)
@@ -296,7 +303,7 @@ FindCipherSuite:
        c.writeRecord(recordTypeHandshake, finished.marshal())
 
        c.handshakeComplete = true
-       c.cipherSuite = suiteId
+       c.cipherSuite = suite.id
 
        return nil
 }
index bc3797947f5fe96ed249ad71fea94327e3a5ce4a..e00c32c5508371645d710daddc831a95841a1ce3 100644 (file)
@@ -15,6 +15,7 @@ import (
        "strconv"
        "strings"
        "testing"
+       "time"
 )
 
 type zeroSource struct{}
@@ -31,7 +32,7 @@ var testConfig *Config
 
 func init() {
        testConfig = new(Config)
-       testConfig.Time = func() int64 { return 0 }
+       testConfig.Time = func() time.Time { return time.Unix(0, 0) }
        testConfig.Rand = zeroSource{}
        testConfig.Certificates = make([]Certificate, 1)
        testConfig.Certificates[0].Certificate = [][]byte{testCertificate}
index 08fb852d66a7403ad5d0ec941f8c6b84e41279c1..b531717d8401c78d170a72aef5f3ca9cf2239e37 100644 (file)
@@ -90,13 +90,13 @@ func md5SHA1Hash(slices ...[]byte) []byte {
        for _, slice := range slices {
                hmd5.Write(slice)
        }
-       copy(md5sha1, hmd5.Sum())
+       copy(md5sha1, hmd5.Sum(nil))
 
        hsha1 := sha1.New()
        for _, slice := range slices {
                hsha1.Write(slice)
        }
-       copy(md5sha1[md5.Size:], hsha1.Sum())
+       copy(md5sha1[md5.Size:], hsha1.Sum(nil))
        return md5sha1
 }
 
index d758f21aa8ebd8ac79d747579186f73695fa8f74..637ef03e2d7844d38d471c14955606bc3d94e21e 100644 (file)
@@ -22,14 +22,14 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
 func pHash(result, secret, seed []byte, hash func() hash.Hash) {
        h := hmac.New(hash, secret)
        h.Write(seed)
-       a := h.Sum()
+       a := h.Sum(nil)
 
        j := 0
        for j < len(result) {
                h.Reset()
                h.Write(a)
                h.Write(seed)
-               b := h.Sum()
+               b := h.Sum(nil)
                todo := len(b)
                if j+todo > len(result) {
                        todo = len(result) - j
@@ -39,7 +39,7 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) {
 
                h.Reset()
                h.Write(a)
-               a = h.Sum()
+               a = h.Sum(nil)
        }
 }
 
@@ -84,13 +84,13 @@ func pRF30(result, secret, label, seed []byte) {
                hashSHA1.Write(b[:i+1])
                hashSHA1.Write(secret)
                hashSHA1.Write(seed)
-               digest := hashSHA1.Sum()
+               digest := hashSHA1.Sum(nil)
 
                hashMD5.Reset()
                hashMD5.Write(secret)
                hashMD5.Write(digest)
 
-               done += copy(result[done:], hashMD5.Sum())
+               done += copy(result[done:], hashMD5.Sum(nil))
                i++
        }
 }
@@ -182,24 +182,24 @@ func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []by
        md5.Write(magic[:])
        md5.Write(masterSecret)
        md5.Write(ssl30Pad1[:])
-       md5Digest := md5.Sum()
+       md5Digest := md5.Sum(nil)
 
        md5.Reset()
        md5.Write(masterSecret)
        md5.Write(ssl30Pad2[:])
        md5.Write(md5Digest)
-       md5Digest = md5.Sum()
+       md5Digest = md5.Sum(nil)
 
        sha1.Write(magic[:])
        sha1.Write(masterSecret)
        sha1.Write(ssl30Pad1[:40])
-       sha1Digest := sha1.Sum()
+       sha1Digest := sha1.Sum(nil)
 
        sha1.Reset()
        sha1.Write(masterSecret)
        sha1.Write(ssl30Pad2[:40])
        sha1.Write(sha1Digest)
-       sha1Digest = sha1.Sum()
+       sha1Digest = sha1.Sum(nil)
 
        ret := make([]byte, len(md5Digest)+len(sha1Digest))
        copy(ret, md5Digest)
@@ -217,8 +217,8 @@ func (h finishedHash) clientSum(masterSecret []byte) []byte {
                return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
        }
 
-       md5 := h.clientMD5.Sum()
-       sha1 := h.clientSHA1.Sum()
+       md5 := h.clientMD5.Sum(nil)
+       sha1 := h.clientSHA1.Sum(nil)
        return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
 }
 
@@ -229,7 +229,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
                return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
        }
 
-       md5 := h.serverMD5.Sum()
-       sha1 := h.serverSHA1.Sum()
+       md5 := h.serverMD5.Sum(nil)
+       sha1 := h.serverSHA1.Sum(nil)
        return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
 }
index 095beec104aaacaf93e4e02ee16ee5be84c48e48..1b9aeb03b5bfe1604104177acb6cab23c895e66a 100644 (file)
@@ -14,6 +14,7 @@ var certFiles = []string{
        "/etc/ssl/certs/ca-certificates.crt", // Linux etc
        "/etc/pki/tls/certs/ca-bundle.crt",   // Fedora/RHEL
        "/etc/ssl/ca-bundle.pem",             // OpenSUSE
+       "/etc/ssl/cert.pem",                  // OpenBSD
 }
 
 func initDefaultRoots() {
index 13073dcee78c453f4788acae5ac99d808929c3aa..319309ae6e7eb5a9ea74bdb1167f3377047cf6d4 100644 (file)
@@ -6,7 +6,6 @@ package tls
 
 import (
        "crypto/x509"
-       "reflect"
        "syscall"
        "unsafe"
 )
@@ -16,29 +15,23 @@ func loadStore(roots *x509.CertPool, name string) {
        if err != nil {
                return
        }
+       defer syscall.CertCloseStore(store, 0)
 
        var cert *syscall.CertContext
        for {
-               cert = syscall.CertEnumCertificatesInStore(store, cert)
-               if cert == nil {
-                       break
+               cert, err = syscall.CertEnumCertificatesInStore(store, cert)
+               if err != nil {
+                       return
                }
 
-               var asn1Slice []byte
-               hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice))
-               hdrp.Data = cert.EncodedCert
-               hdrp.Len = int(cert.Length)
-               hdrp.Cap = int(cert.Length)
-
-               buf := make([]byte, len(asn1Slice))
-               copy(buf, asn1Slice)
-
-               if cert, err := x509.ParseCertificate(buf); err == nil {
-                       roots.AddCert(cert)
+               buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
+               // ParseCertificate requires its own copy of certificate data to keep.
+               buf2 := make([]byte, cert.Length)
+               copy(buf2, buf)
+               if c, err := x509.ParseCertificate(buf2); err == nil {
+                       roots.AddCert(c)
                }
        }
-
-       syscall.CertCloseStore(store, 0)
 }
 
 func initDefaultRoots() {
index 3ca62407ff032dcb6f4449fa77555c78f615eb7c..79ab50231293045e6b0eed4f0efd88818163fb24 100644 (file)
@@ -157,10 +157,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
                return
        }
 
-       key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
-       if err != nil {
-               err = errors.New("crypto/tls: failed to parse key: " + err.Error())
-               return
+       // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
+       // OpenSSL 1.0.0 generates PKCS#8 keys. We try both.
+       var key *rsa.PrivateKey
+       if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil {
+               var privKey interface{}
+               if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil {
+                       err = errors.New("crypto/tls: failed to parse key: " + err.Error())
+                       return
+               }
+
+               var ok bool
+               if key, ok = privKey.(*rsa.PrivateKey); !ok {
+                       err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping")
+                       return
+               }
        }
 
        cert.PrivateKey = key
index b9196ed46ed8fb9b028ae30bda8b09ec63e91ff4..adc7f9bc6d76e09bacbefd4703f9235b12f94756 100644 (file)
@@ -8,7 +8,7 @@ import (
        "encoding/pem"
 )
 
-// Roots is a set of certificates.
+// CertPool is a set of certificates.
 type CertPool struct {
        bySubjectKeyId map[string][]int
        byName         map[string][]int
@@ -70,11 +70,11 @@ func (s *CertPool) AddCert(cert *Certificate) {
        s.byName[name] = append(s.byName[name], n)
 }
 
-// AppendCertsFromPEM attempts to parse a series of PEM encoded root
-// certificates. It appends any certificates found to s and returns true if any
-// certificates were successfully parsed.
+// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
+// It appends any certificates found to s and returns true if any certificates
+// were successfully parsed.
 //
-// On many Linux systems, /etc/ssl/cert.pem will contains the system wide set
+// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
 // of root CAs in a format suitable for this function.
 func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
        for len(pemCerts) > 0 {
diff --git a/libgo/go/crypto/x509/pkcs8.go b/libgo/go/crypto/x509/pkcs8.go
new file mode 100644 (file)
index 0000000..4d8e051
--- /dev/null
@@ -0,0 +1,42 @@
+// 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 x509
+
+import (
+       "crypto/x509/pkix"
+       "encoding/asn1"
+       "errors"
+       "fmt"
+)
+
+// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See 
+// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn.
+type pkcs8 struct {
+       Version    int
+       Algo       pkix.AlgorithmIdentifier
+       PrivateKey []byte
+       // optional attributes omitted.
+}
+
+// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
+// http://www.rsa.com/rsalabs/node.asp?id=2130
+func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
+       var privKey pkcs8
+       if _, err := asn1.Unmarshal(der, &privKey); err != nil {
+               return nil, err
+       }
+       switch {
+       case privKey.Algo.Algorithm.Equal(oidRSA):
+               key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
+               if err != nil {
+                       return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
+               }
+               return key, nil
+       default:
+               return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
+       }
+
+       panic("unreachable")
+}
diff --git a/libgo/go/crypto/x509/pkcs8_test.go b/libgo/go/crypto/x509/pkcs8_test.go
new file mode 100644 (file)
index 0000000..372005f
--- /dev/null
@@ -0,0 +1,20 @@
+// 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 x509
+
+import (
+       "encoding/hex"
+       "testing"
+)
+
+var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
+
+func TestPKCS8(t *testing.T) {
+       derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex)
+       _, err := ParsePKCS8PrivateKey(derBytes)
+       if err != nil {
+               t.Errorf("failed to decode PKCS8 key: %s", err)
+       }
+}
index b35274c9ae16b6340da0f06fccafea651ccef226..8eced55f932a01e658ca3615d1b1d772f65bdb1d 100644 (file)
@@ -142,10 +142,9 @@ type CertificateList struct {
        SignatureValue     asn1.BitString
 }
 
-// HasExpired returns true iff currentTimeSeconds is past the expiry time of
-// certList.
-func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
-       return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds
+// HasExpired returns true iff now is past the expiry time of certList.
+func (certList *CertificateList) HasExpired(now time.Time) bool {
+       return now.After(certList.TBSCertList.NextUpdate)
 }
 
 // TBSCertificateList represents the ASN.1 structure of the same name. See RFC
@@ -155,8 +154,8 @@ type TBSCertificateList struct {
        Version             int `asn1:"optional,default:2"`
        Signature           AlgorithmIdentifier
        Issuer              RDNSequence
-       ThisUpdate          *time.Time
-       NextUpdate          *time.Time
+       ThisUpdate          time.Time
+       NextUpdate          time.Time
        RevokedCertificates []RevokedCertificate `asn1:"optional"`
        Extensions          []Extension          `asn1:"tag:0,optional,explicit"`
 }
@@ -165,6 +164,6 @@ type TBSCertificateList struct {
 // 5280, section 5.1.
 type RevokedCertificate struct {
        SerialNumber   *big.Int
-       RevocationTime *time.Time
+       RevocationTime time.Time
        Extensions     []Extension `asn1:"optional"`
 }
index 3021d20a67f154f5c03c34a48c64644fbdc97886..50a3b66e55506a66ab9bea45f07eee93235b3918 100644 (file)
@@ -76,7 +76,7 @@ type VerifyOptions struct {
        DNSName       string
        Intermediates *CertPool
        Roots         *CertPool
-       CurrentTime   int64 // if 0, the current system time is used.
+       CurrentTime   time.Time // if zero, the current time is used
 }
 
 const (
@@ -87,8 +87,11 @@ const (
 
 // isValid performs validity checks on the c.
 func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
-       if opts.CurrentTime < c.NotBefore.Seconds() ||
-               opts.CurrentTime > c.NotAfter.Seconds() {
+       now := opts.CurrentTime
+       if now.IsZero() {
+               now = time.Now()
+       }
+       if now.Before(c.NotBefore) || now.After(c.NotAfter) {
                return CertificateInvalidError{c, Expired}
        }
 
@@ -136,9 +139,6 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
 //
 // WARNING: this doesn't do any revocation checking.
 func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
-       if opts.CurrentTime == 0 {
-               opts.CurrentTime = time.Seconds()
-       }
        err = c.isValid(leafCertificate, &opts)
        if err != nil {
                return
index 2194d15bc88b5181138618f09b85730c8fb07d97..df5443023ff9923d1633cb2df7e609309aaefe67 100644 (file)
@@ -10,6 +10,7 @@ import (
        "errors"
        "strings"
        "testing"
+       "time"
 )
 
 type verifyTest struct {
@@ -133,7 +134,7 @@ func TestVerify(t *testing.T) {
                        Roots:         NewCertPool(),
                        Intermediates: NewCertPool(),
                        DNSName:       test.dnsName,
-                       CurrentTime:   test.currentTime,
+                       CurrentTime:   time.Unix(test.currentTime, 0),
                }
 
                for j, root := range test.roots {
index 9ff7db9a0f99ffb0fa02e80d88ac1e2110d8b623..7e6b5c96f536c722e0c49c778f0872eb4720f9ab 100644 (file)
@@ -107,7 +107,7 @@ type dsaSignature struct {
 }
 
 type validity struct {
-       NotBefore, NotAfter *time.Time
+       NotBefore, NotAfter time.Time
 }
 
 type publicKeyInfo struct {
@@ -303,7 +303,7 @@ type Certificate struct {
        SerialNumber        *big.Int
        Issuer              pkix.Name
        Subject             pkix.Name
-       NotBefore, NotAfter *time.Time // Validity bounds.
+       NotBefore, NotAfter time.Time // Validity bounds.
        KeyUsage            KeyUsage
 
        ExtKeyUsage        []ExtKeyUsage           // Sequence of extended key usages.
@@ -398,7 +398,7 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
        }
 
        h.Write(signed)
-       digest := h.Sum()
+       digest := h.Sum(nil)
 
        switch pub := c.PublicKey.(type) {
        case *rsa.PublicKey:
@@ -899,11 +899,10 @@ var (
        oidRSA         = []int{1, 2, 840, 113549, 1, 1, 1}
 )
 
-// CreateSelfSignedCertificate creates a new certificate based on
-// a template. The following members of template are used: SerialNumber,
-// Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA,
-// MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
-// PermittedDNSDomains.
+// CreateCertificate creates a new certificate based on a template. The
+// following members of template are used: SerialNumber, Subject, NotBefore,
+// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId,
+// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains.
 //
 // The certificate is signed by parent. If parent is equal to template then the
 // certificate is self-signed. The parameter pub is the public key of the
@@ -958,7 +957,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
 
        h := sha1.New()
        h.Write(tbsCertContents)
-       digest := h.Sum()
+       digest := h.Sum(nil)
 
        signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest)
        if err != nil {
@@ -1006,7 +1005,7 @@ func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) {
 
 // CreateCRL returns a DER encoded CRL, signed by this Certificate, that
 // contains the given list of revoked certificates.
-func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err error) {
+func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
        tbsCertList := pkix.TBSCertificateList{
                Version: 2,
                Signature: pkix.AlgorithmIdentifier{
@@ -1025,7 +1024,7 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCer
 
        h := sha1.New()
        h.Write(tbsCertListContents)
-       digest := h.Sum()
+       digest := h.Sum(nil)
 
        signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest)
        if err != nil {
index c42471507bebf78f122021e5f5fdf61ade367d42..f0327b0124d42bdfca67d079f3f7943cd5999163 100644 (file)
@@ -250,8 +250,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                        CommonName:   commonName,
                        Organization: []string{"Acme Co"},
                },
-               NotBefore: time.SecondsToUTC(1000),
-               NotAfter:  time.SecondsToUTC(100000),
+               NotBefore: time.Unix(1000, 0),
+               NotAfter:  time.Unix(100000, 0),
 
                SubjectKeyId: []byte{1, 2, 3, 4},
                KeyUsage:     KeyUsageCertSign,
@@ -396,8 +396,8 @@ func TestCRLCreation(t *testing.T) {
        block, _ = pem.Decode([]byte(pemCertificate))
        cert, _ := ParseCertificate(block.Bytes)
 
-       now := time.SecondsToUTC(1000)
-       expiry := time.SecondsToUTC(10000)
+       now := time.Unix(1000, 0)
+       expiry := time.Unix(10000, 0)
 
        revokedCerts := []pkix.RevokedCertificate{
                {
@@ -443,7 +443,7 @@ func TestParseDERCRL(t *testing.T) {
                t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
        }
 
-       if certList.HasExpired(1302517272) {
+       if certList.HasExpired(time.Unix(1302517272, 0)) {
                t.Errorf("CRL has expired (but shouldn't have)")
        }
 
@@ -463,7 +463,7 @@ func TestParsePEMCRL(t *testing.T) {
                t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
        }
 
-       if certList.HasExpired(1302517272) {
+       if certList.HasExpired(time.Unix(1302517272, 0)) {
                t.Errorf("CRL has expired (but shouldn't have)")
        }
 
index a0066654f8d0208006c6e4030d9d33f851b93008..22a0dde0da43d8e1d28a575d8429d3dbeaecbe62 100644 (file)
@@ -247,7 +247,7 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
 
 // UTCTime
 
-func parseUTCTime(bytes []byte) (ret *time.Time, err error) {
+func parseUTCTime(bytes []byte) (ret time.Time, err error) {
        s := string(bytes)
        ret, err = time.Parse("0601021504Z0700", s)
        if err == nil {
@@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err error) {
 
 // parseGeneralizedTime parses the GeneralizedTime from the given byte slice
 // and returns the resulting time.
-func parseGeneralizedTime(bytes []byte) (ret *time.Time, err error) {
+func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
        return time.Parse("20060102150405Z0700", string(bytes))
 }
 
@@ -450,7 +450,7 @@ var (
        objectIdentifierType = reflect.TypeOf(ObjectIdentifier{})
        enumeratedType       = reflect.TypeOf(Enumerated(0))
        flagType             = reflect.TypeOf(Flag(false))
-       timeType             = reflect.TypeOf(&time.Time{})
+       timeType             = reflect.TypeOf(time.Time{})
        rawValueType         = reflect.TypeOf(RawValue{})
        rawContentsType      = reflect.TypeOf(RawContent(nil))
        bigIntType           = reflect.TypeOf(new(big.Int))
@@ -647,7 +647,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                err = err1
                return
        case timeType:
-               var time *time.Time
+               var time time.Time
                var err1 error
                if universalTag == tagUTCTime {
                        time, err1 = parseUTCTime(innerBytes)
@@ -799,7 +799,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 //
 // An ASN.1 ENUMERATED can be written to an Enumerated.
 //
-// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time.
+// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
 //
 // An ASN.1 PrintableString or IA5String can be written to a string.
 //
index 1c529bdb30ca0482dce8d1a73ac38dd4a397c9fe..2e6fccf7b80cc3a4df128d0f42ec6162938198fe 100644 (file)
@@ -202,43 +202,51 @@ func TestObjectIdentifier(t *testing.T) {
 type timeTest struct {
        in  string
        ok  bool
-       out *time.Time
+       out time.Time
 }
 
 var utcTestData = []timeTest{
-       {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}},
-       {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}},
-       {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}},
-       {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}},
-       {"a10506234540Z", false, nil},
-       {"91a506234540Z", false, nil},
-       {"9105a6234540Z", false, nil},
-       {"910506a34540Z", false, nil},
-       {"910506334a40Z", false, nil},
-       {"91050633444aZ", false, nil},
-       {"910506334461Z", false, nil},
-       {"910506334400Za", false, nil},
+       {"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))},
+       {"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))},
+       {"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)},
+       {"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)},
+       {"a10506234540Z", false, time.Time{}},
+       {"91a506234540Z", false, time.Time{}},
+       {"9105a6234540Z", false, time.Time{}},
+       {"910506a34540Z", false, time.Time{}},
+       {"910506334a40Z", false, time.Time{}},
+       {"91050633444aZ", false, time.Time{}},
+       {"910506334461Z", false, time.Time{}},
+       {"910506334400Za", false, time.Time{}},
 }
 
 func TestUTCTime(t *testing.T) {
        for i, test := range utcTestData {
                ret, err := parseUTCTime([]byte(test.in))
-               if (err == nil) != test.ok {
-                       t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
-               }
-               if err == nil {
-                       if !reflect.DeepEqual(test.out, ret) {
-                               t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+               if err != nil {
+                       if test.ok {
+                               t.Errorf("#%d: parseUTCTime(%q) = error %v", i, err)
                        }
+                       continue
+               }
+               if !test.ok {
+                       t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i)
+                       continue
+               }
+               const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset
+               have := ret.Format(format)
+               want := test.out.Format(format)
+               if have != want {
+                       t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", test.in, have, want)
                }
        }
 }
 
 var generalizedTimeTestData = []timeTest{
-       {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}},
-       {"20100102030405", false, nil},
-       {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}},
-       {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}},
+       {"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
+       {"20100102030405", false, time.Time{}},
+       {"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
+       {"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
 }
 
 func TestGeneralizedTime(t *testing.T) {
@@ -407,7 +415,7 @@ type AttributeTypeAndValue struct {
 }
 
 type Validity struct {
-       NotBefore, NotAfter *time.Time
+       NotBefore, NotAfter time.Time
 }
 
 type PublicKeyInfo struct {
@@ -475,7 +483,10 @@ var derEncodedSelfSignedCert = Certificate{
                        RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
                        RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
                },
-               Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}},
+               Validity: Validity{
+                       NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC),
+                       NotAfter:  time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC),
+               },
                Subject: RDNSequence{
                        RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
                        RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
index 89c50a70ef4716df86b6beaefbd840f80921955f..c181e43f9798bc893823ace212343bcb60fc9c14 100644 (file)
@@ -288,52 +288,58 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
        return out.WriteByte(byte('0' + v%10))
 }
 
-func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) {
+func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
+       utc := t.UTC()
+       year, month, day := utc.Date()
+
        switch {
-       case 1950 <= t.Year && t.Year < 2000:
-               err = marshalTwoDigits(out, int(t.Year-1900))
-       case 2000 <= t.Year && t.Year < 2050:
-               err = marshalTwoDigits(out, int(t.Year-2000))
+       case 1950 <= year && year < 2000:
+               err = marshalTwoDigits(out, int(year-1900))
+       case 2000 <= year && year < 2050:
+               err = marshalTwoDigits(out, int(year-2000))
        default:
                return StructuralError{"Cannot represent time as UTCTime"}
        }
-
        if err != nil {
                return
        }
 
-       err = marshalTwoDigits(out, t.Month)
+       err = marshalTwoDigits(out, int(month))
        if err != nil {
                return
        }
 
-       err = marshalTwoDigits(out, t.Day)
+       err = marshalTwoDigits(out, day)
        if err != nil {
                return
        }
 
-       err = marshalTwoDigits(out, t.Hour)
+       hour, min, sec := utc.Clock()
+
+       err = marshalTwoDigits(out, hour)
        if err != nil {
                return
        }
 
-       err = marshalTwoDigits(out, t.Minute)
+       err = marshalTwoDigits(out, min)
        if err != nil {
                return
        }
 
-       err = marshalTwoDigits(out, t.Second)
+       err = marshalTwoDigits(out, sec)
        if err != nil {
                return
        }
 
+       _, offset := t.Zone()
+
        switch {
-       case t.ZoneOffset/60 == 0:
+       case offset/60 == 0:
                err = out.WriteByte('Z')
                return
-       case t.ZoneOffset > 0:
+       case offset > 0:
                err = out.WriteByte('+')
-       case t.ZoneOffset < 0:
+       case offset < 0:
                err = out.WriteByte('-')
        }
 
@@ -341,7 +347,7 @@ func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) {
                return
        }
 
-       offsetMinutes := t.ZoneOffset / 60
+       offsetMinutes := offset / 60
        if offsetMinutes < 0 {
                offsetMinutes = -offsetMinutes
        }
@@ -366,7 +372,7 @@ func stripTagAndLength(in []byte) []byte {
 func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
        switch value.Type() {
        case timeType:
-               return marshalUTCTime(out, value.Interface().(*time.Time))
+               return marshalUTCTime(out, value.Interface().(time.Time))
        case bitStringType:
                return marshalBitString(out, value.Interface().(BitString))
        case objectIdentifierType:
index 03df5f1e1d52b86fd24eb7ebd45a57f64b57ae2c..d05b5d8d4e92e398bd359b4258410d8f00a56abf 100644 (file)
@@ -51,10 +51,7 @@ type optionalRawValueTest struct {
 
 type testSET []int
 
-func setPST(t *time.Time) *time.Time {
-       t.ZoneOffset = -28800
-       return t
-}
+var PST = time.FixedZone("PST", -8*60*60)
 
 type marshalTest struct {
        in  interface{}
@@ -73,9 +70,9 @@ var marshalTests = []marshalTest{
        {[]byte{1, 2, 3}, "0403010203"},
        {implicitTagTest{64}, "3003850140"},
        {explicitTagTest{64}, "3005a503020140"},
-       {time.SecondsToUTC(0), "170d3730303130313030303030305a"},
-       {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"},
-       {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"},
+       {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
+       {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
+       {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"},
        {BitString{[]byte{0x80}, 1}, "03020780"},
        {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
        {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
@@ -123,7 +120,8 @@ func TestMarshal(t *testing.T) {
                }
                out, _ := hex.DecodeString(test.out)
                if bytes.Compare(out, data) != 0 {
-                       t.Errorf("#%d got: %x want %x", i, data, out)
+                       t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
+
                }
        }
 }
index 35964c5d9c2378fd5fe021855b6876b67f8b75d4..14284f50e47b5bf7f47a505f6a0e7c62269645d8 100644 (file)
@@ -16,6 +16,7 @@ import (
        "runtime"
        "sort"
        "strconv"
+       "sync"
        "unicode"
        "unicode/utf8"
 )
@@ -295,28 +296,10 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
 
        case reflect.Struct:
                e.WriteByte('{')
-               t := v.Type()
-               n := v.NumField()
                first := true
-               for i := 0; i < n; i++ {
-                       f := t.Field(i)
-                       if f.PkgPath != "" {
-                               continue
-                       }
-                       tag, omitEmpty, quoted := f.Name, false, false
-                       if tv := f.Tag.Get("json"); tv != "" {
-                               if tv == "-" {
-                                       continue
-                               }
-                               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) {
+               for _, ef := range encodeFields(v.Type()) {
+                       fieldValue := v.Field(ef.i)
+                       if ef.omitEmpty && isEmptyValue(fieldValue) {
                                continue
                        }
                        if first {
@@ -324,9 +307,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
                        } else {
                                e.WriteByte(',')
                        }
-                       e.string(tag)
+                       e.string(ef.tag)
                        e.WriteByte(':')
-                       e.reflectValueQuoted(fieldValue, quoted)
+                       e.reflectValueQuoted(fieldValue, ef.quoted)
                }
                e.WriteByte('}')
 
@@ -470,3 +453,63 @@ func (e *encodeState) string(s string) (int, error) {
        e.WriteByte('"')
        return e.Len() - len0, nil
 }
+
+// encodeField contains information about how to encode a field of a
+// struct.
+type encodeField struct {
+       i         int // field index in struct
+       tag       string
+       quoted    bool
+       omitEmpty bool
+}
+
+var (
+       typeCacheLock     sync.RWMutex
+       encodeFieldsCache = make(map[reflect.Type][]encodeField)
+)
+
+// encodeFields returns a slice of encodeField for a given
+// struct type.
+func encodeFields(t reflect.Type) []encodeField {
+       typeCacheLock.RLock()
+       fs, ok := encodeFieldsCache[t]
+       typeCacheLock.RUnlock()
+       if ok {
+               return fs
+       }
+
+       typeCacheLock.Lock()
+       defer typeCacheLock.Unlock()
+       fs, ok = encodeFieldsCache[t]
+       if ok {
+               return fs
+       }
+
+       v := reflect.Zero(t)
+       n := v.NumField()
+       for i := 0; i < n; i++ {
+               f := t.Field(i)
+               if f.PkgPath != "" {
+                       continue
+               }
+               var ef encodeField
+               ef.i = i
+               ef.tag = f.Name
+
+               tv := f.Tag.Get("json")
+               if tv != "" {
+                       if tv == "-" {
+                               continue
+                       }
+                       name, opts := parseTag(tv)
+                       if isValidTag(name) {
+                               ef.tag = name
+                       }
+                       ef.omitEmpty = opts.Contains("omitempty")
+                       ef.quoted = opts.Contains("string")
+               }
+               fs = append(fs, ef)
+       }
+       encodeFieldsCache[t] = fs
+       return fs
+}
index 216d8889b239a92805eff6b7d7af0647b22d3b21..d67a299f5bb657e87c58b366efac3aef8d407e8c 100644 (file)
@@ -61,7 +61,7 @@ type StartElement struct {
 
 func (e StartElement) Copy() StartElement {
        attrs := make([]Attr, len(e.Attr))
-       copy(e.Attr, attrs)
+       copy(attrs, e.Attr)
        e.Attr = attrs
        return e
 }
index bcb22afde00e2d1a38c4e17fc7eea01594f6303e..25ffc917dcb135c3e8068405af0510d6a2530ee7 100644 (file)
@@ -29,71 +29,69 @@ const testInput = `
 </body><!-- missing final newline -->`
 
 var rawTokens = []Token{
-       CharData([]byte("\n")),
+       CharData("\n"),
        ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       CharData("\n"),
+       Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
-       ),
-       CharData([]byte("\n")),
+       CharData("\n"),
        StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
-       CharData([]byte("World <>'\" 白鵬翔")),
+       CharData("World <>'\" 白鵬翔"),
        EndElement{Name{"", "hello"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"", "goodbye"}, []Attr{}},
        EndElement{Name{"", "goodbye"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
-       CharData([]byte("\n    ")),
+       CharData("\n    "),
        StartElement{Name{"", "inner"}, []Attr{}},
        EndElement{Name{"", "inner"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        EndElement{Name{"", "outer"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"tag", "name"}, []Attr{}},
-       CharData([]byte("\n    ")),
-       CharData([]byte("Some text here.")),
-       CharData([]byte("\n  ")),
+       CharData("\n    "),
+       CharData("Some text here."),
+       CharData("\n  "),
        EndElement{Name{"tag", "name"}},
-       CharData([]byte("\n")),
+       CharData("\n"),
        EndElement{Name{"", "body"}},
-       Comment([]byte(" missing final newline ")),
+       Comment(" missing final newline "),
 }
 
 var cookedTokens = []Token{
-       CharData([]byte("\n")),
+       CharData("\n"),
        ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       CharData("\n"),
+       Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
-       ),
-       CharData([]byte("\n")),
+       CharData("\n"),
        StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
-       CharData([]byte("World <>'\" 白鵬翔")),
+       CharData("World <>'\" 白鵬翔"),
        EndElement{Name{"ns2", "hello"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"ns2", "goodbye"}, []Attr{}},
        EndElement{Name{"ns2", "goodbye"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
-       CharData([]byte("\n    ")),
+       CharData("\n    "),
        StartElement{Name{"ns2", "inner"}, []Attr{}},
        EndElement{Name{"ns2", "inner"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        EndElement{Name{"ns2", "outer"}},
-       CharData([]byte("\n  ")),
+       CharData("\n  "),
        StartElement{Name{"ns3", "name"}, []Attr{}},
-       CharData([]byte("\n    ")),
-       CharData([]byte("Some text here.")),
-       CharData([]byte("\n  ")),
+       CharData("\n    "),
+       CharData("Some text here."),
+       CharData("\n  "),
        EndElement{Name{"ns3", "name"}},
-       CharData([]byte("\n")),
+       CharData("\n"),
        EndElement{Name{"ns2", "body"}},
-       Comment([]byte(" missing final newline ")),
+       Comment(" missing final newline "),
 }
 
 const testInputAltEncoding = `
@@ -101,11 +99,11 @@ const testInputAltEncoding = `
 <TAG>VALUE</TAG>`
 
 var rawTokensAltEncoding = []Token{
-       CharData([]byte("\n")),
+       CharData("\n"),
        ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
-       CharData([]byte("\n")),
+       CharData("\n"),
        StartElement{Name{"", "tag"}, []Attr{}},
-       CharData([]byte("value")),
+       CharData("value"),
        EndElement{Name{"", "tag"}},
 }
 
@@ -270,21 +268,21 @@ var nestedDirectivesInput = `
 `
 
 var nestedDirectivesTokens = []Token{
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt ">">]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt "<">]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt '>'>]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt '<'>]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt '">'>]`)),
-       CharData([]byte("\n")),
-       Directive([]byte(`DOCTYPE [<!ENTITY xlt "'<">]`)),
-       CharData([]byte("\n")),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
+       CharData("\n"),
+       Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
+       CharData("\n"),
 }
 
 func TestNestedDirectives(t *testing.T) {
@@ -488,10 +486,13 @@ func TestCopyTokenStartElement(t *testing.T) {
        elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}
        var tok1 Token = elt
        tok2 := CopyToken(tok1)
+       if tok1.(StartElement).Attr[0].Value != "en" {
+               t.Error("CopyToken overwrote Attr[0]")
+       }
        if !reflect.DeepEqual(tok1, tok2) {
                t.Error("CopyToken(StartElement) != StartElement")
        }
-       elt.Attr[0] = Attr{Name{"", "lang"}, "de"}
+       tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"}
        if reflect.DeepEqual(tok1, tok2) {
                t.Error("CopyToken(CharData) uses same buffer.")
        }
index bc4a112c98f3d95a41068f8e58ecc72fa8d9772d..a2a9361866da418b44b8c318d93ee21b3efc4c94 100644 (file)
@@ -150,15 +150,15 @@ func processFiles(filenames []string, allFiles bool) {
                switch info, err := os.Stat(filename); {
                case err != nil:
                        report(err)
-               case info.IsRegular():
-                       if allFiles || isGoFilename(info.Name) {
-                               filenames[i] = filename
-                               i++
-                       }
-               case info.IsDirectory():
+               case info.IsDir():
                        if allFiles || *recursive {
                                processDirectory(filename)
                        }
+               default:
+                       if allFiles || isGoFilename(info.Name()) {
+                               filenames[i] = filename
+                               i++
+                       }
                }
        }
        fset := token.NewFileSet()
diff --git a/libgo/go/exp/gui/gui.go b/libgo/go/exp/gui/gui.go
deleted file mode 100644 (file)
index a69f83a..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 gui defines a basic graphical user interface programming model.
-package gui
-
-import (
-       "image"
-       "image/draw"
-)
-
-// A Window represents a single graphics window.
-type Window interface {
-       // Screen returns an editable Image for the window.
-       Screen() draw.Image
-       // FlushImage flushes changes made to Screen() back to screen.
-       FlushImage()
-       // EventChan returns a channel carrying UI events such as key presses,
-       // mouse movements and window resizes.
-       EventChan() <-chan interface{}
-       // Close closes the window.
-       Close() error
-}
-
-// A KeyEvent is sent for a key press or release.
-type KeyEvent struct {
-       // The value k represents key k being pressed.
-       // The value -k represents key k being released.
-       // The specific set of key values is not specified,
-       // but ordinary characters represent themselves.
-       Key int
-}
-
-// A MouseEvent is sent for a button press or release or for a mouse movement.
-type MouseEvent struct {
-       // Buttons is a bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right.
-       // It represents button state and not necessarily the state delta: bit 0
-       // being on means that the left mouse button is down, but does not imply
-       // that the same button was up in the previous MouseEvent.
-       Buttons int
-       // Loc is the location of the cursor.
-       Loc image.Point
-       // Nsec is the event's timestamp.
-       Nsec int64
-}
-
-// A ConfigEvent is sent each time the window's color model or size changes.
-// The client should respond by calling Window.Screen to obtain a new image.
-type ConfigEvent struct {
-       Config image.Config
-}
-
-// An ErrEvent is sent when an error occurs.
-type ErrEvent struct {
-       Err error
-}
diff --git a/libgo/go/exp/gui/x11/auth.go b/libgo/go/exp/gui/x11/auth.go
deleted file mode 100644 (file)
index 24e941c..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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 x11
-
-import (
-       "bufio"
-       "errors"
-       "io"
-       "os"
-)
-
-// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer.
-func readU16BE(r io.Reader, b []byte) (uint16, error) {
-       _, err := io.ReadFull(r, b[0:2])
-       if err != nil {
-               return 0, err
-       }
-       return uint16(b[0])<<8 + uint16(b[1]), nil
-}
-
-// readStr reads a length-prefixed string from r, using b as a scratch buffer.
-func readStr(r io.Reader, b []byte) (string, error) {
-       n, err := readU16BE(r, b)
-       if err != nil {
-               return "", err
-       }
-       if int(n) > len(b) {
-               return "", errors.New("Xauthority entry too long for buffer")
-       }
-       _, err = io.ReadFull(r, b[0:n])
-       if err != nil {
-               return "", err
-       }
-       return string(b[0:n]), nil
-}
-
-// readAuth reads the X authority file and returns the name/data pair for the display.
-// displayStr is the "12" out of a $DISPLAY like ":12.0".
-func readAuth(displayStr string) (name, data string, err error) {
-       // b is a scratch buffer to use and should be at least 256 bytes long
-       // (i.e. it should be able to hold a hostname).
-       var b [256]byte
-       // As per /usr/include/X11/Xauth.h.
-       const familyLocal = 256
-
-       fn := os.Getenv("XAUTHORITY")
-       if fn == "" {
-               home := os.Getenv("HOME")
-               if home == "" {
-                       err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set")
-                       return
-               }
-               fn = home + "/.Xauthority"
-       }
-       r, err := os.Open(fn)
-       if err != nil {
-               return
-       }
-       defer r.Close()
-       br := bufio.NewReader(r)
-
-       hostname, err := os.Hostname()
-       if err != nil {
-               return
-       }
-       for {
-               var family uint16
-               var addr, disp, name0, data0 string
-               family, err = readU16BE(br, b[0:2])
-               if err != nil {
-                       return
-               }
-               addr, err = readStr(br, b[0:])
-               if err != nil {
-                       return
-               }
-               disp, err = readStr(br, b[0:])
-               if err != nil {
-                       return
-               }
-               name0, err = readStr(br, b[0:])
-               if err != nil {
-                       return
-               }
-               data0, err = readStr(br, b[0:])
-               if err != nil {
-                       return
-               }
-               if family == familyLocal && addr == hostname && disp == displayStr {
-                       return name0, data0, nil
-               }
-       }
-       panic("unreachable")
-}
diff --git a/libgo/go/exp/gui/x11/conn.go b/libgo/go/exp/gui/x11/conn.go
deleted file mode 100644 (file)
index 15afc65..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-// 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 x11 implements an X11 backend for the exp/gui package.
-//
-// The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf.
-// A summary of the wire format can be found in XCB's xproto.xml.
-package x11
-
-import (
-       "bufio"
-       "errors"
-       "exp/gui"
-       "image"
-       "image/draw"
-       "io"
-       "log"
-       "net"
-       "os"
-       "strconv"
-       "strings"
-       "time"
-)
-
-type resID uint32 // X resource IDs.
-
-// TODO(nigeltao): Handle window resizes.
-const (
-       windowHeight = 600
-       windowWidth  = 800
-)
-
-const (
-       keymapLo = 8
-       keymapHi = 255
-)
-
-type conn struct {
-       c io.Closer
-       r *bufio.Reader
-       w *bufio.Writer
-
-       gc, window, root, visual resID
-
-       img        *image.RGBA
-       eventc     chan interface{}
-       mouseState gui.MouseEvent
-
-       buf [256]byte // General purpose scratch buffer.
-
-       flush     chan bool
-       flushBuf0 [24]byte
-       flushBuf1 [4 * 1024]byte
-}
-
-// writeSocket runs in its own goroutine, serving both FlushImage calls
-// directly from the exp/gui client and indirectly from X expose events.
-// It paints c.img to the X server via PutImage requests.
-func (c *conn) writeSocket() {
-       defer c.c.Close()
-       for _ = range c.flush {
-               b := c.img.Bounds()
-               if b.Empty() {
-                       continue
-               }
-               // Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over
-               // this limit, we send PutImage for each row of the image, rather than trying to paint
-               // the entire image in one X request. This approach could easily be optimized (or the
-               // X protocol may have an escape sequence to delimit very large requests).
-               // TODO(nigeltao): See what XCB's xcb_put_image does in this situation.
-               units := 6 + b.Dx()
-               if units > 0xffff || b.Dy() > 0xffff {
-                       log.Print("x11: window is too large for PutImage")
-                       return
-               }
-
-               c.flushBuf0[0] = 0x48 // PutImage opcode.
-               c.flushBuf0[1] = 0x02 // XCB_IMAGE_FORMAT_Z_PIXMAP.
-               c.flushBuf0[2] = uint8(units)
-               c.flushBuf0[3] = uint8(units >> 8)
-               setU32LE(c.flushBuf0[4:8], uint32(c.window))
-               setU32LE(c.flushBuf0[8:12], uint32(c.gc))
-               setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx()))
-               c.flushBuf0[21] = 0x18 // depth = 24 bits.
-
-               for y := b.Min.Y; y < b.Max.Y; y++ {
-                       setU32LE(c.flushBuf0[16:20], uint32(y<<16))
-                       if _, err := c.w.Write(c.flushBuf0[:24]); err != nil {
-                               if err != io.EOF {
-                                       log.Println("x11:", err)
-                               }
-                               return
-                       }
-                       p := c.img.Pix[(y-b.Min.Y)*c.img.Stride:]
-                       for x, dx := 0, 4*b.Dx(); x < dx; {
-                               nx := dx - x
-                               if nx > len(c.flushBuf1) {
-                                       nx = len(c.flushBuf1) &^ 3
-                               }
-                               for i := 0; i < nx; i += 4 {
-                                       // X11's order is BGRX, not RGBA.
-                                       c.flushBuf1[i+0] = p[x+i+2]
-                                       c.flushBuf1[i+1] = p[x+i+1]
-                                       c.flushBuf1[i+2] = p[x+i+0]
-                               }
-                               x += nx
-                               if _, err := c.w.Write(c.flushBuf1[:nx]); err != nil {
-                                       if err != io.EOF {
-                                               log.Println("x11:", err)
-                                       }
-                                       return
-                               }
-                       }
-               }
-               if err := c.w.Flush(); err != nil {
-                       if err != io.EOF {
-                               log.Println("x11:", err)
-                       }
-                       return
-               }
-       }
-}
-
-func (c *conn) Screen() draw.Image { return c.img }
-
-func (c *conn) FlushImage() {
-       select {
-       case c.flush <- false:
-               // Flush notification sent.
-       default:
-               // Could not send.
-               // Flush notification must be pending already.
-       }
-}
-
-func (c *conn) Close() error {
-       // Shut down the writeSocket goroutine. This will close the socket to the
-       // X11 server, which will cause c.eventc to close.
-       close(c.flush)
-       for _ = range c.eventc {
-               // Drain the channel to allow the readSocket goroutine to shut down.
-       }
-       return nil
-}
-
-func (c *conn) EventChan() <-chan interface{} { return c.eventc }
-
-// readSocket runs in its own goroutine, reading X events and sending gui
-// events on c's EventChan.
-func (c *conn) readSocket() {
-       var (
-               keymap            [256][]int
-               keysymsPerKeycode int
-       )
-       defer close(c.eventc)
-       for {
-               // X events are always 32 bytes long.
-               if _, err := io.ReadFull(c.r, c.buf[:32]); err != nil {
-                       if err != io.EOF {
-                               c.eventc <- gui.ErrEvent{err}
-                       }
-                       return
-               }
-               switch c.buf[0] {
-               case 0x01: // Reply from a request (e.g. GetKeyboardMapping).
-                       cookie := int(c.buf[3])<<8 | int(c.buf[2])
-                       if cookie != 1 {
-                               // We issued only one request (GetKeyboardMapping) with a cookie of 1,
-                               // so we shouldn't get any other reply from the X server.
-                               c.eventc <- gui.ErrEvent{errors.New("x11: unexpected cookie")}
-                               return
-                       }
-                       keysymsPerKeycode = int(c.buf[1])
-                       b := make([]int, 256*keysymsPerKeycode)
-                       for i := range keymap {
-                               keymap[i] = b[i*keysymsPerKeycode : (i+1)*keysymsPerKeycode]
-                       }
-                       for i := keymapLo; i <= keymapHi; i++ {
-                               m := keymap[i]
-                               for j := range m {
-                                       u, err := readU32LE(c.r, c.buf[:4])
-                                       if err != nil {
-                                               if err != io.EOF {
-                                                       c.eventc <- gui.ErrEvent{err}
-                                               }
-                                               return
-                                       }
-                                       m[j] = int(u)
-                               }
-                       }
-               case 0x02, 0x03: // Key press, key release.
-                       // X Keyboard Encoding is documented at http://tronche.com/gui/x/xlib/input/keyboard-encoding.html
-                       // TODO(nigeltao): Do we need to implement the "MODE SWITCH / group modifier" feature
-                       // or is that some no-longer-used X construct?
-                       if keysymsPerKeycode < 2 {
-                               // Either we haven't yet received the GetKeyboardMapping reply or
-                               // the X server has sent one that's too short.
-                               continue
-                       }
-                       keycode := int(c.buf[1])
-                       shift := int(c.buf[28]) & 0x01
-                       keysym := keymap[keycode][shift]
-                       if keysym == 0 {
-                               keysym = keymap[keycode][0]
-                       }
-                       // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send
-                       // the same int down the channel as the sent on just the A key?
-                       // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or
-                       // is that outside the scope of the gui.Window interface?
-                       if c.buf[0] == 0x03 {
-                               keysym = -keysym
-                       }
-                       c.eventc <- gui.KeyEvent{keysym}
-               case 0x04, 0x05: // Button press, button release.
-                       mask := 1 << (c.buf[1] - 1)
-                       if c.buf[0] == 0x04 {
-                               c.mouseState.Buttons |= mask
-                       } else {
-                               c.mouseState.Buttons &^= mask
-                       }
-                       c.mouseState.Nsec = time.Nanoseconds()
-                       c.eventc <- c.mouseState
-               case 0x06: // Motion notify.
-                       c.mouseState.Loc.X = int(int16(c.buf[25])<<8 | int16(c.buf[24]))
-                       c.mouseState.Loc.Y = int(int16(c.buf[27])<<8 | int16(c.buf[26]))
-                       c.mouseState.Nsec = time.Nanoseconds()
-                       c.eventc <- c.mouseState
-               case 0x0c: // Expose.
-                       // A single user action could trigger multiple expose events (e.g. if moving another
-                       // window with XShape'd rounded corners over our window). In that case, the X server will
-                       // send a uint16 count (in bytes 16-17) of the number of additional expose events coming.
-                       // We could parse each event for the (x, y, width, height) and maintain a minimal dirty
-                       // rectangle, but for now, the simplest approach is to paint the entire window, when
-                       // receiving the final event in the series.
-                       if c.buf[17] == 0 && c.buf[16] == 0 {
-                               // TODO(nigeltao): Should we ignore the very first expose event? A freshly mapped window
-                               // will trigger expose, but until the first c.FlushImage call, there's probably nothing to
-                               // paint but black. For an 800x600 window, at 4 bytes per pixel, each repaint writes about
-                               // 2MB over the socket.
-                               c.FlushImage()
-                       }
-                       // TODO(nigeltao): Should we listen to DestroyNotify (0x11) and ResizeRequest (0x19) events?
-                       // What about EnterNotify (0x07) and LeaveNotify (0x08)?
-               }
-       }
-}
-
-// connect connects to the X server given by the full X11 display name (e.g.
-// ":12.0") and returns the connection as well as the portion of the full name
-// that is the display number (e.g. "12").
-// Examples:
-//     connect(":1")                 // calls net.Dial("unix", "", "/tmp/.X11-unix/X1"), displayStr="1"
-//     connect("/tmp/launch-123/:0") // calls net.Dial("unix", "", "/tmp/launch-123/:0"), displayStr="0"
-//     connect("hostname:2.1")       // calls net.Dial("tcp", "", "hostname:6002"), displayStr="2"
-//     connect("tcp/hostname:1.0")   // calls net.Dial("tcp", "", "hostname:6001"), displayStr="1"
-func connect(display string) (conn net.Conn, displayStr string, err error) {
-       colonIdx := strings.LastIndex(display, ":")
-       if colonIdx < 0 {
-               return nil, "", errors.New("bad display: " + display)
-       }
-       // Parse the section before the colon.
-       var protocol, host, socket string
-       if display[0] == '/' {
-               socket = display[:colonIdx]
-       } else {
-               if i := strings.LastIndex(display, "/"); i < 0 {
-                       // The default protocol is TCP.
-                       protocol = "tcp"
-                       host = display[:colonIdx]
-               } else {
-                       protocol = display[:i]
-                       host = display[i+1 : colonIdx]
-               }
-       }
-       // Parse the section after the colon.
-       after := display[colonIdx+1:]
-       if after == "" {
-               return nil, "", errors.New("bad display: " + display)
-       }
-       if i := strings.LastIndex(after, "."); i < 0 {
-               displayStr = after
-       } else {
-               displayStr = after[:i]
-       }
-       displayInt, err := strconv.Atoi(displayStr)
-       if err != nil || displayInt < 0 {
-               return nil, "", errors.New("bad display: " + display)
-       }
-       // Make the connection.
-       if socket != "" {
-               conn, err = net.Dial("unix", socket+":"+displayStr)
-       } else if host != "" {
-               conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt))
-       } else {
-               conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr)
-       }
-       if err != nil {
-               return nil, "", errors.New("cannot connect to " + display + ": " + err.Error())
-       }
-       return
-}
-
-// authenticate authenticates ourselves with the X server.
-// displayStr is the "12" out of ":12.0".
-func authenticate(w *bufio.Writer, displayStr string) error {
-       key, value, err := readAuth(displayStr)
-       if err != nil {
-               return err
-       }
-       // Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
-       if len(key) != 18 || len(value) != 16 {
-               return errors.New("unsupported Xauth")
-       }
-       // 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0.
-       // 0x0012 and 0x0010 means the auth key and value have lengths 18 and 16.
-       // The final 0x0000 is padding, so that the string length is a multiple of 4.
-       _, err = io.WriteString(w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00")
-       if err != nil {
-               return err
-       }
-       _, err = io.WriteString(w, key)
-       if err != nil {
-               return err
-       }
-       // Again, the 0x0000 is padding.
-       _, err = io.WriteString(w, "\x00\x00")
-       if err != nil {
-               return err
-       }
-       _, err = io.WriteString(w, value)
-       if err != nil {
-               return err
-       }
-       err = w.Flush()
-       if err != nil {
-               return err
-       }
-       return nil
-}
-
-// readU8 reads a uint8 from r, using b as a scratch buffer.
-func readU8(r io.Reader, b []byte) (uint8, error) {
-       _, err := io.ReadFull(r, b[:1])
-       if err != nil {
-               return 0, err
-       }
-       return uint8(b[0]), nil
-}
-
-// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer.
-func readU16LE(r io.Reader, b []byte) (uint16, error) {
-       _, err := io.ReadFull(r, b[:2])
-       if err != nil {
-               return 0, err
-       }
-       return uint16(b[0]) | uint16(b[1])<<8, nil
-}
-
-// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer.
-func readU32LE(r io.Reader, b []byte) (uint32, error) {
-       _, err := io.ReadFull(r, b[:4])
-       if err != nil {
-               return 0, err
-       }
-       return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil
-}
-
-// setU32LE sets b[:4] to be the little-endian representation of u.
-func setU32LE(b []byte, u uint32) {
-       b[0] = byte((u >> 0) & 0xff)
-       b[1] = byte((u >> 8) & 0xff)
-       b[2] = byte((u >> 16) & 0xff)
-       b[3] = byte((u >> 24) & 0xff)
-}
-
-// checkPixmapFormats checks that we have an agreeable X pixmap Format.
-func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err error) {
-       for i := 0; i < n; i++ {
-               _, err = io.ReadFull(r, b[:8])
-               if err != nil {
-                       return
-               }
-               // Byte 0 is depth, byte 1 is bits-per-pixel, byte 2 is scanline-pad, the rest (5) is padding.
-               if b[0] == 24 && b[1] == 32 {
-                       agree = true
-               }
-       }
-       return
-}
-
-// checkDepths checks that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType).
-func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err error) {
-       for i := 0; i < n; i++ {
-               var depth, visualsLen uint16
-               depth, err = readU16LE(r, b)
-               if err != nil {
-                       return
-               }
-               depth &= 0xff
-               visualsLen, err = readU16LE(r, b)
-               if err != nil {
-                       return
-               }
-               // Ignore 4 bytes of padding.
-               _, err = io.ReadFull(r, b[:4])
-               if err != nil {
-                       return
-               }
-               for j := 0; j < int(visualsLen); j++ {
-                       // Read 24 bytes: visual(4), class(1), bits per rgb value(1), colormap entries(2),
-                       // red mask(4), green mask(4), blue mask(4), padding(4).
-                       v, _ := readU32LE(r, b)
-                       _, _ = readU32LE(r, b)
-                       rm, _ := readU32LE(r, b)
-                       gm, _ := readU32LE(r, b)
-                       bm, _ := readU32LE(r, b)
-                       _, err = readU32LE(r, b)
-                       if err != nil {
-                               return
-                       }
-                       if v == visual && rm == 0xff0000 && gm == 0xff00 && bm == 0xff && depth == 24 {
-                               agree = true
-                       }
-               }
-       }
-       return
-}
-
-// checkScreens checks that we have an agreeable X Screen.
-func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err error) {
-       for i := 0; i < n; i++ {
-               var root0, visual0, x uint32
-               root0, err = readU32LE(r, b)
-               if err != nil {
-                       return
-               }
-               // Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks,
-               // width and height (pixels), width and height (mm), min and max installed maps.
-               _, err = io.ReadFull(r, b[:28])
-               if err != nil {
-                       return
-               }
-               visual0, err = readU32LE(r, b)
-               if err != nil {
-                       return
-               }
-               // Next 4 bytes: backing stores, save unders, root depth, allowed depths length.
-               x, err = readU32LE(r, b)
-               if err != nil {
-                       return
-               }
-               nDepths := int(x >> 24)
-               var agree bool
-               agree, err = checkDepths(r, b, nDepths, visual0)
-               if err != nil {
-                       return
-               }
-               if agree && root == 0 {
-                       root = root0
-                       visual = visual0
-               }
-       }
-       return
-}
-
-// handshake performs the protocol handshake with the X server, and ensures
-// that the server provides a compatible Screen, Depth, etc.
-func (c *conn) handshake() error {
-       _, err := io.ReadFull(c.r, c.buf[:8])
-       if err != nil {
-               return err
-       }
-       // Byte 0 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0).
-       if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 {
-               return errors.New("unsupported X version")
-       }
-       // Ignore the release number.
-       _, err = io.ReadFull(c.r, c.buf[:4])
-       if err != nil {
-               return err
-       }
-       // Read the resource ID base.
-       resourceIdBase, err := readU32LE(c.r, c.buf[:4])
-       if err != nil {
-               return err
-       }
-       // Read the resource ID mask.
-       resourceIdMask, err := readU32LE(c.r, c.buf[:4])
-       if err != nil {
-               return err
-       }
-       if resourceIdMask < 256 {
-               return errors.New("X resource ID mask is too small")
-       }
-       // Ignore the motion buffer size.
-       _, err = io.ReadFull(c.r, c.buf[:4])
-       if err != nil {
-               return err
-       }
-       // Read the vendor length and round it up to a multiple of 4,
-       // for X11 protocol alignment reasons.
-       vendorLen, err := readU16LE(c.r, c.buf[:2])
-       if err != nil {
-               return err
-       }
-       vendorLen = (vendorLen + 3) &^ 3
-       // Read the maximum request length.
-       maxReqLen, err := readU16LE(c.r, c.buf[:2])
-       if err != nil {
-               return err
-       }
-       if maxReqLen != 0xffff {
-               return errors.New("unsupported X maximum request length")
-       }
-       // Read the roots length.
-       rootsLen, err := readU8(c.r, c.buf[:1])
-       if err != nil {
-               return err
-       }
-       // Read the pixmap formats length.
-       pixmapFormatsLen, err := readU8(c.r, c.buf[:1])
-       if err != nil {
-               return err
-       }
-       // Ignore some things that we don't care about (totaling 10 + vendorLen bytes):
-       // imageByteOrder(1), bitmapFormatBitOrder(1), bitmapFormatScanlineUnit(1) bitmapFormatScanlinePad(1),
-       // minKeycode(1), maxKeycode(1), padding(4), vendor (vendorLen).
-       if 10+int(vendorLen) > cap(c.buf) {
-               return errors.New("unsupported X vendor")
-       }
-       _, err = io.ReadFull(c.r, c.buf[:10+int(vendorLen)])
-       if err != nil {
-               return err
-       }
-       // Check that we have an agreeable pixmap format.
-       agree, err := checkPixmapFormats(c.r, c.buf[:8], int(pixmapFormatsLen))
-       if err != nil {
-               return err
-       }
-       if !agree {
-               return errors.New("unsupported X pixmap formats")
-       }
-       // Check that we have an agreeable screen.
-       root, visual, err := checkScreens(c.r, c.buf[:24], int(rootsLen))
-       if err != nil {
-               return err
-       }
-       if root == 0 || visual == 0 {
-               return errors.New("unsupported X screen")
-       }
-       c.gc = resID(resourceIdBase)
-       c.window = resID(resourceIdBase + 1)
-       c.root = resID(root)
-       c.visual = resID(visual)
-       return nil
-}
-
-// NewWindow calls NewWindowDisplay with $DISPLAY.
-func NewWindow() (gui.Window, error) {
-       display := os.Getenv("DISPLAY")
-       if len(display) == 0 {
-               return nil, errors.New("$DISPLAY not set")
-       }
-       return NewWindowDisplay(display)
-}
-
-// NewWindowDisplay returns a new gui.Window, backed by a newly created and
-// mapped X11 window. The X server to connect to is specified by the display
-// string, such as ":1".
-func NewWindowDisplay(display string) (gui.Window, error) {
-       socket, displayStr, err := connect(display)
-       if err != nil {
-               return nil, err
-       }
-       c := new(conn)
-       c.c = socket
-       c.r = bufio.NewReader(socket)
-       c.w = bufio.NewWriter(socket)
-       err = authenticate(c.w, displayStr)
-       if err != nil {
-               return nil, err
-       }
-       err = c.handshake()
-       if err != nil {
-               return nil, err
-       }
-
-       // Now that we're connected, show a window, via three X protocol messages.
-       // First, issue a GetKeyboardMapping request. This is the first request, and
-       // will be associated with a cookie of 1.
-       setU32LE(c.buf[0:4], 0x00020065) // 0x65 is the GetKeyboardMapping opcode, and the message is 2 x 4 bytes long.
-       setU32LE(c.buf[4:8], uint32((keymapHi-keymapLo+1)<<8|keymapLo))
-       // Second, create a graphics context (GC).
-       setU32LE(c.buf[8:12], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long.
-       setU32LE(c.buf[12:16], uint32(c.gc))
-       setU32LE(c.buf[16:20], uint32(c.root))
-       setU32LE(c.buf[20:24], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES.
-       setU32LE(c.buf[24:28], 0x00000000) // The Foreground is black.
-       setU32LE(c.buf[28:32], 0x00000000) // GraphicsExposures' value is unused.
-       // Third, create the window.
-       setU32LE(c.buf[32:36], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long.
-       setU32LE(c.buf[36:40], uint32(c.window))
-       setU32LE(c.buf[40:44], uint32(c.root))
-       setU32LE(c.buf[44:48], 0x00000000) // Initial (x, y) is (0, 0).
-       setU32LE(c.buf[48:52], windowHeight<<16|windowWidth)
-       setU32LE(c.buf[52:56], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1.
-       setU32LE(c.buf[56:60], uint32(c.visual))
-       setU32LE(c.buf[60:64], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK.
-       setU32LE(c.buf[64:68], 0x00000000) // The Back-Pixel is black.
-       setU32LE(c.buf[68:72], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks.
-       // Fourth, map the window.
-       setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long.
-       setU32LE(c.buf[76:80], uint32(c.window))
-       // Write the bytes.
-       _, err = c.w.Write(c.buf[:80])
-       if err != nil {
-               return nil, err
-       }
-       err = c.w.Flush()
-       if err != nil {
-               return nil, err
-       }
-
-       c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight))
-       c.eventc = make(chan interface{}, 16)
-       c.flush = make(chan bool, 1)
-       go c.readSocket()
-       go c.writeSocket()
-       return c, nil
-}
index 91a388421d17b40724b5660b214bb3b1866a4b6c..f0bcca291065d17c058b1cca1ba27d2ea5f5ad03 100644 (file)
@@ -7,7 +7,7 @@
 //
 // Code simply using databases should use package sql.
 //
-// Drivers only need to be aware of a subset of Go's types.  The db package
+// Drivers only need to be aware of a subset of Go's types.  The sql package
 // will convert all types into one of the following:
 //
 //   int64
@@ -94,12 +94,35 @@ type Result interface {
 // used by multiple goroutines concurrently.
 type Stmt interface {
        // Close closes the statement.
+       //
+       // Closing a statement should not interrupt any outstanding
+       // query created from that statement. That is, the following
+       // order of operations is valid:
+       //
+       //  * create a driver statement
+       //  * call Query on statement, returning Rows
+       //  * close the statement
+       //  * read from Rows
+       //
+       // If closing a statement invalidates currently-running
+       // queries, the final step above will incorrectly fail.
+       //
+       // TODO(bradfitz): possibly remove the restriction above, if
+       // enough driver authors object and find it complicates their
+       // code too much. The sql package could be smarter about
+       // refcounting the statement and closing it at the appropriate
+       // time.
        Close() error
 
        // NumInput returns the number of placeholder parameters.
-       // -1 means the driver doesn't know how to count the number of
-       // placeholders, so we won't sanity check input here and instead let the
-       // driver deal with errors.
+       //
+       // If NumInput returns >= 0, the sql package will sanity check
+       // argument counts from callers and return errors to the caller
+       // before the statement's Exec or Query methods are called.
+       //
+       // NumInput may also return -1, if the driver doesn't know
+       // its number of placeholders. In that case, the sql package
+       // will not sanity check Exec or Query argument counts.
        NumInput() int
 
        // Exec executes a query that doesn't return rows, such
index 17028e2cc388401fec45cecff753b79a7f15403d..2474a86f644c46fc5d5988fad918fd7bcf82c9b3 100644 (file)
@@ -90,6 +90,8 @@ type fakeStmt struct {
        cmd   string
        table string
 
+       closed bool
+
        colName      []string      // used by CREATE, INSERT, SELECT (selected columns)
        colType      []string      // used by CREATE
        colValue     []interface{} // used by INSERT (mix of strings and "?" for bound params)
@@ -232,6 +234,9 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e
        stmt.table = parts[0]
        stmt.colName = strings.Split(parts[1], ",")
        for n, colspec := range strings.Split(parts[2], ",") {
+               if colspec == "" {
+                       continue
+               }
                nameVal := strings.Split(colspec, "=")
                if len(nameVal) != 2 {
                        return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
@@ -342,10 +347,16 @@ func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
 }
 
 func (s *fakeStmt) Close() error {
+       s.closed = true
        return nil
 }
 
+var errClosed = errors.New("fakedb: statement has been closed")
+
 func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
+       if s.closed {
+               return nil, errClosed
+       }
        err := checkSubsetTypes(args)
        if err != nil {
                return nil, err
@@ -405,6 +416,9 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
 }
 
 func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) {
+       if s.closed {
+               return nil, errClosed
+       }
        err := checkSubsetTypes(args)
        if err != nil {
                return nil, err
index c055fdd68c63648194d7165790e23f33efaf8449..f17d12eaa13e27f69cd5eec288688617b02dd3ef 100644 (file)
@@ -344,25 +344,26 @@ func (tx *Tx) Rollback() error {
        return tx.txi.Rollback()
 }
 
-// Prepare creates a prepared statement.
+// Prepare creates a prepared statement for use within a transaction.
 //
-// The statement is only valid within the scope of this transaction.
+// The returned statement operates within the transaction and can no longer
+// be used once the transaction has been committed or rolled back.
+//
+// To use an existing prepared statement on this transaction, see Tx.Stmt.
 func (tx *Tx) Prepare(query string) (*Stmt, error) {
-       // TODO(bradfitz): the restriction that the returned statement
-       // is only valid for this Transaction is lame and negates a
-       // lot of the benefit of prepared statements.  We could be
-       // more efficient here and either provide a method to take an
-       // existing Stmt (created on perhaps a different Conn), and
-       // re-create it on this Conn if necessary. Or, better: keep a
-       // map in DB of query string to Stmts, and have Stmt.Execute
-       // do the right thing and re-prepare if the Conn in use
-       // doesn't have that prepared statement.  But we'll want to
-       // avoid caching the statement in the case where we only call
-       // conn.Prepare implicitly (such as in db.Exec or tx.Exec),
-       // but the caller package can't be holding a reference to the
-       // returned statement.  Perhaps just looking at the reference
-       // count (by noting Stmt.Close) would be enough. We might also
-       // want a finalizer on Stmt to drop the reference count.
+       // TODO(bradfitz): We could be more efficient here and either
+       // provide a method to take an existing Stmt (created on
+       // perhaps a different Conn), and re-create it on this Conn if
+       // necessary. Or, better: keep a map in DB of query string to
+       // Stmts, and have Stmt.Execute do the right thing and
+       // re-prepare if the Conn in use doesn't have that prepared
+       // statement.  But we'll want to avoid caching the statement
+       // in the case where we only call conn.Prepare implicitly
+       // (such as in db.Exec or tx.Exec), but the caller package
+       // can't be holding a reference to the returned statement.
+       // Perhaps just looking at the reference count (by noting
+       // Stmt.Close) would be enough. We might also want a finalizer
+       // on Stmt to drop the reference count.
        ci, err := tx.grabConn()
        if err != nil {
                return nil, err
@@ -383,6 +384,39 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
        return stmt, nil
 }
 
+// Stmt returns a transaction-specific prepared statement from
+// an existing statement.
+//
+// Example:
+//  updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
+//  ...
+//  tx, err := db.Begin()
+//  ...
+//  res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
+func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
+       // TODO(bradfitz): optimize this. Currently this re-prepares
+       // each time.  This is fine for now to illustrate the API but
+       // we should really cache already-prepared statements
+       // per-Conn. See also the big comment in Tx.Prepare.
+
+       if tx.db != stmt.db {
+               return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")}
+       }
+       ci, err := tx.grabConn()
+       if err != nil {
+               return &Stmt{stickyErr: err}
+       }
+       defer tx.releaseConn()
+       si, err := ci.Prepare(stmt.query)
+       return &Stmt{
+               db:        tx.db,
+               tx:        tx,
+               txsi:      si,
+               query:     stmt.query,
+               stickyErr: err,
+       }
+}
+
 // Exec executes a query that doesn't return rows.
 // For example: an INSERT and UPDATE.
 func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
@@ -448,8 +482,9 @@ type connStmt struct {
 // Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines.
 type Stmt struct {
        // Immutable:
-       db    *DB    // where we came from
-       query string // that created the Sttm
+       db        *DB    // where we came from
+       query     string // that created the Stmt
+       stickyErr error  // if non-nil, this error is returned for all operations
 
        // If in a transaction, else both nil:
        tx   *Tx
@@ -513,6 +548,9 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
 // statement, a function to call to release the connection, and a
 // statement bound to that connection.
 func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, err error) {
+       if s.stickyErr != nil {
+               return nil, nil, nil, s.stickyErr
+       }
        s.mu.Lock()
        if s.closed {
                s.mu.Unlock()
@@ -621,6 +659,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row {
 
 // Close closes the statement.
 func (s *Stmt) Close() error {
+       if s.stickyErr != nil {
+               return s.stickyErr
+       }
        s.mu.Lock()
        defer s.mu.Unlock()
        if s.closed {
index d365f6ba190970d0f0124a8ea0208f787a6c0844..4f8318d26ef7972884804f2a7a6dde463e7bae0b 100644 (file)
@@ -5,6 +5,7 @@
 package sql
 
 import (
+       "reflect"
        "strings"
        "testing"
 )
@@ -22,7 +23,6 @@ func newTestDB(t *testing.T, name string) *DB {
                exec(t, db, "INSERT|people|name=Alice,age=?", 1)
                exec(t, db, "INSERT|people|name=Bob,age=?", 2)
                exec(t, db, "INSERT|people|name=Chris,age=?", 3)
-
        }
        return db
 }
@@ -42,6 +42,40 @@ func closeDB(t *testing.T, db *DB) {
 }
 
 func TestQuery(t *testing.T) {
+       db := newTestDB(t, "people")
+       defer closeDB(t, db)
+       rows, err := db.Query("SELECT|people|age,name|")
+       if err != nil {
+               t.Fatalf("Query: %v", err)
+       }
+       type row struct {
+               age  int
+               name string
+       }
+       got := []row{}
+       for rows.Next() {
+               var r row
+               err = rows.Scan(&r.age, &r.name)
+               if err != nil {
+                       t.Fatalf("Scan: %v", err)
+               }
+               got = append(got, r)
+       }
+       err = rows.Err()
+       if err != nil {
+               t.Fatalf("Err: %v", err)
+       }
+       want := []row{
+               {age: 1, name: "Alice"},
+               {age: 2, name: "Bob"},
+               {age: 3, name: "Chris"},
+       }
+       if !reflect.DeepEqual(got, want) {
+               t.Logf(" got: %#v\nwant: %#v", got, want)
+       }
+}
+
+func TestQueryRow(t *testing.T) {
        db := newTestDB(t, "people")
        defer closeDB(t, db)
        var name string
@@ -75,6 +109,24 @@ func TestQuery(t *testing.T) {
        }
 }
 
+func TestStatementErrorAfterClose(t *testing.T) {
+       db := newTestDB(t, "people")
+       defer closeDB(t, db)
+       stmt, err := db.Prepare("SELECT|people|age|name=?")
+       if err != nil {
+               t.Fatalf("Prepare: %v", err)
+       }
+       err = stmt.Close()
+       if err != nil {
+               t.Fatalf("Close: %v", err)
+       }
+       var name string
+       err = stmt.QueryRow("foo").Scan(&name)
+       if err == nil {
+               t.Errorf("expected error from QueryRow.Scan after Stmt.Close")
+       }
+}
+
 func TestStatementQueryRow(t *testing.T) {
        db := newTestDB(t, "people")
        defer closeDB(t, db)
@@ -114,7 +166,7 @@ func TestBogusPreboundParameters(t *testing.T) {
        }
 }
 
-func TestDb(t *testing.T) {
+func TestExec(t *testing.T) {
        db := newTestDB(t, "foo")
        defer closeDB(t, db)
        exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
@@ -154,3 +206,25 @@ func TestDb(t *testing.T) {
                }
        }
 }
+
+func TestTxStmt(t *testing.T) {
+       db := newTestDB(t, "")
+       defer closeDB(t, db)
+       exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
+       stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
+       if err != nil {
+               t.Fatalf("Stmt, err = %v, %v", stmt, err)
+       }
+       tx, err := db.Begin()
+       if err != nil {
+               t.Fatalf("Begin = %v", err)
+       }
+       _, err = tx.Stmt(stmt).Exec("Bobby", 7)
+       if err != nil {
+               t.Fatalf("Exec = %v", err)
+       }
+       err = tx.Commit()
+       if err != nil {
+               t.Fatalf("Commit = %v", err)
+       }
+}
index 6ff8203ce27d6cb29f37c9317f60cd111407ca19..9d75f37de748fe35fef2f9f0bf38bb80a0ffef6c 100644 (file)
@@ -244,13 +244,13 @@ func (c *channel) Write(data []byte) (n int, err error) {
 
                packet := make([]byte, 1+4+4+len(todo))
                packet[0] = msgChannelData
-               packet[1] = byte(c.theirId) >> 24
-               packet[2] = byte(c.theirId) >> 16
-               packet[3] = byte(c.theirId) >> 8
+               packet[1] = byte(c.theirId >> 24)
+               packet[2] = byte(c.theirId >> 16)
+               packet[3] = byte(c.theirId >> 8)
                packet[4] = byte(c.theirId)
-               packet[5] = byte(len(todo)) >> 24
-               packet[6] = byte(len(todo)) >> 16
-               packet[7] = byte(len(todo)) >> 8
+               packet[5] = byte(len(todo) >> 24)
+               packet[6] = byte(len(todo) >> 16)
+               packet[7] = byte(len(todo) >> 8)
                packet[8] = byte(len(todo))
                copy(packet[9:], todo)
 
index 24569ad9389bd65d6c026a208606660ecd75d032..429dee975bce754d9876a5b1ea12476bca4837ee 100644 (file)
@@ -172,40 +172,12 @@ func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
        marshalInt(K, kInt)
        h.Write(K)
 
-       H := h.Sum()
+       H := h.Sum(nil)
 
        return H, K, nil
 }
 
-// openChan opens a new client channel. The most common session type is "session". 
-// The full set of valid session types are listed in RFC 4250 4.9.1.
-func (c *ClientConn) openChan(typ string) (*clientChan, error) {
-       ch := c.newChan(c.transport)
-       if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
-               ChanType:      typ,
-               PeersId:       ch.id,
-               PeersWindow:   1 << 14,
-               MaxPacketSize: 1 << 15, // RFC 4253 6.1
-       })); err != nil {
-               c.chanlist.remove(ch.id)
-               return nil, err
-       }
-       // wait for response
-       switch msg := (<-ch.msg).(type) {
-       case *channelOpenConfirmMsg:
-               ch.peersId = msg.MyId
-               ch.win <- int(msg.MyWindow)
-       case *channelOpenFailureMsg:
-               c.chanlist.remove(ch.id)
-               return nil, errors.New(msg.Message)
-       default:
-               c.chanlist.remove(ch.id)
-               return nil, errors.New("Unexpected packet")
-       }
-       return ch, nil
-}
-
-// mainloop reads incoming messages and routes channel messages
+// mainLoop reads incoming messages and routes channel messages
 // to their respective ClientChans.
 func (c *ClientConn) mainLoop() {
        // TODO(dfc) signal the underlying close to all channels
@@ -271,7 +243,7 @@ func (c *ClientConn) mainLoop() {
                        case *windowAdjustMsg:
                                c.getChan(msg.PeersId).win <- int(msg.AdditionalBytes)
                        default:
-                               fmt.Printf("mainLoop: unhandled %#v\n", msg)
+                               fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg)
                        }
                }
        }
@@ -338,27 +310,16 @@ func newClientChan(t *transport, id uint32) *clientChan {
 // Close closes the channel. This does not close the underlying connection.
 func (c *clientChan) Close() error {
        return c.writePacket(marshal(msgChannelClose, channelCloseMsg{
-               PeersId: c.id,
+               PeersId: c.peersId,
        }))
 }
 
-func (c *clientChan) sendChanReq(req channelRequestMsg) error {
-       if err := c.writePacket(marshal(msgChannelRequest, req)); err != nil {
-               return err
-       }
-       msg := <-c.msg
-       if _, ok := msg.(*channelRequestSuccessMsg); ok {
-               return nil
-       }
-       return fmt.Errorf("failed to complete request: %s, %#v", req.Request, msg)
-}
-
 // Thread safe channel list.
 type chanlist struct {
        // protects concurrent access to chans
        sync.Mutex
        // chans are indexed by the local id of the channel, clientChan.id.
-       // The PeersId value of messages received by ClientConn.mainloop is
+       // The PeersId value of messages received by ClientConn.mainLoop is
        // used to locate the right local clientChan in this slice.
        chans []*clientChan
 }
@@ -395,7 +356,7 @@ func (c *chanlist) remove(id uint32) {
 // A chanWriter represents the stdin of a remote process.
 type chanWriter struct {
        win          chan int // receives window adjustments
-       id           uint32   // this channel's id
+       peersId      uint32   // the peer's id
        rwin         int      // current rwin size
        packetWriter          // for sending channelDataMsg
 }
@@ -414,8 +375,8 @@ func (w *chanWriter) Write(data []byte) (n int, err error) {
                n = len(data)
                packet := make([]byte, 0, 9+n)
                packet = append(packet, msgChannelData,
-                       byte(w.id)>>24, byte(w.id)>>16, byte(w.id)>>8, byte(w.id),
-                       byte(n)>>24, byte(n)>>16, byte(n)>>8, byte(n))
+                       byte(w.peersId>>24), byte(w.peersId>>16), byte(w.peersId>>8), byte(w.peersId),
+                       byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
                err = w.writePacket(append(packet, data...))
                w.rwin -= n
                return
@@ -424,7 +385,7 @@ func (w *chanWriter) Write(data []byte) (n int, err error) {
 }
 
 func (w *chanWriter) Close() error {
-       return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.id}))
+       return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.peersId}))
 }
 
 // A chanReader represents stdout or stderr of a remote process.
@@ -433,8 +394,8 @@ type chanReader struct {
        // If writes to this channel block, they will block mainLoop, making
        // it unable to receive new messages from the remote side.
        data         chan []byte // receives data from remote
-       id           uint32
-       packetWriter // for sending windowAdjustMsg
+       peersId      uint32      // the peer's id
+       packetWriter             // for sending windowAdjustMsg
        buf          []byte
 }
 
@@ -446,7 +407,7 @@ func (r *chanReader) Read(data []byte) (int, error) {
                        n := copy(data, r.buf)
                        r.buf = r.buf[n:]
                        msg := windowAdjustMsg{
-                               PeersId:         r.id,
+                               PeersId:         r.peersId,
                                AdditionalBytes: uint32(n),
                        }
                        return n, r.writePacket(marshal(msgChannelWindowAdjust, msg))
@@ -458,7 +419,3 @@ func (r *chanReader) Read(data []byte) (int, error) {
        }
        panic("unreachable")
 }
-
-func (r *chanReader) Close() error {
-       return r.writePacket(marshal(msgChannelEOF, channelEOFMsg{r.id}))
-}
index 6467f578356a21632cd0b1169c050d7c271b59b0..4ef9213a9cd061c72622d6e69d1c725831acac01 100644 (file)
@@ -70,7 +70,7 @@ func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err err
        hashFunc := crypto.SHA1
        h := hashFunc.New()
        h.Write(data)
-       digest := h.Sum()
+       digest := h.Sum(nil)
        return rsa.SignPKCS1v15(rand, k.keys[i], hashFunc, digest)
 }
 
index 01c55219d47578f2133c43663b5052eb762b503c..6844fb89b792c7824c6ecfc677087f3c3abe6345 100644 (file)
@@ -224,3 +224,16 @@ func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
        r = marshalString(r, pubKey)
        return ret
 }
+
+// safeString sanitises s according to RFC 4251, section 9.2. 
+// All control characters except tab, carriage return and newline are
+// replaced by 0x20.
+func safeString(s string) string {
+       out := []byte(s)
+       for i, c := range out {
+               if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 {
+                       out[i] = 0x20
+               }
+       }
+       return string(out)
+}
diff --git a/libgo/go/exp/ssh/common_test.go b/libgo/go/exp/ssh/common_test.go
new file mode 100644 (file)
index 0000000..2f4448a
--- /dev/null
@@ -0,0 +1,26 @@
+// 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 ssh
+
+import (
+       "testing"
+)
+
+var strings = map[string]string{
+       "\x20\x0d\x0a":  "\x20\x0d\x0a",
+       "flibble":       "flibble",
+       "new\x20line":   "new\x20line",
+       "123456\x07789": "123456 789",
+       "\t\t\x10\r\n":  "\t\t \r\n",
+}
+
+func TestSafeString(t *testing.T) {
+       for s, expected := range strings {
+               actual := safeString(s)
+               if expected != actual {
+                       t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual))
+               }
+       }
+}
index 248b2fec4f84c9d06e952c439371b53847db1b15..480f877191a1b705115e4097e7aea8764cea2cef 100644 (file)
@@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess
        session, err := client.NewSession()
 
 Once a Session is created, you can execute a single command on the remote side 
-using the Exec method.
+using the Run method.
 
-       if err := session.Exec("/usr/bin/whoami"); err != nil {
+       if err := session.Run("/usr/bin/whoami"); err != nil {
                panic("Failed to exec: " + err.String())
        }
        reader := bufio.NewReader(session.Stdin)
index 428a747e1e0c89b424d0349d5f6ccde744d2c503..1eee9a4a9776c05ff2605cd25f7c0fb1383b38b0 100644 (file)
@@ -207,11 +207,11 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
        marshalInt(K, kInt)
        h.Write(K)
 
-       H = h.Sum()
+       H = h.Sum(nil)
 
        h.Reset()
        h.Write(H)
-       hh := h.Sum()
+       hh := h.Sum(nil)
 
        var sig []byte
        switch hostKeyAlgo {
@@ -478,7 +478,7 @@ userAuthLoop:
                                        hashFunc := crypto.SHA1
                                        h := hashFunc.New()
                                        h.Write(signedData)
-                                       digest := h.Sum()
+                                       digest := h.Sum(nil)
                                        rsaKey, ok := parseRSA(pubKey)
                                        if !ok {
                                                return ParseError{msgUserAuthRequest}
index 77154f2c3c30f544dbc0ac5764b0d3d26377f8a9..5f98a8d58c683956b701cef5df1c1c9f9ace1fa9 100644 (file)
@@ -8,125 +8,409 @@ package ssh
 // "RFC 4254, section 6".
 
 import (
-       "encoding/binary"
+       "bytes"
        "errors"
+       "fmt"
        "io"
+       "io/ioutil"
+)
+
+type Signal string
+
+// POSIX signals as listed in RFC 4254 Section 6.10.
+const (
+       SIGABRT Signal = "ABRT"
+       SIGALRM Signal = "ALRM"
+       SIGFPE  Signal = "FPE"
+       SIGHUP  Signal = "HUP"
+       SIGILL  Signal = "ILL"
+       SIGINT  Signal = "INT"
+       SIGKILL Signal = "KILL"
+       SIGPIPE Signal = "PIPE"
+       SIGQUIT Signal = "QUIT"
+       SIGSEGV Signal = "SEGV"
+       SIGTERM Signal = "TERM"
+       SIGUSR1 Signal = "USR1"
+       SIGUSR2 Signal = "USR2"
 )
 
 // A Session represents a connection to a remote command or shell.
 type Session struct {
-       // Writes to Stdin are made available to the remote command's standard input.
-       // Closing Stdin causes the command to observe an EOF on its standard input.
-       Stdin io.WriteCloser
-
-       // Reads from Stdout and Stderr consume from the remote command's standard
-       // output and error streams, respectively.
-       // There is a fixed amount of buffering that is shared for the two streams.
-       // Failing to read from either may eventually cause the command to block.
-       // Closing Stdout unblocks such writes and causes them to return errors.
-       Stdout io.ReadCloser
-       Stderr io.Reader
+       // Stdin specifies the remote process's standard input.
+       // If Stdin is nil, the remote process reads from an empty
+       // bytes.Buffer.
+       Stdin io.Reader
+
+       // Stdout and Stderr specify the remote process's standard
+       // output and error.
+       //
+       // If either is nil, Run connects the corresponding file
+       // descriptor to an instance of ioutil.Discard. There is a
+       // fixed amount of buffering that is shared for the two streams.
+       // If either blocks it may eventually cause the remote
+       // command to block.
+       Stdout io.Writer
+       Stderr io.Writer
 
        *clientChan // the channel backing this session
 
-       started bool // started is set to true once a Shell or Exec is invoked.
+       started        bool // true once Start, Run or Shell is invoked.
+       closeAfterWait []io.Closer
+       copyFuncs      []func() error
+       errch          chan error // one send per copyFunc
+}
+
+// RFC 4254 Section 6.4.
+type setenvRequest struct {
+       PeersId   uint32
+       Request   string
+       WantReply bool
+       Name      string
+       Value     string
 }
 
 // Setenv sets an environment variable that will be applied to any
-// command executed by Shell or Exec.
+// command executed by Shell or Run.
 func (s *Session) Setenv(name, value string) error {
-       n, v := []byte(name), []byte(value)
-       nlen, vlen := stringLength(n), stringLength(v)
-       payload := make([]byte, nlen+vlen)
-       marshalString(payload[:nlen], n)
-       marshalString(payload[nlen:], v)
-
-       return s.sendChanReq(channelRequestMsg{
-               PeersId:             s.id,
-               Request:             "env",
-               WantReply:           true,
-               RequestSpecificData: payload,
-       })
+       req := setenvRequest{
+               PeersId:   s.peersId,
+               Request:   "env",
+               WantReply: true,
+               Name:      name,
+               Value:     value,
+       }
+       if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
+               return err
+       }
+       return s.waitForResponse()
 }
 
-// An empty mode list (a string of 1 character, opcode 0), see RFC 4254 Section 8.
-var emptyModeList = []byte{0, 0, 0, 1, 0}
+// An empty mode list, see RFC 4254 Section 8.
+var emptyModelist = "\x00"
+
+// RFC 4254 Section 6.2.
+type ptyRequestMsg struct {
+       PeersId   uint32
+       Request   string
+       WantReply bool
+       Term      string
+       Columns   uint32
+       Rows      uint32
+       Width     uint32
+       Height    uint32
+       Modelist  string
+}
 
 // RequestPty requests the association of a pty with the session on the remote host.
 func (s *Session) RequestPty(term string, h, w int) error {
-       buf := make([]byte, 4+len(term)+16+len(emptyModeList))
-       b := marshalString(buf, []byte(term))
-       binary.BigEndian.PutUint32(b, uint32(h))
-       binary.BigEndian.PutUint32(b[4:], uint32(w))
-       binary.BigEndian.PutUint32(b[8:], uint32(h*8))
-       binary.BigEndian.PutUint32(b[12:], uint32(w*8))
-       copy(b[16:], emptyModeList)
-
-       return s.sendChanReq(channelRequestMsg{
-               PeersId:             s.id,
-               Request:             "pty-req",
-               WantReply:           true,
-               RequestSpecificData: buf,
-       })
+       req := ptyRequestMsg{
+               PeersId:   s.peersId,
+               Request:   "pty-req",
+               WantReply: true,
+               Term:      term,
+               Columns:   uint32(w),
+               Rows:      uint32(h),
+               Width:     uint32(w * 8),
+               Height:    uint32(h * 8),
+               Modelist:  emptyModelist,
+       }
+       if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
+               return err
+       }
+       return s.waitForResponse()
 }
 
-// Exec runs cmd on the remote host. Typically, the remote 
-// server passes cmd to the shell for interpretation. 
-// A Session only accepts one call to Exec or Shell.
-func (s *Session) Exec(cmd string) error {
+// RFC 4254 Section 6.9.
+type signalMsg struct {
+       PeersId   uint32
+       Request   string
+       WantReply bool
+       Signal    string
+}
+
+// Signal sends the given signal to the remote process.
+// sig is one of the SIG* constants.
+func (s *Session) Signal(sig Signal) error {
+       req := signalMsg{
+               PeersId:   s.peersId,
+               Request:   "signal",
+               WantReply: false,
+               Signal:    string(sig),
+       }
+       return s.writePacket(marshal(msgChannelRequest, req))
+}
+
+// RFC 4254 Section 6.5.
+type execMsg struct {
+       PeersId   uint32
+       Request   string
+       WantReply bool
+       Command   string
+}
+
+// Start runs cmd on the remote host. Typically, the remote
+// server passes cmd to the shell for interpretation.
+// A Session only accepts one call to Run, Start or Shell.
+func (s *Session) Start(cmd string) error {
        if s.started {
-               return errors.New("session already started")
+               return errors.New("ssh: session already started")
        }
-       cmdLen := stringLength([]byte(cmd))
-       payload := make([]byte, cmdLen)
-       marshalString(payload, []byte(cmd))
-       s.started = true
+       req := execMsg{
+               PeersId:   s.peersId,
+               Request:   "exec",
+               WantReply: true,
+               Command:   cmd,
+       }
+       if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
+               return err
+       }
+       if err := s.waitForResponse(); err != nil {
+               return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err)
+       }
+       return s.start()
+}
 
-       return s.sendChanReq(channelRequestMsg{
-               PeersId:             s.id,
-               Request:             "exec",
-               WantReply:           true,
-               RequestSpecificData: payload,
-       })
+// Run runs cmd on the remote host and waits for it to terminate.
+// Typically, the remote server passes cmd to the shell for
+// interpretation. A Session only accepts one call to Run,
+// Start or Shell.
+func (s *Session) Run(cmd string) error {
+       err := s.Start(cmd)
+       if err != nil {
+               return err
+       }
+       return s.Wait()
 }
 
-// Shell starts a login shell on the remote host. A Session only 
-// accepts one call to Exec or Shell.
+// Shell starts a login shell on the remote host. A Session only
+// accepts one call to Run, Start or Shell.
 func (s *Session) Shell() error {
        if s.started {
-               return errors.New("session already started")
+               return errors.New("ssh: session already started")
        }
-       s.started = true
-
-       return s.sendChanReq(channelRequestMsg{
-               PeersId:   s.id,
+       req := channelRequestMsg{
+               PeersId:   s.peersId,
                Request:   "shell",
                WantReply: true,
+       }
+       if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
+               return err
+       }
+       if err := s.waitForResponse(); err != nil {
+               return fmt.Errorf("ssh: cound not execute shell: %v", err)
+       }
+       return s.start()
+}
+
+func (s *Session) waitForResponse() error {
+       msg := <-s.msg
+       switch msg.(type) {
+       case *channelRequestSuccessMsg:
+               return nil
+       case *channelRequestFailureMsg:
+               return errors.New("request failed")
+       }
+       return fmt.Errorf("unknown packet %T received: %v", msg, msg)
+}
+
+func (s *Session) start() error {
+       s.started = true
+
+       type F func(*Session) error
+       for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
+               if err := setupFd(s); err != nil {
+                       return err
+               }
+       }
+
+       s.errch = make(chan error, len(s.copyFuncs))
+       for _, fn := range s.copyFuncs {
+               go func(fn func() error) {
+                       s.errch <- fn()
+               }(fn)
+       }
+       return nil
+}
+
+// Wait waits for the remote command to exit.
+func (s *Session) Wait() error {
+       if !s.started {
+               return errors.New("ssh: session not started")
+       }
+       waitErr := s.wait()
+
+       var copyError error
+       for _ = range s.copyFuncs {
+               if err := <-s.errch; err != nil && copyError == nil {
+                       copyError = err
+               }
+       }
+       for _, fd := range s.closeAfterWait {
+               fd.Close()
+       }
+       if waitErr != nil {
+               return waitErr
+       }
+       return copyError
+}
+
+func (s *Session) wait() error {
+       for {
+               switch msg := (<-s.msg).(type) {
+               case *channelRequestMsg:
+                       // TODO(dfc) improve this behavior to match os.Waitmsg
+                       switch msg.Request {
+                       case "exit-status":
+                               d := msg.RequestSpecificData
+                               status := int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
+                               if status > 0 {
+                                       return fmt.Errorf("remote process exited with %d", status)
+                               }
+                               return nil
+                       case "exit-signal":
+                               // TODO(dfc) make a more readable error message
+                               return fmt.Errorf("%v", msg.RequestSpecificData)
+                       default:
+                               return fmt.Errorf("wait: unexpected channel request: %v", msg)
+                       }
+               default:
+                       return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)
+               }
+       }
+       panic("unreachable")
+}
+
+func (s *Session) stdin() error {
+       if s.Stdin == nil {
+               s.Stdin = new(bytes.Buffer)
+       }
+       s.copyFuncs = append(s.copyFuncs, func() error {
+               w := &chanWriter{
+                       packetWriter: s,
+                       peersId:      s.peersId,
+                       win:          s.win,
+               }
+               _, err := io.Copy(w, s.Stdin)
+               if err1 := w.Close(); err == nil {
+                       err = err1
+               }
+               return err
        })
+       return nil
 }
 
+func (s *Session) stdout() error {
+       if s.Stdout == nil {
+               s.Stdout = ioutil.Discard
+       }
+       s.copyFuncs = append(s.copyFuncs, func() error {
+               r := &chanReader{
+                       packetWriter: s,
+                       peersId:      s.peersId,
+                       data:         s.data,
+               }
+               _, err := io.Copy(s.Stdout, r)
+               return err
+       })
+       return nil
+}
+
+func (s *Session) stderr() error {
+       if s.Stderr == nil {
+               s.Stderr = ioutil.Discard
+       }
+       s.copyFuncs = append(s.copyFuncs, func() error {
+               r := &chanReader{
+                       packetWriter: s,
+                       peersId:      s.peersId,
+                       data:         s.dataExt,
+               }
+               _, err := io.Copy(s.Stderr, r)
+               return err
+       })
+       return nil
+}
+
+// StdinPipe returns a pipe that will be connected to the
+// remote command's standard input when the command starts.
+func (s *Session) StdinPipe() (io.WriteCloser, error) {
+       if s.Stdin != nil {
+               return nil, errors.New("ssh: Stdin already set")
+       }
+       if s.started {
+               return nil, errors.New("ssh: StdinPipe after process started")
+       }
+       pr, pw := io.Pipe()
+       s.Stdin = pr
+       s.closeAfterWait = append(s.closeAfterWait, pr)
+       return pw, nil
+}
+
+// StdoutPipe returns a pipe that will be connected to the
+// remote command's standard output when the command starts.
+// There is a fixed amount of buffering that is shared between
+// stdout and stderr streams. If the StdoutPipe reader is
+// not serviced fast enought it may eventually cause the
+// remote command to block.
+func (s *Session) StdoutPipe() (io.ReadCloser, error) {
+       if s.Stdout != nil {
+               return nil, errors.New("ssh: Stdout already set")
+       }
+       if s.started {
+               return nil, errors.New("ssh: StdoutPipe after process started")
+       }
+       pr, pw := io.Pipe()
+       s.Stdout = pw
+       s.closeAfterWait = append(s.closeAfterWait, pw)
+       return pr, nil
+}
+
+// StderrPipe returns a pipe that will be connected to the
+// remote command's standard error when the command starts.
+// There is a fixed amount of buffering that is shared between
+// stdout and stderr streams. If the StderrPipe reader is
+// not serviced fast enought it may eventually cause the
+// remote command to block.
+func (s *Session) StderrPipe() (io.ReadCloser, error) {
+       if s.Stderr != nil {
+               return nil, errors.New("ssh: Stderr already set")
+       }
+       if s.started {
+               return nil, errors.New("ssh: StderrPipe after process started")
+       }
+       pr, pw := io.Pipe()
+       s.Stderr = pw
+       s.closeAfterWait = append(s.closeAfterWait, pw)
+       return pr, nil
+}
+
+// TODO(dfc) add Output and CombinedOutput helpers
+
 // NewSession returns a new interactive session on the remote host.
 func (c *ClientConn) NewSession() (*Session, error) {
-       ch, err := c.openChan("session")
-       if err != nil {
+       ch := c.newChan(c.transport)
+       if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
+               ChanType:      "session",
+               PeersId:       ch.id,
+               PeersWindow:   1 << 14,
+               MaxPacketSize: 1 << 15, // RFC 4253 6.1
+       })); err != nil {
+               c.chanlist.remove(ch.id)
                return nil, err
        }
-       return &Session{
-               Stdin: &chanWriter{
-                       packetWriter: ch,
-                       id:           ch.id,
-                       win:          ch.win,
-               },
-               Stdout: &chanReader{
-                       packetWriter: ch,
-                       id:           ch.id,
-                       data:         ch.data,
-               },
-               Stderr: &chanReader{
-                       packetWriter: ch,
-                       id:           ch.id,
-                       data:         ch.dataExt,
-               },
-               clientChan: ch,
-       }, nil
+       // wait for response
+       msg := <-ch.msg
+       switch msg := msg.(type) {
+       case *channelOpenConfirmMsg:
+               ch.peersId = msg.MyId
+               ch.win <- int(msg.MyWindow)
+               return &Session{
+                       clientChan: ch,
+               }, nil
+       case *channelOpenFailureMsg:
+               c.chanlist.remove(ch.id)
+               return nil, fmt.Errorf("ssh: channel open failed: %s", msg.Message)
+       }
+       c.chanlist.remove(ch.id)
+       return nil, fmt.Errorf("ssh: unexpected message %T: %v", msg, msg)
 }
diff --git a/libgo/go/exp/ssh/session_test.go b/libgo/go/exp/ssh/session_test.go
new file mode 100644 (file)
index 0000000..4be7746
--- /dev/null
@@ -0,0 +1,149 @@
+// 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 ssh
+
+// Session tests.
+
+import (
+       "bytes"
+       "io"
+       "testing"
+)
+
+// dial constructs a new test server and returns a *ClientConn.
+func dial(t *testing.T) *ClientConn {
+       pw := password("tiger")
+       serverConfig.PasswordCallback = func(user, pass string) bool {
+               return user == "testuser" && pass == string(pw)
+       }
+       serverConfig.PubKeyCallback = nil
+
+       l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
+       if err != nil {
+               t.Fatalf("unable to listen: %s", err)
+       }
+       go func() {
+               defer l.Close()
+               conn, err := l.Accept()
+               if err != nil {
+                       t.Errorf("Unable to accept: %v", err)
+                       return
+               }
+               defer conn.Close()
+               if err := conn.Handshake(); err != nil {
+                       t.Errorf("Unable to handshake: %v", err)
+                       return
+               }
+               for {
+                       ch, err := conn.Accept()
+                       if err == io.EOF {
+                               return
+                       }
+                       if err != nil {
+                               t.Errorf("Unable to accept incoming channel request: %v", err)
+                               return
+                       }
+                       if ch.ChannelType() != "session" {
+                               ch.Reject(UnknownChannelType, "unknown channel type")
+                               continue
+                       }
+                       ch.Accept()
+                       go func() {
+                               defer ch.Close()
+                               // this string is returned to stdout
+                               shell := NewServerShell(ch, "golang")
+                               shell.ReadLine()
+                               type exitMsg struct {
+                                       PeersId   uint32
+                                       Request   string
+                                       WantReply bool
+                                       Status    uint32
+                               }
+                               // TODO(dfc) casting to the concrete type should not be
+                               // necessary to send a packet.
+                               msg := exitMsg{
+                                       PeersId:   ch.(*channel).theirId,
+                                       Request:   "exit-status",
+                                       WantReply: false,
+                                       Status:    0,
+                               }
+                               ch.(*channel).serverConn.writePacket(marshal(msgChannelRequest, msg))
+                       }()
+               }
+               t.Log("done")
+       }()
+
+       config := &ClientConfig{
+               User: "testuser",
+               Auth: []ClientAuth{
+                       ClientAuthPassword(pw),
+               },
+       }
+
+       c, err := Dial("tcp", l.Addr().String(), config)
+       if err != nil {
+               t.Fatalf("unable to dial remote side: %s", err)
+       }
+       return c
+}
+
+// Test a simple string is returned to session.Stdout.
+func TestSessionShell(t *testing.T) {
+       conn := dial(t)
+       defer conn.Close()
+       session, err := conn.NewSession()
+       if err != nil {
+               t.Fatalf("Unable to request new session: %s", err)
+       }
+       defer session.Close()
+       stdout := new(bytes.Buffer)
+       session.Stdout = stdout
+       if err := session.Shell(); err != nil {
+               t.Fatalf("Unable to execute command: %s", err)
+       }
+       if err := session.Wait(); err != nil {
+               t.Fatalf("Remote command did not exit cleanly: %s", err)
+       }
+       actual := stdout.String()
+       if actual != "golang" {
+               t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
+       }
+}
+
+// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
+
+// Test a simple string is returned via StdoutPipe.
+func TestSessionStdoutPipe(t *testing.T) {
+       conn := dial(t)
+       defer conn.Close()
+       session, err := conn.NewSession()
+       if err != nil {
+               t.Fatalf("Unable to request new session: %s", err)
+       }
+       defer session.Close()
+       stdout, err := session.StdoutPipe()
+       if err != nil {
+               t.Fatalf("Unable to request StdoutPipe(): %v", err)
+       }
+       var buf bytes.Buffer
+       if err := session.Shell(); err != nil {
+               t.Fatalf("Unable to execute command: %s", err)
+       }
+       done := make(chan bool, 1)
+       go func() {
+               if _, err := io.Copy(&buf, stdout); err != nil {
+                       t.Errorf("Copy of stdout failed: %v", err)
+               }
+               done <- true
+       }()
+       if err := session.Wait(); err != nil {
+               t.Fatalf("Remote command did not exit cleanly: %s", err)
+       }
+       <-done
+       actual := buf.String()
+       if actual != "golang" {
+               t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
+       }
+}
index 859dedc93b3e2d166ca47fff4a80702d490de4cf..f3bbac5d19e1071a07b8e86029692b96b0bfb13b 100644 (file)
@@ -86,12 +86,12 @@ func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tc
                clientChan: ch,
                Reader: &chanReader{
                        packetWriter: ch,
-                       id:           ch.id,
+                       peersId:      ch.peersId,
                        data:         ch.data,
                },
                Writer: &chanWriter{
                        packetWriter: ch,
-                       id:           ch.id,
+                       peersId:      ch.peersId,
                        win:          ch.win,
                },
        }, nil
diff --git a/libgo/go/exp/ssh/tcpip_func_test.go b/libgo/go/exp/ssh/tcpip_func_test.go
new file mode 100644 (file)
index 0000000..2612972
--- /dev/null
@@ -0,0 +1,59 @@
+// 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 ssh
+
+// direct-tcpip functional tests
+
+import (
+       "net"
+       "net/http"
+       "testing"
+)
+
+func TestTCPIPHTTP(t *testing.T) {
+       if *sshuser == "" {
+               t.Log("ssh.user not defined, skipping test")
+               return
+       }
+       // google.com will generate at least one redirect, possibly three
+       // depending on your location.
+       doTest(t, "http://google.com")
+}
+
+func TestTCPIPHTTPS(t *testing.T) {
+       if *sshuser == "" {
+               t.Log("ssh.user not defined, skipping test")
+               return
+       }
+       doTest(t, "https://encrypted.google.com/")
+}
+
+func doTest(t *testing.T, url string) {
+       config := &ClientConfig{
+               User: *sshuser,
+               Auth: []ClientAuth{
+                       ClientAuthPassword(password(*sshpass)),
+               },
+       }
+       conn, err := Dial("tcp", "localhost:22", config)
+       if err != nil {
+               t.Fatalf("Unable to connect: %s", err)
+       }
+       defer conn.Close()
+       tr := &http.Transport{
+               Dial: func(n, addr string) (net.Conn, error) {
+                       return conn.Dial(n, addr)
+               },
+       }
+       client := &http.Client{
+               Transport: tr,
+       }
+       resp, err := client.Get(url)
+       if err != nil {
+               t.Fatalf("unable to proxy: %s", err)
+       }
+       // got a body without error
+       t.Log(resp)
+}
index b8cb2c319d85e119495cee900a81993a4a5ffc73..bcd073e7ce6ec11451df55aa479fa316d0e01ee1 100644 (file)
@@ -123,7 +123,7 @@ func (r *reader) readOnePacket() ([]byte, error) {
 
        if r.mac != nil {
                r.mac.Write(packet[:length-1])
-               if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 {
+               if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 {
                        return nil, errors.New("ssh: MAC failure")
                }
        }
@@ -201,7 +201,7 @@ func (w *writer) writePacket(packet []byte) error {
        }
 
        if w.mac != nil {
-               if _, err := w.Write(w.mac.Sum()); err != nil {
+               if _, err := w.Write(w.mac.Sum(nil)); err != nil {
                        return err
                }
        }
@@ -297,7 +297,7 @@ func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) {
                        h.Write(digestsSoFar)
                }
 
-               digest := h.Sum()
+               digest := h.Sum(nil)
                n := copy(out, digest)
                out = out[n:]
                if len(out) > 0 {
@@ -317,9 +317,9 @@ func (t truncatingMAC) Write(data []byte) (int, error) {
        return t.hmac.Write(data)
 }
 
-func (t truncatingMAC) Sum() []byte {
-       digest := t.hmac.Sum()
-       return digest[:t.length]
+func (t truncatingMAC) Sum(in []byte) []byte {
+       out := t.hmac.Sum(in)
+       return out[:len(in)+t.length]
 }
 
 func (t truncatingMAC) Reset() {
index 4a30acf23154daea9c5573be007d468a9e99f4ff..35535ea406f0c40c566bbd04c63ccfdb6fd6f43d 100644 (file)
@@ -202,7 +202,7 @@ func TestCheck(t *testing.T) {
        // For easy debugging w/o changing the testing code,
        // if there is a local test file, only test that file.
        const testfile = "test.go"
-       if fi, err := os.Stat(testfile); err == nil && fi.IsRegular() {
+       if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() {
                fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile)
                check(t, testfile, []string{testfile})
                return
index 4167caf3f0e7875f283e5068283d205d44481c0b..16a8667ff669bcc1a9c69f120a3f15aa81212730 100644 (file)
@@ -59,7 +59,7 @@ func findPkg(path string) (filename, id string) {
        // try extensions
        for _, ext := range pkgExts {
                filename = noext + ext
-               if f, err := os.Stat(filename); err == nil && f.IsRegular() {
+               if f, err := os.Stat(filename); err == nil && !f.IsDir() {
                        return
                }
        }
index 3f66d226153aa69bbe0c2616c8afb82f6d4c9b40..7475d352209fc7c308d34c28ba831b70892b63d0 100644 (file)
@@ -58,32 +58,32 @@ func testPath(t *testing.T, path string) bool {
        return true
 }
 
-const maxTime = 3e9 // maximum allotted testing time in ns
+const maxTime = 3 * time.Second
 
-func testDir(t *testing.T, dir string, endTime int64) (nimports int) {
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
        dirname := filepath.Join(pkgRoot, dir)
        list, err := ioutil.ReadDir(dirname)
        if err != nil {
                t.Errorf("testDir(%s): %s", dirname, err)
        }
        for _, f := range list {
-               if time.Nanoseconds() >= endTime {
+               if time.Now().After(endTime) {
                        t.Log("testing time used up")
                        return
                }
                switch {
-               case f.IsRegular():
+               case !f.IsDir():
                        // try extensions
                        for _, ext := range pkgExts {
-                               if strings.HasSuffix(f.Name, ext) {
-                                       name := f.Name[0 : len(f.Name)-len(ext)] // remove extension
+                               if strings.HasSuffix(f.Name(), ext) {
+                                       name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
                                        if testPath(t, filepath.Join(dir, name)) {
                                                nimports++
                                        }
                                }
                        }
-               case f.IsDirectory():
-                       nimports += testDir(t, filepath.Join(dir, f.Name), endTime)
+               case f.IsDir():
+                       nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
                }
        }
        return
@@ -96,6 +96,6 @@ func TestGcImport(t *testing.T) {
        if testPath(t, "./testdata/exports") {
                nimports++
        }
-       nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages
+       nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
        t.Logf("tested %d imports", nimports)
 }
index 6370560d0bfa65acfa1b71bc21febb7fecc1494e..00aac798cb0fbc845b90daf7eb75525daaa6499e 100644 (file)
@@ -47,8 +47,10 @@ func TestFmtInterface(t *testing.T) {
 const b32 uint32 = 1<<32 - 1
 const b64 uint64 = 1<<64 - 1
 
-var array = []int{1, 2, 3, 4, 5}
-var iarray = []interface{}{1, "hello", 2.5, nil}
+var array = [5]int{1, 2, 3, 4, 5}
+var iarray = [4]interface{}{1, "hello", 2.5, nil}
+var slice = array[:]
+var islice = iarray[:]
 
 type A struct {
        i int
@@ -327,6 +329,12 @@ var fmttests = []struct {
        {"%v", &array, "&[1 2 3 4 5]"},
        {"%v", &iarray, "&[1 hello 2.5 <nil>]"},
 
+       // slices
+       {"%v", slice, "[1 2 3 4 5]"},
+       {"%v", islice, "[1 hello 2.5 <nil>]"},
+       {"%v", &slice, "&[1 2 3 4 5]"},
+       {"%v", &islice, "&[1 hello 2.5 <nil>]"},
+
        // complexes with %v
        {"%v", 1 + 2i, "(1+2i)"},
        {"%v", complex64(1 + 2i), "(1+2i)"},
@@ -359,6 +367,10 @@ var fmttests = []struct {
        {"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`},
        {"%#v", []int(nil), `[]int(nil)`},
        {"%#v", []int{}, `[]int{}`},
+       {"%#v", array, `[5]int{1, 2, 3, 4, 5}`},
+       {"%#v", &array, `&[5]int{1, 2, 3, 4, 5}`},
+       {"%#v", iarray, `[4]interface {}{1, "hello", 2.5, interface {}(nil)}`},
+       {"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`},
        {"%#v", map[int]byte(nil), `map[int] uint8(nil)`},
        {"%#v", map[int]byte{}, `map[int] uint8{}`},
 
index 7143e07a36e96f6f21b6a041a377535f5c62f46c..e5ca1172405c1799efc7474144dc7f559e45477e 100644 (file)
@@ -877,7 +877,7 @@ BigSwitch:
                }
                if goSyntax {
                        p.buf.WriteString(value.Type().String())
-                       if f.IsNil() {
+                       if f.Kind() == reflect.Slice && f.IsNil() {
                                p.buf.WriteString("(nil)")
                                break
                        }
index b24688d2ea3960d390f951f8ade2d4d1aaab2937..c7c8e7c101e4faae0bd96c258afe4bf1f741c6b2 100644 (file)
@@ -113,7 +113,7 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer,
                                importErrors = true
                                continue
                        }
-                       path, _ := strconv.Unquote(string(spec.Path.Value))
+                       path, _ := strconv.Unquote(spec.Path.Value)
                        pkg, err := importer(imports, path)
                        if err != nil {
                                p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
index e3de8d0fa7f7a80e5de8cccc03c66dcd2e6efdda..5301ab53e519d43b0bcce89ba652db9f899b6ceb 100644 (file)
@@ -15,6 +15,7 @@ import (
        "regexp"
        "runtime"
        "strings"
+       "time"
 )
 
 // Build produces a build Script for the given package.
@@ -150,7 +151,7 @@ func (s *Script) Run() error {
 
 // Stale returns true if the build's inputs are newer than its outputs.
 func (s *Script) Stale() bool {
-       var latest int64
+       var latest time.Time
        // get latest mtime of outputs
        for _, file := range s.Output {
                fi, err := os.Stat(file)
@@ -158,13 +159,13 @@ func (s *Script) Stale() bool {
                        // any error reading output files means stale
                        return true
                }
-               if m := fi.Mtime_ns; m > latest {
-                       latest = m
+               if mtime := fi.ModTime(); mtime.After(latest) {
+                       latest = mtime
                }
        }
        for _, file := range s.Input {
                fi, err := os.Stat(file)
-               if err != nil || fi.Mtime_ns > latest {
+               if err != nil || fi.ModTime().After(latest) {
                        // any error reading input files means stale
                        // (attempt to rebuild to figure out why)
                        return true
index 0d175c75deb456b089755f18bd9a35a7bad9ae9a..12dc99942a742de3db84817b5c2d0332fe9ef598 100644 (file)
@@ -38,16 +38,16 @@ type Context struct {
        // format of the strings dir and file: they can be
        // slash-separated, backslash-separated, even URLs.
 
-       // ReadDir returns a slice of *os.FileInfo, sorted by Name,
+       // ReadDir returns a slice of os.FileInfo, sorted by Name,
        // describing the content of the named directory.
        // The dir argument is the argument to ScanDir.
        // If ReadDir is nil, ScanDir uses io.ReadDir.
-       ReadDir func(dir string) (fi []*os.FileInfo, err error)
+       ReadDir func(dir string) (fi []os.FileInfo, err error)
 
        // ReadFile returns the content of the file named file
        // in the directory named dir.  The dir argument is the
        // argument to ScanDir, and the file argument is the
-       // Name field from an *os.FileInfo returned by ReadDir.
+       // Name field from an os.FileInfo returned by ReadDir.
        // The returned path is the full name of the file, to be
        // used in error messages.
        //
@@ -56,7 +56,7 @@ type Context struct {
        ReadFile func(dir, file string) (path string, content []byte, err error)
 }
 
-func (ctxt *Context) readDir(dir string) ([]*os.FileInfo, error) {
+func (ctxt *Context) readDir(dir string) ([]os.FileInfo, error) {
        if f := ctxt.ReadDir; f != nil {
                return f(dir)
        }
@@ -140,18 +140,19 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
        testImported := make(map[string]bool)
        fset := token.NewFileSet()
        for _, d := range dirs {
-               if !d.IsRegular() {
+               if d.IsDir() {
                        continue
                }
-               if strings.HasPrefix(d.Name, "_") ||
-                       strings.HasPrefix(d.Name, ".") {
+               name := d.Name()
+               if strings.HasPrefix(name, "_") ||
+                       strings.HasPrefix(name, ".") {
                        continue
                }
-               if !ctxt.goodOSArchFile(d.Name) {
+               if !ctxt.goodOSArchFile(name) {
                        continue
                }
 
-               ext := path.Ext(d.Name)
+               ext := path.Ext(name)
                switch ext {
                case ".go", ".c", ".s":
                        // tentatively okay
@@ -161,7 +162,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                }
 
                // Look for +build comments to accept or reject the file.
-               filename, data, err := ctxt.readFile(dir, d.Name)
+               filename, data, err := ctxt.readFile(dir, name)
                if err != nil {
                        return nil, err
                }
@@ -172,10 +173,10 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                // Going to save the file.  For non-Go files, can stop here.
                switch ext {
                case ".c":
-                       di.CFiles = append(di.CFiles, d.Name)
+                       di.CFiles = append(di.CFiles, name)
                        continue
                case ".s":
-                       di.SFiles = append(di.SFiles, d.Name)
+                       di.SFiles = append(di.SFiles, name)
                        continue
                }
 
@@ -192,7 +193,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                        continue
                }
 
-               isTest := strings.HasSuffix(d.Name, "_test.go")
+               isTest := strings.HasSuffix(name, "_test.go")
                if isTest && strings.HasSuffix(pkg, "_test") {
                        pkg = pkg[:len(pkg)-len("_test")]
                }
@@ -255,15 +256,15 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
                        }
                }
                if isCgo {
-                       di.CgoFiles = append(di.CgoFiles, d.Name)
+                       di.CgoFiles = append(di.CgoFiles, name)
                } else if isTest {
                        if pkg == string(pf.Name.Name) {
-                               di.TestGoFiles = append(di.TestGoFiles, d.Name)
+                               di.TestGoFiles = append(di.TestGoFiles, name)
                        } else {
-                               di.XTestGoFiles = append(di.XTestGoFiles, d.Name)
+                               di.XTestGoFiles = append(di.XTestGoFiles, name)
                        }
                } else {
-                       di.GoFiles = append(di.GoFiles, d.Name)
+                       di.GoFiles = append(di.GoFiles, name)
                }
        }
        if di.Package == "" {
index 7ccb12993b306d1810d0807ec23d3fd5a1bb6389..91d6c430a9d6e398900314fdd7de6b4c4ee5c3a8 100644 (file)
@@ -70,7 +70,7 @@ func (t *Tree) HasSrc(pkg string) bool {
        if err != nil {
                return false
        }
-       return fi.IsDirectory()
+       return fi.IsDir()
 }
 
 // HasPkg returns whether the given package's
@@ -80,7 +80,7 @@ func (t *Tree) HasPkg(pkg string) bool {
        if err != nil {
                return false
        }
-       return fi.IsRegular()
+       return !fi.IsDir()
        // TODO(adg): check object version is consistent
 }
 
index 19216f85b96838dfb3e6117140f0db49b6fec61f..d7bb384ed03c5f5f7774989dac0665b34b782e1e 100644 (file)
@@ -7,11 +7,14 @@
 package doc
 
 import (
+       "bytes"
        "go/ast"
        "io"
        "regexp"
        "strings"
        "text/template" // for HTMLEscape
+       "unicode"
+       "unicode/utf8"
 )
 
 func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' }
@@ -168,6 +171,8 @@ var (
        html_endp   = []byte("</p>\n")
        html_pre    = []byte("<pre>")
        html_endpre = []byte("</pre>\n")
+       html_h      = []byte("<h3>")
+       html_endh   = []byte("</h3>\n")
 )
 
 // Emphasize and escape a line of text for HTML. URLs are converted into links;
@@ -268,6 +273,51 @@ func unindent(block [][]byte) {
        }
 }
 
+// heading returns the (possibly trimmed) line if it passes as a valid section
+// heading; otherwise it returns nil. 
+func heading(line []byte) []byte {
+       line = bytes.TrimSpace(line)
+       if len(line) == 0 {
+               return nil
+       }
+
+       // a heading must start with an uppercase letter
+       r, _ := utf8.DecodeRune(line)
+       if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
+               return nil
+       }
+
+       // it must end in a letter, digit or ':'
+       r, _ = utf8.DecodeLastRune(line)
+       if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != ':' {
+               return nil
+       }
+
+       // strip trailing ':', if any
+       if r == ':' {
+               line = line[0 : len(line)-1]
+       }
+
+       // exclude lines with illegal characters
+       if bytes.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 {
+               return nil
+       }
+
+       // allow "'" for possessive "'s" only
+       for b := line; ; {
+               i := bytes.IndexRune(b, '\'')
+               if i < 0 {
+                       break
+               }
+               if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') {
+                       return nil // not followed by "s "
+               }
+               b = b[i+2:]
+       }
+
+       return line
+}
+
 // Convert comment text to formatted HTML.
 // The comment was prepared by DocReader,
 // so it is known not to have leading, trailing blank lines
@@ -276,6 +326,7 @@ func unindent(block [][]byte) {
 //
 // Turn each run of multiple \n into </p><p>.
 // Turn each run of indented lines into a <pre> block without indent.
+// Enclose headings with header tags.
 //
 // URLs in the comment text are converted into links; if the URL also appears
 // in the words map, the link is taken from the map (if the corresponding map
@@ -286,6 +337,8 @@ func unindent(block [][]byte) {
 // into a link.
 func ToHTML(w io.Writer, s []byte, words map[string]string) {
        inpara := false
+       lastWasBlank := false
+       lastWasHeading := false
 
        close := func() {
                if inpara {
@@ -308,6 +361,7 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) {
                        // close paragraph
                        close()
                        i++
+                       lastWasBlank = true
                        continue
                }
                if indentLen(line) > 0 {
@@ -334,10 +388,30 @@ func ToHTML(w io.Writer, s []byte, words map[string]string) {
                                emphasize(w, line, nil, false) // no nice text formatting
                        }
                        w.Write(html_endpre)
+                       lastWasHeading = false
                        continue
                }
+
+               if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
+                       isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
+                       // current line is non-blank, sourounded by blank lines
+                       // and the next non-blank line is not indented: this
+                       // might be a heading.
+                       if head := heading(line); head != nil {
+                               close()
+                               w.Write(html_h)
+                               template.HTMLEscape(w, head)
+                               w.Write(html_endh)
+                               i += 2
+                               lastWasHeading = true
+                               continue
+                       }
+               }
+
                // open paragraph
                open()
+               lastWasBlank = false
+               lastWasHeading = false
                emphasize(w, lines[i], words, true) // nice text formatting
                i++
        }
diff --git a/libgo/go/go/doc/comment_test.go b/libgo/go/go/doc/comment_test.go
new file mode 100644 (file)
index 0000000..870660a
--- /dev/null
@@ -0,0 +1,39 @@
+// 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 doc
+
+import (
+       "testing"
+)
+
+var headingTests = []struct {
+       line string
+       ok   bool
+}{
+       {"Section", true},
+       {"A typical usage", true},
+       {"ΔΛΞ is Greek", true},
+       {"Foo 42", true},
+       {"", false},
+       {"section", false},
+       {"A typical usage:", true},
+       {"δ is Greek", false},
+       {"Foo §", false},
+       {"Fermat's Last Sentence", true},
+       {"Fermat's", true},
+       {"'sX", false},
+       {"Ted 'Too' Bar", false},
+       {"Use n+m", false},
+       {"Scanning:", true},
+       {"N:M", false},
+}
+
+func TestIsHeading(t *testing.T) {
+       for _, tt := range headingTests {
+               if h := heading([]byte(tt.line)); (h != nil) != tt.ok {
+                       t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
+               }
+       }
+}
diff --git a/libgo/go/go/doc/headscan.go b/libgo/go/go/doc/headscan.go
new file mode 100644 (file)
index 0000000..83f2462
--- /dev/null
@@ -0,0 +1,111 @@
+// 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.
+
+/*
+       The headscan command extracts comment headings from package files;
+       it is used to detect false positives which may require an adjustment
+       to the comment formatting heuristics in comment.go.
+
+       Usage: headscan [-root root_directory]
+
+       By default, the $GOROOT/src directory is scanned.
+*/
+package main
+
+import (
+       "bytes"
+       "flag"
+       "fmt"
+       "go/doc"
+       "go/parser"
+       "go/token"
+       "os"
+       "path/filepath"
+       "runtime"
+       "strings"
+)
+
+var (
+       root    = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
+       verbose = flag.Bool("v", false, "verbose mode")
+)
+
+const (
+       html_h    = "<h3>"
+       html_endh = "</h3>\n"
+)
+
+func isGoFile(fi os.FileInfo) bool {
+       return strings.HasSuffix(fi.Name(), ".go") &&
+               !strings.HasSuffix(fi.Name(), "_test.go")
+}
+
+func appendHeadings(list []string, comment string) []string {
+       var buf bytes.Buffer
+       doc.ToHTML(&buf, []byte(comment), nil)
+       for s := buf.String(); ; {
+               i := strings.Index(s, html_h)
+               if i < 0 {
+                       break
+               }
+               i += len(html_h)
+               j := strings.Index(s, html_endh)
+               if j < 0 {
+                       list = append(list, s[i:]) // incorrect HTML
+                       break
+               }
+               list = append(list, s[i:j])
+               s = s[j+len(html_endh):]
+       }
+       return list
+}
+
+func main() {
+       flag.Parse()
+       fset := token.NewFileSet()
+       nheadings := 0
+       err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error {
+               if !fi.IsDir() {
+                       return nil
+               }
+               pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
+               if err != nil {
+                       if *verbose {
+                               fmt.Fprintln(os.Stderr, err)
+                       }
+                       return nil
+               }
+               for _, pkg := range pkgs {
+                       d := doc.NewPackageDoc(pkg, path)
+                       list := appendHeadings(nil, d.Doc)
+                       for _, d := range d.Consts {
+                               list = appendHeadings(list, d.Doc)
+                       }
+                       for _, d := range d.Types {
+                               list = appendHeadings(list, d.Doc)
+                       }
+                       for _, d := range d.Vars {
+                               list = appendHeadings(list, d.Doc)
+                       }
+                       for _, d := range d.Funcs {
+                               list = appendHeadings(list, d.Doc)
+                       }
+                       if len(list) > 0 {
+                               // directories may contain multiple packages;
+                               // print path and package name
+                               fmt.Printf("%s (package %s)\n", path, pkg.Name)
+                               for _, h := range list {
+                                       fmt.Printf("\t%s\n", h)
+                               }
+                               nheadings += len(list)
+                       }
+               }
+               return nil
+       })
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       fmt.Println(nheadings, "headings found")
+}
index d3bab31c5a3aac16a4e0e62bf2409a942ca01e21..be11f461c3b33b7351811c60335aad41c7761030 100644 (file)
@@ -188,7 +188,7 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
 // returned. If a parse error occurred, a non-nil but incomplete map and the
 // error are returned.
 //
-func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) {
+func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) {
        fd, err := os.Open(path)
        if err != nil {
                return nil, err
@@ -202,10 +202,9 @@ func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool,
 
        filenames := make([]string, len(list))
        n := 0
-       for i := 0; i < len(list); i++ {
-               d := &list[i]
+       for _, d := range list {
                if filter == nil || filter(d) {
-                       filenames[n] = filepath.Join(path, d.Name)
+                       filenames[n] = filepath.Join(path, d.Name())
                        n++
                }
        }
index dee90fbcf4c4a37adfa99e15ed170357413ce8d7..f602db8896db5e02e6fe962a98d0f3a656f21fd4 100644 (file)
@@ -113,7 +113,7 @@ func nameFilter(filename string) bool {
        return true
 }
 
-func dirFilter(f *os.FileInfo) bool { return nameFilter(f.Name) }
+func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
 
 func TestParse4(t *testing.T) {
        path := "."
index 248e43d4e72993e93c4f1db9ac5321d395d242ce..53f36092fdafd6c3cd5715014b839931d0c141ce 100644 (file)
@@ -1377,7 +1377,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
        // in RawFormat
        cfg := Config{Mode: RawFormat}
        var buf bytes.Buffer
-       if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
+       if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
                return
        }
        if buf.Len() <= maxSize {
index 84fb2808ebaab734a9a803e418052095f3cf803c..dbd942292b537c83ccd3a7b7b9f6384fda5c1f2c 100644 (file)
@@ -20,7 +20,7 @@ import (
 var testfile *ast.File
 
 func testprint(out io.Writer, file *ast.File) {
-       if _, err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil {
+       if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil {
                log.Fatalf("print error: %s", err)
        }
 }
index 6104c326c665ea7c0cb12419b3521e4c44c5e40f..f8c22f1419d0bd1f8ea8e6eddc331e70198a826e 100644 (file)
@@ -19,9 +19,9 @@ import (
 )
 
 const debug = false // enable for debugging
+const infinity = 1 << 30
 
-
-type whiteSpace int
+type whiteSpace byte
 
 const (
        ignore   = whiteSpace(0)
@@ -33,18 +33,6 @@ const (
        unindent = whiteSpace('<')
 )
 
-var (
-       esc       = []byte{tabwriter.Escape}
-       htab      = []byte{'\t'}
-       htabs     = []byte("\t\t\t\t\t\t\t\t")
-       newlines  = []byte("\n\n\n\n\n\n\n\n") // more than the max determined by nlines
-       formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined by nlines
-)
-
-// Special positions
-var noPos token.Position // use noPos when a position is needed but not known
-var infinity = 1 << 30
-
 // Use ignoreMultiLine if the multiLine information is not important.
 var ignoreMultiLine = new(bool)
 
@@ -52,31 +40,20 @@ var ignoreMultiLine = new(bool)
 type pmode int
 
 const (
-       inLiteral pmode = 1 << iota
-       noExtraLinebreak
+       noExtraLinebreak pmode = 1 << iota
 )
 
-// local error wrapper so we can distinguish errors we want to return
-// as errors from genuine panics (which we don't want to return as errors)
-type osError struct {
-       err error
-}
-
 type printer struct {
        // Configuration (does not change after initialization)
-       output io.Writer
        Config
        fset *token.FileSet
 
        // Current state
-       written int         // number of bytes written
-       indent  int         // current indentation
-       mode    pmode       // current printer mode
-       lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
-
-       // Reused buffers
-       wsbuf  []whiteSpace // delayed white space
-       litbuf bytes.Buffer // for creation of escaped literals and comments
+       output  bytes.Buffer // raw printer result
+       indent  int          // current indentation
+       mode    pmode        // current printer mode
+       lastTok token.Token  // the last token printed (token.ILLEGAL if it's whitespace)
+       wsbuf   []whiteSpace // delayed white space
 
        // The (possibly estimated) position in the generated output;
        // in AST space (i.e., pos is set whenever a token position is
@@ -97,8 +74,7 @@ type printer struct {
        nodeSizes map[ast.Node]int
 }
 
-func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
-       p.output = output
+func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
        p.Config = *cfg
        p.fset = fset
        p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
@@ -113,19 +89,6 @@ func (p *printer) internalError(msg ...interface{}) {
        }
 }
 
-// escape escapes string s by bracketing it with tabwriter.Escape.
-// Escaped strings pass through tabwriter unchanged. (Note that
-// valid Go programs cannot contain tabwriter.Escape bytes since
-// they do not appear in legal UTF-8 sequences).
-//
-func (p *printer) escape(s string) string {
-       p.litbuf.Reset()
-       p.litbuf.WriteByte(tabwriter.Escape)
-       p.litbuf.WriteString(s)
-       p.litbuf.WriteByte(tabwriter.Escape)
-       return p.litbuf.String()
-}
-
 // nlines returns the adjusted number of linebreaks given the desired number
 // of breaks n such that min <= result <= max.
 //
@@ -140,82 +103,79 @@ func (p *printer) nlines(n, min int) int {
        return n
 }
 
-// write0 writes raw (uninterpreted) data to p.output and handles errors.
-// write0 does not indent after newlines, and does not HTML-escape or update p.pos.
-//
-func (p *printer) write0(data []byte) {
-       if len(data) > 0 {
-               n, err := p.output.Write(data)
-               p.written += n
-               if err != nil {
-                       panic(osError{err})
-               }
+// writeByte writes a single byte to p.output and updates p.pos.
+func (p *printer) writeByte(ch byte) {
+       p.output.WriteByte(ch)
+       p.pos.Offset++
+       p.pos.Column++
+
+       if ch == '\n' || ch == '\f' {
+               // write indentation
+               // use "hard" htabs - indentation columns
+               // must not be discarded by the tabwriter
+               const htabs = "\t\t\t\t\t\t\t\t"
+               j := p.indent
+               for j > len(htabs) {
+                       p.output.WriteString(htabs)
+                       j -= len(htabs)
+               }
+               p.output.WriteString(htabs[0:j])
+
+               // update p.pos
+               p.pos.Line++
+               p.pos.Offset += p.indent
+               p.pos.Column = 1 + p.indent
        }
 }
 
-// write interprets data and writes it to p.output. It inserts indentation
-// after a line break unless in a tabwriter escape sequence.
-// It updates p.pos as a side-effect.
+// writeNewlines writes up to n newlines to p.output and updates p.pos.
+// The actual number of newlines written is limited by nlines.
+// nl must be one of '\n' or '\f'.
 //
-func (p *printer) write(data []byte) {
-       i0 := 0
-       for i, b := range data {
-               switch b {
-               case '\n', '\f':
-                       // write segment ending in b
-                       p.write0(data[i0 : i+1])
-
-                       // update p.pos
-                       p.pos.Offset += i + 1 - i0
-                       p.pos.Line++
-                       p.pos.Column = 1
-
-                       if p.mode&inLiteral == 0 {
-                               // write indentation
-                               // use "hard" htabs - indentation columns
-                               // must not be discarded by the tabwriter
-                               j := p.indent
-                               for ; j > len(htabs); j -= len(htabs) {
-                                       p.write0(htabs)
-                               }
-                               p.write0(htabs[0:j])
-
-                               // update p.pos
-                               p.pos.Offset += p.indent
-                               p.pos.Column += p.indent
-                       }
-
-                       // next segment start
-                       i0 = i + 1
-
-               case tabwriter.Escape:
-                       p.mode ^= inLiteral
+func (p *printer) writeNewlines(n int, nl byte) {
+       for n = p.nlines(n, 0); n > 0; n-- {
+               p.writeByte(nl)
+       }
+}
 
-                       // ignore escape chars introduced by printer - they are
-                       // invisible and must not affect p.pos (was issue #1089)
-                       p.pos.Offset--
-                       p.pos.Column--
-               }
+// writeString writes the string s to p.output and updates p.pos.
+// If isLit is set, s is escaped w/ tabwriter.Escape characters
+// to protect s from being interpreted by the tabwriter.
+//
+// Note: writeString is only used to write Go tokens, literals, and
+// comments, all of which must be written literally. Thus, it is correct
+// to always set isLit = true. However, setting it explicitly only when
+// needed (i.e., when we don't know that s contains no tabs or line breaks)
+// avoids processing extra escape characters and reduces run time of the
+// printer benchmark by up to 10%.
+//
+func (p *printer) writeString(s string, isLit bool) {
+       if isLit {
+               // Protect s such that is passes through the tabwriter
+               // unchanged. Note that valid Go programs cannot contain
+               // tabwriter.Escape bytes since they do not appear in legal
+               // UTF-8 sequences.
+               p.output.WriteByte(tabwriter.Escape)
        }
 
-       // write remaining segment
-       p.write0(data[i0:])
+       p.output.WriteString(s)
 
        // update p.pos
-       d := len(data) - i0
-       p.pos.Offset += d
-       p.pos.Column += d
-}
-
-func (p *printer) writeNewlines(n int, useFF bool) {
-       if n > 0 {
-               n = p.nlines(n, 0)
-               if useFF {
-                       p.write(formfeeds[0:n])
-               } else {
-                       p.write(newlines[0:n])
+       nlines := 0
+       column := p.pos.Column + len(s)
+       for i := 0; i < len(s); i++ {
+               if s[i] == '\n' {
+                       nlines++
+                       column = len(s) - i
                }
        }
+       p.pos.Offset += len(s)
+       p.pos.Line += nlines
+       p.pos.Column = column
+
+       if isLit {
+               p.output.WriteByte(tabwriter.Escape)
+       }
 }
 
 // writeItem writes data at position pos. data is the text corresponding to
@@ -224,7 +184,7 @@ func (p *printer) writeNewlines(n int, useFF bool) {
 // source text. writeItem updates p.last to the position immediately following
 // the data.
 //
-func (p *printer) writeItem(pos token.Position, data string) {
+func (p *printer) writeItem(pos token.Position, data string, isLit bool) {
        if pos.IsValid() {
                // continue with previous position if we don't have a valid pos
                if p.last.IsValid() && p.last.Filename != pos.Filename {
@@ -240,9 +200,9 @@ func (p *printer) writeItem(pos token.Position, data string) {
        if debug {
                // do not update p.pos - use write0
                _, filename := filepath.Split(pos.Filename)
-               p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
+               fmt.Fprintf(&p.output, "[%s:%d:%d]", filename, pos.Line, pos.Column)
        }
-       p.write([]byte(data))
+       p.writeString(data, isLit)
        p.last = p.pos
 }
 
@@ -257,14 +217,14 @@ const linePrefix = "//line "
 // next item is a keyword.
 //
 func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) {
-       if p.written == 0 {
+       if p.output.Len() == 0 {
                // the comment is the first item to be printed - don't write any whitespace
                return
        }
 
        if pos.IsValid() && pos.Filename != p.last.Filename {
                // comment in a different file - separate with newlines (writeNewlines will limit the number)
-               p.writeNewlines(10, true)
+               p.writeNewlines(10, '\f')
                return
        }
 
@@ -297,14 +257,14 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
                }
                // make sure there is at least one separator
                if !hasSep {
+                       sep := byte('\t')
                        if pos.Line == next.Line {
                                // next item is on the same line as the comment
                                // (which must be a /*-style comment): separate
                                // with a blank instead of a tab
-                               p.write([]byte{' '})
-                       } else {
-                               p.write(htab)
+                               sep = ' '
                        }
+                       p.writeByte(sep)
                }
 
        } else {
@@ -357,30 +317,31 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
                if n <= 0 && prev != nil && prev.Text[1] == '/' {
                        n = 1
                }
-               p.writeNewlines(n, true)
+               if n > 0 {
+                       p.writeNewlines(n, '\f')
+               }
                p.indent = indent
        }
 }
 
-// TODO(gri): It should be possible to convert the code below from using
-//            []byte to string and in the process eliminate some conversions.
-
 // Split comment text into lines
-func split(text []byte) [][]byte {
+// (using strings.Split(text, "\n") is significantly slower for
+// this specific purpose, as measured with: gotest -bench=Print)
+func split(text string) []string {
        // count lines (comment text never ends in a newline)
        n := 1
-       for _, c := range text {
-               if c == '\n' {
+       for i := 0; i < len(text); i++ {
+               if text[i] == '\n' {
                        n++
                }
        }
 
        // split
-       lines := make([][]byte, n)
+       lines := make([]string, n)
        n = 0
        i := 0
-       for j, c := range text {
-               if c == '\n' {
+       for j := 0; j < len(text); j++ {
+               if text[j] == '\n' {
                        lines[n] = text[i:j] // exclude newline
                        i = j + 1            // discard newline
                        n++
@@ -391,16 +352,18 @@ func split(text []byte) [][]byte {
        return lines
 }
 
-func isBlank(s []byte) bool {
-       for _, b := range s {
-               if b > ' ' {
+// Returns true if s contains only white space
+// (only tabs and blanks can appear in the printer's context).
+func isBlank(s string) bool {
+       for i := 0; i < len(s); i++ {
+               if s[i] > ' ' {
                        return false
                }
        }
        return true
 }
 
-func commonPrefix(a, b []byte) []byte {
+func commonPrefix(a, b string) string {
        i := 0
        for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
                i++
@@ -408,7 +371,7 @@ func commonPrefix(a, b []byte) []byte {
        return a[0:i]
 }
 
-func stripCommonPrefix(lines [][]byte) {
+func stripCommonPrefix(lines []string) {
        if len(lines) < 2 {
                return // at most one line - nothing to do
        }
@@ -432,19 +395,21 @@ func stripCommonPrefix(lines [][]byte) {
        // Note that the first and last line are never empty (they
        // contain the opening /* and closing */ respectively) and
        // thus they can be ignored by the blank line check.
-       var prefix []byte
+       var prefix string
        if len(lines) > 2 {
+               first := true
                for i, line := range lines[1 : len(lines)-1] {
                        switch {
                        case isBlank(line):
-                               lines[1+i] = nil // range starts at line 1
-                       case prefix == nil:
+                               lines[1+i] = "" // range starts at line 1
+                       case first:
                                prefix = commonPrefix(line, line)
+                               first = false
                        default:
                                prefix = commonPrefix(prefix, line)
                        }
                }
-       } else { // len(lines) == 2
+       } else { // len(lines) == 2, lines cannot be blank (contain /* and */)
                line := lines[1]
                prefix = commonPrefix(line, line)
        }
@@ -453,7 +418,7 @@ func stripCommonPrefix(lines [][]byte) {
         * Check for vertical "line of stars" and correct prefix accordingly.
         */
        lineOfStars := false
-       if i := bytes.Index(prefix, []byte{'*'}); i >= 0 {
+       if i := strings.Index(prefix, "*"); i >= 0 {
                // Line of stars present.
                if i > 0 && prefix[i-1] == ' ' {
                        i-- // remove trailing blank from prefix so stars remain aligned
@@ -501,7 +466,7 @@ func stripCommonPrefix(lines [][]byte) {
                        }
                        // Shorten the computed common prefix by the length of
                        // suffix, if it is found as suffix of the prefix.
-                       if bytes.HasSuffix(prefix, suffix) {
+                       if strings.HasSuffix(prefix, string(suffix)) {
                                prefix = prefix[0 : len(prefix)-len(suffix)]
                        }
                }
@@ -511,19 +476,18 @@ func stripCommonPrefix(lines [][]byte) {
        // with the opening /*, otherwise align the text with the other
        // lines.
        last := lines[len(lines)-1]
-       closing := []byte("*/")
-       i := bytes.Index(last, closing)
+       closing := "*/"
+       i := strings.Index(last, closing) // i >= 0 (closing is always present)
        if isBlank(last[0:i]) {
                // last line only contains closing */
-               var sep []byte
                if lineOfStars {
-                       // insert an aligning blank
-                       sep = []byte{' '}
+                       closing = " */" // add blank to align final star
                }
-               lines[len(lines)-1] = bytes.Join([][]byte{prefix, closing}, sep)
+               lines[len(lines)-1] = prefix + closing
        } else {
                // last line contains more comment text - assume
-               // it is aligned like the other lines
+               // it is aligned like the other lines and include
+               // in prefix computation
                prefix = commonPrefix(prefix, last)
        }
 
@@ -549,9 +513,9 @@ func (p *printer) writeComment(comment *ast.Comment) {
                        // update our own idea of the file and line number
                        // accordingly, after printing the directive.
                        file := pos[:i]
-                       line, _ := strconv.Atoi(string(pos[i+1:]))
+                       line, _ := strconv.Atoi(pos[i+1:])
                        defer func() {
-                               p.pos.Filename = string(file)
+                               p.pos.Filename = file
                                p.pos.Line = line
                                p.pos.Column = 1
                        }()
@@ -560,26 +524,25 @@ func (p *printer) writeComment(comment *ast.Comment) {
 
        // shortcut common case of //-style comments
        if text[1] == '/' {
-               p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
+               p.writeItem(p.fset.Position(comment.Pos()), text, true)
                return
        }
 
        // for /*-style comments, print line by line and let the
        // write function take care of the proper indentation
-       lines := split([]byte(text))
+       lines := split(text)
        stripCommonPrefix(lines)
 
        // write comment lines, separated by formfeed,
        // without a line break after the last line
-       linebreak := formfeeds[0:1]
        pos := p.fset.Position(comment.Pos())
        for i, line := range lines {
                if i > 0 {
-                       p.write(linebreak)
+                       p.writeByte('\f')
                        pos = p.pos
                }
                if len(line) > 0 {
-                       p.writeItem(pos, p.escape(string(line)))
+                       p.writeItem(pos, line, true)
                }
        }
 }
@@ -615,7 +578,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
 
        // make sure we have a line break
        if needsLinebreak {
-               p.write([]byte{'\n'})
+               p.writeByte('\n')
        }
 
        return
@@ -641,7 +604,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
                if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == next.Line {
                        // the last comment is a /*-style comment and the next item
                        // follows on the same line: separate with an extra blank
-                       p.write([]byte{' '})
+                       p.writeByte(' ')
                }
                // ensure that there is a line break after a //-style comment,
                // before a closing '}' unless explicitly disabled, or at eof
@@ -661,7 +624,6 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
 // whiteWhitespace writes the first n whitespace entries.
 func (p *printer) writeWhitespace(n int) {
        // write entries
-       var data [1]byte
        for i := 0; i < n; i++ {
                switch ch := p.wsbuf[i]; ch {
                case ignore:
@@ -693,8 +655,7 @@ func (p *printer) writeWhitespace(n int) {
                        }
                        fallthrough
                default:
-                       data[0] = byte(ch)
-                       p.write(data[0:])
+                       p.writeByte(byte(ch))
                }
        }
 
@@ -710,7 +671,6 @@ func (p *printer) writeWhitespace(n int) {
 // ----------------------------------------------------------------------------
 // Printing interface
 
-
 func mayCombine(prev token.Token, next byte) (b bool) {
        switch prev {
        case token.INT:
@@ -743,7 +703,8 @@ func mayCombine(prev token.Token, next byte) (b bool) {
 func (p *printer) print(args ...interface{}) {
        for _, f := range args {
                next := p.pos // estimated position of next item
-               var data string
+               data := ""
+               isLit := false
                var tok token.Token
 
                switch x := f.(type) {
@@ -771,7 +732,8 @@ func (p *printer) print(args ...interface{}) {
                        data = x.Name
                        tok = token.IDENT
                case *ast.BasicLit:
-                       data = p.escape(x.Value)
+                       data = x.Value
+                       isLit = true
                        tok = x.Kind
                case token.Token:
                        s := x.String()
@@ -803,15 +765,20 @@ func (p *printer) print(args ...interface{}) {
                p.pos = next
 
                if data != "" {
-                       droppedFF := p.flush(next, tok)
+                       nl := byte('\n')
+                       if p.flush(next, tok) {
+                               nl = '\f' // dropped formfeed before
+                       }
 
                        // intersperse extra newlines if present in the source
                        // (don't do this in flush as it will cause extra newlines
                        // at the end of a file) - use formfeeds if we dropped one
                        // before
-                       p.writeNewlines(next.Line-p.pos.Line, droppedFF)
+                       if n := next.Line - p.pos.Line; n > 0 {
+                               p.writeNewlines(n, nl)
+                       }
 
-                       p.writeItem(next, data)
+                       p.writeItem(next, data, isLit)
                }
        }
 }
@@ -840,6 +807,35 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
        return
 }
 
+func (p *printer) printNode(node interface{}) error {
+       switch n := node.(type) {
+       case ast.Expr:
+               p.useNodeComments = true
+               p.expr(n, ignoreMultiLine)
+       case ast.Stmt:
+               p.useNodeComments = true
+               // A labeled statement will un-indent to position the
+               // label. Set indent to 1 so we don't get indent "underflow".
+               if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
+                       p.indent = 1
+               }
+               p.stmt(n, false, ignoreMultiLine)
+       case ast.Decl:
+               p.useNodeComments = true
+               p.decl(n, ignoreMultiLine)
+       case ast.Spec:
+               p.useNodeComments = true
+               p.spec(n, 1, false, ignoreMultiLine)
+       case *ast.File:
+               p.comments = n.Comments
+               p.useNodeComments = n.Comments == nil
+               p.file(n)
+       default:
+               return fmt.Errorf("go/printer: unsupported node type %T", n)
+       }
+       return nil
+}
+
 // ----------------------------------------------------------------------------
 // Trimmer
 
@@ -869,6 +865,8 @@ const (
 //              However, this would mess up any formatting done by
 //              the tabwriter.
 
+var aNewline = []byte("\n")
+
 func (p *trimmer) Write(data []byte) (n int, err error) {
        // invariants:
        // p.state == inSpace:
@@ -887,8 +885,8 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
                        case '\t', ' ':
                                p.space.WriteByte(b) // WriteByte returns no errors
                        case '\n', '\f':
-                               p.space.Reset()                        // discard trailing space
-                               _, err = p.output.Write(newlines[0:1]) // write newline
+                               p.space.Reset() // discard trailing space
+                               _, err = p.output.Write(aNewline)
                        case tabwriter.Escape:
                                _, err = p.output.Write(p.space.Bytes())
                                p.state = inEscape
@@ -915,7 +913,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
                                _, err = p.output.Write(data[m:n])
                                p.state = inSpace
                                p.space.Reset()
-                               _, err = p.output.Write(newlines[0:1]) // write newline
+                               _, err = p.output.Write(aNewline)
                        case tabwriter.Escape:
                                _, err = p.output.Write(data[m:n])
                                p.state = inEscape
@@ -957,15 +955,22 @@ type Config struct {
 }
 
 // fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
-func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (written int, err error) {
+func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) {
+       // print node
+       var p printer
+       p.init(cfg, fset, nodeSizes)
+       if err = p.printNode(node); err != nil {
+               return
+       }
+       p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
+
        // redirect output through a trimmer to eliminate trailing whitespace
        // (Input to a tabwriter must be untrimmed since trailing tabs provide
        // formatting information. The tabwriter could provide trimming
        // functionality but no tabwriter is used when RawFormat is set.)
        output = &trimmer{output: output}
 
-       // setup tabwriter if needed and redirect output
-       var tw *tabwriter.Writer
+       // redirect output through a tabwriter if necessary
        if cfg.Mode&RawFormat == 0 {
                minwidth := cfg.Tabwidth
 
@@ -980,63 +985,28 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{
                        twmode |= tabwriter.TabIndent
                }
 
-               tw = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)
-               output = tw
+               output = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)
        }
 
-       // setup printer
-       var p printer
-       p.init(output, cfg, fset, nodeSizes)
-       defer func() {
-               written = p.written
-               if e := recover(); e != nil {
-                       err = e.(osError).err // re-panics if it's not a local osError
-               }
-       }()
-
-       // print node
-       switch n := node.(type) {
-       case ast.Expr:
-               p.useNodeComments = true
-               p.expr(n, ignoreMultiLine)
-       case ast.Stmt:
-               p.useNodeComments = true
-               // A labeled statement will un-indent to position the
-               // label. Set indent to 1 so we don't get indent "underflow".
-               if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
-                       p.indent = 1
-               }
-               p.stmt(n, false, ignoreMultiLine)
-       case ast.Decl:
-               p.useNodeComments = true
-               p.decl(n, ignoreMultiLine)
-       case ast.Spec:
-               p.useNodeComments = true
-               p.spec(n, 1, false, ignoreMultiLine)
-       case *ast.File:
-               p.comments = n.Comments
-               p.useNodeComments = n.Comments == nil
-               p.file(n)
-       default:
-               panic(osError{fmt.Errorf("printer.Fprint: unsupported node type %T", n)})
+       // write printer result via tabwriter/trimmer to output
+       if _, err = output.Write(p.output.Bytes()); err != nil {
+               return
        }
-       p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
 
        // flush tabwriter, if any
-       if tw != nil {
-               tw.Flush() // ignore errors
+       if tw, _ := (output).(*tabwriter.Writer); tw != nil {
+               err = tw.Flush()
        }
 
        return
 }
 
-// Fprint "pretty-prints" an AST node to output and returns the number
-// of bytes written and an error (if any) for a given configuration cfg.
+// Fprint "pretty-prints" an AST node to output for a given configuration cfg.
 // Position information is interpreted relative to the file set fset.
 // The node type must be *ast.File, or assignment-compatible to ast.Expr,
 // ast.Decl, ast.Spec, or ast.Stmt.
 //
-func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, error) {
+func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
        return cfg.fprint(output, fset, node, make(map[ast.Node]int))
 }
 
@@ -1044,6 +1014,5 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
 // It calls Config.Fprint with default settings.
 //
 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
-       _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't care about number of bytes written
-       return err
+       return (&Config{Tabwidth: 8}).Fprint(output, fset, node)
 }
index a644aa383ab9df1006d188709af31d12c6b74ba8..924d4dfdb2901a769901791be091aa9de579c58b 100644 (file)
@@ -62,7 +62,7 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) {
 
        // format source
        var buf bytes.Buffer
-       if _, err := cfg.Fprint(&buf, fset, prog); err != nil {
+       if err := cfg.Fprint(&buf, fset, prog); err != nil {
                t.Error(err)
        }
        res := buf.Bytes()
index 10bed2f05e43626158ecf0d01eae2b861b6f9663..8103a89d439058dc55c3d979381caa3206d0723d 100644 (file)
@@ -71,14 +71,13 @@ func (d *digest) Write(p []byte) (nn int, err error) {
 
 func (d *digest) Sum32() uint32 { return finish(d.a, d.b) }
 
-func (d *digest) Sum() []byte {
-       p := make([]byte, 4)
+func (d *digest) Sum(in []byte) []byte {
        s := d.Sum32()
-       p[0] = byte(s >> 24)
-       p[1] = byte(s >> 16)
-       p[2] = byte(s >> 8)
-       p[3] = byte(s)
-       return p
+       in = append(in, byte(s>>24))
+       in = append(in, byte(s>>16))
+       in = append(in, byte(s>>8))
+       in = append(in, byte(s))
+       return in
 }
 
 // Checksum returns the Adler-32 checksum of data.
index 5980ec0dc98e685f72845f83a86173bcc494b7a5..557fab8a52e1a5314f4e404ad26a3f188d8e22fd 100644 (file)
@@ -119,14 +119,13 @@ func (d *digest) Write(p []byte) (n int, err error) {
 
 func (d *digest) Sum32() uint32 { return d.crc }
 
-func (d *digest) Sum() []byte {
-       p := make([]byte, 4)
+func (d *digest) Sum(in []byte) []byte {
        s := d.Sum32()
-       p[0] = byte(s >> 24)
-       p[1] = byte(s >> 16)
-       p[2] = byte(s >> 8)
-       p[3] = byte(s)
-       return p
+       in = append(in, byte(s>>24))
+       in = append(in, byte(s>>16))
+       in = append(in, byte(s>>8))
+       in = append(in, byte(s))
+       return in
 }
 
 // Checksum returns the CRC-32 checksum of data
index 42e53c3a5bd0faf45d4d30abc441292286e946a8..e5c1db4b3d2f8c9bffb67aa6552124c184420876 100644 (file)
@@ -75,18 +75,17 @@ func (d *digest) Write(p []byte) (n int, err error) {
 
 func (d *digest) Sum64() uint64 { return d.crc }
 
-func (d *digest) Sum() []byte {
-       p := make([]byte, 8)
+func (d *digest) Sum(in []byte) []byte {
        s := d.Sum64()
-       p[0] = byte(s >> 56)
-       p[1] = byte(s >> 48)
-       p[2] = byte(s >> 40)
-       p[3] = byte(s >> 32)
-       p[4] = byte(s >> 24)
-       p[5] = byte(s >> 16)
-       p[6] = byte(s >> 8)
-       p[7] = byte(s)
-       return p
+       in = append(in, byte(s>>56))
+       in = append(in, byte(s>>48))
+       in = append(in, byte(s>>40))
+       in = append(in, byte(s>>32))
+       in = append(in, byte(s>>24))
+       in = append(in, byte(s>>16))
+       in = append(in, byte(s>>8))
+       in = append(in, byte(s))
+       return in
 }
 
 // Checksum returns the CRC-64 checksum of data
index ce3ed0d0f406722fbca8f986dbd96df7f2142660..2c8a25118e24aeda190bb7c9c30a2e95e0e153db 100644 (file)
@@ -8,7 +8,6 @@
 package fnv
 
 import (
-       "encoding/binary"
        "hash"
 )
 
@@ -105,26 +104,46 @@ func (s *sum32a) Size() int { return 4 }
 func (s *sum64) Size() int  { return 8 }
 func (s *sum64a) Size() int { return 8 }
 
-func (s *sum32) Sum() []byte {
-       a := make([]byte, 4)
-       binary.BigEndian.PutUint32(a, uint32(*s))
-       return a
+func (s *sum32) Sum(in []byte) []byte {
+       v := uint32(*s)
+       in = append(in, byte(v>>24))
+       in = append(in, byte(v>>16))
+       in = append(in, byte(v>>8))
+       in = append(in, byte(v))
+       return in
 }
 
-func (s *sum32a) Sum() []byte {
-       a := make([]byte, 4)
-       binary.BigEndian.PutUint32(a, uint32(*s))
-       return a
+func (s *sum32a) Sum(in []byte) []byte {
+       v := uint32(*s)
+       in = append(in, byte(v>>24))
+       in = append(in, byte(v>>16))
+       in = append(in, byte(v>>8))
+       in = append(in, byte(v))
+       return in
 }
 
-func (s *sum64) Sum() []byte {
-       a := make([]byte, 8)
-       binary.BigEndian.PutUint64(a, uint64(*s))
-       return a
+func (s *sum64) Sum(in []byte) []byte {
+       v := uint64(*s)
+       in = append(in, byte(v>>56))
+       in = append(in, byte(v>>48))
+       in = append(in, byte(v>>40))
+       in = append(in, byte(v>>32))
+       in = append(in, byte(v>>24))
+       in = append(in, byte(v>>16))
+       in = append(in, byte(v>>8))
+       in = append(in, byte(v))
+       return in
 }
 
-func (s *sum64a) Sum() []byte {
-       a := make([]byte, 8)
-       binary.BigEndian.PutUint64(a, uint64(*s))
-       return a
+func (s *sum64a) Sum(in []byte) []byte {
+       v := uint64(*s)
+       in = append(in, byte(v>>56))
+       in = append(in, byte(v>>48))
+       in = append(in, byte(v>>40))
+       in = append(in, byte(v>>32))
+       in = append(in, byte(v>>24))
+       in = append(in, byte(v>>16))
+       in = append(in, byte(v>>8))
+       in = append(in, byte(v))
+       return in
 }
index 429230c80b4f58b8f4033da190543bf0f9f9b020..17454deda904ff29c0f024c4dc537bd17e82f111 100644 (file)
@@ -72,7 +72,7 @@ func testGolden(t *testing.T, hash hash.Hash, gold []golden) {
                if done != len(g.text) {
                        t.Fatalf("wrote only %d out of %d bytes", done, len(g.text))
                }
-               if actual := hash.Sum(); !bytes.Equal(g.sum, actual) {
+               if actual := hash.Sum(nil); !bytes.Equal(g.sum, actual) {
                        t.Errorf("hash(%q) = 0x%x want 0x%x", g.text, actual, g.sum)
                }
        }
@@ -97,26 +97,26 @@ func TestIntegrity64a(t *testing.T) {
 func testIntegrity(t *testing.T, h hash.Hash) {
        data := []byte{'1', '2', 3, 4, 5}
        h.Write(data)
-       sum := h.Sum()
+       sum := h.Sum(nil)
 
        if size := h.Size(); size != len(sum) {
                t.Fatalf("Size()=%d but len(Sum())=%d", size, len(sum))
        }
 
-       if a := h.Sum(); !bytes.Equal(sum, a) {
+       if a := h.Sum(nil); !bytes.Equal(sum, a) {
                t.Fatalf("first Sum()=0x%x, second Sum()=0x%x", sum, a)
        }
 
        h.Reset()
        h.Write(data)
-       if a := h.Sum(); !bytes.Equal(sum, a) {
+       if a := h.Sum(nil); !bytes.Equal(sum, a) {
                t.Fatalf("Sum()=0x%x, but after Reset() Sum()=0x%x", sum, a)
        }
 
        h.Reset()
        h.Write(data[:2])
        h.Write(data[2:])
-       if a := h.Sum(); !bytes.Equal(sum, a) {
+       if a := h.Sum(nil); !bytes.Equal(sum, a) {
                t.Fatalf("Sum()=0x%x, but with partial writes, Sum()=0x%x", sum, a)
        }
 
@@ -162,6 +162,6 @@ func benchmark(b *testing.B, h hash.Hash) {
        for todo := b.N; todo != 0; todo-- {
                h.Reset()
                h.Write(data)
-               h.Sum()
+               h.Sum(nil)
        }
 }
index 3536c0b6a64a89dbb279276ded71ef47525bab90..0d7765dc505b155f26c7835c5af9db75c2d0f24c 100644 (file)
@@ -13,9 +13,9 @@ type Hash interface {
        // It never returns an error.
        io.Writer
 
-       // Sum returns the current hash, without changing the
-       // underlying hash state.
-       Sum() []byte
+       // Sum appends the current hash in the same manner as append(), without
+       // changing the underlying hash state.
+       Sum(in []byte) []byte
 
        // Reset resets the hash to one with zero bytes written.
        Reset()
diff --git a/libgo/go/html/doctype.go b/libgo/go/html/doctype.go
new file mode 100644 (file)
index 0000000..f692061
--- /dev/null
@@ -0,0 +1,156 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package html
+
+import (
+       "strings"
+)
+
+// parseDoctype parses the data from a DoctypeToken into a name,
+// public identifier, and system identifier. It returns a Node whose Type 
+// is DoctypeNode, whose Data is the name, and which has attributes
+// named "system" and "public" for the two identifiers if they were present.
+// quirks is whether the document should be parsed in "quirks mode".
+func parseDoctype(s string) (n *Node, quirks bool) {
+       n = &Node{Type: DoctypeNode}
+
+       // Find the name.
+       space := strings.IndexAny(s, whitespace)
+       if space == -1 {
+               space = len(s)
+       }
+       n.Data = s[:space]
+       // The comparison to "html" is case-sensitive.
+       if n.Data != "html" {
+               quirks = true
+       }
+       n.Data = strings.ToLower(n.Data)
+       s = strings.TrimLeft(s[space:], whitespace)
+
+       if len(s) < 6 {
+               // It can't start with "PUBLIC" or "SYSTEM".
+               // Ignore the rest of the string.
+               return n, quirks || s != ""
+       }
+
+       key := strings.ToLower(s[:6])
+       s = s[6:]
+       for key == "public" || key == "system" {
+               s = strings.TrimLeft(s, whitespace)
+               if s == "" {
+                       break
+               }
+               quote := s[0]
+               if quote != '"' && quote != '\'' {
+                       break
+               }
+               s = s[1:]
+               q := strings.IndexRune(s, rune(quote))
+               var id string
+               if q == -1 {
+                       id = s
+                       s = ""
+               } else {
+                       id = s[:q]
+                       s = s[q+1:]
+               }
+               n.Attr = append(n.Attr, Attribute{Key: key, Val: id})
+               if key == "public" {
+                       key = "system"
+               } else {
+                       key = ""
+               }
+       }
+
+       if key != "" || s != "" {
+               quirks = true
+       } else if len(n.Attr) > 0 {
+               if n.Attr[0].Key == "public" {
+                       public := strings.ToLower(n.Attr[0].Val)
+                       switch public {
+                       case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html":
+                               quirks = true
+                       default:
+                               for _, q := range quirkyIDs {
+                                       if strings.HasPrefix(public, q) {
+                                               quirks = true
+                                               break
+                                       }
+                               }
+                       }
+                       // The following two public IDs only cause quirks mode if there is no system ID.
+                       if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") ||
+                               strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) {
+                               quirks = true
+                       }
+               }
+               if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
+                       strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
+                       quirks = true
+               }
+       }
+
+       return n, quirks
+}
+
+// quirkyIDs is a list of public doctype identifiers that cause a document
+// to be interpreted in quirks mode. The identifiers should be in lower case.
+var quirkyIDs = []string{
+       "+//silmaril//dtd html pro v0r11 19970101//",
+       "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
+       "-//as//dtd html 3.0 aswedit + extensions//",
+       "-//ietf//dtd html 2.0 level 1//",
+       "-//ietf//dtd html 2.0 level 2//",
+       "-//ietf//dtd html 2.0 strict level 1//",
+       "-//ietf//dtd html 2.0 strict level 2//",
+       "-//ietf//dtd html 2.0 strict//",
+       "-//ietf//dtd html 2.0//",
+       "-//ietf//dtd html 2.1e//",
+       "-//ietf//dtd html 3.0//",
+       "-//ietf//dtd html 3.2 final//",
+       "-//ietf//dtd html 3.2//",
+       "-//ietf//dtd html 3//",
+       "-//ietf//dtd html level 0//",
+       "-//ietf//dtd html level 1//",
+       "-//ietf//dtd html level 2//",
+       "-//ietf//dtd html level 3//",
+       "-//ietf//dtd html strict level 0//",
+       "-//ietf//dtd html strict level 1//",
+       "-//ietf//dtd html strict level 2//",
+       "-//ietf//dtd html strict level 3//",
+       "-//ietf//dtd html strict//",
+       "-//ietf//dtd html//",
+       "-//metrius//dtd metrius presentational//",
+       "-//microsoft//dtd internet explorer 2.0 html strict//",
+       "-//microsoft//dtd internet explorer 2.0 html//",
+       "-//microsoft//dtd internet explorer 2.0 tables//",
+       "-//microsoft//dtd internet explorer 3.0 html strict//",
+       "-//microsoft//dtd internet explorer 3.0 html//",
+       "-//microsoft//dtd internet explorer 3.0 tables//",
+       "-//netscape comm. corp.//dtd html//",
+       "-//netscape comm. corp.//dtd strict html//",
+       "-//o'reilly and associates//dtd html 2.0//",
+       "-//o'reilly and associates//dtd html extended 1.0//",
+       "-//o'reilly and associates//dtd html extended relaxed 1.0//",
+       "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
+       "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
+       "-//spyglass//dtd html 2.0 extended//",
+       "-//sq//dtd html 2.0 hotmetal + extensions//",
+       "-//sun microsystems corp.//dtd hotjava html//",
+       "-//sun microsystems corp.//dtd hotjava strict html//",
+       "-//w3c//dtd html 3 1995-03-24//",
+       "-//w3c//dtd html 3.2 draft//",
+       "-//w3c//dtd html 3.2 final//",
+       "-//w3c//dtd html 3.2//",
+       "-//w3c//dtd html 3.2s draft//",
+       "-//w3c//dtd html 4.0 frameset//",
+       "-//w3c//dtd html 4.0 transitional//",
+       "-//w3c//dtd html experimental 19960712//",
+       "-//w3c//dtd html experimental 970421//",
+       "-//w3c//dtd w3 html//",
+       "-//w3o//dtd w3 html 3.0//",
+       "-//webtechs//dtd mozilla html 2.0//",
+       "-//webtechs//dtd mozilla html//",
+}
index 9b7e934ac343e27b1ac9e6f55d587a421e0b729d..97fbc514d827abc75ba8b81a79c325afe6ace497 100644 (file)
@@ -37,6 +37,11 @@ type parser struct {
        // fosterParenting is whether new elements should be inserted according to
        // the foster parenting rules (section 11.2.5.3).
        fosterParenting bool
+       // quirks is whether the parser is operating in "quirks mode."
+       quirks bool
+       // context is the context element when parsing an HTML fragment
+       // (section 11.4).
+       context *Node
 }
 
 func (p *parser) top() *Node {
@@ -285,9 +290,10 @@ func (p *parser) setOriginalIM() {
 func (p *parser) resetInsertionMode() {
        for i := len(p.oe) - 1; i >= 0; i-- {
                n := p.oe[i]
-               if i == 0 {
-                       // TODO: set n to the context element, for HTML fragment parsing.
+               if i == 0 && p.context != nil {
+                       n = p.context
                }
+
                switch n.Data {
                case "select":
                        p.im = inSelectIM
@@ -319,9 +325,17 @@ func (p *parser) resetInsertionMode() {
        p.im = inBodyIM
 }
 
+const whitespace = " \t\r\n\f"
+
 // Section 11.2.5.4.1.
 func initialIM(p *parser) bool {
        switch p.tok.Type {
+       case TextToken:
+               p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+               if len(p.tok.Data) == 0 {
+                       // It was all whitespace, so ignore it.
+                       return true
+               }
        case CommentToken:
                p.doc.Add(&Node{
                        Type: CommentNode,
@@ -329,15 +343,13 @@ func initialIM(p *parser) bool {
                })
                return true
        case DoctypeToken:
-               p.doc.Add(&Node{
-                       Type: DoctypeNode,
-                       Data: p.tok.Data,
-               })
+               n, quirks := parseDoctype(p.tok.Data)
+               p.doc.Add(n)
+               p.quirks = quirks
                p.im = beforeHTMLIM
                return true
        }
-       // TODO: set "quirks mode"? It's defined in the DOM spec instead of HTML5 proper,
-       // and so switching on "quirks mode" might belong in a different package.
+       p.quirks = true
        p.im = beforeHTMLIM
        return false
 }
@@ -345,6 +357,12 @@ func initialIM(p *parser) bool {
 // Section 11.2.5.4.2.
 func beforeHTMLIM(p *parser) bool {
        switch p.tok.Type {
+       case TextToken:
+               p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+               if len(p.tok.Data) == 0 {
+                       // It was all whitespace, so ignore it.
+                       return true
+               }
        case StartTagToken:
                if p.tok.Data == "html" {
                        p.addElement(p.tok.Data, p.tok.Attr)
@@ -383,7 +401,11 @@ func beforeHeadIM(p *parser) bool {
        case ErrorToken:
                implied = true
        case TextToken:
-               // TODO: distinguish whitespace text from others.
+               p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
+               if len(p.tok.Data) == 0 {
+                       // It was all whitespace, so ignore it.
+                       return true
+               }
                implied = true
        case StartTagToken:
                switch p.tok.Data {
@@ -417,8 +439,6 @@ func beforeHeadIM(p *parser) bool {
        return !implied
 }
 
-const whitespace = " \t\r\n\f"
-
 // Section 11.2.5.4.4.
 func inHeadIM(p *parser) bool {
        var (
@@ -441,6 +461,8 @@ func inHeadIM(p *parser) bool {
                implied = true
        case StartTagToken:
                switch p.tok.Data {
+               case "html":
+                       return inBodyIM(p)
                case "base", "basefont", "bgsound", "command", "link", "meta":
                        p.addElement(p.tok.Data, p.tok.Attr)
                        p.oe.pop()
@@ -450,6 +472,9 @@ func inHeadIM(p *parser) bool {
                        p.setOriginalIM()
                        p.im = textIM
                        return true
+               case "head":
+                       // Ignore the token.
+                       return true
                default:
                        implied = true
                }
@@ -560,11 +585,30 @@ func copyAttributes(dst *Node, src Token) {
 func inBodyIM(p *parser) bool {
        switch p.tok.Type {
        case TextToken:
+               switch n := p.oe.top(); n.Data {
+               case "pre", "listing", "textarea":
+                       if len(n.Child) == 0 {
+                               // Ignore a newline at the start of a <pre> block.
+                               d := p.tok.Data
+                               if d != "" && d[0] == '\r' {
+                                       d = d[1:]
+                               }
+                               if d != "" && d[0] == '\n' {
+                                       d = d[1:]
+                               }
+                               if d == "" {
+                                       return true
+                               }
+                               p.tok.Data = d
+                       }
+               }
                p.reconstructActiveFormattingElements()
                p.addText(p.tok.Data)
                p.framesetOK = false
        case StartTagToken:
                switch p.tok.Data {
+               case "html":
+                       copyAttributes(p.oe[0], p.tok)
                case "address", "article", "aside", "blockquote", "center", "details", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "menu", "nav", "ol", "p", "section", "summary", "ul":
                        p.popUntil(buttonScopeStopTags, "p")
                        p.addElement(p.tok.Data, p.tok.Attr)
@@ -589,6 +633,13 @@ func inBodyIM(p *parser) bool {
                case "b", "big", "code", "em", "font", "i", "s", "small", "strike", "strong", "tt", "u":
                        p.reconstructActiveFormattingElements()
                        p.addFormattingElement(p.tok.Data, p.tok.Attr)
+               case "nobr":
+                       p.reconstructActiveFormattingElements()
+                       if p.elementInScope(defaultScopeStopTags, "nobr") {
+                               p.inBodyEndTagFormatting("nobr")
+                               p.reconstructActiveFormattingElements()
+                       }
+                       p.addFormattingElement(p.tok.Data, p.tok.Attr)
                case "applet", "marquee", "object":
                        p.reconstructActiveFormattingElements()
                        p.addElement(p.tok.Data, p.tok.Attr)
@@ -601,7 +652,9 @@ func inBodyIM(p *parser) bool {
                        p.acknowledgeSelfClosingTag()
                        p.framesetOK = false
                case "table":
-                       p.popUntil(buttonScopeStopTags, "p") // TODO: skip this step in quirks mode.
+                       if !p.quirks {
+                               p.popUntil(buttonScopeStopTags, "p")
+                       }
                        p.addElement(p.tok.Data, p.tok.Attr)
                        p.framesetOK = false
                        p.im = inTableIM
@@ -721,6 +774,11 @@ func inBodyIM(p *parser) bool {
                        p.oe.pop()
                        p.oe.pop()
                        p.form = nil
+               case "xmp":
+                       p.popUntil(buttonScopeStopTags, "p")
+                       p.reconstructActiveFormattingElements()
+                       p.framesetOK = false
+                       p.addElement(p.tok.Data, p.tok.Attr)
                case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr":
                        // Ignore the token.
                default:
@@ -1462,18 +1520,7 @@ func afterAfterFramesetIM(p *parser) bool {
        return true
 }
 
-// Parse returns the parse tree for the HTML from the given Reader.
-// The input is assumed to be UTF-8 encoded.
-func Parse(r io.Reader) (*Node, error) {
-       p := &parser{
-               tokenizer: NewTokenizer(r),
-               doc: &Node{
-                       Type: DocumentNode,
-               },
-               scripting:  true,
-               framesetOK: true,
-               im:         initialIM,
-       }
+func (p *parser) parse() error {
        // Iterate until EOF. Any other error will cause an early return.
        consumed := true
        for {
@@ -1482,7 +1529,7 @@ func Parse(r io.Reader) (*Node, error) {
                                if err == io.EOF {
                                        break
                                }
-                               return nil, err
+                               return err
                        }
                }
                consumed = p.im(p)
@@ -1493,5 +1540,77 @@ func Parse(r io.Reader) (*Node, error) {
                        break
                }
        }
+       return nil
+}
+
+// Parse returns the parse tree for the HTML from the given Reader.
+// The input is assumed to be UTF-8 encoded.
+func Parse(r io.Reader) (*Node, error) {
+       p := &parser{
+               tokenizer: NewTokenizer(r),
+               doc: &Node{
+                       Type: DocumentNode,
+               },
+               scripting:  true,
+               framesetOK: true,
+               im:         initialIM,
+       }
+       err := p.parse()
+       if err != nil {
+               return nil, err
+       }
        return p.doc, nil
 }
+
+// ParseFragment parses a fragment of HTML and returns the nodes that were 
+// found. If the fragment is the InnerHTML for an existing element, pass that
+// element in context.
+func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
+       p := &parser{
+               tokenizer: NewTokenizer(r),
+               doc: &Node{
+                       Type: DocumentNode,
+               },
+               scripting: true,
+               context:   context,
+       }
+
+       if context != nil {
+               switch context.Data {
+               case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp":
+                       p.tokenizer.rawTag = context.Data
+               }
+       }
+
+       root := &Node{
+               Type: ElementNode,
+               Data: "html",
+       }
+       p.doc.Add(root)
+       p.oe = nodeStack{root}
+       p.resetInsertionMode()
+
+       for n := context; n != nil; n = n.Parent {
+               if n.Type == ElementNode && n.Data == "form" {
+                       p.form = n
+                       break
+               }
+       }
+
+       err := p.parse()
+       if err != nil {
+               return nil, err
+       }
+
+       parent := p.doc
+       if context != nil {
+               parent = root
+       }
+
+       result := parent.Child
+       parent.Child = nil
+       for _, n := range result {
+               n.Parent = nil
+       }
+       return result, nil
+}
index 4f15ae1d554c55eae9a3db4a9708939fc1759468..e0c19cff6da5f91deccd66de4cfe7fd694dcb764 100644 (file)
@@ -10,65 +10,77 @@ import (
        "errors"
        "fmt"
        "io"
-       "io/ioutil"
        "os"
        "strings"
        "testing"
 )
 
-func pipeErr(err error) io.Reader {
-       pr, pw := io.Pipe()
-       pw.CloseWithError(err)
-       return pr
-}
-
-func readDat(filename string, c chan io.Reader) {
-       defer close(c)
-       f, err := os.Open("testdata/webkit/" + filename)
+// readParseTest reads a single test case from r.
+func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
+       line, err := r.ReadSlice('\n')
        if err != nil {
-               c <- pipeErr(err)
-               return
+               return "", "", "", err
        }
-       defer f.Close()
+       var b []byte
 
-       // Loop through the lines of the file. Each line beginning with "#" denotes
-       // a new section, which is returned as a separate io.Reader.
-       r := bufio.NewReader(f)
-       var pw *io.PipeWriter
+       // Read the HTML.
+       if string(line) != "#data\n" {
+               return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line)
+       }
        for {
-               line, err := r.ReadSlice('\n')
+               line, err = r.ReadSlice('\n')
                if err != nil {
-                       if pw != nil {
-                               pw.CloseWithError(err)
-                               pw = nil
-                       } else {
-                               c <- pipeErr(err)
-                       }
-                       return
+                       return "", "", "", err
                }
-               if len(line) == 0 {
-                       continue
+               if line[0] == '#' {
+                       break
+               }
+               b = append(b, line...)
+       }
+       text = strings.TrimRight(string(b), "\n")
+       b = b[:0]
+
+       // Skip the error list.
+       if string(line) != "#errors\n" {
+               return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line)
+       }
+       for {
+               line, err = r.ReadSlice('\n')
+               if err != nil {
+                       return "", "", "", err
                }
                if line[0] == '#' {
-                       if pw != nil {
-                               pw.Close()
-                       }
-                       var pr *io.PipeReader
-                       pr, pw = io.Pipe()
-                       c <- pr
-                       continue
+                       break
+               }
+       }
+
+       if string(line) == "#document-fragment\n" {
+               line, err = r.ReadSlice('\n')
+               if err != nil {
+                       return "", "", "", err
                }
-               if line[0] != '|' {
-                       // Strip the trailing '\n'.
-                       line = line[:len(line)-1]
+               context = strings.TrimSpace(string(line))
+               line, err = r.ReadSlice('\n')
+               if err != nil {
+                       return "", "", "", err
                }
-               if pw != nil {
-                       if _, err := pw.Write(line); err != nil {
-                               pw.CloseWithError(err)
-                               pw = nil
-                       }
+       }
+
+       // Read the dump of what the parse tree should be.
+       if string(line) != "#document\n" {
+               return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line)
+       }
+       for {
+               line, err = r.ReadSlice('\n')
+               if err != nil && err != io.EOF {
+                       return "", "", "", err
+               }
+               if len(line) == 0 || len(line) == 1 && line[0] == '\n' {
+                       break
                }
+               b = append(b, line...)
        }
+       return text, string(b), context, nil
 }
 
 func dumpIndent(w io.Writer, level int) {
@@ -93,11 +105,27 @@ func dumpLevel(w io.Writer, n *Node, level int) error {
                        fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
                }
        case TextNode:
-               fmt.Fprintf(w, "%q", n.Data)
+               fmt.Fprintf(w, `"%s"`, n.Data)
        case CommentNode:
                fmt.Fprintf(w, "<!-- %s -->", n.Data)
        case DoctypeNode:
-               fmt.Fprintf(w, "<!DOCTYPE %s>", n.Data)
+               fmt.Fprintf(w, "<!DOCTYPE %s", n.Data)
+               if n.Attr != nil {
+                       var p, s string
+                       for _, a := range n.Attr {
+                               switch a.Key {
+                               case "public":
+                                       p = a.Val
+                               case "system":
+                                       s = a.Val
+                               }
+                       }
+                       if p != "" || s != "" {
+                               fmt.Fprintf(w, ` "%s"`, p)
+                               fmt.Fprintf(w, ` "%s"`, s)
+                       }
+               }
+               io.WriteString(w, ">")
        case scopeMarkerNode:
                return errors.New("unexpected scopeMarkerNode")
        default:
@@ -133,46 +161,62 @@ func TestParser(t *testing.T) {
                n int
        }{
                // TODO(nigeltao): Process all the test cases from all the .dat files.
+               {"doctype01.dat", -1},
                {"tests1.dat", -1},
-               {"tests2.dat", 43},
-               {"tests3.dat", 0},
+               {"tests2.dat", -1},
+               {"tests3.dat", -1},
+               {"tests4.dat", -1},
+               {"tests5.dat", -1},
        }
        for _, tf := range testFiles {
-               rc := make(chan io.Reader)
-               go readDat(tf.filename, rc)
+               f, err := os.Open("testdata/webkit/" + tf.filename)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer f.Close()
+               r := bufio.NewReader(f)
                for i := 0; i != tf.n; i++ {
-                       // Parse the #data section.
-                       dataReader := <-rc
-                       if dataReader == nil {
+                       text, want, context, err := readParseTest(r)
+                       if err == io.EOF && tf.n == -1 {
                                break
                        }
-                       b, err := ioutil.ReadAll(dataReader)
                        if err != nil {
                                t.Fatal(err)
                        }
-                       text := string(b)
-                       doc, err := Parse(strings.NewReader(text))
-                       if err != nil {
-                               t.Fatal(err)
+
+                       var doc *Node
+                       if context == "" {
+                               doc, err = Parse(strings.NewReader(text))
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                       } else {
+                               contextNode := &Node{
+                                       Type: ElementNode,
+                                       Data: context,
+                               }
+                               nodes, err := ParseFragment(strings.NewReader(text), contextNode)
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                               doc = &Node{
+                                       Type: DocumentNode,
+                               }
+                               for _, n := range nodes {
+                                       doc.Add(n)
+                               }
                        }
+
                        got, err := dump(doc)
                        if err != nil {
                                t.Fatal(err)
                        }
-                       // Skip the #error section.
-                       if _, err := io.Copy(ioutil.Discard, <-rc); err != nil {
-                               t.Fatal(err)
-                       }
                        // Compare the parsed tree to the #document section.
-                       b, err = ioutil.ReadAll(<-rc)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       if want := string(b); got != want {
+                       if got != want {
                                t.Errorf("%s test #%d %q, got vs want:\n----\n%s----\n%s----", tf.filename, i, text, got, want)
                                continue
                        }
-                       if renderTestBlacklist[text] {
+                       if renderTestBlacklist[text] || context != "" {
                                continue
                        }
                        // Check that rendering and re-parsing results in an identical tree.
@@ -193,12 +237,6 @@ func TestParser(t *testing.T) {
                                continue
                        }
                }
-               // Drain any untested cases for the test file.
-               for r := range rc {
-                       if _, err := ioutil.ReadAll(r); err != nil {
-                               t.Fatal(err)
-                       }
-               }
        }
 }
 
index 92c349fb32c890addd69796b02b03279641b0eff..7e1a4669657d1fce4262272eafff29992aeb0b71 100644 (file)
@@ -9,6 +9,7 @@ import (
        "errors"
        "fmt"
        "io"
+       "strings"
 )
 
 type writer interface {
@@ -98,6 +99,40 @@ func render1(w writer, n *Node) error {
                if _, err := w.WriteString(n.Data); err != nil {
                        return err
                }
+               if n.Attr != nil {
+                       var p, s string
+                       for _, a := range n.Attr {
+                               switch a.Key {
+                               case "public":
+                                       p = a.Val
+                               case "system":
+                                       s = a.Val
+                               }
+                       }
+                       if p != "" {
+                               if _, err := w.WriteString(" PUBLIC "); err != nil {
+                                       return err
+                               }
+                               if err := writeQuoted(w, p); err != nil {
+                                       return err
+                               }
+                               if s != "" {
+                                       if err := w.WriteByte(' '); err != nil {
+                                               return err
+                                       }
+                                       if err := writeQuoted(w, s); err != nil {
+                                               return err
+                                       }
+                               }
+                       } else if s != "" {
+                               if _, err := w.WriteString(" SYSTEM "); err != nil {
+                                       return err
+                               }
+                               if err := writeQuoted(w, s); err != nil {
+                                       return err
+                               }
+                       }
+               }
                return w.WriteByte('>')
        default:
                return errors.New("html: unknown node type")
@@ -138,9 +173,19 @@ func render1(w writer, n *Node) error {
                return err
        }
 
+       // Add initial newline where there is danger of a newline beging ignored.
+       if len(n.Child) > 0 && n.Child[0].Type == TextNode && strings.HasPrefix(n.Child[0].Data, "\n") {
+               switch n.Data {
+               case "pre", "listing", "textarea":
+                       if err := w.WriteByte('\n'); err != nil {
+                               return err
+                       }
+               }
+       }
+
        // Render any child nodes.
        switch n.Data {
-       case "noembed", "noframes", "noscript", "plaintext", "script", "style":
+       case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
                for _, c := range n.Child {
                        if c.Type != TextNode {
                                return fmt.Errorf("html: raw text element <%s> has non-text child node", n.Data)
@@ -181,6 +226,27 @@ func render1(w writer, n *Node) error {
        return w.WriteByte('>')
 }
 
+// writeQuoted writes s to w surrounded by quotes. Normally it will use double
+// quotes, but if s contains a double quote, it will use single quotes.
+// It is used for writing the identifiers in a doctype declaration.
+// In valid HTML, they can't contain both types of quotes.
+func writeQuoted(w writer, s string) error {
+       var q byte = '"'
+       if strings.Contains(s, `"`) {
+               q = '\''
+       }
+       if err := w.WriteByte(q); err != nil {
+               return err
+       }
+       if _, err := w.WriteString(s); err != nil {
+               return err
+       }
+       if err := w.WriteByte(q); err != nil {
+               return err
+       }
+       return nil
+}
+
 // Section 13.1.2, "Elements", gives this list of void elements. Void elements
 // are those that can't have any contents.
 var voidElements = map[string]bool{
index ed1698acd8b646e8c36c25ca4a372e2477460ca5..39788173b999019022e57bf6333de7aaf0dc3c39 100644 (file)
@@ -7,8 +7,6 @@ package template
 import (
        "bytes"
        "testing"
-       "text/template"
-       "text/template/parse"
 )
 
 func TestClone(t *testing.T) {
@@ -48,15 +46,20 @@ func TestClone(t *testing.T) {
        }
 
        for _, test := range tests {
-               s := template.Must(template.New("s").Parse(test.input))
-               d := template.New("d")
-               d.Tree = &parse.Tree{Name: d.Name(), Root: cloneList(s.Root)}
+               s, err := New("s").Parse(test.input)
+               if err != nil {
+                       t.Errorf("input=%q: unexpected parse error %v", test.input, err)
+               }
+
+               d, _ := New("d").Parse(test.input)
+               // Hack: just replace the root of the tree.
+               d.text.Root = cloneList(s.text.Root)
 
-               if want, got := s.Root.String(), d.Root.String(); want != got {
+               if want, got := s.text.Root.String(), d.text.Root.String(); want != got {
                        t.Errorf("want %q, got %q", want, got)
                }
 
-               err := escape(d)
+               err = escapeTemplates(d, "d")
                if err != nil {
                        t.Errorf("%q: failed to escape: %s", test.input, err)
                        continue
@@ -73,18 +76,17 @@ func TestClone(t *testing.T) {
 
                data := []string{"foo", "<bar>", "baz"}
 
-               // Make sure escaping d did not affect s.
                var b bytes.Buffer
-               s.Execute(&b, data)
-               if got := b.String(); got != test.want {
-                       t.Errorf("%q: want %q, got %q", test.input, test.want, got)
-                       continue
+               d.Execute(&b, data)
+               if got := b.String(); got != test.wantClone {
+                       t.Errorf("input=%q: want %q, got %q", test.input, test.wantClone, got)
                }
 
+               // Make sure escaping d did not affect s.
                b.Reset()
-               d.Execute(&b, data)
-               if got := b.String(); got != test.wantClone {
-                       t.Errorf("%q: want %q, got %q", test.input, test.wantClone, got)
+               s.text.Execute(&b, data)
+               if got := b.String(); got != test.want {
+                       t.Errorf("input=%q: want %q, got %q", test.input, test.want, got)
                }
        }
 }
index 3fb15a6e93f56e294f169a88c99da5b0dc6b7b1a..4de7ccde9127c1247b3067692a1ea8236387c13c 100644 (file)
@@ -12,10 +12,10 @@ import (
 // Strings of content from a trusted source.
 type (
        // CSS encapsulates known safe content that matches any of:
-       // (1) The CSS3 stylesheet production, such as `p { color: purple }`.
-       // (2) The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
-       // (3) CSS3 declaration productions, such as `color: red; margin: 2px`.
-       // (4) The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
+       //   1. The CSS3 stylesheet production, such as `p { color: purple }`.
+       //   2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
+       //   3. CSS3 declaration productions, such as `color: red; margin: 2px`.
+       //   4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
        // See http://www.w3.org/TR/css3-syntax/#style
        CSS string
 
@@ -41,8 +41,8 @@ type (
        // JSStr encapsulates a sequence of characters meant to be embedded
        // between quotes in a JavaScript expression.
        // The string must match a series of StringCharacters:
-       // StringCharacter :: SourceCharacter but not `\` or LineTerminator
-       //                  | EscapeSequence
+       //   StringCharacter :: SourceCharacter but not `\` or LineTerminator
+       //                    | EscapeSequence
        // Note that LineContinuations are not allowed.
        // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
        JSStr string
index 0324c9c0ee325115a08f85087de2b2d74d1e18eb..fc0e3826442c05b0809ed9ba2e4d84494d981a1d 100644 (file)
@@ -13,9 +13,9 @@ Introduction
 This package wraps package template so you can use the standard template API
 to parse and execute templates.
 
-    set, err := new(template.Set).Parse(...)
-    // Error checking elided
-    err = set.Execute(out, "Foo", data)
+  set, err := new(template.Set).Parse(...)
+  // Error checking elided
+  err = set.Execute(out, "Foo", data)
 
 If successful, set will now be injection-safe. Otherwise, err is an error
 defined in the docs for ErrorCode.
@@ -29,25 +29,25 @@ trusted, while Execute's data parameter is not. More details are provided below.
 
 Example
 
-    import "template"
-    ...
-    t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
-    err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>")
+  import "text/template"
+  ...
+  t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+  err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>")
 
 produces
 
-    Hello, <script>alert('you have been pwned')</script>!
+  Hello, <script>alert('you have been pwned')</script>!
 
 but with contextual autoescaping,
 
-    import "html/template"
-    ...
-    t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
-    err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>")
+  import "html/template"
+  ...
+  t, err := (&template.Set{}).Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+  err = t.Execute(out, "T", "<script>alert('you have been pwned')</script>")
 
 produces safe, escaped HTML output
 
-    Hello, &lt;script&gt;alert('you have been pwned')&lt;/script&gt;!
+  Hello, &lt;script&gt;alert('you have been pwned')&lt;/script&gt;!
 
 
 Contexts
@@ -80,36 +80,36 @@ Contexts
 Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows
 how {{.}} appears when used in the context to the left.
 
-Context                          {{.}} After
-{{.}}                            O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
-<a title='{{.}}'>                O&#39;Reilly: How are you?
-<a href="/{{.}}">                O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
-<a href="?q={{.}}">              O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
-<a onx='f("{{.}}")'>             O\x27Reilly: How are \x3ci\x3eyou...?
-<a onx='f({{.}})'>               "O\x27Reilly: How are \x3ci\x3eyou...?"
-<a onx='pattern = /{{.}}/;'>     O\x27Reilly: How are \x3ci\x3eyou...\x3f
+  Context                          {{.}} After
+  {{.}}                            O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
+  <a title='{{.}}'>                O&#39;Reilly: How are you?
+  <a href="/{{.}}">                O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
+  <a href="?q={{.}}">              O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
+  <a onx='f("{{.}}")'>             O\x27Reilly: How are \x3ci\x3eyou...?
+  <a onx='f({{.}})'>               "O\x27Reilly: How are \x3ci\x3eyou...?"
+  <a onx='pattern = /{{.}}/;'>     O\x27Reilly: How are \x3ci\x3eyou...\x3f
 
 If used in an unsafe context, then the value might be filtered out:
 
-Context                          {{.}} After
-<a href="{{.}}">                 #ZgotmplZ
+  Context                          {{.}} After
+  <a href="{{.}}">                 #ZgotmplZ
 
 since "O'Reilly:" is not an allowed protocol like "http:".
 
 
 If {{.}} is the innocuous word, `left`, then it can appear more widely,
 
-Context                              {{.}} After
-{{.}}                                left
-<a title='{{.}}'>                    left
-<a href='{{.}}'>                     left
-<a href='/{{.}}'>                    left
-<a href='?dir={{.}}'>                left
-<a style="border-{{.}}: 4px">        left
-<a style="align: {{.}}">             left
-<a style="background: '{{.}}'>       left
-<a style="background: url('{{.}}')>  left
-<style>p.{{.}} {color:red}</style>   left
+  Context                              {{.}} After
+  {{.}}                                left
+  <a title='{{.}}'>                    left
+  <a href='{{.}}'>                     left
+  <a href='/{{.}}'>                    left
+  <a href='?dir={{.}}'>                left
+  <a style="border-{{.}}: 4px">        left
+  <a style="align: {{.}}">             left
+  <a style="background: '{{.}}'>       left
+  <a style="background: url('{{.}}')>  left
+  <style>p.{{.}} {color:red}</style>   left
 
 Non-string values can be used in JavaScript contexts.
 If {{.}} is
index 8ac07eae24c3fe029791e18577a14c201c1686b4..4a7a9354c93f5863e00003606d2b5b5f31795ca0 100644 (file)
@@ -12,24 +12,15 @@ import (
        "text/template/parse"
 )
 
-// escape rewrites each action in the template to guarantee that the output is
-// properly escaped.
-func escape(t *template.Template) error {
-       var s template.Set
-       s.Add(t)
-       return escapeSet(&s, t.Name())
-       // TODO: if s contains cloned dependencies due to self-recursion
-       // cross-context, error out.
-}
-
-// escapeSet rewrites the template set to guarantee that the output of any of
-// the named templates is properly escaped.
-// Names should include the names of all templates that might be Executed but
-// need not include helper templates.
-// If no error is returned, then the named templates have been modified. 
-// Otherwise the named templates have been rendered unusable.
-func escapeSet(s *template.Set, names ...string) error {
-       e := newEscaper(s)
+// escapeTemplates rewrites the named templates, which must be
+// associated with t, to guarantee that the output of any of the named
+// templates is properly escaped.  Names should include the names of
+// all templates that might be Executed but need not include helper
+// templates.  If no error is returned, then the named templates have
+// been modified.  Otherwise the named templates have been rendered
+// unusable.
+func escapeTemplates(tmpl *Template, names ...string) error {
+       e := newEscaper(tmpl)
        for _, name := range names {
                c, _ := e.escapeTree(context{}, name, 0)
                var err error
@@ -41,12 +32,13 @@ func escapeSet(s *template.Set, names ...string) error {
                if err != nil {
                        // Prevent execution of unsafe templates.
                        for _, name := range names {
-                               if t := s.Template(name); t != nil {
-                                       t.Tree = nil
+                               if t := tmpl.set[name]; t != nil {
+                                       t.text.Tree = nil
                                }
                        }
                        return err
                }
+               tmpl.escaped = true
        }
        e.commit()
        return nil
@@ -83,8 +75,7 @@ var equivEscapers = map[string]string{
 // escaper collects type inferences about templates and changes needed to make
 // templates injection safe.
 type escaper struct {
-       // set is the template set being escaped.
-       set *template.Set
+       tmpl *Template
        // output[templateName] is the output context for a templateName that
        // has been mangled to include its input context.
        output map[string]context
@@ -102,9 +93,9 @@ type escaper struct {
 }
 
 // newEscaper creates a blank escaper for the given set.
-func newEscaper(s *template.Set) *escaper {
+func newEscaper(t *Template) *escaper {
        return &escaper{
-               s,
+               t,
                map[string]context{},
                map[string]*template.Template{},
                map[string]bool{},
@@ -442,7 +433,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
 // It returns the best guess at an output context, and the result of the filter
 // which is the same as whether e was updated.
 func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
-       e1 := newEscaper(e.set)
+       e1 := newEscaper(e.tmpl)
        // Make type inferences available to f.
        for k, v := range e.output {
                e1.output[k] = v
@@ -501,7 +492,7 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string)
                }, dname
        }
        if dname != name {
-               // Use any template derived during an earlier call to escapeSet
+               // Use any template derived during an earlier call to escapeTemplate
                // with different top level templates, or clone if necessary.
                dt := e.template(dname)
                if dt == nil {
@@ -529,7 +520,7 @@ func (e *escaper) computeOutCtx(c context, t *template.Template) context {
        if !ok && c1.state != stateError {
                return context{
                        state: stateError,
-                       // TODO: Find the first node with a line in t.Tree.Root
+                       // TODO: Find the first node with a line in t.text.Tree.Root
                        err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()),
                }
        }
@@ -729,7 +720,9 @@ func (e *escaper) commit() {
                e.template(name).Funcs(funcMap)
        }
        for _, t := range e.derived {
-               e.set.Add(t)
+               if _, err := e.tmpl.text.AddParseTree(t.Name(), t.Tree); err != nil {
+                       panic("error adding derived template")
+               }
        }
        for n, s := range e.actionNodeEdits {
                ensurePipelineContains(n.Pipe, s)
@@ -744,7 +737,7 @@ func (e *escaper) commit() {
 
 // template returns the named template given a mangled template name.
 func (e *escaper) template(name string) *template.Template {
-       t := e.set.Template(name)
+       t := e.tmpl.text.Lookup(name)
        if t == nil {
                t = e.derived[name]
        }
index 4af583097bd55fb165e69e3b0fc8af3019cc9b10..b4daca7d6bdc3c9ec47d7d4ebd98fe07d8bfdb21 100644 (file)
@@ -806,13 +806,15 @@ func TestEscapeSet(t *testing.T) {
                for name, body := range test.inputs {
                        source += fmt.Sprintf("{{define %q}}%s{{end}} ", name, body)
                }
-               s := &Set{}
-               s.Funcs(fns)
-               s.Parse(source)
+               tmpl, err := New("root").Funcs(fns).Parse(source)
+               if err != nil {
+                       t.Errorf("error parsing %q: %v", source, err)
+                       continue
+               }
                var b bytes.Buffer
 
-               if err := s.Execute(&b, "main", data); err != nil {
-                       t.Errorf("%q executing %v", err.Error(), s.Template("main"))
+               if err := tmpl.ExecuteTemplate(&b, "main", data); err != nil {
+                       t.Errorf("%q executing %v", err.Error(), tmpl.Lookup("main"))
                        continue
                }
                if got := b.String(); test.want != got {
@@ -929,13 +931,13 @@ func TestErrors(t *testing.T) {
                        "z:1: no such template foo",
                },
                {
-                       `{{define "z"}}<div{{template "y"}}>{{end}}` +
+                       `<div{{template "y"}}>` +
                                // Illegal starting in stateTag but not in stateText.
                                `{{define "y"}} foo<b{{end}}`,
                        `"<" in attribute name: " foo<b"`,
                },
                {
-                       `{{define "z"}}<script>reverseList = [{{template "t"}}]</script>{{end}}` +
+                       `<script>reverseList = [{{template "t"}}]</script>` +
                                // Missing " after recursive call.
                                `{{define "t"}}{{if .Tail}}{{template "t" .Tail}}{{end}}{{.Head}}",{{end}}`,
                        `: cannot compute output context for template t$htmltemplate_stateJS_elementScript`,
@@ -967,21 +969,13 @@ func TestErrors(t *testing.T) {
        }
 
        for _, test := range tests {
-               var err error
                buf := new(bytes.Buffer)
-               if strings.HasPrefix(test.input, "{{define") {
-                       var s *Set
-                       s, err = (&Set{}).Parse(test.input)
-                       if err == nil {
-                               err = s.Execute(buf, "z", nil)
-                       }
-               } else {
-                       var t *Template
-                       t, err = New("z").Parse(test.input)
-                       if err == nil {
-                               err = t.Execute(buf, nil)
-                       }
+               tmpl, err := New("z").Parse(test.input)
+               if err != nil {
+                       t.Errorf("input=%q: unexpected parse error %s\n", test.input, err)
+                       continue
                }
+               err = tmpl.Execute(buf, nil)
                var got string
                if err != nil {
                        got = err.Error()
@@ -1569,11 +1563,11 @@ func TestEscapeErrorsNotIgnorable(t *testing.T) {
 
 func TestEscapeSetErrorsNotIgnorable(t *testing.T) {
        var b bytes.Buffer
-       s, err := (&Set{}).Parse(`{{define "t"}}<a{{end}}`)
+       tmpl, err := New("root").Parse(`{{define "t"}}<a{{end}}`)
        if err != nil {
                t.Errorf("failed to parse set: %q", err)
        }
-       err = s.Execute(&b, "t", nil)
+       err = tmpl.ExecuteTemplate(&b, "t", nil)
        if err == nil {
                t.Errorf("Expected error")
        } else if b.Len() != 0 {
index 47334299384bf9c7eedcf60b1c1a46486398bf91..f05ca190f739829cea60d5eb547d77314d939f85 100644 (file)
@@ -7,233 +7,257 @@ package template
 import (
        "fmt"
        "io"
+       "io/ioutil"
        "path/filepath"
+       "sync"
        "text/template"
+       "text/template/parse"
 )
 
-// Set is a specialized template.Set that produces a safe HTML document
-// fragment.
-type Set struct {
-       escaped map[string]bool
-       template.Set
-}
-
 // Template is a specialized template.Template that produces a safe HTML
 // document fragment.
 type Template struct {
        escaped bool
-       *template.Template
+       // We could embed the text/template field, but it's safer not to because
+       // we need to keep our version of the name space and the underlying
+       // template's in sync.
+       text       *template.Template
+       *nameSpace // common to all associated templates
 }
 
-// Execute applies the named template to the specified data object, writing
-// the output to wr.
-func (s *Set) Execute(wr io.Writer, name string, data interface{}) error {
-       if !s.escaped[name] {
-               if err := escapeSet(&s.Set, name); err != nil {
-                       return err
-               }
-               if s.escaped == nil {
-                       s.escaped = make(map[string]bool)
+// nameSpace is the data structure shared by all templates in an association.
+type nameSpace struct {
+       mu  sync.Mutex
+       set map[string]*Template
+}
+
+// Execute applies a parsed template to the specified data object,
+// writing the output to wr.
+func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
+       t.nameSpace.mu.Lock()
+       if !t.escaped {
+               if err = escapeTemplates(t, t.Name()); err != nil {
+                       t.escaped = true
                }
-               s.escaped[name] = true
        }
-       return s.Set.Execute(wr, name, data)
+       t.nameSpace.mu.Unlock()
+       if err != nil {
+               return
+       }
+       return t.text.Execute(wr, data)
 }
 
-// Parse parses a string into a set of named templates.  Parse may be called
-// multiple times for a given set, adding the templates defined in the string
-// to the set.  If a template is redefined, the element in the set is
-// overwritten with the new definition.
-func (set *Set) Parse(src string) (*Set, error) {
-       set.escaped = nil
-       s, err := set.Set.Parse(src)
-       if err != nil {
-               return nil, err
+// ExecuteTemplate applies the template associated with t that has the given name
+// to the specified data object and writes the output to wr.
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) {
+       t.nameSpace.mu.Lock()
+       tmpl := t.set[name]
+       if tmpl == nil {
+               t.nameSpace.mu.Unlock()
+               return fmt.Errorf("template: no template %q associated with template %q", name, t.Name())
        }
-       if s != &(set.Set) {
-               panic("allocated new set")
+       if !tmpl.escaped {
+               err = escapeTemplates(tmpl, name)
+       }
+       t.nameSpace.mu.Unlock()
+       if err != nil {
+               return
        }
-       return set, nil
+       return tmpl.text.ExecuteTemplate(wr, name, data)
 }
 
-// Parse parses the template definition string to construct an internal
-// representation of the template for execution.
-func (tmpl *Template) Parse(src string) (*Template, error) {
-       tmpl.escaped = false
-       t, err := tmpl.Template.Parse(src)
+// Parse parses a string into a template. Nested template definitions
+// will be associated with the top-level template t. Parse may be
+// called multiple times to parse definitions of templates to associate
+// with t. It is an error if a resulting template is non-empty (contains
+// content other than template definitions) and would replace a
+// non-empty template with the same name.  (In multiple calls to Parse
+// with the same receiver template, only one call can contain text
+// other than space, comments, and template definitions.)
+func (t *Template) Parse(src string) (*Template, error) {
+       t.nameSpace.mu.Lock()
+       t.escaped = false
+       t.nameSpace.mu.Unlock()
+       ret, err := t.text.Parse(src)
        if err != nil {
                return nil, err
        }
-       tmpl.Template = t
-       return tmpl, nil
-}
-
-// Execute applies a parsed template to the specified data object,
-// writing the output to wr.
-func (t *Template) Execute(wr io.Writer, data interface{}) error {
-       if !t.escaped {
-               if err := escape(t.Template); err != nil {
-                       return err
+       // In general, all the named templates might have changed underfoot.
+       // Regardless, some new ones may have been defined.
+       // The template.Template set has been updated; update ours.
+       t.nameSpace.mu.Lock()
+       defer t.nameSpace.mu.Unlock()
+       for _, v := range ret.Templates() {
+               name := v.Name()
+               tmpl := t.set[name]
+               if tmpl == nil {
+                       tmpl = t.new(name)
                }
-               t.escaped = true
+               tmpl.escaped = false
+               tmpl.text = v
        }
-       return t.Template.Execute(wr, data)
+       return t, nil
+}
+
+// AddParseTree is unimplemented.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
+       return fmt.Errorf("html/template: AddParseTree unimplemented")
+}
+
+// Clone is unimplemented.
+func (t *Template) Clone(name string) error {
+       return fmt.Errorf("html/template: Add unimplemented")
 }
 
 // New allocates a new HTML template with the given name.
 func New(name string) *Template {
-       return &Template{false, template.New(name)}
+       tmpl := &Template{
+               false,
+               template.New(name),
+               &nameSpace{
+                       set: make(map[string]*Template),
+               },
+       }
+       tmpl.set[name] = tmpl
+       return tmpl
 }
 
-// Must panics if err is non-nil in the same way as template.Must.
-func Must(t *Template, err error) *Template {
-       t.Template = template.Must(t.Template, err)
-       return t
+// New allocates a new HTML template associated with the given one
+// and with the same delimiters. The association, which is transitive,
+// allows one template to invoke another with a {{template}} action.
+func (t *Template) New(name string) *Template {
+       t.nameSpace.mu.Lock()
+       defer t.nameSpace.mu.Unlock()
+       return t.new(name)
 }
 
-// ParseFile creates a new Template and parses the template definition from
-// the named file.  The template name is the base name of the file.
-func ParseFile(filename string) (*Template, error) {
-       t, err := template.ParseFile(filename)
-       if err != nil {
-               return nil, err
+// new is the implementation of New, without the lock.
+func (t *Template) new(name string) *Template {
+       tmpl := &Template{
+               false,
+               t.text.New(name),
+               t.nameSpace,
        }
-       return &Template{false, t}, nil
+       tmpl.set[name] = tmpl
+       return tmpl
 }
 
-// ParseFile reads the template definition from a file and parses it to
-// construct an internal representation of the template for execution.
-// The returned template will be nil if an error occurs.
-func (tmpl *Template) ParseFile(filename string) (*Template, error) {
-       t, err := tmpl.Template.ParseFile(filename)
-       if err != nil {
-               return nil, err
-       }
-       tmpl.Template = t
-       return tmpl, nil
+// Name returns the name of the template.
+func (t *Template) Name() string {
+       return t.text.Name()
 }
 
-// SetMust panics if the error is non-nil just like template.SetMust.
-func SetMust(s *Set, err error) *Set {
-       if err != nil {
-               template.SetMust(&(s.Set), err)
-       }
-       return s
+// Funcs adds the elements of the argument map to the template's function map.
+// It panics if a value in the map is not a function with appropriate return
+// type. However, it is legal to overwrite elements of the map. The return
+// value is the template, so calls can be chained.
+func (t *Template) Funcs(funcMap template.FuncMap) *Template {
+       t.text.Funcs(funcMap)
+       return t
 }
 
-// ParseFiles parses the named files into a set of named templates.
-// Each file must be parseable by itself.
-// If an error occurs, parsing stops and the returned set is nil.
-func (set *Set) ParseFiles(filenames ...string) (*Set, error) {
-       s, err := set.Set.ParseFiles(filenames...)
-       if err != nil {
-               return nil, err
-       }
-       if s != &(set.Set) {
-               panic("allocated new set")
-       }
-       return set, nil
+// Delims sets the action delimiters to the specified strings, to be used in
+// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
+// definitions will inherit the settings. An empty delimiter stands for the
+// corresponding default: {{ or }}.
+// The return value is the template, so calls can be chained.
+func (t *Template) Delims(left, right string) *Template {
+       t.text.Delims(left, right)
+       return t
 }
 
-// ParseSetFiles creates a new Set and parses the set definition from the
-// named files. Each file must be individually parseable.
-func ParseSetFiles(filenames ...string) (*Set, error) {
-       set := new(Set)
-       s, err := set.Set.ParseFiles(filenames...)
-       if err != nil {
-               return nil, err
-       }
-       if s != &(set.Set) {
-               panic("allocated new set")
-       }
-       return set, nil
+// Lookup returns the template with the given name that is associated with t,
+// or nil if there is no such template.
+func (t *Template) Lookup(name string) *Template {
+       t.nameSpace.mu.Lock()
+       defer t.nameSpace.mu.Unlock()
+       return t.set[name]
 }
 
-// ParseGlob parses the set definition from the files identified by the
-// pattern. The pattern is processed by filepath.Glob and must match at
-// least one file.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseGlob(pattern string) (*Set, error) {
-       filenames, err := filepath.Glob(pattern)
-       if err != nil {
-               return nil, err
-       }
-       if len(filenames) == 0 {
-               return nil, fmt.Errorf("pattern matches no files: %#q", pattern)
-       }
-       return s.ParseFiles(filenames...)
+// Must panics if err is non-nil in the same way as template.Must.
+func Must(t *Template, err error) *Template {
+       t.text = template.Must(t.text, err)
+       return t
 }
 
-// ParseSetGlob creates a new Set and parses the set definition from the
-// files identified by the pattern. The pattern is processed by filepath.Glob
-// and must match at least one file.
-func ParseSetGlob(pattern string) (*Set, error) {
-       set, err := new(Set).ParseGlob(pattern)
-       if err != nil {
-               return nil, err
-       }
-       return set, nil
+// ParseFiles creates a new Template and parses the template definitions from
+// the named files. The returned template's name will have the (base) name and
+// (parsed) contents of the first file. There must be at least one file.
+// If an error occurs, parsing stops and the returned *Template is nil.
+func ParseFiles(filenames ...string) (*Template, error) {
+       return parseFiles(nil, filenames...)
 }
 
-// Functions and methods to parse stand-alone template files into a set.
+// ParseFiles parses the named files and associates the resulting templates with
+// t. If an error occurs, parsing stops and the returned template is nil;
+// otherwise it is t. There must be at least one file.
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
+       return parseFiles(t, filenames...)
+}
 
-// ParseTemplateFiles parses the named template files and adds them to the set
-// in the same way as template.ParseTemplateFiles but ensures that templates
-// with upper-case names are contextually-autoescaped.
-func (set *Set) ParseTemplateFiles(filenames ...string) (*Set, error) {
-       s, err := set.Set.ParseTemplateFiles(filenames...)
-       if err != nil {
-               return nil, err
+// parseFiles is the helper for the method and function. If the argument
+// template is nil, it is created from the first file.
+func parseFiles(t *Template, filenames ...string) (*Template, error) {
+       if len(filenames) == 0 {
+               // Not really a problem, but be consistent.
+               return nil, fmt.Errorf("template: no files named in call to ParseFiles")
        }
-       if s != &(set.Set) {
-               panic("new set allocated")
+       for _, filename := range filenames {
+               b, err := ioutil.ReadFile(filename)
+               if err != nil {
+                       return nil, err
+               }
+               s := string(b)
+               name := filepath.Base(filename)
+               // First template becomes return value if not already defined,
+               // and we use that one for subsequent New calls to associate
+               // all the templates together. Also, if this file has the same name
+               // as t, this file becomes the contents of t, so
+               //  t, err := New(name).Funcs(xxx).ParseFiles(name)
+               // works. Otherwise we create a new template associated with t.
+               var tmpl *Template
+               if t == nil {
+                       t = New(name)
+               }
+               if name == t.Name() {
+                       tmpl = t
+               } else {
+                       tmpl = t.New(name)
+               }
+               _, err = tmpl.Parse(s)
+               if err != nil {
+                       return nil, err
+               }
        }
-       return set, nil
-}
-
-// ParseTemplateGlob parses the template files matched by the
-// patern and adds them to the set. Each template will be named
-// the base name of its file.
-// Unlike with ParseGlob, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateGlob is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) {
+       return t, nil
+}
+
+// ParseGlob creates a new Template and parses the template definitions from the
+// files identified by the pattern, which must match at least one file. The
+// returned template will have the (base) name and (parsed) contents of the
+// first file matched by the pattern. ParseGlob is equivalent to calling
+// ParseFiles with the list of files matched by the pattern.
+func ParseGlob(pattern string) (*Template, error) {
+       return parseGlob(nil, pattern)
+}
+
+// ParseGlob parses the template definitions in the files identified by the
+// pattern and associates the resulting templates with t. The pattern is
+// processed by filepath.Glob and must match at least one file. ParseGlob is
+// equivalent to calling t.ParseFiles with the list of files matched by the
+// pattern.
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
+       return parseGlob(t, pattern)
+}
+
+// parseGlob is the implementation of the function and method ParseGlob.
+func parseGlob(t *Template, pattern string) (*Template, error) {
        filenames, err := filepath.Glob(pattern)
        if err != nil {
                return nil, err
        }
-       return s.ParseTemplateFiles(filenames...)
-}
-
-// ParseTemplateFiles creates a set by parsing the named files,
-// each of which defines a single template. Each template will be
-// named the base name of its file.
-// Unlike with ParseFiles, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateFiles is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself. Parsing stops if an error is
-// encountered.
-func ParseTemplateFiles(filenames ...string) (*Set, error) {
-       return new(Set).ParseTemplateFiles(filenames...)
-}
-
-// ParseTemplateGlob creates a set by parsing the files matched
-// by the pattern, each of which defines a single template. The pattern
-// is processed by filepath.Glob and must match at least one file. Each
-// template will be named the base name of its file.
-// Unlike with ParseGlob, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateGlob is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself. Parsing stops if an error is
-// encountered.
-func ParseTemplateGlob(pattern string) (*Set, error) {
-       return new(Set).ParseTemplateGlob(pattern)
+       if len(filenames) == 0 {
+               return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
+       }
+       return parseFiles(t, filenames...)
 }
index 9400873e6b8e6b81639d8358700f63910032bda6..69af96840c272b1c8bfa9313b174315d24f1d9ee 100644 (file)
@@ -289,7 +289,11 @@ func (z *Tokenizer) readComment() {
        for dashCount := 2; ; {
                c := z.readByte()
                if z.err != nil {
-                       z.data.end = z.raw.end
+                       // Ignore up to two dashes at EOF.
+                       if dashCount > 2 {
+                               dashCount = 2
+                       }
+                       z.data.end = z.raw.end - dashCount
                        return
                }
                switch c {
@@ -375,6 +379,28 @@ func (z *Tokenizer) readMarkupDeclaration() TokenType {
        return DoctypeToken
 }
 
+// startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end]
+// case-insensitively matches any element of ss.
+func (z *Tokenizer) startTagIn(ss ...string) bool {
+loop:
+       for _, s := range ss {
+               if z.data.end-z.data.start != len(s) {
+                       continue loop
+               }
+               for i := 0; i < len(s); i++ {
+                       c := z.buf[z.data.start+i]
+                       if 'A' <= c && c <= 'Z' {
+                               c += 'a' - 'A'
+                       }
+                       if c != s[i] {
+                               continue loop
+                       }
+               }
+               return true
+       }
+       return false
+}
+
 // readStartTag reads the next start tag token. The opening "<a" has already
 // been consumed, where 'a' means anything in [A-Za-z].
 func (z *Tokenizer) readStartTag() TokenType {
@@ -401,17 +427,27 @@ func (z *Tokenizer) readStartTag() TokenType {
                        break
                }
        }
-       // Any "<noembed>", "<noframes>", "<noscript>", "<plaintext", "<script>", "<style>",
-       // "<textarea>" or "<title>" tag flags the tokenizer's next token as raw.
-       // The tag name lengths of these special cases ranges in [5, 9].
-       if x := z.data.end - z.data.start; 5 <= x && x <= 9 {
-               switch z.buf[z.data.start] {
-               case 'n', 'p', 's', 't', 'N', 'P', 'S', 'T':
-                       switch s := strings.ToLower(string(z.buf[z.data.start:z.data.end])); s {
-                       case "noembed", "noframes", "noscript", "plaintext", "script", "style", "textarea", "title":
-                               z.rawTag = s
-                       }
-               }
+       // Several tags flag the tokenizer's next token as raw.
+       c, raw := z.buf[z.data.start], false
+       if 'A' <= c && c <= 'Z' {
+               c += 'a' - 'A'
+       }
+       switch c {
+       case 'i':
+               raw = z.startTagIn("iframe")
+       case 'n':
+               raw = z.startTagIn("noembed", "noframes", "noscript")
+       case 'p':
+               raw = z.startTagIn("plaintext")
+       case 's':
+               raw = z.startTagIn("script", "style")
+       case 't':
+               raw = z.startTagIn("textarea", "title")
+       case 'x':
+               raw = z.startTagIn("xmp")
+       }
+       if raw {
+               z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end]))
        }
        // Look for a self-closing token like "<br/>".
        if z.err == nil && z.buf[z.raw.end-2] == '/' {
index 61d4e67c06d7f178400672a65d539fda6e5d1b30..672d60c420940b558f85822013faa9b82d0e80ed 100644 (file)
@@ -325,6 +325,26 @@ var tokenTests = []tokenTest{
        },
        {
                "comment9",
+               "a<!--z-",
+               "a$<!--z-->",
+       },
+       {
+               "comment10",
+               "a<!--z--",
+               "a$<!--z-->",
+       },
+       {
+               "comment11",
+               "a<!--z---",
+               "a$<!--z--->",
+       },
+       {
+               "comment12",
+               "a<!--z----",
+               "a$<!--z---->",
+       },
+       {
+               "comment13",
                "a<!--x--!>z",
                "a$<!--x-->$z",
        },
index f6c8cd8a87391fc69db932b6991fa01109a6ced9..be7fa5f2bc8bc8da61afe778c4f2308251d998d5 100644 (file)
@@ -36,8 +36,8 @@ func ReadFile(filename string) ([]byte, error) {
        // read, so let's try it but be prepared for the answer to be wrong.
        fi, err := f.Stat()
        var n int64
-       if err == nil && fi.Size < 2e9 { // Don't preallocate a huge buffer, just in case.
-               n = fi.Size
+       if size := fi.Size(); err == nil && size < 2e9 { // Don't preallocate a huge buffer, just in case.
+               n = size
        }
        // As initial capacity for readAll, use n + a little extra in case Size is zero,
        // and to avoid another allocation after Read has filled the buffer.  The readAll
@@ -63,16 +63,16 @@ func WriteFile(filename string, data []byte, perm uint32) error {
        return err
 }
 
-// A fileInfoList implements sort.Interface.
-type fileInfoList []*os.FileInfo
+// byName implements sort.Interface.
+type byName []os.FileInfo
 
-func (f fileInfoList) Len() int           { return len(f) }
-func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name }
-func (f fileInfoList) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
+func (f byName) Len() int           { return len(f) }
+func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
+func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
 
 // ReadDir reads the directory named by dirname and returns
 // a list of sorted directory entries.
-func ReadDir(dirname string) ([]*os.FileInfo, error) {
+func ReadDir(dirname string) ([]os.FileInfo, error) {
        f, err := os.Open(dirname)
        if err != nil {
                return nil, err
@@ -82,12 +82,8 @@ func ReadDir(dirname string) ([]*os.FileInfo, error) {
        if err != nil {
                return nil, err
        }
-       fi := make(fileInfoList, len(list))
-       for i := range list {
-               fi[i] = &list[i]
-       }
-       sort.Sort(fi)
-       return fi, nil
+       sort.Sort(byName(list))
+       return list, nil
 }
 
 type nopCloser struct {
index 55e4b2c2bc8ab777023c8b85168a742e13be936c..89d6815ad503beaf2e25dd63b1248fc7e9518687 100644 (file)
@@ -15,8 +15,8 @@ func checkSize(t *testing.T, path string, size int64) {
        if err != nil {
                t.Fatalf("Stat %q (looking for size %d): %s", path, size, err)
        }
-       if dir.Size != size {
-               t.Errorf("Stat %q: size %d want %d", path, dir.Size, size)
+       if dir.Size() != size {
+               t.Errorf("Stat %q: size %d want %d", path, dir.Size(), size)
        }
 }
 
@@ -76,9 +76,9 @@ func TestReadDir(t *testing.T) {
        foundTestDir := false
        for _, dir := range list {
                switch {
-               case dir.IsRegular() && dir.Name == "ioutil_test.go":
+               case !dir.IsDir() && dir.Name() == "ioutil_test.go":
                        foundTest = true
-               case dir.IsDirectory() && dir.Name == "_test":
+               case dir.IsDir() && dir.Name() == "_test":
                        foundTestDir = true
                }
        }
index 71028e226773a40cd393f46b56fdd1aad2851c42..645eed6abb855489554cd833dd21b45b849b22d4 100644 (file)
@@ -18,7 +18,7 @@ import (
 var rand uint32
 
 func reseed() uint32 {
-       return uint32(time.Nanoseconds() + int64(os.Getpid()))
+       return uint32(time.Now().UnixNano() + int64(os.Getpid()))
 }
 
 func nextSuffix() string {
index 0de5cc312d02f24544dfd20d04a64778fc92288e..eb717f7bc21c7405bf1716aa100b06a21d9f22b1 100644 (file)
@@ -77,7 +77,7 @@ func TestMultiWriter(t *testing.T) {
                t.Errorf("unexpected error: %v", err)
        }
 
-       sha1hex := fmt.Sprintf("%x", sha1.Sum())
+       sha1hex := fmt.Sprintf("%x", sha1.Sum(nil))
        if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {
                t.Error("incorrect sha1 value")
        }
index b5368af53197b4844913997f86eda8fcfc1ade6b..a5d88fd9b349f4ff1267932d60bb5ffa3784a9da 100644 (file)
@@ -83,27 +83,28 @@ func itoa(buf *bytes.Buffer, i int, wid int) {
        }
 }
 
-func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int) {
+func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int) {
        buf.WriteString(l.prefix)
        if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
-               t := time.SecondsToLocalTime(ns / 1e9)
                if l.flag&Ldate != 0 {
-                       itoa(buf, int(t.Year), 4)
+                       year, month, day := t.Date()
+                       itoa(buf, year, 4)
                        buf.WriteByte('/')
-                       itoa(buf, int(t.Month), 2)
+                       itoa(buf, int(month), 2)
                        buf.WriteByte('/')
-                       itoa(buf, int(t.Day), 2)
+                       itoa(buf, day, 2)
                        buf.WriteByte(' ')
                }
                if l.flag&(Ltime|Lmicroseconds) != 0 {
-                       itoa(buf, int(t.Hour), 2)
+                       hour, min, sec := t.Clock()
+                       itoa(buf, hour, 2)
                        buf.WriteByte(':')
-                       itoa(buf, int(t.Minute), 2)
+                       itoa(buf, min, 2)
                        buf.WriteByte(':')
-                       itoa(buf, int(t.Second), 2)
+                       itoa(buf, sec, 2)
                        if l.flag&Lmicroseconds != 0 {
                                buf.WriteByte('.')
-                               itoa(buf, int(ns%1e9)/1e3, 6)
+                               itoa(buf, t.Nanosecond()/1e3, 6)
                        }
                        buf.WriteByte(' ')
                }
@@ -133,7 +134,7 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int
 // provided for generality, although at the moment on all pre-defined
 // paths it will be 2.
 func (l *Logger) Output(calldepth int, s string) error {
-       now := time.Nanoseconds() // get this early.
+       now := time.Now() // get this early.
        var file string
        var line int
        l.mu.Lock()
index eb3e4c72b32914654d17981b24a4bac4de5ca901..4c6297c6f3327005f6dbd5eed35760119caffcb5 100644 (file)
@@ -7,8 +7,7 @@ package math
 // Abs returns the absolute value of x.
 //
 // Special cases are:
-//     Abs(+Inf) = +Inf
-//     Abs(-Inf) = +Inf
+//     Abs(±Inf) = +Inf
 //     Abs(NaN) = NaN
 func Abs(x float64) float64 {
        switch {
index c1cad563c76f559cdd6a9d80747c7fb9c927f587..d6979463d65f70b03294ef863ab7b4d1316fef66 100644 (file)
@@ -33,8 +33,7 @@ package math
 // Asinh(x) calculates the inverse hyperbolic sine of x.
 //
 // Special cases are:
-//     Asinh(+Inf) = +Inf
-//     Asinh(-Inf) = -Inf
+//     Asinh(±Inf) = ±Inf
 //     Asinh(NaN) = NaN
 func Asinh(x float64) float64 {
        const (
index 1cd93b1052bd1b2e0561f89b3be3f182cc471f58..0950eeedbd2f3757dc324f030d77c6ae721b058d 100644 (file)
@@ -22,14 +22,14 @@ import (
 var calibrate = flag.Bool("calibrate", false, "run calibration test")
 
 // measure returns the time to run f
-func measure(f func()) int64 {
+func measure(f func()) time.Duration {
        const N = 100
-       start := time.Nanoseconds()
+       start := time.Now()
        for i := N; i > 0; i-- {
                f()
        }
-       stop := time.Nanoseconds()
-       return (stop - start) / N
+       stop := time.Now()
+       return stop.Sub(start) / N
 }
 
 func computeThresholds() {
@@ -46,7 +46,7 @@ func computeThresholds() {
        th1 := -1
        th2 := -1
 
-       var deltaOld int64
+       var deltaOld time.Duration
        for count := -1; count != 0; count-- {
                // determine Tk, the work load execution time using Karatsuba multiplication
                karatsubaThreshold = n // enable karatsuba
index 163c662b0bbdf81bdab8b0d2b2906beffce61614..aa7c19495494fff3778d2d5d326eb789ba8a9371 100644 (file)
@@ -1242,10 +1242,14 @@ func TestBitSet(t *testing.T) {
                x.SetString(test.x, 0)
                b := x.Bit(test.i)
                if b != test.b {
-
-                       t.Errorf("#%d want %v got %v", i, test.b, b)
+                       t.Errorf("#%d got %v want %v", i, b, test.b)
                }
        }
+       z := NewInt(1)
+       z.SetBit(NewInt(0), 2, 1)
+       if z.Cmp(NewInt(4)) != 0 {
+               t.Errorf("destination leaked into result; got %s want 4", z)
+       }
 }
 
 func BenchmarkBitset(b *testing.B) {
index eee8ee3f66cc6dadf4f0c1998aa0cb54ed601d0c..680445dc9a73a30d24f4020b8fcd57a99b877d3d 100644 (file)
@@ -21,7 +21,9 @@ package big
 import (
        "errors"
        "io"
+       "math"
        "math/rand"
+       "sync"
 )
 
 // An unsigned integer x of the form
@@ -719,17 +721,17 @@ func (x nat) string(charset string) string {
 
        // special cases
        switch {
-       case b < 2 || b > 256:
+       case b < 2 || MaxBase < b:
                panic("illegal base")
        case len(x) == 0:
                return string(charset[0])
        }
 
        // allocate buffer for conversion
-       i := x.bitLen()/log2(b) + 1 // +1: round up
+       i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most
        s := make([]byte, i)
 
-       // special case: power of two bases can avoid divisions completely
+       // convert power of two and non power of two bases separately
        if b == b&-b {
                // shift is base-b digit size in bits
                shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2
@@ -771,65 +773,209 @@ func (x nat) string(charset string) string {
                        w >>= shift
                        nbits -= shift
                }
+       } else {
+               // determine "big base" as in 10^19 for 19 decimal digits in a 64 bit Word
+               bb := Word(1) // big base is b**ndigits
+               ndigits := 0  // number of base b digits
+               for max := Word(_M / b); bb <= max; bb *= b {
+                       ndigits++ // maximize ndigits where bb = b**ndigits, bb <= _M
+               }
 
-               return string(s[i:])
-       }
+               // construct table of successive squares of bb*leafSize to use in subdivisions
+               table := divisors(len(x), b, ndigits, bb)
 
-       // general case: extract groups of digits by multiprecision division
+               // preserve x, create local copy for use in divisions
+               q := nat(nil).set(x)
 
-       // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits
-       bb := Word(1)
-       ndigits := 0
-       for max := Word(_M / b); bb <= max; bb *= b {
-               ndigits++
+               // convert q to string s in base b with index of MSD indicated by return value
+               i = q.convertWords(0, i, s, charset, b, ndigits, bb, table)
        }
 
-       // preserve x, create local copy for use in repeated divisions
-       q := nat(nil).set(x)
-       var r Word
+       return string(s[i:])
+}
+
+// Convert words of q to base b digits in s directly using iterated nat/Word divison to extract
+// low-order Words and indirectly by recursive subdivision and nat/nat division by tabulated 
+// divisors. 
+//
+// The direct method processes n Words by n divW() calls, each of which visits every Word in the 
+// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s. 
+// Indirect conversion divides q by its approximate square root, yielding two parts, each half 
+// the size of q. Using the direct method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s plus 
+// the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and is 
+// made better by splitting the subblocks recursively. Best is to split blocks until one more 
+// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the 
+// direct approach. This threshold is represented by leafSize. Benchmarking of leafSize in the 
+// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and 
+// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for 
+// specfic hardware.
+//
+// lo and hi index character array s. conversion starts with the LSD at hi and moves down toward
+// the MSD, which will be at s[0] or s[1]. lo == 0 signals span includes the most significant word.
+//
+func (q nat) convertWords(lo, hi int, s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) int {
+       // indirect conversion: split larger blocks to reduce quadratic expense of iterated nat/W division
+       if leafSize > 0 && len(q) > leafSize && table != nil {
+               var r nat
+               index := len(table) - 1
+               for len(q) > leafSize {
+                       // find divisor close to sqrt(q) if possible, but in any case < q
+                       maxLength := q.bitLen()     // ~= log2 q, or at of least largest possible q of this bit length
+                       minLength := maxLength >> 1 // ~= log2 sqrt(q)
+                       for index > 0 && table[index-1].nbits > minLength {
+                               index-- // desired
+                       }
+                       if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 {
+                               index--
+                               if index < 0 {
+                                       panic("internal inconsistency")
+                               }
+                       }
 
-       // convert
-       if b == 10 { // hard-coding for 10 here speeds this up by 1.25x
+                       // split q into the two digit number (q'*bbb + r) to form independent subblocks
+                       q, r = q.div(r, q, table[index].bbb)
+
+                       // convert subblocks and collect results in s[lo:partition] and s[partition:hi]
+                       partition := hi - table[index].ndigits
+                       r.convertWords(partition, hi, s, charset, b, ndigits, bb, table[0:index])
+                       hi = partition // i.e., q.convertWords(lo, partition, s, charset, b, ndigits, bb, table[0:index+1])
+               }
+       } // having split any large blocks now process the remaining small block
+
+       // direct conversion: process smaller blocks monolithically to avoid overhead of nat/nat division
+       var r Word
+       if b == 10 { // hard-coding for 10 here speeds this up by 1.25x (allows mod as mul vs div)
                for len(q) > 0 {
                        // extract least significant, base bb "digit"
-                       q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW
-                       if len(q) == 0 {
+                       q, r = q.divW(q, bb)
+                       if lo == 0 && len(q) == 0 {
                                // skip leading zeros in most-significant group of digits
                                for j := 0; j < ndigits && r != 0; j++ {
-                                       i--
-                                       s[i] = charset[r%10]
-                                       r /= 10
+                                       hi--
+                                       t := r / 10
+                                       s[hi] = charset[r-(t<<3+t<<1)] // 8*t + 2*t = 10*t; r - 10*int(r/10) = r mod 10
+                                       r = t
                                }
                        } else {
-                               for j := 0; j < ndigits; j++ {
-                                       i--
-                                       s[i] = charset[r%10]
-                                       r /= 10
+                               for j := 0; j < ndigits && hi > lo; j++ {
+                                       hi--
+                                       t := r / 10
+                                       s[hi] = charset[r-(t<<3+t<<1)] // 8*t + 2*t = 10*t; r - 10*int(r/10) = r mod 10
+                                       r = t
                                }
                        }
                }
        } else {
                for len(q) > 0 {
                        // extract least significant group of digits
-                       q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW
-                       if len(q) == 0 {
+                       q, r = q.divW(q, bb)
+                       if lo == 0 && len(q) == 0 {
                                // skip leading zeros in most-significant group of digits
                                for j := 0; j < ndigits && r != 0; j++ {
-                                       i--
-                                       s[i] = charset[r%b]
-                                       r /= b
+                                       hi--
+                                       s[hi] = charset[r%b]
+                                       r = r / b
                                }
                        } else {
-                               for j := 0; j < ndigits; j++ {
-                                       i--
-                                       s[i] = charset[r%b]
-                                       r /= b
+                               for j := 0; j < ndigits && hi > lo; j++ {
+                                       hi--
+                                       s[hi] = charset[r%b]
+                                       r = r / b
                                }
                        }
                }
        }
 
-       return string(s[i:])
+       // prepend high-order zeroes when q has been normalized to a short number of Words.
+       // however, do not prepend zeroes when converting the most dignificant digits.
+       if lo != 0 { // if not MSD
+               zero := charset[0]
+               for hi > lo { // while need more leading zeroes
+                       hi--
+                       s[hi] = zero
+               }
+       }
+
+       // return index of most significant output digit in s[] (stored in lowest index)
+       return hi
+}
+
+// Split blocks greater than leafSize Words (or set to 0 to disable indirect conversion)
+// Benchmark and configure leafSize using: gotest -test.bench="Leaf"
+//   8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
+//   8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
+var leafSize int = 8 // number of Word-size binary values treat as a monolithic block
+
+type divisor struct {
+       bbb     nat // divisor
+       nbits   int // bit length of divisor (discounting leading zeroes) ~= log2(bbb)
+       ndigits int // digit length of divisor in terms of output base digits
+}
+
+const maxCache = 64               // maximum number of divisors in a single table
+var cacheBase10 [maxCache]divisor // cached divisors for base 10
+var cacheLock sync.Mutex          // defense against concurrent table extensions
+
+// construct table of powers of bb*leafSize to use in subdivisions
+func divisors(m int, b Word, ndigits int, bb Word) []divisor {
+       // only build table when indirect conversion is enabled and x is large
+       if leafSize == 0 || m <= leafSize {
+               return nil
+       }
+
+       // determine k where (bb**leafSize)**(2**k) >= sqrt(x)
+       k := 1
+       for words := leafSize; words < m>>1 && k < maxCache; words <<= 1 {
+               k++
+       }
+
+       // create new table of divisors or extend and reuse existing table as appropriate
+       var cached bool
+       var table []divisor
+       switch b {
+       case 10:
+               table = cacheBase10[0:k] // reuse old table for this conversion
+               cached = true
+       default:
+               table = make([]divisor, k) // new table for this conversion
+       }
+
+       // extend table
+       if table[k-1].ndigits == 0 {
+               if cached {
+                       cacheLock.Lock() // begin critical section
+               }
+
+               var i int
+               var larger nat
+               for i < k && table[i].ndigits != 0 { // skip existing entries
+                       i++
+               }
+               for ; i < k; i++ { // add new entries
+                       if i == 0 {
+                               table[i].bbb = nat(nil).expWW(bb, Word(leafSize))
+                               table[i].ndigits = ndigits * leafSize
+                       } else {
+                               table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
+                               table[i].ndigits = 2 * table[i-1].ndigits
+                       }
+
+                       // optimization: exploit aggregated extra bits in macro blocks
+                       larger = nat(nil).set(table[i].bbb)
+                       for mulAddVWW(larger, larger, b, 0) == 0 {
+                               table[i].bbb = table[i].bbb.set(larger)
+                               table[i].ndigits++
+                       }
+
+                       table[i].nbits = table[i].bbb.bitLen()
+               }
+
+               if cached {
+                       cacheLock.Unlock() // end critical section
+               }
+       }
+
+       return table
 }
 
 const deBruijn32 = 0x077CB531
@@ -919,9 +1065,11 @@ func (z nat) setBit(x nat, i uint, b uint) nat {
                return z.norm()
        case 1:
                if j >= n {
-                       n = j + 1
+                       z = z.make(j + 1)
+                       z[n:].clear()
+               } else {
+                       z = z.make(n)
                }
-               z = z.make(n)
                copy(z, x)
                z[j] |= m
                // no need to normalize
@@ -1140,7 +1288,12 @@ func (z nat) expNN(x, y, m nat) nat {
                }
        }
 
-       return z
+       return z.norm()
+}
+
+// calculate x**y for Word arguments y and y
+func (z nat) expWW(x, y Word) nat {
+       return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil)
 }
 
 // probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
index b208646f2f2839c08dc47aae6817a02fffe85988..e3c6552d9fb3de34cb8a75400dbb4da7066c0db2 100644 (file)
@@ -370,86 +370,34 @@ func BenchmarkScanPi(b *testing.B) {
        }
 }
 
-const (
-       // 314**271
-       // base  2: 2249 digits
-       // base  8:  751 digits
-       // base 10:  678 digits
-       // base 16:  563 digits
-       shortBase     = 314
-       shortExponent = 271
-
-       // 3141**2178
-       // base  2: 31577 digits
-       // base  8: 10527 digits
-       // base 10:  9507 digits
-       // base 16:  7895 digits
-       mediumBase     = 3141
-       mediumExponent = 2718
-
-       // 3141**2178
-       // base  2: 406078 digits
-       // base  8: 135360 digits
-       // base 10: 122243 digits
-       // base 16: 101521 digits
-       longBase     = 31415
-       longExponent = 27182
-)
-
-func BenchmarkScanShort2(b *testing.B) {
-       ScanHelper(b, 2, shortBase, shortExponent)
-}
-
-func BenchmarkScanShort8(b *testing.B) {
-       ScanHelper(b, 8, shortBase, shortExponent)
-}
-
-func BenchmarkScanSort10(b *testing.B) {
-       ScanHelper(b, 10, shortBase, shortExponent)
-}
-
-func BenchmarkScanShort16(b *testing.B) {
-       ScanHelper(b, 16, shortBase, shortExponent)
-}
-
-func BenchmarkScanMedium2(b *testing.B) {
-       ScanHelper(b, 2, mediumBase, mediumExponent)
-}
-
-func BenchmarkScanMedium8(b *testing.B) {
-       ScanHelper(b, 8, mediumBase, mediumExponent)
-}
-
-func BenchmarkScanMedium10(b *testing.B) {
-       ScanHelper(b, 10, mediumBase, mediumExponent)
-}
-
-func BenchmarkScanMedium16(b *testing.B) {
-       ScanHelper(b, 16, mediumBase, mediumExponent)
-}
-
-func BenchmarkScanLong2(b *testing.B) {
-       ScanHelper(b, 2, longBase, longExponent)
-}
-
-func BenchmarkScanLong8(b *testing.B) {
-       ScanHelper(b, 8, longBase, longExponent)
-}
-
-func BenchmarkScanLong10(b *testing.B) {
-       ScanHelper(b, 10, longBase, longExponent)
-}
-
-func BenchmarkScanLong16(b *testing.B) {
-       ScanHelper(b, 16, longBase, longExponent)
-}
-
-func ScanHelper(b *testing.B, base int, xv, yv Word) {
+func BenchmarkScan10Base2(b *testing.B)     { ScanHelper(b, 2, 10, 10) }
+func BenchmarkScan100Base2(b *testing.B)    { ScanHelper(b, 2, 10, 100) }
+func BenchmarkScan1000Base2(b *testing.B)   { ScanHelper(b, 2, 10, 1000) }
+func BenchmarkScan10000Base2(b *testing.B)  { ScanHelper(b, 2, 10, 10000) }
+func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) }
+
+func BenchmarkScan10Base8(b *testing.B)     { ScanHelper(b, 8, 10, 10) }
+func BenchmarkScan100Base8(b *testing.B)    { ScanHelper(b, 8, 10, 100) }
+func BenchmarkScan1000Base8(b *testing.B)   { ScanHelper(b, 8, 10, 1000) }
+func BenchmarkScan10000Base8(b *testing.B)  { ScanHelper(b, 8, 10, 10000) }
+func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) }
+
+func BenchmarkScan10Base10(b *testing.B)     { ScanHelper(b, 10, 10, 10) }
+func BenchmarkScan100Base10(b *testing.B)    { ScanHelper(b, 10, 10, 100) }
+func BenchmarkScan1000Base10(b *testing.B)   { ScanHelper(b, 10, 10, 1000) }
+func BenchmarkScan10000Base10(b *testing.B)  { ScanHelper(b, 10, 10, 10000) }
+func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) }
+
+func BenchmarkScan10Base16(b *testing.B)     { ScanHelper(b, 16, 10, 10) }
+func BenchmarkScan100Base16(b *testing.B)    { ScanHelper(b, 16, 10, 100) }
+func BenchmarkScan1000Base16(b *testing.B)   { ScanHelper(b, 16, 10, 1000) }
+func BenchmarkScan10000Base16(b *testing.B)  { ScanHelper(b, 16, 10, 10000) }
+func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) }
+
+func ScanHelper(b *testing.B, base int, x, y Word) {
        b.StopTimer()
-       var x, y, z nat
-       x = x.setWord(xv)
-       y = y.setWord(yv)
-       z = z.expNN(x, y, nil)
+       var z nat
+       z = z.expWW(x, y)
 
        var s string
        s = z.string(lowercaseDigits[0:base])
@@ -459,68 +407,112 @@ func ScanHelper(b *testing.B, base int, xv, yv Word) {
        b.StartTimer()
 
        for i := 0; i < b.N; i++ {
-               x.scan(strings.NewReader(s), base)
+               z.scan(strings.NewReader(s), base)
        }
 }
 
-func BenchmarkStringShort2(b *testing.B) {
-       StringHelper(b, 2, shortBase, shortExponent)
-}
+func BenchmarkString10Base2(b *testing.B)     { StringHelper(b, 2, 10, 10) }
+func BenchmarkString100Base2(b *testing.B)    { StringHelper(b, 2, 10, 100) }
+func BenchmarkString1000Base2(b *testing.B)   { StringHelper(b, 2, 10, 1000) }
+func BenchmarkString10000Base2(b *testing.B)  { StringHelper(b, 2, 10, 10000) }
+func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) }
 
-func BenchmarkStringShort8(b *testing.B) {
-       StringHelper(b, 8, shortBase, shortExponent)
-}
+func BenchmarkString10Base8(b *testing.B)     { StringHelper(b, 8, 10, 10) }
+func BenchmarkString100Base8(b *testing.B)    { StringHelper(b, 8, 10, 100) }
+func BenchmarkString1000Base8(b *testing.B)   { StringHelper(b, 8, 10, 1000) }
+func BenchmarkString10000Base8(b *testing.B)  { StringHelper(b, 8, 10, 10000) }
+func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) }
 
-func BenchmarkStringShort10(b *testing.B) {
-       StringHelper(b, 10, shortBase, shortExponent)
-}
-
-func BenchmarkStringShort16(b *testing.B) {
-       StringHelper(b, 16, shortBase, shortExponent)
-}
+func BenchmarkString10Base10(b *testing.B)     { StringHelper(b, 10, 10, 10) }
+func BenchmarkString100Base10(b *testing.B)    { StringHelper(b, 10, 10, 100) }
+func BenchmarkString1000Base10(b *testing.B)   { StringHelper(b, 10, 10, 1000) }
+func BenchmarkString10000Base10(b *testing.B)  { StringHelper(b, 10, 10, 10000) }
+func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) }
 
-func BenchmarkStringMedium2(b *testing.B) {
-       StringHelper(b, 2, mediumBase, mediumExponent)
-}
+func BenchmarkString10Base16(b *testing.B)     { StringHelper(b, 16, 10, 10) }
+func BenchmarkString100Base16(b *testing.B)    { StringHelper(b, 16, 10, 100) }
+func BenchmarkString1000Base16(b *testing.B)   { StringHelper(b, 16, 10, 1000) }
+func BenchmarkString10000Base16(b *testing.B)  { StringHelper(b, 16, 10, 10000) }
+func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) }
 
-func BenchmarkStringMedium8(b *testing.B) {
-       StringHelper(b, 8, mediumBase, mediumExponent)
-}
+func StringHelper(b *testing.B, base int, x, y Word) {
+       b.StopTimer()
+       var z nat
+       z = z.expWW(x, y)
+       z.string(lowercaseDigits[0:base]) // warm divisor cache
+       b.StartTimer()
 
-func BenchmarkStringMedium10(b *testing.B) {
-       StringHelper(b, 10, mediumBase, mediumExponent)
+       for i := 0; i < b.N; i++ {
+               _ = z.string(lowercaseDigits[0:base])
+       }
 }
 
-func BenchmarkStringMedium16(b *testing.B) {
-       StringHelper(b, 16, mediumBase, mediumExponent)
-}
+func BenchmarkLeafSize0(b *testing.B)  { LeafSizeHelper(b, 10, 0) } // test without splitting
+func BenchmarkLeafSize1(b *testing.B)  { LeafSizeHelper(b, 10, 1) }
+func BenchmarkLeafSize2(b *testing.B)  { LeafSizeHelper(b, 10, 2) }
+func BenchmarkLeafSize3(b *testing.B)  { LeafSizeHelper(b, 10, 3) }
+func BenchmarkLeafSize4(b *testing.B)  { LeafSizeHelper(b, 10, 4) }
+func BenchmarkLeafSize5(b *testing.B)  { LeafSizeHelper(b, 10, 5) }
+func BenchmarkLeafSize6(b *testing.B)  { LeafSizeHelper(b, 10, 6) }
+func BenchmarkLeafSize7(b *testing.B)  { LeafSizeHelper(b, 10, 7) }
+func BenchmarkLeafSize8(b *testing.B)  { LeafSizeHelper(b, 10, 8) }
+func BenchmarkLeafSize9(b *testing.B)  { LeafSizeHelper(b, 10, 9) }
+func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) }
+func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) }
+func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) }
+func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) }
+func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) }
+func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) }
+func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
+func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths 
+func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
+
+func LeafSizeHelper(b *testing.B, base Word, size int) {
+       b.StopTimer()
+       originalLeafSize := leafSize
+       resetTable(cacheBase10[:])
+       leafSize = size
+       b.StartTimer()
 
-func BenchmarkStringLong2(b *testing.B) {
-       StringHelper(b, 2, longBase, longExponent)
-}
+       for d := 1; d <= 10000; d *= 10 {
+               b.StopTimer()
+               var z nat
+               z = z.expWW(base, Word(d))            // build target number
+               _ = z.string(lowercaseDigits[0:base]) // warm divisor cache
+               b.StartTimer()
 
-func BenchmarkStringLong8(b *testing.B) {
-       StringHelper(b, 8, longBase, longExponent)
-}
+               for i := 0; i < b.N; i++ {
+                       _ = z.string(lowercaseDigits[0:base])
+               }
+       }
 
-func BenchmarkStringLong10(b *testing.B) {
-       StringHelper(b, 10, longBase, longExponent)
+       b.StopTimer()
+       resetTable(cacheBase10[:])
+       leafSize = originalLeafSize
+       b.StartTimer()
 }
 
-func BenchmarkStringLong16(b *testing.B) {
-       StringHelper(b, 16, longBase, longExponent)
+func resetTable(table []divisor) {
+       if table != nil && table[0].bbb != nil {
+               for i := 0; i < len(table); i++ {
+                       table[i].bbb = nil
+                       table[i].nbits = 0
+                       table[i].ndigits = 0
+               }
+       }
 }
 
-func StringHelper(b *testing.B, base int, xv, yv Word) {
-       b.StopTimer()
-       var x, y, z nat
-       x = x.setWord(xv)
-       y = y.setWord(yv)
-       z = z.expNN(x, y, nil)
-       b.StartTimer()
-
-       for i := 0; i < b.N; i++ {
-               z.string(lowercaseDigits[0:base])
+func TestStringPowers(t *testing.T) {
+       var b, p Word
+       for b = 2; b <= 16; b++ {
+               for p = 0; p <= 512; p++ {
+                       x := nat(nil).expWW(b, p)
+                       xs := x.string(lowercaseDigits[0:b])
+                       xs2 := toString(x, lowercaseDigits[0:b])
+                       if xs != xs2 {
+                               t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
+                       }
+               }
        }
 }
 
index d2b7e910b84868aaa807fc2dc5e0916e195f8447..09edc0eae881ab6f8c6b827898bb03ccde796c3a 100644 (file)
@@ -45,22 +45,21 @@ func Cbrt(x float64) float64 {
                x = -x
                sign = true
        }
-       // Reduce argument
-       f, e := Frexp(x)
+       // Reduce argument and estimate cube root
+       f, e := Frexp(x) // 0.5 <= f < 1.0
        m := e % 3
        if m > 0 {
                m -= 3
                e -= m // e is multiple of 3
        }
-       f = Ldexp(f, m) // 0.125 <= f < 1.0
-
-       // Estimate cube root
        switch m {
        case 0: // 0.5 <= f < 1.0
                f = A1*f + A2 - A3/(A4+f)
-       case -1: // 0.25 <= f < 0.5
+       case -1:
+               f *= 0.5 // 0.25 <= f < 0.5
                f = B1*f + B2 - B3/(B4+f)
-       default: // 0.125 <= f < 0.25
+       default: // m == -2
+               f *= 0.25 // 0.125 <= f < 0.25
                f = C1*f + C2 - C3/(C4+f)
        }
        y := Ldexp(f, e/3) // e/3 = exponent of cube root
index babbf645f5ce6f03572d406fd3a7631fe85b8651..8de4d7e2ce68cc1147ef0d088500f59c4f083dce 100644 (file)
@@ -7,8 +7,7 @@ package math
 // Floor returns the greatest integer value less than or equal to x.
 //
 // Special cases are:
-//     Floor(+Inf) = +Inf
-//     Floor(-Inf) = -Inf
+//     Floor(±Inf) = ±Inf
 //     Floor(NaN) = NaN
 func Floor(x float64) float64 {
        // TODO(rsc): Remove manual inlining of IsNaN, IsInf
@@ -30,16 +29,14 @@ func Floor(x float64) float64 {
 // Ceil returns the least integer value greater than or equal to x.
 //
 // Special cases are:
-//     Ceil(+Inf) = +Inf
-//     Ceil(-Inf) = -Inf
+//     Ceil(±Inf) = ±Inf
 //     Ceil(NaN) = NaN
 func Ceil(x float64) float64 { return -Floor(-x) }
 
 // Trunc returns the integer value of x.
 //
 // Special cases are:
-//     Trunc(+Inf) = +Inf
-//     Trunc(-Inf) = -Inf
+//     Trunc(±Inf) = ±Inf
 //     Trunc(NaN) = NaN
 func Trunc(x float64) float64 {
        // TODO(rsc): Remove manual inlining of IsNaN, IsInf
index ae2c0c418ab7b04cb3a03adfa7c2e4cae200eb07..7365d8e77541b0446f209e56b13c96444e6697cb 100644 (file)
@@ -113,8 +113,7 @@ func stirling(x float64) float64 {
 // Gamma(x) returns the Gamma function of x.
 //
 // Special cases are:
-//     Gamma(Inf) = Inf
-//     Gamma(-Inf) = -Inf
+//     Gamma(±Inf) = ±Inf
 //     Gamma(NaN) = NaN
 // Large values overflow to +Inf.
 // Negative integer values equal ±Inf.
index c25d73b66411d81090554adf8be6d0c281c70ebf..e8914a1d0533b79916ac95da8429e4ec4011c2bd 100644 (file)
@@ -44,7 +44,7 @@ package math
 //                      2      4      6      8      10      12      14
 //          R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
 //      (the values of Lp1 to Lp7 are listed in the program)
-//      a-0.2929nd
+//      and
 //          |      2          14          |     -58.45
 //          | Lp1*s +...+Lp7*s    -  R(z) | <= 2
 //          |                             |
@@ -88,6 +88,7 @@ package math
 //
 // Special cases are:
 //     Log1p(+Inf) = +Inf
+//     Log1p(±0) = ±0
 //     Log1p(-1) = -Inf
 //     Log1p(x < -1) = NaN
 //     Log1p(NaN) = NaN
index 315174b70144ab31494dbf6c8e585835b11722b9..34889e0c0af8732d9f51c7c6636cc094e9ff135e 100644 (file)
@@ -8,8 +8,7 @@ package math
 // that sum to f.  Both values have the same sign as f.
 //
 // Special cases are:
-//     Modf(+Inf) = +Inf, NaN
-//     Modf(-Inf) = -Inf, NaN
+//     Modf(±Inf) = ±Inf, NaN
 //     Modf(NaN) = NaN, NaN
 func Modf(f float64) (int float64, frac float64) {
        if f < 1 {
index 4c1576bead1fbc1a17004f98299732e4506e96d9..f5412fd726f62ad2d33544eebc898c7ef7bcb59c 100644 (file)
@@ -4,10 +4,66 @@
 
 package math
 
+// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go.
+
 // Sincos(x) returns Sin(x), Cos(x).
 //
 // Special conditions are:
-//     Sincos(+Inf) = NaN, NaN
-//     Sincos(-Inf) = NaN, NaN
+//     Sincos(±0) = ±0, 1
+//     Sincos(±Inf) = NaN, NaN
 //     Sincos(NaN) = NaN, NaN
-func Sincos(x float64) (sin, cos float64) { return Sin(x), Cos(x) }
+func Sincos(x float64) (sin, cos float64) {
+       const (
+               PI4A = 7.85398125648498535156E-1                             // 0x3fe921fb40000000, Pi/4 split into three parts
+               PI4B = 3.77489470793079817668E-8                             // 0x3e64442d00000000,
+               PI4C = 2.69515142907905952645E-15                            // 0x3ce8469898cc5170,
+               M4PI = 1.273239544735162542821171882678754627704620361328125 // 4/pi
+       )
+       // TODO(rsc): Remove manual inlining of IsNaN, IsInf
+       // when compiler does it for us
+       // special cases
+       switch {
+       case x == 0:
+               return x, 1 // return ±0.0, 1.0
+       case x != x || x < -MaxFloat64 || x > MaxFloat64: // IsNaN(x) || IsInf(x, 0):
+               return NaN(), NaN()
+       }
+
+       // make argument positive
+       sinSign, cosSign := false, false
+       if x < 0 {
+               x = -x
+               sinSign = true
+       }
+
+       j := int64(x * M4PI) // integer part of x/(Pi/4), as integer for tests on the phase angle
+       y := float64(j)      // integer part of x/(Pi/4), as float
+
+       if j&1 == 1 { // map zeros to origin
+               j += 1
+               y += 1
+       }
+       j &= 7     // octant modulo 2Pi radians (360 degrees)
+       if j > 3 { // reflect in x axis
+               j -= 4
+               sinSign, cosSign = !sinSign, !cosSign
+       }
+       if j > 1 {
+               cosSign = !cosSign
+       }
+
+       z := ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic
+       zz := z * z
+       cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5])
+       sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5])
+       if j == 1 || j == 2 {
+               sin, cos = cos, sin
+       }
+       if cosSign {
+               cos = -cos
+       }
+       if sinSign {
+               sin = -sin
+       }
+       return
+}
index bab5f2a9b6e94f4eb1258c2c6fa17e0c82b058f9..79a958e3cd0b9d0999d3053cb102530f86c9db95 100644 (file)
@@ -29,7 +29,7 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
                return nil, &DNSError{Err: "name too long", Name: name}
        }
        out := new(dnsMsg)
-       out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
+       out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
        out.question = []dnsQuestion{
                {name, qtype, dnsClassINET},
        }
index 70e04a21c0b5c0cf50f64023771fc80e65d49e33..5318c51c9a2d7142a8f96a4fa3aa76789a5000ad 100644 (file)
@@ -171,7 +171,7 @@ func (s *pollServer) WakeFD(fd *netFD, mode int) {
 }
 
 func (s *pollServer) Now() int64 {
-       return time.Nanoseconds()
+       return time.Now().UnixNano()
 }
 
 func (s *pollServer) CheckDeadlines() {
index 7a1602371e9c726a178f5fc20cda66523c448d3b..264b918c57dc147a2ad4f2cdb493d068aa3ad2ec 100644 (file)
@@ -172,11 +172,12 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
                return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e}
        }
        // Wait for our request to complete.
+       // TODO(rsc): This should stop the timer.
        var r ioResult
        if deadline_delta > 0 {
                select {
                case r = <-o.resultc:
-               case <-time.After(deadline_delta):
+               case <-time.After(time.Duration(deadline_delta) * time.Nanosecond):
                        s.canchan <- oi
                        <-o.errnoc
                        r = <-o.resultc
index ddfb074ee8f12a43f119c344cf218f20c6c15feb..e6674ba3415a8be028fa47d1d733eb65b6271ddc 100644 (file)
@@ -11,7 +11,7 @@ import (
        "time"
 )
 
-const cacheMaxAge = int64(300) // 5 minutes.
+const cacheMaxAge = 5 * time.Minute
 
 // hostsPath points to the file with static IP/address entries.
 var hostsPath = "/etc/hosts"
@@ -21,14 +21,14 @@ var hosts struct {
        sync.Mutex
        byName map[string][]string
        byAddr map[string][]string
-       time   int64
+       expire time.Time
        path   string
 }
 
 func readHosts() {
-       now := time.Seconds()
+       now := time.Now()
        hp := hostsPath
-       if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
+       if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp {
                hs := make(map[string][]string)
                is := make(map[string][]string)
                var file *file
@@ -51,7 +51,7 @@ func readHosts() {
                        }
                }
                // Update the data cache.
-               hosts.time = time.Seconds()
+               hosts.expire = time.Now().Add(cacheMaxAge)
                hosts.path = hp
                hosts.byName = hs
                hosts.byAddr = is
index e6e85e8db3b0f4db9ae139941990aaa2245af580..849cb008b76663ce891d9847587a657660bc0f17 100644 (file)
@@ -365,7 +365,7 @@ func TestCopyError(t *testing.T) {
 
        tries := 0
        for tries < 15 && childRunning() {
-               time.Sleep(50e6 * int64(tries))
+               time.Sleep(50 * time.Millisecond * time.Duration(tries))
                tries++
        }
        if childRunning() {
index 69350143248540db7f7622754aa65dbafdb922be..cad852242e2e520d6db5defe39d5758f0cae6b0f 100644 (file)
@@ -115,7 +115,7 @@ func readSetCookies(h Header) []*Cookie {
                                                break
                                        }
                                }
-                               c.Expires = *exptime
+                               c.Expires = exptime.UTC()
                                continue
                        case "path":
                                c.Path = val
@@ -146,8 +146,8 @@ func (c *Cookie) String() string {
        if len(c.Domain) > 0 {
                fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain))
        }
-       if len(c.Expires.Zone) > 0 {
-               fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123))
+       if c.Expires.Unix() > 0 {
+               fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
        }
        if c.MaxAge > 0 {
                fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
index 24adf2029817e16c77d8b5b2a98fadda0334af44..26bff93f6437cac5d5ab6a267bded419b6026041 100644 (file)
@@ -123,7 +123,7 @@ var readSetCookiesTests = []struct {
                        Path:       "/",
                        Domain:     ".google.ch",
                        HttpOnly:   true,
-                       Expires:    time.Time{Year: 2011, Month: 11, Day: 23, Hour: 1, Minute: 5, Second: 3, ZoneOffset: 0, Zone: "GMT"},
+                       Expires:    time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
                        RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
                        Raw:        "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
                }},
index 3fe658641f8b4e546a4b7b95f5bbb21f1a5e8dd2..13640ca85ee260f22670ada0fc3645fbd85b97d5 100644 (file)
@@ -7,6 +7,8 @@
 
 package http
 
+import "time"
+
 func (t *Transport) IdleConnKeysForTesting() (keys []string) {
        keys = make([]string, 0)
        t.lk.Lock()
@@ -33,8 +35,8 @@ func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
        return len(conns)
 }
 
-func NewTestTimeoutHandler(handler Handler, ch <-chan int64) Handler {
-       f := func() <-chan int64 {
+func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler {
+       f := func() <-chan time.Time {
                return ch
        }
        return &timeoutHandler{handler, f, ""}
index 529440cbe92a0d7a644e93cdefb9215fb8aba7aa..c94b9a7b24919dd372765584aa3082186bf89b55 100644 (file)
@@ -103,7 +103,7 @@ func (r *response) WriteHeader(code int) {
        }
 
        if r.header.Get("Date") == "" {
-               r.header.Set("Date", time.UTC().Format(http.TimeFormat))
+               r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
        }
 
        fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
index 5aadac17a23535401049e52dda1e705a300aac33..70e7849f16701bad9365ec9831a356f76292aa68 100644 (file)
@@ -52,7 +52,7 @@ type FileSystem interface {
 // served by the FileServer implementation.
 type File interface {
        Close() error
-       Stat() (*os.FileInfo, error)
+       Stat() (os.FileInfo, error)
        Readdir(count int) ([]os.FileInfo, error)
        Read([]byte) (int, error)
        Seek(offset int64, whence int) (int64, error)
@@ -93,8 +93,8 @@ func dirList(w ResponseWriter, f File) {
                        break
                }
                for _, d := range dirs {
-                       name := d.Name
-                       if d.IsDirectory() {
+                       name := d.Name()
+                       if d.IsDir() {
                                name += "/"
                        }
                        // TODO htmlescape
@@ -135,7 +135,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
                // redirect to canonical path: / at end of directory url
                // r.URL.Path always begins with /
                url := r.URL.Path
-               if d.IsDirectory() {
+               if d.IsDir() {
                        if url[len(url)-1] != '/' {
                                localRedirect(w, r, path.Base(url)+"/")
                                return
@@ -148,14 +148,14 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
                }
        }
 
-       if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t != nil && d.Mtime_ns/1e9 <= t.Seconds() {
+       if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && !d.ModTime().After(t) {
                w.WriteHeader(StatusNotModified)
                return
        }
-       w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
+       w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat))
 
        // use contents of index.html for directory, if present
-       if d.IsDirectory() {
+       if d.IsDir() {
                index := name + indexPage
                ff, err := fs.Open(index)
                if err == nil {
@@ -169,13 +169,13 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
                }
        }
 
-       if d.IsDirectory() {
+       if d.IsDir() {
                dirList(w, f)
                return
        }
 
        // serve file
-       size := d.Size
+       size := d.Size()
        code := StatusOK
 
        // If Content-Type isn't set, use the file's extension to find it.
@@ -215,7 +215,7 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
                }
                size = ra.length
                code = StatusPartialContent
-               w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
+               w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size()))
        }
 
        w.Header().Set("Accept-Ranges", "bytes")
index 6697189900ed947ddc943ede45420e289057ba97..976ee75c7dd0710a1b6a99afcc5ab1935d14df57 100644 (file)
@@ -190,8 +190,8 @@ func TestDirJoin(t *testing.T) {
                if err != nil {
                        t.Fatalf("stat of %s: %v", name, err)
                }
-               if gfi.Ino != wfi.Ino {
-                       t.Errorf("%s got different inode", name)
+               if !gfi.(*os.FileStat).SameFile(wfi.(*os.FileStat)) {
+                       t.Errorf("%s got different file", name)
                }
        }
        test(Dir("/etc/"), "/hosts")
index f09e826d9c9bbfdf8072ac48666fc3f2f7696322..5b02e143d4a2b8f4294acc2022dbfae46ec30c91 100644 (file)
@@ -7,14 +7,12 @@
 package httptest
 
 import (
-       "crypto/rand"
        "crypto/tls"
        "flag"
        "fmt"
        "net"
        "net/http"
        "os"
-       "time"
 )
 
 // A Server is an HTTP server listening on a system-chosen port on the
@@ -113,8 +111,6 @@ func (s *Server) StartTLS() {
        }
 
        s.TLS = &tls.Config{
-               Rand:         rand.Reader,
-               Time:         time.Seconds,
                NextProtos:   []string{"http/1.1"},
                Certificates: []tls.Certificate{cert},
        }
index bfcb3ca6b111d247dde7b43a92e0c338b893f78f..1dc83e7d032610ec2b9f160ccf64afa7ba53eff5 100644 (file)
@@ -31,11 +31,11 @@ type ReverseProxy struct {
        // If nil, http.DefaultTransport is used.
        Transport http.RoundTripper
 
-       // FlushInterval specifies the flush interval, in
-       // nanoseconds, to flush to the client while
-       // coping the response body.
+       // FlushInterval specifies the flush interval
+       // to flush to the client while copying the
+       // response body.
        // If zero, no periodic flushing is done.
-       FlushInterval int64
+       FlushInterval time.Duration
 }
 
 func singleJoiningSlash(a, b string) string {
@@ -135,7 +135,7 @@ type writeFlusher interface {
 
 type maxLatencyWriter struct {
        dst     writeFlusher
-       latency int64 // nanos
+       latency time.Duration
 
        lk   sync.Mutex // protects init of done, as well Write + Flush
        done chan bool
index c0327a948244cf08bf081b1487894afce99fa725..2de147579d1fae1a537f72c67d42a41c85898afa 100644 (file)
@@ -80,7 +80,7 @@ func Profile(w http.ResponseWriter, r *http.Request) {
                fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
                return
        }
-       time.Sleep(sec * 1e9)
+       time.Sleep(time.Duration(sec) * time.Second)
        pprof.StopCPUProfile()
 }
 
index 97a0b139e392796aa9e03b8f725156905221e856..670b5418fcd36e376cde63d747f8055436704164 100644 (file)
@@ -266,19 +266,19 @@ func TestServerTimeouts(t *testing.T) {
        }
 
        // Slow client that should timeout.
-       t1 := time.Nanoseconds()
+       t1 := time.Now()
        conn, err := net.Dial("tcp", addr.String())
        if err != nil {
                t.Fatalf("Dial: %v", err)
        }
        buf := make([]byte, 1)
        n, err := conn.Read(buf)
-       latency := time.Nanoseconds() - t1
+       latency := time.Now().Sub(t1)
        if n != 0 || err != io.EOF {
                t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF)
        }
-       if latency < second*0.20 /* fudge from 0.25 above */ {
-               t.Errorf("got EOF after %d ns, want >= %d", latency, second*0.20)
+       if latency < 200*time.Millisecond /* fudge from 0.25 above */ {
+               t.Errorf("got EOF after %s, want >= %s", latency, 200*time.Millisecond)
        }
 
        // Hit the HTTP server successfully again, verifying that the
@@ -760,7 +760,7 @@ func TestTimeoutHandler(t *testing.T) {
                _, werr := w.Write([]byte("hi"))
                writeErrors <- werr
        })
-       timeout := make(chan int64, 1) // write to this to force timeouts
+       timeout := make(chan time.Time, 1) // write to this to force timeouts
        ts := httptest.NewServer(NewTestTimeoutHandler(sayHi, timeout))
        defer ts.Close()
 
@@ -782,7 +782,7 @@ func TestTimeoutHandler(t *testing.T) {
        }
 
        // Times out:
-       timeout <- 1
+       timeout <- time.Time{}
        res, err = Get(ts.URL)
        if err != nil {
                t.Error(err)
index 7221d2508bb4e4856e8f61080160190fbf295f5e..125f3f214bb270d61c834f0cbc409041c713821b 100644 (file)
@@ -347,7 +347,7 @@ func (w *response) WriteHeader(code int) {
        }
 
        if _, ok := w.header["Date"]; !ok {
-               w.Header().Set("Date", time.UTC().Format(TimeFormat))
+               w.Header().Set("Date", time.Now().UTC().Format(TimeFormat))
        }
 
        te := w.header.Get("Transfer-Encoding")
@@ -467,7 +467,7 @@ func (w *response) Write(data []byte) (n int, err error) {
                // determine the content type.  Accumulate the
                // initial writes in w.conn.body.
                // Cap m so that append won't allocate.
-               m := cap(w.conn.body) - len(w.conn.body)
+               m = cap(w.conn.body) - len(w.conn.body)
                if m > len(data) {
                        m = len(data)
                }
@@ -1013,8 +1013,8 @@ func (srv *Server) Serve(l net.Listener) error {
 //     package main
 //
 //     import (
-//             "http"
 //             "io"
+//             "net/http"
 //             "log"
 //     )
 //
@@ -1044,8 +1044,8 @@ func ListenAndServe(addr string, handler Handler) error {
 // A trivial example server is:
 //
 //     import (
-//             "http"
 //             "log"
+//             "net/http"
 //     )
 //
 //     func handler(w http.ResponseWriter, req *http.Request) {
@@ -1084,7 +1084,6 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
        }
        config := &tls.Config{
                Rand:       rand.Reader,
-               Time:       time.Seconds,
                NextProtos: []string{"http/1.1"},
        }
 
@@ -1112,9 +1111,9 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
 // (If msg is empty, a suitable default message will be sent.)
 // After such a timeout, writes by h to its ResponseWriter will return
 // ErrHandlerTimeout.
-func TimeoutHandler(h Handler, ns int64, msg string) Handler {
-       f := func() <-chan int64 {
-               return time.After(ns)
+func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler {
+       f := func() <-chan time.Time {
+               return time.After(dt)
        }
        return &timeoutHandler{h, f, msg}
 }
@@ -1125,7 +1124,7 @@ var ErrHandlerTimeout = errors.New("http: Handler timeout")
 
 type timeoutHandler struct {
        handler Handler
-       timeout func() <-chan int64 // returns channel producing a timeout
+       timeout func() <-chan time.Time // returns channel producing a timeout
        body    string
 }
 
index 5707c7f057f2e03152f832dad706e3ec9c0b0b55..c1c78e2417d3f0024c00df1b2e3d6691f1a94cc7 100644 (file)
@@ -48,23 +48,23 @@ type sniffSig interface {
 
 // Data matching the table in section 6.
 var sniffSignatures = []sniffSig{
-       htmlSig([]byte("<!DOCTYPE HTML")),
-       htmlSig([]byte("<HTML")),
-       htmlSig([]byte("<HEAD")),
-       htmlSig([]byte("<SCRIPT")),
-       htmlSig([]byte("<IFRAME")),
-       htmlSig([]byte("<H1")),
-       htmlSig([]byte("<DIV")),
-       htmlSig([]byte("<FONT")),
-       htmlSig([]byte("<TABLE")),
-       htmlSig([]byte("<A")),
-       htmlSig([]byte("<STYLE")),
-       htmlSig([]byte("<TITLE")),
-       htmlSig([]byte("<B")),
-       htmlSig([]byte("<BODY")),
-       htmlSig([]byte("<BR")),
-       htmlSig([]byte("<P")),
-       htmlSig([]byte("<!--")),
+       htmlSig("<!DOCTYPE HTML"),
+       htmlSig("<HTML"),
+       htmlSig("<HEAD"),
+       htmlSig("<SCRIPT"),
+       htmlSig("<IFRAME"),
+       htmlSig("<H1"),
+       htmlSig("<DIV"),
+       htmlSig("<FONT"),
+       htmlSig("<TABLE"),
+       htmlSig("<A"),
+       htmlSig("<STYLE"),
+       htmlSig("<TITLE"),
+       htmlSig("<B"),
+       htmlSig("<BODY"),
+       htmlSig("<BR"),
+       htmlSig("<P"),
+       htmlSig("<!--"),
 
        &maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},
 
index 86744eeb56b762f22e517e6ece559ff023baf704..6efa8ce1ca228de6cd938eb76dafa44ed11a3472 100644 (file)
@@ -6,12 +6,14 @@ package http_test
 
 import (
        "bytes"
+       "fmt"
        "io"
        "io/ioutil"
        "log"
        . "net/http"
        "net/http/httptest"
        "strconv"
+       "strings"
        "testing"
 )
 
@@ -112,3 +114,24 @@ func TestContentTypeWithCopy(t *testing.T) {
        }
        resp.Body.Close()
 }
+
+func TestSniffWriteSize(t *testing.T) {
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               size, _ := strconv.Atoi(r.FormValue("size"))
+               written, err := io.WriteString(w, strings.Repeat("a", size))
+               if err != nil {
+                       t.Errorf("write of %d bytes: %v", size, err)
+                       return
+               }
+               if written != size {
+                       t.Errorf("write of %d bytes wrote %d bytes", size, written)
+               }
+       }))
+       defer ts.Close()
+       for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} {
+               _, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size))
+               if err != nil {
+                       t.Fatalf("size %d: %v", size, err)
+               }
+       }
+}
index 772979724492bd20d020f5076f19251067aa3c95..6f50f6f27677786157e282957b1a65f422764bbf 100644 (file)
@@ -263,7 +263,7 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) {
                                t.Fatalf(format, arg...)
                        }
                        t.Logf("retrying shortly after expected error: "+format, arg...)
-                       time.Sleep(1e9 / int64(retries))
+                       time.Sleep(time.Second / time.Duration(retries))
                }
                for retries >= 0 {
                        retries--
index 95246b2fa1dd460bf2ad4a1ec009ab7b8e2597fd..e1afa32062f99bb07d951cb3027b7f0eaa3ce0a4 100644 (file)
@@ -89,14 +89,14 @@ func init() {
        }
 }
 
-func parseDate(date string) (*time.Time, error) {
+func parseDate(date string) (time.Time, error) {
        for _, layout := range dateLayouts {
                t, err := time.Parse(layout, date)
                if err == nil {
                        return t, nil
                }
        }
-       return nil, errors.New("mail: header could not be parsed")
+       return time.Time{}, errors.New("mail: header could not be parsed")
 }
 
 // A Header represents the key-value pairs in a mail message header.
@@ -111,10 +111,10 @@ func (h Header) Get(key string) string {
 var ErrHeaderNotPresent = errors.New("mail: header not in message")
 
 // Date parses the Date header field.
-func (h Header) Date() (*time.Time, error) {
+func (h Header) Date() (time.Time, error) {
        hdr := h.Get("Date")
        if hdr == "" {
-               return nil, ErrHeaderNotPresent
+               return time.Time{}, ErrHeaderNotPresent
        }
        return parseDate(hdr)
 }
@@ -185,7 +185,7 @@ func (a *Address) String() string {
 type addrParser []byte
 
 func newAddrParser(s string) *addrParser {
-       p := addrParser([]byte(s))
+       p := addrParser(s)
        return &p
 }
 
index 5653647b8cce0fc7d25d2fccd05929c881d48e37..1f71cc480aff8b78eda742241b825d7b042140d5 100644 (file)
@@ -82,34 +82,18 @@ func headerEq(a, b Header) bool {
 func TestDateParsing(t *testing.T) {
        tests := []struct {
                dateStr string
-               exp     *time.Time
+               exp     time.Time
        }{
                // RFC 5322, Appendix A.1.1
                {
                        "Fri, 21 Nov 1997 09:55:06 -0600",
-                       &time.Time{
-                               Year:       1997,
-                               Month:      11,
-                               Day:        21,
-                               Hour:       9,
-                               Minute:     55,
-                               Second:     6,
-                               ZoneOffset: -6 * 60 * 60,
-                       },
+                       time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
                },
                // RFC5322, Appendix A.6.2
                // Obsolete date.
                {
                        "21 Nov 97 09:55:06 GMT",
-                       &time.Time{
-                               Year:   1997,
-                               Month:  11,
-                               Day:    21,
-                               Hour:   9,
-                               Minute: 55,
-                               Second: 6,
-                               Zone:   "GMT",
-                       },
+                       time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
                },
        }
        for _, test := range tests {
index 3c884ca7cfe2d5639eb5af7e7f5d36f4f387ca11..f6e5238c1b1d481873be8886fb258f58f491ca27 100644 (file)
@@ -17,7 +17,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
                return
        }
        defer fd.Close()
-       t0 := time.Nanoseconds()
+       t0 := time.Now()
        fd.SetReadTimeout(1e8) // 100ms
        var b [100]byte
        var n int
@@ -27,7 +27,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
        } else {
                n, err1 = fd.Read(b[0:])
        }
-       t1 := time.Nanoseconds()
+       t1 := time.Now()
        what := "Read"
        if readFrom {
                what = "ReadFrom"
@@ -35,8 +35,8 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
        if n != 0 || err1 == nil || !err1.(Error).Timeout() {
                t.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
        }
-       if t1-t0 < 0.5e8 || t1-t0 > 1.5e8 {
-               t.Errorf("fd.%s on %s %s took %f seconds, expected 0.1", what, network, addr, float64(t1-t0)/1e9)
+       if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 150*time.Millisecond {
+               t.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
        }
 }
 
index dfd1fd03427c97fcc3268f3bb6a7d9574f3a6067..03fa8ff6c4128661b1dbdb93e007e5ffbdef8f83 100644 (file)
@@ -129,8 +129,8 @@ func (ed *encDec) encode(hdr *header, payloadType int, payload interface{}) erro
 }
 
 // See the comment for Exporter.Drain.
-func (cs *clientSet) drain(timeout int64) error {
-       startTime := time.Nanoseconds()
+func (cs *clientSet) drain(timeout time.Duration) error {
+       deadline := time.Now().Add(timeout)
        for {
                pending := false
                cs.mu.Lock()
@@ -152,7 +152,7 @@ func (cs *clientSet) drain(timeout int64) error {
                if !pending {
                        break
                }
-               if timeout > 0 && time.Nanoseconds()-startTime >= timeout {
+               if timeout > 0 && time.Now().After(deadline) {
                        return errors.New("timeout")
                }
                time.Sleep(100 * 1e6) // 100 milliseconds
@@ -161,8 +161,8 @@ func (cs *clientSet) drain(timeout int64) error {
 }
 
 // See the comment for Exporter.Sync.
-func (cs *clientSet) sync(timeout int64) error {
-       startTime := time.Nanoseconds()
+func (cs *clientSet) sync(timeout time.Duration) error {
+       deadline := time.Now().Add(timeout)
        // seq remembers the clients and their seqNum at point of entry.
        seq := make(map[unackedCounter]int64)
        for client := range cs.clients {
@@ -185,7 +185,7 @@ func (cs *clientSet) sync(timeout int64) error {
                if !pending {
                        break
                }
-               if timeout > 0 && time.Nanoseconds()-startTime >= timeout {
+               if timeout > 0 && time.Now().After(deadline) {
                        return errors.New("timeout")
                }
                time.Sleep(100 * 1e6) // 100 milliseconds
index d698dd53a900c069515b2dbd6d25d715a6f580ed..d94c4b16b21e3c80555f0763b3eb02a9dc25c378 100644 (file)
@@ -29,6 +29,7 @@ import (
        "reflect"
        "strconv"
        "sync"
+       "time"
 )
 
 // Export
@@ -322,9 +323,9 @@ func (exp *Exporter) delClient(client *expClient) {
 // those not yet sent to any client and possibly including those sent while
 // Drain was executing, have been received by the importer.  In short, it
 // waits until all the exporter's messages have been received by a client.
-// If the timeout (measured in nanoseconds) is positive and Drain takes
-// longer than that to complete, an error is returned.
-func (exp *Exporter) Drain(timeout int64) error {
+// If the timeout is positive and Drain takes longer than that to complete,
+// an error is returned.
+func (exp *Exporter) Drain(timeout time.Duration) error {
        // This wrapper function is here so the method's comment will appear in godoc.
        return exp.clientSet.drain(timeout)
 }
@@ -332,10 +333,9 @@ func (exp *Exporter) Drain(timeout int64) error {
 // Sync waits until all clients of the exporter have received the messages
 // that were sent at the time Sync was invoked.  Unlike Drain, it does not
 // wait for messages sent while it is running or messages that have not been
-// dispatched to any client.  If the timeout (measured in nanoseconds) is
-// positive and Sync takes longer than that to complete, an error is
-// returned.
-func (exp *Exporter) Sync(timeout int64) error {
+// dispatched to any client.  If the timeout is positive and Sync takes longer
+// than that to complete, an error is returned.
+func (exp *Exporter) Sync(timeout time.Duration) error {
        // This wrapper function is here so the method's comment will appear in godoc.
        return exp.clientSet.sync(timeout)
 }
index 7243672ecd3cc3ec50d2ec85a05a14d8b557a897..a6da8210b99ccce86804274b5438eec43d582f47 100644 (file)
@@ -276,9 +276,9 @@ func (imp *Importer) unackedCount() int64 {
 // If the timeout (measured in nanoseconds) is positive and Drain takes
 // longer than that to complete, an error is returned.
 func (imp *Importer) Drain(timeout int64) error {
-       startTime := time.Nanoseconds()
+       deadline := time.Now().Add(time.Duration(timeout))
        for imp.unackedCount() > 0 {
-               if timeout > 0 && time.Nanoseconds()-startTime >= timeout {
+               if timeout > 0 && time.Now().After(deadline) {
                        return errors.New("timeout")
                }
                time.Sleep(100 * 1e6)
index d234641acc47ae073e1cbdfe438640e9c5b52bac..9665ea8f4138a516a01200cf130b1e5537f57246 100644 (file)
@@ -20,7 +20,7 @@ func findExecutable(file string) error {
        if err != nil {
                return err
        }
-       if d.IsRegular() && d.Permission()&0111 != 0 {
+       if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
                return nil
        }
        return os.EPERM
index db326236ee8698c03e06840ff61d11dac144943d..ef5bd921668f7e613242a0f2c5a4082099b6d3ad 100644 (file)
@@ -18,10 +18,10 @@ func chkStat(file string) error {
        if err != nil {
                return err
        }
-       if d.IsRegular() {
-               return nil
+       if d.IsDir() {
+               return os.EPERM
        }
-       return os.EPERM
+       return nil
 }
 
 func findExecutable(file string, exts []string) (string, error) {
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
new file mode 100644 (file)
index 0000000..9c6ef42
--- /dev/null
@@ -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 os
+
+// Export for testing.
+
+var Atime = atime
index 386afb889b2c9c3e8e4225e0a013a530d6fe14d8..71845d3c9c93d833d25be13136acae0acf90e7cb 100644 (file)
@@ -14,7 +14,7 @@ import (
 )
 
 // Name returns the name of the file as presented to Open.
-func (file *File) Name() string { return file.name }
+func (f *File) Name() string { return f.name }
 
 // Stdin, Stdout, and Stderr are open Files pointing to the standard input,
 // standard output, and standard error file descriptors.
@@ -51,11 +51,11 @@ const (
 // Read reads up to len(b) bytes from the File.
 // It returns the number of bytes read and an error, if any.
 // EOF is signaled by a zero count with err set to io.EOF.
-func (file *File) Read(b []byte) (n int, err error) {
-       if file == nil {
+func (f *File) Read(b []byte) (n int, err error) {
+       if f == nil {
                return 0, EINVAL
        }
-       n, e := file.read(b)
+       n, e := f.read(b)
        if n < 0 {
                n = 0
        }
@@ -63,26 +63,26 @@ func (file *File) Read(b []byte) (n int, err error) {
                return 0, io.EOF
        }
        if e != nil {
-               err = &PathError{"read", file.name, e}
+               err = &PathError{"read", f.name, e}
        }
        return n, err
 }
 
 // ReadAt reads len(b) bytes from the File starting at byte offset off.
 // It returns the number of bytes read and the error, if any.
-// EOF is signaled by a zero count with err set to io.EOF.
-// ReadAt always returns a non-nil error when n != len(b).
-func (file *File) ReadAt(b []byte, off int64) (n int, err error) {
-       if file == nil {
+// ReadAt always returns a non-nil error when n < len(b).
+// At end of file, that error is io.EOF.
+func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
+       if f == nil {
                return 0, EINVAL
        }
        for len(b) > 0 {
-               m, e := file.pread(b, off)
+               m, e := f.pread(b, off)
                if m == 0 && e == nil {
                        return n, io.EOF
                }
                if e != nil {
-                       err = &PathError{"read", file.name, e}
+                       err = &PathError{"read", f.name, e}
                        break
                }
                n += m
@@ -95,19 +95,19 @@ func (file *File) ReadAt(b []byte, off int64) (n int, err error) {
 // Write writes len(b) bytes to the File.
 // It returns the number of bytes written and an error, if any.
 // Write returns a non-nil error when n != len(b).
-func (file *File) Write(b []byte) (n int, err error) {
-       if file == nil {
+func (f *File) Write(b []byte) (n int, err error) {
+       if f == nil {
                return 0, EINVAL
        }
-       n, e := file.write(b)
+       n, e := f.write(b)
        if n < 0 {
                n = 0
        }
 
-       epipecheck(file, e)
+       epipecheck(f, e)
 
        if e != nil {
-               err = &PathError{"write", file.name, e}
+               err = &PathError{"write", f.name, e}
        }
        return n, err
 }
@@ -115,14 +115,14 @@ func (file *File) Write(b []byte) (n int, err error) {
 // WriteAt writes len(b) bytes to the File starting at byte offset off.
 // It returns the number of bytes written and an error, if any.
 // WriteAt returns a non-nil error when n != len(b).
-func (file *File) WriteAt(b []byte, off int64) (n int, err error) {
-       if file == nil {
+func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
+       if f == nil {
                return 0, EINVAL
        }
        for len(b) > 0 {
-               m, e := file.pwrite(b, off)
+               m, e := f.pwrite(b, off)
                if e != nil {
-                       err = &PathError{"write", file.name, e}
+                       err = &PathError{"write", f.name, e}
                        break
                }
                n += m
@@ -136,24 +136,24 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err error) {
 // according to whence: 0 means relative to the origin of the file, 1 means
 // relative to the current offset, and 2 means relative to the end.
 // It returns the new offset and an error, if any.
-func (file *File) Seek(offset int64, whence int) (ret int64, err error) {
-       r, e := file.seek(offset, whence)
-       if e == nil && file.dirinfo != nil && r != 0 {
+func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
+       r, e := f.seek(offset, whence)
+       if e == nil && f.dirinfo != nil && r != 0 {
                e = syscall.EISDIR
        }
        if e != nil {
-               return 0, &PathError{"seek", file.name, e}
+               return 0, &PathError{"seek", f.name, e}
        }
        return r, nil
 }
 
 // WriteString is like Write, but writes the contents of string s rather than
 // an array of bytes.
-func (file *File) WriteString(s string) (ret int, err error) {
-       if file == nil {
+func (f *File) WriteString(s string) (ret int, err error) {
+       if f == nil {
                return 0, EINVAL
        }
-       return file.Write([]byte(s))
+       return f.Write([]byte(s))
 }
 
 // Mkdir creates a new directory with the specified name and permission bits.
index c80d3df5e50a5ffc64783913f41664b76f07c834..a4ab5d6ae25d36b4d6572d20193ee991234b1854 100644 (file)
@@ -8,6 +8,7 @@ package os
 
 import (
        "syscall"
+       "time"
 )
 
 func sigpipe() // implemented in package runtime
@@ -168,11 +169,11 @@ func (f *File) Truncate(size int64) error {
 // Sync commits the current contents of the file to stable storage.
 // Typically, this means flushing the file system's in-memory copy
 // of recently written data to disk.
-func (file *File) Sync() (err error) {
-       if file == nil {
+func (f *File) Sync() (err error) {
+       if f == nil {
                return EINVAL
        }
-       if e := syscall.Fsync(file.fd); e != nil {
+       if e := syscall.Fsync(f.fd); e != nil {
                return NewSyscallError("fsync", e)
        }
        return nil
@@ -181,11 +182,12 @@ func (file *File) Sync() (err error) {
 // Chtimes changes the access and modification times of the named
 // file, similar to the Unix utime() or utimes() functions.
 //
-// The argument times are in nanoseconds, although the underlying
-// filesystem may truncate or round the values to a more
-// coarse time unit.
-func Chtimes(name string, atime_ns int64, mtime_ns int64) error {
+// The underlying filesystem may truncate or round the values to a
+// less precise time unit.
+func Chtimes(name string, atime time.Time, mtime time.Time) error {
        var utimes [2]syscall.Timeval
+       atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond())
+       mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond())
        utimes[0] = syscall.NsecToTimeval(atime_ns)
        utimes[1] = syscall.NsecToTimeval(mtime_ns)
        if e := syscall.Utimes(name, utimes[0:]); e != nil {
index 0bf31ecb9a74258abf8a4b1768ea58e3421c114c..671d1a4cb62e73772180f5780af36fc40c63129f 100644 (file)
@@ -28,11 +28,11 @@ type file struct {
 }
 
 // Fd returns the integer Unix file descriptor referencing the open file.
-func (file *File) Fd() int {
-       if file == nil {
+func (f *File) Fd() int {
+       if f == nil {
                return -1
        }
-       return file.fd
+       return f.fd
 }
 
 // NewFile returns a new File with the given file descriptor and name.
@@ -77,8 +77,8 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
 
 // Close closes the File, rendering it unusable for I/O.
 // It returns an error, if any.
-func (file *File) Close() error {
-       return file.file.close()
+func (f *File) Close() error {
+       return f.file.close()
 }
 
 func (file *file) close() error {
@@ -105,50 +105,43 @@ func (file *file) close() error {
 
 // Stat returns the FileInfo structure describing file.
 // It returns the FileInfo and an error, if any.
-func (file *File) Stat() (fi *FileInfo, err error) {
+func (f *File) Stat() (fi FileInfo, err error) {
        var stat syscall.Stat_t
-       e := syscall.Fstat(file.fd, &stat)
-       if e != nil {
-               return nil, &PathError{"stat", file.name, e}
+       err = syscall.Fstat(f.fd, &stat)
+       if err != nil {
+               return nil, &PathError{"stat", f.name, err}
        }
-       return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil
+       return fileInfoFromStat(&stat, f.name), nil
 }
 
-// Stat returns a FileInfo structure describing the named file and an error, if any.
+// Stat returns a FileInfo 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 e != nil {
-               return nil, &PathError{"stat", name, e}
-       }
-       statp := &lstat
-       if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
-               e := syscall.Stat(name, &stat)
-               if e == nil {
-                       statp = &stat
-               }
+func Stat(name string) (fi FileInfo, err error) {
+       var stat syscall.Stat_t
+       err = syscall.Stat(name, &stat)
+       if err != nil {
+               return nil, &PathError{"stat", name, err}
        }
-       return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
+       return fileInfoFromStat(&stat, name), nil
 }
 
-// Lstat returns the FileInfo structure describing the named file and an
+// Lstat returns a FileInfo 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) {
+func Lstat(name string) (fi FileInfo, err error) {
        var stat syscall.Stat_t
-       e := syscall.Lstat(name, &stat)
-       if e != nil {
-               return nil, &PathError{"lstat", name, e}
+       err = syscall.Lstat(name, &stat)
+       if err != nil {
+               return nil, &PathError{"lstat", name, err}
        }
-       return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
+       return fileInfoFromStat(&stat, name), 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
+// returns an array of up to n FileInfo values, as would be returned
 // by Lstat, in directory order. Subsequent calls on the same file will yield
 // further FileInfos.
 //
@@ -162,23 +155,23 @@ func Lstat(name string) (fi *FileInfo, err error) {
 // nil error. If it encounters an error before the end of the
 // directory, Readdir returns the FileInfo read until that point
 // and a non-nil error.
-func (file *File) Readdir(n int) (fi []FileInfo, err error) {
-       dirname := file.name
+func (f *File) Readdir(n int) (fi []FileInfo, err error) {
+       dirname := f.name
        if dirname == "" {
                dirname = "."
        }
        dirname += "/"
-       names, err := file.Readdirnames(n)
+       names, err := f.Readdirnames(n)
        fi = make([]FileInfo, len(names))
        for i, filename := range names {
                fip, err := Lstat(dirname + filename)
-               if fip == nil || err != nil {
-                       fi[i].Name = filename // rest is already zeroed out
+               if err == nil {
+                       fi[i] = fip
                } else {
-                       fi[i] = *fip
+                       fi[i] = &FileStat{name: filename}
                }
        }
-       return
+       return fi, err
 }
 
 // read reads up to len(b) bytes from the File.
index d5f5ae6bed712dede351aa34cf8989cc2b156076..a0d3c99a503eeaab5d1f11ab5fcf57fc4ffd775b 100644 (file)
@@ -12,7 +12,7 @@ import (
 // current directory.  If the current directory can be
 // reached via multiple paths (due to symbolic links),
 // Getwd may return any one of them.
-func Getwd() (string, error) {
+func Getwd() (pwd string, err error) {
        // If the operating system provides a Getwd call, use it.
        if syscall.ImplementsGetwd {
                s, e := syscall.Getwd()
@@ -27,10 +27,10 @@ func Getwd() (string, error) {
 
        // Clumsy but widespread kludge:
        // if $PWD is set and matches ".", use it.
-       pwd := Getenv("PWD")
+       pwd = Getenv("PWD")
        if len(pwd) > 0 && pwd[0] == '/' {
                d, err := Stat(pwd)
-               if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino {
+               if err == nil && dot.(*FileStat).SameFile(d.(*FileStat)) {
                        return pwd, nil
                }
        }
@@ -42,7 +42,7 @@ func Getwd() (string, error) {
                // Can't stat root - no hope.
                return "", err
        }
-       if root.Dev == dot.Dev && root.Ino == dot.Ino {
+       if root.(*FileStat).SameFile(dot.(*FileStat)) {
                return "/", nil
        }
 
@@ -67,7 +67,7 @@ func Getwd() (string, error) {
                        }
                        for _, name := range names {
                                d, _ := Lstat(parent + "/" + name)
-                               if d.Dev == dot.Dev && d.Ino == dot.Ino {
+                               if d.(*FileStat).SameFile(dot.(*FileStat)) {
                                        pwd = "/" + name + pwd
                                        goto Found
                                }
@@ -82,7 +82,7 @@ func Getwd() (string, error) {
                        return "", err
                }
                fd.Close()
-               if pd.Dev == root.Dev && pd.Ino == root.Ino {
+               if pd.(*FileStat).SameFile(root.(*FileStat)) {
                        break
                }
                // Set up for next round.
index 170f7331c6e4547f1229dbbac220c983b5392f26..299d2e86155bcd8010a03333ceaab6ea4de73453 100644 (file)
@@ -14,6 +14,7 @@ import (
        "strings"
        "syscall"
        "testing"
+       "time"
 )
 
 var dot = []string{
@@ -119,12 +120,12 @@ func TestStat(t *testing.T) {
        if err != nil {
                t.Fatal("stat failed:", err)
        }
-       if !equal(sfname, dir.Name) {
-               t.Error("name should be ", sfname, "; is", dir.Name)
+       if !equal(sfname, dir.Name()) {
+               t.Error("name should be ", sfname, "; is", dir.Name())
        }
        filesize := size(path, t)
-       if dir.Size != filesize {
-               t.Error("size should be", filesize, "; is", dir.Size)
+       if dir.Size() != filesize {
+               t.Error("size should be", filesize, "; is", dir.Size())
        }
 }
 
@@ -139,12 +140,12 @@ func TestFstat(t *testing.T) {
        if err2 != nil {
                t.Fatal("fstat failed:", err2)
        }
-       if !equal(sfname, dir.Name) {
-               t.Error("name should be ", sfname, "; is", dir.Name)
+       if !equal(sfname, dir.Name()) {
+               t.Error("name should be ", sfname, "; is", dir.Name())
        }
        filesize := size(path, t)
-       if dir.Size != filesize {
-               t.Error("size should be", filesize, "; is", dir.Size)
+       if dir.Size() != filesize {
+               t.Error("size should be", filesize, "; is", dir.Size())
        }
 }
 
@@ -154,12 +155,12 @@ func TestLstat(t *testing.T) {
        if err != nil {
                t.Fatal("lstat failed:", err)
        }
-       if !equal(sfname, dir.Name) {
-               t.Error("name should be ", sfname, "; is", dir.Name)
+       if !equal(sfname, dir.Name()) {
+               t.Error("name should be ", sfname, "; is", dir.Name())
        }
        filesize := size(path, t)
-       if dir.Size != filesize {
-               t.Error("size should be", filesize, "; is", dir.Size)
+       if dir.Size() != filesize {
+               t.Error("size should be", filesize, "; is", dir.Size())
        }
 }
 
@@ -226,7 +227,7 @@ func testReaddir(dir string, contents []string, t *testing.T) {
        for _, m := range contents {
                found := false
                for _, n := range s {
-                       if equal(m, n.Name) {
+                       if equal(m, n.Name()) {
                                if found {
                                        t.Error("present twice:", m)
                                }
@@ -405,7 +406,7 @@ func TestHardLink(t *testing.T) {
        if err != nil {
                t.Fatalf("stat %q failed: %v", from, err)
        }
-       if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino {
+       if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
                t.Errorf("link %q, %q did not create hard link", to, from)
        }
 }
@@ -430,32 +431,32 @@ func TestSymLink(t *testing.T) {
                t.Fatalf("symlink %q, %q failed: %v", to, from, err)
        }
        defer Remove(from)
-       tostat, err := Stat(to)
+       tostat, err := Lstat(to)
        if err != nil {
                t.Fatalf("stat %q failed: %v", to, err)
        }
-       if tostat.FollowedSymlink {
-               t.Fatalf("stat %q claims to have followed a symlink", to)
+       if tostat.Mode()&ModeSymlink != 0 {
+               t.Fatalf("stat %q claims to have found a symlink", to)
        }
        fromstat, err := Stat(from)
        if err != nil {
                t.Fatalf("stat %q failed: %v", from, err)
        }
-       if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino {
+       if !tostat.(*FileStat).SameFile(fromstat.(*FileStat)) {
                t.Errorf("symlink %q, %q did not create symlink", to, from)
        }
        fromstat, err = Lstat(from)
        if err != nil {
                t.Fatalf("lstat %q failed: %v", from, err)
        }
-       if !fromstat.IsSymlink() {
+       if fromstat.Mode()&ModeSymlink == 0 {
                t.Fatalf("symlink %q, %q did not create symlink", to, from)
        }
        fromstat, err = Stat(from)
        if err != nil {
                t.Fatalf("stat %q failed: %v", from, err)
        }
-       if !fromstat.FollowedSymlink {
+       if fromstat.Mode()&ModeSymlink != 0 {
                t.Fatalf("stat %q did not follow symlink", from)
        }
        s, err := Readlink(from)
@@ -563,13 +564,13 @@ func TestStartProcess(t *testing.T) {
        exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le)
 }
 
-func checkMode(t *testing.T, path string, mode uint32) {
+func checkMode(t *testing.T, path string, mode FileMode) {
        dir, err := Stat(path)
        if err != nil {
                t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
        }
-       if dir.Mode&0777 != mode {
-               t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode, mode)
+       if dir.Mode()&0777 != mode {
+               t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
        }
 }
 
@@ -593,73 +594,13 @@ func TestChmod(t *testing.T) {
        checkMode(t, f.Name(), 0123)
 }
 
-func checkUidGid(t *testing.T, path string, uid, gid int) {
-       dir, err := Stat(path)
-       if err != nil {
-               t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
-       }
-       if dir.Uid != uid {
-               t.Errorf("Stat %q: uid %d want %d", path, dir.Uid, uid)
-       }
-       if dir.Gid != gid {
-               t.Errorf("Stat %q: gid %d want %d", path, dir.Gid, gid)
-       }
-}
-
-func TestChown(t *testing.T) {
-       // Chown is not supported under windows or Plan 9.
-       // Plan9 provides a native ChownPlan9 version instead.
-       if syscall.OS == "windows" || syscall.OS == "plan9" {
-               return
-       }
-       // Use TempDir() to make sure we're on a local file system,
-       // so that the group ids returned by Getgroups will be allowed
-       // on the file.  On NFS, the Getgroups groups are
-       // basically useless.
-       f := newFile("TestChown", t)
-       defer Remove(f.Name())
-       defer f.Close()
-       dir, err := f.Stat()
-       if err != nil {
-               t.Fatalf("stat %s: %s", f.Name(), err)
-       }
-
-       // Can't change uid unless root, but can try
-       // changing the group id.  First try our current group.
-       gid := Getgid()
-       t.Log("gid:", gid)
-       if err = Chown(f.Name(), -1, gid); err != nil {
-               t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
-       }
-       checkUidGid(t, f.Name(), dir.Uid, gid)
-
-       // Then try all the auxiliary groups.
-       groups, err := Getgroups()
-       if err != nil {
-               t.Fatalf("getgroups: %s", err)
-       }
-       t.Log("groups: ", groups)
-       for _, g := range groups {
-               if err = Chown(f.Name(), -1, g); err != nil {
-                       t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
-               }
-               checkUidGid(t, f.Name(), dir.Uid, g)
-
-               // change back to gid to test fd.Chown
-               if err = f.Chown(-1, gid); err != nil {
-                       t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
-               }
-               checkUidGid(t, f.Name(), dir.Uid, gid)
-       }
-}
-
 func checkSize(t *testing.T, f *File, size int64) {
        dir, err := f.Stat()
        if err != nil {
                t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
        }
-       if dir.Size != size {
-               t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size, size)
+       if dir.Size() != size {
+               t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size)
        }
 }
 
@@ -711,37 +652,38 @@ func TestChtimes(t *testing.T) {
        f.Write([]byte("hello, world\n"))
        f.Close()
 
-       preStat, err := Stat(f.Name())
+       st, err := Stat(f.Name())
        if err != nil {
                t.Fatalf("Stat %s: %s", f.Name(), err)
        }
+       preStat := st.(*FileStat)
 
        // Move access and modification time back a second
-       const OneSecond = 1e9 // in nanoseconds
-       err = Chtimes(f.Name(), preStat.Atime_ns-OneSecond, preStat.Mtime_ns-OneSecond)
+       at := Atime(preStat)
+       mt := preStat.ModTime()
+       err = Chtimes(f.Name(), at.Add(-time.Second), mt.Add(-time.Second))
        if err != nil {
                t.Fatalf("Chtimes %s: %s", f.Name(), err)
        }
 
-       postStat, err := Stat(f.Name())
+       st, err = Stat(f.Name())
        if err != nil {
                t.Fatalf("second Stat %s: %s", f.Name(), err)
        }
+       postStat := st.(*FileStat)
 
        /* Plan 9:
                Mtime is the time of the last change of content.  Similarly, atime is set whenever the
            contents are accessed; also, it is set whenever mtime is set.
        */
-       if postStat.Atime_ns >= preStat.Atime_ns && syscall.OS != "plan9" {
-               t.Errorf("Atime_ns didn't go backwards; was=%d, after=%d",
-                       preStat.Atime_ns,
-                       postStat.Atime_ns)
+       pat := Atime(postStat)
+       pmt := postStat.ModTime()
+       if !pat.Before(at) && syscall.OS != "plan9" {
+               t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat)
        }
 
-       if postStat.Mtime_ns >= preStat.Mtime_ns {
-               t.Errorf("Mtime_ns didn't go backwards; was=%d, after=%d",
-                       preStat.Mtime_ns,
-                       postStat.Mtime_ns)
+       if !pmt.Before(mt) {
+               t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt)
        }
 }
 
@@ -883,7 +825,7 @@ func TestOpenError(t *testing.T) {
                }
                perr, ok := err.(*PathError)
                if !ok {
-                       t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err)
+                       t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err)
                }
                if perr.Err != tt.error {
                        if syscall.OS == "plan9" {
@@ -899,6 +841,14 @@ func TestOpenError(t *testing.T) {
        }
 }
 
+func TestOpenNoName(t *testing.T) {
+       f, err := Open("")
+       if err == nil {
+               t.Fatal(`Open("") succeeded`)
+               f.Close()
+       }
+}
+
 func run(t *testing.T, cmd []string) string {
        // Run /bin/hostname and collect output.
        r, w, err := Pipe()
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
new file mode 100644 (file)
index 0000000..3109a81
--- /dev/null
@@ -0,0 +1,75 @@
+// 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.
+
+// +build darwin freebsd linux openbsd
+
+package os_test
+
+import (
+       . "os"
+       "syscall"
+       "testing"
+)
+
+func checkUidGid(t *testing.T, path string, uid, gid int) {
+       dir, err := Stat(path)
+       if err != nil {
+               t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
+       }
+       sys := dir.(*FileStat).Sys.(*syscall.Stat_t)
+       if int(sys.Uid) != uid {
+               t.Errorf("Stat %q: uid %d want %d", path, sys.Uid, uid)
+       }
+       if int(sys.Gid) != gid {
+               t.Errorf("Stat %q: gid %d want %d", path, sys.Gid, gid)
+       }
+}
+
+func TestChown(t *testing.T) {
+       // Chown is not supported under windows or Plan 9.
+       // Plan9 provides a native ChownPlan9 version instead.
+       if syscall.OS == "windows" || syscall.OS == "plan9" {
+               return
+       }
+       // Use TempDir() to make sure we're on a local file system,
+       // so that the group ids returned by Getgroups will be allowed
+       // on the file.  On NFS, the Getgroups groups are
+       // basically useless.
+       f := newFile("TestChown", t)
+       defer Remove(f.Name())
+       defer f.Close()
+       dir, err := f.Stat()
+       if err != nil {
+               t.Fatalf("stat %s: %s", f.Name(), err)
+       }
+
+       // Can't change uid unless root, but can try
+       // changing the group id.  First try our current group.
+       gid := Getgid()
+       t.Log("gid:", gid)
+       if err = Chown(f.Name(), -1, gid); err != nil {
+               t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
+       }
+       sys := dir.(*FileStat).Sys.(*syscall.Stat_t)
+       checkUidGid(t, f.Name(), int(sys.Uid), gid)
+
+       // Then try all the auxiliary groups.
+       groups, err := Getgroups()
+       if err != nil {
+               t.Fatalf("getgroups: %s", err)
+       }
+       t.Log("groups: ", groups)
+       for _, g := range groups {
+               if err = Chown(f.Name(), -1, g); err != nil {
+                       t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
+               }
+               checkUidGid(t, f.Name(), int(sys.Uid), g)
+
+               // change back to gid to test fd.Chown
+               if err = f.Chown(-1, gid); err != nil {
+                       t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
+               }
+               checkUidGid(t, f.Name(), int(sys.Uid), gid)
+       }
+}
index 82fade63ae245b8a8550fdec217761fb50cfd277..bc14a78318811d8d9e5678c0f1c6f5517b9209f6 100644 (file)
@@ -17,7 +17,7 @@ func MkdirAll(path string, perm uint32) error {
        // If path exists, stop with success or error.
        dir, err := Stat(path)
        if err == nil {
-               if dir.IsDirectory() {
+               if dir.IsDir() {
                        return nil
                }
                return &PathError{"mkdir", path, ENOTDIR}
@@ -48,7 +48,7 @@ func MkdirAll(path string, perm uint32) error {
                // Handle arguments like "foo/." by
                // double-checking that directory doesn't exist.
                dir, err1 := Lstat(path)
-               if err1 == nil && dir.IsDirectory() {
+               if err1 == nil && dir.IsDir() {
                        return nil
                }
                return err
@@ -75,7 +75,7 @@ func RemoveAll(path string) error {
                }
                return serr
        }
-       if !dir.IsDirectory() {
+       if !dir.IsDir() {
                // Not a directory; return the error from Remove.
                return err
        }
index 8eb4ab4391d5b66aac5276a8a73659c48a0bb60a..c664fc189b88cb2905952d81d113eeff64e426b6 100644 (file)
@@ -2,39 +2,55 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// AMD64, Linux
-
 package os
 
-import syscall "syscall"
+import (
+       "syscall"
+       "time"
+)
 
-func isSymlink(stat *syscall.Stat_t) bool {
-       return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK
+func sameFile(fs1, fs2 *FileStat) bool {
+       sys1 := fs1.Sys.(*syscall.Stat_t)
+       sys2 := fs2.Sys.(*syscall.Stat_t)
+       return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
 }
 
-func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo {
-       fi.Dev = uint64(stat.Dev)
-       fi.Ino = uint64(stat.Ino)
-       fi.Nlink = uint64(stat.Nlink)
-       fi.Mode = uint32(stat.Mode)
-       fi.Uid = int(stat.Uid)
-       fi.Gid = int(stat.Gid)
-       fi.Rdev = uint64(stat.Rdev)
-       fi.Size = int64(stat.Size)
-       fi.Blksize = int64(stat.Blksize)
-       fi.Blocks = int64(stat.Blocks)
-       fi.Atime_ns = int64(stat.Atime.Sec)*1e9 + int64(stat.Atime.Nsec)
-       fi.Mtime_ns = int64(stat.Mtime.Sec)*1e9 + int64(stat.Mtime.Nsec)
-       fi.Ctime_ns = int64(stat.Ctime.Sec)*1e9 + int64(stat.Ctime.Nsec)
-       for i := len(name)-1; i >= 0; i-- {
-               if name[i] == '/' {
-                       name = name[i+1:]
-                       break
-               }
+func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
+       fs := &FileStat{
+               name:    basename(name),
+               size:    int64(st.Size),
+               modTime: timespecToTime(st.Mtime),
+               Sys:     st,
+       }
+       fs.mode = FileMode(st.Mode & 0777)
+       switch st.Mode & syscall.S_IFMT {
+       case syscall.S_IFBLK, syscall.S_IFCHR:
+               fs.mode |= ModeDevice
+       case syscall.S_IFDIR:
+               fs.mode |= ModeDir
+       case syscall.S_IFIFO:
+               fs.mode |= ModeNamedPipe
+       case syscall.S_IFLNK:
+               fs.mode |= ModeSymlink
+       case syscall.S_IFREG:
+               // nothing to do
+       case syscall.S_IFSOCK:
+               fs.mode |= ModeSocket
        }
-       fi.Name = name
-       if isSymlink(lstat) && !isSymlink(stat) {
-               fi.FollowedSymlink = true
+       if st.Mode&syscall.S_ISGID != 0 {
+               fs.mode |= ModeSetgid
        }
-       return fi
+       if st.Mode&syscall.S_ISUID != 0 {
+               fs.mode |= ModeSetuid
+       }
+       return fs
+}
+
+func timespecToTime(ts syscall.Timespec) time.Time {
+       return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+}
+
+// For testing.
+func atime(fi FileInfo) time.Time {
+       return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atime)
 }
index 6d3a3813b0923b9545cc16c588dc23d28808179d..66189a6b9baaa323ae807eb5904c70a5d45b1d07 100644 (file)
@@ -4,29 +4,53 @@
 
 package os
 
-import "syscall"
+import (
+       "syscall"
+       "time"
+)
 
-func isSymlink(stat *syscall.Stat_t) bool {
-       return stat.Mode&syscall.S_IFMT == syscall.S_IFLNK
+func sameFile(fs1, fs2 *FileStat) bool {
+       sys1 := fs1.Sys.(*syscall.Stat_t)
+       sys2 := fs2.Sys.(*syscall.Stat_t)
+       return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
 }
 
-func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo {
-       fi.Dev = uint64(stat.Dev)
-       fi.Ino = uint64(stat.Ino)
-       fi.Nlink = uint64(stat.Nlink)
-       fi.Mode = uint32(stat.Mode)
-       fi.Uid = int(stat.Uid)
-       fi.Gid = int(stat.Gid)
-       fi.Rdev = uint64(stat.Rdev)
-       fi.Size = int64(stat.Size)
-       fi.Blksize = int64(stat.Blksize)
-       fi.Blocks = stat.Blocks
-       fi.Atime_ns = syscall.TimespecToNsec(stat.Atim)
-       fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtim)
-       fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctim)
-       fi.Name = basename(name)
-       if isSymlink(lstat) && !isSymlink(stat) {
-               fi.FollowedSymlink = true
+func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
+       fs := &FileStat{
+               name:    basename(name),
+               size:    int64(st.Size),
+               modTime: timespecToTime(st.Mtim),
+               Sys:     st,
        }
-       return fi
+       fs.mode = FileMode(st.Mode & 0777)
+       switch st.Mode & syscall.S_IFMT {
+       case syscall.S_IFBLK, syscall.S_IFCHR:
+               fs.mode |= ModeDevice
+       case syscall.S_IFDIR:
+               fs.mode |= ModeDir
+       case syscall.S_IFIFO:
+               fs.mode |= ModeNamedPipe
+       case syscall.S_IFLNK:
+               fs.mode |= ModeSymlink
+       case syscall.S_IFREG:
+               // nothing to do
+       case syscall.S_IFSOCK:
+               fs.mode |= ModeSocket
+       }
+       if st.Mode&syscall.S_ISGID != 0 {
+               fs.mode |= ModeSetgid
+       }
+       if st.Mode&syscall.S_ISUID != 0 {
+               fs.mode |= ModeSetuid
+       }
+       return fs
+}
+
+func timespecToTime(ts syscall.Timespec) time.Time {
+       return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+}
+
+// For testing.
+func atime(fi FileInfo) time.Time {
+       return timespecToTime(fi.(*FileStat).Sys.(*syscall.Stat_t).Atim)
 }
index df57b59a388033bfca492d6db7676f3f5e7685df..2638153ddbe1d1fa6ebad5f852d1cbcbdd61a938 100644 (file)
 
 package os
 
-import "syscall"
-
-// An operating-system independent representation of Unix data structures.
-// OS-specific routines in this directory convert the OS-local versions to these.
+import (
+       "syscall"
+       "time"
+)
 
 // Getpagesize returns the underlying system's memory page size.
 func Getpagesize() int { return syscall.Getpagesize() }
 
-// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat
-type FileInfo struct {
-       Dev             uint64 // device number of file system holding file.
-       Ino             uint64 // inode number.
-       Nlink           uint64 // number of hard links.
-       Mode            uint32 // permission and mode bits.
-       Uid             int    // user id of owner.
-       Gid             int    // group id of owner.
-       Rdev            uint64 // device type for special file.
-       Size            int64  // length in bytes.
-       Blksize         int64  // size of blocks, in bytes.
-       Blocks          int64  // number of blocks allocated for file.
-       Atime_ns        int64  // access time; nanoseconds since epoch.
-       Mtime_ns        int64  // modified time; nanoseconds since epoch.
-       Ctime_ns        int64  // status change time; nanoseconds since epoch.
-       Name            string // base name of the file name provided in Open, Stat, etc.
-       FollowedSymlink bool   // followed a symlink to get this information
+// A FileInfo describes a file and is returned by Stat and Lstat
+type FileInfo interface {
+       Name() string       // base name of the file
+       Size() int64        // length in bytes
+       Mode() FileMode     // file mode bits
+       ModTime() time.Time // modification time
+       IsDir() bool        // abbreviation for Mode().IsDir()
 }
 
-// IsFifo reports whether the FileInfo describes a FIFO file.
-func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO }
+// A FileMode represents a file's mode and permission bits.
+// The bits have the same definition on all systems, so that
+// information about files can be moved from one system
+// to another portably.  Not all bits apply to all systems.
+// The only required bit is ModeDir for directories.
+type FileMode uint32
 
-// IsChar reports whether the FileInfo describes a character special file.
-func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR }
+// The defined file mode bits are the most significant bits of the FileMode.
+// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
+const (
+       // The single letters are the abbreviations
+       // used by the String method's formatting.
+       ModeDir       FileMode = 1 << (32 - 1 - iota) // d: is a directory
+       ModeAppend                                    // a: append-only
+       ModeExclusive                                 // l: exclusive use
+       ModeTemporary                                 // t: temporary file (not backed up)
+       ModeSymlink                                   // L: symbolic link
+       ModeDevice                                    // D: device file
+       ModeNamedPipe                                 // p: named pipe (FIFO)
+       ModeSocket                                    // S: Unix domain socket
+       ModeSetuid                                    // u: setuid
+       ModeSetgid                                    // g: setgid
 
-// IsDirectory reports whether the FileInfo describes a directory.
-func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR }
+       // Mask for the type bits. For regular files, none will be set.
+       ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
 
-// IsBlock reports whether the FileInfo describes a block special file.
-func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK }
+       ModePerm FileMode = 0777 // permission bits
+)
 
-// IsRegular reports whether the FileInfo describes a regular file.
-func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG }
+func (m FileMode) String() string {
+       const str = "daltLDpSug"
+       var buf [20]byte
+       w := 0
+       for i, c := range str {
+               if m&(1<<uint(32-1-i)) != 0 {
+                       buf[w] = byte(c)
+                       w++
+               }
+       }
+       if w == 0 {
+               buf[w] = '-'
+               w++
+       }
+       const rwx = "rwxrwxrwx"
+       for i, c := range rwx {
+               if m&(1<<uint(9-1-i)) != 0 {
+                       buf[w] = byte(c)
+               } else {
+                       buf[w] = '-'
+               }
+               w++
+       }
+       return string(buf[:w])
+}
 
-// IsSymlink reports whether the FileInfo describes a symbolic link.
-func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK }
+// IsDir reports whether m describes a directory.
+// That is, it tests for the ModeDir bit being set in m.
+func (m FileMode) IsDir() bool {
+       return m&ModeDir != 0
+}
+
+// Perm returns the Unix permission bits in m.
+func (m FileMode) Perm() FileMode {
+       return m & ModePerm
+}
 
-// IsSocket reports whether the FileInfo describes a socket.
-func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK }
+// A FileStat is the implementation of FileInfo returned by Stat and Lstat.
+// Clients that need access to the underlying system-specific stat information
+// can test for *os.FileStat and then consult the Sys field.
+type FileStat struct {
+       name    string
+       size    int64
+       mode    FileMode
+       modTime time.Time
 
-// Permission returns the file permission bits.
-func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 }
+       Sys interface{}
+}
+
+func (fs *FileStat) Name() string       { return fs.name }
+func (fs *FileStat) Size() int64        { return fs.size }
+func (fs *FileStat) Mode() FileMode     { return fs.mode }
+func (fs *FileStat) ModTime() time.Time { return fs.modTime }
+func (fs *FileStat) IsDir() bool        { return fs.mode.IsDir() }
+
+// SameFile reports whether fs and other describe the same file.
+// For example, on Unix this means that the device and inode fields
+// of the two underlying structures are identical; on other systems
+// the decision may be based on the path names.
+func (fs *FileStat) SameFile(other *FileStat) bool {
+       return sameFile(fs, other)
+}
index 59f15e4c67551bbe67b3d4dbf4b34411f1c6a2ba..f9f44af8a93a9007c657b434e38310cf1dd1eb3b 100644 (file)
@@ -41,8 +41,8 @@ func TestLookup(t *testing.T) {
                t.Errorf("expected Uid of %d; got %d", e, g)
        }
        fi, err := os.Stat(u.HomeDir)
-       if err != nil || !fi.IsDirectory() {
-               t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDirectory=%v", u.HomeDir, err, fi.IsDirectory())
+       if err != nil || !fi.IsDir() {
+               t.Errorf("expected a valid HomeDir; stat(%q): err=%v, IsDir=%v", u.HomeDir, err, fi.IsDir())
        }
        if u.Username == "" {
                t.Fatalf("didn't get a username")
index 454eadececade674081026fddb4676b41e1612b0..5c233fbaebff6f6e66180d344d449a97d52f3541 100644 (file)
@@ -22,7 +22,7 @@ func gitSHA1(data []byte) []byte {
        h := sha1.New()
        fmt.Fprintf(h, "blob %d\x00", len(data))
        h.Write(data)
-       return h.Sum()
+       return h.Sum(nil)
 }
 
 // BUG(rsc): The Git binary delta format is not implemented, only Git binary literals.
index 8cf1f9ad1076c2610830edc9c4d9ac1b75f004d5..c3678f541d4631b13f61d7c000665922535a14bc 100644 (file)
@@ -260,7 +260,7 @@ func glob(dir, pattern string, matches []string) (m []string, e error) {
        if err != nil {
                return
        }
-       if !fi.IsDirectory() {
+       if !fi.IsDir() {
                return
        }
        d, err := os.Open(dir)
index 1b5d6c36495b142b5afbb0794b83c30a58eb1350..e3d6c342ca6878ca209f54a10f8d9c81bf0f3996 100644 (file)
@@ -223,7 +223,7 @@ func EvalSymlinks(path string) (string, error) {
                if err != nil {
                        return "", err
                }
-               if !fi.IsSymlink() {
+               if fi.Mode()&os.ModeSymlink == 0 {
                        b.WriteString(p)
                        if path != "" {
                                b.WriteRune(Separator)
@@ -312,7 +312,11 @@ func Rel(basepath, targpath string) (string, error) {
        if b0 != bl {
                // Base elements left. Must go up before going down.
                seps := strings.Count(base[b0:bl], string(Separator))
-               buf := make([]byte, 3+seps*3+tl-t0)
+               size := 2 + seps*3
+               if tl != t0 {
+                       size += 1 + tl - t0
+               }
+               buf := make([]byte, size)
                n := copy(buf, "..")
                for i := 0; i < seps; i++ {
                        buf[n] = Separator
@@ -341,19 +345,19 @@ var SkipDir = errors.New("skip this directory")
 // sole exception is that if path is a directory and the function returns the
 // special value SkipDir, the contents of the directory are skipped
 // and processing continues as usual on the next file.
-type WalkFunc func(path string, info *os.FileInfo, err error) error
+type WalkFunc func(path string, info os.FileInfo, err error) error
 
 // walk recursively descends path, calling w.
-func walk(path string, info *os.FileInfo, walkFn WalkFunc) error {
+func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
        err := walkFn(path, info, nil)
        if err != nil {
-               if info.IsDirectory() && err == SkipDir {
+               if info.IsDir() && err == SkipDir {
                        return nil
                }
                return err
        }
 
-       if !info.IsDirectory() {
+       if !info.IsDir() {
                return nil
        }
 
@@ -363,7 +367,7 @@ func walk(path string, info *os.FileInfo, walkFn WalkFunc) error {
        }
 
        for _, fileInfo := range list {
-               if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil {
+               if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
                        return err
                }
        }
@@ -386,7 +390,7 @@ func Walk(root string, walkFn WalkFunc) error {
 // readDir reads the directory named by dirname and returns
 // a sorted list of directory entries.
 // Copied from io/ioutil to avoid the circular import.
-func readDir(dirname string) ([]*os.FileInfo, error) {
+func readDir(dirname string) ([]os.FileInfo, error) {
        f, err := os.Open(dirname)
        if err != nil {
                return nil, err
@@ -396,20 +400,16 @@ func readDir(dirname string) ([]*os.FileInfo, error) {
        if err != nil {
                return nil, err
        }
-       fi := make(fileInfoList, len(list))
-       for i := range list {
-               fi[i] = &list[i]
-       }
-       sort.Sort(fi)
-       return fi, nil
+       sort.Sort(byName(list))
+       return list, nil
 }
 
-// A dirList implements sort.Interface.
-type fileInfoList []*os.FileInfo
+// byName implements sort.Interface.
+type byName []os.FileInfo
 
-func (f fileInfoList) Len() int           { return len(f) }
-func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name }
-func (f fileInfoList) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
+func (f byName) Len() int           { return len(f) }
+func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
+func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
 
 // Base returns the last element of path.
 // Trailing path separators are removed before extracting the last element.
index c81cbf0ddc75bcdd005eb2abbba7b58aae43523e..2bd62d34aeb5659a06fbef82a783d8a2085eb402 100644 (file)
@@ -317,7 +317,7 @@ func checkMarks(t *testing.T, report bool) {
 // Assumes that each node name is unique. Good enough for a test.
 // If clear is true, any incoming error is cleared before return. The errors
 // are always accumulated, though.
-func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool) error {
+func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
        if err != nil {
                *errors = append(*errors, err)
                if clear {
@@ -325,8 +325,9 @@ func mark(path string, info *os.FileInfo, err error, errors *[]error, clear bool
                }
                return err
        }
+       name := info.Name()
        walkTree(tree, tree.name, func(path string, n *Node) {
-               if n.name == info.Name {
+               if n.name == name {
                        n.mark++
                }
        })
@@ -337,7 +338,7 @@ func TestWalk(t *testing.T) {
        makeTree(t)
        errors := make([]error, 0, 10)
        clear := true
-       markFn := func(path string, info *os.FileInfo, err error) error {
+       markFn := func(path string, info os.FileInfo, err error) error {
                return mark(path, info, err, &errors, clear)
        }
        // Expect no errors.
@@ -548,7 +549,7 @@ func TestEvalSymlinks(t *testing.T) {
        // relative
        testEvalSymlinks(t, tests)
        // absolute
-/* These tests do not work in the gccgo test environment.
+       /* These tests do not work in the gccgo test environment.
        goroot, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
        if err != nil {
                t.Fatalf("EvalSymlinks(%q) error: %v", os.Getenv("GOROOT"), err)
@@ -564,7 +565,7 @@ func TestEvalSymlinks(t *testing.T) {
                }
        }
        testEvalSymlinks(t, tests)
-*/
+       */
 }
 
 /* These tests do not work in the gccgo test environment.
@@ -603,7 +604,7 @@ func TestAbs(t *testing.T) {
                        t.Errorf("Abs(%q) error: %v", path, err)
                }
                absinfo, err := os.Stat(abspath)
-               if err != nil || absinfo.Ino != info.Ino {
+               if err != nil || !absinfo.(*os.FileStat).SameFile(info.(*os.FileStat)) {
                        t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
                }
                if !filepath.IsAbs(abspath) {
@@ -634,6 +635,10 @@ var reltests = []RelTests{
        {"a/b/../c", "a/b", "../b"},
        {"a/b/c", "a/c/d", "../../c/d"},
        {"a/b", "c/d", "../../c/d"},
+       {"a/b/c/d", "a/b", "../.."},
+       {"a/b/c/d", "a/b/", "../.."},
+       {"a/b/c/d/", "a/b", "../.."},
+       {"a/b/c/d/", "a/b/", "../.."},
        {"../../a/b", "../../a/b/c/d", "c/d"},
        {"/a/b", "/a/b", "."},
        {"/a/b/.", "/a/b", "."},
@@ -645,6 +650,10 @@ var reltests = []RelTests{
        {"/a/b/../c", "/a/b", "../b"},
        {"/a/b/c", "/a/c/d", "../../c/d"},
        {"/a/b", "/c/d", "../../c/d"},
+       {"/a/b/c/d", "/a/b", "../.."},
+       {"/a/b/c/d", "/a/b/", "../.."},
+       {"/a/b/c/d/", "/a/b", "../.."},
+       {"/a/b/c/d/", "/a/b/", "../.."},
        {"/../../a/b", "/../../a/b/c/d", "c/d"},
        {".", "a/b", "a/b"},
        {".", "..", ".."},
index b4d920714ac4ef5662e6ffd22773023414e08a3e..53fdeadf97b42659e501e24bc504cee533d84c67 100644 (file)
@@ -64,7 +64,17 @@ func Count(s, sep string) int {
 
 // Contains returns true if substr is within s.
 func Contains(s, substr string) bool {
-       return Index(s, substr) != -1
+       return Index(s, substr) >= 0
+}
+
+// ContainsAny returns true if any Unicode code points in chars are within s.
+func ContainsAny(s, chars string) bool {
+       return IndexAny(s, chars) >= 0
+}
+
+// ContainsRune returns true if the Unicode code point r is within s.
+func ContainsRune(s string, r rune) bool {
+       return IndexRune(s, r) >= 0
 }
 
 // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
@@ -269,7 +279,7 @@ func FieldsFunc(s string, f func(rune) bool) []string {
                        fieldStart = i
                }
        }
-       if fieldStart != -1 { // Last field might end at EOF.
+       if fieldStart >= 0 { // Last field might end at EOF.
                a[na] = s[fieldStart:]
        }
        return a
@@ -512,7 +522,7 @@ func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
 }
 
 func makeCutsetFunc(cutset string) func(rune) bool {
-       return func(r rune) bool { return IndexRune(cutset, r) != -1 }
+       return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
 }
 
 // Trim returns a slice of the string s with all leading and
index 96207f5a2da6f995938dab15be67e5806023a4c2..957af67b2ba5d4aa4505bfc409a4e19cb1015394 100644 (file)
@@ -527,7 +527,7 @@ func TestTrim(t *testing.T) {
                case "TrimRight":
                        f = TrimRight
                default:
-                       t.Error("Undefined trim function %s", name)
+                       t.Errorf("Undefined trim function %s", name)
                }
                actual := f(tc.in, tc.cutset)
                if actual != tc.out {
@@ -908,6 +908,56 @@ func TestContains(t *testing.T) {
        }
 }
 
+var ContainsAnyTests = []struct {
+       str, substr string
+       expected    bool
+}{
+       {"", "", false},
+       {"", "a", false},
+       {"", "abc", false},
+       {"a", "", false},
+       {"a", "a", true},
+       {"aaa", "a", true},
+       {"abc", "xyz", false},
+       {"abc", "xcz", true},
+       {"a☺b☻c☹d", "uvw☻xyz", true},
+       {"aRegExp*", ".(|)*+?^$[]", true},
+       {dots + dots + dots, " ", false},
+}
+
+func TestContainsAny(t *testing.T) {
+       for _, ct := range ContainsAnyTests {
+               if ContainsAny(ct.str, ct.substr) != ct.expected {
+                       t.Errorf("ContainsAny(%s, %s) = %v, want %v",
+                               ct.str, ct.substr, !ct.expected, ct.expected)
+               }
+       }
+}
+
+var ContainsRuneTests = []struct {
+       str      string
+       r        rune
+       expected bool
+}{
+       {"", 'a', false},
+       {"a", 'a', true},
+       {"aaa", 'a', true},
+       {"abc", 'y', false},
+       {"abc", 'c', true},
+       {"a☺b☻c☹d", 'x', false},
+       {"a☺b☻c☹d", '☻', true},
+       {"aRegExp*", '*', true},
+}
+
+func TestContainsRune(t *testing.T) {
+       for _, ct := range ContainsRuneTests {
+               if ContainsRune(ct.str, ct.r) != ct.expected {
+                       t.Errorf("ContainsRune(%s, %s) = %v, want %v",
+                               ct.str, ct.r, !ct.expected, ct.expected)
+               }
+       }
+}
+
 var EqualFoldTests = []struct {
        s, t string
        out  bool
index 4f049a31f758d5c3ae1b8713050bbfce25e78f80..e81e5c5845c3ae901c9b5e3b74326dda2df46494 100644 (file)
@@ -27,17 +27,19 @@ type InternalBenchmark struct {
 type B struct {
        N         int
        benchmark InternalBenchmark
-       ns        int64
+       ns        time.Duration
        bytes     int64
-       start     int64
+       start     time.Time
+       timerOn   bool
 }
 
 // StartTimer starts timing a test.  This function is called automatically
 // before a benchmark starts, but it can also used to resume timing after
 // a call to StopTimer.
 func (b *B) StartTimer() {
-       if b.start == 0 {
-               b.start = time.Nanoseconds()
+       if !b.timerOn {
+               b.start = time.Now()
+               b.timerOn = true
        }
 }
 
@@ -45,17 +47,17 @@ func (b *B) StartTimer() {
 // while performing complex initialization that you don't
 // want to measure.
 func (b *B) StopTimer() {
-       if b.start > 0 {
-               b.ns += time.Nanoseconds() - b.start
+       if b.timerOn {
+               b.ns += time.Now().Sub(b.start)
+               b.timerOn = false
        }
-       b.start = 0
 }
 
 // ResetTimer sets the elapsed benchmark time to zero.
 // It does not affect whether the timer is running.
 func (b *B) ResetTimer() {
-       if b.start > 0 {
-               b.start = time.Nanoseconds()
+       if b.timerOn {
+               b.start = time.Now()
        }
        b.ns = 0
 }
@@ -68,7 +70,7 @@ func (b *B) nsPerOp() int64 {
        if b.N <= 0 {
                return 0
        }
-       return b.ns / int64(b.N)
+       return b.ns.Nanoseconds() / int64(b.N)
 }
 
 // runN runs a single benchmark for the specified number of iterations.
@@ -134,14 +136,14 @@ func (b *B) run() BenchmarkResult {
        n := 1
        b.runN(n)
        // Run the benchmark for at least the specified amount of time.
-       time := int64(*benchTime * 1e9)
-       for b.ns < time && n < 1e9 {
+       d := time.Duration(*benchTime * float64(time.Second))
+       for b.ns < d && n < 1e9 {
                last := n
                // Predict iterations/sec.
                if b.nsPerOp() == 0 {
                        n = 1e9
                } else {
-                       n = int(time / b.nsPerOp())
+                       n = int(d.Nanoseconds() / b.nsPerOp())
                }
                // Run more iterations than we think we'll need for a second (1.5x).
                // Don't grow too fast in case we had timing errors previously.
@@ -156,23 +158,23 @@ func (b *B) run() BenchmarkResult {
 
 // The results of a benchmark run.
 type BenchmarkResult struct {
-       N     int   // The number of iterations.
-       Ns    int64 // The total time taken.
-       Bytes int64 // Bytes processed in one iteration.
+       N     int           // The number of iterations.
+       T     time.Duration // The total time taken.
+       Bytes int64         // Bytes processed in one iteration.
 }
 
 func (r BenchmarkResult) NsPerOp() int64 {
        if r.N <= 0 {
                return 0
        }
-       return r.Ns / int64(r.N)
+       return r.T.Nanoseconds() / int64(r.N)
 }
 
 func (r BenchmarkResult) mbPerSec() float64 {
-       if r.Bytes <= 0 || r.Ns <= 0 || r.N <= 0 {
+       if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
                return 0
        }
-       return float64(r.Bytes) * float64(r.N) / float64(r.Ns) * 1e3
+       return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
 }
 
 func (r BenchmarkResult) String() string {
@@ -187,9 +189,9 @@ func (r BenchmarkResult) String() string {
                // The format specifiers here make sure that
                // the ones digits line up for all three possible formats.
                if nsop < 10 {
-                       ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N))
+                       ns = fmt.Sprintf("%13.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
                } else {
-                       ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N))
+                       ns = fmt.Sprintf("%12.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
                }
        }
        return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
index 3b026ee66e00787d78f58eb94c3f29a15b181023..e23f13b6f16794b5d01c94d14c887a30d93b5201 100644 (file)
@@ -56,9 +56,9 @@ func RunExamples(examples []InternalExample) (ok bool) {
                }()
 
                // run example
-               ns := -time.Nanoseconds()
+               t0 := time.Now()
                eg.F()
-               ns += time.Nanoseconds()
+               dt := time.Now().Sub(t0)
 
                // close pipe, restore stdout/stderr, get output
                w.Close()
@@ -66,7 +66,7 @@ func RunExamples(examples []InternalExample) (ok bool) {
                out := <-outC
 
                // report any errors
-               tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
+               tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
                if out != eg.Output {
                        fmt.Printf(
                                "--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
index 08443a31259d702db23e4ea04e4d3ec1eff88808..0b3a07108ccb68b21f8845015af394845ff2a10f 100644 (file)
@@ -111,12 +111,13 @@ func decorate(s string, addFileLine bool) string {
 // T is a type passed to Test functions to manage test state and support formatted test logs.
 // Logs are accumulated during execution and dumped to standard error when done.
 type T struct {
-       name          string    // Name of test.
-       errors        string    // Error string from test.
-       failed        bool      // Test has failed.
-       ch            chan *T   // Output for serial tests.
-       startParallel chan bool // Parallel tests will wait on this.
-       ns            int64     // Duration of test in nanoseconds.
+       name          string        // Name of test.
+       errors        string        // Error string from test.
+       failed        bool          // Test has failed.
+       ch            chan *T       // Output for serial tests.
+       startParallel chan bool     // Parallel tests will wait on this.
+       start         time.Time     // Time test started
+       dt            time.Duration // Length of test
 }
 
 // Fail marks the Test function as having failed but continues execution.
@@ -128,7 +129,7 @@ func (t *T) Failed() bool { return t.failed }
 // FailNow marks the Test function as having failed and stops its execution.
 // Execution will continue at the next Test.
 func (t *T) FailNow() {
-       t.ns = time.Nanoseconds() - t.ns
+       t.dt = time.Now().Sub(t.start)
        t.Fail()
        t.ch <- t
        runtime.Goexit()
@@ -184,9 +185,9 @@ type InternalTest struct {
 }
 
 func tRunner(t *T, test *InternalTest) {
-       t.ns = time.Nanoseconds()
+       t.start = time.Now()
        test.F(t)
-       t.ns = time.Nanoseconds() - t.ns
+       t.dt = time.Now().Sub(t.start)
        t.ch <- t
 }
 
@@ -211,7 +212,7 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
 }
 
 func report(t *T) {
-       tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9)
+       tstr := fmt.Sprintf("(%.2f seconds)", t.dt.Seconds())
        format := "--- %s: %s %s\n%s"
        if t.failed {
                fmt.Printf(format, "FAIL", t.name, tstr, t.errors)
index 42f9e560be148b733e542d21c88dbfa648f8322a..4208d53a0a47eabba94ea87820cb14db9f6767d1 100644 (file)
@@ -18,8 +18,7 @@ The input text for a template is UTF-8-encoded text in any format.
 "{{" and "}}"; all text outside actions is copied to the output unchanged.
 Actions may not span newlines, although comments can.
 
-Once constructed, templates and template sets can be executed safely in
-parallel.
+Once constructed, a template may be executed safely in parallel.
 
 Actions
 
@@ -221,10 +220,9 @@ All produce the quoted word "output":
 
 Functions
 
-During execution functions are found in three function maps: first in the
-template, then in the "template set" (described below), and finally in the
-global function map. By default, no functions are defined in the template or
-the set but the Funcs methods can be used to add them.
+During execution functions are found in two function maps: first in the
+template, then in the global function map. By default, no functions are defined
+in the template but the Funcs methods can be used to add them.
 
 Predefined global functions are named as follows.
 
@@ -265,49 +263,63 @@ Predefined global functions are named as follows.
 The boolean functions take any zero value to be false and a non-zero value to
 be true.
 
-Template sets
+Associated templates
 
-Each template is named by a string specified when it is created.  A template may
-use a template invocation to instantiate another template directly or by its
-name; see the explanation of the template action above. The name is looked up
-in the template set associated with the template.
+Each template is named by a string specified when it is created. Also, each
+template is associated with zero or more other templates that it may invoke by
+name; such associations are transitive and form a name space of templates.
 
-If no template invocation actions occur in the template, the issue of template
-sets can be ignored.  If it does contain invocations, though, the template
-containing the invocations must be part of a template set in which to look up
-the names.
+A template may use a template invocation to instantiate another associated
+template; see the explanation of the "template" action above. The name must be
+that of a template associated with the template that contains the invocation.
 
-There are two ways to construct template sets.
+Nested template definitions
 
-The first is to use a Set's Parse method to create a set of named templates from
-a single input defining multiple templates.  The syntax of the definitions is to
-surround each template declaration with a define and end action.
+When parsing a template, another template may be defined and associated with the
+template being parsed. Template definitions must appear at the top level of the
+template, much like global variables in a Go program.
+
+The syntax of such definitions is to surround each template declaration with a
+"define" and "end" action.
 
 The define action names the template being created by providing a string
-constant. Here is a simple example of input to Set.Parse:
+constant. Here is a simple example:
 
-       `{{define "T1"}} definition of template T1 {{end}}
-       {{define "T2"}} definition of template T2 {{end}}
-       {{define "T3"}} {{template "T1"}} {{template "T2"}} {{end}}`
+       `{{define "T1"}}ONE{{end}}
+       {{define "T2"}}TWO{{end}}
+       {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
+       {{template "T3"}}`
 
 This defines two templates, T1 and T2, and a third T3 that invokes the other two
-when it is executed.
+when it is executed. Finally it invokes T3. If executed this template will
+produce the text
+
+       ONE TWO
+
+By construction, a template may reside in only one association. If it's
+necessary to have a template addressable from multiple associations, the
+template definition must be parsed multiple times to create distinct *Template
+values.
+
+Parse may be called multiple times to assemble the various associated templates;
+see the ParseFiles and ParseGlob functions and methods for simple ways to parse
+related templates stored in files.
 
-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 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.
+A template may be executed directly or through ExecuteTemplate, which executes
+an associated template identified by name. To invoke our example above, we
+might write,
 
-Set.Parse may be called multiple times on different inputs to construct the set.
-Two sets may therefore be constructed with a common base set of templates plus,
-through a second Parse call each, specializations for some elements.
+       err := tmpl.Execute(os.Stdout, "no data needed")
+       if err != nil {
+               log.Fatalf("execution failed: %s", err)
+       }
 
-A template may be executed directly or through Set.Execute, which executes a
-named template from the set.  To invoke our example above, we might write,
+or to invoke a particular template explicitly by name,
 
-       err := set.Execute(os.Stdout, "T3", "no data needed")
+       err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
        if err != nil {
                log.Fatalf("execution failed: %s", err)
        }
+
 */
 package template
index 19108825d58d9558524b5b13c8e67f99158b3964..b74bc3b01c90847363d7f19e6d1b6a518d8a2256 100644 (file)
@@ -85,8 +85,18 @@ func errRecover(errp *error) {
        }
 }
 
+// ExecuteTemplate applies the template associated with t that has the given name
+// to the specified data object and writes the output to wr.
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
+       tmpl := t.tmpl[name]
+       if tmpl == nil {
+               return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
+       }
+       return tmpl.Execute(wr, data)
+}
+
 // Execute applies a parsed template to the specified data object,
-// writing the output to wr.
+// and writes the output to wr.
 func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
        defer errRecover(&err)
        value := reflect.ValueOf(data)
@@ -251,13 +261,9 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
 }
 
 func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
-       set := s.tmpl.set
-       if set == nil {
-               s.errorf("no set defined in which to invoke template named %q", t.Name)
-       }
-       tmpl := set.tmpl[t.Name]
+       tmpl := s.tmpl.tmpl[t.Name]
        if tmpl == nil {
-               s.errorf("template %q not in set", t.Name)
+               s.errorf("template %q not defined", t.Name)
        }
        // Variables declared by the pipeline persist.
        dot = s.evalPipeline(dot, t.Pipe)
@@ -376,7 +382,7 @@ func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args
 }
 
 func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value {
-       function, ok := findFunction(name, s.tmpl, s.tmpl.set)
+       function, ok := findFunction(name, s.tmpl)
        if !ok {
                s.errorf("%q is not a defined function", name)
        }
@@ -398,7 +404,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
        if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
                ptr = ptr.Addr()
        }
-       if method, ok := methodByName(ptr, fieldName); ok {
+       if method := ptr.MethodByName(fieldName); method.IsValid() {
                return s.evalCall(dot, method, fieldName, args, final)
        }
        hasArgs := len(args) > 1 || final.IsValid()
@@ -433,17 +439,6 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
        panic("not reached")
 }
 
-// TODO: delete when reflect's own MethodByName is released.
-func methodByName(receiver reflect.Value, name string) (reflect.Value, bool) {
-       typ := receiver.Type()
-       for i := 0; i < typ.NumMethod(); i++ {
-               if typ.Method(i).Name == name {
-                       return receiver.Method(i), true // This value includes the receiver.
-               }
-       }
-       return zero, false
-}
-
 var (
        errorType       = reflect.TypeOf((*error)(nil)).Elem()
        fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
index 67b9416cd7673b87de38de93a7792d3d5efd0350..cf3c41572816f578b98a539db57b85e8bc975d4e 100644 (file)
@@ -476,7 +476,7 @@ func vfunc(V, *V) string {
        return "vfunc"
 }
 
-func testExecute(execTests []execTest, set *Set, t *testing.T) {
+func testExecute(execTests []execTest, template *Template, t *testing.T) {
        b := new(bytes.Buffer)
        funcs := FuncMap{
                "count":    count,
@@ -486,12 +486,13 @@ func testExecute(execTests []execTest, set *Set, t *testing.T) {
                "zeroArgs": zeroArgs,
        }
        for _, test := range execTests {
-               tmpl := New(test.name).Funcs(funcs)
-               theSet := set
-               if theSet == nil {
-                       theSet = new(Set)
+               var tmpl *Template
+               var err error
+               if template == nil {
+                       tmpl, err = New(test.name).Funcs(funcs).Parse(test.input)
+               } else {
+                       tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input)
                }
-               _, err := tmpl.ParseInSet(test.input, theSet)
                if err != nil {
                        t.Errorf("%s: parse error: %s", test.name, err)
                        continue
@@ -663,24 +664,34 @@ func TestTree(t *testing.T) {
                        },
                },
        }
-       set := new(Set)
-       _, err := set.Delims("(", ")").Parse(treeTemplate)
+       tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate)
        if err != nil {
                t.Fatal("parse error:", err)
        }
        var b bytes.Buffer
-       err = set.Execute(&b, "tree", tree)
-       if err != nil {
-               t.Fatal("exec error:", err)
-       }
        stripSpace := func(r rune) rune {
                if r == '\t' || r == '\n' {
                        return -1
                }
                return r
        }
-       result := strings.Map(stripSpace, b.String())
        const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]"
+       // First by looking up the template.
+       err = tmpl.Lookup("tree").Execute(&b, tree)
+       if err != nil {
+               t.Fatal("exec error:", err)
+       }
+       result := strings.Map(stripSpace, b.String())
+       if result != expect {
+               t.Errorf("expected %q got %q", expect, result)
+       }
+       // Then direct to execution.
+       b.Reset()
+       err = tmpl.ExecuteTemplate(&b, "tree", tree)
+       if err != nil {
+               t.Fatal("exec error:", err)
+       }
+       result = strings.Map(stripSpace, b.String())
        if result != expect {
                t.Errorf("expected %q got %q", expect, result)
        }
index 2ca09a7c17f15c481e09a4fc0a9f8f87d330e0b5..d6e4bf1a216dd927f473e05a8740b089a912774c 100644 (file)
@@ -17,8 +17,9 @@ import (
 
 // FuncMap is the type of the map defining the mapping from names to functions.
 // Each function must have either a single return value, or two return values of
-// which the second has type error. If the second argument evaluates to non-nil
-// during execution, execution terminates and Execute returns an error.
+// which the second has type error. In that case, if the second (error)
+// argument evaluates to non-nil during execution, execution terminates and
+// Execute returns that error.
 type FuncMap map[string]interface{}
 
 var builtins = FuncMap{
@@ -78,18 +79,13 @@ func goodFunc(typ reflect.Type) bool {
        return false
 }
 
-// findFunction looks for a function in the template, set, and global map.
-func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
-       if tmpl != nil {
+// findFunction looks for a function in the template, and global map.
+func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
+       if tmpl != nil && tmpl.common != nil {
                if fn := tmpl.execFuncs[name]; fn.IsValid() {
                        return fn, true
                }
        }
-       if set != nil {
-               if fn := set.execFuncs[name]; fn.IsValid() {
-                       return fn, true
-               }
-       }
        if fn := builtinFuncs[name]; fn.IsValid() {
                return fn, true
        }
@@ -310,7 +306,6 @@ func JSEscape(w io.Writer, b []byte) {
                        if unicode.IsPrint(r) {
                                w.Write(b[i : i+size])
                        } else {
-                               // TODO(dsymonds): Do this without fmt?
                                fmt.Fprintf(w, "\\u%04X", r)
                        }
                        i += size - 1
index a743a8326ec255f6715d5e8b3e0b492ea6fd9fe9..3636fb54d697f635a9411e063f75c0a296482730 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Helper functions to make constructing templates and sets easier.
+// Helper functions to make constructing templates easier.
 
 package template
 
@@ -12,11 +12,11 @@ import (
        "path/filepath"
 )
 
-// Functions and methods to parse a single template.
+// Functions and methods to parse templates.
 
 // Must is a helper that wraps a call to a function returning (*Template, error)
-// and panics if the error is non-nil. It is intended for use in variable initializations
-// such as
+// and panics if the error is non-nil. It is intended for use in variable
+// initializations such as
 //     var t = template.Must(template.New("name").Parse("text"))
 func Must(t *Template, err error) *Template {
        if err != nil {
@@ -25,217 +25,84 @@ func Must(t *Template, err error) *Template {
        return t
 }
 
-// ParseFile creates a new Template and parses the template definition from
-// the named file.  The template name is the base name of the file.
-func ParseFile(filename string) (*Template, error) {
-       t := New(filepath.Base(filename))
-       return t.ParseFile(filename)
+// ParseFiles creates a new Template and parses the template definitions from
+// the named files. The returned template's name will have the (base) name and
+// (parsed) contents of the first file. There must be at least one file.
+// If an error occurs, parsing stops and the returned *Template is nil.
+func ParseFiles(filenames ...string) (*Template, error) {
+       return parseFiles(nil, filenames...)
 }
 
-// parseFileInSet creates a new Template and parses the template
-// definition from the named file. The template name is the base name
-// of the file. It also adds the template to the set. Function bindings are
-// checked against those in the set.
-func parseFileInSet(filename string, set *Set) (*Template, error) {
-       t := New(filepath.Base(filename))
-       return t.parseFileInSet(filename, set)
+// ParseFiles parses the named files and associates the resulting templates with
+// t. If an error occurs, parsing stops and the returned template is nil;
+// otherwise it is t. There must be at least one file.
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
+       return parseFiles(t, filenames...)
 }
 
-// ParseFile reads the template definition from a file and parses it to
-// construct an internal representation of the template for execution.
-// The returned template will be nil if an error occurs.
-func (t *Template) ParseFile(filename string) (*Template, error) {
-       b, err := ioutil.ReadFile(filename)
-       if err != nil {
-               return nil, err
-       }
-       return t.Parse(string(b))
-}
-
-// parseFileInSet is the same as ParseFile except that function bindings
-// are checked against those in the set and the template is added
-// to the set.
-// The returned template will be nil if an error occurs.
-func (t *Template) parseFileInSet(filename string, set *Set) (*Template, error) {
-       b, err := ioutil.ReadFile(filename)
-       if err != nil {
-               return nil, err
-       }
-       return t.ParseInSet(string(b), set)
-}
-
-// Functions and methods to parse a set.
-
-// SetMust is a helper that wraps a call to a function returning (*Set, error)
-// and panics if the error is non-nil. It is intended for use in variable initializations
-// such as
-//     var s = template.SetMust(template.ParseSetFiles("file"))
-func SetMust(s *Set, err error) *Set {
-       if err != nil {
-               panic(err)
+// parseFiles is the helper for the method and function. If the argument
+// template is nil, it is created from the first file.
+func parseFiles(t *Template, filenames ...string) (*Template, error) {
+       if len(filenames) == 0 {
+               // Not really a problem, but be consistent.
+               return nil, fmt.Errorf("template: no files named in call to ParseFiles")
        }
-       return s
-}
-
-// ParseFiles parses the named files into a set of named templates.
-// Each file must be parseable by itself.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseFiles(filenames ...string) (*Set, error) {
        for _, filename := range filenames {
                b, err := ioutil.ReadFile(filename)
                if err != nil {
                        return nil, err
                }
-               _, err = s.Parse(string(b))
-               if err != nil {
-                       return nil, err
+               s := string(b)
+               name := filepath.Base(filename)
+               // First template becomes return value if not already defined,
+               // and we use that one for subsequent New calls to associate
+               // all the templates together. Also, if this file has the same name
+               // as t, this file becomes the contents of t, so
+               //  t, err := New(name).Funcs(xxx).ParseFiles(name)
+               // works. Otherwise we create a new template associated with t.
+               var tmpl *Template
+               if t == nil {
+                       t = New(name)
                }
-       }
-       return s, nil
-}
-
-// ParseSetFiles creates a new Set and parses the set definition from the
-// named files. Each file must be individually parseable.
-func ParseSetFiles(filenames ...string) (*Set, error) {
-       s := new(Set)
-       for _, filename := range filenames {
-               b, err := ioutil.ReadFile(filename)
-               if err != nil {
-                       return nil, err
+               if name == t.Name() {
+                       tmpl = t
+               } else {
+                       tmpl = t.New(name)
                }
-               _, err = s.Parse(string(b))
+               _, err = tmpl.Parse(s)
                if err != nil {
                        return nil, err
                }
        }
-       return s, nil
+       return t, nil
 }
 
-// ParseGlob parses the set definition from the files identified by the
-// pattern.  The pattern is processed by filepath.Glob and must match at
-// least one file.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseGlob(pattern string) (*Set, error) {
-       filenames, err := filepath.Glob(pattern)
-       if err != nil {
-               return nil, err
-       }
-       if len(filenames) == 0 {
-               return nil, fmt.Errorf("pattern matches no files: %#q", pattern)
-       }
-       return s.ParseFiles(filenames...)
+// ParseGlob creates a new Template and parses the template definitions from the
+// files identified by the pattern, which must match at least one file. The
+// returned template will have the (base) name and (parsed) contents of the
+// first file matched by the pattern. ParseGlob is equivalent to calling
+// ParseFiles with the list of files matched by the pattern.
+func ParseGlob(pattern string) (*Template, error) {
+       return parseGlob(nil, pattern)
 }
 
-// ParseSetGlob creates a new Set and parses the set definition from the
-// files identified by the pattern. The pattern is processed by filepath.Glob
-// and must match at least one file.
-func ParseSetGlob(pattern string) (*Set, error) {
-       set, err := new(Set).ParseGlob(pattern)
-       if err != nil {
-               return nil, err
-       }
-       return set, nil
+// ParseGlob parses the template definitions in the files identified by the
+// pattern and associates the resulting templates with t. The pattern is
+// processed by filepath.Glob and must match at least one file. ParseGlob is
+// equivalent to calling t.ParseFiles with the list of files matched by the
+// pattern.
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
+       return parseGlob(t, pattern)
 }
 
-// Functions and methods to parse stand-alone template files into a set.
-
-// ParseTemplateFiles parses the named template files and adds
-// them to the set. Each template will be named the base name of
-// its file.
-// Unlike with ParseFiles, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateFiles is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseTemplateFiles(filenames ...string) (*Set, error) {
-       for _, filename := range filenames {
-               _, err := parseFileInSet(filename, s)
-               if err != nil {
-                       return nil, err
-               }
-       }
-       return s, nil
-}
-
-// ParseTemplateGlob parses the template files matched by the
-// patern and adds them to the set. Each template will be named
-// the base name of its file.
-// Unlike with ParseGlob, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateGlob is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself.
-// If an error occurs, parsing stops and the returned set is nil.
-func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) {
-       filenames, err := filepath.Glob(pattern)
-       if err != nil {
-               return nil, err
-       }
-       for _, filename := range filenames {
-               _, err := parseFileInSet(filename, s)
-               if err != nil {
-                       return nil, err
-               }
-       }
-       return s, nil
-}
-
-// ParseTemplateFiles creates a set by parsing the named files,
-// each of which defines a single template. Each template will be
-// named the base name of its file.
-// Unlike with ParseFiles, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateFiles is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself. Parsing stops if an error is
-// encountered.
-func ParseTemplateFiles(filenames ...string) (*Set, error) {
-       set := new(Set)
-       set.init()
-       for _, filename := range filenames {
-               t, err := ParseFile(filename)
-               if err != nil {
-                       return nil, err
-               }
-               if err := set.add(t); err != nil {
-                       return nil, err
-               }
-       }
-       return set, nil
-}
-
-// ParseTemplateGlob creates a set by parsing the files matched
-// by the pattern, each of which defines a single template. The pattern
-// is processed by filepath.Glob and must match at least one file. Each
-// template will be named the base name of its file.
-// Unlike with ParseGlob, each file should be a stand-alone template
-// definition suitable for Template.Parse (not Set.Parse); that is, the
-// file does not contain {{define}} clauses. ParseTemplateGlob is
-// therefore equivalent to calling the ParseFile function to create
-// individual templates, which are then added to the set.
-// Each file must be parseable by itself. Parsing stops if an error is
-// encountered.
-func ParseTemplateGlob(pattern string) (*Set, error) {
-       set := new(Set)
+// parseGlob is the implementation of the function and method ParseGlob.
+func parseGlob(t *Template, pattern string) (*Template, error) {
        filenames, err := filepath.Glob(pattern)
        if err != nil {
                return nil, err
        }
        if len(filenames) == 0 {
-               return nil, fmt.Errorf("pattern matches no files: %#q", pattern)
-       }
-       for _, filename := range filenames {
-               t, err := ParseFile(filename)
-               if err != nil {
-                       return nil, err
-               }
-               if err := set.add(t); err != nil {
-                       return nil, err
-               }
+               return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
        }
-       return set, nil
+       return parseFiles(t, filenames...)
 }
diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go
new file mode 100644 (file)
index 0000000..7b35d26
--- /dev/null
@@ -0,0 +1,288 @@
+// 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 template
+
+// Tests for mulitple-template parsing and execution.
+
+import (
+       "bytes"
+       "fmt"
+       "testing"
+       "text/template/parse"
+)
+
+type isEmptyTest struct {
+       name  string
+       input string
+       empty bool
+}
+
+var isEmptyTests = []isEmptyTest{
+       {"empty", ``, true},
+       {"nonempty", `hello`, false},
+       {"spaces only", " \t\n \t\n", true},
+       {"definition", `{{define "x"}}something{{end}}`, true},
+       {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
+       {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n}}", false},
+       {"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false},
+}
+
+func TestIsEmpty(t *testing.T) {
+       for _, test := range isEmptyTests {
+               template, err := New("root").Parse(test.input)
+               if err != nil {
+                       t.Errorf("%q: unexpected error: %v", test.name, err)
+                       continue
+               }
+               if empty := isEmpty(template.Root); empty != test.empty {
+                       t.Errorf("%q: expected %t got %t", test.name, test.empty, empty)
+               }
+       }
+}
+
+const (
+       noError  = true
+       hasError = false
+)
+
+type multiParseTest struct {
+       name    string
+       input   string
+       ok      bool
+       names   []string
+       results []string
+}
+
+var multiParseTests = []multiParseTest{
+       {"empty", "", noError,
+               nil,
+               nil},
+       {"one", `{{define "foo"}} FOO {{end}}`, noError,
+               []string{"foo"},
+               []string{`[(text: " FOO ")]`}},
+       {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
+               []string{"foo", "bar"},
+               []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}},
+       // errors
+       {"missing end", `{{define "foo"}} FOO `, hasError,
+               nil,
+               nil},
+       {"malformed name", `{{define "foo}} FOO `, hasError,
+               nil,
+               nil},
+}
+
+func TestMultiParse(t *testing.T) {
+       for _, test := range multiParseTests {
+               template, err := New("root").Parse(test.input)
+               switch {
+               case err == nil && !test.ok:
+                       t.Errorf("%q: expected error; got none", test.name)
+                       continue
+               case err != nil && test.ok:
+                       t.Errorf("%q: unexpected error: %v", test.name, err)
+                       continue
+               case err != nil && !test.ok:
+                       // expected error, got one
+                       if *debug {
+                               fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
+                       }
+                       continue
+               }
+               if template == nil {
+                       continue
+               }
+               if len(template.tmpl) != len(test.names)+1 { // +1 for root
+                       t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl))
+                       continue
+               }
+               for i, name := range test.names {
+                       tmpl, ok := template.tmpl[name]
+                       if !ok {
+                               t.Errorf("%s: can't find template %q", test.name, name)
+                               continue
+                       }
+                       result := tmpl.Root.String()
+                       if result != test.results[i] {
+                               t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
+                       }
+               }
+       }
+}
+
+var multiExecTests = []execTest{
+       {"empty", "", "", nil, true},
+       {"text", "some text", "some text", nil, true},
+       {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
+       {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
+       {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
+       {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
+       {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
+       {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
+       {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
+
+       // User-defined function: test argument evaluator.
+       {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
+       {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
+}
+
+// These strings are also in testdata/*.
+const multiText1 = `
+       {{define "x"}}TEXT{{end}}
+       {{define "dotV"}}{{.V}}{{end}}
+`
+
+const multiText2 = `
+       {{define "dot"}}{{.}}{{end}}
+       {{define "nested"}}{{template "dot" .}}{{end}}
+`
+
+func TestMultiExecute(t *testing.T) {
+       // Declare a couple of templates first.
+       template, err := New("root").Parse(multiText1)
+       if err != nil {
+               t.Fatalf("parse error for 1: %s", err)
+       }
+       _, err = template.Parse(multiText2)
+       if err != nil {
+               t.Fatalf("parse error for 2: %s", err)
+       }
+       testExecute(multiExecTests, template, t)
+}
+
+func TestParseFiles(t *testing.T) {
+       _, err := ParseFiles("DOES NOT EXIST")
+       if err == nil {
+               t.Error("expected error for non-existent file; got none")
+       }
+       template := New("root")
+       _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
+       if err != nil {
+               t.Fatalf("error parsing files: %v", err)
+       }
+       testExecute(multiExecTests, template, t)
+}
+
+func TestParseGlob(t *testing.T) {
+       _, err := ParseGlob("DOES NOT EXIST")
+       if err == nil {
+               t.Error("expected error for non-existent file; got none")
+       }
+       _, err = New("error").ParseGlob("[x")
+       if err == nil {
+               t.Error("expected error for bad pattern; got none")
+       }
+       template := New("root")
+       _, err = template.ParseGlob("testdata/file*.tmpl")
+       if err != nil {
+               t.Fatalf("error parsing files: %v", err)
+       }
+       testExecute(multiExecTests, template, t)
+}
+
+// In these tests, actual content (not just template definitions) comes from the parsed files.
+
+var templateFileExecTests = []execTest{
+       {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
+}
+
+func TestParseFilesWithData(t *testing.T) {
+       template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
+       if err != nil {
+               t.Fatalf("error parsing files: %v", err)
+       }
+       testExecute(templateFileExecTests, template, t)
+}
+
+func TestParseGlobWithData(t *testing.T) {
+       template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
+       if err != nil {
+               t.Fatalf("error parsing files: %v", err)
+       }
+       testExecute(templateFileExecTests, template, t)
+}
+
+const (
+       cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
+       cloneText2 = `{{define "b"}}b{{end}}`
+       cloneText3 = `{{define "c"}}root{{end}}`
+       cloneText4 = `{{define "c"}}clone{{end}}`
+)
+
+func TestClone(t *testing.T) {
+       // Create some templates and clone the root.
+       root, err := New("root").Parse(cloneText1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = root.Parse(cloneText2)
+       if err != nil {
+               t.Fatal(err)
+       }
+       clone := root.Clone()
+       // Add variants to both.
+       _, err = root.Parse(cloneText3)
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = clone.Parse(cloneText4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       // Verify that the clone is self-consistent.
+       for k, v := range clone.tmpl {
+               if k == clone.name && v.tmpl[k] != clone {
+                       t.Error("clone does not contain root")
+               }
+               if v != v.tmpl[v.name] {
+                       t.Errorf("clone does not contain self for %q", k)
+               }
+       }
+       // Execute root.
+       var b bytes.Buffer
+       err = root.ExecuteTemplate(&b, "a", 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if b.String() != "broot" {
+               t.Errorf("expected %q got %q", "broot", b.String())
+       }
+       // Execute copy.
+       b.Reset()
+       err = clone.ExecuteTemplate(&b, "a", 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if b.String() != "bclone" {
+               t.Errorf("expected %q got %q", "bclone", b.String())
+       }
+}
+
+func TestAddParseTree(t *testing.T) {
+       // Create some templates.
+       root, err := New("root").Parse(cloneText1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = root.Parse(cloneText2)
+       if err != nil {
+               t.Fatal(err)
+       }
+       // Add a new parse tree.
+       tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins)
+       if err != nil {
+               t.Fatal(err)
+       }
+       added, err := root.AddParseTree("c", tree["c"])
+       // Execute.
+       var b bytes.Buffer
+       err = added.ExecuteTemplate(&b, "a", 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if b.String() != "broot" {
+               t.Errorf("expected %q got %q", "broot", b.String())
+       }
+}
diff --git a/libgo/go/text/template/parse.go b/libgo/go/text/template/parse.go
deleted file mode 100644 (file)
index 7075f2a..0000000
+++ /dev/null
@@ -1,83 +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 template
-
-import (
-       "reflect"
-       "text/template/parse"
-)
-
-// Template is the representation of a parsed template.
-type Template struct {
-       name string
-       *parse.Tree
-       leftDelim  string
-       rightDelim string
-       // We use two maps, one for parsing and one for execution.
-       // This separation makes the API cleaner since it doesn't
-       // expose reflection to the client.
-       parseFuncs FuncMap
-       execFuncs  map[string]reflect.Value
-       set        *Set // can be nil.
-}
-
-// Name returns the name of the template.
-func (t *Template) Name() string {
-       return t.name
-}
-
-// Parsing.
-
-// New allocates a new template with the given name.
-func New(name string) *Template {
-       return &Template{
-               name:       name,
-               parseFuncs: make(FuncMap),
-               execFuncs:  make(map[string]reflect.Value),
-       }
-}
-
-// Delims sets the action delimiters, to be used in a subsequent
-// parse, to the specified strings.
-// An empty delimiter stands for the corresponding default: {{ or }}.
-// The return value is the template, so calls can be chained.
-func (t *Template) Delims(left, right string) *Template {
-       t.leftDelim = left
-       t.rightDelim = right
-       return t
-}
-
-// Funcs adds the elements of the argument map to the template's function
-// map.  It panics if a value in the map is not a function with appropriate
-// return type.
-// The return value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap FuncMap) *Template {
-       addValueFuncs(t.execFuncs, funcMap)
-       addFuncs(t.parseFuncs, funcMap)
-       return t
-}
-
-// Parse parses the template definition string to construct an internal
-// representation of the template for execution.
-func (t *Template) Parse(s string) (tmpl *Template, err error) {
-       t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, nil, t.parseFuncs, builtins)
-       if err != nil {
-               return nil, err
-       }
-       return t, nil
-}
-
-// ParseInSet parses the template definition string to construct an internal
-// representation of the template for execution. It also adds the template
-// to the set, which must not be nil. It is an error if s is already defined in the set.
-// Function bindings are checked against those in the set.
-func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err error) {
-       t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, set.trees, t.parseFuncs, set.parseFuncs, builtins)
-       if err != nil {
-               return nil, err
-       }
-       err = set.add(t)
-       return t, err
-}
index e906ee83aacb5a14b38ec8bbaf72eaba12ebf0d2..346f613b048398e8bda870c62a6998a73085c45b 100644 (file)
@@ -13,10 +13,10 @@ import (
        "unicode"
 )
 
-// Tree is the representation of a parsed template.
+// Tree is the representation of a single parsed template.
 type Tree struct {
-       Name string    // Name is the name of the template.
-       Root *ListNode // Root is the top-level root of the parse tree.
+       Name string    // name of the template represented by the tree.
+       Root *ListNode // top-level root of the tree.
        // Parsing only; cleared after parse.
        funcs     []map[string]interface{}
        lex       *lexer
@@ -25,6 +25,16 @@ type Tree struct {
        vars      []string // variables defined at the moment.
 }
 
+// Parse returns a map from template name to parse.Tree, created by parsing the
+// templates described in the argument string. The top-level template will be
+// given the specified name. If an error is encountered, parsing stops and an
+// empty map is returned with the error.
+func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
+       treeSet = make(map[string]*Tree)
+       _, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...)
+       return
+}
+
 // next returns the next token.
 func (t *Tree) next() item {
        if t.peekCount > 0 {
@@ -58,7 +68,7 @@ func (t *Tree) peek() item {
 
 // Parsing.
 
-// New allocates a new template with the given name.
+// New allocates a new parse tree with the given name.
 func New(name string, funcs ...map[string]interface{}) *Tree {
        return &Tree{
                Name:  name,
@@ -87,6 +97,15 @@ func (t *Tree) expect(expected itemType, context string) item {
        return token
 }
 
+// expectEither consumes the next token and guarantees it has one of the required types.
+func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
+       token := t.next()
+       if token.typ != expected1 && token.typ != expected2 {
+               t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token)
+       }
+       return token
+}
+
 // unexpected complains about the token and terminates processing.
 func (t *Tree) unexpected(token item, context string) {
        t.errorf("unexpected %s in %s", token, context)
@@ -107,7 +126,7 @@ func (t *Tree) recover(errp *error) {
        return
 }
 
-// startParse starts the template parsing from the lexer.
+// startParse initializes the parser, using the lexer.
 func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
        t.Root = nil
        t.lex = lex
@@ -143,17 +162,27 @@ func (t *Tree) atEOF() bool {
        return false
 }
 
-// Parse parses the template definition string to construct an internal
-// representation of the template for execution. If either action delimiter
-// string is empty, the default ("{{" or "}}") is used.
+// Parse parses the template definition string to construct a representation of
+// the template for execution. If either action delimiter string is empty, the
+// default ("{{" or "}}") is used. Embedded template definitions are added to
+// the treeSet map.
 func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
        defer t.recover(&err)
        t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
        t.parse(treeSet)
+       t.add(treeSet)
        t.stopParse()
        return t, nil
 }
 
+// add adds tree to the treeSet.
+func (t *Tree) add(treeSet map[string]*Tree) {
+       if _, present := treeSet[t.Name]; present {
+               t.errorf("template: multiple definition of template %q", t.Name)
+       }
+       treeSet[t.Name] = t
+}
+
 // parse is the top-level parser for a template, essentially the same
 // as itemList except it also parses {{define}} actions.
 // It runs to EOF.
@@ -163,7 +192,7 @@ func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
                if t.peek().typ == itemLeftDelim {
                        delim := t.next()
                        if t.next().typ == itemDefine {
-                               newT := New("new definition") // name will be updated once we know it.
+                               newT := New("definition") // name will be updated once we know it.
                                newT.startParse(t.funcs, t.lex)
                                newT.parseDefinition(treeSet)
                                continue
@@ -183,11 +212,8 @@ func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
 // installs the definition in the treeSet map.  The "define" keyword has already
 // been scanned.
 func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
-       if treeSet == nil {
-               t.errorf("no set specified for template definition")
-       }
        const context = "define clause"
-       name := t.expect(itemString, context)
+       name := t.expectOneOf(itemString, itemRawString, context)
        var err error
        t.Name, err = strconv.Unquote(name.val)
        if err != nil {
@@ -200,10 +226,7 @@ func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
                t.errorf("unexpected %s in %s", end, context)
        }
        t.stopParse()
-       if _, present := treeSet[t.Name]; present {
-               t.errorf("template: %q multiply defined", name)
-       }
-       treeSet[t.Name] = t
+       t.add(treeSet)
 }
 
 // itemList:
index 5c10086cc7c2cf765944658a6e935f3541ac9ae2..fc93455ecbc1fbc1781356745ba857e478d82dec 100644 (file)
@@ -236,7 +236,7 @@ var builtins = map[string]interface{}{
 
 func TestParse(t *testing.T) {
        for _, test := range parseTests {
-               tmpl, err := New(test.name).Parse(test.input, "", "", nil, builtins)
+               tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
                switch {
                case err == nil && !test.ok:
                        t.Errorf("%q: expected error; got none", test.name)
diff --git a/libgo/go/text/template/parse/set.go b/libgo/go/text/template/parse/set.go
deleted file mode 100644 (file)
index 55f3ceb..0000000
+++ /dev/null
@@ -1,15 +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 parse
-
-// Set returns a slice of Trees created by parsing the template set
-// definition in the argument string. If an error is encountered,
-// parsing stops and an empty slice is returned with the error.
-func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err error) {
-       tree = make(map[string]*Tree)
-       // Top-level template name is needed but unused. TODO: clean this up.
-       _, err = New("ROOT").Parse(text, leftDelim, rightDelim, tree, funcs...)
-       return
-}
diff --git a/libgo/go/text/template/set.go b/libgo/go/text/template/set.go
deleted file mode 100644 (file)
index 4841704..0000000
+++ /dev/null
@@ -1,121 +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 template
-
-import (
-       "fmt"
-       "io"
-       "reflect"
-       "text/template/parse"
-)
-
-// Set holds a set of related templates that can refer to one another by name.
-// The zero value represents an empty set.
-// A template may be a member of multiple sets.
-type Set struct {
-       tmpl       map[string]*Template
-       trees      map[string]*parse.Tree // maintained by parse package
-       leftDelim  string
-       rightDelim string
-       parseFuncs FuncMap
-       execFuncs  map[string]reflect.Value
-}
-
-func (s *Set) init() {
-       if s.tmpl == nil {
-               s.tmpl = make(map[string]*Template)
-               s.parseFuncs = make(FuncMap)
-               s.execFuncs = make(map[string]reflect.Value)
-       }
-}
-
-// Delims sets the action delimiters, to be used in a subsequent
-// parse, to the specified strings.
-// An empty delimiter stands for the corresponding default: {{ or }}.
-// The return value is the set, so calls can be chained.
-func (s *Set) Delims(left, right string) *Set {
-       s.leftDelim = left
-       s.rightDelim = right
-       return s
-}
-
-// Funcs adds the elements of the argument map to the set's function map.  It
-// panics if a value in the map is not a function with appropriate return
-// type.
-// The return value is the set, so calls can be chained.
-func (s *Set) Funcs(funcMap FuncMap) *Set {
-       s.init()
-       addValueFuncs(s.execFuncs, funcMap)
-       addFuncs(s.parseFuncs, funcMap)
-       return s
-}
-
-// Add adds the argument templates to the set. It panics if two templates
-// with the same name are added or if a template is already a member of
-// a set.
-// The return value is the set, so calls can be chained.
-func (s *Set) Add(templates ...*Template) *Set {
-       for _, t := range templates {
-               if err := s.add(t); err != nil {
-                       panic(err)
-               }
-       }
-       return s
-}
-
-// add adds the argument template to the set.
-func (s *Set) add(t *Template) error {
-       s.init()
-       if t.set != nil {
-               return fmt.Errorf("template: %q already in a set", t.name)
-       }
-       if _, ok := s.tmpl[t.name]; ok {
-               return fmt.Errorf("template: %q already defined in set", t.name)
-       }
-       s.tmpl[t.name] = t
-       t.set = s
-       return nil
-}
-
-// Template returns the template with the given name in the set,
-// or nil if there is no such template.
-func (s *Set) Template(name string) *Template {
-       return s.tmpl[name]
-}
-
-// FuncMap returns the set's function map.
-func (s *Set) FuncMap() FuncMap {
-       return s.parseFuncs
-}
-
-// Execute applies the named template to the specified data object, writing
-// the output to wr.
-func (s *Set) Execute(wr io.Writer, name string, data interface{}) error {
-       tmpl := s.tmpl[name]
-       if tmpl == nil {
-               return fmt.Errorf("template: no template %q in set", name)
-       }
-       return tmpl.Execute(wr, data)
-}
-
-// Parse parses a string into a set of named templates.  Parse may be called
-// multiple times for a given set, adding the templates defined in the string
-// to the set.  It is an error if a template has a name already defined in the set.
-func (s *Set) Parse(text string) (*Set, error) {
-       trees, err := parse.Set(text, s.leftDelim, s.rightDelim, s.parseFuncs, builtins)
-       if err != nil {
-               return nil, err
-       }
-       s.init()
-       for name, tree := range trees {
-               tmpl := New(name)
-               tmpl.Tree = tree
-               err = s.add(tmpl)
-               if err != nil {
-                       return s, err
-               }
-       }
-       return s, nil
-}
diff --git a/libgo/go/text/template/set_test.go b/libgo/go/text/template/set_test.go
deleted file mode 100644 (file)
index f437bc7..0000000
+++ /dev/null
@@ -1,239 +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 template
-
-import (
-       "fmt"
-       "testing"
-)
-
-const (
-       noError  = true
-       hasError = false
-)
-
-type setParseTest struct {
-       name    string
-       input   string
-       ok      bool
-       names   []string
-       results []string
-}
-
-var setParseTests = []setParseTest{
-       {"empty", "", noError,
-               nil,
-               nil},
-       {"one", `{{define "foo"}} FOO {{end}}`, noError,
-               []string{"foo"},
-               []string{`[(text: " FOO ")]`}},
-       {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
-               []string{"foo", "bar"},
-               []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}},
-       // errors
-       {"missing end", `{{define "foo"}} FOO `, hasError,
-               nil,
-               nil},
-       {"malformed name", `{{define "foo}} FOO `, hasError,
-               nil,
-               nil},
-}
-
-func TestSetParse(t *testing.T) {
-       for _, test := range setParseTests {
-               set, err := new(Set).Parse(test.input)
-               switch {
-               case err == nil && !test.ok:
-                       t.Errorf("%q: expected error; got none", test.name)
-                       continue
-               case err != nil && test.ok:
-                       t.Errorf("%q: unexpected error: %v", test.name, err)
-                       continue
-               case err != nil && !test.ok:
-                       // expected error, got one
-                       if *debug {
-                               fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
-                       }
-                       continue
-               }
-               if set == nil {
-                       continue
-               }
-               if len(set.tmpl) != len(test.names) {
-                       t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl))
-                       continue
-               }
-               for i, name := range test.names {
-                       tmpl, ok := set.tmpl[name]
-                       if !ok {
-                               t.Errorf("%s: can't find template %q", test.name, name)
-                               continue
-                       }
-                       result := tmpl.Root.String()
-                       if result != test.results[i] {
-                               t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
-                       }
-               }
-       }
-}
-
-var setExecTests = []execTest{
-       {"empty", "", "", nil, true},
-       {"text", "some text", "some text", nil, true},
-       {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
-       {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
-       {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
-       {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
-       {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
-       {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
-       {"variable declared by template", `{{template "nested" $x=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
-
-       // User-defined function: test argument evaluator.
-       {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
-       {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
-}
-
-// These strings are also in testdata/*.
-const setText1 = `
-       {{define "x"}}TEXT{{end}}
-       {{define "dotV"}}{{.V}}{{end}}
-`
-
-const setText2 = `
-       {{define "dot"}}{{.}}{{end}}
-       {{define "nested"}}{{template "dot" .}}{{end}}
-`
-
-func TestSetExecute(t *testing.T) {
-       // Declare a set with a couple of templates first.
-       set := new(Set)
-       _, err := set.Parse(setText1)
-       if err != nil {
-               t.Fatalf("error parsing set: %s", err)
-       }
-       _, err = set.Parse(setText2)
-       if err != nil {
-               t.Fatalf("error parsing set: %s", err)
-       }
-       testExecute(setExecTests, set, t)
-}
-
-func TestSetParseFiles(t *testing.T) {
-       set := new(Set)
-       _, err := set.ParseFiles("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       _, err = set.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(setExecTests, set, t)
-}
-
-func TestParseSetFiles(t *testing.T) {
-       set := new(Set)
-       _, err := ParseSetFiles("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       set, err = ParseSetFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(setExecTests, set, t)
-}
-
-func TestSetParseGlob(t *testing.T) {
-       _, err := new(Set).ParseGlob("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       _, err = new(Set).ParseGlob("[x")
-       if err == nil {
-               t.Error("expected error for bad pattern; got none")
-       }
-       set, err := new(Set).ParseGlob("testdata/file*.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(setExecTests, set, t)
-}
-
-func TestParseSetGlob(t *testing.T) {
-       _, err := ParseSetGlob("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       _, err = ParseSetGlob("[x")
-       if err == nil {
-               t.Error("expected error for bad pattern; got none")
-       }
-       set, err := ParseSetGlob("testdata/file*.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(setExecTests, set, t)
-}
-
-var templateFileExecTests = []execTest{
-       {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\ntemplate2\n", 0, true},
-}
-
-func TestSetParseTemplateFiles(t *testing.T) {
-       _, err := ParseTemplateFiles("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(templateFileExecTests, set, t)
-}
-
-func TestParseTemplateFiles(t *testing.T) {
-       _, err := ParseTemplateFiles("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       set, err := new(Set).ParseTemplateFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(templateFileExecTests, set, t)
-}
-
-func TestSetParseTemplateGlob(t *testing.T) {
-       _, err := ParseTemplateGlob("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       _, err = new(Set).ParseTemplateGlob("[x")
-       if err == nil {
-               t.Error("expected error for bad pattern; got none")
-       }
-       set, err := new(Set).ParseTemplateGlob("testdata/tmpl*.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(templateFileExecTests, set, t)
-}
-
-func TestParseTemplateGlob(t *testing.T) {
-       _, err := ParseTemplateGlob("DOES NOT EXIST")
-       if err == nil {
-               t.Error("expected error for non-existent file; got none")
-       }
-       _, err = ParseTemplateGlob("[x")
-       if err == nil {
-               t.Error("expected error for bad pattern; got none")
-       }
-       set, err := ParseTemplateGlob("testdata/tmpl*.tmpl")
-       if err != nil {
-               t.Fatalf("error parsing files: %v", err)
-       }
-       testExecute(templateFileExecTests, set, t)
-}
diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go
new file mode 100644 (file)
index 0000000..04fca40
--- /dev/null
@@ -0,0 +1,236 @@
+// 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 template
+
+import (
+       "bytes"
+       "fmt"
+       "reflect"
+       "text/template/parse"
+)
+
+// common holds the information shared by related templates.
+type common struct {
+       tmpl map[string]*Template
+       // We use two maps, one for parsing and one for execution.
+       // This separation makes the API cleaner since it doesn't
+       // expose reflection to the client.
+       parseFuncs FuncMap
+       execFuncs  map[string]reflect.Value
+}
+
+// Template is the representation of a parsed template. The *parse.Tree
+// field is exported only for use by html/template and should be treated
+// as unexported by all other clients.
+type Template struct {
+       name string
+       *parse.Tree
+       *common
+       leftDelim  string
+       rightDelim string
+}
+
+// New allocates a new template with the given name.
+func New(name string) *Template {
+       return &Template{
+               name: name,
+       }
+}
+
+// Name returns the name of the template.
+func (t *Template) Name() string {
+       return t.name
+}
+
+// New allocates a new template associated with the given one and with the same
+// delimiters. The association, which is transitive, allows one template to
+// invoke another with a {{template}} action.
+func (t *Template) New(name string) *Template {
+       t.init()
+       return &Template{
+               name:       name,
+               common:     t.common,
+               leftDelim:  t.leftDelim,
+               rightDelim: t.rightDelim,
+       }
+}
+
+func (t *Template) init() {
+       if t.common == nil {
+               t.common = new(common)
+               t.tmpl = make(map[string]*Template)
+               t.parseFuncs = make(FuncMap)
+               t.execFuncs = make(map[string]reflect.Value)
+       }
+}
+
+// Clone returns a duplicate of the template, including all associated
+// templates. The actual representation is not copied, but the name space of
+// associated templates is, so further calls to Parse in the copy will add
+// templates to the copy but not to the original. Clone can be used to prepare
+// common templates and use them with variant definitions for other templates by
+// adding the variants after the clone is made.
+func (t *Template) Clone() *Template {
+       nt := t.copy(nil)
+       nt.init()
+       nt.tmpl[t.name] = nt
+       for k, v := range t.tmpl {
+               if k == t.name { // Already installed.
+                       continue
+               }
+               // The associated templates share nt's common structure.
+               tmpl := v.copy(nt.common)
+               nt.tmpl[k] = tmpl
+       }
+       for k, v := range t.parseFuncs {
+               nt.parseFuncs[k] = v
+       }
+       for k, v := range t.execFuncs {
+               nt.execFuncs[k] = v
+       }
+       return nt
+}
+
+// copy returns a shallow copy of t, with common set to the argument.
+func (t *Template) copy(c *common) *Template {
+       nt := New(t.name)
+       nt.Tree = t.Tree
+       nt.common = c
+       nt.leftDelim = t.leftDelim
+       nt.rightDelim = t.rightDelim
+       return nt
+}
+
+// AddParseTree creates a new template with the name and parse tree
+// and associates it with t.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+       if t.tmpl[name] != nil {
+               return nil, fmt.Errorf("template: redefinition of template %q", name)
+       }
+       nt := t.New(name)
+       nt.Tree = tree
+       t.tmpl[name] = nt
+       return nt, nil
+}
+
+// Templates returns a slice of the templates associated with t, including t
+// itself.
+func (t *Template) Templates() []*Template {
+       // Return a slice so we don't expose the map.
+       m := make([]*Template, 0, len(t.tmpl))
+       for _, v := range t.tmpl {
+               m = append(m, v)
+       }
+       return m
+}
+
+// Delims sets the action delimiters to the specified strings, to be used in
+// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
+// definitions will inherit the settings. An empty delimiter stands for the
+// corresponding default: {{ or }}.
+// The return value is the template, so calls can be chained.
+func (t *Template) Delims(left, right string) *Template {
+       t.leftDelim = left
+       t.rightDelim = right
+       return t
+}
+
+// Funcs adds the elements of the argument map to the template's function map.
+// It panics if a value in the map is not a function with appropriate return
+// type. However, it is legal to overwrite elements of the map. The return
+// value is the template, so calls can be chained.
+func (t *Template) Funcs(funcMap FuncMap) *Template {
+       t.init()
+       addValueFuncs(t.execFuncs, funcMap)
+       addFuncs(t.parseFuncs, funcMap)
+       return t
+}
+
+// Lookup returns the template with the given name that is associated with t,
+// or nil if there is no such template.
+func (t *Template) Lookup(name string) *Template {
+       if t.common == nil {
+               return nil
+       }
+       return t.tmpl[name]
+}
+
+// Parse parses a string into a template. Nested template definitions will be
+// associated with the top-level template t. Parse may be called multiple times
+// to parse definitions of templates to associate with t. It is an error if a
+// resulting template is non-empty (contains content other than template
+// definitions) and would replace a non-empty template with the same name.
+// (In multiple calls to Parse with the same receiver template, only one call
+// can contain text other than space, comments, and template definitions.)
+func (t *Template) Parse(text string) (*Template, error) {
+       t.init()
+       trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+       if err != nil {
+               return nil, err
+       }
+       // Add the newly parsed trees, including the one for t, into our common structure.
+       for name, tree := range trees {
+               // If the name we parsed is the name of this template, overwrite this template.
+               // The associate method checks it's not a redefinition.
+               tmpl := t
+               if name != t.name {
+                       tmpl = t.New(name)
+               }
+               // Even if t == tmpl, we need to install it in the common.tmpl map.
+               if err := t.associate(tmpl); err != nil {
+                       return nil, err
+               }
+               tmpl.Tree = tree
+               tmpl.leftDelim = t.leftDelim
+               tmpl.rightDelim = t.rightDelim
+       }
+       return t, nil
+}
+
+// associate installs the new template into the group of templates associated
+// with t. It is an error to reuse a name except to overwrite an empty
+// template. The two are already known to share the common structure.
+func (t *Template) associate(new *Template) error {
+       if new.common != t.common {
+               panic("internal error: associate not common")
+       }
+       name := new.name
+       if old := t.tmpl[name]; old != nil {
+               oldIsEmpty := isEmpty(old.Root)
+               newIsEmpty := isEmpty(new.Root)
+               if !oldIsEmpty && !newIsEmpty {
+                       return fmt.Errorf("template: redefinition of template %q", name)
+               }
+               if newIsEmpty {
+                       // Whether old is empty or not, new is empty; no reason to replace old.
+                       return nil
+               }
+       }
+       t.tmpl[name] = new
+       return nil
+}
+
+// isEmpty reports whether this tree (node) is empty of everything but space.
+func isEmpty(n parse.Node) bool {
+       switch n := n.(type) {
+       case *parse.ActionNode:
+       case *parse.IfNode:
+       case *parse.ListNode:
+               for _, node := range n.Nodes {
+                       if !isEmpty(node) {
+                               return false
+                       }
+               }
+               return true
+       case *parse.RangeNode:
+       case *parse.TemplateNode:
+       case *parse.TextNode:
+               return len(bytes.TrimSpace(n.Text)) == 0
+       case *parse.WithNode:
+       default:
+               panic("unknown node: " + n.String())
+       }
+       return false
+}
index 3d15b81735afcafbd17c06697efd1c62fa63f74e..b72b3a340c7a2b9c36c69da7881357a2d7924040 100644 (file)
@@ -1 +1,3 @@
 template1
+{{define "x"}}x{{end}}
+{{template "y"}}
index a374d2fe7fc97dd77d03fab100090a5d84262784..16beba6e7ddc291ed5db32c210da29358b8a8d56 100644 (file)
@@ -1 +1,3 @@
 template2
+{{define "y"}}y{{end}}
+{{template "x"}}
diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go
new file mode 100644 (file)
index 0000000..153b1a3
--- /dev/null
@@ -0,0 +1,58 @@
+// 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_test
+
+import (
+       "fmt"
+       "time"
+)
+
+func expensiveCall() {}
+
+func ExampleDuration() {
+       t0 := time.Now()
+       expensiveCall()
+       t1 := time.Now()
+       fmt.Printf("The call took %v to run.\n", t1.Sub(t0))
+}
+
+var c chan int
+
+func handle(int) {}
+
+func ExampleAfter() {
+       select {
+       case m := <-c:
+               handle(m)
+       case <-time.After(5 * time.Minute):
+               fmt.Println("timed out")
+       }
+}
+
+func ExampleSleep() {
+       time.Sleep(100 * time.Millisecond)
+}
+
+func statusUpdate() string { return "" }
+
+func ExampleTick() {
+       c := time.Tick(1 * time.Minute)
+       for now := range c {
+               fmt.Printf("%v %s\n", now, statusUpdate())
+       }
+}
+
+func ExampleMonth() {
+       _, month, day := time.Now().Date()
+       if month == time.November && day == 10 {
+               fmt.Println("Happy Go day!")
+       }
+}
+
+// Go launched at Tue Nov 10 15:00:00 -0800 PST 2009
+func ExampleDate() {
+       t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
+       fmt.Printf("Go launched at %s\n", t.Local())
+}
index 14b712ad08624abe4c3a07d67991df8578454562..082a51a1621ddac36123841b8c5e7e40eb8b6249 100644 (file)
@@ -1,10 +1,6 @@
 package time
 
-import (
-       "bytes"
-       "errors"
-       "strconv"
-)
+import "errors"
 
 const (
        numeric = iota
@@ -259,8 +255,60 @@ func lookup(tab []string, val string) (int, string, error) {
        return -1, val, errBad
 }
 
+// Duplicates functionality in strconv, but avoids dependency.
+func itoa(x int) string {
+       var buf [32]byte
+       n := len(buf)
+       if x == 0 {
+               return "0"
+       }
+       u := uint(x)
+       if x < 0 {
+               u = -u
+       }
+       for u > 0 {
+               n--
+               buf[n] = byte(u%10 + '0')
+               u /= 10
+       }
+       if x < 0 {
+               n--
+               buf[n] = '-'
+       }
+       return string(buf[n:])
+}
+
+// Never printed, just needs to be non-nil for return by atoi.
+var atoiError = errors.New("time: invalid number")
+
+// Duplicates functionality in strconv, but avoids dependency.
+func atoi(s string) (x int, err error) {
+       i := 0
+       if len(s) > 0 && s[0] == '-' {
+               i++
+       }
+       if i >= len(s) {
+               return 0, atoiError
+       }
+       for ; i < len(s); i++ {
+               c := s[i]
+               if c < '0' || c > '9' {
+                       return 0, atoiError
+               }
+               if x >= (1<<31-10)/10 {
+                       // will overflow
+                       return 0, atoiError
+               }
+               x = x*10 + int(c) - '0'
+       }
+       if s[0] == '-' {
+               x = -x
+       }
+       return x, nil
+}
+
 func pad(i int, padding string) string {
-       s := strconv.Itoa(i)
+       s := itoa(i)
        if i < 10 {
                s = padding + s
        }
@@ -273,7 +321,7 @@ func zeroPad(i int) string { return pad(i, "0") }
 func formatNano(nanosec, n int) string {
        // User might give us bad data. Make sure it's positive and in range.
        // They'll get nonsense output but it will have the right format.
-       s := strconv.Uitoa(uint(nanosec) % 1e9)
+       s := itoa(int(uint(nanosec) % 1e9))
        // Zero pad left without fmt.
        if len(s) < 9 {
                s = "000000000"[:9-len(s)] + s
@@ -284,14 +332,42 @@ func formatNano(nanosec, n int) string {
        return "." + s[:n]
 }
 
+// String returns the time formatted using the format string
+//     "Mon Jan _2 15:04:05 -0700 MST 2006"
+func (t Time) String() string {
+       return t.Format("Mon Jan _2 15:04:05 -0700 MST 2006")
+}
+
+type buffer []byte
+
+func (b *buffer) WriteString(s string) {
+       *b = append(*b, s...)
+}
+
+func (b *buffer) WriteByte(c byte) {
+       *b = append(*b, c)
+}
+
+func (b *buffer) String() string {
+       return string([]byte(*b))
+}
+
 // Format returns a textual representation of the time value formatted
 // according to layout.  The layout defines the format by showing the
 // representation of a standard time, which is then used to describe
 // the time to be formatted.  Predefined layouts ANSIC, UnixDate,
 // RFC3339 and others describe standard representations. For more
 // information about the formats, see the documentation for ANSIC.
-func (t *Time) Format(layout string) string {
-       b := new(bytes.Buffer)
+func (t Time) Format(layout string) string {
+       var (
+               year  int = -1
+               month Month
+               day   int
+               hour  int = -1
+               min   int
+               sec   int
+               b     buffer
+       )
        // Each iteration generates one std value.
        for {
                prefix, std, suffix := nextStdChunk(layout)
@@ -299,62 +375,92 @@ func (t *Time) Format(layout string) string {
                if std == "" {
                        break
                }
+
+               // Compute year, month, day if needed.
+               if year < 0 {
+                       // Jan 01 02 2006
+                       if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' {
+                               year, month, day = t.Date()
+                       }
+               }
+
+               // Compute hour, minute, second if needed.
+               if hour < 0 {
+                       // 03 04 05 15 pm
+                       if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' {
+                               hour, min, sec = t.Clock()
+                       }
+               }
+
                var p string
                switch std {
                case stdYear:
-                       p = zeroPad(int(t.Year % 100))
+                       p = zeroPad(year % 100)
                case stdLongYear:
-                       p = strconv.Itoa64(t.Year)
+                       p = itoa(year)
                case stdMonth:
-                       p = shortMonthNames[t.Month]
+                       p = month.String()[:3]
                case stdLongMonth:
-                       p = longMonthNames[t.Month]
+                       p = month.String()
                case stdNumMonth:
-                       p = strconv.Itoa(t.Month)
+                       p = itoa(int(month))
                case stdZeroMonth:
-                       p = zeroPad(t.Month)
+                       p = zeroPad(int(month))
                case stdWeekDay:
-                       p = shortDayNames[t.Weekday()]
+                       p = t.Weekday().String()[:3]
                case stdLongWeekDay:
-                       p = longDayNames[t.Weekday()]
+                       p = t.Weekday().String()
                case stdDay:
-                       p = strconv.Itoa(t.Day)
+                       p = itoa(day)
                case stdUnderDay:
-                       p = pad(t.Day, " ")
+                       p = pad(day, " ")
                case stdZeroDay:
-                       p = zeroPad(t.Day)
+                       p = zeroPad(day)
                case stdHour:
-                       p = zeroPad(t.Hour)
+                       p = zeroPad(hour)
                case stdHour12:
                        // Noon is 12PM, midnight is 12AM.
-                       hr := t.Hour % 12
+                       hr := hour % 12
                        if hr == 0 {
                                hr = 12
                        }
-                       p = strconv.Itoa(hr)
+                       p = itoa(hr)
                case stdZeroHour12:
                        // Noon is 12PM, midnight is 12AM.
-                       hr := t.Hour % 12
+                       hr := hour % 12
                        if hr == 0 {
                                hr = 12
                        }
                        p = zeroPad(hr)
                case stdMinute:
-                       p = strconv.Itoa(t.Minute)
+                       p = itoa(min)
                case stdZeroMinute:
-                       p = zeroPad(t.Minute)
+                       p = zeroPad(min)
                case stdSecond:
-                       p = strconv.Itoa(t.Second)
+                       p = itoa(sec)
                case stdZeroSecond:
-                       p = zeroPad(t.Second)
+                       p = zeroPad(sec)
+               case stdPM:
+                       if hour >= 12 {
+                               p = "PM"
+                       } else {
+                               p = "AM"
+                       }
+               case stdpm:
+                       if hour >= 12 {
+                               p = "pm"
+                       } else {
+                               p = "am"
+                       }
                case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
                        // Ugly special case.  We cheat and take the "Z" variants
                        // to mean "the time zone as formatted for ISO 8601".
-                       if t.ZoneOffset == 0 && std[0] == 'Z' {
+                       _, offset := t.Zone()
+                       if offset == 0 && std[0] == 'Z' {
                                p = "Z"
                                break
                        }
-                       zone := t.ZoneOffset / 60 // convert to minutes
+                       zone := offset / 60 // convert to minutes
                        if zone < 0 {
                                p = "-"
                                zone = -zone
@@ -366,25 +472,14 @@ func (t *Time) Format(layout string) string {
                                p += ":"
                        }
                        p += zeroPad(zone % 60)
-               case stdPM:
-                       if t.Hour >= 12 {
-                               p = "PM"
-                       } else {
-                               p = "AM"
-                       }
-               case stdpm:
-                       if t.Hour >= 12 {
-                               p = "pm"
-                       } else {
-                               p = "am"
-                       }
                case stdTZ:
-                       if t.Zone != "" {
-                               p = t.Zone
+                       name, offset := t.Zone()
+                       if name != "" {
+                               p = name
                        } else {
                                // No time zone known for this time, but we must print one.
                                // Use the -0700 format.
-                               zone := t.ZoneOffset / 60 // convert to minutes
+                               zone := offset / 60 // convert to minutes
                                if zone < 0 {
                                        p = "-"
                                        zone = -zone
@@ -396,7 +491,7 @@ func (t *Time) Format(layout string) string {
                        }
                default:
                        if len(std) >= 2 && std[0:2] == ".0" {
-                               p = formatNano(t.Nanosecond, len(std)-1)
+                               p = formatNano(t.Nanosecond(), len(std)-1)
                        }
                }
                b.WriteString(p)
@@ -405,14 +500,6 @@ func (t *Time) Format(layout string) string {
        return b.String()
 }
 
-// String returns a Unix-style representation of the time value.
-func (t *Time) String() string {
-       if t == nil {
-               return "<nil>"
-       }
-       return t.Format(UnixDate)
-}
-
 var errBad = errors.New("bad value for field") // placeholder not passed to user
 
 // ParseError describes a problem parsing a time string.
@@ -424,17 +511,21 @@ type ParseError struct {
        Message    string
 }
 
-// String is the string representation of a ParseError.
+func quote(s string) string {
+       return "\"" + s + "\""
+}
+
+// Error returns the string representation of a ParseError.
 func (e *ParseError) Error() string {
        if e.Message == "" {
                return "parsing time " +
-                       strconv.Quote(e.Value) + " as " +
-                       strconv.Quote(e.Layout) + ": cannot parse " +
-                       strconv.Quote(e.ValueElem) + " as " +
-                       strconv.Quote(e.LayoutElem)
+                       quote(e.Value) + " as " +
+                       quote(e.Layout) + ": cannot parse " +
+                       quote(e.ValueElem) + " as " +
+                       quote(e.LayoutElem)
        }
        return "parsing time " +
-               strconv.Quote(e.Value) + e.Message
+               quote(e.Value) + e.Message
 }
 
 // isDigit returns true if s[i] is a decimal digit, false if not or
@@ -498,30 +589,42 @@ func skip(value, prefix string) (string, error) {
 // representations.For more information about the formats, see the
 // documentation for ANSIC.
 //
-// Only those elements present in the value will be set in the returned time
-// structure.  Also, if the input string represents an inconsistent time
-// (such as having the wrong day of the week), the returned value will also
-// be inconsistent.  In any case, the elements of the returned time will be
-// sane: hours in 0..23, minutes in 0..59, day of month in 1..31, etc.
+// Elements omitted from the value are assumed to be zero, or when
+// zero is impossible, one, so parsing "3:04pm" returns the time
+// corresponding to Jan 1, year 0, 15:04:00 UTC.
 // Years must be in the range 0000..9999. The day of the week is checked
 // for syntax but it is otherwise ignored.
-func Parse(alayout, avalue string) (*Time, error) {
-       var t Time
+func Parse(layout, value string) (Time, error) {
+       alayout, avalue := layout, value
        rangeErrString := "" // set if a value is out of range
        amSet := false       // do we need to subtract 12 from the hour for midnight?
        pmSet := false       // do we need to add 12 to the hour?
-       layout, value := alayout, avalue
+
+       // Time being constructed.
+       var (
+               year       int
+               month      int = 1 // January
+               day        int = 1
+               hour       int
+               min        int
+               sec        int
+               nsec       int
+               z          *Location
+               zoneOffset int = -1
+               zoneName   string
+       )
+
        // Each iteration processes one std value.
        for {
                var err error
                prefix, std, suffix := nextStdChunk(layout)
                value, err = skip(value, prefix)
                if err != nil {
-                       return nil, &ParseError{alayout, avalue, prefix, value, ""}
+                       return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
                }
                if len(std) == 0 {
                        if len(value) != 0 {
-                               return nil, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
+                               return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
                        }
                        break
                }
@@ -534,11 +637,11 @@ func Parse(alayout, avalue string) (*Time, error) {
                                break
                        }
                        p, value = value[0:2], value[2:]
-                       t.Year, err = strconv.Atoi64(p)
-                       if t.Year >= 69 { // Unix time starts Dec 31 1969 in some time zones
-                               t.Year += 1900
+                       year, err = atoi(p)
+                       if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
+                               year += 1900
                        } else {
-                               t.Year += 2000
+                               year += 2000
                        }
                case stdLongYear:
                        if len(value) < 4 || !isDigit(value, 0) {
@@ -546,14 +649,14 @@ func Parse(alayout, avalue string) (*Time, error) {
                                break
                        }
                        p, value = value[0:4], value[4:]
-                       t.Year, err = strconv.Atoi64(p)
+                       year, err = atoi(p)
                case stdMonth:
-                       t.Month, value, err = lookup(shortMonthNames, value)
+                       month, value, err = lookup(shortMonthNames, value)
                case stdLongMonth:
-                       t.Month, value, err = lookup(longMonthNames, value)
+                       month, value, err = lookup(longMonthNames, value)
                case stdNumMonth, stdZeroMonth:
-                       t.Month, value, err = getnum(value, std == stdZeroMonth)
-                       if t.Month <= 0 || 12 < t.Month {
+                       month, value, err = getnum(value, std == stdZeroMonth)
+                       if month <= 0 || 12 < month {
                                rangeErrString = "month"
                        }
                case stdWeekDay:
@@ -565,29 +668,28 @@ func Parse(alayout, avalue string) (*Time, error) {
                        if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
                                value = value[1:]
                        }
-                       t.Day, value, err = getnum(value, std == stdZeroDay)
-                       if t.Day < 0 || 31 < t.Day {
-                               // TODO: be more thorough in date check?
+                       day, value, err = getnum(value, std == stdZeroDay)
+                       if day < 0 || 31 < day {
                                rangeErrString = "day"
                        }
                case stdHour:
-                       t.Hour, value, err = getnum(value, false)
-                       if t.Hour < 0 || 24 <= t.Hour {
+                       hour, value, err = getnum(value, false)
+                       if hour < 0 || 24 <= hour {
                                rangeErrString = "hour"
                        }
                case stdHour12, stdZeroHour12:
-                       t.Hour, value, err = getnum(value, std == stdZeroHour12)
-                       if t.Hour < 0 || 12 < t.Hour {
+                       hour, value, err = getnum(value, std == stdZeroHour12)
+                       if hour < 0 || 12 < hour {
                                rangeErrString = "hour"
                        }
                case stdMinute, stdZeroMinute:
-                       t.Minute, value, err = getnum(value, std == stdZeroMinute)
-                       if t.Minute < 0 || 60 <= t.Minute {
+                       min, value, err = getnum(value, std == stdZeroMinute)
+                       if min < 0 || 60 <= min {
                                rangeErrString = "minute"
                        }
                case stdSecond, stdZeroSecond:
-                       t.Second, value, err = getnum(value, std == stdZeroSecond)
-                       if t.Second < 0 || 60 <= t.Second {
+                       sec, value, err = getnum(value, std == stdZeroSecond)
+                       if sec < 0 || 60 <= sec {
                                rangeErrString = "second"
                        }
                        // Special case: do we have a fractional second but no
@@ -602,16 +704,44 @@ func Parse(alayout, avalue string) (*Time, error) {
                                n := 2
                                for ; n < len(value) && isDigit(value, n); n++ {
                                }
-                               rangeErrString, err = t.parseNanoseconds(value, n)
+                               nsec, rangeErrString, err = parseNanoseconds(value, n)
                                value = value[n:]
                        }
+               case stdPM:
+                       if len(value) < 2 {
+                               err = errBad
+                               break
+                       }
+                       p, value = value[0:2], value[2:]
+                       switch p {
+                       case "PM":
+                               pmSet = true
+                       case "AM":
+                               amSet = true
+                       default:
+                               err = errBad
+                       }
+               case stdpm:
+                       if len(value) < 2 {
+                               err = errBad
+                               break
+                       }
+                       p, value = value[0:2], value[2:]
+                       switch p {
+                       case "pm":
+                               pmSet = true
+                       case "am":
+                               amSet = true
+                       default:
+                               err = errBad
+                       }
                case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
                        if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
                                value = value[1:]
-                               t.Zone = "UTC"
+                               z = UTC
                                break
                        }
-                       var sign, hh, mm string
+                       var sign, hour, min string
                        if std == stdISO8601ColonTZ || std == stdNumColonTZ {
                                if len(value) < 6 {
                                        err = errBad
@@ -621,65 +751,38 @@ func Parse(alayout, avalue string) (*Time, error) {
                                        err = errBad
                                        break
                                }
-                               sign, hh, mm, value = value[0:1], value[1:3], value[4:6], value[6:]
+                               sign, hour, min, value = value[0:1], value[1:3], value[4:6], value[6:]
                        } else if std == stdNumShortTZ {
                                if len(value) < 3 {
                                        err = errBad
                                        break
                                }
-                               sign, hh, mm, value = value[0:1], value[1:3], "00", value[3:]
+                               sign, hour, min, value = value[0:1], value[1:3], "00", value[3:]
                        } else {
                                if len(value) < 5 {
                                        err = errBad
                                        break
                                }
-                               sign, hh, mm, value = value[0:1], value[1:3], value[3:5], value[5:]
+                               sign, hour, min, value = value[0:1], value[1:3], value[3:5], value[5:]
                        }
-                       var hr, min int
-                       hr, err = strconv.Atoi(hh)
+                       var hr, mm int
+                       hr, err = atoi(hour)
                        if err == nil {
-                               min, err = strconv.Atoi(mm)
+                               mm, err = atoi(min)
                        }
-                       t.ZoneOffset = (hr*60 + min) * 60 // offset is in seconds
+                       zoneOffset = (hr*60 + mm) * 60 // offset is in seconds
                        switch sign[0] {
                        case '+':
                        case '-':
-                               t.ZoneOffset = -t.ZoneOffset
-                       default:
-                               err = errBad
-                       }
-               case stdPM:
-                       if len(value) < 2 {
-                               err = errBad
-                               break
-                       }
-                       p, value = value[0:2], value[2:]
-                       switch p {
-                       case "PM":
-                               pmSet = true
-                       case "AM":
-                               amSet = true
-                       default:
-                               err = errBad
-                       }
-               case stdpm:
-                       if len(value) < 2 {
-                               err = errBad
-                               break
-                       }
-                       p, value = value[0:2], value[2:]
-                       switch p {
-                       case "pm":
-                               pmSet = true
-                       case "am":
-                               amSet = true
+                               zoneOffset = -zoneOffset
                        default:
                                err = errBad
                        }
                case stdTZ:
                        // Does it look like a time zone?
                        if len(value) >= 3 && value[0:3] == "UTC" {
-                               t.Zone, value = value[0:3], value[3:]
+                               z = UTC
+                               value = value[3:]
                                break
                        }
 
@@ -700,47 +803,86 @@ func Parse(alayout, avalue string) (*Time, error) {
                                break
                        }
                        // It's a valid format.
-                       t.Zone = p
-                       // Can we find its offset?
-                       if offset, found := lookupByName(p); found {
-                               t.ZoneOffset = offset
-                       }
+                       zoneName = p
                default:
                        if len(value) < len(std) {
                                err = errBad
                                break
                        }
                        if len(std) >= 2 && std[0:2] == ".0" {
-                               rangeErrString, err = t.parseNanoseconds(value, len(std))
+                               nsec, rangeErrString, err = parseNanoseconds(value, len(std))
                                value = value[len(std):]
                        }
                }
                if rangeErrString != "" {
-                       return nil, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
+                       return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
                }
                if err != nil {
-                       return nil, &ParseError{alayout, avalue, std, value, ""}
+                       return Time{}, &ParseError{alayout, avalue, std, value, ""}
+               }
+       }
+       if pmSet && hour < 12 {
+               hour += 12
+       } else if amSet && hour == 12 {
+               hour = 0
+       }
+
+       // TODO: be more aggressive checking day?
+       if z != nil {
+               return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
+       }
+
+       t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
+       if zoneOffset != -1 {
+               t.sec -= int64(zoneOffset)
+
+               // Look for local zone with the given offset.
+               // If that zone was in effect at the given time, use it.
+               name, offset, _, _, _ := Local.lookup(t.sec + internalToUnix)
+               if offset == zoneOffset && (zoneName == "" || name == zoneName) {
+                       t.loc = Local
+                       return t, nil
                }
+
+               // Otherwise create fake zone to record offset.
+               t.loc = FixedZone(zoneName, zoneOffset)
+               return t, nil
        }
-       if pmSet && t.Hour < 12 {
-               t.Hour += 12
-       } else if amSet && t.Hour == 12 {
-               t.Hour = 0
+
+       if zoneName != "" {
+               // Look for local zone with the given offset.
+               // If that zone was in effect at the given time, use it.
+               offset, _, ok := Local.lookupName(zoneName)
+               if ok {
+                       name, off, _, _, _ := Local.lookup(t.sec + internalToUnix - int64(offset))
+                       if name == zoneName && off == offset {
+                               t.sec -= int64(offset)
+                               t.loc = Local
+                               return t, nil
+                       }
+               }
+
+               // Otherwise, create fake zone with unknown offset.
+               t.loc = FixedZone(zoneName, 0)
+               return t, nil
        }
-       return &t, nil
+
+       // Otherwise, fall back to UTC.
+       return t, nil
 }
 
-func (t *Time) parseNanoseconds(value string, nbytes int) (rangErrString string, err error) {
+func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
        if value[0] != '.' {
-               return "", errBad
+               err = errBad
+               return
        }
-       var ns int
-       ns, err = strconv.Atoi(value[1:nbytes])
+       ns, err = atoi(value[1:nbytes])
        if err != nil {
-               return "", err
+               return
        }
        if ns < 0 || 1e9 <= ns {
-               return "fractional second", nil
+               rangeErrString = "fractional second"
+               return
        }
        // We need nanoseconds, which means scaling by the number
        // of missing digits in the format, maximum length 10. If it's
@@ -749,6 +891,5 @@ func (t *Time) parseNanoseconds(value string, nbytes int) (rangErrString string,
        for i := 0; i < scaleDigits; i++ {
                ns *= 10
        }
-       t.Nanosecond = ns
        return
 }
index d7e7076539f48542ab9199b44d8fce4239730056..2c4df335f9b44a4e247e4cc3183eb65d71e5bd7e 100644 (file)
@@ -6,7 +6,7 @@ package time
 
 func init() {
        // force US/Pacific for time zone tests
-       onceSetupZone.Do(setupTestingZone)
+       localOnce.Do(initTestingZone)
 }
 
 var Interrupt = interrupt
index 967fca09b99641e04bec2f1b067681c37570a8fa..1e23118f3786152bd4b2d584d7a3f01bba378adb 100644 (file)
@@ -4,6 +4,11 @@
 
 package time
 
+func nano() int64 {
+       sec, nsec := now()
+       return sec*1e9 + int64(nsec)
+}
+
 // Interface to timers implemented in package runtime.
 // Must be in sync with ../runtime/runtime.h:/^struct.Timer$
 type runtimeTimer struct {
@@ -21,7 +26,7 @@ func stopTimer(*runtimeTimer) bool
 // When the Timer expires, the current time will be sent on C,
 // unless the Timer was created by AfterFunc.
 type Timer struct {
-       C <-chan int64
+       C <-chan Time
        r runtimeTimer
 }
 
@@ -34,12 +39,12 @@ func (t *Timer) Stop() (ok bool) {
 
 // NewTimer creates a new Timer that will send
 // the current time on its channel after at least ns nanoseconds.
-func NewTimer(ns int64) *Timer {
-       c := make(chan int64, 1)
+func NewTimer(d Duration) *Timer {
+       c := make(chan Time, 1)
        t := &Timer{
                C: c,
                r: runtimeTimer{
-                       when: Nanoseconds() + ns,
+                       when: nano() + int64(d),
                        f:    sendTime,
                        arg:  c,
                },
@@ -55,16 +60,16 @@ func sendTime(now int64, c interface{}) {
        // the desired behavior when the reader gets behind,
        // because the sends are periodic.
        select {
-       case c.(chan int64) <- now:
+       case c.(chan Time) <- Unix(0, now):
        default:
        }
 }
 
-// After waits at least ns nanoseconds before sending the current time
+// After waits for the duration to elapse and then sends the current time
 // on the returned channel.
 // It is equivalent to NewTimer(ns).C.
-func After(ns int64) <-chan int64 {
-       return NewTimer(ns).C
+func After(d Duration) <-chan Time {
+       return NewTimer(d).C
 }
 
 // AfterFunc waits at least ns nanoseconds before calling f
@@ -73,7 +78,7 @@ func After(ns int64) <-chan int64 {
 func AfterFunc(ns int64, f func()) *Timer {
        t := &Timer{
                r: runtimeTimer{
-                       when: Nanoseconds() + ns,
+                       when: nano() + ns,
                        f:    goFunc,
                        arg:  f,
                },
index 9171da3af1cec59bea2d1dbf3f58060668d3c31a..cbcc897fd405bc3a10a2b4d0dd5f2ccf2e055898 100644 (file)
@@ -15,16 +15,16 @@ import (
 )
 
 func TestSleep(t *testing.T) {
-       const delay = int64(100e6)
+       const delay = 100 * Millisecond
        go func() {
                Sleep(delay / 2)
                Interrupt()
        }()
-       start := Nanoseconds()
+       start := Now()
        Sleep(delay)
-       duration := Nanoseconds() - start
+       duration := Now().Sub(start)
        if duration < delay {
-               t.Fatalf("Sleep(%d) slept for only %d ns", delay, duration)
+               t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
        }
 }
 
@@ -96,32 +96,32 @@ func BenchmarkStop(b *testing.B) {
 }
 
 func TestAfter(t *testing.T) {
-       const delay = int64(100e6)
-       start := Nanoseconds()
+       const delay = 100 * Millisecond
+       start := Now()
        end := <-After(delay)
-       if duration := Nanoseconds() - start; duration < delay {
-               t.Fatalf("After(%d) slept for only %d ns", delay, duration)
+       if duration := Now().Sub(start); duration < delay {
+               t.Fatalf("After(%s) slept for only %d ns", delay, duration)
        }
-       if min := start + delay; end < min {
-               t.Fatalf("After(%d) expect >= %d, got %d", delay, min, end)
+       if min := start.Add(delay); end.Before(min) {
+               t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
        }
 }
 
 func TestAfterTick(t *testing.T) {
        const (
-               Delta = 100 * 1e6
+               Delta = 100 * Millisecond
                Count = 10
        )
-       t0 := Nanoseconds()
+       t0 := Now()
        for i := 0; i < Count; i++ {
                <-After(Delta)
        }
-       t1 := Nanoseconds()
-       ns := t1 - t0
-       target := int64(Delta * Count)
+       t1 := Now()
+       d := t1.Sub(t0)
+       target := Delta * Count
        slop := target * 2 / 10
-       if ns < target-slop || ns > target+slop {
-               t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target))
+       if d < target-slop || d > target+slop {
+               t.Fatalf("%d ticks of %s took %s, expected %s", Count, Delta, d, target)
        }
 }
 
@@ -171,38 +171,54 @@ var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8 /*0*/ }
 
 type afterResult struct {
        slot int
-       t    int64
+       t    Time
 }
 
-func await(slot int, result chan<- afterResult, ac <-chan int64) {
+func await(slot int, result chan<- afterResult, ac <-chan Time) {
        result <- afterResult{slot, <-ac}
 }
 
 func testAfterQueuing(t *testing.T) error {
        const (
-               Delta = 100 * 1e6
+               Delta = 100 * Millisecond
        )
        // make the result channel buffered because we don't want
        // to depend on channel queueing semantics that might
        // possibly change in the future.
        result := make(chan afterResult, len(slots))
 
-       t0 := Nanoseconds()
+       t0 := Now()
        for _, slot := range slots {
-               go await(slot, result, After(int64(slot)*Delta))
+               go await(slot, result, After(Duration(slot)*Delta))
        }
        sort.Ints(slots)
        for _, slot := range slots {
                r := <-result
                if r.slot != slot {
-                       return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot)
+                       return fmt.Errorf("after slot %d, expected %d", r.slot, slot)
                }
-               ns := r.t - t0
-               target := int64(slot * Delta)
-               slop := int64(Delta) / 4
-               if ns < target-slop || ns > target+slop {
-                       return fmt.Errorf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop))
+               dt := r.t.Sub(t0)
+               target := Duration(slot) * Delta
+               slop := Delta / 4
+               if dt < target-slop || dt > target+slop {
+                       return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-slop, target+slop)
                }
        }
        return nil
 }
+
+func TestTimerStopStress(t *testing.T) {
+       if testing.Short() {
+               return
+       }
+       for i := 0; i < 100; i++ {
+               go func(i int) {
+                       timer := AfterFunc(2e9, func() {
+                               t.Fatalf("timer %d was not stopped", i)
+                       })
+                       Sleep(1e9)
+                       timer.Stop()
+               }(i)
+       }
+       Sleep(3e9)
+}
index a5e529b814a936af7150660602debafbd449b52f..fe6bc27d301f113e9ee460010f00eb105c3c5750 100644 (file)
@@ -4,17 +4,33 @@
 
 package time
 
-// Seconds reports the number of seconds since the Unix epoch,
-// January 1, 1970 00:00:00 UTC.
-func Seconds() int64 {
-       return Nanoseconds() / 1e9
-}
-
-// Nanoseconds is implemented by package runtime.
+import "syscall"
 
-// Nanoseconds reports the number of nanoseconds since the Unix epoch,
-// January 1, 1970 00:00:00 UTC.
-func Nanoseconds() int64
+// Sleep pauses the current goroutine for the duration d.
+func Sleep(d Duration)
 
-// Sleep pauses the current goroutine for at least ns nanoseconds.
-func Sleep(ns int64)
+// readFile reads and returns the content of the named file.
+// It is a trivial implementation of ioutil.ReadFile, reimplemented
+// here to avoid depending on io/ioutil or os.
+func readFile(name string) ([]byte, error) {
+       f, err := syscall.Open(name, syscall.O_RDONLY, 0)
+       if err != nil {
+               return nil, err
+       }
+       defer syscall.Close(f)
+       var (
+               buf [4096]byte
+               ret []byte
+               n   int
+       )
+       for {
+               n, err = syscall.Read(f, buf[:])
+               if n > 0 {
+                       ret = append(ret, buf[:n]...)
+               }
+               if n == 0 || err != nil {
+                       break
+               }
+       }
+       return ret, err
+}
index 3d313228b01970ad6cd0eb038a527d444255f433..715d186be17fc72ac5130e30193164fdcced4ae7 100644 (file)
@@ -6,12 +6,9 @@
 
 package time
 
-import (
-       "os"
-       "syscall"
-)
+import "syscall"
 
 // for testing: whatever interrupts a sleep
 func interrupt() {
-       syscall.Kill(os.Getpid(), syscall.SIGCHLD)
+       syscall.Kill(syscall.Getpid(), syscall.SIGCHLD)
 }
index 95941a1e8196a6e85816d2b888364d6e650dbb6b..4440c2207b33715ce54d087051b10ba5547c6121 100644 (file)
@@ -9,27 +9,27 @@ import "errors"
 // A Ticker holds a synchronous channel that delivers `ticks' of a clock
 // at intervals.
 type Ticker struct {
-       C <-chan int64 // The channel on which the ticks are delivered.
+       C <-chan Time // The channel on which the ticks are delivered.
        r runtimeTimer
 }
 
-// NewTicker returns a new Ticker containing a channel that will
-// send the time, in nanoseconds, every ns nanoseconds.  It adjusts the
-// intervals to make up for pauses in delivery of the ticks. The value of
-// ns must be greater than zero; if not, NewTicker will panic.
-func NewTicker(ns int64) *Ticker {
-       if ns <= 0 {
+// NewTicker returns a new Ticker containing a channel that will send the
+// time, in nanoseconds, with a period specified by the duration argument.
+// It adjusts the intervals or drops ticks to make up for slow receivers.
+// The duration d must be greater than zero; if not, NewTicker will panic.
+func NewTicker(d Duration) *Ticker {
+       if d <= 0 {
                panic(errors.New("non-positive interval for NewTicker"))
        }
        // Give the channel a 1-element time buffer.
        // If the client falls behind while reading, we drop ticks
        // on the floor until the client catches up.
-       c := make(chan int64, 1)
+       c := make(chan Time, 1)
        t := &Ticker{
                C: c,
                r: runtimeTimer{
-                       when:   Nanoseconds() + ns,
-                       period: ns,
+                       when:   nano() + int64(d),
+                       period: int64(d),
                        f:      sendTime,
                        arg:    c,
                },
@@ -45,9 +45,9 @@ func (t *Ticker) Stop() {
 
 // Tick is a convenience wrapper for NewTicker providing access to the ticking
 // channel only.  Useful for clients that have no need to shut down the ticker.
-func Tick(ns int64) <-chan int64 {
-       if ns <= 0 {
+func Tick(d Duration) <-chan Time {
+       if d <= 0 {
                return nil
        }
-       return NewTicker(ns).C
+       return NewTicker(d).C
 }
index 4dcb63956b2ce1c8bcd4563ce86316d30ffdc108..36349349ce0ff4644ded7b953a9b3120fa1580b7 100644 (file)
@@ -11,21 +11,21 @@ import (
 
 func TestTicker(t *testing.T) {
        const (
-               Delta = 100 * 1e6
+               Delta = 100 * Millisecond
                Count = 10
        )
        ticker := NewTicker(Delta)
-       t0 := Nanoseconds()
+       t0 := Now()
        for i := 0; i < Count; i++ {
                <-ticker.C
        }
        ticker.Stop()
-       t1 := Nanoseconds()
-       ns := t1 - t0
-       target := int64(Delta * Count)
+       t1 := Now()
+       dt := t1.Sub(t0)
+       target := Delta * Count
        slop := target * 2 / 10
-       if ns < target-slop || ns > target+slop {
-               t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target))
+       if dt < target-slop || dt > target+slop {
+               t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop)
        }
        // Now test that the ticker stopped
        Sleep(2 * Delta)
index e11d17731b4fe3dd5c6b400e1ff14c840fd141b1..04ed86cf25f860e8de782092627a184db98d3e62 100644 (file)
 // license that can be found in the LICENSE file.
 
 // Package time provides functionality for measuring and displaying time.
+//
+// The calendrical calculations always assume a Gregorian calendar.
 package time
 
-// Days of the week.
+// A Time represents an instant in time with nanosecond precision.
+//
+// Programs using times should typically store and pass them as values,
+// not pointers.  That is, time variables and struct fields should be of
+// type time.Time, not *time.Time.
+//
+// Time instants can be compared using the Before, After, and Equal methods.
+// The Sub method subtracts two instants, producing a Duration.
+// The Add method adds a Time and a Duration, producing a Time.
+//
+// The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC.
+// As this time is unlikely to come up in practice, the IsZero method gives
+// a simple way of detecting a time that has not been initialized explicitly.
+//
+// Each Time has associated with it a Location, consulted when computing the
+// presentation form of the time, such as in the Format, Hour, and Year methods.
+// The methods Local, UTC, and In return a Time with a specific location.
+// Changing the location in this way changes only the presentation; it does not
+// change the instant in time being denoted and therefore does not affect the
+// computations described in earlier paragraphs.
+//
+type Time struct {
+       // sec gives the number of seconds elapsed since
+       // January 1, year 1 00:00:00 UTC.
+       sec int64
+
+       // nsec specifies a non-negative nanosecond
+       // offset within the second named by Seconds.
+       // It must be in the range [0, 999999999].
+       nsec int32
+
+       // loc specifies the Location that should be used to
+       // determine the minute, hour, month, day, and year
+       // that correspond to this Time.
+       // Only the zero Time has a nil Location.
+       // In that case it is interpreted to mean UTC.
+       loc *Location
+}
+
+// After reports whether the time instant t is after u.
+func (t Time) After(u Time) bool {
+       return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec
+}
+
+// Before reports whether the time instant t is before u.
+func (t Time) Before(u Time) bool {
+       return t.sec < u.sec || t.sec == u.sec && t.nsec < u.nsec
+}
+
+// Equal reports whether t and u represent the same time instant.
+// Two times can be equal even if they are in different locations.
+// For example, 6:00 +0200 CEST and 4:00 UTC are Equal.
+// This comparison is different from using t == u, which also compares
+// the locations.
+func (t Time) Equal(u Time) bool {
+       return t.sec == u.sec && t.nsec == u.nsec
+}
+
+// A Month specifies a month of the year (January = 1, ...).
+type Month int
+
 const (
-       Sunday = iota
+       January Month = 1 + iota
+       February
+       March
+       April
+       May
+       June
+       July
+       August
+       September
+       October
+       November
+       December
+)
+
+var months = [...]string{
+       "January",
+       "February",
+       "March",
+       "April",
+       "May",
+       "June",
+       "July",
+       "August",
+       "September",
+       "October",
+       "November",
+       "December",
+}
+
+// String returns the English name of the month ("January", "February", ...).
+func (m Month) String() string { return months[m-1] }
+
+// A Weekday specifies a day of the week (Sunday = 0, ...).
+type Weekday int
+
+const (
+       Sunday Weekday = iota
        Monday
        Tuesday
        Wednesday
@@ -16,284 +114,749 @@ const (
        Saturday
 )
 
-// Time is the struct representing a parsed time value.
-type Time struct {
-       Year                 int64  // 2006 is 2006
-       Month, Day           int    // Jan-2 is 1, 2
-       Hour, Minute, Second int    // 15:04:05 is 15, 4, 5.
-       Nanosecond           int    // Fractional second.
-       ZoneOffset           int    // seconds east of UTC, e.g. -7*60*60 for -0700
-       Zone                 string // e.g., "MST"
+var days = [...]string{
+       "Sunday",
+       "Monday",
+       "Tuesday",
+       "Wednesday",
+       "Thursday",
+       "Friday",
+       "Saturday",
 }
 
-var nonleapyear = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-var leapyear = []int{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+// String returns the English name of the day ("Sunday", "Monday", ...).
+func (d Weekday) String() string { return days[d] }
+
+// Computations on time.
+// 
+// The zero value for a Time is defined to be
+//     January 1, year 1, 00:00:00.000000000 UTC
+// which (1) looks like a zero, or as close as you can get in a date
+// (1-1-1 00:00:00 UTC), (2) is unlikely enough to arise in practice to
+// be a suitable "not set" sentinel, unlike Jan 1 1970, and (3) has a
+// non-negative year even in time zones west of UTC, unlike 1-1-0
+// 00:00:00 UTC, which would be 12-31-(-1) 19:00:00 in New York.
+// 
+// The zero Time value does not force a specific epoch for the time
+// representation.  For example, to use the Unix epoch internally, we
+// could define that to distinguish a zero value from Jan 1 1970, that
+// time would be represented by sec=-1, nsec=1e9.  However, it does
+// suggest a representation, namely using 1-1-1 00:00:00 UTC as the
+// epoch, and that's what we do.
+// 
+// The Add and Sub computations are oblivious to the choice of epoch.
+// 
+// The presentation computations - year, month, minute, and so on - all
+// rely heavily on division and modulus by positive constants.  For
+// calendrical calculations we want these divisions to round down, even
+// for negative values, so that the remainder is always positive, but
+// Go's division (like most hardware divison instructions) rounds to
+// zero.  We can still do those computations and then adjust the result
+// for a negative numerator, but it's annoying to write the adjustment
+// over and over.  Instead, we can change to a different epoch so long
+// ago that all the times we care about will be positive, and then round
+// to zero and round down coincide.  These presentation routines already
+// have to add the zone offset, so adding the translation to the
+// alternate epoch is cheap.  For example, having a non-negative time t
+// means that we can write
+//
+//     sec = t % 60
+//
+// instead of
+//
+//     sec = t % 60
+//     if sec < 0 {
+//             sec += 60
+//     }
+//
+// everywhere.
+// 
+// The calendar runs on an exact 400 year cycle: a 400-year calendar
+// printed for 1970-2469 will apply as well to 2470-2869.  Even the days
+// of the week match up.  It simplifies the computations to choose the
+// cycle boundaries so that the exceptional years are always delayed as
+// long as possible.  That means choosing a year equal to 1 mod 400, so
+// that the first leap year is the 4th year, the first missed leap year
+// is the 100th year, and the missed missed leap year is the 400th year.
+// So we'd prefer instead to print a calendar for 2001-2400 and reuse it
+// for 2401-2800.
+// 
+// Finally, it's convenient if the delta between the Unix epoch and
+// long-ago epoch is representable by an int64 constant.
+// 
+// These three considerations—choose an epoch as early as possible, that
+// uses a year equal to 1 mod 400, and that is no more than 2⁶³ seconds
+// earlier than 1970—bring us to the year -292277022399.  We refer to
+// this year as the absolute zero year, and to times measured as a uint64
+// seconds since this year as absolute times.
+// 
+// Times measured as an int64 seconds since the year 1—the representation
+// used for Time's sec field—are called internal times.
+// 
+// Times measured as an int64 seconds since the year 1970 are called Unix
+// times.
+// 
+// It is tempting to just use the year 1 as the absolute epoch, defining
+// that the routines are only valid for years >= 1.  However, the
+// routines would then be invalid when displaying the epoch in time zones
+// west of UTC, since it is year 0.  It doesn't seem tenable to say that
+// printing the zero time correctly isn't supported in half the time
+// zones.  By comparison, it's reasonable to mishandle some times in
+// the year -292277022399.
+// 
+// All this is opaque to clients of the API and can be changed if a
+// better implementation presents itself.
 
-func months(year int64) []int {
-       if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
-               return leapyear
+const (
+       // The unsigned zero year for internal calculations.
+       // Must be 1 mod 400, and times before it will not compute correctly,
+       // but otherwise can be changed at will.
+       absoluteZeroYear = -292277022399
+
+       // The year of the zero Time.
+       // Assumed by the unixToInternal computation below.
+       internalYear = 1
+
+       // The year of the zero Unix time.
+       unixYear = 1970
+
+       // Offsets to convert between internal and absolute or Unix times.
+       absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay
+       internalToAbsolute       = -absoluteToInternal
+
+       unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay
+       internalToUnix int64 = -unixToInternal
+)
+
+// IsZero reports whether t represents the zero time instant,
+// January 1, year 1, 00:00:00 UTC.
+func (t Time) IsZero() bool {
+       return t.sec == 0 && t.nsec == 0
+}
+
+// abs returns the time t as an absolute time, adjusted by the zone offset.
+// It is called when computing a presentation property like Month or Hour.
+func (t Time) abs() uint64 {
+       l := t.loc
+       if l == nil {
+               l = &utcLoc
        }
-       return nonleapyear
+       // Avoid function call if we hit the local time cache.
+       sec := t.sec + internalToUnix
+       if l != &utcLoc {
+               if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
+                       sec += int64(l.cacheZone.offset)
+               } else {
+                       _, offset, _, _, _ := l.lookup(sec)
+                       sec += int64(offset)
+               }
+       }
+       return uint64(sec + (unixToInternal + internalToAbsolute))
 }
 
-const (
-       secondsPerDay   = 24 * 60 * 60
-       daysPer400Years = 365*400 + 97
-       daysPer100Years = 365*100 + 24
-       daysPer4Years   = 365*4 + 1
-       days1970To2001  = 31*365 + 8
-)
+// Date returns the year, month, and day in which t occurs.
+func (t Time) Date() (year int, month Month, day int) {
+       year, month, day, _ = t.date(true)
+       return
+}
 
-// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
-// into a parsed Time value in the UTC time zone.
-func SecondsToUTC(sec int64) *Time {
-       t := new(Time)
+// Year returns the year in which t occurs.
+func (t Time) Year() int {
+       year, _, _, _ := t.date(false)
+       return year
+}
 
-       // Split into time and day.
-       day := sec / secondsPerDay
-       sec -= day * secondsPerDay
-       if sec < 0 {
-               day--
-               sec += secondsPerDay
+// Month returns the month of the year specified by t.
+func (t Time) Month() Month {
+       _, month, _, _ := t.date(true)
+       return month
+}
+
+// Day returns the day of the month specified by t.
+func (t Time) Day() int {
+       _, _, day, _ := t.date(true)
+       return day
+}
+
+// Weekday returns the day of the week specified by t.
+func (t Time) Weekday() Weekday {
+       // January 1 of the absolute year, like January 1 of 2001, was a Monday.
+       sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek
+       return Weekday(int(sec) / secondsPerDay)
+}
+
+// ISOWeek returns the ISO 8601 year and week number in which t occurs.
+// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to 
+// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1 
+// of year n+1.
+func (t Time) ISOWeek() (year, week int) {
+       year, month, day, yday := t.date(true)
+       wday := int(t.Weekday()+6) % 7 // weekday but Monday = 0.
+       const (
+               Mon int = iota
+               Tue
+               Wed
+               Thu
+               Fri
+               Sat
+               Sun
+       )
+
+       // Calculate week as number of Mondays in year up to
+       // and including today, plus 1 because the first week is week 0.
+       // Putting the + 1 inside the numerator as a + 7 keeps the
+       // numerator from being negative, which would cause it to
+       // round incorrectly.
+       week = (yday - wday + 7) / 7
+
+       // The week number is now correct under the assumption
+       // that the first Monday of the year is in week 1.
+       // If Jan 1 is a Tuesday, Wednesday, or Thursday, the first Monday
+       // is actually in week 2.
+       jan1wday := (wday - yday + 7*53) % 7
+       if Tue <= jan1wday && jan1wday <= Thu {
+               week++
        }
 
-       // Time
-       t.Hour = int(sec / 3600)
-       t.Minute = int((sec / 60) % 60)
-       t.Second = int(sec % 60)
-
-       // Change day from 0 = 1970 to 0 = 2001,
-       // to make leap year calculations easier
-       // (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
-       day -= days1970To2001
-
-       year := int64(2001)
-       if day < 0 {
-               // Go back enough 400 year cycles to make day positive.
-               n := -day/daysPer400Years + 1
-               year -= 400 * n
-               day += daysPer400Years * n
+       // If the week number is still 0, we're in early January but in
+       // the last week of last year.
+       if week == 0 {
+               year--
+               week = 52
+               // A year has 53 weeks when Jan 1 or Dec 31 is a Thursday,
+               // meaning Jan 1 of the next year is a Friday
+               // or it was a leap year and Jan 1 of the next year is a Saturday.
+               if jan1wday == Fri || (jan1wday == Sat && isLeap(year)) {
+                       week++
+               }
        }
 
-       // Cut off 400 year cycles.
-       n := day / daysPer400Years
-       year += 400 * n
-       day -= daysPer400Years * n
+       // December 29 to 31 are in week 1 of next year if
+       // they are after the last Thursday of the year and
+       // December 31 is a Monday, Tuesday, or Wednesday.
+       if month == December && day >= 29 && wday < Thu {
+               if dec31wday := (wday + 31 - day) % 7; Mon <= dec31wday && dec31wday <= Wed {
+                       year++
+                       week = 1
+               }
+       }
+
+       return
+}
+
+// Clock returns the hour, minute, and second within the day specified by t.
+func (t Time) Clock() (hour, min, sec int) {
+       sec = int(t.abs() % secondsPerDay)
+       hour = sec / secondsPerHour
+       sec -= hour * secondsPerHour
+       min = sec / secondsPerMinute
+       sec -= min * secondsPerMinute
+       return
+}
+
+// Hour returns the hour within the day specified by t, in the range [0, 23].
+func (t Time) Hour() int {
+       return int(t.abs()%secondsPerDay) / secondsPerHour
+}
+
+// Minute returns the minute offset within the hour specified by t, in the range [0, 59].
+func (t Time) Minute() int {
+       return int(t.abs()%secondsPerHour) / secondsPerMinute
+}
+
+// Second returns the second offset within the minute specified by t, in the range [0, 59].
+func (t Time) Second() int {
+       return int(t.abs() % secondsPerMinute)
+}
+
+// Nanosecond returns the nanosecond offset within the second specified by t,
+// in the range [0, 999999999].
+func (t Time) Nanosecond() int {
+       return int(t.nsec)
+}
+
+// A Duration represents the elapsed time between two instants
+// as an int64 nanosecond count.  The representation limits the
+// largest representable duration to approximately 290 years.
+type Duration int64
 
-       // Cut off 100-year cycles
-       n = day / daysPer100Years
-       if n > 3 { // happens on last day of 400th year
-               n = 3
+// Common durations.  There is no definition for units of Day or larger
+// to avoid confusion across daylight savings time zone transitions.
+const (
+       Nanosecond  Duration = 1
+       Microsecond          = 1000 * Nanosecond
+       Millisecond          = 1000 * Microsecond
+       Second               = 1000 * Millisecond
+       Minute               = 60 * Second
+       Hour                 = 60 * Minute
+)
+
+// Duration returns a string representing the duration in the form "72h3m0.5s".
+// Leading zero units are omitted.  As a special case, durations less than one
+// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure
+// that the leading digit is non-zero.  The zero duration formats as 0,
+// with no unit.
+func (d Duration) String() string {
+       // Largest time is 2540400h10m10.000000000s
+       var buf [32]byte
+       w := len(buf)
+
+       u := uint64(d)
+       neg := d < 0
+       if neg {
+               u = -u
        }
-       year += 100 * n
-       day -= daysPer100Years * n
 
-       // Cut off 4-year cycles
-       n = day / daysPer4Years
-       if n > 24 { // happens on last day of 100th year
-               n = 24
+       if u < uint64(Second) {
+               // Special case: if duration is smaller than a second,
+               // use smaller units, like 1.2ms
+               var (
+                       prec int
+                       unit byte
+               )
+               switch {
+               case u == 0:
+                       return "0"
+               case u < uint64(Microsecond):
+                       // print nanoseconds
+                       prec = 0
+                       unit = 'n'
+               case u < uint64(Millisecond):
+                       // print microseconds
+                       prec = 3
+                       unit = 'u'
+               default:
+                       // print milliseconds
+                       prec = 6
+                       unit = 'm'
+               }
+               w -= 2
+               buf[w] = unit
+               buf[w+1] = 's'
+               w, u = fmtFrac(buf[:w], u, prec)
+               w = fmtInt(buf[:w], u)
+       } else {
+               w--
+               buf[w] = 's'
+
+               w, u = fmtFrac(buf[:w], u, 9)
+
+               // u is now integer seconds
+               w = fmtInt(buf[:w], u%60)
+               u /= 60
+
+               // u is now integer minutes
+               if u > 0 {
+                       w--
+                       buf[w] = 'm'
+                       w = fmtInt(buf[:w], u%60)
+                       u /= 60
+
+                       // u is now integer hours
+                       // Stop at hours because days can be different lengths.
+                       if u > 0 {
+                               w--
+                               buf[w] = 'h'
+                               w = fmtInt(buf[:w], u)
+                       }
+               }
        }
-       year += 4 * n
-       day -= daysPer4Years * n
 
-       // Cut off non-leap years.
-       n = day / 365
-       if n > 3 { // happens on last day of 4th year
-               n = 3
+       if neg {
+               w--
+               buf[w] = '-'
        }
-       year += n
-       day -= 365 * n
 
-       t.Year = year
+       return string(buf[w:])
+}
 
-       // If someone ever needs yearday,
-       // tyearday = day (+1?)
+// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
+// tail of buf, omitting trailing zeros.  it omits the decimal
+// point too when the fraction is 0.  It returns the index where the
+// output bytes begin and the value v/10**prec.
+func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
+       // Omit trailing zeros up to and including decimal point.
+       w := len(buf)
+       print := false
+       for i := 0; i < prec; i++ {
+               digit := v % 10
+               print = print || digit != 0
+               if print {
+                       w--
+                       buf[w] = byte(digit) + '0'
+               }
+               v /= 10
+       }
+       if print {
+               w--
+               buf[w] = '.'
+       }
+       return w, v
+}
 
-       months := months(year)
-       var m int
-       yday := int(day)
-       for m = 0; m < 12 && yday >= months[m]; m++ {
-               yday -= months[m]
+// fmtInt formats v into the tail of buf.
+// It returns the index where the output begins.
+func fmtInt(buf []byte, v uint64) int {
+       w := len(buf)
+       if v == 0 {
+               w--
+               buf[w] = '0'
+       } else {
+               for v > 0 {
+                       w--
+                       buf[w] = byte(v%10) + '0'
+                       v /= 10
+               }
        }
-       t.Month = m + 1
-       t.Day = yday + 1
-       t.Zone = "UTC"
+       return w
+}
 
-       return t
+// Nanoseconds returns the duration as an integer nanosecond count.
+func (d Duration) Nanoseconds() int64 { return int64(d) }
+
+// These methods return float64 because the dominant
+// use case is for printing a floating point number like 1.5s, and
+// a truncation to integer would make them not useful in those cases.
+// Splitting the integer and fraction ourselves guarantees that
+// converting the returned float64 to an integer rounds the same
+// way that a pure integer conversion would have, even in cases
+// where, say, float64(d.Nanoseconds())/1e9 would have rounded
+// differently.
+
+// Seconds returns the duration as a floating point number of seconds.
+func (d Duration) Seconds() float64 {
+       sec := d / Second
+       nsec := d % Second
+       return float64(sec) + float64(nsec)*1e-9
 }
 
-// NanosecondsToUTC converts nsec, in number of nanoseconds since the Unix epoch,
-// into a parsed Time value in the UTC time zone.
-func NanosecondsToUTC(nsec int64) *Time {
-       // This one calls SecondsToUTC rather than the other way around because
-       // that admits a much larger span of time; NanosecondsToUTC is limited
-       // to a few hundred years only.
-       t := SecondsToUTC(nsec / 1e9)
-       t.Nanosecond = int(nsec % 1e9)
-       return t
+// Minutes returns the duration as a floating point number of minutes.
+func (d Duration) Minutes() float64 {
+       min := d / Minute
+       nsec := d % Minute
+       return float64(min) + float64(nsec)*(1e-9/60)
 }
 
-// UTC returns the current time as a parsed Time value in the UTC time zone.
-func UTC() *Time { return NanosecondsToUTC(Nanoseconds()) }
+// Hours returns the duration as a floating point number of hours.
+func (d Duration) Hours() float64 {
+       hour := d / Hour
+       nsec := d % Hour
+       return float64(hour) + float64(nsec)*(1e-9/60/60)
+}
 
-// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
-// into a parsed Time value in the local time zone.
-func SecondsToLocalTime(sec int64) *Time {
-       z, offset := lookupTimezone(sec)
-       t := SecondsToUTC(sec + int64(offset))
-       t.Zone = z
-       t.ZoneOffset = offset
+// Add returns the time t+d.
+func (t Time) Add(d Duration) Time {
+       t.sec += int64(d / 1e9)
+       t.nsec += int32(d % 1e9)
+       if t.nsec > 1e9 {
+               t.sec++
+               t.nsec -= 1e9
+       } else if t.nsec < 0 {
+               t.sec--
+               t.nsec += 1e9
+       }
        return t
 }
 
-// NanosecondsToLocalTime converts nsec, in number of nanoseconds since the Unix epoch,
-// into a parsed Time value in the local time zone.
-func NanosecondsToLocalTime(nsec int64) *Time {
-       t := SecondsToLocalTime(nsec / 1e9)
-       t.Nanosecond = int(nsec % 1e9)
-       return t
+// Sub returns the duration t-u.
+// To compute t-d for a duration d, use t.Add(-d).
+func (t Time) Sub(u Time) Duration {
+       return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
 }
 
-// LocalTime returns the current time as a parsed Time value in the local time zone.
-func LocalTime() *Time { return NanosecondsToLocalTime(Nanoseconds()) }
-
-// Seconds returns the number of seconds since January 1, 1970 represented by the
-// parsed Time value.
-func (t *Time) Seconds() int64 {
-       // First, accumulate days since January 1, 2001.
-       // Using 2001 instead of 1970 makes the leap-year
-       // handling easier (see SecondsToUTC), because
-       // it is at the beginning of the 4-, 100-, and 400-year cycles.
-       day := int64(0)
-
-       // Rewrite year to be >= 2001.
-       year := t.Year
-       if year < 2001 {
-               n := (2001-year)/400 + 1
-               year += 400 * n
-               day -= daysPer400Years * n
+const (
+       secondsPerMinute = 60
+       secondsPerHour   = 60 * 60
+       secondsPerDay    = 24 * secondsPerHour
+       secondsPerWeek   = 7 * secondsPerDay
+       daysPer400Years  = 365*400 + 97
+       daysPer100Years  = 365*100 + 24
+       daysPer4Years    = 365*4 + 1
+       days1970To2001   = 31*365 + 8
+)
+
+// date computes the year and, only when full=true,
+// the month and day in which t occurs.
+func (t Time) date(full bool) (year int, month Month, day int, yday int) {
+       // Split into time and day.
+       d := t.abs() / secondsPerDay
+
+       // Account for 400 year cycles.
+       n := d / daysPer400Years
+       y := 400 * n
+       d -= daysPer400Years * n
+
+       // Cut off 100-year cycles.
+       // The last cycle has one extra leap year, so on the last day
+       // of that year, day / daysPer100Years will be 4 instead of 3.
+       // Cut it back down to 3 by subtracting n>>2.
+       n = d / daysPer100Years
+       n -= n >> 2
+       y += 100 * n
+       d -= daysPer100Years * n
+
+       // Cut off 4-year cycles.
+       // The last cycle has a missing leap year, which does not
+       // affect the computation.
+       n = d / daysPer4Years
+       y += 4 * n
+       d -= daysPer4Years * n
+
+       // Cut off years within a 4-year cycle.
+       // The last year is a leap year, so on the last day of that year,
+       // day / 365 will be 4 instead of 3.  Cut it back down to 3
+       // by subtracting n>>2.
+       n = d / 365
+       n -= n >> 2
+       y += n
+       d -= 365 * n
+
+       year = int(int64(y) + absoluteZeroYear)
+       yday = int(d)
+
+       if !full {
+               return
        }
 
-       // Add in days from 400-year cycles.
-       n := (year - 2001) / 400
-       year -= 400 * n
-       day += daysPer400Years * n
+       day = yday
+       if isLeap(year) {
+               // Leap year
+               switch {
+               case day > 31+29-1:
+                       // After leap day; pretend it wasn't there.
+                       day--
+               case day == 31+29-1:
+                       // Leap day.
+                       month = February
+                       day = 29
+                       return
+               }
+       }
 
-       // Add in 100-year cycles.
-       n = (year - 2001) / 100
-       year -= 100 * n
-       day += daysPer100Years * n
+       // Estimate month on assumption that every month has 31 days.
+       // The estimate may be too low by at most one month, so adjust.
+       month = Month(day / 31)
+       end := int(daysBefore[month+1])
+       var begin int
+       if day >= end {
+               month++
+               begin = end
+       } else {
+               begin = int(daysBefore[month])
+       }
 
-       // Add in 4-year cycles.
-       n = (year - 2001) / 4
-       year -= 4 * n
-       day += daysPer4Years * n
+       month++ // because January is 1
+       day = day - begin + 1
+       return
+}
 
-       // Add in non-leap years.
-       n = year - 2001
-       day += 365 * n
+// daysBefore[m] counts the number of days in a non-leap year
+// before month m begins.  There is an entry for m=12, counting
+// the number of days before January of next year (365).
+var daysBefore = [...]int32{
+       0,
+       31,
+       31 + 28,
+       31 + 28 + 31,
+       31 + 28 + 31 + 30,
+       31 + 28 + 31 + 30 + 31,
+       31 + 28 + 31 + 30 + 31 + 30,
+       31 + 28 + 31 + 30 + 31 + 30 + 31,
+       31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+       31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+       31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+       31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+       31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
+}
 
-       // Add in days this year.
-       months := months(t.Year)
-       for m := 0; m < t.Month-1; m++ {
-               day += int64(months[m])
+func daysIn(m Month, year int) int {
+       if m == February && isLeap(year) {
+               return 29
        }
-       day += int64(t.Day - 1)
-
-       // Convert days to seconds since January 1, 2001.
-       sec := day * secondsPerDay
+       return int(daysBefore[m+1] - daysBefore[m])
+}
 
-       // Add in time elapsed today.
-       sec += int64(t.Hour) * 3600
-       sec += int64(t.Minute) * 60
-       sec += int64(t.Second)
+// Provided by package runtime.
+func now() (sec int64, nsec int32)
 
-       // Convert from seconds since 2001 to seconds since 1970.
-       sec += days1970To2001 * secondsPerDay
+// Now returns the current local time.
+func Now() Time {
+       sec, nsec := now()
+       return Time{sec + unixToInternal, nsec, Local}
+}
 
-       // Account for local time zone.
-       sec -= int64(t.ZoneOffset)
-       return sec
+// UTC returns t with the location set to UTC.
+func (t Time) UTC() Time {
+       t.loc = UTC
+       return t
 }
 
-// Nanoseconds returns the number of nanoseconds since January 1, 1970 represented by the
-// parsed Time value.
-func (t *Time) Nanoseconds() int64 {
-       return t.Seconds()*1e9 + int64(t.Nanosecond)
+// Local returns t with the location set to local time.
+func (t Time) Local() Time {
+       t.loc = Local
+       return t
 }
 
-// Weekday returns the time's day of the week. Sunday is day 0.
-func (t *Time) Weekday() int {
-       sec := t.Seconds() + int64(t.ZoneOffset)
-       day := sec / secondsPerDay
-       sec -= day * secondsPerDay
-       if sec < 0 {
-               day--
+// In returns t with the location information set to loc.
+//
+// In panics if loc is nil.
+func (t Time) In(loc *Location) Time {
+       if loc == nil {
+               panic("time: missing Location in call to Time.In")
        }
-       // Day 0 = January 1, 1970 was a Thursday
-       weekday := int((day + Thursday) % 7)
-       if weekday < 0 {
-               weekday += 7
-       }
-       return weekday
+       t.loc = loc
+       return t
 }
 
-// julianDayNumber returns the time's Julian Day Number
-// relative to the epoch 12:00 January 1, 4713 BC, Monday.
-func julianDayNumber(year int64, month, day int) int64 {
-       a := int64(14-month) / 12
-       y := year + 4800 - a
-       m := int64(month) + 12*a - 3
-       return int64(day) + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
+// Location returns the time zone information associated with t.
+func (t Time) Location() *Location {
+       l := t.loc
+       if l == nil {
+               l = UTC
+       }
+       return l
 }
 
-// startOfFirstWeek returns the julian day number of the first day
-// of the first week of the given year.
-func startOfFirstWeek(year int64) (d int64) {
-       jan01 := julianDayNumber(year, 1, 1)
-       weekday := (jan01 % 7) + 1
-       if weekday <= 4 {
-               d = jan01 - weekday + 1
-       } else {
-               d = jan01 + 8 - weekday
-       }
+// Zone computes the time zone in effect at time t, returning the abbreviated
+// name of the zone (such as "CET") and its offset in seconds east of UTC.
+func (t Time) Zone() (name string, offset int) {
+       name, offset, _, _, _ = t.loc.lookup(t.sec + internalToUnix)
        return
 }
 
-// dayOfWeek returns the weekday of the given date.
-func dayOfWeek(year int64, month, day int) int {
-       t := Time{Year: year, Month: month, Day: day}
-       return t.Weekday()
+// Unix returns the Unix time, the number of seconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) Unix() int64 {
+       return t.sec + internalToUnix
 }
 
-// ISOWeek returns the time's year and week number according to ISO 8601. 
-// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to 
-// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1 
-// of year n+1.
-func (t *Time) ISOWeek() (year int64, week int) {
-       d := julianDayNumber(t.Year, t.Month, t.Day)
-       week1Start := startOfFirstWeek(t.Year)
-
-       if d < week1Start {
-               // Previous year, week 52 or 53
-               year = t.Year - 1
-               if dayOfWeek(t.Year-1, 1, 1) == 4 || dayOfWeek(t.Year-1, 12, 31) == 4 {
-                       week = 53
-               } else {
-                       week = 52
+// UnixNano returns the Unix time, the number of nanoseconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) UnixNano() int64 {
+       return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
+}
+
+// Unix returns the local Time corresponding to the given Unix time,
+// sec seconds and nsec nanoseconds since January 1, 1970 UTC.
+// It is valid to pass nsec outside the range [0, 999999999].
+func Unix(sec int64, nsec int64) Time {
+       if nsec < 0 || nsec >= 1e9 {
+               n := nsec / 1e9
+               sec += n
+               nsec -= n * 1e9
+               if nsec < 0 {
+                       nsec += 1e9
+                       sec--
                }
-               return
        }
+       return Time{sec + unixToInternal, int32(nsec), Local}
+}
+
+func isLeap(year int) bool {
+       return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+}
+
+// norm returns nhi, nlo such that
+//     hi * base + lo == nhi * base + nlo
+//     0 <= nlo < base
+func norm(hi, lo, base int) (nhi, nlo int) {
+       if lo < 0 {
+               n := (-lo-1)/base + 1
+               hi -= n
+               lo += n * base
+       }
+       if lo >= base {
+               n := lo / base
+               hi += n
+               lo -= n * base
+       }
+       return hi, lo
+}
 
-       if d < startOfFirstWeek(t.Year+1) {
-               // Current year, week 01..52(,53)
-               year = t.Year
-               week = int((d-week1Start)/7 + 1)
-               return
+// Date returns the Time corresponding to
+//     yyyy-mm-dd hh:mm:ss + nsec nanoseconds
+// in the appropriate zone for that time in the given location.
+//
+// The month, day, hour, min, sec, and nsec values may be outside
+// their usual ranges and will be normalized during the conversion.
+// For example, October 32 converts to November 1.
+//
+// A daylight savings time transition skips or repeats times.
+// For example, in the United States, March 13, 2011 2:15am never occurred,
+// while November 6, 2011 1:15am occurred twice.  In such cases, the
+// choice of time zone, and therefore the time, is not well-defined.
+// Date returns a time that is correct in one of the two zones involved
+// in the transition, but it does not guarantee which.
+//
+// Date panics if loc is nil.
+func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {
+       if loc == nil {
+               panic("time: missing Location in call to Date")
        }
 
-       // Next year, week 1
-       year = t.Year + 1
-       week = 1
-       return
+       // Normalize month, overflowing into year.
+       m := int(month) - 1
+       year, m = norm(year, m, 12)
+       month = Month(m) + 1
+
+       // Normalize nsec, sec, min, hour, overflowing into day.
+       sec, nsec = norm(sec, nsec, 1e9)
+       min, sec = norm(min, sec, 60)
+       hour, min = norm(hour, min, 60)
+       day, hour = norm(day, hour, 24)
+
+       y := uint64(int64(year) - absoluteZeroYear)
+
+       // Compute days since the absolute epoch.
+
+       // Add in days from 400-year cycles.
+       n := y / 400
+       y -= 400 * n
+       d := daysPer400Years * n
+
+       // Add in 100-year cycles.
+       n = y / 100
+       y -= 100 * n
+       d += daysPer100Years * n
+
+       // Add in 4-year cycles.
+       n = y / 4
+       y -= 4 * n
+       d += daysPer4Years * n
+
+       // Add in non-leap years.
+       n = y
+       d += 365 * n
+
+       // Add in days before this month.
+       d += uint64(daysBefore[month-1])
+       if isLeap(year) && month >= March {
+               d++ // February 29
+       }
+
+       // Add in days before today.
+       d += uint64(day - 1)
+
+       // Add in time elapsed today.
+       abs := d * secondsPerDay
+       abs += uint64(hour*secondsPerHour + min*secondsPerMinute + sec)
+
+       unix := int64(abs) + (absoluteToInternal + internalToUnix)
+
+       // Look for zone offset for t, so we can adjust to UTC.
+       // The lookup function expects UTC, so we pass t in the
+       // hope that it will not be too close to a zone transition,
+       // and then adjust if it is.
+       _, offset, _, start, end := loc.lookup(unix)
+       if offset != 0 {
+               switch utc := unix - int64(offset); {
+               case utc < start:
+                       _, offset, _, _, _ = loc.lookup(start - 1)
+               case utc >= end:
+                       _, offset, _, _, _ = loc.lookup(end)
+               }
+               unix -= int64(offset)
+       }
+
+       return Time{unix + unixToInternal, int32(nsec), loc}
 }
index 01b8bea4aad495a6e933a5f9f62fc377f29896b8..9590e281a66bef30675fa011c285fdd0c140f906 100644 (file)
@@ -16,73 +16,89 @@ import (
 // won't be. The purpose of this test is to at least explain why some of
 // the subsequent tests fail.
 func TestZoneData(t *testing.T) {
-       lt := LocalTime()
+       lt := Now()
        // PST is 8 hours west, PDT is 7 hours west.  We could use the name but it's not unique.
-       if off := lt.ZoneOffset; off != -8*60*60 && off != -7*60*60 {
-               t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", lt.Zone, off)
+       if name, off := lt.Zone(); off != -8*60*60 && off != -7*60*60 {
+               t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", name, off)
                t.Error("Likely problem: the time zone files have not been installed.")
        }
 }
 
+// parsedTime is the struct representing a parsed time value.
+type parsedTime struct {
+       Year                 int
+       Month                Month
+       Day                  int
+       Hour, Minute, Second int // 15:04:05 is 15, 4, 5.
+       Nanosecond           int // Fractional second.
+       Weekday              Weekday
+       ZoneOffset           int    // seconds east of UTC, e.g. -7*60*60 for -0700
+       Zone                 string // e.g., "MST"
+}
+
 type TimeTest struct {
        seconds int64
-       golden  Time
+       golden  parsedTime
 }
 
 var utctests = []TimeTest{
-       {0, Time{1970, 1, 1, 0, 0, 0, 0, 0, "UTC"}},
-       {1221681866, Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}},
-       {-1221681866, Time{1931, 4, 16, 3, 55, 34, 0, 0, "UTC"}},
-       {-11644473600, Time{1601, 1, 1, 0, 0, 0, 0, 0, "UTC"}},
-       {599529660, Time{1988, 12, 31, 0, 1, 0, 0, 0, "UTC"}},
-       {978220860, Time{2000, 12, 31, 0, 1, 0, 0, 0, "UTC"}},
-       {1e18, Time{31688740476, 10, 23, 1, 46, 40, 0, 0, "UTC"}},
-       {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, 0, 0, "UTC"}},
-       {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, 0, 0, "UTC"}},
-       {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, 0, 0, "UTC"}},
+       {0, parsedTime{1970, January, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}},
+       {1221681866, parsedTime{2008, September, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}},
+       {-1221681866, parsedTime{1931, April, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}},
+       {-11644473600, parsedTime{1601, January, 1, 0, 0, 0, 0, Monday, 0, "UTC"}},
+       {599529660, parsedTime{1988, December, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}},
+       {978220860, parsedTime{2000, December, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}},
 }
 
 var nanoutctests = []TimeTest{
-       {0, Time{1970, 1, 1, 0, 0, 0, 1e8, 0, "UTC"}},
-       {1221681866, Time{2008, 9, 17, 20, 4, 26, 2e8, 0, "UTC"}},
+       {0, parsedTime{1970, January, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}},
+       {1221681866, parsedTime{2008, September, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}},
 }
 
 var localtests = []TimeTest{
-       {0, Time{1969, 12, 31, 16, 0, 0, 0, -8 * 60 * 60, "PST"}},
-       {1221681866, Time{2008, 9, 17, 13, 4, 26, 0, -7 * 60 * 60, "PDT"}},
+       {0, parsedTime{1969, December, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}},
+       {1221681866, parsedTime{2008, September, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}},
 }
 
 var nanolocaltests = []TimeTest{
-       {0, Time{1969, 12, 31, 16, 0, 0, 1e8, -8 * 60 * 60, "PST"}},
-       {1221681866, Time{2008, 9, 17, 13, 4, 26, 3e8, -7 * 60 * 60, "PDT"}},
-}
-
-func same(t, u *Time) bool {
-       return t.Year == u.Year &&
-               t.Month == u.Month &&
-               t.Day == u.Day &&
-               t.Hour == u.Hour &&
-               t.Minute == u.Minute &&
-               t.Second == u.Second &&
-               t.Nanosecond == u.Nanosecond &&
-               t.Weekday() == u.Weekday() &&
-               t.ZoneOffset == u.ZoneOffset &&
-               t.Zone == u.Zone
+       {0, parsedTime{1969, December, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}},
+       {1221681866, parsedTime{2008, September, 17, 13, 4, 26, 3e8, Wednesday, -7 * 60 * 60, "PDT"}},
+}
+
+func same(t Time, u *parsedTime) bool {
+       // Check aggregates.
+       year, month, day := t.Date()
+       hour, min, sec := t.Clock()
+       name, offset := t.Zone()
+       if year != u.Year || month != u.Month || day != u.Day ||
+               hour != u.Hour || min != u.Minute || sec != u.Second ||
+               name != u.Zone || offset != u.ZoneOffset {
+               return false
+       }
+       // Check individual entries.
+       return t.Year() == u.Year &&
+               t.Month() == u.Month &&
+               t.Day() == u.Day &&
+               t.Hour() == u.Hour &&
+               t.Minute() == u.Minute &&
+               t.Second() == u.Second &&
+               t.Nanosecond() == u.Nanosecond &&
+               t.Weekday() == u.Weekday
 }
 
 func TestSecondsToUTC(t *testing.T) {
        for _, test := range utctests {
                sec := test.seconds
                golden := &test.golden
-               tm := SecondsToUTC(sec)
-               newsec := tm.Seconds()
+               tm := Unix(sec, 0).UTC()
+               newsec := tm.Unix()
                if newsec != sec {
                        t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec)
                }
                if !same(tm, golden) {
-                       t.Errorf("SecondsToUTC(%d):", sec)
+                       t.Errorf("SecondsToUTC(%d):  // %#v", sec, tm)
                        t.Errorf("  want=%+v", *golden)
-                       t.Errorf("  have=%+v", *tm)
+                       t.Errorf("  have=%v", tm.Format(RFC3339+" MST"))
                }
        }
 }
@@ -91,15 +107,15 @@ func TestNanosecondsToUTC(t *testing.T) {
        for _, test := range nanoutctests {
                golden := &test.golden
                nsec := test.seconds*1e9 + int64(golden.Nanosecond)
-               tm := NanosecondsToUTC(nsec)
-               newnsec := tm.Nanoseconds()
+               tm := Unix(0, nsec).UTC()
+               newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
                if newnsec != nsec {
                        t.Errorf("NanosecondsToUTC(%d).Nanoseconds() = %d", nsec, newnsec)
                }
                if !same(tm, golden) {
                        t.Errorf("NanosecondsToUTC(%d):", nsec)
                        t.Errorf("  want=%+v", *golden)
-                       t.Errorf("  have=%+v", *tm)
+                       t.Errorf("  have=%+v", tm.Format(RFC3339+" MST"))
                }
        }
 }
@@ -108,38 +124,38 @@ func TestSecondsToLocalTime(t *testing.T) {
        for _, test := range localtests {
                sec := test.seconds
                golden := &test.golden
-               tm := SecondsToLocalTime(sec)
-               newsec := tm.Seconds()
+               tm := Unix(sec, 0)
+               newsec := tm.Unix()
                if newsec != sec {
                        t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec)
                }
                if !same(tm, golden) {
                        t.Errorf("SecondsToLocalTime(%d):", sec)
                        t.Errorf("  want=%+v", *golden)
-                       t.Errorf("  have=%+v", *tm)
+                       t.Errorf("  have=%+v", tm.Format(RFC3339+" MST"))
                }
        }
 }
 
-func TestNanoecondsToLocalTime(t *testing.T) {
+func TestNanosecondsToLocalTime(t *testing.T) {
        for _, test := range nanolocaltests {
                golden := &test.golden
                nsec := test.seconds*1e9 + int64(golden.Nanosecond)
-               tm := NanosecondsToLocalTime(nsec)
-               newnsec := tm.Nanoseconds()
+               tm := Unix(0, nsec)
+               newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
                if newnsec != nsec {
                        t.Errorf("NanosecondsToLocalTime(%d).Seconds() = %d", nsec, newnsec)
                }
                if !same(tm, golden) {
                        t.Errorf("NanosecondsToLocalTime(%d):", nsec)
                        t.Errorf("  want=%+v", *golden)
-                       t.Errorf("  have=%+v", *tm)
+                       t.Errorf("  have=%+v", tm.Format(RFC3339+" MST"))
                }
        }
 }
 
 func TestSecondsToUTCAndBack(t *testing.T) {
-       f := func(sec int64) bool { return SecondsToUTC(sec).Seconds() == sec }
+       f := func(sec int64) bool { return Unix(sec, 0).UTC().Unix() == sec }
        f32 := func(sec int32) bool { return f(int64(sec)) }
        cfg := &quick.Config{MaxCount: 10000}
 
@@ -153,7 +169,11 @@ func TestSecondsToUTCAndBack(t *testing.T) {
 }
 
 func TestNanosecondsToUTCAndBack(t *testing.T) {
-       f := func(nsec int64) bool { return NanosecondsToUTC(nsec).Nanoseconds() == nsec }
+       f := func(nsec int64) bool {
+               t := Unix(0, nsec).UTC()
+               ns := t.Unix()*1e9 + int64(t.Nanosecond())
+               return ns == nsec
+       }
        f32 := func(nsec int32) bool { return f(int64(nsec)) }
        cfg := &quick.Config{MaxCount: 10000}
 
@@ -173,9 +193,9 @@ type TimeFormatTest struct {
 }
 
 var rfc3339Formats = []TimeFormatTest{
-       {Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}, "2008-09-17T20:04:26Z"},
-       {Time{1994, 9, 17, 20, 4, 26, 0, -18000, "EST"}, "1994-09-17T20:04:26-05:00"},
-       {Time{2000, 12, 26, 1, 15, 6, 0, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"},
+       {Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"},
+       {Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"},
+       {Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "2000-12-26T01:15:06+04:20"},
 }
 
 func TestRFC3339Conversion(t *testing.T) {
@@ -216,7 +236,7 @@ var formatTests = []FormatTest{
 
 func TestFormat(t *testing.T) {
        // The numeric time represents Thu Feb  4 21:00:57.012345678 PST 2010
-       time := NanosecondsToLocalTime(1233810057012345678)
+       time := Unix(0, 1233810057012345678)
        for _, test := range formatTests {
                result := time.Format(test.format)
                if result != test.result {
@@ -229,10 +249,10 @@ type ParseTest struct {
        name       string
        format     string
        value      string
-       hasTZ      bool  // contains a time zone
-       hasWD      bool  // contains a weekday
-       yearSign   int64 // sign of year
-       fracDigits int   // number of digits of fractional second
+       hasTZ      bool // contains a time zone
+       hasWD      bool // contains a weekday
+       yearSign   int  // sign of year
+       fracDigits int  // number of digits of fractional second
 }
 
 var parseTests = []ParseTest{
@@ -298,47 +318,48 @@ func TestRubyParse(t *testing.T) {
        }
 }
 
-func checkTime(time *Time, test *ParseTest, t *testing.T) {
+func checkTime(time Time, test *ParseTest, t *testing.T) {
        // The time should be Thu Feb  4 21:00:57 PST 2010
-       if test.yearSign*time.Year != 2010 {
-               t.Errorf("%s: bad year: %d not %d", test.name, time.Year, 2010)
+       if test.yearSign*time.Year() != 2010 {
+               t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010)
        }
-       if time.Month != 2 {
-               t.Errorf("%s: bad month: %d not %d", test.name, time.Month, 2)
+       if time.Month() != February {
+               t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February)
        }
-       if time.Day != 4 {
-               t.Errorf("%s: bad day: %d not %d", test.name, time.Day, 4)
+       if time.Day() != 4 {
+               t.Errorf("%s: bad day: %d not %d", test.name, time.Day(), 4)
        }
-       if time.Hour != 21 {
-               t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour, 21)
+       if time.Hour() != 21 {
+               t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour(), 21)
        }
-       if time.Minute != 0 {
-               t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute, 0)
+       if time.Minute() != 0 {
+               t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute(), 0)
        }
-       if time.Second != 57 {
-               t.Errorf("%s: bad second: %d not %d", test.name, time.Second, 57)
+       if time.Second() != 57 {
+               t.Errorf("%s: bad second: %d not %d", test.name, time.Second(), 57)
        }
        // Nanoseconds must be checked against the precision of the input.
        nanosec, err := strconv.Atoui("012345678"[:test.fracDigits] + "000000000"[:9-test.fracDigits])
        if err != nil {
                panic(err)
        }
-       if time.Nanosecond != int(nanosec) {
-               t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond, nanosec)
+       if time.Nanosecond() != int(nanosec) {
+               t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec)
        }
-       if test.hasTZ && time.ZoneOffset != -28800 {
-               t.Errorf("%s: bad tz offset: %d not %d", test.name, time.ZoneOffset, -28800)
+       name, offset := time.Zone()
+       if test.hasTZ && offset != -28800 {
+               t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800)
        }
-       if test.hasWD && time.Weekday() != 4 {
-               t.Errorf("%s: bad weekday: %d not %d", test.name, time.Weekday(), 4)
+       if test.hasWD && time.Weekday() != Thursday {
+               t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday)
        }
 }
 
 func TestFormatAndParse(t *testing.T) {
        const fmt = "Mon MST " + RFC3339 // all fields
        f := func(sec int64) bool {
-               t1 := SecondsToLocalTime(sec)
-               if t1.Year < 1000 || t1.Year > 9999 {
+               t1 := Unix(sec, 0)
+               if t1.Year() < 1000 || t1.Year() > 9999 {
                        // not required to work
                        return true
                }
@@ -347,8 +368,8 @@ func TestFormatAndParse(t *testing.T) {
                        t.Errorf("error: %s", err)
                        return false
                }
-               if !same(t1, t2) {
-                       t.Errorf("different: %q %q", t1, t2)
+               if t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() {
+                       t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix())
                        return false
                }
                return true
@@ -394,7 +415,7 @@ func TestParseErrors(t *testing.T) {
 }
 
 func TestNoonIs12PM(t *testing.T) {
-       noon := Time{Hour: 12}
+       noon := Date(0, January, 1, 12, 0, 0, 0, UTC)
        const expect = "12:00PM"
        got := noon.Format("3:04PM")
        if got != expect {
@@ -407,7 +428,7 @@ func TestNoonIs12PM(t *testing.T) {
 }
 
 func TestMidnightIs12AM(t *testing.T) {
-       midnight := Time{Hour: 0}
+       midnight := Date(0, January, 1, 0, 0, 0, 0, UTC)
        expect := "12:00AM"
        got := midnight.Format("3:04PM")
        if got != expect {
@@ -424,15 +445,15 @@ func Test12PMIsNoon(t *testing.T) {
        if err != nil {
                t.Fatal("error parsing date:", err)
        }
-       if noon.Hour != 12 {
-               t.Errorf("got %d; expect 12", noon.Hour)
+       if noon.Hour() != 12 {
+               t.Errorf("got %d; expect 12", noon.Hour())
        }
        noon, err = Parse("03:04PM", "12:00PM")
        if err != nil {
                t.Fatal("error parsing date:", err)
        }
-       if noon.Hour != 12 {
-               t.Errorf("got %d; expect 12", noon.Hour)
+       if noon.Hour() != 12 {
+               t.Errorf("got %d; expect 12", noon.Hour())
        }
 }
 
@@ -441,15 +462,15 @@ func Test12AMIsMidnight(t *testing.T) {
        if err != nil {
                t.Fatal("error parsing date:", err)
        }
-       if midnight.Hour != 0 {
-               t.Errorf("got %d; expect 0", midnight.Hour)
+       if midnight.Hour() != 0 {
+               t.Errorf("got %d; expect 0", midnight.Hour())
        }
        midnight, err = Parse("03:04PM", "12:00AM")
        if err != nil {
                t.Fatal("error parsing date:", err)
        }
-       if midnight.Hour != 0 {
-               t.Errorf("got %d; expect 0", midnight.Hour)
+       if midnight.Hour() != 0 {
+               t.Errorf("got %d; expect 0", midnight.Hour())
        }
 }
 
@@ -463,7 +484,7 @@ func TestMissingZone(t *testing.T) {
        expect := "Thu Feb  2 16:10:03 -0500 2006" // -0500 not EST
        str := time.Format(UnixDate)               // uses MST as its time zone
        if str != expect {
-               t.Errorf("expected %q got %q", expect, str)
+               t.Errorf("got %s; expect %s", str, expect)
        }
 }
 
@@ -473,16 +494,17 @@ func TestMinutesInTimeZone(t *testing.T) {
                t.Fatal("error parsing date:", err)
        }
        expected := (1*60 + 23) * 60
-       if time.ZoneOffset != expected {
-               t.Errorf("ZoneOffset incorrect, expected %d got %d", expected, time.ZoneOffset)
+       _, offset := time.Zone()
+       if offset != expected {
+               t.Errorf("ZoneOffset = %d, want %d", offset, expected)
        }
 }
 
 type ISOWeekTest struct {
-       year       int64 // year
-       month, day int   // month and day
-       yex        int64 // expected year
-       wex        int   // expected week
+       year       int // year
+       month, day int // month and day
+       yex        int // expected year
+       wex        int // expected week
 }
 
 var isoWeekTests = []ISOWeekTest{
@@ -524,7 +546,7 @@ var isoWeekTests = []ISOWeekTest{
 func TestISOWeek(t *testing.T) {
        // Selected dates and corner cases
        for _, wt := range isoWeekTests {
-               dt := &Time{Year: wt.year, Month: wt.month, Day: wt.day}
+               dt := Date(wt.year, Month(wt.month), wt.day, 0, 0, 0, 0, UTC)
                y, w := dt.ISOWeek()
                if w != wt.wex || y != wt.yex {
                        t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d",
@@ -533,27 +555,91 @@ func TestISOWeek(t *testing.T) {
        }
 
        // The only real invariant: Jan 04 is in week 1
-       for year := int64(1950); year < 2100; year++ {
-               if y, w := (&Time{Year: year, Month: 1, Day: 4}).ISOWeek(); y != year || w != 1 {
+       for year := 1950; year < 2100; year++ {
+               if y, w := Date(year, January, 4, 0, 0, 0, 0, UTC).ISOWeek(); y != year || w != 1 {
                        t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year)
                }
        }
 }
 
-func BenchmarkSeconds(b *testing.B) {
-       for i := 0; i < b.N; i++ {
-               Seconds()
+var durationTests = []struct {
+       str string
+       d   Duration
+}{
+       {"0", 0},
+       {"1ns", 1 * Nanosecond},
+       {"1.1us", 1100 * Nanosecond},
+       {"2.2ms", 2200 * Microsecond},
+       {"3.3s", 3300 * Millisecond},
+       {"4m5s", 4*Minute + 5*Second},
+       {"4m5.001s", 4*Minute + 5001*Millisecond},
+       {"5h6m7.001s", 5*Hour + 6*Minute + 7001*Millisecond},
+       {"8m0.000000001s", 8*Minute + 1*Nanosecond},
+       {"2562047h47m16.854775807s", 1<<63 - 1},
+       {"-2562047h47m16.854775808s", -1 << 63},
+}
+
+func TestDurationString(t *testing.T) {
+       for _, tt := range durationTests {
+               if str := tt.d.String(); str != tt.str {
+                       t.Errorf("Duration(%d).String() = %s, want %s", int64(tt.d), str, tt.str)
+               }
+               if tt.d > 0 {
+                       if str := (-tt.d).String(); str != "-"+tt.str {
+                               t.Errorf("Duration(%d).String() = %s, want %s", int64(-tt.d), str, "-"+tt.str)
+                       }
+               }
        }
 }
 
-func BenchmarkNanoseconds(b *testing.B) {
+var dateTests = []struct {
+       year, month, day, hour, min, sec, nsec int
+       z                                      *Location
+       unix                                   int64
+}{
+       {2011, 11, 6, 1, 0, 0, 0, Local, 1320566400},   // 1:00:00 PDT
+       {2011, 11, 6, 1, 59, 59, 0, Local, 1320569999}, // 1:59:59 PDT
+       {2011, 11, 6, 2, 0, 0, 0, Local, 1320573600},   // 2:00:00 PST
+
+       {2011, 3, 13, 1, 0, 0, 0, Local, 1300006800},   // 1:00:00 PST
+       {2011, 3, 13, 1, 59, 59, 0, Local, 1300010399}, // 1:59:59 PST
+       {2011, 3, 13, 3, 0, 0, 0, Local, 1300010400},   // 3:00:00 PDT
+       {2011, 3, 13, 2, 30, 0, 0, Local, 1300008600},  // 2:30:00 PDT ≡ 1:30 PST
+
+       // Many names for Fri Nov 18 7:56:35 PST 2011
+       {2011, 11, 18, 7, 56, 35, 0, Local, 1321631795},                 // Nov 18 7:56:35
+       {2011, 11, 19, -17, 56, 35, 0, Local, 1321631795},               // Nov 19 -17:56:35
+       {2011, 11, 17, 31, 56, 35, 0, Local, 1321631795},                // Nov 17 31:56:35
+       {2011, 11, 18, 6, 116, 35, 0, Local, 1321631795},                // Nov 18 6:116:35
+       {2011, 10, 49, 7, 56, 35, 0, Local, 1321631795},                 // Oct 49 7:56:35
+       {2011, 11, 18, 7, 55, 95, 0, Local, 1321631795},                 // Nov 18 7:55:95
+       {2011, 11, 18, 7, 56, 34, 1e9, Local, 1321631795},               // Nov 18 7:56:34 + 10⁹ns
+       {2011, 12, -12, 7, 56, 35, 0, Local, 1321631795},                // Dec -21 7:56:35
+       {2012, 1, -43, 7, 56, 35, 0, Local, 1321631795},                 // Jan -52 7:56:35 2012
+       {2012, int(January - 2), 18, 7, 56, 35, 0, Local, 1321631795},   // (Jan-2) 18 7:56:35 2012
+       {2010, int(December + 11), 18, 7, 56, 35, 0, Local, 1321631795}, // (Dec+11) 18 7:56:35 2010
+}
+
+func TestDate(t *testing.T) {
+       for _, tt := range dateTests {
+               time := Date(tt.year, Month(tt.month), tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z)
+               want := Unix(tt.unix, 0)
+               if !time.Equal(want) {
+                       t.Errorf("Date(%d, %d, %d, %d, %d, %d, %d, %s) = %v, want %v",
+                               tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z,
+                               time, want)
+               }
+       }
+}
+
+func BenchmarkNow(b *testing.B) {
        for i := 0; i < b.N; i++ {
-               Nanoseconds()
+               Now()
        }
 }
 
 func BenchmarkFormat(b *testing.B) {
-       time := SecondsToLocalTime(1265346057)
+       time := Unix(1265346057, 0)
        for i := 0; i < b.N; i++ {
                time.Format("Mon Jan  2 15:04:05 2006")
        }
@@ -564,3 +650,31 @@ func BenchmarkParse(b *testing.B) {
                Parse(ANSIC, "Mon Jan  2 15:04:05 2006")
        }
 }
+
+func BenchmarkHour(b *testing.B) {
+       t := Now()
+       for i := 0; i < b.N; i++ {
+               _ = t.Hour()
+       }
+}
+
+func BenchmarkSecond(b *testing.B) {
+       t := Now()
+       for i := 0; i < b.N; i++ {
+               _ = t.Second()
+       }
+}
+
+func BenchmarkYear(b *testing.B) {
+       t := Now()
+       for i := 0; i < b.N; i++ {
+               _ = t.Year()
+       }
+}
+
+func BenchmarkDay(b *testing.B) {
+       t := Now()
+       for i := 0; i < b.N; i++ {
+               _ = t.Day()
+       }
+}
diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go
new file mode 100644 (file)
index 0000000..aca56e7
--- /dev/null
@@ -0,0 +1,191 @@
+// 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 "sync"
+
+// A Location maps time instants to the zone in use at that time.
+// Typically, the Location represents the collection of time offsets
+// in use in a geographical area, such as CEST and CET for central Europe.
+type Location struct {
+       name string
+       zone []zone
+       tx   []zoneTrans
+
+       // Most lookups will be for the current time.
+       // To avoid the binary search through tx, keep a
+       // static one-element cache that gives the correct
+       // zone for the time when the Location was created.
+       // if cacheStart <= t <= cacheEnd,
+       // lookup can return cacheZone.
+       // The units for cacheStart and cacheEnd are seconds
+       // since January 1, 1970 UTC, to match the argument
+       // to lookup.
+       cacheStart int64
+       cacheEnd   int64
+       cacheZone  *zone
+}
+
+// A zone represents a single time zone such as CEST or CET.
+type zone struct {
+       name   string // abbreviated name, "CET"
+       offset int    // seconds east of UTC
+       isDST  bool   // is this zone Daylight Savings Time?
+}
+
+// A zoneTrans represents a single time zone transition.
+type zoneTrans struct {
+       when         int64 // transition time, in seconds since 1970 GMT
+       index        uint8 // the index of the zone that goes into effect at that time
+       isstd, isutc bool  // ignored - no idea what these mean
+}
+
+// UTC represents Universal Coordinated Time (UTC).
+var UTC *Location = &utcLoc
+
+// utcLoc is separate so that get can refer to &utcLoc
+// and ensure that it never returns a nil *Location,
+// even if a badly behaved client has changed UTC.
+var utcLoc = Location{name: "UTC"}
+
+// Local represents the system's local time zone.
+var Local *Location = &localLoc
+
+// localLoc is separate so that initLocal can initialize
+// it even if a client has changed Local.
+var localLoc Location
+var localOnce sync.Once
+
+func (l *Location) get() *Location {
+       if l == nil {
+               return &utcLoc
+       }
+       if l == &localLoc {
+               localOnce.Do(initLocal)
+       }
+       return l
+}
+
+// String returns a descriptive name for the time zone information,
+// corresponding to the argument to LoadLocation.
+func (l *Location) String() string {
+       return l.get().name
+}
+
+// FixedZone returns a Location that always uses
+// the given zone name and offset (seconds east of UTC).
+func FixedZone(name string, offset int) *Location {
+       l := &Location{
+               name:       name,
+               zone:       []zone{{name, offset, false}},
+               tx:         []zoneTrans{{-1 << 63, 0, false, false}},
+               cacheStart: -1 << 63,
+               cacheEnd:   1<<63 - 1,
+       }
+       l.cacheZone = &l.zone[0]
+       return l
+}
+
+// lookup returns information about the time zone in use at an
+// instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
+//
+// The returned information gives the name of the zone (such as "CET"),
+// the start and end times bracketing sec when that zone is in effect,
+// the offset in seconds east of UTC (such as -5*60*60), and whether
+// the daylight savings is being observed at that time.
+func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) {
+       l = l.get()
+
+       if len(l.tx) == 0 {
+               name = "UTC"
+               offset = 0
+               isDST = false
+               start = -1 << 63
+               end = 1<<63 - 1
+               return
+       }
+
+       if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
+               name = zone.name
+               offset = zone.offset
+               isDST = zone.isDST
+               start = l.cacheStart
+               end = l.cacheEnd
+               return
+       }
+
+       // Binary search for entry with largest time <= sec.
+       // Not using sort.Search to avoid dependencies.
+       tx := l.tx
+       end = 1<<63 - 1
+       for len(tx) > 1 {
+               m := len(tx) / 2
+               lim := tx[m].when
+               if sec < lim {
+                       end = lim
+                       tx = tx[0:m]
+               } else {
+                       tx = tx[m:]
+               }
+       }
+       zone := &l.zone[tx[0].index]
+       name = zone.name
+       offset = zone.offset
+       isDST = zone.isDST
+       start = tx[0].when
+       // end = maintained during the search
+       return
+}
+
+// lookupName returns information about the time zone with
+// the given name (such as "EST").
+func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) {
+       l = l.get()
+       for i := range l.zone {
+               zone := &l.zone[i]
+               if zone.name == name {
+                       return zone.offset, zone.isDST, true
+               }
+       }
+       return
+}
+
+// lookupOffset returns information about the time zone with
+// the given offset (such as -5*60*60).
+func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
+       l = l.get()
+       for i := range l.zone {
+               zone := &l.zone[i]
+               if zone.offset == offset {
+                       return zone.name, zone.isDST, true
+               }
+       }
+       return
+}
+
+// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
+// syntax too, but I don't feel like implementing it today.
+
+// NOTE(rsc): Using the IANA names below means ensuring we have access
+// to the database.  Probably we will ship the files in $GOROOT/lib/zoneinfo/
+// and only look there if there are no system files available (such as on Windows).
+// The files total 200 kB.
+
+// LoadLocation returns the Location with the given name.
+//
+// If the name is "" or "UTC", LoadLocation returns UTC.
+// If the name is "Local", LoadLocation returns Local.
+//
+// Otherwise, the name is taken to be a location name corresponding to a file
+// in the IANA Time Zone database, such as "America/New_York".
+func LoadLocation(name string) (*Location, error) {
+       if name == "" || name == "UTC" {
+               return UTC, nil
+       }
+       if name == "Local" {
+               return Local, nil
+       }
+       return loadLocation(name)
+}
index 577ef85bd68be56e5a9eadb1de005457d32ab184..38aefc7a977c729b1d2eb6265cbf02b26dcea09e 100644 (file)
@@ -6,11 +6,10 @@
 
 package time
 
-import (
-       "os"
-       "strconv"
-       "strings"
-)
+//import (
+//     "strconv"
+//     "strings"
+//)
 
 func parseZones(s string) (zt []zonetime) {
        f := strings.Fields(s)
@@ -49,7 +48,7 @@ func parseZones(s string) (zt []zonetime) {
        return
 }
 
-func setupZone() {
+func initLocal() {
        t, err := os.Getenverror("timezone")
        if err != nil {
                // do nothing: use UTC
@@ -58,16 +57,8 @@ 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)
+func initTestingZone() {
+       buf, err := readFile("/adm/timezone/US_Pacific")
        if err != nil {
                return
        }
diff --git a/libgo/go/time/zoneinfo_posix.go b/libgo/go/time/zoneinfo_posix.go
deleted file mode 100644 (file)
index b0fa6c3..0000000
+++ /dev/null
@@ -1,64 +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.
-
-// +build darwin freebsd linux openbsd plan9
-
-package time
-
-import "sync"
-
-// Parsed representation
-type zone struct {
-       utcoff int
-       isdst  bool
-       name   string
-}
-
-type zonetime struct {
-       time         int32 // transition time, in seconds since 1970 GMT
-       zone         *zone // the zone that goes into effect at that time
-       isstd, isutc bool  // ignored - no idea what these mean
-}
-
-var zones []zonetime
-var onceSetupZone sync.Once
-
-// 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)
-       if len(zones) == 0 {
-               return "UTC", 0
-       }
-
-       // Binary search for entry with largest time <= sec
-       tz := zones
-       for len(tz) > 1 {
-               m := len(tz) / 2
-               if sec < int64(tz[m].time) {
-                       tz = tz[0:m]
-               } else {
-                       tz = tz[m:]
-               }
-       }
-       z := tz[0].zone
-       return z.name, z.utcoff
-}
-
-// lookupByName returns the time offset for the
-// time zone with the given abbreviation. It only considers
-// time zones that apply to the current system.
-// For example, for a system configured as being in New York,
-// it only recognizes "EST" and "EDT".
-// For a system in San Francisco, "PST" and "PDT".
-// For a system in Sydney, "EST" and "EDT", though they have
-// different meanings than they do in New York.
-func lookupByName(name string) (off int, found bool) {
-       onceSetupZone.Do(setupZone)
-       for _, z := range zones {
-               if name == z.zone.name {
-                       return z.zone.utcoff, true
-               }
-       }
-       return 0, false
-}
index b552e589aa947a08a97d0fbb9dcadb458330ff79..83d5b983c6ec928bea451b346bc3d5652a8712c8 100644 (file)
@@ -12,8 +12,8 @@
 package time
 
 import (
-       "bytes"
-       "os"
+       "errors"
+       "syscall"
 )
 
 const (
@@ -65,18 +65,20 @@ func byteString(p []byte) string {
        return string(p)
 }
 
-func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
+var badData = errors.New("malformed time zone information")
+
+func loadZoneData(bytes []byte) (l *Location, err error) {
        d := data{bytes, false}
 
        // 4-byte magic "TZif"
        if magic := d.read(4); string(magic) != "TZif" {
-               return nil, false
+               return nil, badData
        }
 
        // 1-byte version, then 15 bytes of padding
        var p []byte
        if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
-               return nil, false
+               return nil, badData
        }
 
        // six big-endian 32-bit integers:
@@ -98,7 +100,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
        for i := 0; i < 6; i++ {
                nn, ok := d.big4()
                if !ok {
-                       return nil, false
+                       return nil, badData
                }
                n[i] = int(nn)
        }
@@ -127,7 +129,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
        isutc := d.read(n[NUTCLocal])
 
        if d.error { // ran out of data
-               return nil, false
+               return nil, badData
        }
 
        // If version == 2, the entire file repeats, this time using
@@ -137,90 +139,119 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
        // Now we can build up a useful data structure.
        // First the zone information.
        //      utcoff[4] isdst[1] nameindex[1]
-       z := make([]zone, n[NZone])
-       for i := 0; i < len(z); i++ {
+       zone := make([]zone, n[NZone])
+       for i := range zone {
                var ok bool
                var n uint32
                if n, ok = zonedata.big4(); !ok {
-                       return nil, false
+                       return nil, badData
                }
-               z[i].utcoff = int(n)
+               zone[i].offset = int(n)
                var b byte
                if b, ok = zonedata.byte(); !ok {
-                       return nil, false
+                       return nil, badData
                }
-               z[i].isdst = b != 0
+               zone[i].isDST = b != 0
                if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
-                       return nil, false
+                       return nil, badData
                }
-               z[i].name = byteString(abbrev[b:])
+               zone[i].name = byteString(abbrev[b:])
        }
 
        // Now the transition time info.
-       zt = make([]zonetime, n[NTime])
-       for i := 0; i < len(zt); i++ {
+       tx := make([]zoneTrans, n[NTime])
+       for i := range tx {
                var ok bool
                var n uint32
                if n, ok = txtimes.big4(); !ok {
-                       return nil, false
+                       return nil, badData
                }
-               zt[i].time = int32(n)
-               if int(txzones[i]) >= len(z) {
-                       return nil, false
+               tx[i].when = int64(int32(n))
+               if int(txzones[i]) >= len(zone) {
+                       return nil, badData
                }
-               zt[i].zone = &z[txzones[i]]
+               tx[i].index = txzones[i]
                if i < len(isstd) {
-                       zt[i].isstd = isstd[i] != 0
+                       tx[i].isstd = isstd[i] != 0
                }
                if i < len(isutc) {
-                       zt[i].isutc = isutc[i] != 0
+                       tx[i].isutc = isutc[i] != 0
                }
        }
-       return zt, true
-}
 
-func readinfofile(name string) ([]zonetime, bool) {
-       var b bytes.Buffer
+       // Commited to succeed.
+       l = &Location{zone: zone, tx: tx}
 
-       f, err := os.Open(name)
-       if err != nil {
-               return nil, false
+       // Fill in the cache with information about right now,
+       // since that will be the most common lookup.
+       sec, _ := now()
+       for i := range tx {
+               if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
+                       l.cacheStart = tx[i].when
+                       l.cacheEnd = 1<<63 - 1
+                       if i+1 < len(tx) {
+                               l.cacheEnd = tx[i+1].when
+                       }
+                       l.cacheZone = &l.zone[tx[i].index]
+               }
        }
-       defer f.Close()
-       if _, err := b.ReadFrom(f); err != nil {
-               return nil, false
+
+       return l, nil
+}
+
+func loadZoneFile(name string) (l *Location, err error) {
+       buf, err := readFile(name)
+       if err != nil {
+               return
        }
-       return parseinfo(b.Bytes())
+       return loadZoneData(buf)
 }
 
-func setupTestingZone() {
-       os.Setenv("TZ", "America/Los_Angeles")
-       setupZone()
+func initTestingZone() {
+       syscall.Setenv("TZ", "America/Los_Angeles")
+       initLocal()
 }
 
-func setupZone() {
+// Many systems use /usr/share/zoneinfo, Solaris 2 has
+// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
+var zoneDirs = []string{
+       "/usr/share/zoneinfo/",
+       "/usr/share/lib/zoneinfo/",
+       "/usr/lib/locale/TZ/",
+}
+
+func initLocal() {
        // consult $TZ to find the time zone to use.
        // no $TZ means use the system default /etc/localtime.
        // $TZ="" means use UTC.
        // $TZ="foo" means use /usr/share/zoneinfo/foo.
-       // Many systems use /usr/share/zoneinfo, Solaris 2 has
-       // /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
-       zoneDirs := []string{"/usr/share/zoneinfo/",
-               "/usr/share/lib/zoneinfo/",
-               "/usr/lib/locale/TZ/"}
 
-       tz, err := os.Getenverror("TZ")
+       tz, ok := syscall.Getenv("TZ")
        switch {
-       case err == os.ENOENV:
-               zones, _ = readinfofile("/etc/localtime")
-       case len(tz) > 0:
-               for _, zoneDir := range zoneDirs {
-                       var ok bool
-                       if zones, ok = readinfofile(zoneDir + tz); ok {
-                               break
-                       }
+       case !ok:
+               z, err := loadZoneFile("/etc/localtime")
+               if err == nil {
+                       localLoc = *z
+                       localLoc.name = "Local"
+                       return
+               }
+       case tz != "" && tz != "UTC":
+               if z, err := loadLocation(tz); err == nil {
+                       localLoc = *z
+                       return
+               }
+       }
+
+       // Fall back to UTC.
+       localLoc.name = "UTC"
+}
+
+func loadLocation(name string) (*Location, error) {
+       for _, zoneDir := range zoneDirs {
+               if z, err := loadZoneFile(zoneDir + name); err == nil {
+                       z.name = name
+                       return z, nil
                }
-       case len(tz) == 0:
-               // do nothing: use UTC
        }
+       return nil, errors.New("unknown time zone " + name)
 }
index 995fd44dc063a5a7f6cf58d10ecf26750e43ccf2..beef4de92b077f70f4eca693303531874fa23d92 100644 (file)
@@ -5,34 +5,21 @@
 package time
 
 import (
-       "os"
-       "sync"
+       "errors"
        "syscall"
 )
 
-// BUG(brainman): The Windows implementation assumes that
-// this year's rules for daylight savings time apply to all previous
-// and future years as well.
-
-// TODO(brainman): use GetDynamicTimeZoneInformation, whenever possible (Vista and up),
-// to improve on situation described in the bug above.
-
-type zone struct {
-       name                  string
-       offset                int
-       year                  int64
-       month, day, dayofweek int
-       hour, minute, second  int
-       abssec                int64
-       prev                  *zone
-}
+// TODO(rsc): Fall back to copy of zoneinfo files.
 
-// BUG(rsc): On Windows, time zone abbreviations are unavailable.
-// This package constructs them using the capital letters from a longer
-// time zone description.
+// BUG(brainman,rsc): On Windows, the operating system does not provide complete
+// time zone information.
+// The implementation assumes that this year's rules for daylight savings
+// time apply to all previous and future years as well. 
+// Also, time zone abbreviations are unavailable.  The implementation 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) {
+// abbrev returns the abbreviation to use for the given zone name.
+func abbrev(name []uint16) string {
        // 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.
@@ -41,147 +28,101 @@ func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uin
        //
        // 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
+       var short []rune
        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)
-       z.day = int(d.Day)
-       z.dayofweek = int(d.DayOfWeek)
-       z.hour = int(d.Hour)
-       z.minute = int(d.Minute)
-       z.second = int(d.Second)
-       dateisgood = d.Month != 0
-       if dateisgood {
-               z.offset += int(biasdelta)
-       }
-       z.offset = -z.offset * 60
-       return
-}
-
-// Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
-func (z *zone) preCalculateAbsSec() {
-       if z.year != 0 {
-               t := &Time{
-                       Year:   z.year,
-                       Month:  int(z.month),
-                       Day:    int(z.day),
-                       Hour:   int(z.hour),
-                       Minute: int(z.minute),
-                       Second: int(z.second),
+                       short = append(short, rune(c))
                }
-               z.abssec = t.Seconds()
-               // Time given is in "local" time. Adjust it for "utc".
-               z.abssec -= int64(z.prev.offset)
        }
+       return string(short)
 }
 
-// Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particular year.
-func (z *zone) cutoffSeconds(year int64) int64 {
+// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
+// denoted by the system date+time d in the given year.
+// It is up to the caller to convert this local time into a UTC-based time.
+func pseudoUnix(year int, d *syscall.Systemtime) int64 {
        // Windows specifies daylight savings information in "day in month" format:
-       // z.month is month number (1-12)
-       // z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6)
-       // z.day is week within the month (1 to 5, where 5 is last week of the month)
-       // z.hour, z.minute and z.second are absolute time
-       t := &Time{
-               Year:   year,
-               Month:  int(z.month),
-               Day:    1,
-               Hour:   int(z.hour),
-               Minute: int(z.minute),
-               Second: int(z.second),
-       }
-       t = SecondsToUTC(t.Seconds())
-       i := int(z.dayofweek) - t.Weekday()
+       // d.Month is month number (1-12)
+       // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
+       // d.Day is week within the month (1 to 5, where 5 is last week of the month)
+       // d.Hour, d.Minute and d.Second are absolute time
+       day := 1
+       t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
+       i := int(d.DayOfWeek) - int(t.Weekday())
        if i < 0 {
                i += 7
        }
-       t.Day += i
-       if week := int(z.day) - 1; week < 4 {
-               t.Day += week * 7
+       day += i
+       if week := int(d.Day) - 1; week < 4 {
+               day += week * 7
        } else {
                // "Last" instance of the day.
-               t.Day += 4 * 7
-               if t.Day > months(year)[t.Month] {
-                       t.Day -= 7
+               day += 4 * 7
+               if day > daysIn(Month(d.Month), year) {
+                       day -= 7
                }
        }
-       // Result is in "local" time. Adjust it for "utc".
-       return t.Seconds() - int64(z.prev.offset)
+       return t.sec + int64(day-1)*secondsPerDay + internalToUnix
 }
 
-// Is t before the cutoff for switching to z?
-func (z *zone) isBeforeCutoff(t *Time) bool {
-       var coff int64
-       if z.year == 0 {
-               // "day in month" format used
-               coff = z.cutoffSeconds(t.Year)
-       } else {
-               // "absolute" format used
-               coff = z.abssec
-       }
-       return t.Seconds() < coff
-}
-
-type zoneinfo struct {
-       disabled         bool // daylight saving time is not used locally
-       offsetIfDisabled int
-       januaryIsStd     bool // is january 1 standard time?
-       std, dst         zone
-}
+func initLocalFromTZI(i *syscall.Timezoneinformation) {
+       l := &localLoc
 
-// Pick zone (std or dst) t time belongs to.
-func (zi *zoneinfo) pickZone(t *Time) *zone {
-       z := &zi.std
-       if tz.januaryIsStd {
-               if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) {
-                       // after switch to daylight time and before the switch back to standard
-                       z = &zi.dst
-               }
-       } else {
-               if zi.std.isBeforeCutoff(t) || !zi.dst.isBeforeCutoff(t) {
-                       // before switch to standard time or after the switch back to daylight
-                       z = &zi.dst
-               }
+       nzone := 1
+       if i.StandardDate.Month > 0 {
+               nzone++
        }
-       return z
-}
-
-var tz zoneinfo
-var initError error
-var onceSetupZone sync.Once
+       l.zone = make([]zone, nzone)
 
-func setupZone() {
-       var i syscall.Timezoneinformation
-       if _, e := syscall.GetTimeZoneInformation(&i); e != nil {
-               initError = os.NewSyscallError("GetTimeZoneInformation", e)
+       std := &l.zone[0]
+       std.name = abbrev(i.StandardName[0:])
+       if nzone == 1 {
+               // No daylight savings.
+               std.offset = -int(i.Bias) * 60
+               l.cacheStart = -1 << 63
+               l.cacheEnd = 1<<63 - 1
+               l.cacheZone = std
                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
-               return
+       // StandardBias must be ignored if StandardDate is not set,
+       // so this computation is delayed until after the nzone==1
+       // return above.
+       std.offset = -int(i.Bias+i.StandardBias) * 60
+
+       dst := &l.zone[1]
+       dst.name = abbrev(i.DaylightName[0:])
+       dst.offset = -int(i.Bias+i.DaylightBias) * 60
+       dst.isDST = true
+
+       // Arrange so that d0 is first transition date, d1 second,
+       // i0 is index of zone after first transition, i1 second.
+       d0 := &i.StandardDate
+       d1 := &i.DaylightDate
+       i0 := 0
+       i1 := 1
+       if d0.Month > d1.Month {
+               d0, d1 = d1, d0
+               i0, i1 = i1, i0
+       }
+
+       // 2 tx per year, 100 years on each side of this year
+       l.tx = make([]zoneTrans, 400)
+
+       t := Now().UTC()
+       year := t.Year()
+       txi := 0
+       for y := year - 100; y < year+100; y++ {
+               tx := &l.tx[txi]
+               tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
+               tx.index = uint8(i0)
+               txi++
+
+               tx = &l.tx[txi]
+               tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
+               tx.index = uint8(i1)
+               txi++
        }
-       tz.std.prev = &tz.dst
-       tz.dst.populate(i.Bias, i.DaylightBias, &i.DaylightDate, i.DaylightName[0:])
-       tz.dst.prev = &tz.std
-       tz.std.preCalculateAbsSec()
-       tz.dst.preCalculateAbsSec()
-       // Is january 1 standard time this year?
-       t := UTC()
-       tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year)
 }
 
 var usPacific = syscall.Timezoneinformation{
@@ -197,53 +138,20 @@ var usPacific = syscall.Timezoneinformation{
        DaylightBias: -60,
 }
 
-func setupTestingZone() {
-       setupZoneFromTZI(&usPacific)
+func initTestingZone() {
+       initLocalFromTZI(&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)
-       if initError != nil {
-               return "", 0
-       }
-       if tz.disabled {
-               return "", tz.offsetIfDisabled
-       }
-       t := SecondsToUTC(sec)
-       z := &tz.std
-       if tz.std.year == 0 {
-               // "day in month" format used
-               z = tz.pickZone(t)
-       } else {
-               // "absolute" format used
-               if tz.std.year == t.Year {
-                       // we have rule for the year in question
-                       z = tz.pickZone(t)
-               } else {
-                       // we do not have any information for that year,
-                       // will assume standard offset all year around
-               }
+func initLocal() {
+       var i syscall.Timezoneinformation
+       if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
+               localLoc.name = "UTC"
+               return
        }
-       return z.name, z.offset
+       initLocalFromTZI(&i)
 }
 
-// lookupByName returns the time offset for the
-// time zone with the given abbreviation. It only considers
-// time zones that apply to the current system.
-func lookupByName(name string) (off int, found bool) {
-       onceSetupZone.Do(setupZone)
-       if initError != nil {
-               return 0, false
-       }
-       if tz.disabled {
-               return tz.offsetIfDisabled, false
-       }
-       switch name {
-       case tz.std.name:
-               return tz.std.offset, true
-       case tz.dst.name:
-               return tz.dst.offset, true
-       }
-       return 0, false
+// TODO(rsc): Implement.
+func loadLocation(name string) (*Location, error) {
+       return nil, errors.New("unknown time zone " + name)
 }
index 5dfd824e6e5b9a110f0ae5fd20872b7f2ee81672..89cdcda71aeda5bd27947c32cac0a3d15e35a3fc 100644 (file)
@@ -72,8 +72,8 @@ A trivial example client:
        package main
 
        import (
-               "http"
                "log"
+               "net/http"
                "strings"
                "websocket"
        )
index 4d5360ff4b9fecc9b2f02481eaf47165d717c193..ec7b7ae0a452a5ae722f5d87cc37fb5fd7899d9b 100644 (file)
@@ -274,7 +274,7 @@ func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte
        if _, err = h.Write(challenge); err != nil {
                return
        }
-       expected = h.Sum()
+       expected = h.Sum(nil)
        return
 }
 
index b17d9470bbc3b383e2dc556de928799439d84a66..ff386dc7f2105cfbbbee3b1314c3c122fbd5e6c3 100644 (file)
@@ -371,7 +371,7 @@ func getNonceAccept(nonce []byte) (expected []byte, err error) {
                return
        }
        expected = make([]byte, 28)
-       base64.StdEncoding.Encode(expected, h.Sum())
+       base64.StdEncoding.Encode(expected, h.Sum(nil))
        return
 }
 
index 57dc4fd1dfff4d6e1ec6fb0441441e02ef6149ed..8320b032ead3abae7f65aad00fddfc725a770bfd 100644 (file)
@@ -60,8 +60,8 @@ A trivial example server:
        package main
 
        import (
-               "http"
                "io"
+               "net/http"
                "websocket"
        )
 
index 197fb15d2a5370ca0667bf114fcc0b64bfdcacac..7e5e3e098a99101df3cb5883f4433095402da95f 100644 (file)
@@ -6,17 +6,16 @@
 
 #include <sys/time.h>
 
-#include "go-assert.h"
 #include "runtime.h"
 
+int64 runtime_nanotime (void)
+  __attribute__ ((no_split_stack));
+
 int64
 runtime_nanotime (void)
 {
-  int i;
   struct timeval tv;
 
-  i = gettimeofday (&tv, NULL);
-  __go_assert (i == 0);
-
+  gettimeofday (&tv, NULL);
   return (int64) tv.tv_sec * 1000000000 + (int64) tv.tv_usec * 1000;
 }
diff --git a/libgo/runtime/go-now.c b/libgo/runtime/go-now.c
new file mode 100644 (file)
index 0000000..5df8085
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+// Return current time.  This is the implementation of time.now().
+
+struct time_now_ret
+{
+  int64_t sec;
+  int32_t nsec;
+};
+
+struct time_now_ret now()
+  __asm__ ("libgo_time.time.now")
+  __attribute__ ((no_split_stack));
+
+struct time_now_ret
+now()
+{
+  struct timeval tv;
+  struct time_now_ret ret;
+
+  gettimeofday (&tv, NULL);
+  ret.sec = tv.tv_sec;
+  ret.nsec = tv.tv_usec * 1000;
+  return ret;
+}
index 93b896ed0d2eb85e2a15fcb1b0d81e393a768d95..cae15e5946a1ae4de33768a6fd481c0d08462a9b 100644 (file)
@@ -18,10 +18,7 @@ static bool deltimer(Timer*);
 // Package time APIs.
 // Godoc uses the comments in package time, not these.
 
-// Nanoseconds returns the current time in nanoseconds.
-func Nanoseconds() (ret int64) {
-       ret = runtime_nanotime();
-}
+// time.now is implemented in assembly.
 
 // Sleep puts the current goroutine to sleep for at least ns nanoseconds.
 func Sleep(ns int64) {