aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/testsuite/go.test/test/fixedbugs/bug257.go5
-rw-r--r--gcc/testsuite/go.test/test/initsyscall.go3
-rw-r--r--libgo/MERGE2
-rw-r--r--libgo/Makefile.am51
-rw-r--r--libgo/Makefile.in168
-rw-r--r--libgo/go/archive/tar/common.go53
-rw-r--r--libgo/go/archive/tar/reader.go7
-rw-r--r--libgo/go/archive/tar/reader_test.go55
-rw-r--r--libgo/go/archive/tar/writer.go26
-rw-r--r--libgo/go/archive/tar/writer_test.go9
-rw-r--r--libgo/go/archive/zip/reader.go2
-rw-r--r--libgo/go/archive/zip/reader_test.go4
-rw-r--r--libgo/go/archive/zip/struct.go32
-rw-r--r--libgo/go/bytes/bytes_test.go2
-rw-r--r--libgo/go/compress/gzip/gunzip.go13
-rw-r--r--libgo/go/compress/gzip/gzip.go2
-rw-r--r--libgo/go/compress/gzip/gzip_test.go7
-rw-r--r--libgo/go/crypto/bcrypt/bcrypt.go2
-rw-r--r--libgo/go/crypto/ecdsa/ecdsa_test.go2
-rw-r--r--libgo/go/crypto/hmac/hmac.go8
-rw-r--r--libgo/go/crypto/hmac/hmac_test.go2
-rw-r--r--libgo/go/crypto/md4/md4.go15
-rw-r--r--libgo/go/crypto/md4/md4_test.go4
-rw-r--r--libgo/go/crypto/md5/md5.go15
-rw-r--r--libgo/go/crypto/md5/md5_test.go4
-rw-r--r--libgo/go/crypto/ocsp/ocsp.go12
-rw-r--r--libgo/go/crypto/ocsp/ocsp_test.go8
-rw-r--r--libgo/go/crypto/openpgp/canonical_text.go4
-rw-r--r--libgo/go/crypto/openpgp/canonical_text_test.go6
-rw-r--r--libgo/go/crypto/openpgp/keys.go18
-rw-r--r--libgo/go/crypto/openpgp/packet/private_key.go17
-rw-r--r--libgo/go/crypto/openpgp/packet/private_key_test.go9
-rw-r--r--libgo/go/crypto/openpgp/packet/public_key.go22
-rw-r--r--libgo/go/crypto/openpgp/packet/public_key_test.go11
-rw-r--r--libgo/go/crypto/openpgp/packet/signature.go19
-rw-r--r--libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go4
-rw-r--r--libgo/go/crypto/openpgp/s2k/s2k.go4
-rw-r--r--libgo/go/crypto/openpgp/write.go20
-rw-r--r--libgo/go/crypto/openpgp/write_test.go2
-rw-r--r--libgo/go/crypto/rand/rand_unix.go2
-rw-r--r--libgo/go/crypto/ripemd160/ripemd160.go12
-rw-r--r--libgo/go/crypto/ripemd160/ripemd160_test.go6
-rw-r--r--libgo/go/crypto/rsa/pkcs1v15_test.go4
-rw-r--r--libgo/go/crypto/rsa/rsa.go6
-rw-r--r--libgo/go/crypto/sha1/sha1.go15
-rw-r--r--libgo/go/crypto/sha1/sha1_test.go4
-rw-r--r--libgo/go/crypto/sha256/sha256.go22
-rw-r--r--libgo/go/crypto/sha256/sha256_test.go8
-rw-r--r--libgo/go/crypto/sha512/sha512.go30
-rw-r--r--libgo/go/crypto/sha512/sha512_test.go8
-rw-r--r--libgo/go/crypto/tls/cipher_suites.go34
-rw-r--r--libgo/go/crypto/tls/common.go12
-rw-r--r--libgo/go/crypto/tls/handshake_client.go10
-rw-r--r--libgo/go/crypto/tls/handshake_server.go23
-rw-r--r--libgo/go/crypto/tls/handshake_server_test.go3
-rw-r--r--libgo/go/crypto/tls/key_agreement.go4
-rw-r--r--libgo/go/crypto/tls/prf.go26
-rw-r--r--libgo/go/crypto/tls/root_unix.go1
-rw-r--r--libgo/go/crypto/tls/root_windows.go27
-rw-r--r--libgo/go/crypto/tls/tls.go19
-rw-r--r--libgo/go/crypto/x509/cert_pool.go10
-rw-r--r--libgo/go/crypto/x509/pkcs8.go42
-rw-r--r--libgo/go/crypto/x509/pkcs8_test.go20
-rw-r--r--libgo/go/crypto/x509/pkix/pkix.go13
-rw-r--r--libgo/go/crypto/x509/verify.go12
-rw-r--r--libgo/go/crypto/x509/verify_test.go3
-rw-r--r--libgo/go/crypto/x509/x509.go21
-rw-r--r--libgo/go/crypto/x509/x509_test.go12
-rw-r--r--libgo/go/encoding/asn1/asn1.go10
-rw-r--r--libgo/go/encoding/asn1/asn1_test.go61
-rw-r--r--libgo/go/encoding/asn1/marshal.go38
-rw-r--r--libgo/go/encoding/asn1/marshal_test.go14
-rw-r--r--libgo/go/encoding/json/encode.go89
-rw-r--r--libgo/go/encoding/xml/xml.go2
-rw-r--r--libgo/go/encoding/xml/xml_test.go107
-rw-r--r--libgo/go/exp/gotype/gotype.go12
-rw-r--r--libgo/go/exp/gui/gui.go57
-rw-r--r--libgo/go/exp/gui/x11/auth.go96
-rw-r--r--libgo/go/exp/gui/x11/conn.go631
-rw-r--r--libgo/go/exp/sql/driver/driver.go31
-rw-r--r--libgo/go/exp/sql/fakedb_test.go14
-rw-r--r--libgo/go/exp/sql/sql.go79
-rw-r--r--libgo/go/exp/sql/sql_test.go78
-rw-r--r--libgo/go/exp/ssh/channel.go12
-rw-r--r--libgo/go/exp/ssh/client.go67
-rw-r--r--libgo/go/exp/ssh/client_auth_test.go2
-rw-r--r--libgo/go/exp/ssh/common.go13
-rw-r--r--libgo/go/exp/ssh/common_test.go26
-rw-r--r--libgo/go/exp/ssh/doc.go4
-rw-r--r--libgo/go/exp/ssh/server.go6
-rw-r--r--libgo/go/exp/ssh/session.go452
-rw-r--r--libgo/go/exp/ssh/session_test.go149
-rw-r--r--libgo/go/exp/ssh/tcpip.go4
-rw-r--r--libgo/go/exp/ssh/tcpip_func_test.go59
-rw-r--r--libgo/go/exp/ssh/transport.go12
-rw-r--r--libgo/go/exp/types/check_test.go2
-rw-r--r--libgo/go/exp/types/gcimporter.go2
-rw-r--r--libgo/go/exp/types/gcimporter_test.go18
-rw-r--r--libgo/go/fmt/fmt_test.go16
-rw-r--r--libgo/go/fmt/print.go2
-rw-r--r--libgo/go/go/ast/resolve.go2
-rw-r--r--libgo/go/go/build/build.go9
-rw-r--r--libgo/go/go/build/dir.go35
-rw-r--r--libgo/go/go/build/path.go4
-rw-r--r--libgo/go/go/doc/comment.go74
-rw-r--r--libgo/go/go/doc/comment_test.go39
-rw-r--r--libgo/go/go/doc/headscan.go111
-rw-r--r--libgo/go/go/parser/interface.go7
-rw-r--r--libgo/go/go/parser/parser_test.go2
-rw-r--r--libgo/go/go/printer/nodes.go2
-rw-r--r--libgo/go/go/printer/performance_test.go2
-rw-r--r--libgo/go/go/printer/printer.go401
-rw-r--r--libgo/go/go/printer/printer_test.go2
-rw-r--r--libgo/go/hash/adler32/adler32.go13
-rw-r--r--libgo/go/hash/crc32/crc32.go13
-rw-r--r--libgo/go/hash/crc64/crc64.go21
-rw-r--r--libgo/go/hash/fnv/fnv.go53
-rw-r--r--libgo/go/hash/fnv/fnv_test.go12
-rw-r--r--libgo/go/hash/hash.go6
-rw-r--r--libgo/go/html/doctype.go156
-rw-r--r--libgo/go/html/parse.go169
-rw-r--r--libgo/go/html/parse_test.go182
-rw-r--r--libgo/go/html/render.go68
-rw-r--r--libgo/go/html/template/clone_test.go32
-rw-r--r--libgo/go/html/template/content.go12
-rw-r--r--libgo/go/html/template/doc.go68
-rw-r--r--libgo/go/html/template/escape.go51
-rw-r--r--libgo/go/html/template/escape_test.go38
-rw-r--r--libgo/go/html/template/template.go376
-rw-r--r--libgo/go/html/token.go60
-rw-r--r--libgo/go/html/token_test.go20
-rw-r--r--libgo/go/io/ioutil/ioutil.go24
-rw-r--r--libgo/go/io/ioutil/ioutil_test.go8
-rw-r--r--libgo/go/io/ioutil/tempfile.go2
-rw-r--r--libgo/go/io/multi_test.go2
-rw-r--r--libgo/go/log/log.go21
-rw-r--r--libgo/go/math/abs.go3
-rw-r--r--libgo/go/math/asinh.go3
-rw-r--r--libgo/go/math/big/calibrate_test.go10
-rw-r--r--libgo/go/math/big/int_test.go8
-rw-r--r--libgo/go/math/big/nat.go229
-rw-r--r--libgo/go/math/big/nat_test.go240
-rw-r--r--libgo/go/math/cbrt.go13
-rw-r--r--libgo/go/math/floor.go9
-rw-r--r--libgo/go/math/gamma.go3
-rw-r--r--libgo/go/math/log1p.go3
-rw-r--r--libgo/go/math/modf.go3
-rw-r--r--libgo/go/math/sincos.go62
-rw-r--r--libgo/go/net/dnsclient_unix.go2
-rw-r--r--libgo/go/net/fd.go2
-rw-r--r--libgo/go/net/fd_windows.go3
-rw-r--r--libgo/go/net/hosts.go10
-rw-r--r--libgo/go/net/http/cgi/host_test.go2
-rw-r--r--libgo/go/net/http/cookie.go6
-rw-r--r--libgo/go/net/http/cookie_test.go2
-rw-r--r--libgo/go/net/http/export_test.go6
-rw-r--r--libgo/go/net/http/fcgi/child.go2
-rw-r--r--libgo/go/net/http/fs.go20
-rw-r--r--libgo/go/net/http/fs_test.go4
-rw-r--r--libgo/go/net/http/httptest/server.go4
-rw-r--r--libgo/go/net/http/httputil/reverseproxy.go10
-rw-r--r--libgo/go/net/http/pprof/pprof.go2
-rw-r--r--libgo/go/net/http/serve_test.go12
-rw-r--r--libgo/go/net/http/server.go17
-rw-r--r--libgo/go/net/http/sniff.go34
-rw-r--r--libgo/go/net/http/sniff_test.go23
-rw-r--r--libgo/go/net/http/transport_test.go2
-rw-r--r--libgo/go/net/mail/message.go10
-rw-r--r--libgo/go/net/mail/message_test.go22
-rw-r--r--libgo/go/net/timeout_test.go8
-rw-r--r--libgo/go/old/netchan/common.go12
-rw-r--r--libgo/go/old/netchan/export.go14
-rw-r--r--libgo/go/old/netchan/import.go4
-rw-r--r--libgo/go/os/exec/lp_unix.go2
-rw-r--r--libgo/go/os/exec/lp_windows.go6
-rw-r--r--libgo/go/os/export_test.go9
-rw-r--r--libgo/go/os/file.go54
-rw-r--r--libgo/go/os/file_posix.go16
-rw-r--r--libgo/go/os/file_unix.go69
-rw-r--r--libgo/go/os/getwd.go12
-rw-r--r--libgo/go/os/os_test.go146
-rw-r--r--libgo/go/os/os_unix_test.go75
-rw-r--r--libgo/go/os/path.go6
-rw-r--r--libgo/go/os/stat.go72
-rw-r--r--libgo/go/os/stat_openbsd.go66
-rw-r--r--libgo/go/os/types.go132
-rw-r--r--libgo/go/os/user/user_test.go4
-rw-r--r--libgo/go/patch/git.go2
-rw-r--r--libgo/go/path/filepath/match.go2
-rw-r--r--libgo/go/path/filepath/path.go38
-rw-r--r--libgo/go/path/filepath/path_test.go21
-rw-r--r--libgo/go/strings/strings.go16
-rw-r--r--libgo/go/strings/strings_test.go52
-rw-r--r--libgo/go/testing/benchmark.go44
-rw-r--r--libgo/go/testing/example.go6
-rw-r--r--libgo/go/testing/testing.go21
-rw-r--r--libgo/go/text/template/doc.go80
-rw-r--r--libgo/go/text/template/exec.go35
-rw-r--r--libgo/go/text/template/exec_test.go37
-rw-r--r--libgo/go/text/template/funcs.go17
-rw-r--r--libgo/go/text/template/helper.go245
-rw-r--r--libgo/go/text/template/multi_test.go288
-rw-r--r--libgo/go/text/template/parse.go83
-rw-r--r--libgo/go/text/template/parse/parse.go57
-rw-r--r--libgo/go/text/template/parse/parse_test.go2
-rw-r--r--libgo/go/text/template/parse/set.go15
-rw-r--r--libgo/go/text/template/set.go121
-rw-r--r--libgo/go/text/template/set_test.go239
-rw-r--r--libgo/go/text/template/template.go236
-rw-r--r--libgo/go/text/template/testdata/tmpl1.tmpl2
-rw-r--r--libgo/go/text/template/testdata/tmpl2.tmpl2
-rw-r--r--libgo/go/time/example_test.go58
-rw-r--r--libgo/go/time/format.go441
-rw-r--r--libgo/go/time/internal_test.go2
-rw-r--r--libgo/go/time/sleep.go23
-rw-r--r--libgo/go/time/sleep_test.go72
-rw-r--r--libgo/go/time/sys.go40
-rw-r--r--libgo/go/time/sys_unix.go7
-rw-r--r--libgo/go/time/tick.go26
-rw-r--r--libgo/go/time/tick_test.go14
-rw-r--r--libgo/go/time/time.go1003
-rw-r--r--libgo/go/time/time_test.go326
-rw-r--r--libgo/go/time/zoneinfo.go191
-rw-r--r--libgo/go/time/zoneinfo_plan9.go23
-rw-r--r--libgo/go/time/zoneinfo_posix.go64
-rw-r--r--libgo/go/time/zoneinfo_unix.go141
-rw-r--r--libgo/go/time/zoneinfo_windows.go278
-rw-r--r--libgo/go/websocket/client.go2
-rw-r--r--libgo/go/websocket/hixie.go2
-rw-r--r--libgo/go/websocket/hybi.go2
-rw-r--r--libgo/go/websocket/server.go2
-rw-r--r--libgo/runtime/go-nanotime.c9
-rw-r--r--libgo/runtime/go-now.c31
-rw-r--r--libgo/runtime/time.goc5
234 files changed, 6590 insertions, 4641 deletions
diff --git a/gcc/testsuite/go.test/test/fixedbugs/bug257.go b/gcc/testsuite/go.test/test/fixedbugs/bug257.go
index 713c424..1b32475 100644
--- a/gcc/testsuite/go.test/test/fixedbugs/bug257.go
+++ b/gcc/testsuite/go.test/test/fixedbugs/bug257.go
@@ -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))
diff --git a/gcc/testsuite/go.test/test/initsyscall.go b/gcc/testsuite/go.test/test/initsyscall.go
index b5e5812..d0c26d2 100644
--- a/gcc/testsuite/go.test/test/initsyscall.go
+++ b/gcc/testsuite/go.test/test/initsyscall.go
@@ -19,9 +19,8 @@ func f() {
func init() {
go f()
- time.Nanoseconds()
+ time.Now()
}
func main() {
}
-
diff --git a/libgo/MERGE b/libgo/MERGE
index f62ea21..9847f47 100644
--- a/libgo/MERGE
+++ b/libgo/MERGE
@@ -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.
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index 9701bad..3db823e 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -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 \
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 3d9ed7c..6552074 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -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)
diff --git a/libgo/go/archive/tar/common.go b/libgo/go/archive/tar/common.go
index 6735508..fc7a409 100644
--- a/libgo/go/archive/tar/common.go
+++ b/libgo/go/archive/tar/common.go
@@ -11,41 +11,42 @@
// 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)
diff --git a/libgo/go/archive/tar/reader.go b/libgo/go/archive/tar/reader.go
index facba2c..76955e2 100644
--- a/libgo/go/archive/tar/reader.go
+++ b/libgo/go/archive/tar/reader.go
@@ -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
diff --git a/libgo/go/archive/tar/reader_test.go b/libgo/go/archive/tar/reader_test.go
index 00eea6b..5ca4212 100644
--- a/libgo/go/archive/tar/reader_test.go
+++ b/libgo/go/archive/tar/reader_test.go
@@ -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)
diff --git a/libgo/go/archive/tar/writer.go b/libgo/go/archive/tar/writer.go
index 222df90..b9310b3 100644
--- a/libgo/go/archive/tar/writer.go
+++ b/libgo/go/archive/tar/writer.go
@@ -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 {
diff --git a/libgo/go/archive/tar/writer_test.go b/libgo/go/archive/tar/writer_test.go
index 6cc9386..8d7ed32 100644
--- a/libgo/go/archive/tar/writer_test.go
+++ b/libgo/go/archive/tar/writer_test.go
@@ -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",
diff --git a/libgo/go/archive/zip/reader.go b/libgo/go/archive/zip/reader.go
index cfbe549..4365009 100644
--- a/libgo/go/archive/zip/reader.go
+++ b/libgo/go/archive/zip/reader.go
@@ -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
}
diff --git a/libgo/go/archive/zip/reader_test.go b/libgo/go/archive/zip/reader_test.go
index ca0b04e..8c0ecaa 100644
--- a/libgo/go/archive/zip/reader_test.go
+++ b/libgo/go/archive/zip/reader_test.go
@@ -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)
diff --git a/libgo/go/archive/zip/struct.go b/libgo/go/archive/zip/struct.go
index b862b5a..43c04bb 100644
--- a/libgo/go/archive/zip/struct.go
+++ b/libgo/go/archive/zip/struct.go
@@ -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.
diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go
index 21a1a4f..829ef05 100644
--- a/libgo/go/bytes/bytes_test.go
+++ b/libgo/go/bytes/bytes_test.go
@@ -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 {
diff --git a/libgo/go/compress/gzip/gunzip.go b/libgo/go/compress/gzip/gunzip.go
index a23e515..7c78b9e 100644
--- a/libgo/go/compress/gzip/gunzip.go
+++ b/libgo/go/compress/gzip/gunzip.go
@@ -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]
}
diff --git a/libgo/go/compress/gzip/gzip.go b/libgo/go/compress/gzip/gzip.go
index 94b0f1f..07b91b6 100644
--- a/libgo/go/compress/gzip/gzip.go
+++ b/libgo/go/compress/gzip/gzip.go
@@ -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 {
diff --git a/libgo/go/compress/gzip/gzip_test.go b/libgo/go/compress/gzip/gzip_test.go
index 121e627..815825b 100644
--- a/libgo/go/compress/gzip/gzip_test.go
+++ b/libgo/go/compress/gzip/gzip_test.go
@@ -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")
diff --git a/libgo/go/crypto/bcrypt/bcrypt.go b/libgo/go/crypto/bcrypt/bcrypt.go
index 9740135..362b2eb 100644
--- a/libgo/go/crypto/bcrypt/bcrypt.go
+++ b/libgo/go/crypto/bcrypt/bcrypt.go
@@ -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
diff --git a/libgo/go/crypto/ecdsa/ecdsa_test.go b/libgo/go/crypto/ecdsa/ecdsa_test.go
index 22360b5..45433e1 100644
--- a/libgo/go/crypto/ecdsa/ecdsa_test.go
+++ b/libgo/go/crypto/ecdsa/ecdsa_test.go
@@ -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 {
diff --git a/libgo/go/crypto/hmac/hmac.go b/libgo/go/crypto/hmac/hmac.go
index 6a17bbd..deaceaf 100644
--- a/libgo/go/crypto/hmac/hmac.go
+++ b/libgo/go/crypto/hmac/hmac.go
@@ -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)
diff --git a/libgo/go/crypto/hmac/hmac_test.go b/libgo/go/crypto/hmac/hmac_test.go
index 03431c9..eac254b 100644
--- a/libgo/go/crypto/hmac/hmac_test.go
+++ b/libgo/go/crypto/hmac/hmac_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/md4/md4.go b/libgo/go/crypto/md4/md4.go
index f21cc51..e51e8be 100644
--- a/libgo/go/crypto/md4/md4.go
+++ b/libgo/go/crypto/md4/md4.go
@@ -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
}
diff --git a/libgo/go/crypto/md4/md4_test.go b/libgo/go/crypto/md4/md4_test.go
index 721bd4c..b56edd7 100644
--- a/libgo/go/crypto/md4/md4_test.go
+++ b/libgo/go/crypto/md4/md4_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/md5/md5.go b/libgo/go/crypto/md5/md5.go
index 20f3a1b..182cfb8 100644
--- a/libgo/go/crypto/md5/md5.go
+++ b/libgo/go/crypto/md5/md5.go
@@ -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
}
diff --git a/libgo/go/crypto/md5/md5_test.go b/libgo/go/crypto/md5/md5_test.go
index 857002b..b15e466 100644
--- a/libgo/go/crypto/md5/md5_test.go
+++ b/libgo/go/crypto/md5/md5_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/ocsp/ocsp.go b/libgo/go/crypto/ocsp/ocsp.go
index a04b5bd..b9dfdf9 100644
--- a/libgo/go/crypto/ocsp/ocsp.go
+++ b/libgo/go/crypto/ocsp/ocsp.go
@@ -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 {
diff --git a/libgo/go/crypto/ocsp/ocsp_test.go b/libgo/go/crypto/ocsp/ocsp_test.go
index 7be3721..bacca55 100644
--- a/libgo/go/crypto/ocsp/ocsp_test.go
+++ b/libgo/go/crypto/ocsp/ocsp_test.go
@@ -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)
diff --git a/libgo/go/crypto/openpgp/canonical_text.go b/libgo/go/crypto/openpgp/canonical_text.go
index fe4557a..98cee5e 100644
--- a/libgo/go/crypto/openpgp/canonical_text.go
+++ b/libgo/go/crypto/openpgp/canonical_text.go
@@ -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() {
diff --git a/libgo/go/crypto/openpgp/canonical_text_test.go b/libgo/go/crypto/openpgp/canonical_text_test.go
index ae54f8c8..841475f 100644
--- a/libgo/go/crypto/openpgp/canonical_text_test.go
+++ b/libgo/go/crypto/openpgp/canonical_text_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/openpgp/keys.go b/libgo/go/crypto/openpgp/keys.go
index b705d22..df39970 100644
--- a/libgo/go/crypto/openpgp/keys.go
+++ b/libgo/go/crypto/openpgp/keys.go
@@ -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 {
diff --git a/libgo/go/crypto/openpgp/packet/private_key.go b/libgo/go/crypto/openpgp/packet/private_key.go
index c0ff82b4..d67e968 100644
--- a/libgo/go/crypto/openpgp/packet/private_key.go
+++ b/libgo/go/crypto/openpgp/packet/private_key.go
@@ -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")
}
diff --git a/libgo/go/crypto/openpgp/packet/private_key_test.go b/libgo/go/crypto/openpgp/packet/private_key_test.go
index 60eebaa..35d8951 100644
--- a/libgo/go/crypto/openpgp/packet/private_key_test.go
+++ b/libgo/go/crypto/openpgp/packet/private_key_test.go
@@ -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)
}
}
diff --git a/libgo/go/crypto/openpgp/packet/public_key.go b/libgo/go/crypto/openpgp/packet/public_key.go
index 7d71dc4..9aa30e0 100644
--- a/libgo/go/crypto/openpgp/packet/public_key.go
+++ b/libgo/go/crypto/openpgp/packet/public_key.go
@@ -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")
diff --git a/libgo/go/crypto/openpgp/packet/public_key_test.go b/libgo/go/crypto/openpgp/packet/public_key_test.go
index 6e8bfbc..72f459f 100644
--- a/libgo/go/crypto/openpgp/packet/public_key_test.go
+++ b/libgo/go/crypto/openpgp/packet/public_key_test.go
@@ -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[:]) {
diff --git a/libgo/go/crypto/openpgp/packet/signature.go b/libgo/go/crypto/openpgp/packet/signature.go
index 4ebb906..1cdc1ee 100644
--- a/libgo/go/crypto/openpgp/packet/signature.go
+++ b/libgo/go/crypto/openpgp/packet/signature.go
@@ -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 {
diff --git a/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go b/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go
index 8225db6..dff776e 100644
--- a/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go
+++ b/libgo/go/crypto/openpgp/packet/symmetrically_encrypted.go
@@ -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[:])
diff --git a/libgo/go/crypto/openpgp/s2k/s2k.go b/libgo/go/crypto/openpgp/s2k/s2k.go
index 2a753db..83673e1 100644
--- a/libgo/go/crypto/openpgp/s2k/s2k.go
+++ b/libgo/go/crypto/openpgp/s2k/s2k.go
@@ -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
}
}
diff --git a/libgo/go/crypto/openpgp/write.go b/libgo/go/crypto/openpgp/write.go
index 6f3450c..60dae01 100644
--- a/libgo/go/crypto/openpgp/write.go
+++ b/libgo/go/crypto/openpgp/write.go
@@ -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,
}
diff --git a/libgo/go/crypto/openpgp/write_test.go b/libgo/go/crypto/openpgp/write_test.go
index 3cadf4c..02fa5b7 100644
--- a/libgo/go/crypto/openpgp/write_test.go
+++ b/libgo/go/crypto/openpgp/write_test.go
@@ -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
diff --git a/libgo/go/crypto/rand/rand_unix.go b/libgo/go/crypto/rand/rand_unix.go
index 09442ad..d9cddf6 100644
--- a/libgo/go/crypto/rand/rand_unix.go
+++ b/libgo/go/crypto/rand/rand_unix.go
@@ -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)
diff --git a/libgo/go/crypto/ripemd160/ripemd160.go b/libgo/go/crypto/ripemd160/ripemd160.go
index 6ccfe87..c128ee4 100644
--- a/libgo/go/crypto/ripemd160/ripemd160.go
+++ b/libgo/go/crypto/ripemd160/ripemd160.go
@@ -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
}
diff --git a/libgo/go/crypto/ripemd160/ripemd160_test.go b/libgo/go/crypto/ripemd160/ripemd160_test.go
index f4135f5..5df1b25 100644
--- a/libgo/go/crypto/ripemd160/ripemd160_test.go
+++ b/libgo/go/crypto/ripemd160/ripemd160_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/rsa/pkcs1v15_test.go b/libgo/go/crypto/rsa/pkcs1v15_test.go
index 66188ac..58d5fda 100644
--- a/libgo/go/crypto/rsa/pkcs1v15_test.go
+++ b/libgo/go/crypto/rsa/pkcs1v15_test.go
@@ -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)
diff --git a/libgo/go/crypto/rsa/rsa.go b/libgo/go/crypto/rsa/rsa.go
index 27ccf61..f74525c 100644
--- a/libgo/go/crypto/rsa/rsa.go
+++ b/libgo/go/crypto/rsa/rsa.go
@@ -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
diff --git a/libgo/go/crypto/sha1/sha1.go b/libgo/go/crypto/sha1/sha1.go
index 4cdf5b2..f41cdb5 100644
--- a/libgo/go/crypto/sha1/sha1.go
+++ b/libgo/go/crypto/sha1/sha1.go
@@ -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
}
diff --git a/libgo/go/crypto/sha1/sha1_test.go b/libgo/go/crypto/sha1/sha1_test.go
index 2712fe35..c23df6c 100644
--- a/libgo/go/crypto/sha1/sha1_test.go
+++ b/libgo/go/crypto/sha1/sha1_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/sha256/sha256.go b/libgo/go/crypto/sha256/sha256.go
index 14b8cfc..34861f6 100644
--- a/libgo/go/crypto/sha256/sha256.go
+++ b/libgo/go/crypto/sha256/sha256.go
@@ -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
}
diff --git a/libgo/go/crypto/sha256/sha256_test.go b/libgo/go/crypto/sha256/sha256_test.go
index 42a3fa7..a6efb37 100644
--- a/libgo/go/crypto/sha256/sha256_test.go
+++ b/libgo/go/crypto/sha256/sha256_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/sha512/sha512.go b/libgo/go/crypto/sha512/sha512.go
index 1bd2798..3cf65cb 100644
--- a/libgo/go/crypto/sha512/sha512.go
+++ b/libgo/go/crypto/sha512/sha512.go
@@ -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
}
diff --git a/libgo/go/crypto/sha512/sha512_test.go b/libgo/go/crypto/sha512/sha512_test.go
index dd116dc..a70f7c5 100644
--- a/libgo/go/crypto/sha512/sha512_test.go
+++ b/libgo/go/crypto/sha512/sha512_test.go
@@ -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)
}
diff --git a/libgo/go/crypto/tls/cipher_suites.go b/libgo/go/crypto/tls/cipher_suites.go
index 1134f36..c0e8656 100644
--- a/libgo/go/crypto/tls/cipher_suites.go
+++ b/libgo/go/crypto/tls/cipher_suites.go
@@ -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
diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go
index ea52085..f57d932 100644
--- a/libgo/go/crypto/tls/common.go
+++ b/libgo/go/crypto/tls/common.go
@@ -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
}
}
diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go
index aed991cc..b4337f2 100644
--- a/libgo/go/crypto/tls/handshake_client.go
+++ b/libgo/go/crypto/tls/handshake_client.go
@@ -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
}
diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go
index d5af084..bbb23c0 100644
--- a/libgo/go/crypto/tls/handshake_server.go
+++ b/libgo/go/crypto/tls/handshake_server.go
@@ -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
}
diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go
index bc37979..e00c32c 100644
--- a/libgo/go/crypto/tls/handshake_server_test.go
+++ b/libgo/go/crypto/tls/handshake_server_test.go
@@ -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}
diff --git a/libgo/go/crypto/tls/key_agreement.go b/libgo/go/crypto/tls/key_agreement.go
index 08fb852..b531717 100644
--- a/libgo/go/crypto/tls/key_agreement.go
+++ b/libgo/go/crypto/tls/key_agreement.go
@@ -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
}
diff --git a/libgo/go/crypto/tls/prf.go b/libgo/go/crypto/tls/prf.go
index d758f21..637ef03e 100644
--- a/libgo/go/crypto/tls/prf.go
+++ b/libgo/go/crypto/tls/prf.go
@@ -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)
}
diff --git a/libgo/go/crypto/tls/root_unix.go b/libgo/go/crypto/tls/root_unix.go
index 095beec..1b9aeb0 100644
--- a/libgo/go/crypto/tls/root_unix.go
+++ b/libgo/go/crypto/tls/root_unix.go
@@ -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() {
diff --git a/libgo/go/crypto/tls/root_windows.go b/libgo/go/crypto/tls/root_windows.go
index 13073dc..319309a 100644
--- a/libgo/go/crypto/tls/root_windows.go
+++ b/libgo/go/crypto/tls/root_windows.go
@@ -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() {
diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go
index 3ca6240..79ab502 100644
--- a/libgo/go/crypto/tls/tls.go
+++ b/libgo/go/crypto/tls/tls.go
@@ -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
diff --git a/libgo/go/crypto/x509/cert_pool.go b/libgo/go/crypto/x509/cert_pool.go
index b9196ed..adc7f9b 100644
--- a/libgo/go/crypto/x509/cert_pool.go
+++ b/libgo/go/crypto/x509/cert_pool.go
@@ -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
index 0000000..4d8e051
--- /dev/null
+++ b/libgo/go/crypto/x509/pkcs8.go
@@ -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
index 0000000..372005f
--- /dev/null
+++ b/libgo/go/crypto/x509/pkcs8_test.go
@@ -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)
+ }
+}
diff --git a/libgo/go/crypto/x509/pkix/pkix.go b/libgo/go/crypto/x509/pkix/pkix.go
index b35274c..8eced55 100644
--- a/libgo/go/crypto/x509/pkix/pkix.go
+++ b/libgo/go/crypto/x509/pkix/pkix.go
@@ -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"`
}
diff --git a/libgo/go/crypto/x509/verify.go b/libgo/go/crypto/x509/verify.go
index 3021d20..50a3b66 100644
--- a/libgo/go/crypto/x509/verify.go
+++ b/libgo/go/crypto/x509/verify.go
@@ -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
diff --git a/libgo/go/crypto/x509/verify_test.go b/libgo/go/crypto/x509/verify_test.go
index 2194d15..df54430 100644
--- a/libgo/go/crypto/x509/verify_test.go
+++ b/libgo/go/crypto/x509/verify_test.go
@@ -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 {
diff --git a/libgo/go/crypto/x509/x509.go b/libgo/go/crypto/x509/x509.go
index 9ff7db9..7e6b5c9 100644
--- a/libgo/go/crypto/x509/x509.go
+++ b/libgo/go/crypto/x509/x509.go
@@ -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 {
diff --git a/libgo/go/crypto/x509/x509_test.go b/libgo/go/crypto/x509/x509_test.go
index c424715..f0327b0 100644
--- a/libgo/go/crypto/x509/x509_test.go
+++ b/libgo/go/crypto/x509/x509_test.go
@@ -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)")
}
diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go
index a006665..22a0dde 100644
--- a/libgo/go/encoding/asn1/asn1.go
+++ b/libgo/go/encoding/asn1/asn1.go
@@ -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.
//
diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go
index 1c529bd..2e6fccf 100644
--- a/libgo/go/encoding/asn1/asn1_test.go
+++ b/libgo/go/encoding/asn1/asn1_test.go
@@ -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"}},
diff --git a/libgo/go/encoding/asn1/marshal.go b/libgo/go/encoding/asn1/marshal.go
index 89c50a7..c181e43 100644
--- a/libgo/go/encoding/asn1/marshal.go
+++ b/libgo/go/encoding/asn1/marshal.go
@@ -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:
diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go
index 03df5f1..d05b5d8 100644
--- a/libgo/go/encoding/asn1/marshal_test.go
+++ b/libgo/go/encoding/asn1/marshal_test.go
@@ -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)
+
}
}
}
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go
index 35964c5..14284f5 100644
--- a/libgo/go/encoding/json/encode.go
+++ b/libgo/go/encoding/json/encode.go
@@ -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
+}
diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go
index 216d888..d67a299 100644
--- a/libgo/go/encoding/xml/xml.go
+++ b/libgo/go/encoding/xml/xml.go
@@ -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
}
diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go
index bcb22af..25ffc91 100644
--- a/libgo/go/encoding/xml/xml_test.go
+++ b/libgo/go/encoding/xml/xml_test.go
@@ -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.")
}
diff --git a/libgo/go/exp/gotype/gotype.go b/libgo/go/exp/gotype/gotype.go
index bc4a112..a2a9361 100644
--- a/libgo/go/exp/gotype/gotype.go
+++ b/libgo/go/exp/gotype/gotype.go
@@ -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
index a69f83a..0000000
--- a/libgo/go/exp/gui/gui.go
+++ /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
index 24e941c..0000000
--- a/libgo/go/exp/gui/x11/auth.go
+++ /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
index 15afc65..0000000
--- a/libgo/go/exp/gui/x11/conn.go
+++ /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
-}
diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go
index 91a3884..f0bcca2 100644
--- a/libgo/go/exp/sql/driver/driver.go
+++ b/libgo/go/exp/sql/driver/driver.go
@@ -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
diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go
index 17028e2..2474a86 100644
--- a/libgo/go/exp/sql/fakedb_test.go
+++ b/libgo/go/exp/sql/fakedb_test.go
@@ -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
diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go
index c055fdd..f17d12e 100644
--- a/libgo/go/exp/sql/sql.go
+++ b/libgo/go/exp/sql/sql.go
@@ -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 {
diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go
index d365f6b..4f8318d 100644
--- a/libgo/go/exp/sql/sql_test.go
+++ b/libgo/go/exp/sql/sql_test.go
@@ -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
}
@@ -44,6 +44,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
var age int
@@ -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)
+ }
+}
diff --git a/libgo/go/exp/ssh/channel.go b/libgo/go/exp/ssh/channel.go
index 6ff8203..9d75f37 100644
--- a/libgo/go/exp/ssh/channel.go
+++ b/libgo/go/exp/ssh/channel.go
@@ -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)
diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go
index 24569ad..429dee9 100644
--- a/libgo/go/exp/ssh/client.go
+++ b/libgo/go/exp/ssh/client.go
@@ -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}))
-}
diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go
index 6467f57..4ef9213 100644
--- a/libgo/go/exp/ssh/client_auth_test.go
+++ b/libgo/go/exp/ssh/client_auth_test.go
@@ -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)
}
diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go
index 01c55219..6844fb8 100644
--- a/libgo/go/exp/ssh/common.go
+++ b/libgo/go/exp/ssh/common.go
@@ -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
index 0000000..2f4448a
--- /dev/null
+++ b/libgo/go/exp/ssh/common_test.go
@@ -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))
+ }
+ }
+}
diff --git a/libgo/go/exp/ssh/doc.go b/libgo/go/exp/ssh/doc.go
index 248b2fe..480f877 100644
--- a/libgo/go/exp/ssh/doc.go
+++ b/libgo/go/exp/ssh/doc.go
@@ -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)
diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go
index 428a747..1eee9a4 100644
--- a/libgo/go/exp/ssh/server.go
+++ b/libgo/go/exp/ssh/server.go
@@ -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}
diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go
index 77154f2..5f98a8d 100644
--- a/libgo/go/exp/ssh/session.go
+++ b/libgo/go/exp/ssh/session.go
@@ -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
index 0000000..4be7746
--- /dev/null
+++ b/libgo/go/exp/ssh/session_test.go
@@ -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)
+ }
+}
diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go
index 859dedc..f3bbac5 100644
--- a/libgo/go/exp/ssh/tcpip.go
+++ b/libgo/go/exp/ssh/tcpip.go
@@ -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
index 0000000..2612972
--- /dev/null
+++ b/libgo/go/exp/ssh/tcpip_func_test.go
@@ -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)
+}
diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go
index b8cb2c3..bcd073e 100644
--- a/libgo/go/exp/ssh/transport.go
+++ b/libgo/go/exp/ssh/transport.go
@@ -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() {
diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go
index 4a30acf..35535ea 100644
--- a/libgo/go/exp/types/check_test.go
+++ b/libgo/go/exp/types/check_test.go
@@ -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
diff --git a/libgo/go/exp/types/gcimporter.go b/libgo/go/exp/types/gcimporter.go
index 4167caf..16a8667 100644
--- a/libgo/go/exp/types/gcimporter.go
+++ b/libgo/go/exp/types/gcimporter.go
@@ -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
}
}
diff --git a/libgo/go/exp/types/gcimporter_test.go b/libgo/go/exp/types/gcimporter_test.go
index 3f66d22..7475d35 100644
--- a/libgo/go/exp/types/gcimporter_test.go
+++ b/libgo/go/exp/types/gcimporter_test.go
@@ -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)
}
diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go
index 6370560..00aac79 100644
--- a/libgo/go/fmt/fmt_test.go
+++ b/libgo/go/fmt/fmt_test.go
@@ -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{}`},
diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go
index 7143e07..e5ca117 100644
--- a/libgo/go/fmt/print.go
+++ b/libgo/go/fmt/print.go
@@ -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
}
diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go
index b24688d..c7c8e7c 100644
--- a/libgo/go/go/ast/resolve.go
+++ b/libgo/go/go/ast/resolve.go
@@ -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)
diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go
index e3de8d0..5301ab5 100644
--- a/libgo/go/go/build/build.go
+++ b/libgo/go/go/build/build.go
@@ -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
diff --git a/libgo/go/go/build/dir.go b/libgo/go/go/build/dir.go
index 0d175c7..12dc999 100644
--- a/libgo/go/go/build/dir.go
+++ b/libgo/go/go/build/dir.go
@@ -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 == "" {
diff --git a/libgo/go/go/build/path.go b/libgo/go/go/build/path.go
index 7ccb129..91d6c43 100644
--- a/libgo/go/go/build/path.go
+++ b/libgo/go/go/build/path.go
@@ -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
}
diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go
index 19216f8..d7bb384 100644
--- a/libgo/go/go/doc/comment.go
+++ b/libgo/go/go/doc/comment.go
@@ -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
index 0000000..870660a
--- /dev/null
+++ b/libgo/go/go/doc/comment_test.go
@@ -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
index 0000000..83f2462
--- /dev/null
+++ b/libgo/go/go/doc/headscan.go
@@ -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")
+}
diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go
index d3bab31..be11f46 100644
--- a/libgo/go/go/parser/interface.go
+++ b/libgo/go/go/parser/interface.go
@@ -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++
}
}
diff --git a/libgo/go/go/parser/parser_test.go b/libgo/go/go/parser/parser_test.go
index dee90fbc..f602db8 100644
--- a/libgo/go/go/parser/parser_test.go
+++ b/libgo/go/go/parser/parser_test.go
@@ -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 := "."
diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go
index 248e43d..53f3609 100644
--- a/libgo/go/go/printer/nodes.go
+++ b/libgo/go/go/printer/nodes.go
@@ -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 {
diff --git a/libgo/go/go/printer/performance_test.go b/libgo/go/go/printer/performance_test.go
index 84fb280..dbd9422 100644
--- a/libgo/go/go/printer/performance_test.go
+++ b/libgo/go/go/printer/performance_test.go
@@ -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)
}
}
diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go
index 6104c32..f8c22f1 100644
--- a/libgo/go/go/printer/printer.go
+++ b/libgo/go/go/printer/printer.go
@@ -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)
}
diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go
index a644aa3..924d4df 100644
--- a/libgo/go/go/printer/printer_test.go
+++ b/libgo/go/go/printer/printer_test.go
@@ -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()
diff --git a/libgo/go/hash/adler32/adler32.go b/libgo/go/hash/adler32/adler32.go
index 10bed2f..8103a89 100644
--- a/libgo/go/hash/adler32/adler32.go
+++ b/libgo/go/hash/adler32/adler32.go
@@ -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.
diff --git a/libgo/go/hash/crc32/crc32.go b/libgo/go/hash/crc32/crc32.go
index 5980ec0..557fab8 100644
--- a/libgo/go/hash/crc32/crc32.go
+++ b/libgo/go/hash/crc32/crc32.go
@@ -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
diff --git a/libgo/go/hash/crc64/crc64.go b/libgo/go/hash/crc64/crc64.go
index 42e53c3..e5c1db4 100644
--- a/libgo/go/hash/crc64/crc64.go
+++ b/libgo/go/hash/crc64/crc64.go
@@ -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
diff --git a/libgo/go/hash/fnv/fnv.go b/libgo/go/hash/fnv/fnv.go
index ce3ed0d..2c8a251 100644
--- a/libgo/go/hash/fnv/fnv.go
+++ b/libgo/go/hash/fnv/fnv.go
@@ -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
}
diff --git a/libgo/go/hash/fnv/fnv_test.go b/libgo/go/hash/fnv/fnv_test.go
index 429230c..17454de 100644
--- a/libgo/go/hash/fnv/fnv_test.go
+++ b/libgo/go/hash/fnv/fnv_test.go
@@ -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)
}
}
diff --git a/libgo/go/hash/hash.go b/libgo/go/hash/hash.go
index 3536c0b..0d7765d 100644
--- a/libgo/go/hash/hash.go
+++ b/libgo/go/hash/hash.go
@@ -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
index 0000000..f692061
--- /dev/null
+++ b/libgo/go/html/doctype.go
@@ -0,0 +1,156 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package html
+
+import (
+ "strings"
+)
+
+// parseDoctype parses the data from a DoctypeToken into a name,
+// public identifier, and system identifier. It returns a Node whose Type
+// is DoctypeNode, whose Data is the name, and which has attributes
+// named "system" and "public" for the two identifiers if they were present.
+// quirks is whether the document should be parsed in "quirks mode".
+func parseDoctype(s string) (n *Node, quirks bool) {
+ n = &Node{Type: DoctypeNode}
+
+ // Find the name.
+ space := strings.IndexAny(s, whitespace)
+ if space == -1 {
+ space = len(s)
+ }
+ n.Data = s[:space]
+ // The comparison to "html" is case-sensitive.
+ if n.Data != "html" {
+ quirks = true
+ }
+ n.Data = strings.ToLower(n.Data)
+ s = strings.TrimLeft(s[space:], whitespace)
+
+ if len(s) < 6 {
+ // It can't start with "PUBLIC" or "SYSTEM".
+ // Ignore the rest of the string.
+ return n, quirks || s != ""
+ }
+
+ key := strings.ToLower(s[:6])
+ s = s[6:]
+ for key == "public" || key == "system" {
+ s = strings.TrimLeft(s, whitespace)
+ if s == "" {
+ break
+ }
+ quote := s[0]
+ if quote != '"' && quote != '\'' {
+ break
+ }
+ s = s[1:]
+ q := strings.IndexRune(s, rune(quote))
+ var id string
+ if q == -1 {
+ id = s
+ s = ""
+ } else {
+ id = s[:q]
+ s = s[q+1:]
+ }
+ n.Attr = append(n.Attr, Attribute{Key: key, Val: id})
+ if key == "public" {
+ key = "system"
+ } else {
+ key = ""
+ }
+ }
+
+ if key != "" || s != "" {
+ quirks = true
+ } else if len(n.Attr) > 0 {
+ if n.Attr[0].Key == "public" {
+ public := strings.ToLower(n.Attr[0].Val)
+ switch public {
+ case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html":
+ quirks = true
+ default:
+ for _, q := range quirkyIDs {
+ if strings.HasPrefix(public, q) {
+ quirks = true
+ break
+ }
+ }
+ }
+ // The following two public IDs only cause quirks mode if there is no system ID.
+ if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") ||
+ strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) {
+ quirks = true
+ }
+ }
+ if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
+ strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
+ quirks = true
+ }
+ }
+
+ return n, quirks
+}
+
+// quirkyIDs is a list of public doctype identifiers that cause a document
+// to be interpreted in quirks mode. The identifiers should be in lower case.
+var quirkyIDs = []string{
+ "+//silmaril//dtd html pro v0r11 19970101//",
+ "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
+ "-//as//dtd html 3.0 aswedit + extensions//",
+ "-//ietf//dtd html 2.0 level 1//",
+ "-//ietf//dtd html 2.0 level 2//",
+ "-//ietf//dtd html 2.0 strict level 1//",
+ "-//ietf//dtd html 2.0 strict level 2//",
+ "-//ietf//dtd html 2.0 strict//",
+ "-//ietf//dtd html 2.0//",
+ "-//ietf//dtd html 2.1e//",
+ "-//ietf//dtd html 3.0//",
+ "-//ietf//dtd html 3.2 final//",
+ "-//ietf//dtd html 3.2//",
+ "-//ietf//dtd html 3//",
+ "-//ietf//dtd html level 0//",
+ "-//ietf//dtd html level 1//",
+ "-//ietf//dtd html level 2//",
+ "-//ietf//dtd html level 3//",
+ "-//ietf//dtd html strict level 0//",
+ "-//ietf//dtd html strict level 1//",
+ "-//ietf//dtd html strict level 2//",
+ "-//ietf//dtd html strict level 3//",
+ "-//ietf//dtd html strict//",
+ "-//ietf//dtd html//",
+ "-//metrius//dtd metrius presentational//",
+ "-//microsoft//dtd internet explorer 2.0 html strict//",
+ "-//microsoft//dtd internet explorer 2.0 html//",
+ "-//microsoft//dtd internet explorer 2.0 tables//",
+ "-//microsoft//dtd internet explorer 3.0 html strict//",
+ "-//microsoft//dtd internet explorer 3.0 html//",
+ "-//microsoft//dtd internet explorer 3.0 tables//",
+ "-//netscape comm. corp.//dtd html//",
+ "-//netscape comm. corp.//dtd strict html//",
+ "-//o'reilly and associates//dtd html 2.0//",
+ "-//o'reilly and associates//dtd html extended 1.0//",
+ "-//o'reilly and associates//dtd html extended relaxed 1.0//",
+ "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
+ "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
+ "-//spyglass//dtd html 2.0 extended//",
+ "-//sq//dtd html 2.0 hotmetal + extensions//",
+ "-//sun microsystems corp.//dtd hotjava html//",
+ "-//sun microsystems corp.//dtd hotjava strict html//",
+ "-//w3c//dtd html 3 1995-03-24//",
+ "-//w3c//dtd html 3.2 draft//",
+ "-//w3c//dtd html 3.2 final//",
+ "-//w3c//dtd html 3.2//",
+ "-//w3c//dtd html 3.2s draft//",
+ "-//w3c//dtd html 4.0 frameset//",
+ "-//w3c//dtd html 4.0 transitional//",
+ "-//w3c//dtd html experimental 19960712//",
+ "-//w3c//dtd html experimental 970421//",
+ "-//w3c//dtd w3 html//",
+ "-//w3o//dtd w3 html 3.0//",
+ "-//webtechs//dtd mozilla html 2.0//",
+ "-//webtechs//dtd mozilla html//",
+}
diff --git a/libgo/go/html/parse.go b/libgo/go/html/parse.go
index 9b7e934..97fbc51 100644
--- a/libgo/go/html/parse.go
+++ b/libgo/go/html/parse.go
@@ -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
+}
diff --git a/libgo/go/html/parse_test.go b/libgo/go/html/parse_test.go
index 4f15ae1..e0c19cf 100644
--- a/libgo/go/html/parse_test.go
+++ b/libgo/go/html/parse_test.go
@@ -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)
- }
- }
}
}
diff --git a/libgo/go/html/render.go b/libgo/go/html/render.go
index 92c349f..7e1a466 100644
--- a/libgo/go/html/render.go
+++ b/libgo/go/html/render.go
@@ -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{
diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go
index ed1698a..3978817 100644
--- a/libgo/go/html/template/clone_test.go
+++ b/libgo/go/html/template/clone_test.go
@@ -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)
}
}
}
diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go
index 3fb15a6..4de7ccd 100644
--- a/libgo/go/html/template/content.go
+++ b/libgo/go/html/template/content.go
@@ -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
diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go
index 0324c9c..fc0e382 100644
--- a/libgo/go/html/template/doc.go
+++ b/libgo/go/html/template/doc.go
@@ -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
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go
index 8ac07ea..4a7a935 100644
--- a/libgo/go/html/template/escape.go
+++ b/libgo/go/html/template/escape.go
@@ -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]
}
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index 4af58309..b4daca7 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -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 {
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 4733429..f05ca19 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -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...)
}
diff --git a/libgo/go/html/token.go b/libgo/go/html/token.go
index 9400873..69af968 100644
--- a/libgo/go/html/token.go
+++ b/libgo/go/html/token.go
@@ -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] == '/' {
diff --git a/libgo/go/html/token_test.go b/libgo/go/html/token_test.go
index 61d4e67..672d60c 100644
--- a/libgo/go/html/token_test.go
+++ b/libgo/go/html/token_test.go
@@ -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",
},
diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go
index f6c8cd8..be7fa5f 100644
--- a/libgo/go/io/ioutil/ioutil.go
+++ b/libgo/go/io/ioutil/ioutil.go
@@ -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 {
diff --git a/libgo/go/io/ioutil/ioutil_test.go b/libgo/go/io/ioutil/ioutil_test.go
index 55e4b2c..89d6815 100644
--- a/libgo/go/io/ioutil/ioutil_test.go
+++ b/libgo/go/io/ioutil/ioutil_test.go
@@ -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
}
}
diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go
index 71028e2..645eed6 100644
--- a/libgo/go/io/ioutil/tempfile.go
+++ b/libgo/go/io/ioutil/tempfile.go
@@ -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 {
diff --git a/libgo/go/io/multi_test.go b/libgo/go/io/multi_test.go
index 0de5cc3..eb717f7 100644
--- a/libgo/go/io/multi_test.go
+++ b/libgo/go/io/multi_test.go
@@ -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")
}
diff --git a/libgo/go/log/log.go b/libgo/go/log/log.go
index b5368af..a5d88fd 100644
--- a/libgo/go/log/log.go
+++ b/libgo/go/log/log.go
@@ -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()
diff --git a/libgo/go/math/abs.go b/libgo/go/math/abs.go
index eb3e4c7..4c6297c 100644
--- a/libgo/go/math/abs.go
+++ b/libgo/go/math/abs.go
@@ -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 {
diff --git a/libgo/go/math/asinh.go b/libgo/go/math/asinh.go
index c1cad56..d697946 100644
--- a/libgo/go/math/asinh.go
+++ b/libgo/go/math/asinh.go
@@ -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 (
diff --git a/libgo/go/math/big/calibrate_test.go b/libgo/go/math/big/calibrate_test.go
index 1cd93b1..0950eee 100644
--- a/libgo/go/math/big/calibrate_test.go
+++ b/libgo/go/math/big/calibrate_test.go
@@ -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
diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go
index 163c662..aa7c194 100644
--- a/libgo/go/math/big/int_test.go
+++ b/libgo/go/math/big/int_test.go
@@ -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) {
diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go
index eee8ee3..680445d 100644
--- a/libgo/go/math/big/nat.go
+++ b/libgo/go/math/big/nat.go
@@ -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.
diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go
index b208646..e3c6552 100644
--- a/libgo/go/math/big/nat_test.go
+++ b/libgo/go/math/big/nat_test.go
@@ -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)
+ }
+ }
}
}
diff --git a/libgo/go/math/cbrt.go b/libgo/go/math/cbrt.go
index d2b7e91..09edc0e 100644
--- a/libgo/go/math/cbrt.go
+++ b/libgo/go/math/cbrt.go
@@ -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
diff --git a/libgo/go/math/floor.go b/libgo/go/math/floor.go
index babbf64..8de4d7e 100644
--- a/libgo/go/math/floor.go
+++ b/libgo/go/math/floor.go
@@ -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
diff --git a/libgo/go/math/gamma.go b/libgo/go/math/gamma.go
index ae2c0c4..7365d8e 100644
--- a/libgo/go/math/gamma.go
+++ b/libgo/go/math/gamma.go
@@ -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.
diff --git a/libgo/go/math/log1p.go b/libgo/go/math/log1p.go
index c25d73b..e8914a1 100644
--- a/libgo/go/math/log1p.go
+++ b/libgo/go/math/log1p.go
@@ -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
diff --git a/libgo/go/math/modf.go b/libgo/go/math/modf.go
index 315174b..34889e0 100644
--- a/libgo/go/math/modf.go
+++ b/libgo/go/math/modf.go
@@ -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 {
diff --git a/libgo/go/math/sincos.go b/libgo/go/math/sincos.go
index 4c1576b..f5412fd 100644
--- a/libgo/go/math/sincos.go
+++ b/libgo/go/math/sincos.go
@@ -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
+}
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index bab5f2a..79a958e 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_unix.go
@@ -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},
}
diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go
index 70e04a2..5318c51 100644
--- a/libgo/go/net/fd.go
+++ b/libgo/go/net/fd.go
@@ -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() {
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index 7a16023..264b918 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -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
diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go
index ddfb074..e6674ba 100644
--- a/libgo/go/net/hosts.go
+++ b/libgo/go/net/hosts.go
@@ -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
diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go
index e6e85e8..849cb00 100644
--- a/libgo/go/net/http/cgi/host_test.go
+++ b/libgo/go/net/http/cgi/host_test.go
@@ -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() {
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index 6935014..cad8522 100644
--- a/libgo/go/net/http/cookie.go
+++ b/libgo/go/net/http/cookie.go
@@ -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)
diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go
index 24adf20..26bff93 100644
--- a/libgo/go/net/http/cookie_test.go
+++ b/libgo/go/net/http/cookie_test.go
@@ -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",
}},
diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go
index 3fe6586..13640ca8 100644
--- a/libgo/go/net/http/export_test.go
+++ b/libgo/go/net/http/export_test.go
@@ -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, ""}
diff --git a/libgo/go/net/http/fcgi/child.go b/libgo/go/net/http/fcgi/child.go
index 529440c..c94b9a7 100644
--- a/libgo/go/net/http/fcgi/child.go
+++ b/libgo/go/net/http/fcgi/child.go
@@ -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))
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go
index 5aadac1..70e7849 100644
--- a/libgo/go/net/http/fs.go
+++ b/libgo/go/net/http/fs.go
@@ -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")
diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go
index 6697189..976ee75 100644
--- a/libgo/go/net/http/fs_test.go
+++ b/libgo/go/net/http/fs_test.go
@@ -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")
diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go
index f09e826..5b02e14 100644
--- a/libgo/go/net/http/httptest/server.go
+++ b/libgo/go/net/http/httptest/server.go
@@ -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},
}
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index bfcb3ca..1dc83e7 100644
--- a/libgo/go/net/http/httputil/reverseproxy.go
+++ b/libgo/go/net/http/httputil/reverseproxy.go
@@ -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
diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index c0327a9..2de1475 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -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()
}
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 97a0b13..670b541 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -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)
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index 7221d25..125f3f2 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -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
}
diff --git a/libgo/go/net/http/sniff.go b/libgo/go/net/http/sniff.go
index 5707c7f..c1c78e2 100644
--- a/libgo/go/net/http/sniff.go
+++ b/libgo/go/net/http/sniff.go
@@ -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"},
diff --git a/libgo/go/net/http/sniff_test.go b/libgo/go/net/http/sniff_test.go
index 86744ee..6efa8ce 100644
--- a/libgo/go/net/http/sniff_test.go
+++ b/libgo/go/net/http/sniff_test.go
@@ -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)
+ }
+ }
+}
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index 7729797..6f50f6f 100644
--- a/libgo/go/net/http/transport_test.go
+++ b/libgo/go/net/http/transport_test.go
@@ -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--
diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go
index 95246b2..e1afa32 100644
--- a/libgo/go/net/mail/message.go
+++ b/libgo/go/net/mail/message.go
@@ -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
}
diff --git a/libgo/go/net/mail/message_test.go b/libgo/go/net/mail/message_test.go
index 5653647..1f71cc4 100644
--- a/libgo/go/net/mail/message_test.go
+++ b/libgo/go/net/mail/message_test.go
@@ -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 {
diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go
index 3c884ca..f6e5238 100644
--- a/libgo/go/net/timeout_test.go
+++ b/libgo/go/net/timeout_test.go
@@ -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)
}
}
diff --git a/libgo/go/old/netchan/common.go b/libgo/go/old/netchan/common.go
index dfd1fd0..03fa8ff 100644
--- a/libgo/go/old/netchan/common.go
+++ b/libgo/go/old/netchan/common.go
@@ -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
diff --git a/libgo/go/old/netchan/export.go b/libgo/go/old/netchan/export.go
index d698dd5..d94c4b1 100644
--- a/libgo/go/old/netchan/export.go
+++ b/libgo/go/old/netchan/export.go
@@ -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)
}
diff --git a/libgo/go/old/netchan/import.go b/libgo/go/old/netchan/import.go
index 7243672..a6da821 100644
--- a/libgo/go/old/netchan/import.go
+++ b/libgo/go/old/netchan/import.go
@@ -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)
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index d234641..9665ea8 100644
--- a/libgo/go/os/exec/lp_unix.go
+++ b/libgo/go/os/exec/lp_unix.go
@@ -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
diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go
index db32623..ef5bd92 100644
--- a/libgo/go/os/exec/lp_windows.go
+++ b/libgo/go/os/exec/lp_windows.go
@@ -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
index 0000000..9c6ef42
--- /dev/null
+++ b/libgo/go/os/export_test.go
@@ -0,0 +1,9 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Export for testing.
+
+var Atime = atime
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index 386afb8..71845d3 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -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.
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index c80d3df..a4ab5d6 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -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 {
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 0bf31ec..671d1a4 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -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.
diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go
index d5f5ae6..a0d3c99 100644
--- a/libgo/go/os/getwd.go
+++ b/libgo/go/os/getwd.go
@@ -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.
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 170f733..299d2e8 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -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
index 0000000..3109a81
--- /dev/null
+++ b/libgo/go/os/os_unix_test.go
@@ -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)
+ }
+}
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index 82fade6..bc14a78 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -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
}
diff --git a/libgo/go/os/stat.go b/libgo/go/os/stat.go
index 8eb4ab4..c664fc1 100644
--- a/libgo/go/os/stat.go
+++ b/libgo/go/os/stat.go
@@ -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)
}
diff --git a/libgo/go/os/stat_openbsd.go b/libgo/go/os/stat_openbsd.go
index 6d3a381..66189a6 100644
--- a/libgo/go/os/stat_openbsd.go
+++ b/libgo/go/os/stat_openbsd.go
@@ -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)
}
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index df57b59..2638153 100644
--- a/libgo/go/os/types.go
+++ b/libgo/go/os/types.go
@@ -4,53 +4,111 @@
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)
+}
diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go
index 59f15e4..f9f44af 100644
--- a/libgo/go/os/user/user_test.go
+++ b/libgo/go/os/user/user_test.go
@@ -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")
diff --git a/libgo/go/patch/git.go b/libgo/go/patch/git.go
index 454eade..5c233fb 100644
--- a/libgo/go/patch/git.go
+++ b/libgo/go/patch/git.go
@@ -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.
diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go
index 8cf1f9a..c3678f5 100644
--- a/libgo/go/path/filepath/match.go
+++ b/libgo/go/path/filepath/match.go
@@ -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)
diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go
index 1b5d6c3..e3d6c34 100644
--- a/libgo/go/path/filepath/path.go
+++ b/libgo/go/path/filepath/path.go
@@ -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.
diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go
index c81cbf0..2bd62d3 100644
--- a/libgo/go/path/filepath/path_test.go
+++ b/libgo/go/path/filepath/path_test.go
@@ -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"},
{".", "..", ".."},
diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go
index b4d9207..53fdead 100644
--- a/libgo/go/strings/strings.go
+++ b/libgo/go/strings/strings.go
@@ -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
diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go
index 96207f5..957af67 100644
--- a/libgo/go/strings/strings_test.go
+++ b/libgo/go/strings/strings_test.go
@@ -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
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go
index 4f049a3..e81e5c5 100644
--- a/libgo/go/testing/benchmark.go
+++ b/libgo/go/testing/benchmark.go
@@ -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)
diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go
index 3b026ee..e23f13b 100644
--- a/libgo/go/testing/example.go
+++ b/libgo/go/testing/example.go
@@ -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",
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 08443a3..0b3a071 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -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)
diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go
index 42f9e56..4208d53 100644
--- a/libgo/go/text/template/doc.go
+++ b/libgo/go/text/template/doc.go
@@ -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
diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go
index 1910882..b74bc3b 100644
--- a/libgo/go/text/template/exec.go
+++ b/libgo/go/text/template/exec.go
@@ -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()
diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go
index 67b9416..cf3c415 100644
--- a/libgo/go/text/template/exec_test.go
+++ b/libgo/go/text/template/exec_test.go
@@ -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)
}
diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go
index 2ca09a7..d6e4bf1 100644
--- a/libgo/go/text/template/funcs.go
+++ b/libgo/go/text/template/funcs.go
@@ -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
diff --git a/libgo/go/text/template/helper.go b/libgo/go/text/template/helper.go
index a743a83..3636fb5 100644
--- a/libgo/go/text/template/helper.go
+++ b/libgo/go/text/template/helper.go
@@ -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
index 0000000..7b35d263
--- /dev/null
+++ b/libgo/go/text/template/multi_test.go
@@ -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
index 7075f2a..0000000
--- a/libgo/go/text/template/parse.go
+++ /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
-}
diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go
index e906ee8..346f613 100644
--- a/libgo/go/text/template/parse/parse.go
+++ b/libgo/go/text/template/parse/parse.go
@@ -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:
diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go
index 5c10086..fc93455 100644
--- a/libgo/go/text/template/parse/parse_test.go
+++ b/libgo/go/text/template/parse/parse_test.go
@@ -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
index 55f3ceb..0000000
--- a/libgo/go/text/template/parse/set.go
+++ /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
index 4841704..0000000
--- a/libgo/go/text/template/set.go
+++ /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
index f437bc7..0000000
--- a/libgo/go/text/template/set_test.go
+++ /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
index 0000000..04fca40
--- /dev/null
+++ b/libgo/go/text/template/template.go
@@ -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
+}
diff --git a/libgo/go/text/template/testdata/tmpl1.tmpl b/libgo/go/text/template/testdata/tmpl1.tmpl
index 3d15b81..b72b3a3 100644
--- a/libgo/go/text/template/testdata/tmpl1.tmpl
+++ b/libgo/go/text/template/testdata/tmpl1.tmpl
@@ -1 +1,3 @@
template1
+{{define "x"}}x{{end}}
+{{template "y"}}
diff --git a/libgo/go/text/template/testdata/tmpl2.tmpl b/libgo/go/text/template/testdata/tmpl2.tmpl
index a374d2f..16beba6 100644
--- a/libgo/go/text/template/testdata/tmpl2.tmpl
+++ b/libgo/go/text/template/testdata/tmpl2.tmpl
@@ -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
index 0000000..153b1a3
--- /dev/null
+++ b/libgo/go/time/example_test.go
@@ -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())
+}
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go
index 14b712a..082a51a 100644
--- a/libgo/go/time/format.go
+++ b/libgo/go/time/format.go
@@ -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
}
diff --git a/libgo/go/time/internal_test.go b/libgo/go/time/internal_test.go
index d7e7076..2c4df33 100644
--- a/libgo/go/time/internal_test.go
+++ b/libgo/go/time/internal_test.go
@@ -6,7 +6,7 @@ package time
func init() {
// force US/Pacific for time zone tests
- onceSetupZone.Do(setupTestingZone)
+ localOnce.Do(initTestingZone)
}
var Interrupt = interrupt
diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go
index 967fca0..1e23118 100644
--- a/libgo/go/time/sleep.go
+++ b/libgo/go/time/sleep.go
@@ -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,
},
diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go
index 9171da3..cbcc897 100644
--- a/libgo/go/time/sleep_test.go
+++ b/libgo/go/time/sleep_test.go
@@ -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)
+}
diff --git a/libgo/go/time/sys.go b/libgo/go/time/sys.go
index a5e529b..fe6bc27d 100644
--- a/libgo/go/time/sys.go
+++ b/libgo/go/time/sys.go
@@ -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
+}
diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go
index 3d31322..715d186 100644
--- a/libgo/go/time/sys_unix.go
+++ b/libgo/go/time/sys_unix.go
@@ -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)
}
diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go
index 95941a1..4440c22 100644
--- a/libgo/go/time/tick.go
+++ b/libgo/go/time/tick.go
@@ -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
}
diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go
index 4dcb639..3634934 100644
--- a/libgo/go/time/tick_test.go
+++ b/libgo/go/time/tick_test.go
@@ -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)
diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go
index e11d177..04ed86c 100644
--- a/libgo/go/time/time.go
+++ b/libgo/go/time/time.go
@@ -3,11 +3,109 @@
// 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}
}
diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go
index 01b8bea4a..9590e28 100644
--- a/libgo/go/time/time_test.go
+++ b/libgo/go/time/time_test.go
@@ -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
index 0000000..aca56e7
--- /dev/null
+++ b/libgo/go/time/zoneinfo.go
@@ -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)
+}
diff --git a/libgo/go/time/zoneinfo_plan9.go b/libgo/go/time/zoneinfo_plan9.go
index 577ef85..38aefc7 100644
--- a/libgo/go/time/zoneinfo_plan9.go
+++ b/libgo/go/time/zoneinfo_plan9.go
@@ -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
index b0fa6c3..0000000
--- a/libgo/go/time/zoneinfo_posix.go
+++ /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
-}
diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go
index b552e58..83d5b98 100644
--- a/libgo/go/time/zoneinfo_unix.go
+++ b/libgo/go/time/zoneinfo_unix.go
@@ -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)
}
diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go
index 995fd44..beef4de 100644
--- a/libgo/go/time/zoneinfo_windows.go
+++ b/libgo/go/time/zoneinfo_windows.go
@@ -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)
}
diff --git a/libgo/go/websocket/client.go b/libgo/go/websocket/client.go
index 5dfd824..89cdcda 100644
--- a/libgo/go/websocket/client.go
+++ b/libgo/go/websocket/client.go
@@ -72,8 +72,8 @@ A trivial example client:
package main
import (
- "http"
"log"
+ "net/http"
"strings"
"websocket"
)
diff --git a/libgo/go/websocket/hixie.go b/libgo/go/websocket/hixie.go
index 4d5360ff..ec7b7ae 100644
--- a/libgo/go/websocket/hixie.go
+++ b/libgo/go/websocket/hixie.go
@@ -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
}
diff --git a/libgo/go/websocket/hybi.go b/libgo/go/websocket/hybi.go
index b17d947..ff386dc 100644
--- a/libgo/go/websocket/hybi.go
+++ b/libgo/go/websocket/hybi.go
@@ -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
}
diff --git a/libgo/go/websocket/server.go b/libgo/go/websocket/server.go
index 57dc4fd..8320b03 100644
--- a/libgo/go/websocket/server.go
+++ b/libgo/go/websocket/server.go
@@ -60,8 +60,8 @@ A trivial example server:
package main
import (
- "http"
"io"
+ "net/http"
"websocket"
)
diff --git a/libgo/runtime/go-nanotime.c b/libgo/runtime/go-nanotime.c
index 197fb15..7e5e3e0 100644
--- a/libgo/runtime/go-nanotime.c
+++ b/libgo/runtime/go-nanotime.c
@@ -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
index 0000000..5df8085
--- /dev/null
+++ b/libgo/runtime/go-now.c
@@ -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;
+}
diff --git a/libgo/runtime/time.goc b/libgo/runtime/time.goc
index 93b896e..cae15e5 100644
--- a/libgo/runtime/time.goc
+++ b/libgo/runtime/time.goc
@@ -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) {