summaryrefslogtreecommitdiff
path: root/libgo
diff options
context:
space:
mode:
authorbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2011-12-14 08:52:21 +0000
committerbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2011-12-14 08:52:21 +0000
commitd0b175bffc2f2cb91d1fb529b7c36d3e984d9594 (patch)
treeeb438cb5c7cabed8d102b2c0c1bdd1b0aebb59eb /libgo
parent5e3123db0a9b4c8def9fee64446b130ce81ace45 (diff)
downloadgcc-d0b175bffc2f2cb91d1fb529b7c36d3e984d9594.tar.gz
2011-12-14 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk rev 182322 using svnmerge git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@182325 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo')
-rw-r--r--libgo/MERGE2
-rw-r--r--libgo/Makefile.am75
-rw-r--r--libgo/Makefile.in190
-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/bufio/bufio_test.go7
-rw-r--r--libgo/go/builtin/builtin.go12
-rw-r--r--libgo/go/bytes/bytes_test.go63
-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/compress/lzw/reader.go5
-rw-r--r--libgo/go/compress/lzw/writer.go17
-rw-r--r--libgo/go/compress/lzw/writer_test.go4
-rw-r--r--libgo/go/compress/zlib/writer_test.go4
-rw-r--r--libgo/go/crypto/aes/cipher.go2
-rw-r--r--libgo/go/crypto/bcrypt/bcrypt.go2
-rw-r--r--libgo/go/crypto/blowfish/cipher.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/rand/rand_windows.go12
-rw-r--r--libgo/go/crypto/rand/util.go4
-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/conn.go5
-rw-r--r--libgo/go/crypto/tls/handshake_client.go10
-rw-r--r--libgo/go/crypto/tls/handshake_messages.go161
-rw-r--r--libgo/go/crypto/tls/handshake_messages_test.go4
-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.go31
-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/crypto/xtea/cipher.go2
-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/bench_test.go157
-rw-r--r--libgo/go/encoding/json/decode.go6
-rw-r--r--libgo/go/encoding/json/encode.go89
-rw-r--r--libgo/go/encoding/json/scanner.go19
-rw-r--r--libgo/go/encoding/json/scanner_test.go5
-rw-r--r--libgo/go/encoding/xml/xml.go2
-rw-r--r--libgo/go/encoding/xml/xml_test.go124
-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/inotify/inotify_linux.go22
-rw-r--r--libgo/go/exp/sql/convert.go15
-rw-r--r--libgo/go/exp/sql/driver/driver.go47
-rw-r--r--libgo/go/exp/sql/fakedb_test.go47
-rw-r--r--libgo/go/exp/sql/sql.go157
-rw-r--r--libgo/go/exp/sql/sql_test.go89
-rw-r--r--libgo/go/exp/ssh/channel.go12
-rw-r--r--libgo/go/exp/ssh/cipher.go88
-rw-r--r--libgo/go/exp/ssh/cipher_test.go62
-rw-r--r--libgo/go/exp/ssh/client.go82
-rw-r--r--libgo/go/exp/ssh/client_auth.go148
-rw-r--r--libgo/go/exp/ssh/client_auth_test.go248
-rw-r--r--libgo/go/exp/ssh/client_func_test.go61
-rw-r--r--libgo/go/exp/ssh/common.go114
-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/messages.go8
-rw-r--r--libgo/go/exp/ssh/server.go60
-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.go146
-rw-r--r--libgo/go/exp/ssh/tcpip_func_test.go59
-rw-r--r--libgo/go/exp/ssh/transport.go63
-rw-r--r--libgo/go/exp/terminal/shell.go356
-rw-r--r--libgo/go/exp/terminal/terminal.go401
-rw-r--r--libgo/go/exp/terminal/terminal_test.go (renamed from libgo/go/exp/terminal/shell_test.go)4
-rw-r--r--libgo/go/exp/terminal/util.go102
-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.go20
-rw-r--r--libgo/go/fmt/print.go8
-rw-r--r--libgo/go/fmt/scan_test.go8
-rw-r--r--libgo/go/go/ast/ast.go100
-rw-r--r--libgo/go/go/ast/filter.go70
-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/build_test.go16
-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.go431
-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/doc.go4
-rw-r--r--libgo/go/html/doctype.go156
-rw-r--r--libgo/go/html/parse.go753
-rw-r--r--libgo/go/html/parse_test.go188
-rw-r--r--libgo/go/html/render.go91
-rw-r--r--libgo/go/html/template/clone_test.go32
-rw-r--r--libgo/go/html/template/content.go33
-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.go73
-rw-r--r--libgo/go/html/template/js.go20
-rw-r--r--libgo/go/html/template/template.go376
-rw-r--r--libgo/go/html/token.go76
-rw-r--r--libgo/go/html/token_test.go20
-rw-r--r--libgo/go/image/tiff/buffer.go7
-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.go4
-rw-r--r--libgo/go/io/multi_test.go2
-rw-r--r--libgo/go/log/log.go21
-rw-r--r--libgo/go/log/syslog/syslog.go3
-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.go32
-rw-r--r--libgo/go/math/big/int_test.go8
-rw-r--r--libgo/go/math/big/nat.go237
-rw-r--r--libgo/go/math/big/nat_test.go258
-rw-r--r--libgo/go/math/big/rat.go14
-rw-r--r--libgo/go/math/cbrt.go13
-rw-r--r--libgo/go/math/floor.go9
-rw-r--r--libgo/go/math/gamma.go15
-rw-r--r--libgo/go/math/lgamma.go155
-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/mime/multipart/formdata.go2
-rw-r--r--libgo/go/mime/type.go45
-rw-r--r--libgo/go/mime/type_test.go8
-rw-r--r--libgo/go/mime/type_unix.go59
-rw-r--r--libgo/go/mime/type_windows.go61
-rw-r--r--libgo/go/net/cgo_unix.go2
-rw-r--r--libgo/go/net/dnsclient_unix.go2
-rw-r--r--libgo/go/net/fd.go206
-rw-r--r--libgo/go/net/fd_linux.go16
-rw-r--r--libgo/go/net/fd_openbsd.go11
-rw-r--r--libgo/go/net/fd_select.go5
-rw-r--r--libgo/go/net/fd_windows.go79
-rw-r--r--libgo/go/net/file.go4
-rw-r--r--libgo/go/net/hosts.go12
-rw-r--r--libgo/go/net/http/cgi/host_test.go15
-rw-r--r--libgo/go/net/http/chunked.go125
-rw-r--r--libgo/go/net/http/chunked_test.go39
-rw-r--r--libgo/go/net/http/client_test.go27
-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.go145
-rw-r--r--libgo/go/net/http/fcgi/fcgi.go41
-rw-r--r--libgo/go/net/http/fcgi/fcgi_test.go47
-rw-r--r--libgo/go/net/http/filetransport_test.go3
-rw-r--r--libgo/go/net/http/fs.go28
-rw-r--r--libgo/go/net/http/fs_test.go32
-rw-r--r--libgo/go/net/http/httptest/server.go4
-rw-r--r--libgo/go/net/http/httputil/chunked.go140
-rw-r--r--libgo/go/net/http/httputil/chunked_test.go8
-rw-r--r--libgo/go/net/http/httputil/persist.go10
-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/readrequest_test.go6
-rw-r--r--libgo/go/net/http/request.go90
-rw-r--r--libgo/go/net/http/response_test.go4
-rw-r--r--libgo/go/net/http/serve_test.go39
-rw-r--r--libgo/go/net/http/server.go27
-rw-r--r--libgo/go/net/http/sniff.go34
-rw-r--r--libgo/go/net/http/sniff_test.go56
-rw-r--r--libgo/go/net/http/transfer.go4
-rw-r--r--libgo/go/net/http/transport.go2
-rw-r--r--libgo/go/net/http/transport_test.go2
-rw-r--r--libgo/go/net/http/transport_windows.go6
-rw-r--r--libgo/go/net/interface_bsd.go16
-rw-r--r--libgo/go/net/interface_darwin.go8
-rw-r--r--libgo/go/net/interface_freebsd.go8
-rw-r--r--libgo/go/net/interface_linux.go32
-rw-r--r--libgo/go/net/interface_windows.go4
-rw-r--r--libgo/go/net/ipsock.go4
-rw-r--r--libgo/go/net/ipsock_posix.go8
-rw-r--r--libgo/go/net/lookup_windows.go16
-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/newpollserver.go7
-rw-r--r--libgo/go/net/pipe.go4
-rw-r--r--libgo/go/net/sendfile_linux.go6
-rw-r--r--libgo/go/net/sendfile_windows.go2
-rw-r--r--libgo/go/net/sock.go8
-rw-r--r--libgo/go/net/tcpsock_posix.go4
-rw-r--r--libgo/go/net/timeout_test.go8
-rw-r--r--libgo/go/net/unixsock_posix.go4
-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/dir_largefile.go2
-rw-r--r--libgo/go/os/dir_regfile.go2
-rw-r--r--libgo/go/os/dir_unix.go4
-rw-r--r--libgo/go/os/env.go49
-rw-r--r--libgo/go/os/env_plan9.go99
-rw-r--r--libgo/go/os/env_unix.go112
-rw-r--r--libgo/go/os/env_windows.go128
-rw-r--r--libgo/go/os/error_plan9.go8
-rw-r--r--libgo/go/os/error_posix.go106
-rw-r--r--libgo/go/os/exec/exec.go8
-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/exec_plan9.go10
-rw-r--r--libgo/go/os/exec_posix.go8
-rw-r--r--libgo/go/os/exec_unix.go6
-rw-r--r--libgo/go/os/exec_windows.go23
-rw-r--r--libgo/go/os/export_test.go9
-rw-r--r--libgo/go/os/file.go80
-rw-r--r--libgo/go/os/file_plan9.go61
-rw-r--r--libgo/go/os/file_posix.go68
-rw-r--r--libgo/go/os/file_unix.go118
-rw-r--r--libgo/go/os/getwd.go12
-rw-r--r--libgo/go/os/os_test.go153
-rw-r--r--libgo/go/os/os_unix_test.go75
-rw-r--r--libgo/go/os/path.go6
-rw-r--r--libgo/go/os/proc.go4
-rw-r--r--libgo/go/os/stat.go72
-rw-r--r--libgo/go/os/stat_openbsd.go66
-rw-r--r--libgo/go/os/stat_plan9.go6
-rw-r--r--libgo/go/os/sys_bsd.go7
-rw-r--r--libgo/go/os/sys_uname.go2
-rw-r--r--libgo/go/os/time.go2
-rw-r--r--libgo/go/os/types.go132
-rw-r--r--libgo/go/os/user/lookup_unix.go5
-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/reflect/all_test.go55
-rw-r--r--libgo/go/reflect/deepequal.go6
-rw-r--r--libgo/go/reflect/type.go36
-rw-r--r--libgo/go/reflect/value.go1396
-rw-r--r--libgo/go/regexp/regexp.go2
-rw-r--r--libgo/go/runtime/gc_test.go16
-rw-r--r--libgo/go/strconv/decimal.go27
-rw-r--r--libgo/go/strconv/decimal_test.go12
-rw-r--r--libgo/go/strconv/ftoa.go9
-rw-r--r--libgo/go/strconv/ftoa_test.go24
-rw-r--r--libgo/go/strconv/internal_test.go6
-rw-r--r--libgo/go/strings/strings.go16
-rw-r--r--libgo/go/strings/strings_test.go113
-rw-r--r--libgo/go/sync/mutex.go2
-rw-r--r--libgo/go/syscall/bpf_bsd.go160
-rw-r--r--libgo/go/syscall/env_plan9.go74
-rw-r--r--libgo/go/syscall/env_unix.go85
-rw-r--r--libgo/go/syscall/env_windows.go77
-rw-r--r--libgo/go/syscall/errno.c13
-rw-r--r--libgo/go/syscall/errstr.go4
-rw-r--r--libgo/go/syscall/exec_stubs.go20
-rw-r--r--libgo/go/syscall/exec_unix.go121
-rw-r--r--libgo/go/syscall/exec_windows.go48
-rw-r--r--libgo/go/syscall/libcall_irix.go2
-rw-r--r--libgo/go/syscall/libcall_linux.go172
-rw-r--r--libgo/go/syscall/libcall_posix.go160
-rw-r--r--libgo/go/syscall/libcall_posix_largefile.go20
-rw-r--r--libgo/go/syscall/libcall_posix_regfile.go20
-rw-r--r--libgo/go/syscall/libcall_solaris_386.go4
-rw-r--r--libgo/go/syscall/libcall_solaris_amd64.go2
-rw-r--r--libgo/go/syscall/libcall_solaris_sparc.go2
-rw-r--r--libgo/go/syscall/libcall_solaris_sparc64.go2
-rw-r--r--libgo/go/syscall/libcall_support.go4
-rw-r--r--libgo/go/syscall/libcall_uname.go2
-rw-r--r--libgo/go/syscall/libcall_wait4.go5
-rw-r--r--libgo/go/syscall/libcall_waitpid.go5
-rw-r--r--libgo/go/syscall/lsf_linux.go28
-rw-r--r--libgo/go/syscall/mksyscall.awk6
-rw-r--r--libgo/go/syscall/netlink_linux.go43
-rw-r--r--libgo/go/syscall/route_bsd.go35
-rw-r--r--libgo/go/syscall/route_darwin.go2
-rw-r--r--libgo/go/syscall/route_freebsd.go2
-rw-r--r--libgo/go/syscall/sleep_rtems.go4
-rw-r--r--libgo/go/syscall/sleep_select.go4
-rw-r--r--libgo/go/syscall/sockcmsg_linux.go4
-rw-r--r--libgo/go/syscall/sockcmsg_unix.go14
-rw-r--r--libgo/go/syscall/socket.go182
-rw-r--r--libgo/go/syscall/socket_bsd.go10
-rw-r--r--libgo/go/syscall/socket_irix.go10
-rw-r--r--libgo/go/syscall/socket_linux.go26
-rw-r--r--libgo/go/syscall/socket_solaris.go10
-rw-r--r--libgo/go/syscall/syscall.go5
-rw-r--r--libgo/go/syscall/syscall_unix.go66
-rw-r--r--libgo/go/testing/benchmark.go52
-rw-r--r--libgo/go/testing/example.go32
-rw-r--r--libgo/go/testing/testing.go76
-rw-r--r--libgo/go/text/tabwriter/tabwriter.go3
-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.go35
-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.go89
-rw-r--r--libgo/go/text/template/parse/parse.go114
-rw-r--r--libgo/go/text/template/parse/parse_test.go2
-rw-r--r--libgo/go/text/template/parse/set.go49
-rw-r--r--libgo/go/text/template/set.go120
-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.go211
-rw-r--r--libgo/go/time/sleep_test.go91
-rw-r--r--libgo/go/time/sys.go55
-rw-r--r--libgo/go/time/sys_plan9.go13
-rw-r--r--libgo/go/time/sys_unix.go15
-rw-r--r--libgo/go/time/sys_windows.go13
-rw-r--r--libgo/go/time/tick.go188
-rw-r--r--libgo/go/time/tick_test.go14
-rw-r--r--libgo/go/time/time.go957
-rw-r--r--libgo/go/time/time_test.go374
-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.go135
-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/go/websocket/websocket.go10
-rwxr-xr-xlibgo/merge.sh3
-rwxr-xr-xlibgo/mksysinfo.sh56
-rw-r--r--libgo/runtime/go-nanotime.c9
-rw-r--r--libgo/runtime/go-now.c31
-rw-r--r--libgo/runtime/go-setenv.c4
-rw-r--r--libgo/runtime/go-signal.c4
-rw-r--r--libgo/runtime/mem_posix_memalign.c3
-rw-r--r--libgo/runtime/mgc0.c1
-rw-r--r--libgo/runtime/proc.c18
-rw-r--r--libgo/runtime/runtime.c12
-rw-r--r--libgo/runtime/runtime.h38
-rw-r--r--libgo/runtime/thread-linux.c2
-rw-r--r--libgo/runtime/time.goc256
398 files changed, 12850 insertions, 8470 deletions
diff --git a/libgo/MERGE b/libgo/MERGE
index 5e896c008bf..9847f4715a5 100644
--- a/libgo/MERGE
+++ b/libgo/MERGE
@@ -1,4 +1,4 @@
-2f4482b89a6b
+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 cd264e32668..67ae8710903 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 \
@@ -648,7 +644,8 @@ go_math_files = \
go_mime_files = \
go/mime/grammar.go \
go/mime/mediatype.go \
- go/mime/type.go
+ go/mime/type.go \
+ go/mime/type_unix.go
if LIBGO_IS_RTEMS
go_net_fd_os_file = go/net/fd_select.go
@@ -770,7 +767,6 @@ go_os_files = \
$(go_os_dir_file) \
go/os/dir.go \
go/os/env.go \
- go/os/env_unix.go \
go/os/error.go \
go/os/error_posix.go \
go/os/exec.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 = \
@@ -1156,6 +1151,7 @@ go_exp_sql_files = \
go/exp/sql/sql.go
go_exp_ssh_files = \
go/exp/ssh/channel.go \
+ go/exp/ssh/cipher.go \
go/exp/ssh/client.go \
go/exp/ssh/client_auth.go \
go/exp/ssh/common.go \
@@ -1164,10 +1160,11 @@ go_exp_ssh_files = \
go/exp/ssh/server.go \
go/exp/ssh/server_shell.go \
go/exp/ssh/session.go \
+ go/exp/ssh/tcpip.go \
go/exp/ssh/transport.go
go_exp_terminal_files = \
- go/exp/terminal/shell.go \
- go/exp/terminal/terminal.go
+ go/exp/terminal/terminal.go \
+ go/exp/terminal/util.go
go_exp_types_files = \
go/exp/types/check.go \
go/exp/types/const.go \
@@ -1176,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
@@ -1413,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
@@ -1459,11 +1450,22 @@ syscall_exec_file = go/syscall/exec_unix.go
endif
# Define Wait4.
+if LIBGO_IS_RTEMS
+syscall_wait_file =
+else
if HAVE_WAIT4
syscall_wait_file = go/syscall/libcall_wait4.go
else
syscall_wait_file = go/syscall/libcall_waitpid.go
endif
+endif
+
+# Support for pulling apart wait status.
+if LIBGO_IS_RTEMS
+syscall_wait_c_file =
+else
+syscall_wait_c_file = go/syscall/wait.c
+endif
# Define Sleep.
if LIBGO_IS_RTEMS
@@ -1546,6 +1548,7 @@ syscall_netlink_file =
endif
go_base_syscall_files = \
+ go/syscall/env_unix.go \
go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \
go/syscall/socket.go \
@@ -1572,7 +1575,7 @@ go_syscall_files = \
syscall_arch.go
go_syscall_c_files = \
go/syscall/errno.c \
- go/syscall/wait.c
+ $(syscall_wait_c_file)
libcalls.go: s-libcalls; @true
s-libcalls: Makefile go/syscall/mksyscall.awk $(go_base_syscall_files)
@@ -1722,14 +1725,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 \
@@ -2781,16 +2782,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)
@@ -2851,16 +2842,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)
@@ -3683,8 +3664,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
@@ -3700,9 +3679,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)
@@ -3947,6 +3923,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 11b86509e6f..2e1ea76eb2c 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 \
@@ -1032,7 +1028,8 @@ go_math_files = \
go_mime_files = \
go/mime/grammar.go \
go/mime/mediatype.go \
- go/mime/type.go
+ go/mime/type.go \
+ go/mime/type_unix.go
# By default use select with pipes. Most systems should have
# something better.
@@ -1103,7 +1100,6 @@ go_os_files = \
$(go_os_dir_file) \
go/os/dir.go \
go/os/env.go \
- go/os/env_unix.go \
go/os/error.go \
go/os/error_posix.go \
go/os/exec.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
@@ -1521,6 +1515,7 @@ go_exp_sql_files = \
go_exp_ssh_files = \
go/exp/ssh/channel.go \
+ go/exp/ssh/cipher.go \
go/exp/ssh/client.go \
go/exp/ssh/client_auth.go \
go/exp/ssh/common.go \
@@ -1529,11 +1524,12 @@ go_exp_ssh_files = \
go/exp/ssh/server.go \
go/exp/ssh/server_shell.go \
go/exp/ssh/session.go \
+ go/exp/ssh/tcpip.go \
go/exp/ssh/transport.go
go_exp_terminal_files = \
- go/exp/terminal/shell.go \
- go/exp/terminal/terminal.go
+ go/exp/terminal/terminal.go \
+ go/exp/terminal/util.go
go_exp_types_files = \
go/exp/types/check.go \
@@ -1543,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
@@ -1803,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
@@ -1847,10 +1837,15 @@ go_unicode_utf8_files = \
# Define ForkExec and Exec.
@LIBGO_IS_RTEMS_TRUE@syscall_exec_file = go/syscall/exec_stubs.go
-@HAVE_WAIT4_FALSE@syscall_wait_file = go/syscall/libcall_waitpid.go
+@HAVE_WAIT4_FALSE@@LIBGO_IS_RTEMS_FALSE@syscall_wait_file = go/syscall/libcall_waitpid.go
+@HAVE_WAIT4_TRUE@@LIBGO_IS_RTEMS_FALSE@syscall_wait_file = go/syscall/libcall_wait4.go
# Define Wait4.
-@HAVE_WAIT4_TRUE@syscall_wait_file = go/syscall/libcall_wait4.go
+@LIBGO_IS_RTEMS_TRUE@syscall_wait_file =
+@LIBGO_IS_RTEMS_FALSE@syscall_wait_c_file = go/syscall/wait.c
+
+# Support for pulling apart wait status.
+@LIBGO_IS_RTEMS_TRUE@syscall_wait_c_file =
@LIBGO_IS_RTEMS_FALSE@syscall_sleep_file = go/syscall/sleep_select.go
# Define Sleep.
@@ -1890,6 +1885,7 @@ go_unicode_utf8_files = \
# Support for netlink sockets and messages.
@LIBGO_IS_LINUX_TRUE@syscall_netlink_file = go/syscall/netlink_linux.go
go_base_syscall_files = \
+ go/syscall/env_unix.go \
go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \
go/syscall/socket.go \
@@ -1917,7 +1913,7 @@ go_syscall_files = \
go_syscall_c_files = \
go/syscall/errno.c \
- go/syscall/wait.c
+ $(syscall_wait_c_file)
@LIBGO_IS_LINUX_FALSE@os_lib_inotify_lo =
@@ -2013,14 +2009,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 \
@@ -2292,6 +2286,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 \
@@ -2508,6 +2503,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@
@@ -2796,6 +2792,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
@@ -3371,26 +3374,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)"
@@ -4168,7 +4151,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
@@ -4238,7 +4221,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 \
@@ -4304,7 +4286,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA \
- uninstall-toolexeclibgoexpguiDATA \
uninstall-toolexeclibgoexpsqlDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
@@ -4352,7 +4333,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 \
@@ -4382,7 +4362,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA \
- uninstall-toolexeclibgoexpguiDATA \
uninstall-toolexeclibgoexpsqlDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
@@ -5380,16 +5359,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)
@@ -5450,16 +5419,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)
@@ -6277,8 +6236,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
@@ -6294,9 +6251,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 67355086a63..fc7a40923cd 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 facba2cc7a3..76955e2ec03 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 00eea6b62d7..5ca4212ae7b 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 222df90782c..b9310b3f189 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 6cc93868820..8d7ed32d32e 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 cfbe5498a15..4365009a308 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 ca0b04e2bba..8c0ecaa4386 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 b862b5a6acb..43c04bb27b2 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/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go
index 1f893951c15..54029cd40fd 100644
--- a/libgo/go/bufio/bufio_test.go
+++ b/libgo/go/bufio/bufio_test.go
@@ -10,7 +10,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "os"
"strings"
"testing"
"testing/iotest"
@@ -425,9 +424,9 @@ var errorWriterTests = []errorWriterTest{
{0, 1, nil, io.ErrShortWrite},
{1, 2, nil, io.ErrShortWrite},
{1, 1, nil, nil},
- {0, 1, os.EPIPE, os.EPIPE},
- {1, 2, os.EPIPE, os.EPIPE},
- {1, 1, os.EPIPE, os.EPIPE},
+ {0, 1, io.ErrClosedPipe, io.ErrClosedPipe},
+ {1, 2, io.ErrClosedPipe, io.ErrClosedPipe},
+ {1, 1, io.ErrClosedPipe, io.ErrClosedPipe},
}
func TestWriteErrors(t *testing.T) {
diff --git a/libgo/go/builtin/builtin.go b/libgo/go/builtin/builtin.go
index 5a7aaf364db..e81616ca418 100644
--- a/libgo/go/builtin/builtin.go
+++ b/libgo/go/builtin/builtin.go
@@ -91,6 +91,11 @@ type rune rune
// invocation.
type Type int
+// Type1 is here for the purposes of documentation only. It is a stand-in
+// for any Go type, but represents the same type for any given function
+// invocation.
+type Type1 int
+
// IntegerType is here for the purposes of documentation only. It is a stand-in
// for any integer type: int, uint, int8 etc.
type IntegerType int
@@ -119,6 +124,11 @@ func append(slice []Type, elems ...Type) []Type
// len(src) and len(dst).
func copy(dst, src []Type) int
+// The delete built-in function deletes the element with the specified key
+// (m[key]) from the map. If there is no such element, delete is a no-op.
+// If m is nil, delete panics.
+func delete(m map[Type]Type1, key Type)
+
// The len built-in function returns the length of v, according to its type:
// Array: the number of elements in v.
// Pointer to array: the number of elements in *v (even if v is nil).
@@ -171,7 +181,7 @@ func complex(r, i FloatType) ComplexType
// The return value will be floating point type corresponding to the type of c.
func real(c ComplexType) FloatType
-// The imaginary built-in function returns the imaginary part of the complex
+// The imag built-in function returns the imaginary part of the complex
// number c. The return value will be floating point type corresponding to
// the type of c.
func imag(c ComplexType) FloatType
diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go
index 9256b184274..829ef05319c 100644
--- a/libgo/go/bytes/bytes_test.go
+++ b/libgo/go/bytes/bytes_test.go
@@ -662,48 +662,49 @@ func TestRunes(t *testing.T) {
}
type TrimTest struct {
- f func([]byte, string) []byte
+ f string
in, cutset, out string
}
var trimTests = []TrimTest{
- {Trim, "abba", "a", "bb"},
- {Trim, "abba", "ab", ""},
- {TrimLeft, "abba", "ab", ""},
- {TrimRight, "abba", "ab", ""},
- {TrimLeft, "abba", "a", "bba"},
- {TrimRight, "abba", "a", "abb"},
- {Trim, "<tag>", "<>", "tag"},
- {Trim, "* listitem", " *", "listitem"},
- {Trim, `"quote"`, `"`, "quote"},
- {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
+ {"Trim", "abba", "a", "bb"},
+ {"Trim", "abba", "ab", ""},
+ {"TrimLeft", "abba", "ab", ""},
+ {"TrimRight", "abba", "ab", ""},
+ {"TrimLeft", "abba", "a", "bba"},
+ {"TrimRight", "abba", "a", "abb"},
+ {"Trim", "<tag>", "<>", "tag"},
+ {"Trim", "* listitem", " *", "listitem"},
+ {"Trim", `"quote"`, `"`, "quote"},
+ {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
//empty string tests
- {Trim, "abba", "", "abba"},
- {Trim, "", "123", ""},
- {Trim, "", "", ""},
- {TrimLeft, "abba", "", "abba"},
- {TrimLeft, "", "123", ""},
- {TrimLeft, "", "", ""},
- {TrimRight, "abba", "", "abba"},
- {TrimRight, "", "123", ""},
- {TrimRight, "", "", ""},
- {TrimRight, "☺\xc0", "☺", "☺\xc0"},
+ {"Trim", "abba", "", "abba"},
+ {"Trim", "", "123", ""},
+ {"Trim", "", "", ""},
+ {"TrimLeft", "abba", "", "abba"},
+ {"TrimLeft", "", "123", ""},
+ {"TrimLeft", "", "", ""},
+ {"TrimRight", "abba", "", "abba"},
+ {"TrimRight", "", "123", ""},
+ {"TrimRight", "", "", ""},
+ {"TrimRight", "☺\xc0", "☺", "☺\xc0"},
}
func TestTrim(t *testing.T) {
for _, tc := range trimTests {
- actual := string(tc.f([]byte(tc.in), tc.cutset))
- var name string
- switch tc.f {
- case Trim:
- name = "Trim"
- case TrimLeft:
- name = "TrimLeft"
- case TrimRight:
- name = "TrimRight"
+ name := tc.f
+ var f func([]byte, string) []byte
+ switch name {
+ case "Trim":
+ f = Trim
+ case "TrimLeft":
+ f = TrimLeft
+ case "TrimRight":
+ f = TrimRight
default:
- t.Error("Undefined trim function")
+ t.Errorf("Undefined trim function %s", name)
}
+ actual := string(f([]byte(tc.in), tc.cutset))
if actual != tc.out {
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
}
diff --git a/libgo/go/compress/gzip/gunzip.go b/libgo/go/compress/gzip/gunzip.go
index a23e515e0e0..7c78b9e366d 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 94b0f1f85e2..07b91b66823 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 121e627e6b2..815825be999 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/compress/lzw/reader.go b/libgo/go/compress/lzw/reader.go
index c787a9568b3..0ed742c8977 100644
--- a/libgo/go/compress/lzw/reader.go
+++ b/libgo/go/compress/lzw/reader.go
@@ -19,7 +19,6 @@ import (
"errors"
"fmt"
"io"
- "os"
)
// Order specifies the bit ordering in an LZW data stream.
@@ -212,8 +211,10 @@ func (d *decoder) flush() {
d.o = 0
}
+var errClosed = errors.New("compress/lzw: reader/writer is closed")
+
func (d *decoder) Close() error {
- d.err = os.EINVAL // in case any Reads come along
+ d.err = errClosed // in case any Reads come along
return nil
}
diff --git a/libgo/go/compress/lzw/writer.go b/libgo/go/compress/lzw/writer.go
index 3f380fadce2..488ba6428db 100644
--- a/libgo/go/compress/lzw/writer.go
+++ b/libgo/go/compress/lzw/writer.go
@@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"io"
- "os"
)
// A writer is a buffered, flushable writer.
@@ -49,8 +48,9 @@ const (
type encoder struct {
// w is the writer that compressed bytes are written to.
w writer
- // write, bits, nBits and width are the state for converting a code stream
- // into a byte stream.
+ // order, write, bits, nBits and width are the state for
+ // converting a code stream into a byte stream.
+ order Order
write func(*encoder, uint32) error
bits uint32
nBits uint
@@ -64,7 +64,7 @@ type encoder struct {
// call. It is equal to invalidCode if there was no such call.
savedCode uint32
// err is the first error encountered during writing. Closing the encoder
- // will make any future Write calls return os.EINVAL.
+ // will make any future Write calls return errClosed
err error
// table is the hash table from 20-bit keys to 12-bit values. Each table
// entry contains key<<12|val and collisions resolve by linear probing.
@@ -191,13 +191,13 @@ loop:
// flush e's underlying writer.
func (e *encoder) Close() error {
if e.err != nil {
- if e.err == os.EINVAL {
+ if e.err == errClosed {
return nil
}
return e.err
}
- // Make any future calls to Write return os.EINVAL.
- e.err = os.EINVAL
+ // Make any future calls to Write return errClosed.
+ e.err = errClosed
// Write the savedCode if valid.
if e.savedCode != invalidCode {
if err := e.write(e, e.savedCode); err != nil {
@@ -214,7 +214,7 @@ func (e *encoder) Close() error {
}
// Write the final bits.
if e.nBits > 0 {
- if e.write == (*encoder).writeMSB {
+ if e.order == MSB {
e.bits >>= 24
}
if err := e.w.WriteByte(uint8(e.bits)); err != nil {
@@ -250,6 +250,7 @@ func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser {
lw := uint(litWidth)
return &encoder{
w: bw,
+ order: order,
write: write,
width: 1 + lw,
litWidth: lw,
diff --git a/libgo/go/compress/lzw/writer_test.go b/libgo/go/compress/lzw/writer_test.go
index 154cdf8090e..d249a09b295 100644
--- a/libgo/go/compress/lzw/writer_test.go
+++ b/libgo/go/compress/lzw/writer_test.go
@@ -50,10 +50,6 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) {
return
}
_, err1 := lzww.Write(b[:n])
- if err1 == os.EPIPE {
- // Fail, but do not report the error, as some other (presumably reportable) error broke the pipe.
- return
- }
if err1 != nil {
t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1)
return
diff --git a/libgo/go/compress/zlib/writer_test.go b/libgo/go/compress/zlib/writer_test.go
index 32f05ab6856..a71894da320 100644
--- a/libgo/go/compress/zlib/writer_test.go
+++ b/libgo/go/compress/zlib/writer_test.go
@@ -59,10 +59,6 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) {
}
defer zlibw.Close()
_, err = zlibw.Write(b0)
- if err == os.EPIPE {
- // Fail, but do not report the error, as some other (presumably reported) error broke the pipe.
- return
- }
if err != nil {
t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err)
return
diff --git a/libgo/go/crypto/aes/cipher.go b/libgo/go/crypto/aes/cipher.go
index 5ad75eccb50..28752e73613 100644
--- a/libgo/go/crypto/aes/cipher.go
+++ b/libgo/go/crypto/aes/cipher.go
@@ -41,7 +41,7 @@ func NewCipher(key []byte) (*Cipher, error) {
}
// BlockSize returns the AES block size, 16 bytes.
-// It is necessary to satisfy the Cipher interface in the
+// It is necessary to satisfy the Block interface in the
// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
diff --git a/libgo/go/crypto/bcrypt/bcrypt.go b/libgo/go/crypto/bcrypt/bcrypt.go
index 97401356228..362b2eb53cb 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/blowfish/cipher.go b/libgo/go/crypto/blowfish/cipher.go
index a5d56d2ebae..94e10f0e267 100644
--- a/libgo/go/crypto/blowfish/cipher.go
+++ b/libgo/go/crypto/blowfish/cipher.go
@@ -54,7 +54,7 @@ func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
}
// BlockSize returns the Blowfish block size, 8 bytes.
-// It is necessary to satisfy the Cipher interface in the
+// It is necessary to satisfy the Block interface in the
// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
diff --git a/libgo/go/crypto/ecdsa/ecdsa_test.go b/libgo/go/crypto/ecdsa/ecdsa_test.go
index 22360b5708c..45433e10203 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 6a17bbd44fa..deaceafb260 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 03431c92f75..eac254b9d19 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 f21cc51a21a..e51e8bee50c 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 721bd4cbcc8..b56edd7875d 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 20f3a1b6f75..182cfb85370 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 857002b7013..b15e4668c32 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 a04b5bd7135..b9dfdf94e31 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 7be37211c10..bacca558b48 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 fe4557aafc1..98cee5e75ae 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 ae54f8c83ee..841475f80c0 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 b705d226e1f..df39970c0b6 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 c0ff82b4135..d67e9688617 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 60eebaa6b09..35d8951a86b 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 7d71dc49a7b..9aa30e0c15f 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 6e8bfbce66e..72f459f47bf 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 4ebb906cad7..1cdc1ee0f0c 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 8225db6d2f6..dff776e3eb2 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 2a753db16bd..83673e17335 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 6f3450c9cdb..60dae01e64b 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 3cadf4cc95a..02fa5b75bff 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 09442ad2830..d9cddf6d2ad 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/rand/rand_windows.go b/libgo/go/crypto/rand/rand_windows.go
index 590571d23f6..2b2bd4bba6b 100644
--- a/libgo/go/crypto/rand/rand_windows.go
+++ b/libgo/go/crypto/rand/rand_windows.go
@@ -28,16 +28,16 @@ func (r *rngReader) Read(b []byte) (n int, err error) {
if r.prov == 0 {
const provType = syscall.PROV_RSA_FULL
const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT
- errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags)
- if errno != 0 {
+ err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags)
+ if err != nil {
r.mu.Unlock()
- return 0, os.NewSyscallError("CryptAcquireContext", errno)
+ return 0, os.NewSyscallError("CryptAcquireContext", err)
}
}
r.mu.Unlock()
- errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
- if errno != 0 {
- return 0, os.NewSyscallError("CryptGenRandom", errno)
+ err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
+ if err != nil {
+ return 0, os.NewSyscallError("CryptGenRandom", err)
}
return len(b), nil
}
diff --git a/libgo/go/crypto/rand/util.go b/libgo/go/crypto/rand/util.go
index b44ae9897ba..fc5fe6c65e9 100644
--- a/libgo/go/crypto/rand/util.go
+++ b/libgo/go/crypto/rand/util.go
@@ -5,16 +5,16 @@
package rand
import (
+ "errors"
"io"
"math/big"
- "os"
)
// Prime returns a number, p, of the given size, such that p is prime
// with high probability.
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
if bits < 1 {
- err = os.EINVAL
+ err = errors.New("crypto/rand: prime size must be positive")
}
b := uint(bits % 8)
diff --git a/libgo/go/crypto/ripemd160/ripemd160.go b/libgo/go/crypto/ripemd160/ripemd160.go
index 6ccfe875f55..c128ee445a5 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 f4135f5cf65..5df1b2593d2 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 66188ac10ed..58d5fda1976 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 27ccf61c4fc..f74525c103a 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 4cdf5b2e989..f41cdb5b027 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 2712fe35eaf..c23df6c41e9 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 14b8cfc7eca..34861f6cf49 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 42a3fa7a010..a6efb375456 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 1bd27982bb7..3cf65cbe7c8 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 dd116dc17b3..a70f7c54e39 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 1134f362583..c0e8656f79b 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 ea520859b82..f57d932a98f 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/conn.go b/libgo/go/crypto/tls/conn.go
index f4178e30c58..b8fa2737f67 100644
--- a/libgo/go/crypto/tls/conn.go
+++ b/libgo/go/crypto/tls/conn.go
@@ -93,7 +93,8 @@ func (c *Conn) SetTimeout(nsec int64) error {
}
// SetReadTimeout sets the time (in nanoseconds) that
-// Read will wait for data before returning os.EAGAIN.
+// Read will wait for data before returning a net.Error
+// with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline.
func (c *Conn) SetReadTimeout(nsec int64) error {
return c.conn.SetReadTimeout(nsec)
@@ -737,7 +738,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
return c.writeRecord(recordTypeApplicationData, b)
}
-// Read can be made to time out and return err == os.EAGAIN
+// Read can be made to time out and return a net.Error with Timeout() == true
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *Conn) Read(b []byte) (n int, err error) {
if err = c.Handshake(); err != nil {
diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go
index aed991ccd1b..b4337f2aac6 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_messages.go b/libgo/go/crypto/tls/handshake_messages.go
index f11232d8ee5..5438e749ce8 100644
--- a/libgo/go/crypto/tls/handshake_messages.go
+++ b/libgo/go/crypto/tls/handshake_messages.go
@@ -4,6 +4,8 @@
package tls
+import "bytes"
+
type clientHelloMsg struct {
raw []byte
vers uint16
@@ -18,6 +20,25 @@ type clientHelloMsg struct {
supportedPoints []uint8
}
+func (m *clientHelloMsg) equal(i interface{}) bool {
+ m1, ok := i.(*clientHelloMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ m.vers == m1.vers &&
+ bytes.Equal(m.random, m1.random) &&
+ bytes.Equal(m.sessionId, m1.sessionId) &&
+ eqUint16s(m.cipherSuites, m1.cipherSuites) &&
+ bytes.Equal(m.compressionMethods, m1.compressionMethods) &&
+ m.nextProtoNeg == m1.nextProtoNeg &&
+ m.serverName == m1.serverName &&
+ m.ocspStapling == m1.ocspStapling &&
+ eqUint16s(m.supportedCurves, m1.supportedCurves) &&
+ bytes.Equal(m.supportedPoints, m1.supportedPoints)
+}
+
func (m *clientHelloMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -309,6 +330,23 @@ type serverHelloMsg struct {
ocspStapling bool
}
+func (m *serverHelloMsg) equal(i interface{}) bool {
+ m1, ok := i.(*serverHelloMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ m.vers == m1.vers &&
+ bytes.Equal(m.random, m1.random) &&
+ bytes.Equal(m.sessionId, m1.sessionId) &&
+ m.cipherSuite == m1.cipherSuite &&
+ m.compressionMethod == m1.compressionMethod &&
+ m.nextProtoNeg == m1.nextProtoNeg &&
+ eqStrings(m.nextProtos, m1.nextProtos) &&
+ m.ocspStapling == m1.ocspStapling
+}
+
func (m *serverHelloMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -463,6 +501,16 @@ type certificateMsg struct {
certificates [][]byte
}
+func (m *certificateMsg) equal(i interface{}) bool {
+ m1, ok := i.(*certificateMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ eqByteSlices(m.certificates, m1.certificates)
+}
+
func (m *certificateMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -540,6 +588,16 @@ type serverKeyExchangeMsg struct {
key []byte
}
+func (m *serverKeyExchangeMsg) equal(i interface{}) bool {
+ m1, ok := i.(*serverKeyExchangeMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.key, m1.key)
+}
+
func (m *serverKeyExchangeMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -571,6 +629,17 @@ type certificateStatusMsg struct {
response []byte
}
+func (m *certificateStatusMsg) equal(i interface{}) bool {
+ m1, ok := i.(*certificateStatusMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ m.statusType == m1.statusType &&
+ bytes.Equal(m.response, m1.response)
+}
+
func (m *certificateStatusMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -622,6 +691,11 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool {
type serverHelloDoneMsg struct{}
+func (m *serverHelloDoneMsg) equal(i interface{}) bool {
+ _, ok := i.(*serverHelloDoneMsg)
+ return ok
+}
+
func (m *serverHelloDoneMsg) marshal() []byte {
x := make([]byte, 4)
x[0] = typeServerHelloDone
@@ -637,6 +711,16 @@ type clientKeyExchangeMsg struct {
ciphertext []byte
}
+func (m *clientKeyExchangeMsg) equal(i interface{}) bool {
+ m1, ok := i.(*clientKeyExchangeMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.ciphertext, m1.ciphertext)
+}
+
func (m *clientKeyExchangeMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -671,6 +755,16 @@ type finishedMsg struct {
verifyData []byte
}
+func (m *finishedMsg) equal(i interface{}) bool {
+ m1, ok := i.(*finishedMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.verifyData, m1.verifyData)
+}
+
func (m *finishedMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -698,6 +792,16 @@ type nextProtoMsg struct {
proto string
}
+func (m *nextProtoMsg) equal(i interface{}) bool {
+ m1, ok := i.(*nextProtoMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ m.proto == m1.proto
+}
+
func (m *nextProtoMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -759,6 +863,17 @@ type certificateRequestMsg struct {
certificateAuthorities [][]byte
}
+func (m *certificateRequestMsg) equal(i interface{}) bool {
+ m1, ok := i.(*certificateRequestMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
+ eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities)
+}
+
func (m *certificateRequestMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -859,6 +974,16 @@ type certificateVerifyMsg struct {
signature []byte
}
+func (m *certificateVerifyMsg) equal(i interface{}) bool {
+ m1, ok := i.(*certificateVerifyMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.signature, m1.signature)
+}
+
func (m *certificateVerifyMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -902,3 +1027,39 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return true
}
+
+func eqUint16s(x, y []uint16) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, v := range x {
+ if y[i] != v {
+ return false
+ }
+ }
+ return true
+}
+
+func eqStrings(x, y []string) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, v := range x {
+ if y[i] != v {
+ return false
+ }
+ }
+ return true
+}
+
+func eqByteSlices(x, y [][]byte) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, v := range x {
+ if !bytes.Equal(v, y[i]) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/libgo/go/crypto/tls/handshake_messages_test.go b/libgo/go/crypto/tls/handshake_messages_test.go
index 87e8f7e428d..e62a9d581b3 100644
--- a/libgo/go/crypto/tls/handshake_messages_test.go
+++ b/libgo/go/crypto/tls/handshake_messages_test.go
@@ -27,10 +27,12 @@ var tests = []interface{}{
type testMessage interface {
marshal() []byte
unmarshal([]byte) bool
+ equal(interface{}) bool
}
func TestMarshalUnmarshal(t *testing.T) {
rand := rand.New(rand.NewSource(0))
+
for i, iface := range tests {
ty := reflect.ValueOf(iface).Type()
@@ -54,7 +56,7 @@ func TestMarshalUnmarshal(t *testing.T) {
}
m2.marshal() // to fill any marshal cache in the message
- if !reflect.DeepEqual(m1, m2) {
+ if !m1.equal(m2) {
t.Errorf("#%d got:%#v want:%#v %x", i, m2, m1, marshaled)
break
}
diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go
index d5af084eda5..bbb23c0c9f6 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 bc3797947f5..e00c32c5508 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 08fb852d66a..b531717d840 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 d758f21aa8e..637ef03e2d7 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 095beec104a..1b9aeb03b5b 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 b8e27a9a5d2..319309ae6e7 100644
--- a/libgo/go/crypto/tls/root_windows.go
+++ b/libgo/go/crypto/tls/root_windows.go
@@ -6,39 +6,32 @@ package tls
import (
"crypto/x509"
- "reflect"
"syscall"
"unsafe"
)
func loadStore(roots *x509.CertPool, name string) {
- store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
- if errno != 0 {
+ store, err := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
+ 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 3ca62407ff0..79ab5023129 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 b9196ed46ed..adc7f9bc6d7 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 00000000000..4d8e0518e02
--- /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 00000000000..372005f908c
--- /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 b35274c9ae1..8eced55f932 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 3021d20a67f..50a3b66e555 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 2194d15bc88..df5443023ff 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 9ff7db9a0f9..7e6b5c96f53 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 c42471507be..f0327b0124d 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/crypto/xtea/cipher.go b/libgo/go/crypto/xtea/cipher.go
index 64d933c2b67..3ed05814a3b 100644
--- a/libgo/go/crypto/xtea/cipher.go
+++ b/libgo/go/crypto/xtea/cipher.go
@@ -44,7 +44,7 @@ func NewCipher(key []byte) (*Cipher, error) {
}
// BlockSize returns the XTEA block size, 8 bytes.
-// It is necessary to satisfy the Cipher interface in the
+// It is necessary to satisfy the Block interface in the
// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go
index a0066654f8d..22a0dde0da4 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 1c529bdb30c..2e6fccf7b80 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 89c50a70ef4..c181e43f979 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 03df5f1e1d5..d05b5d8d4e9 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/bench_test.go b/libgo/go/encoding/json/bench_test.go
new file mode 100644
index 00000000000..f0c52011a1d
--- /dev/null
+++ b/libgo/go/encoding/json/bench_test.go
@@ -0,0 +1,157 @@
+// 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.
+
+// Large data benchmark.
+// The JSON data is a summary of agl's changes in the
+// go, webkit, and chromium open source projects.
+// We benchmark converting between the JSON form
+// and in-memory data structures.
+
+package json
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+type codeResponse struct {
+ Tree *codeNode `json:"tree"`
+ Username string `json:"username"`
+}
+
+type codeNode struct {
+ Name string `json:"name"`
+ Kids []*codeNode `json:"kids"`
+ CLWeight float64 `json:"cl_weight"`
+ Touches int `json:"touches"`
+ MinT int64 `json:"min_t"`
+ MaxT int64 `json:"max_t"`
+ MeanT int64 `json:"mean_t"`
+}
+
+var codeJSON []byte
+var codeStruct codeResponse
+
+func codeInit() {
+ f, err := os.Open("testdata/code.json.gz")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ gz, err := gzip.NewReader(f)
+ if err != nil {
+ panic(err)
+ }
+ data, err := ioutil.ReadAll(gz)
+ if err != nil {
+ panic(err)
+ }
+
+ codeJSON = data
+
+ if err := Unmarshal(codeJSON, &codeStruct); err != nil {
+ panic("unmarshal code.json: " + err.Error())
+ }
+
+ if data, err = Marshal(&codeStruct); err != nil {
+ panic("marshal code.json: " + err.Error())
+ }
+
+ if !bytes.Equal(data, codeJSON) {
+ println("different lengths", len(data), len(codeJSON))
+ for i := 0; i < len(data) && i < len(codeJSON); i++ {
+ if data[i] != codeJSON[i] {
+ println("re-marshal: changed at byte", i)
+ println("orig: ", string(codeJSON[i-10:i+10]))
+ println("new: ", string(data[i-10:i+10]))
+ break
+ }
+ }
+ panic("re-marshal code.json: different result")
+ }
+}
+
+func BenchmarkCodeEncoder(b *testing.B) {
+ if codeJSON == nil {
+ b.StopTimer()
+ codeInit()
+ b.StartTimer()
+ }
+ enc := NewEncoder(ioutil.Discard)
+ for i := 0; i < b.N; i++ {
+ if err := enc.Encode(&codeStruct); err != nil {
+ panic(err)
+ }
+ }
+ b.SetBytes(int64(len(codeJSON)))
+}
+
+func BenchmarkCodeMarshal(b *testing.B) {
+ if codeJSON == nil {
+ b.StopTimer()
+ codeInit()
+ b.StartTimer()
+ }
+ for i := 0; i < b.N; i++ {
+ if _, err := Marshal(&codeStruct); err != nil {
+ panic(err)
+ }
+ }
+ b.SetBytes(int64(len(codeJSON)))
+}
+
+func BenchmarkCodeDecoder(b *testing.B) {
+ if codeJSON == nil {
+ b.StopTimer()
+ codeInit()
+ b.StartTimer()
+ }
+ var buf bytes.Buffer
+ dec := NewDecoder(&buf)
+ var r codeResponse
+ for i := 0; i < b.N; i++ {
+ buf.Write(codeJSON)
+ // hide EOF
+ buf.WriteByte('\n')
+ buf.WriteByte('\n')
+ buf.WriteByte('\n')
+ if err := dec.Decode(&r); err != nil {
+ panic(err)
+ }
+ }
+ b.SetBytes(int64(len(codeJSON)))
+}
+
+func BenchmarkCodeUnmarshal(b *testing.B) {
+ if codeJSON == nil {
+ b.StopTimer()
+ codeInit()
+ b.StartTimer()
+ }
+ for i := 0; i < b.N; i++ {
+ var r codeResponse
+ if err := Unmarshal(codeJSON, &r); err != nil {
+ panic(err)
+ }
+ }
+ b.SetBytes(int64(len(codeJSON)))
+}
+
+func BenchmarkCodeUnmarshalReuse(b *testing.B) {
+ if codeJSON == nil {
+ b.StopTimer()
+ codeInit()
+ b.StartTimer()
+ }
+ var r codeResponse
+ for i := 0; i < b.N; i++ {
+ if err := Unmarshal(codeJSON, &r); err != nil {
+ panic(err)
+ }
+ }
+ b.SetBytes(int64(len(codeJSON)))
+}
diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go
index 41295d2d241..2ea06c50c27 100644
--- a/libgo/go/encoding/json/decode.go
+++ b/libgo/go/encoding/json/decode.go
@@ -227,7 +227,7 @@ func (d *decodeState) value(v reflect.Value) {
// d.scan thinks we're still at the beginning of the item.
// Feed in an empty string - the shortest, simplest value -
// so that it knows we got to the end of the value.
- if d.scan.step == stateRedo {
+ if d.scan.redo {
panic("redo")
}
d.scan.step(&d.scan, '"')
@@ -381,6 +381,7 @@ func (d *decodeState) array(v reflect.Value) {
d.error(errPhase)
}
}
+
if i < av.Len() {
if !sv.IsValid() {
// Array. Zero the rest.
@@ -392,6 +393,9 @@ func (d *decodeState) array(v reflect.Value) {
sv.SetLen(i)
}
}
+ if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() {
+ sv.Set(reflect.MakeSlice(sv.Type(), 0, 0))
+ }
}
// object consumes an object from d.data[d.off-1:], decoding into the value v.
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go
index 35964c5d9c2..14284f50e47 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/json/scanner.go b/libgo/go/encoding/json/scanner.go
index 179690464b9..2661f410e01 100644
--- a/libgo/go/encoding/json/scanner.go
+++ b/libgo/go/encoding/json/scanner.go
@@ -80,6 +80,9 @@ type scanner struct {
// on a 64-bit Mac Mini, and it's nicer to read.
step func(*scanner, int) int
+ // Reached end of top-level value.
+ endTop bool
+
// Stack of what we're in the middle of - array values, object keys, object values.
parseState []int
@@ -87,6 +90,7 @@ type scanner struct {
err error
// 1-byte redo (see undo method)
+ redo bool
redoCode int
redoState func(*scanner, int) int
@@ -135,6 +139,8 @@ func (s *scanner) reset() {
s.step = stateBeginValue
s.parseState = s.parseState[0:0]
s.err = nil
+ s.redo = false
+ s.endTop = false
}
// eof tells the scanner that the end of input has been reached.
@@ -143,11 +149,11 @@ func (s *scanner) eof() int {
if s.err != nil {
return scanError
}
- if s.step == stateEndTop {
+ if s.endTop {
return scanEnd
}
s.step(s, ' ')
- if s.step == stateEndTop {
+ if s.endTop {
return scanEnd
}
if s.err == nil {
@@ -166,8 +172,10 @@ func (s *scanner) pushParseState(p int) {
func (s *scanner) popParseState() {
n := len(s.parseState) - 1
s.parseState = s.parseState[0:n]
+ s.redo = false
if n == 0 {
s.step = stateEndTop
+ s.endTop = true
} else {
s.step = stateEndValue
}
@@ -269,6 +277,7 @@ func stateEndValue(s *scanner, c int) int {
if n == 0 {
// Completed top-level before the current byte.
s.step = stateEndTop
+ s.endTop = true
return stateEndTop(s, c)
}
if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
@@ -606,16 +615,18 @@ func quoteChar(c int) string {
// undo causes the scanner to return scanCode from the next state transition.
// This gives callers a simple 1-byte undo mechanism.
func (s *scanner) undo(scanCode int) {
- if s.step == stateRedo {
- panic("invalid use of scanner")
+ if s.redo {
+ panic("json: invalid use of scanner")
}
s.redoCode = scanCode
s.redoState = s.step
s.step = stateRedo
+ s.redo = true
}
// stateRedo helps implement the scanner's 1-byte undo.
func stateRedo(s *scanner, c int) int {
+ s.redo = false
s.step = s.redoState
return s.redoCode
}
diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go
index a0a5995af8f..14d850865a6 100644
--- a/libgo/go/encoding/json/scanner_test.go
+++ b/libgo/go/encoding/json/scanner_test.go
@@ -186,11 +186,12 @@ func TestNextValueBig(t *testing.T) {
}
}
+var benchScan scanner
+
func BenchmarkSkipValue(b *testing.B) {
initBig()
- var scan scanner
for i := 0; i < b.N; i++ {
- nextValue(jsonBig, &scan)
+ nextValue(jsonBig, &benchScan)
}
b.SetBytes(int64(len(jsonBig)))
}
diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go
index 216d8889b23..d67a299f5bb 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 6c874fadb7a..25ffc917dcb 100644
--- a/libgo/go/encoding/xml/xml_test.go
+++ b/libgo/go/encoding/xml/xml_test.go
@@ -7,7 +7,6 @@ package xml
import (
"bytes"
"io"
- "os"
"reflect"
"strings"
"testing"
@@ -30,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 ")),
- StartElement{Name{"", "goodbye"}, nil},
+ 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 ")),
- StartElement{Name{"", "inner"}, nil},
+ CharData("\n "),
+ StartElement{Name{"", "inner"}, []Attr{}},
EndElement{Name{"", "inner"}},
- CharData([]byte("\n ")),
+ CharData("\n "),
EndElement{Name{"", "outer"}},
- CharData([]byte("\n ")),
- StartElement{Name{"tag", "name"}, nil},
- CharData([]byte("\n ")),
- CharData([]byte("Some text here.")),
- CharData([]byte("\n ")),
+ CharData("\n "),
+ StartElement{Name{"tag", "name"}, []Attr{}},
+ 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 ")),
- StartElement{Name{"ns2", "goodbye"}, nil},
+ 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 ")),
- StartElement{Name{"ns2", "inner"}, nil},
+ CharData("\n "),
+ StartElement{Name{"ns2", "inner"}, []Attr{}},
EndElement{Name{"ns2", "inner"}},
- CharData([]byte("\n ")),
+ CharData("\n "),
EndElement{Name{"ns2", "outer"}},
- CharData([]byte("\n ")),
- StartElement{Name{"ns3", "name"}, nil},
- CharData([]byte("\n ")),
- CharData([]byte("Some text here.")),
- CharData([]byte("\n ")),
+ CharData("\n "),
+ StartElement{Name{"ns3", "name"}, []Attr{}},
+ 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 = `
@@ -102,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")),
- StartElement{Name{"", "tag"}, nil},
- CharData([]byte("value")),
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("value"),
EndElement{Name{"", "tag"}},
}
@@ -205,7 +202,7 @@ func (d *downCaser) ReadByte() (c byte, err error) {
func (d *downCaser) Read(p []byte) (int, error) {
d.t.Fatalf("unexpected Read call on downCaser reader")
- return 0, os.EINVAL
+ panic("unreachable")
}
func TestRawTokenAltEncoding(t *testing.T) {
@@ -271,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) {
@@ -489,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 bc4a112c98f..a2a9361866d 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 a69f83a1f50..00000000000
--- 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 24e941cb36b..00000000000
--- 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 15afc657ecb..00000000000
--- 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/inotify/inotify_linux.go b/libgo/go/exp/inotify/inotify_linux.go
index d6b7e8514e6..f12436618f2 100644
--- a/libgo/go/exp/inotify/inotify_linux.go
+++ b/libgo/go/exp/inotify/inotify_linux.go
@@ -105,9 +105,9 @@ func (w *Watcher) AddWatch(path string, flags uint32) error {
watchEntry.flags |= flags
flags |= syscall.IN_MASK_ADD
}
- wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
- if wd == -1 {
- return &os.PathError{"inotify_add_watch", path, os.Errno(errno)}
+ wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
+ if err != nil {
+ return &os.PathError{"inotify_add_watch", path, err}
}
if !found {
@@ -139,14 +139,10 @@ func (w *Watcher) RemoveWatch(path string) error {
// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Event channel
func (w *Watcher) readEvents() {
- var (
- buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
- n int // Number of bytes read with read()
- errno int // Syscall errno
- )
+ var buf [syscall.SizeofInotifyEvent * 4096]byte
for {
- n, errno = syscall.Read(w.fd, buf[0:])
+ n, err := syscall.Read(w.fd, buf[0:])
// See if there is a message on the "done" channel
var done bool
select {
@@ -156,16 +152,16 @@ func (w *Watcher) readEvents() {
// If EOF or a "done" message is received
if n == 0 || done {
- errno := syscall.Close(w.fd)
- if errno == -1 {
- w.Error <- os.NewSyscallError("close", errno)
+ err := syscall.Close(w.fd)
+ if err != nil {
+ w.Error <- os.NewSyscallError("close", err)
}
close(w.Event)
close(w.Error)
return
}
if n < 0 {
- w.Error <- os.NewSyscallError("read", errno)
+ w.Error <- os.NewSyscallError("read", err)
continue
}
if n < syscall.SizeofInotifyEvent {
diff --git a/libgo/go/exp/sql/convert.go b/libgo/go/exp/sql/convert.go
index e46cebe9a3d..48e281203be 100644
--- a/libgo/go/exp/sql/convert.go
+++ b/libgo/go/exp/sql/convert.go
@@ -14,6 +14,21 @@ import (
"strconv"
)
+// subsetTypeArgs takes a slice of arguments from callers of the sql
+// package and converts them into a slice of the driver package's
+// "subset types".
+func subsetTypeArgs(args []interface{}) ([]interface{}, error) {
+ out := make([]interface{}, len(args))
+ for n, arg := range args {
+ var err error
+ out[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ if err != nil {
+ return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err)
+ }
+ }
+ return out, nil
+}
+
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go
index 6a51c342415..f0bcca29106 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
@@ -36,19 +36,22 @@ type Driver interface {
Open(name string) (Conn, error)
}
-// Execer is an optional interface that may be implemented by a Driver
-// or a Conn.
-//
-// If a Driver does not implement Execer, the sql package's DB.Exec
-// method first obtains a free connection from its free pool or from
-// the driver's Open method. Execer should only be implemented by
-// drivers that can provide a more efficient implementation.
+// ErrSkip may be returned by some optional interfaces' methods to
+// indicate at runtime that the fast path is unavailable and the sql
+// package should continue as if the optional interface was not
+// implemented. ErrSkip is only supported where explicitly
+// documented.
+var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
+
+// Execer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Execer, the db package's DB.Exec will
// first prepare a query, execute the statement, and then close the
// statement.
//
// All arguments are of a subset type as defined in the package docs.
+//
+// Exec may return ErrSkip.
type Execer interface {
Exec(query string, args []interface{}) (Result, error)
}
@@ -91,9 +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.
+ //
+ // 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
@@ -135,6 +164,8 @@ type Rows interface {
// The dest slice may be populated with only with values
// of subset types defined above, but excluding string.
// All string values must be converted to []byte.
+ //
+ // Next should return io.EOF when there are no more rows.
Next(dest []interface{}) error
}
diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go
index c8a19974d64..2474a86f644 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)
@@ -195,6 +197,29 @@ func (c *fakeConn) Close() error {
return nil
}
+func checkSubsetTypes(args []interface{}) error {
+ for n, arg := range args {
+ switch arg.(type) {
+ case int64, float64, bool, nil, []byte, string:
+ default:
+ return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
+ }
+ }
+ return nil
+}
+
+func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) {
+ // This is an optional interface, but it's implemented here
+ // just to check that all the args of of the proper types.
+ // ErrSkip is returned so the caller acts as if we didn't
+ // implement this at all.
+ err := checkSubsetTypes(args)
+ if err != nil {
+ return nil, err
+ }
+ return nil, driver.ErrSkip
+}
+
func errf(msg string, args ...interface{}) error {
return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
}
@@ -209,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)
@@ -319,10 +347,21 @@ 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
+ }
+
db := s.c.db
switch s.cmd {
case "WIPE":
@@ -377,6 +416,14 @@ 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
+ }
+
db := s.c.db
if len(args) != s.placeholders {
panic("error in pkg db; should only get here if size is correct")
diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go
index 291af7f67dc..f17d12eaa13 100644
--- a/libgo/go/exp/sql/sql.go
+++ b/libgo/go/exp/sql/sql.go
@@ -88,8 +88,9 @@ type DB struct {
driver driver.Driver
dsn string
- mu sync.Mutex
+ mu sync.Mutex // protects freeConn and closed
freeConn []driver.Conn
+ closed bool
}
// Open opens a database specified by its database driver name and a
@@ -106,6 +107,22 @@ func Open(driverName, dataSourceName string) (*DB, error) {
return &DB{driver: driver, dsn: dataSourceName}, nil
}
+// Close closes the database, releasing any open resources.
+func (db *DB) Close() error {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ var err error
+ for _, c := range db.freeConn {
+ err1 := c.Close()
+ if err1 != nil {
+ err = err1
+ }
+ }
+ db.freeConn = nil
+ db.closed = true
+ return err
+}
+
func (db *DB) maxIdleConns() int {
const defaultMaxIdleConns = 2
// TODO(bradfitz): ask driver, if supported, for its default preference
@@ -116,6 +133,9 @@ func (db *DB) maxIdleConns() int {
// conn returns a newly-opened or cached driver.Conn
func (db *DB) conn() (driver.Conn, error) {
db.mu.Lock()
+ if db.closed {
+ return nil, errors.New("sql: database is closed")
+ }
if n := len(db.freeConn); n > 0 {
conn := db.freeConn[n-1]
db.freeConn = db.freeConn[:n-1]
@@ -140,11 +160,13 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
}
func (db *DB) putConn(c driver.Conn) {
- if n := len(db.freeConn); n < db.maxIdleConns() {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() {
db.freeConn = append(db.freeConn, c)
return
}
- db.closeConn(c)
+ db.closeConn(c) // TODO(bradfitz): release lock before calling this?
}
func (db *DB) closeConn(c driver.Conn) {
@@ -180,17 +202,11 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
// Exec executes a query without returning any rows.
func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
- // Optional fast path, if the driver implements driver.Execer.
- if execer, ok := db.driver.(driver.Execer); ok {
- resi, err := execer.Exec(query, args)
- if err != nil {
- return nil, err
- }
- return result{resi}, nil
+ sargs, err := subsetTypeArgs(args)
+ if err != nil {
+ return nil, err
}
- // If the driver does not implement driver.Execer, we need
- // a connection.
ci, err := db.conn()
if err != nil {
return nil, err
@@ -198,11 +214,13 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
defer db.putConn(ci)
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, args)
- if err != nil {
- return nil, err
+ resi, err := execer.Exec(query, sargs)
+ if err != driver.ErrSkip {
+ if err != nil {
+ return nil, err
+ }
+ return result{resi}, nil
}
- return result{resi}, nil
}
sti, err := ci.Prepare(query)
@@ -210,7 +228,8 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
return nil, err
}
defer sti.Close()
- resi, err := sti.Exec(args)
+
+ resi, err := sti.Exec(sargs)
if err != nil {
return nil, err
}
@@ -325,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
@@ -364,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) {
@@ -386,7 +439,13 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
return nil, err
}
defer sti.Close()
- resi, err := sti.Exec(args)
+
+ sargs, err := subsetTypeArgs(args)
+ if err != nil {
+ return nil, err
+ }
+
+ resi, err := sti.Exec(sargs)
if err != nil {
return nil, err
}
@@ -423,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
@@ -449,7 +509,10 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
}
defer releaseConn()
- if want := si.NumInput(); len(args) != want {
+ // -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 want := si.NumInput(); want != -1 && len(args) != want {
return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args))
}
@@ -485,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()
@@ -545,10 +611,18 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
if err != nil {
return nil, err
}
- if len(args) != si.NumInput() {
+
+ // -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 want := si.NumInput(); want != -1 && len(args) != want {
return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args))
}
- rowsi, err := si.Query(args)
+ sargs, err := subsetTypeArgs(args)
+ if err != nil {
+ return nil, err
+ }
+ rowsi, err := si.Query(sargs)
if err != nil {
s.db.putConn(ci)
return nil, err
@@ -585,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 eb1bb58966e..4f8318d26ef 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
}
@@ -34,8 +34,50 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) {
}
}
+func closeDB(t *testing.T, db *DB) {
+ err := db.Close()
+ if err != nil {
+ t.Fatalf("error closing DB: %v", err)
+ }
+}
+
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
@@ -67,8 +109,27 @@ 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)
stmt, err := db.Prepare("SELECT|people|age|name=?")
if err != nil {
t.Fatalf("Prepare: %v", err)
@@ -94,6 +155,7 @@ func TestStatementQueryRow(t *testing.T) {
// just a test of fakedb itself
func TestBogusPreboundParameters(t *testing.T) {
db := newTestDB(t, "foo")
+ defer closeDB(t, db)
exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
_, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion")
if err == nil {
@@ -104,8 +166,9 @@ 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")
stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
if err != nil {
@@ -143,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 6ff8203ce27..9d75f37de74 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/cipher.go b/libgo/go/exp/ssh/cipher.go
new file mode 100644
index 00000000000..de4926d7b81
--- /dev/null
+++ b/libgo/go/exp/ssh/cipher.go
@@ -0,0 +1,88 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rc4"
+)
+
+// streamDump is used to dump the initial keystream for stream ciphers. It is a
+// a write-only buffer, and not intended for reading so do not require a mutex.
+var streamDump [512]byte
+
+// noneCipher implements cipher.Stream and provides no encryption. It is used
+// by the transport before the first key-exchange.
+type noneCipher struct{}
+
+func (c noneCipher) XORKeyStream(dst, src []byte) {
+ copy(dst, src)
+}
+
+func newAESCTR(key, iv []byte) (cipher.Stream, error) {
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ return cipher.NewCTR(c, iv), nil
+}
+
+func newRC4(key, iv []byte) (cipher.Stream, error) {
+ return rc4.NewCipher(key)
+}
+
+type cipherMode struct {
+ keySize int
+ ivSize int
+ skip int
+ createFn func(key, iv []byte) (cipher.Stream, error)
+}
+
+func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) {
+ if len(key) < c.keySize {
+ panic("ssh: key length too small for cipher")
+ }
+ if len(iv) < c.ivSize {
+ panic("ssh: iv too small for cipher")
+ }
+
+ stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize])
+ if err != nil {
+ return nil, err
+ }
+
+ for remainingToDump := c.skip; remainingToDump > 0; {
+ dumpThisTime := remainingToDump
+ if dumpThisTime > len(streamDump) {
+ dumpThisTime = len(streamDump)
+ }
+ stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
+ remainingToDump -= dumpThisTime
+ }
+
+ return stream, nil
+}
+
+// Specifies a default set of ciphers and a preference order. This is based on
+// OpenSSH's default client preference order, minus algorithms that are not
+// implemented.
+var DefaultCipherOrder = []string{
+ "aes128-ctr", "aes192-ctr", "aes256-ctr",
+ "arcfour256", "arcfour128",
+}
+
+var cipherModes = map[string]*cipherMode{
+ // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
+ // are defined in the order specified in the RFC.
+ "aes128-ctr": &cipherMode{16, aes.BlockSize, 0, newAESCTR},
+ "aes192-ctr": &cipherMode{24, aes.BlockSize, 0, newAESCTR},
+ "aes256-ctr": &cipherMode{32, aes.BlockSize, 0, newAESCTR},
+
+ // Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
+ // They are defined in the order specified in the RFC.
+ "arcfour128": &cipherMode{16, 0, 1536, newRC4},
+ "arcfour256": &cipherMode{32, 0, 1536, newRC4},
+}
diff --git a/libgo/go/exp/ssh/cipher_test.go b/libgo/go/exp/ssh/cipher_test.go
new file mode 100644
index 00000000000..ea27bd8a803
--- /dev/null
+++ b/libgo/go/exp/ssh/cipher_test.go
@@ -0,0 +1,62 @@
+// 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 (
+ "bytes"
+ "testing"
+)
+
+// TestCipherReversal tests that each cipher factory produces ciphers that can
+// encrypt and decrypt some data successfully.
+func TestCipherReversal(t *testing.T) {
+ testData := []byte("abcdefghijklmnopqrstuvwxyz012345")
+ testKey := []byte("AbCdEfGhIjKlMnOpQrStUvWxYz012345")
+ testIv := []byte("sdflkjhsadflkjhasdflkjhsadfklhsa")
+
+ cryptBuffer := make([]byte, 32)
+
+ for name, cipherMode := range cipherModes {
+ encrypter, err := cipherMode.createCipher(testKey, testIv)
+ if err != nil {
+ t.Errorf("failed to create encrypter for %q: %s", name, err)
+ continue
+ }
+ decrypter, err := cipherMode.createCipher(testKey, testIv)
+ if err != nil {
+ t.Errorf("failed to create decrypter for %q: %s", name, err)
+ continue
+ }
+
+ copy(cryptBuffer, testData)
+
+ encrypter.XORKeyStream(cryptBuffer, cryptBuffer)
+ if name == "none" {
+ if !bytes.Equal(cryptBuffer, testData) {
+ t.Errorf("encryption made change with 'none' cipher")
+ continue
+ }
+ } else {
+ if bytes.Equal(cryptBuffer, testData) {
+ t.Errorf("encryption made no change with %q", name)
+ continue
+ }
+ }
+
+ decrypter.XORKeyStream(cryptBuffer, cryptBuffer)
+ if !bytes.Equal(cryptBuffer, testData) {
+ t.Errorf("decrypted bytes not equal to input with %q", name)
+ continue
+ }
+ }
+}
+
+func TestDefaultCiphersExist(t *testing.T) {
+ for _, cipherAlgo := range DefaultCipherOrder {
+ if _, ok := cipherModes[cipherAlgo]; !ok {
+ t.Errorf("default cipher %q is unknown", cipherAlgo)
+ }
+ }
+}
diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go
index da45688eee0..429dee975bc 100644
--- a/libgo/go/exp/ssh/client.go
+++ b/libgo/go/exp/ssh/client.go
@@ -35,10 +35,6 @@ func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) {
conn.Close()
return nil, err
}
- if err := conn.authenticate(); err != nil {
- conn.Close()
- return nil, err
- }
go conn.mainLoop()
return conn, nil
}
@@ -64,8 +60,8 @@ func (c *ClientConn) handshake() error {
clientKexInit := kexInitMsg{
KexAlgos: supportedKexAlgos,
ServerHostKeyAlgos: supportedHostKeyAlgos,
- CiphersClientServer: supportedCiphers,
- CiphersServerClient: supportedCiphers,
+ CiphersClientServer: c.config.Crypto.ciphers(),
+ CiphersServerClient: c.config.Crypto.ciphers(),
MACsClientServer: supportedMACs,
MACsServerClient: supportedMACs,
CompressionClientServer: supportedCompressions,
@@ -128,7 +124,10 @@ func (c *ClientConn) handshake() error {
if packet[0] != msgNewKeys {
return UnexpectedMessageError{msgNewKeys, packet[0]}
}
- return c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc)
+ if err := c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc); err != nil {
+ return err
+ }
+ return c.authenticate(H)
}
// kexDH performs Diffie-Hellman key agreement on a ClientConn. The
@@ -173,39 +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
- 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)
}
}
}
@@ -301,6 +273,9 @@ type ClientConfig struct {
// A slice of ClientAuth methods. Only the first instance
// of a particular RFC 4252 method will be used during authentication.
Auth []ClientAuth
+
+ // Cryptographic-related configuration.
+ Crypto CryptoConfig
}
func (c *ClientConfig) rand() io.Reader {
@@ -335,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
}
@@ -392,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
}
@@ -411,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
@@ -421,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.
@@ -430,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
}
@@ -443,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))
@@ -455,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.go b/libgo/go/exp/ssh/client_auth.go
index 0089d0c769e..25f9e216225 100644
--- a/libgo/go/exp/ssh/client_auth.go
+++ b/libgo/go/exp/ssh/client_auth.go
@@ -6,10 +6,11 @@ package ssh
import (
"errors"
+ "io"
)
// authenticate authenticates with the remote server. See RFC 4252.
-func (c *ClientConn) authenticate() error {
+func (c *ClientConn) authenticate(session []byte) error {
// initiate user auth session
if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil {
return err
@@ -26,7 +27,7 @@ func (c *ClientConn) authenticate() error {
// then any untried methods suggested by the server.
tried, remain := make(map[string]bool), make(map[string]bool)
for auth := ClientAuth(new(noneAuth)); auth != nil; {
- ok, methods, err := auth.auth(c.config.User, c.transport)
+ ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand())
if err != nil {
return err
}
@@ -60,7 +61,7 @@ type ClientAuth interface {
// Returns true if authentication is successful.
// If authentication is not successful, a []string of alternative
// method names is returned.
- auth(user string, t *transport) (bool, []string, error)
+ auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error)
// method returns the RFC 4252 method name.
method() string
@@ -69,7 +70,7 @@ type ClientAuth interface {
// "none" authentication, RFC 4252 section 5.2.
type noneAuth int
-func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) {
+func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{
User: user,
Service: serviceSSH,
@@ -102,7 +103,7 @@ type passwordAuth struct {
ClientPassword
}
-func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) {
+func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
type passwordAuthMsg struct {
User string
Service string
@@ -155,3 +156,140 @@ type ClientPassword interface {
func ClientAuthPassword(impl ClientPassword) ClientAuth {
return &passwordAuth{impl}
}
+
+// ClientKeyring implements access to a client key ring.
+type ClientKeyring interface {
+ // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if
+ // no key exists at i.
+ Key(i int) (key interface{}, err error)
+
+ // Sign returns a signature of the given data using the i'th key
+ // and the supplied random source.
+ Sign(i int, rand io.Reader, data []byte) (sig []byte, err error)
+}
+
+// "publickey" authentication, RFC 4252 Section 7.
+type publickeyAuth struct {
+ ClientKeyring
+}
+
+func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) {
+ type publickeyAuthMsg struct {
+ User string
+ Service string
+ Method string
+ // HasSig indicates to the reciver packet that the auth request is signed and
+ // should be used for authentication of the request.
+ HasSig bool
+ Algoname string
+ Pubkey string
+ // Sig is defined as []byte so marshal will exclude it during the query phase
+ Sig []byte `ssh:"rest"`
+ }
+
+ // Authentication is performed in two stages. The first stage sends an
+ // enquiry to test if each key is acceptable to the remote. The second
+ // stage attempts to authenticate with the valid keys obtained in the
+ // first stage.
+
+ var index int
+ // a map of public keys to their index in the keyring
+ validKeys := make(map[int]interface{})
+ for {
+ key, err := p.Key(index)
+ if err != nil {
+ return false, nil, err
+ }
+ if key == nil {
+ // no more keys in the keyring
+ break
+ }
+ pubkey := serializePublickey(key)
+ algoname := algoName(key)
+ msg := publickeyAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: p.method(),
+ HasSig: false,
+ Algoname: algoname,
+ Pubkey: string(pubkey),
+ }
+ if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil {
+ return false, nil, err
+ }
+ packet, err := t.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+ switch packet[0] {
+ case msgUserAuthPubKeyOk:
+ msg := decode(packet).(*userAuthPubKeyOkMsg)
+ if msg.Algo != algoname || msg.PubKey != string(pubkey) {
+ continue
+ }
+ validKeys[index] = key
+ case msgUserAuthFailure:
+ default:
+ return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+ }
+ index++
+ }
+
+ // methods that may continue if this auth is not successful.
+ var methods []string
+ for i, key := range validKeys {
+ pubkey := serializePublickey(key)
+ algoname := algoName(key)
+ sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: p.method(),
+ }, []byte(algoname), pubkey))
+ if err != nil {
+ return false, nil, err
+ }
+ // manually wrap the serialized signature in a string
+ s := serializeSignature(algoname, sign)
+ sig := make([]byte, stringLength(s))
+ marshalString(sig, s)
+ msg := publickeyAuthMsg{
+ User: user,
+ Service: serviceSSH,
+ Method: p.method(),
+ HasSig: true,
+ Algoname: algoname,
+ Pubkey: string(pubkey),
+ Sig: sig,
+ }
+ p := marshal(msgUserAuthRequest, msg)
+ if err := t.writePacket(p); err != nil {
+ return false, nil, err
+ }
+ packet, err := t.readPacket()
+ if err != nil {
+ return false, nil, err
+ }
+ switch packet[0] {
+ case msgUserAuthSuccess:
+ return true, nil, nil
+ case msgUserAuthFailure:
+ msg := decode(packet).(*userAuthFailureMsg)
+ methods = msg.Methods
+ continue
+ case msgDisconnect:
+ return false, nil, io.EOF
+ default:
+ return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]}
+ }
+ }
+ return false, methods, nil
+}
+
+func (p *publickeyAuth) method() string {
+ return "publickey"
+}
+
+// ClientAuthPublickey returns a ClientAuth using public key authentication.
+func ClientAuthPublickey(impl ClientKeyring) ClientAuth {
+ return &publickeyAuth{impl}
+}
diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go
new file mode 100644
index 00000000000..4ef9213a9cd
--- /dev/null
+++ b/libgo/go/exp/ssh/client_auth_test.go
@@ -0,0 +1,248 @@
+// 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 (
+ "bytes"
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "io"
+ "io/ioutil"
+ "testing"
+)
+
+const _pem = `-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU
+70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx
+9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF
+tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z
+s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc
+qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT
++IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea
+riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH
+D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh
+atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT
+b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN
+ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M
+MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4
+KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8
+e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1
+D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+
+3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj
+orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw
+64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc
+XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc
+QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g
+/SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ
+I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk
+gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl
+NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw==
+-----END RSA PRIVATE KEY-----`
+
+// reused internally by tests
+var serverConfig = new(ServerConfig)
+
+func init() {
+ if err := serverConfig.SetRSAPrivateKey([]byte(_pem)); err != nil {
+ panic("unable to set private key: " + err.Error())
+ }
+}
+
+// keychain implements the ClientPublickey interface
+type keychain struct {
+ keys []*rsa.PrivateKey
+}
+
+func (k *keychain) Key(i int) (interface{}, error) {
+ if i < 0 || i >= len(k.keys) {
+ return nil, nil
+ }
+ return k.keys[i].PublicKey, nil
+}
+
+func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
+ hashFunc := crypto.SHA1
+ h := hashFunc.New()
+ h.Write(data)
+ digest := h.Sum(nil)
+ return rsa.SignPKCS1v15(rand, k.keys[i], hashFunc, digest)
+}
+
+func (k *keychain) loadPEM(file string) error {
+ buf, err := ioutil.ReadFile(file)
+ if err != nil {
+ return err
+ }
+ block, _ := pem.Decode(buf)
+ if block == nil {
+ return errors.New("ssh: no key found")
+ }
+ r, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+ if err != nil {
+ return err
+ }
+ k.keys = append(k.keys, r)
+ return nil
+}
+
+var pkey *rsa.PrivateKey
+
+func init() {
+ var err error
+ pkey, err = rsa.GenerateKey(rand.Reader, 512)
+ if err != nil {
+ panic("unable to generate public key")
+ }
+}
+
+func TestClientAuthPublickey(t *testing.T) {
+ k := new(keychain)
+ k.keys = append(k.keys, pkey)
+
+ serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool {
+ expected := []byte(serializePublickey(k.keys[0].PublicKey))
+ algoname := algoName(k.keys[0].PublicKey)
+ return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected)
+ }
+ serverConfig.PasswordCallback = nil
+
+ l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
+ if err != nil {
+ t.Fatalf("unable to listen: %s", err)
+ }
+ defer l.Close()
+
+ done := make(chan bool, 1)
+ go func() {
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ if err := c.Handshake(); err != nil {
+ t.Error(err)
+ }
+ done <- true
+ }()
+
+ config := &ClientConfig{
+ User: "testuser",
+ Auth: []ClientAuth{
+ ClientAuthPublickey(k),
+ },
+ }
+
+ c, err := Dial("tcp", l.Addr().String(), config)
+ if err != nil {
+ t.Fatalf("unable to dial remote side: %s", err)
+ }
+ defer c.Close()
+ <-done
+}
+
+// password implements the ClientPassword interface
+type password string
+
+func (p password) Password(user string) (string, error) {
+ return string(p), nil
+}
+
+func TestClientAuthPassword(t *testing.T) {
+ 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)
+ }
+ defer l.Close()
+
+ done := make(chan bool)
+ go func() {
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := c.Handshake(); err != nil {
+ t.Error(err)
+ }
+ defer c.Close()
+ done <- true
+ }()
+
+ 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)
+ }
+ defer c.Close()
+ <-done
+}
+
+func TestClientAuthPasswordAndPublickey(t *testing.T) {
+ pw := password("tiger")
+
+ serverConfig.PasswordCallback = func(user, pass string) bool {
+ return user == "testuser" && pass == string(pw)
+ }
+
+ k := new(keychain)
+ k.keys = append(k.keys, pkey)
+
+ serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool {
+ expected := []byte(serializePublickey(k.keys[0].PublicKey))
+ algoname := algoName(k.keys[0].PublicKey)
+ return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected)
+ }
+
+ l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
+ if err != nil {
+ t.Fatalf("unable to listen: %s", err)
+ }
+ defer l.Close()
+
+ done := make(chan bool)
+ go func() {
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := c.Handshake(); err != nil {
+ t.Error(err)
+ }
+ defer c.Close()
+ done <- true
+ }()
+
+ wrongPw := password("wrong")
+ config := &ClientConfig{
+ User: "testuser",
+ Auth: []ClientAuth{
+ ClientAuthPassword(wrongPw),
+ ClientAuthPublickey(k),
+ },
+ }
+
+ c, err := Dial("tcp", l.Addr().String(), config)
+ if err != nil {
+ t.Fatalf("unable to dial remote side: %s", err)
+ }
+ defer c.Close()
+ <-done
+}
diff --git a/libgo/go/exp/ssh/client_func_test.go b/libgo/go/exp/ssh/client_func_test.go
new file mode 100644
index 00000000000..137456095a0
--- /dev/null
+++ b/libgo/go/exp/ssh/client_func_test.go
@@ -0,0 +1,61 @@
+// 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
+
+// ClientConn functional tests.
+// These tests require a running ssh server listening on port 22
+// on the local host. Functional tests will be skipped unless
+// -ssh.user and -ssh.pass must be passed to gotest.
+
+import (
+ "flag"
+ "testing"
+)
+
+var (
+ sshuser = flag.String("ssh.user", "", "ssh username")
+ sshpass = flag.String("ssh.pass", "", "ssh password")
+ sshprivkey = flag.String("ssh.privkey", "", "ssh privkey file")
+)
+
+func TestFuncPasswordAuth(t *testing.T) {
+ if *sshuser == "" {
+ t.Log("ssh.user not defined, skipping test")
+ return
+ }
+ 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()
+}
+
+func TestFuncPublickeyAuth(t *testing.T) {
+ if *sshuser == "" {
+ t.Log("ssh.user not defined, skipping test")
+ return
+ }
+ kc := new(keychain)
+ if err := kc.loadPEM(*sshprivkey); err != nil {
+ t.Fatalf("unable to load private key: %s", err)
+ }
+ config := &ClientConfig{
+ User: *sshuser,
+ Auth: []ClientAuth{
+ ClientAuthPublickey(kc),
+ },
+ }
+ conn, err := Dial("tcp", "localhost:22", config)
+ if err != nil {
+ t.Fatalf("unable to connect: %s", err)
+ }
+ defer conn.Close()
+}
diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go
index 273820b6428..6844fb89b79 100644
--- a/libgo/go/exp/ssh/common.go
+++ b/libgo/go/exp/ssh/common.go
@@ -5,6 +5,8 @@
package ssh
import (
+ "crypto/dsa"
+ "crypto/rsa"
"math/big"
"strconv"
"sync"
@@ -14,7 +16,6 @@ import (
const (
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
hostAlgoRSA = "ssh-rsa"
- cipherAES128CTR = "aes128-ctr"
macSHA196 = "hmac-sha1-96"
compressionNone = "none"
serviceUserAuth = "ssh-userauth"
@@ -23,7 +24,6 @@ const (
var supportedKexAlgos = []string{kexAlgoDH14SHA1}
var supportedHostKeyAlgos = []string{hostAlgoRSA}
-var supportedCiphers = []string{cipherAES128CTR}
var supportedMACs = []string{macSHA196}
var supportedCompressions = []string{compressionNone}
@@ -127,3 +127,113 @@ func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *ke
ok = true
return
}
+
+// Cryptographic configuration common to both ServerConfig and ClientConfig.
+type CryptoConfig struct {
+ // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is
+ // used.
+ Ciphers []string
+}
+
+func (c *CryptoConfig) ciphers() []string {
+ if c.Ciphers == nil {
+ return DefaultCipherOrder
+ }
+ return c.Ciphers
+}
+
+// serialize a signed slice according to RFC 4254 6.6.
+func serializeSignature(algoname string, sig []byte) []byte {
+ length := stringLength([]byte(algoname))
+ length += stringLength(sig)
+
+ ret := make([]byte, length)
+ r := marshalString(ret, []byte(algoname))
+ r = marshalString(r, sig)
+
+ return ret
+}
+
+// serialize an rsa.PublicKey or dsa.PublicKey according to RFC 4253 6.6.
+func serializePublickey(key interface{}) []byte {
+ algoname := algoName(key)
+ switch key := key.(type) {
+ case rsa.PublicKey:
+ e := new(big.Int).SetInt64(int64(key.E))
+ length := stringLength([]byte(algoname))
+ length += intLength(e)
+ length += intLength(key.N)
+ ret := make([]byte, length)
+ r := marshalString(ret, []byte(algoname))
+ r = marshalInt(r, e)
+ marshalInt(r, key.N)
+ return ret
+ case dsa.PublicKey:
+ length := stringLength([]byte(algoname))
+ length += intLength(key.P)
+ length += intLength(key.Q)
+ length += intLength(key.G)
+ length += intLength(key.Y)
+ ret := make([]byte, length)
+ r := marshalString(ret, []byte(algoname))
+ r = marshalInt(r, key.P)
+ r = marshalInt(r, key.Q)
+ r = marshalInt(r, key.G)
+ marshalInt(r, key.Y)
+ return ret
+ }
+ panic("unexpected key type")
+}
+
+func algoName(key interface{}) string {
+ switch key.(type) {
+ case rsa.PublicKey:
+ return "ssh-rsa"
+ case dsa.PublicKey:
+ return "ssh-dss"
+ }
+ panic("unexpected key type")
+}
+
+// buildDataSignedForAuth returns the data that is signed in order to prove
+// posession of a private key. See RFC 4252, section 7.
+func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
+ user := []byte(req.User)
+ service := []byte(req.Service)
+ method := []byte(req.Method)
+
+ length := stringLength(sessionId)
+ length += 1
+ length += stringLength(user)
+ length += stringLength(service)
+ length += stringLength(method)
+ length += 1
+ length += stringLength(algo)
+ length += stringLength(pubKey)
+
+ ret := make([]byte, length)
+ r := marshalString(ret, sessionId)
+ r[0] = msgUserAuthRequest
+ r = r[1:]
+ r = marshalString(r, user)
+ r = marshalString(r, service)
+ r = marshalString(r, method)
+ r[0] = 1
+ r = r[1:]
+ r = marshalString(r, algo)
+ 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 00000000000..2f4448a1bd4
--- /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 248b2fec4f8..480f877191a 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/messages.go b/libgo/go/exp/ssh/messages.go
index e24b6398b56..cebb5609db3 100644
--- a/libgo/go/exp/ssh/messages.go
+++ b/libgo/go/exp/ssh/messages.go
@@ -392,7 +392,10 @@ func parseString(in []byte) (out, rest []byte, ok bool) {
return
}
-var comma = []byte{','}
+var (
+ comma = []byte{','}
+ emptyNameList = []string{}
+)
func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
contents, rest, ok := parseString(in)
@@ -400,6 +403,7 @@ func parseNameList(in []byte) (out []string, rest []byte, ok bool) {
return
}
if len(contents) == 0 {
+ out = emptyNameList
return
}
parts := bytes.Split(contents, comma)
@@ -444,8 +448,6 @@ func parseUint32(in []byte) (out uint32, rest []byte, ok bool) {
return
}
-const maxPacketSize = 36000
-
func nameListLength(namelist []string) int {
length := 4 /* uint32 length prefix */
for i, name := range namelist {
diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go
index 62035d52b7d..1eee9a4a977 100644
--- a/libgo/go/exp/ssh/server.go
+++ b/libgo/go/exp/ssh/server.go
@@ -40,6 +40,9 @@ type ServerConfig struct {
// key authentication. It must return true iff the given public key is
// valid for the given user.
PubKeyCallback func(user, algo string, pubkey []byte) bool
+
+ // Cryptographic-related configuration.
+ Crypto CryptoConfig
}
func (c *ServerConfig) rand() io.Reader {
@@ -204,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 {
@@ -221,7 +224,7 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
return nil, nil, errors.New("internal error")
}
- serializedSig := serializeRSASignature(sig)
+ serializedSig := serializeSignature(hostAlgoRSA, sig)
kexDHReply := kexDHReplyMsg{
HostKey: serializedHostKey,
@@ -234,50 +237,9 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
return
}
-func serializeRSASignature(sig []byte) []byte {
- length := stringLength([]byte(hostAlgoRSA))
- length += stringLength(sig)
-
- ret := make([]byte, length)
- r := marshalString(ret, []byte(hostAlgoRSA))
- r = marshalString(r, sig)
-
- return ret
-}
-
// serverVersion is the fixed identification string that Server will use.
var serverVersion = []byte("SSH-2.0-Go\r\n")
-// buildDataSignedForAuth returns the data that is signed in order to prove
-// posession of a private key. See RFC 4252, section 7.
-func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
- user := []byte(req.User)
- service := []byte(req.Service)
- method := []byte(req.Method)
-
- length := stringLength(sessionId)
- length += 1
- length += stringLength(user)
- length += stringLength(service)
- length += stringLength(method)
- length += 1
- length += stringLength(algo)
- length += stringLength(pubKey)
-
- ret := make([]byte, length)
- r := marshalString(ret, sessionId)
- r[0] = msgUserAuthRequest
- r = r[1:]
- r = marshalString(r, user)
- r = marshalString(r, service)
- r = marshalString(r, method)
- r[0] = 1
- r = r[1:]
- r = marshalString(r, algo)
- r = marshalString(r, pubKey)
- return ret
-}
-
// Handshake performs an SSH transport and client authentication on the given ServerConn.
func (s *ServerConn) Handshake() error {
var magics handshakeMagics
@@ -298,8 +260,8 @@ func (s *ServerConn) Handshake() error {
serverKexInit := kexInitMsg{
KexAlgos: supportedKexAlgos,
ServerHostKeyAlgos: supportedHostKeyAlgos,
- CiphersClientServer: supportedCiphers,
- CiphersServerClient: supportedCiphers,
+ CiphersClientServer: s.config.Crypto.ciphers(),
+ CiphersServerClient: s.config.Crypto.ciphers(),
MACsClientServer: supportedMACs,
MACsServerClient: supportedMACs,
CompressionClientServer: supportedCompressions,
@@ -364,7 +326,9 @@ func (s *ServerConn) Handshake() error {
if packet[0] != msgNewKeys {
return UnexpectedMessageError{msgNewKeys, packet[0]}
}
- s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc)
+ if err = s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc); err != nil {
+ return err
+ }
if packet, err = s.readPacket(); err != nil {
return err
}
@@ -514,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 77154f2c3c3..5f98a8d58c6 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 00000000000..4be7746d17e
--- /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
new file mode 100644
index 00000000000..f3bbac5d19e
--- /dev/null
+++ b/libgo/go/exp/ssh/tcpip.go
@@ -0,0 +1,146 @@
+// 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 (
+ "errors"
+ "io"
+ "net"
+)
+// Dial initiates a connection to the addr from the remote host.
+// addr is resolved using net.ResolveTCPAddr before connection.
+// This could allow an observer to observe the DNS name of the
+// remote host. Consider using ssh.DialTCP to avoid this.
+func (c *ClientConn) Dial(n, addr string) (net.Conn, error) {
+ raddr, err := net.ResolveTCPAddr(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ return c.DialTCP(n, nil, raddr)
+}
+
+// DialTCP connects to the remote address raddr on the network net,
+// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
+// as the local address for the connection.
+func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
+ if laddr == nil {
+ laddr = &net.TCPAddr{
+ IP: net.IPv4zero,
+ Port: 0,
+ }
+ }
+ ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
+ if err != nil {
+ return nil, err
+ }
+ return &tcpchanconn{
+ tcpchan: ch,
+ laddr: laddr,
+ raddr: raddr,
+ }, nil
+}
+
+// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as
+// strings and are expected to be resolveable at the remote end.
+func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpchan, error) {
+ // RFC 4254 7.2
+ type channelOpenDirectMsg struct {
+ ChanType string
+ PeersId uint32
+ PeersWindow uint32
+ MaxPacketSize uint32
+ raddr string
+ rport uint32
+ laddr string
+ lport uint32
+ }
+ ch := c.newChan(c.transport)
+ if err := c.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{
+ ChanType: "direct-tcpip",
+ PeersId: ch.id,
+ PeersWindow: 1 << 14,
+ MaxPacketSize: 1 << 15, // RFC 4253 6.1
+ raddr: raddr,
+ rport: uint32(rport),
+ laddr: laddr,
+ lport: uint32(lport),
+ })); 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("ssh: error opening remote TCP connection: " + msg.Message)
+ default:
+ c.chanlist.remove(ch.id)
+ return nil, errors.New("ssh: unexpected packet")
+ }
+ return &tcpchan{
+ clientChan: ch,
+ Reader: &chanReader{
+ packetWriter: ch,
+ peersId: ch.peersId,
+ data: ch.data,
+ },
+ Writer: &chanWriter{
+ packetWriter: ch,
+ peersId: ch.peersId,
+ win: ch.win,
+ },
+ }, nil
+}
+
+type tcpchan struct {
+ *clientChan // the backing channel
+ io.Reader
+ io.Writer
+}
+
+// tcpchanconn fulfills the net.Conn interface without
+// the tcpchan having to hold laddr or raddr directly.
+type tcpchanconn struct {
+ *tcpchan
+ laddr, raddr net.Addr
+}
+
+// LocalAddr returns the local network address.
+func (t *tcpchanconn) LocalAddr() net.Addr {
+ return t.laddr
+}
+
+// RemoteAddr returns the remote network address.
+func (t *tcpchanconn) RemoteAddr() net.Addr {
+ return t.raddr
+}
+
+// SetTimeout sets the read and write deadlines associated
+// with the connection.
+func (t *tcpchanconn) SetTimeout(nsec int64) error {
+ if err := t.SetReadTimeout(nsec); err != nil {
+ return err
+ }
+ return t.SetWriteTimeout(nsec)
+}
+
+// SetReadTimeout sets the time (in nanoseconds) that
+// Read will wait for data before returning an error with Timeout() == true.
+// Setting nsec == 0 (the default) disables the deadline.
+func (t *tcpchanconn) SetReadTimeout(nsec int64) error {
+ return errors.New("ssh: tcpchan: timeout not supported")
+}
+
+// SetWriteTimeout sets the time (in nanoseconds) that
+// Write will wait to send its data before returning an error with Timeout() == true.
+// Setting nsec == 0 (the default) disables the deadline.
+// Even if write times out, it may return n > 0, indicating that
+// some of the data was successfully written.
+func (t *tcpchanconn) SetWriteTimeout(nsec int64) error {
+ return errors.New("ssh: tcpchan: timeout not supported")
+}
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 00000000000..261297241e9
--- /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 579a9d82de9..bcd073e7ce6 100644
--- a/libgo/go/exp/ssh/transport.go
+++ b/libgo/go/exp/ssh/transport.go
@@ -7,7 +7,6 @@ package ssh
import (
"bufio"
"crypto"
- "crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/subtle"
@@ -19,7 +18,10 @@ import (
)
const (
- paddingMultiple = 16 // TODO(dfc) does this need to be configurable?
+ packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher.
+ minPacketSize = 16
+ maxPacketSize = 36000
+ minPaddingSize = 4 // TODO(huin) should this be configurable?
)
// filteredConn reduces the set of methods exposed when embeddeding
@@ -61,8 +63,7 @@ type reader struct {
type writer struct {
*sync.Mutex // protects writer.Writer from concurrent writes
*bufio.Writer
- paddingMultiple int
- rand io.Reader
+ rand io.Reader
common
}
@@ -82,14 +83,11 @@ type common struct {
func (r *reader) readOnePacket() ([]byte, error) {
var lengthBytes = make([]byte, 5)
var macSize uint32
-
if _, err := io.ReadFull(r, lengthBytes); err != nil {
return nil, err
}
- if r.cipher != nil {
- r.cipher.XORKeyStream(lengthBytes, lengthBytes)
- }
+ r.cipher.XORKeyStream(lengthBytes, lengthBytes)
if r.mac != nil {
r.mac.Reset()
@@ -125,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")
}
}
@@ -153,9 +151,9 @@ func (w *writer) writePacket(packet []byte) error {
w.Mutex.Lock()
defer w.Mutex.Unlock()
- paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple
+ paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple
if paddingLength < 4 {
- paddingLength += paddingMultiple
+ paddingLength += packetSizeMultiple
}
length := len(packet) + 1 + paddingLength
@@ -188,11 +186,9 @@ func (w *writer) writePacket(packet []byte) error {
// TODO(dfc) lengthBytes, packet and padding should be
// subslices of a single buffer
- if w.cipher != nil {
- w.cipher.XORKeyStream(lengthBytes, lengthBytes)
- w.cipher.XORKeyStream(packet, packet)
- w.cipher.XORKeyStream(padding, padding)
- }
+ w.cipher.XORKeyStream(lengthBytes, lengthBytes)
+ w.cipher.XORKeyStream(packet, packet)
+ w.cipher.XORKeyStream(padding, padding)
if _, err := w.Write(lengthBytes); err != nil {
return err
@@ -205,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
}
}
@@ -227,11 +223,17 @@ func newTransport(conn net.Conn, rand io.Reader) *transport {
return &transport{
reader: reader{
Reader: bufio.NewReader(conn),
+ common: common{
+ cipher: noneCipher{},
+ },
},
writer: writer{
Writer: bufio.NewWriter(conn),
rand: rand,
Mutex: new(sync.Mutex),
+ common: common{
+ cipher: noneCipher{},
+ },
},
filteredConn: conn,
}
@@ -249,29 +251,32 @@ var (
clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
)
-// setupKeys sets the cipher and MAC keys from K, H and sessionId, as
+// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
// described in RFC 4253, section 6.4. direction should either be serverKeys
// (to setup server->client keys) or clientKeys (for client->server keys).
func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error {
- h := hashFunc.New()
+ cipherMode := cipherModes[c.cipherAlgo]
- blockSize := 16
- keySize := 16
macKeySize := 20
- iv := make([]byte, blockSize)
- key := make([]byte, keySize)
+ iv := make([]byte, cipherMode.ivSize)
+ key := make([]byte, cipherMode.keySize)
macKey := make([]byte, macKeySize)
+
+ h := hashFunc.New()
generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)}
- aes, err := aes.NewCipher(key)
+
+ cipher, err := cipherMode.createCipher(key, iv)
if err != nil {
return err
}
- c.cipher = cipher.NewCTR(aes, iv)
+
+ c.cipher = cipher
+
return nil
}
@@ -292,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 {
@@ -312,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/terminal/shell.go b/libgo/go/exp/terminal/shell.go
deleted file mode 100644
index 5c5916755d6..00000000000
--- a/libgo/go/exp/terminal/shell.go
+++ /dev/null
@@ -1,356 +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 terminal
-
-import "io"
-
-// Shell contains the state for running a VT100 terminal that is capable of
-// reading lines of input.
-type Shell struct {
- c io.ReadWriter
- prompt string
-
- // line is the current line being entered.
- line []byte
- // pos is the logical position of the cursor in line
- pos int
-
- // cursorX contains the current X value of the cursor where the left
- // edge is 0. cursorY contains the row number where the first row of
- // the current line is 0.
- cursorX, cursorY int
- // maxLine is the greatest value of cursorY so far.
- maxLine int
-
- termWidth, termHeight int
-
- // outBuf contains the terminal data to be sent.
- outBuf []byte
- // remainder contains the remainder of any partial key sequences after
- // a read. It aliases into inBuf.
- remainder []byte
- inBuf [256]byte
-}
-
-// NewShell runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
-// a local terminal, that terminal must first have been put into raw mode.
-// prompt is a string that is written at the start of each input line (i.e.
-// "> ").
-func NewShell(c io.ReadWriter, prompt string) *Shell {
- return &Shell{
- c: c,
- prompt: prompt,
- termWidth: 80,
- termHeight: 24,
- }
-}
-
-const (
- keyCtrlD = 4
- keyEnter = '\r'
- keyEscape = 27
- keyBackspace = 127
- keyUnknown = 256 + iota
- keyUp
- keyDown
- keyLeft
- keyRight
- keyAltLeft
- keyAltRight
-)
-
-// bytesToKey tries to parse a key sequence from b. If successful, it returns
-// the key and the remainder of the input. Otherwise it returns -1.
-func bytesToKey(b []byte) (int, []byte) {
- if len(b) == 0 {
- return -1, nil
- }
-
- if b[0] != keyEscape {
- return int(b[0]), b[1:]
- }
-
- if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
- switch b[2] {
- case 'A':
- return keyUp, b[3:]
- case 'B':
- return keyDown, b[3:]
- case 'C':
- return keyRight, b[3:]
- case 'D':
- return keyLeft, b[3:]
- }
- }
-
- if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
- switch b[5] {
- case 'C':
- return keyAltRight, b[6:]
- case 'D':
- return keyAltLeft, b[6:]
- }
- }
-
- // If we get here then we have a key that we don't recognise, or a
- // partial sequence. It's not clear how one should find the end of a
- // sequence without knowing them all, but it seems that [a-zA-Z] only
- // appears at the end of a sequence.
- for i, c := range b[0:] {
- if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
- return keyUnknown, b[i+1:]
- }
- }
-
- return -1, b
-}
-
-// queue appends data to the end of ss.outBuf
-func (ss *Shell) queue(data []byte) {
- if len(ss.outBuf)+len(data) > cap(ss.outBuf) {
- newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data)))
- copy(newOutBuf, ss.outBuf)
- ss.outBuf = newOutBuf
- }
-
- oldLen := len(ss.outBuf)
- ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)]
- copy(ss.outBuf[oldLen:], data)
-}
-
-var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
-
-func isPrintable(key int) bool {
- return key >= 32 && key < 127
-}
-
-// moveCursorToPos appends data to ss.outBuf which will move the cursor to the
-// given, logical position in the text.
-func (ss *Shell) moveCursorToPos(pos int) {
- x := len(ss.prompt) + pos
- y := x / ss.termWidth
- x = x % ss.termWidth
-
- up := 0
- if y < ss.cursorY {
- up = ss.cursorY - y
- }
-
- down := 0
- if y > ss.cursorY {
- down = y - ss.cursorY
- }
-
- left := 0
- if x < ss.cursorX {
- left = ss.cursorX - x
- }
-
- right := 0
- if x > ss.cursorX {
- right = x - ss.cursorX
- }
-
- movement := make([]byte, 3*(up+down+left+right))
- m := movement
- for i := 0; i < up; i++ {
- m[0] = keyEscape
- m[1] = '['
- m[2] = 'A'
- m = m[3:]
- }
- for i := 0; i < down; i++ {
- m[0] = keyEscape
- m[1] = '['
- m[2] = 'B'
- m = m[3:]
- }
- for i := 0; i < left; i++ {
- m[0] = keyEscape
- m[1] = '['
- m[2] = 'D'
- m = m[3:]
- }
- for i := 0; i < right; i++ {
- m[0] = keyEscape
- m[1] = '['
- m[2] = 'C'
- m = m[3:]
- }
-
- ss.cursorX = x
- ss.cursorY = y
- ss.queue(movement)
-}
-
-const maxLineLength = 4096
-
-// handleKey processes the given key and, optionally, returns a line of text
-// that the user has entered.
-func (ss *Shell) handleKey(key int) (line string, ok bool) {
- switch key {
- case keyBackspace:
- if ss.pos == 0 {
- return
- }
- ss.pos--
-
- copy(ss.line[ss.pos:], ss.line[1+ss.pos:])
- ss.line = ss.line[:len(ss.line)-1]
- ss.writeLine(ss.line[ss.pos:])
- ss.moveCursorToPos(ss.pos)
- ss.queue(eraseUnderCursor)
- case keyAltLeft:
- // move left by a word.
- if ss.pos == 0 {
- return
- }
- ss.pos--
- for ss.pos > 0 {
- if ss.line[ss.pos] != ' ' {
- break
- }
- ss.pos--
- }
- for ss.pos > 0 {
- if ss.line[ss.pos] == ' ' {
- ss.pos++
- break
- }
- ss.pos--
- }
- ss.moveCursorToPos(ss.pos)
- case keyAltRight:
- // move right by a word.
- for ss.pos < len(ss.line) {
- if ss.line[ss.pos] == ' ' {
- break
- }
- ss.pos++
- }
- for ss.pos < len(ss.line) {
- if ss.line[ss.pos] != ' ' {
- break
- }
- ss.pos++
- }
- ss.moveCursorToPos(ss.pos)
- case keyLeft:
- if ss.pos == 0 {
- return
- }
- ss.pos--
- ss.moveCursorToPos(ss.pos)
- case keyRight:
- if ss.pos == len(ss.line) {
- return
- }
- ss.pos++
- ss.moveCursorToPos(ss.pos)
- case keyEnter:
- ss.moveCursorToPos(len(ss.line))
- ss.queue([]byte("\r\n"))
- line = string(ss.line)
- ok = true
- ss.line = ss.line[:0]
- ss.pos = 0
- ss.cursorX = 0
- ss.cursorY = 0
- ss.maxLine = 0
- default:
- if !isPrintable(key) {
- return
- }
- if len(ss.line) == maxLineLength {
- return
- }
- if len(ss.line) == cap(ss.line) {
- newLine := make([]byte, len(ss.line), 2*(1+len(ss.line)))
- copy(newLine, ss.line)
- ss.line = newLine
- }
- ss.line = ss.line[:len(ss.line)+1]
- copy(ss.line[ss.pos+1:], ss.line[ss.pos:])
- ss.line[ss.pos] = byte(key)
- ss.writeLine(ss.line[ss.pos:])
- ss.pos++
- ss.moveCursorToPos(ss.pos)
- }
- return
-}
-
-func (ss *Shell) writeLine(line []byte) {
- for len(line) != 0 {
- if ss.cursorX == ss.termWidth {
- ss.queue([]byte("\r\n"))
- ss.cursorX = 0
- ss.cursorY++
- if ss.cursorY > ss.maxLine {
- ss.maxLine = ss.cursorY
- }
- }
-
- remainingOnLine := ss.termWidth - ss.cursorX
- todo := len(line)
- if todo > remainingOnLine {
- todo = remainingOnLine
- }
- ss.queue(line[:todo])
- ss.cursorX += todo
- line = line[todo:]
- }
-}
-
-func (ss *Shell) Write(buf []byte) (n int, err error) {
- return ss.c.Write(buf)
-}
-
-// ReadLine returns a line of input from the terminal.
-func (ss *Shell) ReadLine() (line string, err error) {
- ss.writeLine([]byte(ss.prompt))
- ss.c.Write(ss.outBuf)
- ss.outBuf = ss.outBuf[:0]
-
- for {
- // ss.remainder is a slice at the beginning of ss.inBuf
- // containing a partial key sequence
- readBuf := ss.inBuf[len(ss.remainder):]
- var n int
- n, err = ss.c.Read(readBuf)
- if err != nil {
- return
- }
-
- if err == nil {
- ss.remainder = ss.inBuf[:n+len(ss.remainder)]
- rest := ss.remainder
- lineOk := false
- for !lineOk {
- var key int
- key, rest = bytesToKey(rest)
- if key < 0 {
- break
- }
- if key == keyCtrlD {
- return "", io.EOF
- }
- line, lineOk = ss.handleKey(key)
- }
- if len(rest) > 0 {
- n := copy(ss.inBuf[:], rest)
- ss.remainder = ss.inBuf[:n]
- } else {
- ss.remainder = nil
- }
- ss.c.Write(ss.outBuf)
- ss.outBuf = ss.outBuf[:0]
- if lineOk {
- return
- }
- continue
- }
- }
- panic("unreachable")
-}
diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go
index 5732543ffc2..18d76cd6b90 100644
--- a/libgo/go/exp/terminal/terminal.go
+++ b/libgo/go/exp/terminal/terminal.go
@@ -2,102 +2,361 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package terminal provides support functions for dealing with terminals, as
-// commonly found on UNIX systems.
-//
-// Putting a terminal into raw mode is the most common requirement:
-//
-// oldState, err := terminal.MakeRaw(0)
-// if err != nil {
-// panic(err.String())
-// }
-// defer terminal.Restore(0, oldState)
package terminal
-import (
- "io"
- "os"
- "syscall"
-)
+import "io"
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal struct {
+ c io.ReadWriter
+ prompt string
+
+ // line is the current line being entered.
+ line []byte
+ // pos is the logical position of the cursor in line
+ pos int
+
+ // cursorX contains the current X value of the cursor where the left
+ // edge is 0. cursorY contains the row number where the first row of
+ // the current line is 0.
+ cursorX, cursorY int
+ // maxLine is the greatest value of cursorY so far.
+ maxLine int
+
+ termWidth, termHeight int
-// State contains the state of a terminal.
-type State struct {
- termios syscall.Termios
+ // outBuf contains the terminal data to be sent.
+ outBuf []byte
+ // remainder contains the remainder of any partial key sequences after
+ // a read. It aliases into inBuf.
+ remainder []byte
+ inBuf [256]byte
}
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(fd int) bool {
- var termios syscall.Termios
- e := syscall.Tcgetattr(fd, &termios)
- return e == 0
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+ return &Terminal{
+ c: c,
+ prompt: prompt,
+ termWidth: 80,
+ termHeight: 24,
+ }
}
-// MakeRaw put the terminal connected to the given file descriptor into raw
-// mode and returns the previous state of the terminal so that it can be
-// restored.
-func MakeRaw(fd int) (*State, error) {
- var oldState State
- if e := syscall.Tcgetattr(fd, &oldState.termios); e != 0 {
- return nil, os.Errno(e)
+const (
+ keyCtrlD = 4
+ keyEnter = '\r'
+ keyEscape = 27
+ keyBackspace = 127
+ keyUnknown = 256 + iota
+ keyUp
+ keyDown
+ keyLeft
+ keyRight
+ keyAltLeft
+ keyAltRight
+)
+
+// bytesToKey tries to parse a key sequence from b. If successful, it returns
+// the key and the remainder of the input. Otherwise it returns -1.
+func bytesToKey(b []byte) (int, []byte) {
+ if len(b) == 0 {
+ return -1, nil
+ }
+
+ if b[0] != keyEscape {
+ return int(b[0]), b[1:]
+ }
+
+ if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
+ switch b[2] {
+ case 'A':
+ return keyUp, b[3:]
+ case 'B':
+ return keyDown, b[3:]
+ case 'C':
+ return keyRight, b[3:]
+ case 'D':
+ return keyLeft, b[3:]
+ }
+ }
+
+ if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
+ switch b[5] {
+ case 'C':
+ return keyAltRight, b[6:]
+ case 'D':
+ return keyAltLeft, b[6:]
+ }
+ }
+
+ // If we get here then we have a key that we don't recognise, or a
+ // partial sequence. It's not clear how one should find the end of a
+ // sequence without knowing them all, but it seems that [a-zA-Z] only
+ // appears at the end of a sequence.
+ for i, c := range b[0:] {
+ if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
+ return keyUnknown, b[i+1:]
+ }
}
- newState := oldState.termios
- newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
- newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
- if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 {
- return nil, os.Errno(e)
+ return -1, b
+}
+
+// queue appends data to the end of t.outBuf
+func (t *Terminal) queue(data []byte) {
+ if len(t.outBuf)+len(data) > cap(t.outBuf) {
+ newOutBuf := make([]byte, len(t.outBuf), 2*(len(t.outBuf)+len(data)))
+ copy(newOutBuf, t.outBuf)
+ t.outBuf = newOutBuf
}
- return &oldState, nil
+ oldLen := len(t.outBuf)
+ t.outBuf = t.outBuf[:len(t.outBuf)+len(data)]
+ copy(t.outBuf[oldLen:], data)
}
-// Restore restores the terminal connected to the given file descriptor to a
-// previous state.
-func Restore(fd int, state *State) error {
- e := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios)
- return os.Errno(e)
+var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
+
+func isPrintable(key int) bool {
+ return key >= 32 && key < 127
}
-// ReadPassword reads a line of input from a terminal without local echo. This
-// is commonly used for inputting passwords and other sensitive data. The slice
-// returned does not include the \n.
-func ReadPassword(fd int) ([]byte, error) {
- var oldState syscall.Termios
- if e := syscall.Tcgetattr(fd, &oldState); e != 0 {
- return nil, os.Errno(e)
+// moveCursorToPos appends data to t.outBuf which will move the cursor to the
+// given, logical position in the text.
+func (t *Terminal) moveCursorToPos(pos int) {
+ x := len(t.prompt) + pos
+ y := x / t.termWidth
+ x = x % t.termWidth
+
+ up := 0
+ if y < t.cursorY {
+ up = t.cursorY - y
}
- newState := oldState
- newState.Lflag &^= syscall.ECHO
- if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 {
- return nil, os.Errno(e)
+ down := 0
+ if y > t.cursorY {
+ down = y - t.cursorY
}
- defer func() {
- syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState)
- }()
+ left := 0
+ if x < t.cursorX {
+ left = t.cursorX - x
+ }
- var buf [16]byte
- var ret []byte
- for {
- n, errno := syscall.Read(fd, buf[:])
- if errno != 0 {
- return nil, os.Errno(errno)
+ right := 0
+ if x > t.cursorX {
+ right = x - t.cursorX
+ }
+
+ movement := make([]byte, 3*(up+down+left+right))
+ m := movement
+ for i := 0; i < up; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'A'
+ m = m[3:]
+ }
+ for i := 0; i < down; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'B'
+ m = m[3:]
+ }
+ for i := 0; i < left; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'D'
+ m = m[3:]
+ }
+ for i := 0; i < right; i++ {
+ m[0] = keyEscape
+ m[1] = '['
+ m[2] = 'C'
+ m = m[3:]
+ }
+
+ t.cursorX = x
+ t.cursorY = y
+ t.queue(movement)
+}
+
+const maxLineLength = 4096
+
+// handleKey processes the given key and, optionally, returns a line of text
+// that the user has entered.
+func (t *Terminal) handleKey(key int) (line string, ok bool) {
+ switch key {
+ case keyBackspace:
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+
+ copy(t.line[t.pos:], t.line[1+t.pos:])
+ t.line = t.line[:len(t.line)-1]
+ t.writeLine(t.line[t.pos:])
+ t.moveCursorToPos(t.pos)
+ t.queue(eraseUnderCursor)
+ case keyAltLeft:
+ // move left by a word.
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+ for t.pos > 0 {
+ if t.line[t.pos] != ' ' {
+ break
+ }
+ t.pos--
+ }
+ for t.pos > 0 {
+ if t.line[t.pos] == ' ' {
+ t.pos++
+ break
+ }
+ t.pos--
+ }
+ t.moveCursorToPos(t.pos)
+ case keyAltRight:
+ // move right by a word.
+ for t.pos < len(t.line) {
+ if t.line[t.pos] == ' ' {
+ break
+ }
+ t.pos++
}
- if n == 0 {
- if len(ret) == 0 {
- return nil, io.EOF
+ for t.pos < len(t.line) {
+ if t.line[t.pos] != ' ' {
+ break
+ }
+ t.pos++
+ }
+ t.moveCursorToPos(t.pos)
+ case keyLeft:
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+ t.moveCursorToPos(t.pos)
+ case keyRight:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+ case keyEnter:
+ t.moveCursorToPos(len(t.line))
+ t.queue([]byte("\r\n"))
+ line = string(t.line)
+ ok = true
+ t.line = t.line[:0]
+ t.pos = 0
+ t.cursorX = 0
+ t.cursorY = 0
+ t.maxLine = 0
+ default:
+ if !isPrintable(key) {
+ return
+ }
+ if len(t.line) == maxLineLength {
+ return
+ }
+ if len(t.line) == cap(t.line) {
+ newLine := make([]byte, len(t.line), 2*(1+len(t.line)))
+ copy(newLine, t.line)
+ t.line = newLine
+ }
+ t.line = t.line[:len(t.line)+1]
+ copy(t.line[t.pos+1:], t.line[t.pos:])
+ t.line[t.pos] = byte(key)
+ t.writeLine(t.line[t.pos:])
+ t.pos++
+ t.moveCursorToPos(t.pos)
+ }
+ return
+}
+
+func (t *Terminal) writeLine(line []byte) {
+ for len(line) != 0 {
+ if t.cursorX == t.termWidth {
+ t.queue([]byte("\r\n"))
+ t.cursorX = 0
+ t.cursorY++
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
}
- break
}
- if buf[n-1] == '\n' {
- n--
+
+ remainingOnLine := t.termWidth - t.cursorX
+ todo := len(line)
+ if todo > remainingOnLine {
+ todo = remainingOnLine
}
- ret = append(ret, buf[:n]...)
- if n < len(buf) {
- break
+ t.queue(line[:todo])
+ t.cursorX += todo
+ line = line[todo:]
+ }
+}
+
+func (t *Terminal) Write(buf []byte) (n int, err error) {
+ return t.c.Write(buf)
+}
+
+// ReadLine returns a line of input from the terminal.
+func (t *Terminal) ReadLine() (line string, err error) {
+ if t.cursorX == 0 {
+ t.writeLine([]byte(t.prompt))
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ }
+
+ for {
+ // t.remainder is a slice at the beginning of t.inBuf
+ // containing a partial key sequence
+ readBuf := t.inBuf[len(t.remainder):]
+ var n int
+ n, err = t.c.Read(readBuf)
+ if err != nil {
+ return
+ }
+
+ if err == nil {
+ t.remainder = t.inBuf[:n+len(t.remainder)]
+ rest := t.remainder
+ lineOk := false
+ for !lineOk {
+ var key int
+ key, rest = bytesToKey(rest)
+ if key < 0 {
+ break
+ }
+ if key == keyCtrlD {
+ return "", io.EOF
+ }
+ line, lineOk = t.handleKey(key)
+ }
+ if len(rest) > 0 {
+ n := copy(t.inBuf[:], rest)
+ t.remainder = t.inBuf[:n]
+ } else {
+ t.remainder = nil
+ }
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ if lineOk {
+ return
+ }
+ continue
}
}
+ panic("unreachable")
+}
- return ret, nil
+func (t *Terminal) SetSize(width, height int) {
+ t.termWidth, t.termHeight = width, height
}
diff --git a/libgo/go/exp/terminal/shell_test.go b/libgo/go/exp/terminal/terminal_test.go
index 8a76a85d5dc..a2197210e2a 100644
--- a/libgo/go/exp/terminal/shell_test.go
+++ b/libgo/go/exp/terminal/terminal_test.go
@@ -41,7 +41,7 @@ func (c *MockTerminal) Write(data []byte) (n int, err error) {
func TestClose(t *testing.T) {
c := &MockTerminal{}
- ss := NewShell(c, "> ")
+ ss := NewTerminal(c, "> ")
line, err := ss.ReadLine()
if line != "" {
t.Errorf("Expected empty line but got: %s", line)
@@ -95,7 +95,7 @@ func TestKeyPresses(t *testing.T) {
toSend: []byte(test.in),
bytesPerRead: j,
}
- ss := NewShell(c, "> ")
+ ss := NewTerminal(c, "> ")
line, err := ss.ReadLine()
if line != test.line {
t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
diff --git a/libgo/go/exp/terminal/util.go b/libgo/go/exp/terminal/util.go
new file mode 100644
index 00000000000..03035673869
--- /dev/null
+++ b/libgo/go/exp/terminal/util.go
@@ -0,0 +1,102 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err.String())
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "io"
+ "syscall"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios syscall.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var termios syscall.Termios
+ err := syscall.Tcgetattr(fd, &termios)
+ return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var oldState State
+ if err := syscall.Tcgetattr(fd, &oldState.termios); err != nil {
+ return nil, err
+ }
+
+ newState := oldState.termios
+ newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
+ newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
+ if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ err := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios)
+ return err
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ var oldState syscall.Termios
+ if err := syscall.Tcgetattr(fd, &oldState); err != nil {
+ return nil, err
+ }
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState)
+ }()
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(fd, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go
index 4a30acf2315..35535ea406f 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 4167caf3f0e..16a8667ff66 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 3f66d226153..7475d352209 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 db83f85f957..00aac798cb0 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)"},
@@ -357,6 +365,14 @@ var fmttests = []struct {
{"%#v", map[string]B{"a": {1, 2}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}}`},
{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`},
{"%#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{}`},
// slices with other formats
{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`},
diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go
index bfa88d18704..e5ca1172405 100644
--- a/libgo/go/fmt/print.go
+++ b/libgo/go/fmt/print.go
@@ -795,6 +795,10 @@ BigSwitch:
case reflect.Map:
if goSyntax {
p.buf.WriteString(f.Type().String())
+ if f.IsNil() {
+ p.buf.WriteString("(nil)")
+ break
+ }
p.buf.WriteByte('{')
} else {
p.buf.Write(mapBytes)
@@ -873,6 +877,10 @@ BigSwitch:
}
if goSyntax {
p.buf.WriteString(value.Type().String())
+ if f.Kind() == reflect.Slice && f.IsNil() {
+ p.buf.WriteString("(nil)")
+ break
+ }
p.buf.WriteByte('{')
} else {
p.buf.WriteByte('[')
diff --git a/libgo/go/fmt/scan_test.go b/libgo/go/fmt/scan_test.go
index d3c39be6071..0689bf3b6e4 100644
--- a/libgo/go/fmt/scan_test.go
+++ b/libgo/go/fmt/scan_test.go
@@ -324,7 +324,7 @@ var x, y Xs
var z IntString
var multiTests = []ScanfMultiTest{
- {"", "", nil, nil, ""},
+ {"", "", []interface{}{}, []interface{}{}, ""},
{"%d", "23", args(&i), args(23), ""},
{"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""},
{"%2d%3d", "44555", args(&i, &j), args(44, 555), ""},
@@ -378,7 +378,7 @@ func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{}
}
val := v.Interface()
if !reflect.DeepEqual(val, test.out) {
- t.Errorf("%s scanning %q: expected %v got %v, type %T", name, test.text, test.out, val, val)
+ t.Errorf("%s scanning %q: expected %#v got %#v, type %T", name, test.text, test.out, val, val)
}
}
}
@@ -417,7 +417,7 @@ func TestScanf(t *testing.T) {
}
val := v.Interface()
if !reflect.DeepEqual(val, test.out) {
- t.Errorf("scanning (%q, %q): expected %v got %v, type %T", test.format, test.text, test.out, val, val)
+ t.Errorf("scanning (%q, %q): expected %#v got %#v, type %T", test.format, test.text, test.out, val, val)
}
}
}
@@ -520,7 +520,7 @@ func testScanfMulti(name string, t *testing.T) {
}
result := resultVal.Interface()
if !reflect.DeepEqual(result, test.out) {
- t.Errorf("scanning (%q, %q): expected %v got %v", test.format, test.text, test.out, result)
+ t.Errorf("scanning (%q, %q): expected %#v got %#v", test.format, test.text, test.out, result)
}
}
}
diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go
index a0aa5ff1204..1485f351c07 100644
--- a/libgo/go/go/ast/ast.go
+++ b/libgo/go/go/ast/ast.go
@@ -412,29 +412,29 @@ func (x *ChanType) End() token.Pos { return x.Value.End() }
// exprNode() ensures that only expression/type nodes can be
// assigned to an ExprNode.
//
-func (x *BadExpr) exprNode() {}
-func (x *Ident) exprNode() {}
-func (x *Ellipsis) exprNode() {}
-func (x *BasicLit) exprNode() {}
-func (x *FuncLit) exprNode() {}
-func (x *CompositeLit) exprNode() {}
-func (x *ParenExpr) exprNode() {}
-func (x *SelectorExpr) exprNode() {}
-func (x *IndexExpr) exprNode() {}
-func (x *SliceExpr) exprNode() {}
-func (x *TypeAssertExpr) exprNode() {}
-func (x *CallExpr) exprNode() {}
-func (x *StarExpr) exprNode() {}
-func (x *UnaryExpr) exprNode() {}
-func (x *BinaryExpr) exprNode() {}
-func (x *KeyValueExpr) exprNode() {}
-
-func (x *ArrayType) exprNode() {}
-func (x *StructType) exprNode() {}
-func (x *FuncType) exprNode() {}
-func (x *InterfaceType) exprNode() {}
-func (x *MapType) exprNode() {}
-func (x *ChanType) exprNode() {}
+func (*BadExpr) exprNode() {}
+func (*Ident) exprNode() {}
+func (*Ellipsis) exprNode() {}
+func (*BasicLit) exprNode() {}
+func (*FuncLit) exprNode() {}
+func (*CompositeLit) exprNode() {}
+func (*ParenExpr) exprNode() {}
+func (*SelectorExpr) exprNode() {}
+func (*IndexExpr) exprNode() {}
+func (*SliceExpr) exprNode() {}
+func (*TypeAssertExpr) exprNode() {}
+func (*CallExpr) exprNode() {}
+func (*StarExpr) exprNode() {}
+func (*UnaryExpr) exprNode() {}
+func (*BinaryExpr) exprNode() {}
+func (*KeyValueExpr) exprNode() {}
+
+func (*ArrayType) exprNode() {}
+func (*StructType) exprNode() {}
+func (*FuncType) exprNode() {}
+func (*InterfaceType) exprNode() {}
+func (*MapType) exprNode() {}
+func (*ChanType) exprNode() {}
// ----------------------------------------------------------------------------
// Convenience functions for Idents
@@ -711,27 +711,27 @@ func (s *RangeStmt) End() token.Pos { return s.Body.End() }
// stmtNode() ensures that only statement nodes can be
// assigned to a StmtNode.
//
-func (s *BadStmt) stmtNode() {}
-func (s *DeclStmt) stmtNode() {}
-func (s *EmptyStmt) stmtNode() {}
-func (s *LabeledStmt) stmtNode() {}
-func (s *ExprStmt) stmtNode() {}
-func (s *SendStmt) stmtNode() {}
-func (s *IncDecStmt) stmtNode() {}
-func (s *AssignStmt) stmtNode() {}
-func (s *GoStmt) stmtNode() {}
-func (s *DeferStmt) stmtNode() {}
-func (s *ReturnStmt) stmtNode() {}
-func (s *BranchStmt) stmtNode() {}
-func (s *BlockStmt) stmtNode() {}
-func (s *IfStmt) stmtNode() {}
-func (s *CaseClause) stmtNode() {}
-func (s *SwitchStmt) stmtNode() {}
-func (s *TypeSwitchStmt) stmtNode() {}
-func (s *CommClause) stmtNode() {}
-func (s *SelectStmt) stmtNode() {}
-func (s *ForStmt) stmtNode() {}
-func (s *RangeStmt) stmtNode() {}
+func (*BadStmt) stmtNode() {}
+func (*DeclStmt) stmtNode() {}
+func (*EmptyStmt) stmtNode() {}
+func (*LabeledStmt) stmtNode() {}
+func (*ExprStmt) stmtNode() {}
+func (*SendStmt) stmtNode() {}
+func (*IncDecStmt) stmtNode() {}
+func (*AssignStmt) stmtNode() {}
+func (*GoStmt) stmtNode() {}
+func (*DeferStmt) stmtNode() {}
+func (*ReturnStmt) stmtNode() {}
+func (*BranchStmt) stmtNode() {}
+func (*BlockStmt) stmtNode() {}
+func (*IfStmt) stmtNode() {}
+func (*CaseClause) stmtNode() {}
+func (*SwitchStmt) stmtNode() {}
+func (*TypeSwitchStmt) stmtNode() {}
+func (*CommClause) stmtNode() {}
+func (*SelectStmt) stmtNode() {}
+func (*ForStmt) stmtNode() {}
+func (*RangeStmt) stmtNode() {}
// ----------------------------------------------------------------------------
// Declarations
@@ -807,9 +807,9 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() }
// specNode() ensures that only spec nodes can be
// assigned to a Spec.
//
-func (s *ImportSpec) specNode() {}
-func (s *ValueSpec) specNode() {}
-func (s *TypeSpec) specNode() {}
+func (*ImportSpec) specNode() {}
+func (*ValueSpec) specNode() {}
+func (*TypeSpec) specNode() {}
// A declaration is represented by one of the following declaration nodes.
//
@@ -875,9 +875,9 @@ func (d *FuncDecl) End() token.Pos {
// declNode() ensures that only declaration nodes can be
// assigned to a DeclNode.
//
-func (d *BadDecl) declNode() {}
-func (d *GenDecl) declNode() {}
-func (d *FuncDecl) declNode() {}
+func (*BadDecl) declNode() {}
+func (*GenDecl) declNode() {}
+func (*FuncDecl) declNode() {}
// ----------------------------------------------------------------------------
// Files and packages
diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go
index d7d4b4b6b6d..bec235e2f98 100644
--- a/libgo/go/go/ast/filter.go
+++ b/libgo/go/go/ast/filter.go
@@ -24,7 +24,7 @@ func exportFilter(name string) bool {
// it returns false otherwise.
//
func FileExports(src *File) bool {
- return FilterFile(src, exportFilter)
+ return filterFile(src, exportFilter, true)
}
// PackageExports trims the AST for a Go package in place such that
@@ -35,7 +35,7 @@ func FileExports(src *File) bool {
// it returns false otherwise.
//
func PackageExports(pkg *Package) bool {
- return FilterPackage(pkg, exportFilter)
+ return filterPackage(pkg, exportFilter, true)
}
// ----------------------------------------------------------------------------
@@ -72,7 +72,7 @@ func fieldName(x Expr) *Ident {
return nil
}
-func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
+func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {
if fields == nil {
return false
}
@@ -93,8 +93,8 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
keepField = len(f.Names) > 0
}
if keepField {
- if filter == exportFilter {
- filterType(f.Type, filter)
+ if export {
+ filterType(f.Type, filter, export)
}
list[j] = f
j++
@@ -107,84 +107,84 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
return
}
-func filterParamList(fields *FieldList, filter Filter) bool {
+func filterParamList(fields *FieldList, filter Filter, export bool) bool {
if fields == nil {
return false
}
var b bool
for _, f := range fields.List {
- if filterType(f.Type, filter) {
+ if filterType(f.Type, filter, export) {
b = true
}
}
return b
}
-func filterType(typ Expr, f Filter) bool {
+func filterType(typ Expr, f Filter, export bool) bool {
switch t := typ.(type) {
case *Ident:
return f(t.Name)
case *ParenExpr:
- return filterType(t.X, f)
+ return filterType(t.X, f, export)
case *ArrayType:
- return filterType(t.Elt, f)
+ return filterType(t.Elt, f, export)
case *StructType:
- if filterFieldList(t.Fields, f) {
+ if filterFieldList(t.Fields, f, export) {
t.Incomplete = true
}
return len(t.Fields.List) > 0
case *FuncType:
- b1 := filterParamList(t.Params, f)
- b2 := filterParamList(t.Results, f)
+ b1 := filterParamList(t.Params, f, export)
+ b2 := filterParamList(t.Results, f, export)
return b1 || b2
case *InterfaceType:
- if filterFieldList(t.Methods, f) {
+ if filterFieldList(t.Methods, f, export) {
t.Incomplete = true
}
return len(t.Methods.List) > 0
case *MapType:
- b1 := filterType(t.Key, f)
- b2 := filterType(t.Value, f)
+ b1 := filterType(t.Key, f, export)
+ b2 := filterType(t.Value, f, export)
return b1 || b2
case *ChanType:
- return filterType(t.Value, f)
+ return filterType(t.Value, f, export)
}
return false
}
-func filterSpec(spec Spec, f Filter) bool {
+func filterSpec(spec Spec, f Filter, export bool) bool {
switch s := spec.(type) {
case *ValueSpec:
s.Names = filterIdentList(s.Names, f)
if len(s.Names) > 0 {
- if f == exportFilter {
- filterType(s.Type, f)
+ if export {
+ filterType(s.Type, f, export)
}
return true
}
case *TypeSpec:
if f(s.Name.Name) {
- if f == exportFilter {
- filterType(s.Type, f)
+ if export {
+ filterType(s.Type, f, export)
}
return true
}
- if f != exportFilter {
+ if !export {
// For general filtering (not just exports),
// filter type even if name is not filtered
// out.
// If the type contains filtered elements,
// keep the declaration.
- return filterType(s.Type, f)
+ return filterType(s.Type, f, export)
}
}
return false
}
-func filterSpecList(list []Spec, f Filter) []Spec {
+func filterSpecList(list []Spec, f Filter, export bool) []Spec {
j := 0
for _, s := range list {
- if filterSpec(s, f) {
+ if filterSpec(s, f, export) {
list[j] = s
j++
}
@@ -200,9 +200,13 @@ func filterSpecList(list []Spec, f Filter) []Spec {
// filtering; it returns false otherwise.
//
func FilterDecl(decl Decl, f Filter) bool {
+ return filterDecl(decl, f, false)
+}
+
+func filterDecl(decl Decl, f Filter, export bool) bool {
switch d := decl.(type) {
case *GenDecl:
- d.Specs = filterSpecList(d.Specs, f)
+ d.Specs = filterSpecList(d.Specs, f, export)
return len(d.Specs) > 0
case *FuncDecl:
return f(d.Name.Name)
@@ -221,9 +225,13 @@ func FilterDecl(decl Decl, f Filter) bool {
// left after filtering; it returns false otherwise.
//
func FilterFile(src *File, f Filter) bool {
+ return filterFile(src, f, false)
+}
+
+func filterFile(src *File, f Filter, export bool) bool {
j := 0
for _, d := range src.Decls {
- if FilterDecl(d, f) {
+ if filterDecl(d, f, export) {
src.Decls[j] = d
j++
}
@@ -244,9 +252,13 @@ func FilterFile(src *File, f Filter) bool {
// left after filtering; it returns false otherwise.
//
func FilterPackage(pkg *Package, f Filter) bool {
+ return filterPackage(pkg, f, false)
+}
+
+func filterPackage(pkg *Package, f Filter, export bool) bool {
hasDecls := false
for _, src := range pkg.Files {
- if FilterFile(src, f) {
+ if filterFile(src, f, export) {
hasDecls = true
}
}
diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go
index b24688d2ea3..c7c8e7c101e 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 e3de8d0fa7f..5301ab53e51 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/build_test.go b/libgo/go/go/build/build_test.go
index db8bc6c8a59..e22a49aa3d6 100644
--- a/libgo/go/go/build/build_test.go
+++ b/libgo/go/go/build/build_test.go
@@ -37,18 +37,20 @@ var buildPkgs = []struct {
{
"go/build/cmdtest",
&DirInfo{
- GoFiles: []string{"main.go"},
- Package: "main",
- Imports: []string{"go/build/pkgtest"},
+ GoFiles: []string{"main.go"},
+ Package: "main",
+ Imports: []string{"go/build/pkgtest"},
+ TestImports: []string{},
},
},
{
"go/build/cgotest",
&DirInfo{
- CgoFiles: []string{"cgotest.go"},
- CFiles: []string{"cgotest.c"},
- Imports: []string{"C", "unsafe"},
- Package: "cgotest",
+ CgoFiles: []string{"cgotest.go"},
+ CFiles: []string{"cgotest.c"},
+ Imports: []string{"C", "unsafe"},
+ TestImports: []string{},
+ Package: "cgotest",
},
},
}
diff --git a/libgo/go/go/build/dir.go b/libgo/go/go/build/dir.go
index 0d175c75deb..12dc99942a7 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 7ccb12993b3..91d6c430a9d 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 19216f85b96..d7bb384ed03 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 00000000000..870660ad628
--- /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 00000000000..83f24627c95
--- /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 d3bab31c5a3..be11f461c3b 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 dee90fbcf4c..f602db8896d 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 248e43d4e72..53f36092fda 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 84fb2808eba..dbd942292b5 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 aba7d93a64b..f8c22f1419d 100644
--- a/libgo/go/go/printer/printer.go
+++ b/libgo/go/go/printer/printer.go
@@ -13,13 +13,15 @@ import (
"io"
"os"
"path/filepath"
+ "strconv"
+ "strings"
"text/tabwriter"
)
const debug = false // enable for debugging
+const infinity = 1 << 30
-
-type whiteSpace int
+type whiteSpace byte
const (
ignore = whiteSpace(0)
@@ -31,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)
@@ -50,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
@@ -95,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
@@ -111,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.
//
@@ -138,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
@@ -222,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 {
@@ -238,12 +200,14 @@ 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
}
+const linePrefix = "//line "
+
// writeCommentPrefix writes the whitespace before a comment.
// If there is any pending whitespace, it consumes as much of
// it as is likely to help position the comment nicely.
@@ -252,15 +216,15 @@ func (p *printer) writeItem(pos token.Position, data string) {
// a group of comments (or nil), and isKeyword indicates if the
// next item is a keyword.
//
-func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, isKeyword bool) {
- if p.written == 0 {
+func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) {
+ 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
}
@@ -293,14 +257,14 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
}
// 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 {
@@ -337,6 +301,13 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
}
p.writeWhitespace(j)
}
+
+ // turn off indent if we're about to print a line directive.
+ indent := p.indent
+ if strings.HasPrefix(comment.Text, linePrefix) {
+ p.indent = 0
+ }
+
// use formfeeds to break columns before a comment;
// this is analogous to using formfeeds to separate
// individual lines of /*-style comments - but make
@@ -346,29 +317,31 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
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++
@@ -379,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++
@@ -396,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
}
@@ -420,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)
}
@@ -441,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
@@ -489,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)]
}
}
@@ -499,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)
}
@@ -526,28 +502,47 @@ func stripCommonPrefix(lines [][]byte) {
func (p *printer) writeComment(comment *ast.Comment) {
text := comment.Text
+ if strings.HasPrefix(text, linePrefix) {
+ pos := strings.TrimSpace(text[len(linePrefix):])
+ i := strings.LastIndex(pos, ":")
+ if i >= 0 {
+ // The line directive we are about to print changed
+ // the Filename and Line number used by go/token
+ // as it was reading the input originally.
+ // In order to match the original input, we have to
+ // update our own idea of the file and line number
+ // accordingly, after printing the directive.
+ file := pos[:i]
+ line, _ := strconv.Atoi(pos[i+1:])
+ defer func() {
+ p.pos.Filename = file
+ p.pos.Line = line
+ p.pos.Column = 1
+ }()
+ }
+ }
+
// 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)
}
}
}
@@ -583,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
@@ -599,7 +594,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
var last *ast.Comment
for ; p.commentBefore(next); p.cindex++ {
for _, c := range p.comments[p.cindex].List {
- p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, tok.IsKeyword())
+ p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, c, tok.IsKeyword())
p.writeComment(c)
last = c
}
@@ -609,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
@@ -629,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:
@@ -661,8 +655,7 @@ func (p *printer) writeWhitespace(n int) {
}
fallthrough
default:
- data[0] = byte(ch)
- p.write(data[0:])
+ p.writeByte(byte(ch))
}
}
@@ -678,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:
@@ -711,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) {
@@ -739,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()
@@ -771,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)
}
}
}
@@ -808,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
@@ -837,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:
@@ -855,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
@@ -883,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
@@ -925,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
@@ -948,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))
}
@@ -1012,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 a644aa383ab..924d4dfdb29 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 10bed2f05e4..8103a89d439 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 5980ec0dc98..557fab8a52e 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 42e53c3a5bd..e5c1db4b3d2 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 ce3ed0d0f40..2c8a25118e2 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 429230c80b4..17454deda90 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 3536c0b6a64..0d7765dc505 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/doc.go b/libgo/go/html/doc.go
index 1bea690c2c8..56b194ffb90 100644
--- a/libgo/go/html/doc.go
+++ b/libgo/go/html/doc.go
@@ -37,7 +37,7 @@ lower-cased, and attributes are collected into a []Attribute. For example:
for {
if z.Next() == html.ErrorToken {
// Returning io.EOF indicates success.
- return z.Error()
+ return z.Err()
}
emitToken(z.Token())
}
@@ -51,7 +51,7 @@ call to Next. For example, to extract an HTML page's anchor text:
tt := z.Next()
switch tt {
case ErrorToken:
- return z.Error()
+ return z.Err()
case TextToken:
if depth > 0 {
// emitBytes should copy the []byte it receives,
diff --git a/libgo/go/html/doctype.go b/libgo/go/html/doctype.go
new file mode 100644
index 00000000000..f692061a551
--- /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 f47d4ea147c..97fbc514d82 100644
--- a/libgo/go/html/parse.go
+++ b/libgo/go/html/parse.go
@@ -29,12 +29,19 @@ type parser struct {
head, form *Node
// Other parsing state flags (section 11.2.3.5).
scripting, framesetOK bool
+ // im is the current insertion mode.
+ im insertionMode
// originalIM is the insertion mode to go back to after completing a text
// or inTableText insertion mode.
originalIM insertionMode
// fosterParenting is whether new elements should be inserted according to
// the foster parenting rules (section 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 {
@@ -265,133 +272,126 @@ func (p *parser) acknowledgeSelfClosingTag() {
// An insertion mode (section 11.2.3.1) is the state transition function from
// a particular state in the HTML5 parser's state machine. It updates the
-// parser's fields depending on parser.token (where ErrorToken means EOF). In
-// addition to returning the next insertionMode state, it also returns whether
-// the token was consumed.
-type insertionMode func(*parser) (insertionMode, bool)
-
-// useTheRulesFor runs the delegate insertionMode over p, returning the actual
-// insertionMode unless the delegate caused a state transition.
-// Section 11.2.3.1, "using the rules for".
-func useTheRulesFor(p *parser, actual, delegate insertionMode) (insertionMode, bool) {
- im, consumed := delegate(p)
- if p.originalIM == delegate {
- p.originalIM = actual
- }
- if im != delegate {
- return im, consumed
- }
- return actual, consumed
-}
+// parser's fields depending on parser.tok (where ErrorToken means EOF).
+// It returns whether the token was consumed.
+type insertionMode func(*parser) bool
// setOriginalIM sets the insertion mode to return to after completing a text or
// inTableText insertion mode.
// Section 11.2.3.1, "using the rules for".
-func (p *parser) setOriginalIM(im insertionMode) {
+func (p *parser) setOriginalIM() {
if p.originalIM != nil {
panic("html: bad parser state: originalIM was set twice")
}
- p.originalIM = im
+ p.originalIM = p.im
}
// Section 11.2.3.1, "reset the insertion mode".
-func (p *parser) resetInsertionMode() insertionMode {
+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":
- return inSelectIM
+ p.im = inSelectIM
case "td", "th":
- return inCellIM
+ p.im = inCellIM
case "tr":
- return inRowIM
+ p.im = inRowIM
case "tbody", "thead", "tfoot":
- return inTableBodyIM
+ p.im = inTableBodyIM
case "caption":
- // TODO: return inCaptionIM
+ p.im = inCaptionIM
case "colgroup":
- // TODO: return inColumnGroupIM
+ p.im = inColumnGroupIM
case "table":
- return inTableIM
+ p.im = inTableIM
case "head":
- return inBodyIM
+ p.im = inBodyIM
case "body":
- return inBodyIM
+ p.im = inBodyIM
case "frameset":
- // TODO: return inFramesetIM
+ p.im = inFramesetIM
case "html":
- return beforeHeadIM
+ p.im = beforeHeadIM
+ default:
+ continue
}
+ return
}
- return inBodyIM
+ p.im = inBodyIM
}
+const whitespace = " \t\r\n\f"
+
// Section 11.2.5.4.1.
-func initialIM(p *parser) (insertionMode, bool) {
+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,
Data: p.tok.Data,
})
- return initialIM, true
+ return true
case DoctypeToken:
- p.doc.Add(&Node{
- Type: DoctypeNode,
- Data: p.tok.Data,
- })
- return beforeHTMLIM, true
+ 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.
- return beforeHTMLIM, false
+ p.quirks = true
+ p.im = beforeHTMLIM
+ return false
}
// Section 11.2.5.4.2.
-func beforeHTMLIM(p *parser) (insertionMode, bool) {
- var (
- add bool
- attr []Attribute
- implied bool
- )
+func beforeHTMLIM(p *parser) bool {
switch p.tok.Type {
- case ErrorToken:
- implied = true
case TextToken:
- // TODO: distinguish whitespace text from others.
- implied = true
+ 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" {
- add = true
- attr = p.tok.Attr
- } else {
- implied = true
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.im = beforeHeadIM
+ return true
}
case EndTagToken:
switch p.tok.Data {
case "head", "body", "html", "br":
- implied = true
+ // Drop down to creating an implied <html> tag.
default:
// Ignore the token.
+ return true
}
case CommentToken:
p.doc.Add(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return beforeHTMLIM, true
- }
- if add || implied {
- p.addElement("html", attr)
+ return true
}
- return beforeHeadIM, !implied
+ // Create an implied <html> tag.
+ p.addElement("html", nil)
+ p.im = beforeHeadIM
+ return false
}
// Section 11.2.5.4.3.
-func beforeHeadIM(p *parser) (insertionMode, bool) {
+func beforeHeadIM(p *parser) bool {
var (
add bool
attr []Attribute
@@ -401,7 +401,11 @@ func beforeHeadIM(p *parser) (insertionMode, 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 {
@@ -409,7 +413,7 @@ func beforeHeadIM(p *parser) (insertionMode, bool) {
add = true
attr = p.tok.Attr
case "html":
- return useTheRulesFor(p, beforeHeadIM, inBodyIM)
+ return inBodyIM(p)
default:
implied = true
}
@@ -425,19 +429,18 @@ func beforeHeadIM(p *parser) (insertionMode, bool) {
Type: CommentNode,
Data: p.tok.Data,
})
- return beforeHeadIM, true
+ return true
}
if add || implied {
p.addElement("head", attr)
p.head = p.top()
}
- return inHeadIM, !implied
+ p.im = inHeadIM
+ return !implied
}
-const whitespace = " \t\r\n\f"
-
// Section 11.2.5.4.4.
-func inHeadIM(p *parser) (insertionMode, bool) {
+func inHeadIM(p *parser) bool {
var (
pop bool
implied bool
@@ -451,48 +454,60 @@ func inHeadIM(p *parser) (insertionMode, bool) {
// Add the initial whitespace to the current node.
p.addText(p.tok.Data[:len(p.tok.Data)-len(s)])
if s == "" {
- return inHeadIM, true
+ return true
}
p.tok.Data = s
}
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()
p.acknowledgeSelfClosingTag()
case "script", "title", "noscript", "noframes", "style":
p.addElement(p.tok.Data, p.tok.Attr)
- p.setOriginalIM(inHeadIM)
- return textIM, true
+ p.setOriginalIM()
+ p.im = textIM
+ return true
+ case "head":
+ // Ignore the token.
+ return true
default:
implied = true
}
case EndTagToken:
- if p.tok.Data == "head" {
+ switch p.tok.Data {
+ case "head":
pop = true
+ case "body", "html", "br":
+ implied = true
+ default:
+ // Ignore the token.
+ return true
}
- // TODO.
case CommentToken:
p.addChild(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return inHeadIM, true
+ return true
}
if pop || implied {
n := p.oe.pop()
if n.Data != "head" {
panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
}
- return afterHeadIM, !implied
+ p.im = afterHeadIM
+ return !implied
}
- return inHeadIM, true
+ return true
}
// Section 11.2.5.4.6.
-func afterHeadIM(p *parser) (insertionMode, bool) {
+func afterHeadIM(p *parser) bool {
var (
add bool
attr []Attribute
@@ -512,11 +527,13 @@ func afterHeadIM(p *parser) (insertionMode, bool) {
attr = p.tok.Attr
framesetOK = false
case "frameset":
- // TODO.
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.im = inFramesetIM
+ return true
case "base", "basefont", "bgsound", "link", "meta", "noframes", "script", "style", "title":
p.oe = append(p.oe, p.head)
defer p.oe.pop()
- return useTheRulesFor(p, afterHeadIM, inHeadIM)
+ return inHeadIM(p)
case "head":
// TODO.
default:
@@ -524,19 +541,27 @@ func afterHeadIM(p *parser) (insertionMode, bool) {
framesetOK = true
}
case EndTagToken:
- // TODO.
+ switch p.tok.Data {
+ case "body", "html", "br":
+ implied = true
+ framesetOK = true
+ default:
+ // Ignore the token.
+ return true
+ }
case CommentToken:
p.addChild(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return afterHeadIM, true
+ return true
}
if add || implied {
p.addElement("body", attr)
p.framesetOK = framesetOK
}
- return inBodyIM, !implied
+ p.im = inBodyIM
+ return !implied
}
// copyAttributes copies attributes of src not found on dst to dst.
@@ -557,14 +582,33 @@ func copyAttributes(dst *Node, src Token) {
}
// Section 11.2.5.4.7.
-func inBodyIM(p *parser) (insertionMode, bool) {
+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) (insertionMode, 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,10 +652,13 @@ func inBodyIM(p *parser) (insertionMode, 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
- return inTableIM, true
+ p.im = inTableIM
+ return true
case "hr":
p.popUntil(buttonScopeStopTags, "p")
p.addElement(p.tok.Data, p.tok.Attr)
@@ -616,7 +670,14 @@ func inBodyIM(p *parser) (insertionMode, bool) {
p.addElement(p.tok.Data, p.tok.Attr)
p.framesetOK = false
// TODO: detect <select> inside a table.
- return inSelectIM, true
+ p.im = inSelectIM
+ return true
+ case "form":
+ if p.form == nil {
+ p.popUntil(buttonScopeStopTags, "p")
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.form = p.top()
+ }
case "li":
p.framesetOK = false
for i := len(p.oe) - 1; i >= 0; i-- {
@@ -634,7 +695,28 @@ func inBodyIM(p *parser) (insertionMode, bool) {
break
}
p.popUntil(buttonScopeStopTags, "p")
- p.addElement("li", p.tok.Attr)
+ p.addElement(p.tok.Data, p.tok.Attr)
+ case "dd", "dt":
+ p.framesetOK = false
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ node := p.oe[i]
+ switch node.Data {
+ case "dd", "dt":
+ p.oe = p.oe[:i]
+ case "address", "div", "p":
+ continue
+ default:
+ if !isSpecialElement[node.Data] {
+ continue
+ }
+ }
+ break
+ }
+ p.popUntil(buttonScopeStopTags, "p")
+ p.addElement(p.tok.Data, p.tok.Attr)
+ case "plaintext":
+ p.popUntil(buttonScopeStopTags, "p")
+ p.addElement(p.tok.Data, p.tok.Attr)
case "optgroup", "option":
if p.top().Data == "option" {
p.oe.pop()
@@ -650,10 +732,55 @@ func inBodyIM(p *parser) (insertionMode, bool) {
}
}
case "base", "basefont", "bgsound", "command", "link", "meta", "noframes", "script", "style", "title":
- return useTheRulesFor(p, inBodyIM, inHeadIM)
+ return inHeadIM(p)
case "image":
p.tok.Data = "img"
- return inBodyIM, false
+ return false
+ case "isindex":
+ if p.form != nil {
+ // Ignore the token.
+ return true
+ }
+ action := ""
+ prompt := "This is a searchable index. Enter search keywords: "
+ attr := []Attribute{{Key: "name", Val: "isindex"}}
+ for _, a := range p.tok.Attr {
+ switch a.Key {
+ case "action":
+ action = a.Val
+ case "name":
+ // Ignore the attribute.
+ case "prompt":
+ prompt = a.Val
+ default:
+ attr = append(attr, a)
+ }
+ }
+ p.acknowledgeSelfClosingTag()
+ p.popUntil(buttonScopeStopTags, "p")
+ p.addElement("form", nil)
+ p.form = p.top()
+ if action != "" {
+ p.form.Attr = []Attribute{{Key: "action", Val: action}}
+ }
+ p.addElement("hr", nil)
+ p.oe.pop()
+ p.addElement("label", nil)
+ p.addText(prompt)
+ p.addElement("input", attr)
+ p.oe.pop()
+ p.oe.pop()
+ p.addElement("hr", nil)
+ 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:
// TODO.
p.addElement(p.tok.Data, p.tok.Attr)
@@ -662,7 +789,8 @@ func inBodyIM(p *parser) (insertionMode, bool) {
switch p.tok.Data {
case "body":
// TODO: autoclose the stack of open elements.
- return afterBodyIM, true
+ p.im = afterBodyIM
+ return true
case "p":
if !p.elementInScope(buttonScopeStopTags, "p") {
p.addElement("p", nil)
@@ -676,6 +804,9 @@ func inBodyIM(p *parser) (insertionMode, bool) {
if p.popUntil(defaultScopeStopTags, p.tok.Data) {
p.clearActiveFormattingElements()
}
+ case "br":
+ p.tok.Type = StartTagToken
+ return false
default:
p.inBodyEndTagOther(p.tok.Data)
}
@@ -686,7 +817,7 @@ func inBodyIM(p *parser) (insertionMode, bool) {
})
}
- return inBodyIM, true
+ return true
}
func (p *parser) inBodyEndTagFormatting(tag string) {
@@ -827,45 +958,64 @@ func (p *parser) inBodyEndTagOther(tag string) {
}
// Section 11.2.5.4.8.
-func textIM(p *parser) (insertionMode, bool) {
+func textIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
p.oe.pop()
case TextToken:
p.addText(p.tok.Data)
- return textIM, true
+ return true
case EndTagToken:
p.oe.pop()
}
- o := p.originalIM
+ p.im = p.originalIM
p.originalIM = nil
- return o, p.tok.Type == EndTagToken
+ return p.tok.Type == EndTagToken
}
// Section 11.2.5.4.9.
-func inTableIM(p *parser) (insertionMode, bool) {
+func inTableIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
// Stop parsing.
- return nil, true
+ return true
case TextToken:
// TODO.
case StartTagToken:
switch p.tok.Data {
+ case "caption":
+ p.clearStackToContext(tableScopeStopTags)
+ p.afe = append(p.afe, &scopeMarker)
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.im = inCaptionIM
+ return true
case "tbody", "tfoot", "thead":
p.clearStackToContext(tableScopeStopTags)
p.addElement(p.tok.Data, p.tok.Attr)
- return inTableBodyIM, true
+ p.im = inTableBodyIM
+ return true
case "td", "th", "tr":
p.clearStackToContext(tableScopeStopTags)
p.addElement("tbody", nil)
- return inTableBodyIM, false
+ p.im = inTableBodyIM
+ return false
case "table":
if p.popUntil(tableScopeStopTags, "table") {
- return p.resetInsertionMode(), false
+ p.resetInsertionMode()
+ return false
}
// Ignore the token.
- return inTableIM, true
+ return true
+ case "colgroup":
+ p.clearStackToContext(tableScopeStopTags)
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.im = inColumnGroupIM
+ return true
+ case "col":
+ p.clearStackToContext(tableScopeStopTags)
+ p.addElement("colgroup", p.tok.Attr)
+ p.im = inColumnGroupIM
+ return false
default:
// TODO.
}
@@ -873,20 +1023,21 @@ func inTableIM(p *parser) (insertionMode, bool) {
switch p.tok.Data {
case "table":
if p.popUntil(tableScopeStopTags, "table") {
- return p.resetInsertionMode(), true
+ p.resetInsertionMode()
+ return true
}
// Ignore the token.
- return inTableIM, true
+ return true
case "body", "caption", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr":
// Ignore the token.
- return inTableIM, true
+ return true
}
case CommentToken:
p.addChild(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return inTableIM, true
+ return true
}
switch p.top().Data {
@@ -895,7 +1046,7 @@ func inTableIM(p *parser) (insertionMode, bool) {
defer func() { p.fosterParenting = false }()
}
- return useTheRulesFor(p, inTableIM, inBodyIM)
+ return inBodyIM(p)
}
// clearStackToContext pops elements off the stack of open elements
@@ -911,8 +1062,90 @@ func (p *parser) clearStackToContext(stopTags []string) {
}
}
+// Section 11.2.5.4.11.
+func inCaptionIM(p *parser) bool {
+ switch p.tok.Type {
+ case StartTagToken:
+ switch p.tok.Data {
+ case "caption", "col", "colgroup", "tbody", "td", "tfoot", "thead", "tr":
+ if p.popUntil(tableScopeStopTags, "caption") {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ return false
+ } else {
+ // Ignore the token.
+ return true
+ }
+ }
+ case EndTagToken:
+ switch p.tok.Data {
+ case "caption":
+ if p.popUntil(tableScopeStopTags, "caption") {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ }
+ return true
+ case "table":
+ if p.popUntil(tableScopeStopTags, "caption") {
+ p.clearActiveFormattingElements()
+ p.im = inTableIM
+ return false
+ } else {
+ // Ignore the token.
+ return true
+ }
+ case "body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr":
+ // Ignore the token.
+ return true
+ }
+ }
+ return inBodyIM(p)
+}
+
+// Section 11.2.5.4.12.
+func inColumnGroupIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ return true
+ case DoctypeToken:
+ // Ignore the token.
+ return true
+ case StartTagToken:
+ switch p.tok.Data {
+ case "html":
+ return inBodyIM(p)
+ case "col":
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ return true
+ }
+ case EndTagToken:
+ switch p.tok.Data {
+ case "colgroup":
+ if p.oe.top().Data != "html" {
+ p.oe.pop()
+ }
+ p.im = inTableIM
+ return true
+ case "col":
+ // Ignore the token.
+ return true
+ }
+ }
+ if p.oe.top().Data != "html" {
+ p.oe.pop()
+ }
+ p.im = inTableIM
+ return false
+}
+
// Section 11.2.5.4.13.
-func inTableBodyIM(p *parser) (insertionMode, bool) {
+func inTableBodyIM(p *parser) bool {
var (
add bool
data string
@@ -942,31 +1175,33 @@ func inTableBodyIM(p *parser) (insertionMode, bool) {
switch p.tok.Data {
case "table":
if p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") {
- return inTableIM, false
+ p.im = inTableIM
+ return false
}
// Ignore the token.
- return inTableBodyIM, true
+ return true
case "body", "caption", "col", "colgroup", "html", "td", "th", "tr":
// Ignore the token.
- return inTableBodyIM, true
+ return true
}
case CommentToken:
p.addChild(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return inTableBodyIM, true
+ return true
}
if add {
// TODO: clear the stack back to a table body context.
p.addElement(data, attr)
- return inRowIM, consumed
+ p.im = inRowIM
+ return consumed
}
- return useTheRulesFor(p, inTableBodyIM, inTableIM)
+ return inTableIM(p)
}
// Section 11.2.5.4.14.
-func inRowIM(p *parser) (insertionMode, bool) {
+func inRowIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
// TODO.
@@ -978,13 +1213,15 @@ func inRowIM(p *parser) (insertionMode, bool) {
p.clearStackToContext(tableRowContextStopTags)
p.addElement(p.tok.Data, p.tok.Attr)
p.afe = append(p.afe, &scopeMarker)
- return inCellIM, true
+ p.im = inCellIM
+ return true
case "caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr":
if p.popUntil(tableScopeStopTags, "tr") {
- return inTableBodyIM, false
+ p.im = inTableBodyIM
+ return false
}
// Ignore the token.
- return inRowIM, true
+ return true
default:
// TODO.
}
@@ -992,21 +1229,23 @@ func inRowIM(p *parser) (insertionMode, bool) {
switch p.tok.Data {
case "tr":
if p.popUntil(tableScopeStopTags, "tr") {
- return inTableBodyIM, true
+ p.im = inTableBodyIM
+ return true
}
// Ignore the token.
- return inRowIM, true
+ return true
case "table":
if p.popUntil(tableScopeStopTags, "tr") {
- return inTableBodyIM, false
+ p.im = inTableBodyIM
+ return false
}
// Ignore the token.
- return inRowIM, true
+ return true
case "tbody", "tfoot", "thead":
// TODO.
case "body", "caption", "col", "colgroup", "html", "td", "th":
// Ignore the token.
- return inRowIM, true
+ return true
default:
// TODO.
}
@@ -1015,13 +1254,13 @@ func inRowIM(p *parser) (insertionMode, bool) {
Type: CommentNode,
Data: p.tok.Data,
})
- return inRowIM, true
+ return true
}
- return useTheRulesFor(p, inRowIM, inTableIM)
+ return inTableIM(p)
}
// Section 11.2.5.4.15.
-func inCellIM(p *parser) (insertionMode, bool) {
+func inCellIM(p *parser) bool {
var (
closeTheCellAndReprocess bool
)
@@ -1037,10 +1276,11 @@ func inCellIM(p *parser) (insertionMode, bool) {
case "td", "th":
if !p.popUntil(tableScopeStopTags, p.tok.Data) {
// Ignore the token.
- return inCellIM, true
+ return true
}
p.clearActiveFormattingElements()
- return inRowIM, true
+ p.im = inRowIM
+ return true
case "body", "caption", "col", "colgroup", "html":
// TODO.
case "table", "tbody", "tfoot", "thead", "tr":
@@ -1052,19 +1292,20 @@ func inCellIM(p *parser) (insertionMode, bool) {
Type: CommentNode,
Data: p.tok.Data,
})
- return inCellIM, true
+ return true
}
if closeTheCellAndReprocess {
if p.popUntil(tableScopeStopTags, "td") || p.popUntil(tableScopeStopTags, "th") {
p.clearActiveFormattingElements()
- return inRowIM, false
+ p.im = inRowIM
+ return false
}
}
- return useTheRulesFor(p, inCellIM, inBodyIM)
+ return inBodyIM(p)
}
// Section 11.2.5.4.16.
-func inSelectIM(p *parser) (insertionMode, bool) {
+func inSelectIM(p *parser) bool {
endSelect := false
switch p.tok.Type {
case ErrorToken:
@@ -1081,7 +1322,13 @@ func inSelectIM(p *parser) (insertionMode, bool) {
}
p.addElement(p.tok.Data, p.tok.Attr)
case "optgroup":
- // TODO.
+ if p.top().Data == "option" {
+ p.oe.pop()
+ }
+ if p.top().Data == "optgroup" {
+ p.oe.pop()
+ }
+ p.addElement(p.tok.Data, p.tok.Attr)
case "select":
endSelect = true
case "input", "keygen", "textarea":
@@ -1094,9 +1341,17 @@ func inSelectIM(p *parser) (insertionMode, bool) {
case EndTagToken:
switch p.tok.Data {
case "option":
- // TODO.
+ if p.top().Data == "option" {
+ p.oe.pop()
+ }
case "optgroup":
- // TODO.
+ i := len(p.oe) - 1
+ if p.oe[i].Data == "option" {
+ i--
+ }
+ if p.oe[i].Data == "optgroup" {
+ p.oe = p.oe[:i]
+ }
case "select":
endSelect = true
default:
@@ -1113,34 +1368,33 @@ func inSelectIM(p *parser) (insertionMode, bool) {
switch p.oe[i].Data {
case "select":
p.oe = p.oe[:i]
- return p.resetInsertionMode(), true
+ p.resetInsertionMode()
+ return true
case "option", "optgroup":
continue
default:
// Ignore the token.
- return inSelectIM, true
+ return true
}
}
}
- return inSelectIM, true
+ return true
}
// Section 11.2.5.4.18.
-func afterBodyIM(p *parser) (insertionMode, bool) {
+func afterBodyIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
- // TODO.
- case TextToken:
- // TODO.
+ // Stop parsing.
+ return true
case StartTagToken:
- // TODO.
+ if p.tok.Data == "html" {
+ return inBodyIM(p)
+ }
case EndTagToken:
- switch p.tok.Data {
- case "html":
- // TODO: autoclose the stack of open elements.
- return afterAfterBodyIM, true
- default:
- // TODO.
+ if p.tok.Data == "html" {
+ p.im = afterAfterBodyIM
+ return true
}
case CommentToken:
// The comment is attached to the <html> element.
@@ -1151,63 +1405,212 @@ func afterBodyIM(p *parser) (insertionMode, bool) {
Type: CommentNode,
Data: p.tok.Data,
})
- return afterBodyIM, true
+ return true
+ }
+ p.im = inBodyIM
+ return false
+}
+
+// Section 11.2.5.4.19.
+func inFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case StartTagToken:
+ switch p.tok.Data {
+ case "html":
+ return inBodyIM(p)
+ case "frameset":
+ p.addElement(p.tok.Data, p.tok.Attr)
+ case "frame":
+ p.addElement(p.tok.Data, p.tok.Attr)
+ p.oe.pop()
+ p.acknowledgeSelfClosingTag()
+ case "noframes":
+ return inHeadIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.Data {
+ case "frameset":
+ if p.oe.top().Data != "html" {
+ p.oe.pop()
+ if p.oe.top().Data != "frameset" {
+ p.im = afterFramesetIM
+ return true
+ }
+ }
+ }
+ default:
+ // Ignore the token.
+ }
+ return true
+}
+
+// Section 11.2.5.4.20.
+func afterFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case StartTagToken:
+ switch p.tok.Data {
+ case "html":
+ return inBodyIM(p)
+ case "noframes":
+ return inHeadIM(p)
+ }
+ case EndTagToken:
+ switch p.tok.Data {
+ case "html":
+ p.im = afterAfterFramesetIM
+ return true
+ }
+ default:
+ // Ignore the token.
}
- // TODO: should this be "return inBodyIM, true"?
- return afterBodyIM, true
+ return true
}
// Section 11.2.5.4.21.
-func afterAfterBodyIM(p *parser) (insertionMode, bool) {
+func afterAfterBodyIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
// Stop parsing.
- return nil, true
+ return true
case TextToken:
// TODO.
case StartTagToken:
if p.tok.Data == "html" {
- return useTheRulesFor(p, afterAfterBodyIM, inBodyIM)
+ return inBodyIM(p)
}
case CommentToken:
p.doc.Add(&Node{
Type: CommentNode,
Data: p.tok.Data,
})
- return afterAfterBodyIM, true
+ return true
}
- return inBodyIM, false
+ p.im = inBodyIM
+ return false
}
-// 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,
+// Section 11.2.5.4.22.
+func afterAfterFramesetIM(p *parser) bool {
+ switch p.tok.Type {
+ case CommentToken:
+ p.addChild(&Node{
+ Type: CommentNode,
+ Data: p.tok.Data,
+ })
+ case StartTagToken:
+ switch p.tok.Data {
+ case "html":
+ return inBodyIM(p)
+ case "noframes":
+ return inHeadIM(p)
+ }
+ default:
+ // Ignore the token.
}
+ return true
+}
+
+func (p *parser) parse() error {
// Iterate until EOF. Any other error will cause an early return.
- im, consumed := initialIM, true
+ consumed := true
for {
if consumed {
if err := p.read(); err != nil {
if err == io.EOF {
break
}
- return nil, err
+ return err
}
}
- im, consumed = im(p)
+ consumed = p.im(p)
}
// Loop until the final token (the ErrorToken signifying EOF) is consumed.
for {
- if im, consumed = im(p); consumed {
+ if consumed = p.im(p); consumed {
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 27979225b33..e0c19cff6da 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.
- {"tests1.dat", 92},
- {"tests2.dat", 0},
- {"tests3.dat", 0},
+ {"doctype01.dat", -1},
+ {"tests1.dat", -1},
+ {"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)
- }
- }
}
}
@@ -213,4 +251,8 @@ var renderTestBlacklist = map[string]bool{
// More cases of <a> being reparented:
`<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true,
`<a><table><a></table><p><a><div><a>`: true,
+ `<a><table><td><a><table></table><a></tr><a></table><a>`: true,
+ // A <plaintext> element is reparented, putting it before a table.
+ // A <plaintext> element can't have anything after it in HTML.
+ `<table><plaintext><td>`: true,
}
diff --git a/libgo/go/html/render.go b/libgo/go/html/render.go
index c815f35f1e1..7e1a4669657 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 {
@@ -52,7 +53,19 @@ func Render(w io.Writer, n *Node) error {
return buf.Flush()
}
+// plaintextAbort is returned from render1 when a <plaintext> element
+// has been rendered. No more end tags should be rendered after that.
+var plaintextAbort = errors.New("html: internal error (plaintext abort)")
+
func render(w writer, n *Node) error {
+ err := render1(w, n)
+ if err == plaintextAbort {
+ err = nil
+ }
+ return err
+}
+
+func render1(w writer, n *Node) error {
// Render non-element nodes; these are the easy cases.
switch n.Type {
case ErrorNode:
@@ -61,7 +74,7 @@ func render(w writer, n *Node) error {
return escape(w, n.Data)
case DocumentNode:
for _, c := range n.Child {
- if err := render(w, c); err != nil {
+ if err := render1(w, c); err != nil {
return err
}
}
@@ -86,6 +99,40 @@ func render(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")
@@ -126,9 +173,19 @@ func render(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", "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)
@@ -137,18 +194,23 @@ func render(w writer, n *Node) error {
return err
}
}
+ if n.Data == "plaintext" {
+ // Don't render anything else. <plaintext> must be the
+ // last element in the file, with no closing tag.
+ return plaintextAbort
+ }
case "textarea", "title":
for _, c := range n.Child {
if c.Type != TextNode {
return fmt.Errorf("html: RCDATA element <%s> has non-text child node", n.Data)
}
- if err := render(w, c); err != nil {
+ if err := render1(w, c); err != nil {
return err
}
}
default:
for _, c := range n.Child {
- if err := render(w, c); err != nil {
+ if err := render1(w, c); err != nil {
return err
}
}
@@ -164,6 +226,27 @@ func render(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 ed1698acd8b..39788173b99 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 d720d4ba689..4de7ccde912 100644
--- a/libgo/go/html/template/content.go
+++ b/libgo/go/html/template/content.go
@@ -6,15 +6,16 @@ package template
import (
"fmt"
+ "reflect"
)
// 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
@@ -40,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
@@ -70,10 +71,25 @@ const (
contentTypeUnsafe
)
+// indirect returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil).
+func indirect(a interface{}) interface{} {
+ if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
+ // Avoid creating a reflect.Value if it's not a pointer.
+ return a
+ }
+ v := reflect.ValueOf(a)
+ for v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
// stringify converts its arguments to a string and the type of the content.
+// All pointers are dereferenced, as in the text/template package.
func stringify(args ...interface{}) (string, contentType) {
if len(args) == 1 {
- switch s := args[0].(type) {
+ switch s := indirect(args[0]).(type) {
case string:
return s, contentTypePlain
case CSS:
@@ -90,5 +106,8 @@ func stringify(args ...interface{}) (string, contentType) {
return string(s), contentTypeURL
}
}
+ for i, arg := range args {
+ args[i] = indirect(arg)
+ }
return fmt.Sprint(args...), contentTypePlain
}
diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go
index 0324c9c0ee3..fc0e3826442 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 8ac07eae24c..4a7a9354c93 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 d8bfa321121..b4daca7d6bd 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -28,7 +28,7 @@ func (x *goodMarshaler) MarshalJSON() ([]byte, error) {
}
func TestEscape(t *testing.T) {
- var data = struct {
+ data := struct {
F, T bool
C, G, H string
A, E []string
@@ -50,6 +50,7 @@ func TestEscape(t *testing.T) {
Z: nil,
W: HTML(`&iexcl;<b class="foo">Hello</b>, <textarea>O'World</textarea>!`),
}
+ pdata := &data
tests := []struct {
name string
@@ -668,6 +669,15 @@ func TestEscape(t *testing.T) {
t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
continue
}
+ b.Reset()
+ if err := tmpl.Execute(b, pdata); err != nil {
+ t.Errorf("%s: template execution failed for pointer: %s", test.name, err)
+ continue
+ }
+ if w, g := test.output, b.String(); w != g {
+ t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
+ continue
+ }
}
}
@@ -796,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 {
@@ -919,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`,
@@ -957,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()
@@ -1559,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 {
@@ -1605,6 +1609,29 @@ func TestRedundantFuncs(t *testing.T) {
}
}
+func TestIndirectPrint(t *testing.T) {
+ a := 3
+ ap := &a
+ b := "hello"
+ bp := &b
+ bpp := &bp
+ tmpl := Must(New("t").Parse(`{{.}}`))
+ var buf bytes.Buffer
+ err := tmpl.Execute(&buf, ap)
+ if err != nil {
+ t.Errorf("Unexpected error: %s", err)
+ } else if buf.String() != "3" {
+ t.Errorf(`Expected "3"; got %q`, buf.String())
+ }
+ buf.Reset()
+ err = tmpl.Execute(&buf, bpp)
+ if err != nil {
+ t.Errorf("Unexpected error: %s", err)
+ } else if buf.String() != "hello" {
+ t.Errorf(`Expected "hello"; got %q`, buf.String())
+ }
+}
+
func BenchmarkEscapedExecute(b *testing.B) {
tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`))
var buf bytes.Buffer
diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go
index 68c53e5ca3b..0e632df4220 100644
--- a/libgo/go/html/template/js.go
+++ b/libgo/go/html/template/js.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/json"
"fmt"
+ "reflect"
"strings"
"unicode/utf8"
)
@@ -117,12 +118,24 @@ var regexpPrecederKeywords = map[string]bool{
"void": true,
}
+var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
+
+// indirectToJSONMarshaler returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
+func indirectToJSONMarshaler(a interface{}) interface{} {
+ v := reflect.ValueOf(a)
+ for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
-// nether side-effects nor free variables outside (NaN, Infinity).
+// neither side-effects nor free variables outside (NaN, Infinity).
func jsValEscaper(args ...interface{}) string {
var a interface{}
if len(args) == 1 {
- a = args[0]
+ a = indirectToJSONMarshaler(args[0])
switch t := a.(type) {
case JS:
return string(t)
@@ -135,6 +148,9 @@ func jsValEscaper(args ...interface{}) string {
a = t.String()
}
} else {
+ for i, arg := range args {
+ args[i] = indirectToJSONMarshaler(arg)
+ }
a = fmt.Sprint(args...)
}
// TODO: detect cycles before calling Marshal which loops infinitely on
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 47334299384..f05ca190f73 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 2c138227b10..69af96840c2 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>", "<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, 8].
- if x := z.data.end - z.data.start; 5 <= x && x <= 8 {
- switch z.buf[z.data.start] {
- case 'n', 's', 't', 'N', 'S', 'T':
- switch s := strings.ToLower(string(z.buf[z.data.start:z.data.end])); s {
- case "noembed", "noframes", "noscript", "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] == '/' {
@@ -551,9 +587,19 @@ func (z *Tokenizer) Next() TokenType {
z.data.start = z.raw.end
z.data.end = z.raw.end
if z.rawTag != "" {
- z.readRawOrRCDATA()
- z.tt = TextToken
- return z.tt
+ if z.rawTag == "plaintext" {
+ // Read everything up to EOF.
+ for z.err == nil {
+ z.readByte()
+ }
+ z.textIsRaw = true
+ } else {
+ z.readRawOrRCDATA()
+ }
+ if z.data.end > z.data.start {
+ z.tt = TextToken
+ return z.tt
+ }
}
z.textIsRaw = false
diff --git a/libgo/go/html/token_test.go b/libgo/go/html/token_test.go
index 61d4e67c06d..672d60c4209 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/image/tiff/buffer.go b/libgo/go/image/tiff/buffer.go
index ce350738ed8..27533c6047d 100644
--- a/libgo/go/image/tiff/buffer.go
+++ b/libgo/go/image/tiff/buffer.go
@@ -4,10 +4,7 @@
package tiff
-import (
- "io"
- "os"
-)
+import "io"
// buffer buffers an io.Reader to satisfy io.ReaderAt.
type buffer struct {
@@ -19,7 +16,7 @@ func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
o := int(off)
end := o + len(p)
if int64(end) != off+int64(len(p)) {
- return 0, os.EINVAL
+ return 0, io.ErrUnexpectedEOF
}
m := len(b.buf)
diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go
index f6c8cd8a873..be7fa5f2bc8 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 55e4b2c2bc8..89d6815ad50 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 658ea78bb7c..645eed6abb8 100644
--- a/libgo/go/io/ioutil/tempfile.go
+++ b/libgo/go/io/ioutil/tempfile.go
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"strconv"
+ "time"
)
// Random number state, accessed without lock; racy but harmless.
@@ -17,8 +18,7 @@ import (
var rand uint32
func reseed() uint32 {
- sec, nsec, _ := os.Time()
- return uint32(sec*1e9 + nsec + 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 0de5cc312d0..eb717f7bc21 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 b5368af5319..a5d88fd9b34 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/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go
index 26a2f736b17..546bc296a5f 100644
--- a/libgo/go/log/syslog/syslog.go
+++ b/libgo/go/log/syslog/syslog.go
@@ -8,6 +8,7 @@
package syslog
import (
+ "errors"
"fmt"
"log"
"net"
@@ -75,7 +76,7 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e
// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, error) {
if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
- return 0, os.EINVAL
+ return 0, errors.New("log/syslog: invalid priority")
}
return w.conn.writeBytes(w.priority, w.prefix, b)
}
diff --git a/libgo/go/math/abs.go b/libgo/go/math/abs.go
index eb3e4c72b32..4c6297c6f33 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 c1cad563c76..d6979463d65 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 1cd93b1052b..0950eeedbd2 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.go b/libgo/go/math/big/int.go
index 533a97f7495..35e2e294183 100644
--- a/libgo/go/math/big/int.go
+++ b/libgo/go/math/big/int.go
@@ -176,7 +176,7 @@ func (z *Int) Quo(x, y *Int) *Int {
// If y == 0, a division-by-zero run-time panic occurs.
// Rem implements truncated modulus (like Go); see QuoRem for more details.
func (z *Int) Rem(x, y *Int) *Int {
- _, z.abs = nat{}.div(z.abs, x.abs, y.abs)
+ _, z.abs = nat(nil).div(z.abs, x.abs, y.abs)
z.neg = len(z.abs) > 0 && x.neg // 0 has no sign
return z
}
@@ -678,14 +678,14 @@ func (z *Int) Bit(i int) uint {
panic("negative bit index")
}
if z.neg {
- t := nat{}.sub(z.abs, natOne)
+ t := nat(nil).sub(z.abs, natOne)
return t.bit(uint(i)) ^ 1
}
return z.abs.bit(uint(i))
}
-// SetBit sets the i'th bit of z to bit and returns z.
+// SetBit sets z to x, with x's i'th bit set to b (0 or 1).
// That is, if bit is 1 SetBit sets z = x | (1 << i);
// if bit is 0 it sets z = x &^ (1 << i). If bit is not 0 or 1,
// SetBit will panic.
@@ -710,8 +710,8 @@ func (z *Int) And(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1)
- x1 := nat{}.sub(x.abs, natOne)
- y1 := nat{}.sub(y.abs, natOne)
+ x1 := nat(nil).sub(x.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.add(z.abs.or(x1, y1), natOne)
z.neg = true // z cannot be zero if x and y are negative
return z
@@ -729,7 +729,7 @@ func (z *Int) And(x, y *Int) *Int {
}
// x & (-y) == x & ^(y-1) == x &^ (y-1)
- y1 := nat{}.sub(y.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.andNot(x.abs, y1)
z.neg = false
return z
@@ -740,8 +740,8 @@ func (z *Int) AndNot(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1)
- x1 := nat{}.sub(x.abs, natOne)
- y1 := nat{}.sub(y.abs, natOne)
+ x1 := nat(nil).sub(x.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.andNot(y1, x1)
z.neg = false
return z
@@ -755,14 +755,14 @@ func (z *Int) AndNot(x, y *Int) *Int {
if x.neg {
// (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1)
- x1 := nat{}.sub(x.abs, natOne)
+ x1 := nat(nil).sub(x.abs, natOne)
z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne)
z.neg = true // z cannot be zero if x is negative and y is positive
return z
}
// x &^ (-y) == x &^ ^(y-1) == x & (y-1)
- y1 := nat{}.add(y.abs, natOne)
+ y1 := nat(nil).add(y.abs, natOne)
z.abs = z.abs.and(x.abs, y1)
z.neg = false
return z
@@ -773,8 +773,8 @@ func (z *Int) Or(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1)
- x1 := nat{}.sub(x.abs, natOne)
- y1 := nat{}.sub(y.abs, natOne)
+ x1 := nat(nil).sub(x.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.add(z.abs.and(x1, y1), natOne)
z.neg = true // z cannot be zero if x and y are negative
return z
@@ -792,7 +792,7 @@ func (z *Int) Or(x, y *Int) *Int {
}
// x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1)
- y1 := nat{}.sub(y.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne)
z.neg = true // z cannot be zero if one of x or y is negative
return z
@@ -803,8 +803,8 @@ func (z *Int) Xor(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1)
- x1 := nat{}.sub(x.abs, natOne)
- y1 := nat{}.sub(y.abs, natOne)
+ x1 := nat(nil).sub(x.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.xor(x1, y1)
z.neg = false
return z
@@ -822,7 +822,7 @@ func (z *Int) Xor(x, y *Int) *Int {
}
// x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1)
- y1 := nat{}.sub(y.abs, natOne)
+ y1 := nat(nil).sub(y.abs, natOne)
z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne)
z.neg = true // z cannot be zero if only one of x or y is negative
return z
diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go
index 163c662b0bb..aa7c1949549 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 3fa41e7565f..680445dc9a7 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
@@ -447,10 +449,10 @@ func (z nat) mulRange(a, b uint64) nat {
case a == b:
return z.setUint64(a)
case a+1 == b:
- return z.mul(nat{}.setUint64(a), nat{}.setUint64(b))
+ return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b))
}
m := (a + b) / 2
- return z.mul(nat{}.mulRange(a, m), nat{}.mulRange(m+1, b))
+ return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b))
}
// q = (x-r)/y, with 0 <= r < y
@@ -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{}.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.
@@ -1191,11 +1344,11 @@ func (n nat) probablyPrime(reps int) bool {
return false
}
- nm1 := nat{}.sub(n, natOne)
+ nm1 := nat(nil).sub(n, natOne)
// 1<<k * q = nm1;
q, k := nm1.powersOfTwoDecompose()
- nm3 := nat{}.sub(nm1, natTwo)
+ nm3 := nat(nil).sub(nm1, natTwo)
rand := rand.New(rand.NewSource(int64(n[0])))
var x, y, quotient nat
diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go
index 041a6c4a255..e3c6552d9fb 100644
--- a/libgo/go/math/big/nat_test.go
+++ b/libgo/go/math/big/nat_test.go
@@ -16,9 +16,9 @@ var cmpTests = []struct {
r int
}{
{nil, nil, 0},
- {nil, nat{}, 0},
- {nat{}, nil, 0},
- {nat{}, nat{}, 0},
+ {nil, nat(nil), 0},
+ {nat(nil), nil, 0},
+ {nat(nil), nat(nil), 0},
{nat{0}, nat{0}, 0},
{nat{0}, nat{1}, -1},
{nat{1}, nat{0}, 1},
@@ -67,7 +67,7 @@ var prodNN = []argNN{
func TestSet(t *testing.T) {
for _, a := range sumNN {
- z := nat{}.set(a.z)
+ z := nat(nil).set(a.z)
if z.cmp(a.z) != 0 {
t.Errorf("got z = %v; want %v", z, a.z)
}
@@ -129,7 +129,7 @@ var mulRangesN = []struct {
func TestMulRangeN(t *testing.T) {
for i, r := range mulRangesN {
- prod := nat{}.mulRange(r.a, r.b).decimalString()
+ prod := nat(nil).mulRange(r.a, r.b).decimalString()
if prod != r.prod {
t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
}
@@ -175,7 +175,7 @@ func toString(x nat, charset string) string {
s := make([]byte, i)
// don't destroy x
- q := nat{}.set(x)
+ q := nat(nil).set(x)
// convert
for len(q) > 0 {
@@ -212,7 +212,7 @@ func TestString(t *testing.T) {
t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
}
- x, b, err := nat{}.scan(strings.NewReader(a.s), len(a.c))
+ x, b, err := nat(nil).scan(strings.NewReader(a.s), len(a.c))
if x.cmp(a.x) != 0 {
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
}
@@ -271,7 +271,7 @@ var natScanTests = []struct {
func TestScanBase(t *testing.T) {
for _, a := range natScanTests {
r := strings.NewReader(a.s)
- x, b, err := nat{}.scan(r, a.base)
+ x, b, err := nat(nil).scan(r, a.base)
if err == nil && !a.ok {
t.Errorf("scan%+v\n\texpected error", a)
}
@@ -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 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 BenchmarkScanMedium10(b *testing.B) {
- ScanHelper(b, 10, mediumBase, mediumExponent)
-}
-
-func BenchmarkScanMedium16(b *testing.B) {
- ScanHelper(b, 16, mediumBase, mediumExponent)
-}
+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 BenchmarkScanLong2(b *testing.B) {
- ScanHelper(b, 2, longBase, longExponent)
-}
+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 BenchmarkScanLong8(b *testing.B) {
- ScanHelper(b, 8, longBase, longExponent)
-}
-
-func BenchmarkScanLong10(b *testing.B) {
- ScanHelper(b, 10, longBase, longExponent)
-}
+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 BenchmarkScanLong16(b *testing.B) {
- ScanHelper(b, 16, longBase, longExponent)
-}
-
-func ScanHelper(b *testing.B, base int, xv, yv Word) {
+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 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 BenchmarkStringShort16(b *testing.B) {
- StringHelper(b, 16, shortBase, shortExponent)
-}
+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 BenchmarkStringMedium2(b *testing.B) {
- StringHelper(b, 2, mediumBase, mediumExponent)
-}
-
-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)
+ }
+ }
}
}
@@ -651,17 +643,17 @@ var expNNTests = []struct {
func TestExpNN(t *testing.T) {
for i, test := range expNNTests {
- x, _, _ := nat{}.scan(strings.NewReader(test.x), 0)
- y, _, _ := nat{}.scan(strings.NewReader(test.y), 0)
- out, _, _ := nat{}.scan(strings.NewReader(test.out), 0)
+ x, _, _ := nat(nil).scan(strings.NewReader(test.x), 0)
+ y, _, _ := nat(nil).scan(strings.NewReader(test.y), 0)
+ out, _, _ := nat(nil).scan(strings.NewReader(test.out), 0)
var m nat
if len(test.m) > 0 {
- m, _, _ = nat{}.scan(strings.NewReader(test.m), 0)
+ m, _, _ = nat(nil).scan(strings.NewReader(test.m), 0)
}
- z := nat{}.expNN(x, y, m)
+ z := nat(nil).expNN(x, y, m)
if z.cmp(out) != 0 {
t.Errorf("#%d got %v want %v", i, z, out)
}
diff --git a/libgo/go/math/big/rat.go b/libgo/go/math/big/rat.go
index 3a0add32363..adf412485f6 100644
--- a/libgo/go/math/big/rat.go
+++ b/libgo/go/math/big/rat.go
@@ -33,7 +33,7 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
panic("division by zero")
}
if &z.a == b || alias(z.a.abs, babs) {
- babs = nat{}.set(babs) // make a copy
+ babs = nat(nil).set(babs) // make a copy
}
z.a.abs = z.a.abs.set(a.abs)
z.b = z.b.set(babs)
@@ -315,7 +315,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
if _, ok := z.a.SetString(s, 10); !ok {
return nil, false
}
- powTen := nat{}.expNN(natTen, exp.abs, nil)
+ powTen := nat(nil).expNN(natTen, exp.abs, nil)
if exp.neg {
z.b = powTen
z.norm()
@@ -357,23 +357,23 @@ func (z *Rat) FloatString(prec int) string {
}
// z.b != 0
- q, r := nat{}.div(nat{}, z.a.abs, z.b)
+ q, r := nat(nil).div(nat(nil), z.a.abs, z.b)
p := natOne
if prec > 0 {
- p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil)
+ p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
}
r = r.mul(r, p)
- r, r2 := r.div(nat{}, r, z.b)
+ r, r2 := r.div(nat(nil), r, z.b)
// see if we need to round up
r2 = r2.add(r2, r2)
if z.b.cmp(r2) <= 0 {
r = r.add(r, natOne)
if r.cmp(p) >= 0 {
- q = nat{}.add(q, natOne)
- r = nat{}.sub(r, p)
+ q = nat(nil).add(q, natOne)
+ r = nat(nil).sub(r, p)
}
}
diff --git a/libgo/go/math/cbrt.go b/libgo/go/math/cbrt.go
index d2b7e910b84..09edc0eae88 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 babbf645f5c..8de4d7e2ce6 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 e117158fee2..7365d8e7754 100644
--- a/libgo/go/math/gamma.go
+++ b/libgo/go/math/gamma.go
@@ -63,7 +63,7 @@ package math
// Stephen L. Moshier
// moshier@na-net.ornl.gov
-var _P = [...]float64{
+var _gamP = [...]float64{
1.60119522476751861407e-04,
1.19135147006586384913e-03,
1.04213797561761569935e-02,
@@ -72,7 +72,7 @@ var _P = [...]float64{
4.94214826801497100753e-01,
9.99999999999999996796e-01,
}
-var _Q = [...]float64{
+var _gamQ = [...]float64{
-2.31581873324120129819e-05,
5.39605580493303397842e-04,
-4.45641913851797240494e-03,
@@ -82,7 +82,7 @@ var _Q = [...]float64{
7.14304917030273074085e-02,
1.00000000000000000320e+00,
}
-var _S = [...]float64{
+var _gamS = [...]float64{
7.87311395793093628397e-04,
-2.29549961613378126380e-04,
-2.68132617805781232825e-03,
@@ -98,7 +98,7 @@ func stirling(x float64) float64 {
MaxStirling = 143.01608
)
w := 1 / x
- w = 1 + w*((((_S[0]*w+_S[1])*w+_S[2])*w+_S[3])*w+_S[4])
+ w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4])
y := Exp(x)
if x > MaxStirling { // avoid Pow() overflow
v := Pow(x, 0.5*x-0.25)
@@ -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.
@@ -176,8 +175,8 @@ func Gamma(x float64) float64 {
}
x = x - 2
- p = (((((x*_P[0]+_P[1])*x+_P[2])*x+_P[3])*x+_P[4])*x+_P[5])*x + _P[6]
- q = ((((((x*_Q[0]+_Q[1])*x+_Q[2])*x+_Q[3])*x+_Q[4])*x+_Q[5])*x+_Q[6])*x + _Q[7]
+ p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
+ q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
return z * p / q
small:
diff --git a/libgo/go/math/lgamma.go b/libgo/go/math/lgamma.go
index 8f6d7b99fc5..e2bad69dc03 100644
--- a/libgo/go/math/lgamma.go
+++ b/libgo/go/math/lgamma.go
@@ -88,6 +88,81 @@ package math
//
//
+var _lgamA = [...]float64{
+ 7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8
+ 3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD
+ 6.73523010531292681824e-02, // 0x3FB13E001A5562A7
+ 2.05808084325167332806e-02, // 0x3F951322AC92547B
+ 7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8
+ 2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B
+ 1.19270763183362067845e-03, // 0x3F538A94116F3F5D
+ 5.10069792153511336608e-04, // 0x3F40B6C689B99C00
+ 2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D
+ 1.08011567247583939954e-04, // 0x3F1C5088987DFB07
+ 2.52144565451257326939e-05, // 0x3EFA7074428CFA52
+ 4.48640949618915160150e-05, // 0x3F07858E90A45837
+}
+var _lgamR = [...]float64{
+ 1.0, // placeholder
+ 1.39200533467621045958e+00, // 0x3FF645A762C4AB74
+ 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC
+ 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27
+ 1.86459191715652901344e-02, // 0x3F9317EA742ED475
+ 7.77942496381893596434e-04, // 0x3F497DDACA41A95B
+ 7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140
+}
+var _lgamS = [...]float64{
+ -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8
+ 2.14982415960608852501e-01, // 0x3FCB848B36E20878
+ 3.25778796408930981787e-01, // 0x3FD4D98F4F139F59
+ 1.46350472652464452805e-01, // 0x3FC2BB9CBEE5F2F7
+ 2.66422703033638609560e-02, // 0x3F9B481C7E939961
+ 1.84028451407337715652e-03, // 0x3F5E26B67368F239
+ 3.19475326584100867617e-05, // 0x3F00BFECDD17E945
+}
+var _lgamT = [...]float64{
+ 4.83836122723810047042e-01, // 0x3FDEF72BC8EE38A2
+ -1.47587722994593911752e-01, // 0xBFC2E4278DC6C509
+ 6.46249402391333854778e-02, // 0x3FB08B4294D5419B
+ -3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713
+ 1.79706750811820387126e-02, // 0x3F9266E7970AF9EC
+ -1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A
+ 6.10053870246291332635e-03, // 0x3F78FCE0E370E344
+ -3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7
+ 2.25964780900612472250e-03, // 0x3F6282D32E15C915
+ -1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1
+ 8.81081882437654011382e-04, // 0x3F4CDF0CEF61A8E9
+ -5.38595305356740546715e-04, // 0xBF41A6109C73E0EC
+ 3.15632070903625950361e-04, // 0x3F34AF6D6C0EBBF7
+ -3.12754168375120860518e-04, // 0xBF347F24ECC38C38
+ 3.35529192635519073543e-04, // 0x3F35FD3EE8C2D3F4
+}
+var _lgamU = [...]float64{
+ -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8
+ 6.32827064025093366517e-01, // 0x3FE4401E8B005DFF
+ 1.45492250137234768737e+00, // 0x3FF7475CD119BD6F
+ 9.77717527963372745603e-01, // 0x3FEF497644EA8450
+ 2.28963728064692451092e-01, // 0x3FCD4EAEF6010924
+ 1.33810918536787660377e-02, // 0x3F8B678BBF2BAB09
+}
+var _lgamV = [...]float64{
+ 1.0,
+ 2.45597793713041134822e+00, // 0x4003A5D7C2BD619C
+ 2.12848976379893395361e+00, // 0x40010725A42B18F5
+ 7.69285150456672783825e-01, // 0x3FE89DFBE45050AF
+ 1.04222645593369134254e-01, // 0x3FBAAE55D6537C88
+ 3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61
+}
+var _lgamW = [...]float64{
+ 4.18938533204672725052e-01, // 0x3FDACFE390C97D69
+ 8.33333333333329678849e-02, // 0x3FB555555555553B
+ -2.77777777728775536470e-03, // 0xBF66C16C16B02E5C
+ 7.93650558643019558500e-04, // 0x3F4A019F98CF38B6
+ -5.95187557450339963135e-04, // 0xBF4380CB8C0FE741
+ 8.36339918996282139126e-04, // 0x3F4B67BA4CDAD5D1
+ -1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4
+}
+
// Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x).
//
// Special cases are:
@@ -103,68 +178,10 @@ func Lgamma(x float64) (lgamma float64, sign int) {
Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15
Two58 = 1 << 58 // 0x4390000000000000 ~2.8823e+17
Tiny = 1.0 / (1 << 70) // 0x3b90000000000000 ~8.47033e-22
- A0 = 7.72156649015328655494e-02 // 0x3FB3C467E37DB0C8
- A1 = 3.22467033424113591611e-01 // 0x3FD4A34CC4A60FAD
- A2 = 6.73523010531292681824e-02 // 0x3FB13E001A5562A7
- A3 = 2.05808084325167332806e-02 // 0x3F951322AC92547B
- A4 = 7.38555086081402883957e-03 // 0x3F7E404FB68FEFE8
- A5 = 2.89051383673415629091e-03 // 0x3F67ADD8CCB7926B
- A6 = 1.19270763183362067845e-03 // 0x3F538A94116F3F5D
- A7 = 5.10069792153511336608e-04 // 0x3F40B6C689B99C00
- A8 = 2.20862790713908385557e-04 // 0x3F2CF2ECED10E54D
- A9 = 1.08011567247583939954e-04 // 0x3F1C5088987DFB07
- A10 = 2.52144565451257326939e-05 // 0x3EFA7074428CFA52
- A11 = 4.48640949618915160150e-05 // 0x3F07858E90A45837
Tc = 1.46163214496836224576e+00 // 0x3FF762D86356BE3F
Tf = -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42
// Tt = -(tail of Tf)
- Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F
- T0 = 4.83836122723810047042e-01 // 0x3FDEF72BC8EE38A2
- T1 = -1.47587722994593911752e-01 // 0xBFC2E4278DC6C509
- T2 = 6.46249402391333854778e-02 // 0x3FB08B4294D5419B
- T3 = -3.27885410759859649565e-02 // 0xBFA0C9A8DF35B713
- T4 = 1.79706750811820387126e-02 // 0x3F9266E7970AF9EC
- T5 = -1.03142241298341437450e-02 // 0xBF851F9FBA91EC6A
- T6 = 6.10053870246291332635e-03 // 0x3F78FCE0E370E344
- T7 = -3.68452016781138256760e-03 // 0xBF6E2EFFB3E914D7
- T8 = 2.25964780900612472250e-03 // 0x3F6282D32E15C915
- T9 = -1.40346469989232843813e-03 // 0xBF56FE8EBF2D1AF1
- T10 = 8.81081882437654011382e-04 // 0x3F4CDF0CEF61A8E9
- T11 = -5.38595305356740546715e-04 // 0xBF41A6109C73E0EC
- T12 = 3.15632070903625950361e-04 // 0x3F34AF6D6C0EBBF7
- T13 = -3.12754168375120860518e-04 // 0xBF347F24ECC38C38
- T14 = 3.35529192635519073543e-04 // 0x3F35FD3EE8C2D3F4
- U0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8
- U1 = 6.32827064025093366517e-01 // 0x3FE4401E8B005DFF
- U2 = 1.45492250137234768737e+00 // 0x3FF7475CD119BD6F
- U3 = 9.77717527963372745603e-01 // 0x3FEF497644EA8450
- U4 = 2.28963728064692451092e-01 // 0x3FCD4EAEF6010924
- U5 = 1.33810918536787660377e-02 // 0x3F8B678BBF2BAB09
- V1 = 2.45597793713041134822e+00 // 0x4003A5D7C2BD619C
- V2 = 2.12848976379893395361e+00 // 0x40010725A42B18F5
- V3 = 7.69285150456672783825e-01 // 0x3FE89DFBE45050AF
- V4 = 1.04222645593369134254e-01 // 0x3FBAAE55D6537C88
- V5 = 3.21709242282423911810e-03 // 0x3F6A5ABB57D0CF61
- S0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8
- S1 = 2.14982415960608852501e-01 // 0x3FCB848B36E20878
- S2 = 3.25778796408930981787e-01 // 0x3FD4D98F4F139F59
- S3 = 1.46350472652464452805e-01 // 0x3FC2BB9CBEE5F2F7
- S4 = 2.66422703033638609560e-02 // 0x3F9B481C7E939961
- S5 = 1.84028451407337715652e-03 // 0x3F5E26B67368F239
- S6 = 3.19475326584100867617e-05 // 0x3F00BFECDD17E945
- R1 = 1.39200533467621045958e+00 // 0x3FF645A762C4AB74
- R2 = 7.21935547567138069525e-01 // 0x3FE71A1893D3DCDC
- R3 = 1.71933865632803078993e-01 // 0x3FC601EDCCFBDF27
- R4 = 1.86459191715652901344e-02 // 0x3F9317EA742ED475
- R5 = 7.77942496381893596434e-04 // 0x3F497DDACA41A95B
- R6 = 7.32668430744625636189e-06 // 0x3EDEBAF7A5B38140
- W0 = 4.18938533204672725052e-01 // 0x3FDACFE390C97D69
- W1 = 8.33333333333329678849e-02 // 0x3FB555555555553B
- W2 = -2.77777777728775536470e-03 // 0xBF66C16C16B02E5C
- W3 = 7.93650558643019558500e-04 // 0x3F4A019F98CF38B6
- W4 = -5.95187557450339963135e-04 // 0xBF4380CB8C0FE741
- W5 = 8.36339918996282139126e-04 // 0x3F4B67BA4CDAD5D1
- W6 = -1.63092934096575273989e-03 // 0xBF5AB89D0B9E43E4
+ Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F
)
// TODO(rsc): Remove manual inlining of IsNaN, IsInf
// when compiler does it for us
@@ -249,28 +266,28 @@ func Lgamma(x float64) (lgamma float64, sign int) {
switch i {
case 0:
z := y * y
- p1 := A0 + z*(A2+z*(A4+z*(A6+z*(A8+z*A10))))
- p2 := z * (A1 + z*(A3+z*(A5+z*(A7+z*(A9+z*A11)))))
+ p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10]))))
+ p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11])))))
p := y*p1 + p2
lgamma += (p - 0.5*y)
case 1:
z := y * y
w := z * y
- p1 := T0 + w*(T3+w*(T6+w*(T9+w*T12))) // parallel comp
- p2 := T1 + w*(T4+w*(T7+w*(T10+w*T13)))
- p3 := T2 + w*(T5+w*(T8+w*(T11+w*T14)))
+ p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp
+ p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13])))
+ p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14])))
p := z*p1 - (Tt - w*(p2+y*p3))
lgamma += (Tf + p)
case 2:
- p1 := y * (U0 + y*(U1+y*(U2+y*(U3+y*(U4+y*U5)))))
- p2 := 1 + y*(V1+y*(V2+y*(V3+y*(V4+y*V5))))
+ p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5])))))
+ p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5]))))
lgamma += (-0.5*y + p1/p2)
}
case x < 8: // 2 <= x < 8
i := int(x)
y := x - float64(i)
- p := y * (S0 + y*(S1+y*(S2+y*(S3+y*(S4+y*(S5+y*S6))))))
- q := 1 + y*(R1+y*(R2+y*(R3+y*(R4+y*(R5+y*R6)))))
+ p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6]))))))
+ q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6])))))
lgamma = 0.5*y + p/q
z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s)
switch i {
@@ -294,7 +311,7 @@ func Lgamma(x float64) (lgamma float64, sign int) {
t := Log(x)
z := 1 / x
y := z * z
- w := W0 + z*(W1+y*(W2+y*(W3+y*(W4+y*(W5+y*W6)))))
+ w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6])))))
lgamma = (x-0.5)*(t-1) + w
default: // 2**58 <= x <= Inf
lgamma = x * (Log(x) - 1)
diff --git a/libgo/go/math/log1p.go b/libgo/go/math/log1p.go
index c25d73b6641..e8914a1d053 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 315174b7014..34889e0c0af 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 4c1576bead1..f5412fd726f 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/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go
index d9982e5b9c8..ec643c1476f 100644
--- a/libgo/go/mime/multipart/formdata.go
+++ b/libgo/go/mime/multipart/formdata.go
@@ -160,7 +160,7 @@ type sliceReaderAt []byte
func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, error) {
if int(off) >= len(r) || off < 0 {
- return 0, os.EINVAL
+ return 0, io.ErrUnexpectedEOF
}
n := copy(b, r[int(off):])
return n, nil
diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go
index ce72bb5f7f9..e3d968fb81a 100644
--- a/libgo/go/mime/type.go
+++ b/libgo/go/mime/type.go
@@ -6,19 +6,11 @@
package mime
import (
- "bufio"
"fmt"
- "os"
"strings"
"sync"
)
-var typeFiles = []string{
- "/etc/mime.types",
- "/etc/apache2/mime.types",
- "/etc/apache/mime.types",
-}
-
var mimeTypes = map[string]string{
".css": "text/css; charset=utf-8",
".gif": "image/gif",
@@ -33,46 +25,13 @@ var mimeTypes = map[string]string{
var mimeLock sync.RWMutex
-func loadMimeFile(filename string) {
- f, err := os.Open(filename)
- if err != nil {
- return
- }
-
- reader := bufio.NewReader(f)
- for {
- line, err := reader.ReadString('\n')
- if err != nil {
- f.Close()
- return
- }
- fields := strings.Fields(line)
- if len(fields) <= 1 || fields[0][0] == '#' {
- continue
- }
- mimeType := fields[0]
- for _, ext := range fields[1:] {
- if ext[0] == '#' {
- break
- }
- setExtensionType("."+ext, mimeType)
- }
- }
-}
-
-func initMime() {
- for _, filename := range typeFiles {
- loadMimeFile(filename)
- }
-}
-
var once sync.Once
// TypeByExtension returns the MIME type associated with the file extension ext.
// The extension ext should begin with a leading dot, as in ".html".
// When ext has no associated type, TypeByExtension returns "".
//
-// The built-in table is small but is is augmented by the local
+// The built-in table is small but on unix it is augmented by the local
// system's mime.types file(s) if available under one or more of these
// names:
//
@@ -80,6 +39,8 @@ var once sync.Once
// /etc/apache2/mime.types
// /etc/apache/mime.types
//
+// Windows system mime types are extracted from registry.
+//
// Text types have the charset parameter set to "utf-8" by default.
func TypeByExtension(ext string) string {
once.Do(initMime)
diff --git a/libgo/go/mime/type_test.go b/libgo/go/mime/type_test.go
index 976f8534309..07e1cd5daec 100644
--- a/libgo/go/mime/type_test.go
+++ b/libgo/go/mime/type_test.go
@@ -6,15 +6,9 @@ package mime
import "testing"
-var typeTests = map[string]string{
- ".t1": "application/test",
- ".t2": "text/test; charset=utf-8",
- ".png": "image/png",
-}
+var typeTests = initMimeForTests()
func TestTypeByExtension(t *testing.T) {
- typeFiles = []string{"test.types"}
-
for ext, want := range typeTests {
val := TypeByExtension(ext)
if val != want {
diff --git a/libgo/go/mime/type_unix.go b/libgo/go/mime/type_unix.go
new file mode 100644
index 00000000000..45127ba29df
--- /dev/null
+++ b/libgo/go/mime/type_unix.go
@@ -0,0 +1,59 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mime
+
+import (
+ "bufio"
+ "os"
+ "strings"
+)
+
+var typeFiles = []string{
+ "/etc/mime.types",
+ "/etc/apache2/mime.types",
+ "/etc/apache/mime.types",
+}
+
+func loadMimeFile(filename string) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+
+ reader := bufio.NewReader(f)
+ for {
+ line, err := reader.ReadString('\n')
+ if err != nil {
+ f.Close()
+ return
+ }
+ fields := strings.Fields(line)
+ if len(fields) <= 1 || fields[0][0] == '#' {
+ continue
+ }
+ mimeType := fields[0]
+ for _, ext := range fields[1:] {
+ if ext[0] == '#' {
+ break
+ }
+ setExtensionType("."+ext, mimeType)
+ }
+ }
+}
+
+func initMime() {
+ for _, filename := range typeFiles {
+ loadMimeFile(filename)
+ }
+}
+
+func initMimeForTests() map[string]string {
+ typeFiles = []string{"test.types"}
+ return map[string]string{
+ ".t1": "application/test",
+ ".t2": "text/test; charset=utf-8",
+ ".png": "image/png",
+ }
+}
diff --git a/libgo/go/mime/type_windows.go b/libgo/go/mime/type_windows.go
new file mode 100644
index 00000000000..7cf2d3984b1
--- /dev/null
+++ b/libgo/go/mime/type_windows.go
@@ -0,0 +1,61 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mime
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func initMime() {
+ var root syscall.Handle
+ if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`),
+ 0, syscall.KEY_READ, &root) != 0 {
+ return
+ }
+ defer syscall.RegCloseKey(root)
+ var count uint32
+ if syscall.RegQueryInfoKey(root, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil) != 0 {
+ return
+ }
+ var buf [1 << 10]uint16
+ for i := uint32(0); i < count; i++ {
+ n := uint32(len(buf))
+ if syscall.RegEnumKeyEx(root, i, &buf[0], &n, nil, nil, nil, nil) != 0 {
+ continue
+ }
+ ext := syscall.UTF16ToString(buf[:])
+ if len(ext) < 2 || ext[0] != '.' { // looking for extensions only
+ continue
+ }
+ var h syscall.Handle
+ if syscall.RegOpenKeyEx(
+ syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`+ext),
+ 0, syscall.KEY_READ, &h) != 0 {
+ continue
+ }
+ var typ uint32
+ n = uint32(len(buf) * 2) // api expects array of bytes, not uint16
+ if syscall.RegQueryValueEx(
+ h, syscall.StringToUTF16Ptr("Content Type"),
+ nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != 0 {
+ syscall.RegCloseKey(h)
+ continue
+ }
+ syscall.RegCloseKey(h)
+ if typ != syscall.REG_SZ { // null terminated strings only
+ continue
+ }
+ mimeType := syscall.UTF16ToString(buf[:])
+ setExtensionType(ext, mimeType)
+ }
+}
+
+func initMimeForTests() map[string]string {
+ return map[string]string{
+ ".bmp": "image/bmp",
+ ".png": "image/png",
+ }
+}
diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go
index 680d3e70b11..1a0f4063c5a 100644
--- a/libgo/go/net/cgo_unix.go
+++ b/libgo/go/net/cgo_unix.go
@@ -109,7 +109,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
if gerrno == syscall.EAI_NONAME {
str = noSuchHost
} else if gerrno == syscall.EAI_SYSTEM {
- str = syscall.Errstr(syscall.GetErrno())
+ str = syscall.GetErrno().Error()
} else {
str = bytePtrToString(libc_gai_strerror(gerrno))
}
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index bab5f2a9b6e..79a958e3cd0 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 025075de48f..5318c51c9a2 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() {
@@ -278,8 +278,8 @@ func startServer() {
func newFD(fd, family, proto int, net string) (f *netFD, err error) {
onceStartServer.Do(startServer)
- if e := syscall.SetNonblock(fd, true); e != 0 {
- return nil, os.Errno(e)
+ if e := syscall.SetNonblock(fd, true); e != nil {
+ return nil, e
}
f = &netFD{
sysfd: fd,
@@ -306,19 +306,19 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
}
func (fd *netFD) connect(ra syscall.Sockaddr) (err error) {
- e := syscall.Connect(fd.sysfd, ra)
- if e == syscall.EINPROGRESS {
- var errno int
+ err = syscall.Connect(fd.sysfd, ra)
+ if err == syscall.EINPROGRESS {
pollserver.WaitWrite(fd)
- e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
- if errno != 0 {
- return os.NewSyscallError("getsockopt", errno)
+ var e int
+ e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+ if err != nil {
+ return os.NewSyscallError("getsockopt", err)
+ }
+ if e != 0 {
+ err = syscall.Errno(e)
}
}
- if e != 0 {
- return os.Errno(e)
- }
- return nil
+ return err
}
// Add a reference to this fd.
@@ -362,9 +362,9 @@ func (fd *netFD) shutdown(how int) error {
if fd == nil || fd.sysfile == nil {
return os.EINVAL
}
- errno := syscall.Shutdown(fd.sysfd, how)
- if errno != 0 {
- return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)}
+ err := syscall.Shutdown(fd.sysfd, how)
+ if err != nil {
+ return &OpError{"shutdown", fd.net, fd.laddr, err}
}
return nil
}
@@ -377,6 +377,14 @@ func (fd *netFD) CloseWrite() error {
return fd.shutdown(syscall.SHUT_WR)
}
+type timeoutError struct{}
+
+func (e *timeoutError) Error() string { return "i/o timeout" }
+func (e *timeoutError) Timeout() bool { return true }
+func (e *timeoutError) Temporary() bool { return true }
+
+var errTimeout error = &timeoutError{}
+
func (fd *netFD) Read(p []byte) (n int, err error) {
if fd == nil {
return 0, os.EINVAL
@@ -393,24 +401,24 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
} else {
fd.rdeadline = 0
}
- var oserr error
for {
- var errno int
- n, errno = syscall.Read(fd.sysfile.Fd(), p)
- if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
- pollserver.WaitRead(fd)
- continue
+ n, err = syscall.Read(fd.sysfile.Fd(), p)
+ if err == syscall.EAGAIN {
+ if fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd)
+ continue
+ }
+ err = errTimeout
}
- if errno != 0 {
+ if err != nil {
n = 0
- oserr = os.Errno(errno)
- } else if n == 0 && errno == 0 && fd.proto != syscall.SOCK_DGRAM {
+ } else if n == 0 && err == nil && fd.proto != syscall.SOCK_DGRAM {
err = io.EOF
}
break
}
- if oserr != nil {
- err = &OpError{"read", fd.net, fd.raddr, oserr}
+ if err != nil && err != io.EOF {
+ err = &OpError{"read", fd.net, fd.raddr, err}
}
return
}
@@ -428,22 +436,22 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
} else {
fd.rdeadline = 0
}
- var oserr error
for {
- var errno int
- n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0)
- if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
- pollserver.WaitRead(fd)
- continue
+ n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
+ if err == syscall.EAGAIN {
+ if fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd)
+ continue
+ }
+ err = errTimeout
}
- if errno != 0 {
+ if err != nil {
n = 0
- oserr = os.Errno(errno)
}
break
}
- if oserr != nil {
- err = &OpError{"read", fd.net, fd.laddr, oserr}
+ if err != nil {
+ err = &OpError{"read", fd.net, fd.laddr, err}
}
return
}
@@ -461,24 +469,22 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
} else {
fd.rdeadline = 0
}
- var oserr error
for {
- var errno int
- n, oobn, flags, sa, errno = syscall.Recvmsg(fd.sysfd, p, oob, 0)
- if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
- pollserver.WaitRead(fd)
- continue
- }
- if errno != 0 {
- oserr = os.Errno(errno)
+ n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
+ if err == syscall.EAGAIN {
+ if fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd)
+ continue
+ }
+ err = errTimeout
}
- if n == 0 {
- oserr = io.EOF
+ if err == nil && n == 0 {
+ err = io.EOF
}
break
}
- if oserr != nil {
- err = &OpError{"read", fd.net, fd.laddr, oserr}
+ if err != nil && err != io.EOF {
+ err = &OpError{"read", fd.net, fd.laddr, err}
return
}
return
@@ -501,32 +507,34 @@ func (fd *netFD) Write(p []byte) (n int, err error) {
fd.wdeadline = 0
}
nn := 0
- var oserr error
for {
- n, errno := syscall.Write(fd.sysfile.Fd(), p[nn:])
+ var n int
+ n, err = syscall.Write(fd.sysfile.Fd(), p[nn:])
if n > 0 {
nn += n
}
if nn == len(p) {
break
}
- if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
- pollserver.WaitWrite(fd)
- continue
+ if err == syscall.EAGAIN {
+ if fd.wdeadline >= 0 {
+ pollserver.WaitWrite(fd)
+ continue
+ }
+ err = errTimeout
}
- if errno != 0 {
+ if err != nil {
n = 0
- oserr = os.Errno(errno)
break
}
if n == 0 {
- oserr = io.ErrUnexpectedEOF
+ err = io.ErrUnexpectedEOF
break
}
}
- if oserr != nil {
- err = &OpError{"write", fd.net, fd.raddr, oserr}
+ if err != nil {
+ err = &OpError{"write", fd.net, fd.raddr, err}
}
return nn, err
}
@@ -544,22 +552,21 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
} else {
fd.wdeadline = 0
}
- var oserr error
for {
- errno := syscall.Sendto(fd.sysfd, p, 0, sa)
- if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
- pollserver.WaitWrite(fd)
- continue
- }
- if errno != 0 {
- oserr = os.Errno(errno)
+ err = syscall.Sendto(fd.sysfd, p, 0, sa)
+ if err == syscall.EAGAIN {
+ if fd.wdeadline >= 0 {
+ pollserver.WaitWrite(fd)
+ continue
+ }
+ err = errTimeout
}
break
}
- if oserr == nil {
+ if err == nil {
n = len(p)
} else {
- err = &OpError{"write", fd.net, fd.raddr, oserr}
+ err = &OpError{"write", fd.net, fd.raddr, err}
}
return
}
@@ -577,24 +584,22 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
} else {
fd.wdeadline = 0
}
- var oserr error
for {
- var errno int
- errno = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
- if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
- pollserver.WaitWrite(fd)
- continue
- }
- if errno != 0 {
- oserr = os.Errno(errno)
+ err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
+ if err == syscall.EAGAIN {
+ if fd.wdeadline >= 0 {
+ pollserver.WaitWrite(fd)
+ continue
+ }
+ err = errTimeout
}
break
}
- if oserr == nil {
+ if err == nil {
n = len(p)
oobn = len(oob)
} else {
- err = &OpError{"write", fd.net, fd.raddr, oserr}
+ err = &OpError{"write", fd.net, fd.raddr, err}
}
return
}
@@ -615,25 +620,26 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
// See ../syscall/exec.go for description of ForkLock.
// It is okay to hold the lock across syscall.Accept
// because we have put fd.sysfd into non-blocking mode.
- syscall.ForkLock.RLock()
- var s, e int
+ var s int
var rsa syscall.Sockaddr
for {
if fd.closing {
- syscall.ForkLock.RUnlock()
return nil, os.EINVAL
}
- s, rsa, e = syscall.Accept(fd.sysfd)
- if e != syscall.EAGAIN || fd.rdeadline < 0 {
- break
- }
- syscall.ForkLock.RUnlock()
- pollserver.WaitRead(fd)
syscall.ForkLock.RLock()
- }
- if e != 0 {
- syscall.ForkLock.RUnlock()
- return nil, &OpError{"accept", fd.net, fd.laddr, os.Errno(e)}
+ s, rsa, err = syscall.Accept(fd.sysfd)
+ if err != nil {
+ syscall.ForkLock.RUnlock()
+ if err == syscall.EAGAIN {
+ if fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd)
+ continue
+ }
+ err = errTimeout
+ }
+ return nil, &OpError{"accept", fd.net, fd.laddr, err}
+ }
+ break
}
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
@@ -648,19 +654,19 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
}
func (fd *netFD) dup() (f *os.File, err error) {
- ns, e := syscall.Dup(fd.sysfd)
- if e != 0 {
- return nil, &OpError{"dup", fd.net, fd.laddr, os.Errno(e)}
+ ns, err := syscall.Dup(fd.sysfd)
+ if err != nil {
+ return nil, &OpError{"dup", fd.net, fd.laddr, err}
}
// We want blocking mode for the new fd, hence the double negative.
- if e = syscall.SetNonblock(ns, false); e != 0 {
- return nil, &OpError{"setnonblock", fd.net, fd.laddr, os.Errno(e)}
+ if err = syscall.SetNonblock(ns, false); err != nil {
+ return nil, &OpError{"setnonblock", fd.net, fd.laddr, err}
}
return os.NewFile(ns, fd.sysfile.Name()), nil
}
-func closesocket(s int) (errno int) {
+func closesocket(s int) error {
return syscall.Close(s)
}
diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go
index cce74cd6760..8e07833882e 100644
--- a/libgo/go/net/fd_linux.go
+++ b/libgo/go/net/fd_linux.go
@@ -35,12 +35,12 @@ type pollster struct {
func newpollster() (p *pollster, err error) {
p = new(pollster)
- var e int
+ var e error
// The arg to epoll_create is a hint to the kernel
// about the number of FDs we will care about.
// We don't know, and since 2.6.8 the kernel ignores it anyhow.
- if p.epfd, e = syscall.EpollCreate(16); e != 0 {
+ if p.epfd, e = syscall.EpollCreate(16); e != nil {
return nil, os.NewSyscallError("epoll_create", e)
}
p.events = make(map[int]uint32)
@@ -68,7 +68,7 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
} else {
op = syscall.EPOLL_CTL_ADD
}
- if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 {
+ if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != nil {
return false, os.NewSyscallError("epoll_ctl", e)
}
p.events[fd] = p.ctlEvent.Events
@@ -97,13 +97,13 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
if int32(events)&^syscall.EPOLLONESHOT != 0 {
p.ctlEvent.Fd = int32(fd)
p.ctlEvent.Events = events
- if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 {
- print("Epoll modify fd=", fd, ": ", os.Errno(e).Error(), "\n")
+ if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != nil {
+ print("Epoll modify fd=", fd, ": ", e.Error(), "\n")
}
p.events[fd] = events
} else {
- if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
- print("Epoll delete fd=", fd, ": ", os.Errno(e).Error(), "\n")
+ if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != nil {
+ print("Epoll delete fd=", fd, ": ", e.Error(), "\n")
}
delete(p.events, fd)
}
@@ -141,7 +141,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro
n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec)
s.Lock()
- if e != 0 {
+ if e != nil {
if e == syscall.EAGAIN || e == syscall.EINTR {
continue
}
diff --git a/libgo/go/net/fd_openbsd.go b/libgo/go/net/fd_openbsd.go
index f61008a2fe1..e52ac356b9f 100644
--- a/libgo/go/net/fd_openbsd.go
+++ b/libgo/go/net/fd_openbsd.go
@@ -23,9 +23,8 @@ type pollster struct {
func newpollster() (p *pollster, err error) {
p = new(pollster)
- var e int
- if p.kq, e = syscall.Kqueue(); e != 0 {
- return nil, os.NewSyscallError("kqueue", e)
+ if p.kq, err = syscall.Kqueue(); err != nil {
+ return nil, os.NewSyscallError("kqueue", err)
}
p.events = p.eventbuf[0:0]
return p, nil
@@ -50,14 +49,14 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
syscall.SetKevent(ev, fd, kmode, flags)
n, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- if e != 0 {
+ if e != nil {
return false, os.NewSyscallError("kevent", e)
}
if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
return false, os.NewSyscallError("kqueue phase error", e)
}
if ev.Data != 0 {
- return false, os.Errno(int(ev.Data))
+ return false, syscall.Errno(int(ev.Data))
}
return false, nil
}
@@ -91,7 +90,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro
nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
s.Lock()
- if e != 0 {
+ if e != nil {
if e == syscall.EINTR {
continue
}
diff --git a/libgo/go/net/fd_select.go b/libgo/go/net/fd_select.go
index b9544e96c75..39c8f2777b2 100644
--- a/libgo/go/net/fd_select.go
+++ b/libgo/go/net/fd_select.go
@@ -85,7 +85,8 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro
timeout = &tv
}
- var n, e int
+ var n int
+ var e error
var tmpReadFds, tmpWriteFds syscall.FdSet
for {
// Temporary syscall.FdSet's into which the values are copied
@@ -101,7 +102,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro
break
}
}
- if e != 0 {
+ if e != nil {
return -1, 0, os.NewSyscallError("select", e)
}
if n == 0 {
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index ce228e91edb..264b918c57d 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -26,11 +26,11 @@ func init() {
var d syscall.WSAData
e := syscall.WSAStartup(uint32(0x202), &d)
if e != 0 {
- initErr = os.NewSyscallError("WSAStartup", e)
+ initErr = os.NewSyscallError("WSAStartup", syscall.Errno(e))
}
}
-func closesocket(s syscall.Handle) (errno int) {
+func closesocket(s syscall.Handle) (err error) {
return syscall.Closesocket(s)
}
@@ -38,13 +38,13 @@ func closesocket(s syscall.Handle) (errno int) {
type anOpIface interface {
Op() *anOp
Name() string
- Submit() (errno int)
+ Submit() (err error)
}
// IO completion result parameters.
type ioResult struct {
qty uint32
- err int
+ err error
}
// anOp implements functionality common to all io operations.
@@ -54,7 +54,7 @@ type anOp struct {
o syscall.Overlapped
resultc chan ioResult
- errnoc chan int
+ errnoc chan error
fd *netFD
}
@@ -71,7 +71,7 @@ func (o *anOp) Init(fd *netFD, mode int) {
}
o.resultc = fd.resultc[i]
if fd.errnoc[i] == nil {
- fd.errnoc[i] = make(chan int)
+ fd.errnoc[i] = make(chan error)
}
o.errnoc = fd.errnoc[i]
}
@@ -111,14 +111,14 @@ func (s *resultSrv) Run() {
for {
r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
switch {
- case r.err == 0:
+ case r.err == nil:
// Dequeued successfully completed io packet.
- case r.err == syscall.WAIT_TIMEOUT && o == nil:
+ case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:
// Wait has timed out (should not happen now, but might be used in the future).
panic("GetQueuedCompletionStatus timed out")
case o == nil:
// Failed to dequeue anything -> report the error.
- panic("GetQueuedCompletionStatus failed " + syscall.Errstr(r.err))
+ panic("GetQueuedCompletionStatus failed " + r.err.Error())
default:
// Dequeued failed io packet.
}
@@ -153,7 +153,7 @@ func (s *ioSrv) ProcessRemoteIO() {
// inline, or, if timeouts are employed, passes the request onto
// a special goroutine and waits for completion or cancels request.
func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
- var e int
+ var e error
o := oi.Op()
if deadline_delta > 0 {
// Send request to a special dedicated thread,
@@ -164,19 +164,20 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
e = oi.Submit()
}
switch e {
- case 0:
+ case nil:
// IO completed immediately, but we need to get our completion message anyway.
case syscall.ERROR_IO_PENDING:
// IO started, and we have to wait for its completion.
default:
- return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)}
+ 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
@@ -187,8 +188,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
} else {
r = <-o.resultc
}
- if r.err != 0 {
- err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(r.err)}
+ if r.err != nil {
+ err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}
}
return int(r.qty), err
}
@@ -200,10 +201,10 @@ var onceStartServer sync.Once
func startServer() {
resultsrv = new(resultSrv)
- var errno int
- resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1)
- if errno != 0 {
- panic("CreateIoCompletionPort failed " + syscall.Errstr(errno))
+ var err error
+ resultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1)
+ if err != nil {
+ panic("CreateIoCompletionPort: " + err.Error())
}
go resultsrv.Run()
@@ -228,7 +229,7 @@ type netFD struct {
laddr Addr
raddr Addr
resultc [2]chan ioResult // read/write completion results
- errnoc [2]chan int // read/write submit or cancel operation errors
+ errnoc [2]chan error // read/write submit or cancel operation errors
// owned by client
rdeadline_delta int64
@@ -256,8 +257,8 @@ func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err erro
}
onceStartServer.Do(startServer)
// Associate our socket with resultsrv.iocp.
- if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 {
- return nil, os.Errno(e)
+ if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != nil {
+ return nil, e
}
return allocFD(fd, family, proto, net), nil
}
@@ -268,11 +269,7 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
}
func (fd *netFD) connect(ra syscall.Sockaddr) (err error) {
- e := syscall.Connect(fd.sysfd, ra)
- if e != 0 {
- return os.Errno(e)
- }
- return nil
+ return syscall.Connect(fd.sysfd, ra)
}
// Add a reference to this fd.
@@ -317,9 +314,9 @@ func (fd *netFD) shutdown(how int) error {
if fd == nil || fd.sysfd == syscall.InvalidHandle {
return os.EINVAL
}
- errno := syscall.Shutdown(fd.sysfd, how)
- if errno != 0 {
- return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)}
+ err := syscall.Shutdown(fd.sysfd, how)
+ if err != nil {
+ return &OpError{"shutdown", fd.net, fd.laddr, err}
}
return nil
}
@@ -338,7 +335,7 @@ type readOp struct {
bufOp
}
-func (o *readOp) Submit() (errno int) {
+func (o *readOp) Submit() (err error) {
var d, f uint32
return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
}
@@ -375,7 +372,7 @@ type readFromOp struct {
rsan int32
}
-func (o *readFromOp) Submit() (errno int) {
+func (o *readFromOp) Submit() (err error) {
var d, f uint32
return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil)
}
@@ -415,7 +412,7 @@ type writeOp struct {
bufOp
}
-func (o *writeOp) Submit() (errno int) {
+func (o *writeOp) Submit() (err error) {
var d uint32
return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil)
}
@@ -447,7 +444,7 @@ type writeToOp struct {
sa syscall.Sockaddr
}
-func (o *writeToOp) Submit() (errno int) {
+func (o *writeToOp) Submit() (err error) {
var d uint32
return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil)
}
@@ -484,7 +481,7 @@ type acceptOp struct {
attrs [2]syscall.RawSockaddrAny // space for local and remote address only
}
-func (o *acceptOp) Submit() (errno int) {
+func (o *acceptOp) Submit() (err error) {
var d uint32
l := uint32(unsafe.Sizeof(o.attrs[0]))
return syscall.AcceptEx(o.fd.sysfd, o.newsock,
@@ -506,17 +503,17 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
// See ../syscall/exec.go for description of ForkLock.
syscall.ForkLock.RLock()
s, e := syscall.Socket(fd.family, fd.proto, 0)
- if e != 0 {
+ if e != nil {
syscall.ForkLock.RUnlock()
- return nil, os.Errno(e)
+ return nil, e
}
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
// Associate our new socket with IOCP.
onceStartServer.Do(startServer)
- if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 {
- return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)}
+ if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != nil {
+ return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, e}
}
// Submit accept request.
@@ -531,9 +528,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
// Inherit properties of the listening socket.
e = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
- if e != 0 {
+ if e != nil {
closesocket(s)
- return nil, err
+ return nil, e
}
// Get local and peer addr out of AcceptEx buffer.
diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go
index 0ad869dbd57..bf8cd9dae04 100644
--- a/libgo/go/net/file.go
+++ b/libgo/go/net/file.go
@@ -13,12 +13,12 @@ import (
func newFileFD(f *os.File) (nfd *netFD, err error) {
fd, errno := syscall.Dup(f.Fd())
- if errno != 0 {
+ if errno != nil {
return nil, os.NewSyscallError("dup", errno)
}
proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
- if errno != 0 {
+ if errno != nil {
return nil, os.NewSyscallError("getsockopt", errno)
}
diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go
index d75e9e038a6..e6674ba3415 100644
--- a/libgo/go/net/hosts.go
+++ b/libgo/go/net/hosts.go
@@ -7,11 +7,11 @@
package net
import (
- "os"
"sync"
+ "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, _, _ := os.Time()
+ 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, _, _ = os.Time()
+ 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 2bc913a1696..849cb008b76 100644
--- a/libgo/go/net/http/cgi/host_test.go
+++ b/libgo/go/net/http/cgi/host_test.go
@@ -363,14 +363,13 @@ func TestCopyError(t *testing.T) {
}
conn.Close()
- if tries := 0; childRunning() {
- for tries < 15 && childRunning() {
- time.Sleep(50e6 * int64(tries))
- tries++
- }
- if childRunning() {
- t.Fatalf("post-conn.Close, expected child to be gone")
- }
+ tries := 0
+ for tries < 15 && childRunning() {
+ time.Sleep(50 * time.Millisecond * time.Duration(tries))
+ tries++
+ }
+ if childRunning() {
+ t.Fatalf("post-conn.Close, expected child to be gone")
}
}
diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/chunked.go
index b012dd18496..74c41aabd41 100644
--- a/libgo/go/net/http/chunked.go
+++ b/libgo/go/net/http/chunked.go
@@ -2,20 +2,137 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The wire protocol for HTTP's "chunked" Transfer-Encoding.
+
+// This code is duplicated in httputil/chunked.go.
+// Please make any changes in both files.
+
package http
import (
"bufio"
+ "bytes"
+ "errors"
"io"
"strconv"
)
+const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
+
+var ErrLineTooLong = errors.New("header line too long")
+
+// newChunkedReader returns a new chunkedReader that translates the data read from r
+// out of HTTP "chunked" format before returning it.
+// The chunkedReader returns io.EOF when the final 0-length chunk is read.
+//
+// newChunkedReader is not needed by normal applications. The http package
+// automatically decodes chunking when reading response bodies.
+func newChunkedReader(r io.Reader) io.Reader {
+ br, ok := r.(*bufio.Reader)
+ if !ok {
+ br = bufio.NewReader(r)
+ }
+ return &chunkedReader{r: br}
+}
+
+type chunkedReader struct {
+ r *bufio.Reader
+ n uint64 // unread bytes in chunk
+ err error
+}
+
+func (cr *chunkedReader) beginChunk() {
+ // chunk-size CRLF
+ var line string
+ line, cr.err = readLine(cr.r)
+ if cr.err != nil {
+ return
+ }
+ cr.n, cr.err = strconv.Btoui64(line, 16)
+ if cr.err != nil {
+ return
+ }
+ if cr.n == 0 {
+ cr.err = io.EOF
+ }
+}
+
+func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
+ if cr.err != nil {
+ return 0, cr.err
+ }
+ if cr.n == 0 {
+ cr.beginChunk()
+ if cr.err != nil {
+ return 0, cr.err
+ }
+ }
+ if uint64(len(b)) > cr.n {
+ b = b[0:cr.n]
+ }
+ n, cr.err = cr.r.Read(b)
+ cr.n -= uint64(n)
+ if cr.n == 0 && cr.err == nil {
+ // end of chunk (CRLF)
+ b := make([]byte, 2)
+ if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
+ if b[0] != '\r' || b[1] != '\n' {
+ cr.err = errors.New("malformed chunked encoding")
+ }
+ }
+ }
+ return n, cr.err
+}
+
+// Read a line of bytes (up to \n) from b.
+// Give up if the line exceeds maxLineLength.
+// The returned bytes are a pointer into storage in
+// the bufio, so they are only valid until the next bufio read.
+func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+ if p, err = b.ReadSlice('\n'); err != nil {
+ // We always know when EOF is coming.
+ // If the caller asked for a line, there should be a line.
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ } else if err == bufio.ErrBufferFull {
+ err = ErrLineTooLong
+ }
+ return nil, err
+ }
+ if len(p) >= maxLineLength {
+ return nil, ErrLineTooLong
+ }
+
+ // Chop off trailing white space.
+ p = bytes.TrimRight(p, " \r\t\n")
+
+ return p, nil
+}
+
+// readLineBytes, but convert the bytes into a string.
+func readLine(b *bufio.Reader) (s string, err error) {
+ p, e := readLineBytes(b)
+ if e != nil {
+ return "", e
+ }
+ return string(p), nil
+}
+
+// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
+// "chunked" format before writing them to w. Closing the returned chunkedWriter
+// sends the final 0-length chunk that marks the end of the stream.
+//
+// newChunkedWriter is not needed by normal applications. The http
+// package adds chunking automatically if handlers don't set a
+// Content-Length header. Using newChunkedWriter inside a handler
+// would result in double chunking or chunking with a Content-Length
+// length, both of which are wrong.
func newChunkedWriter(w io.Writer) io.WriteCloser {
return &chunkedWriter{w}
}
-// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer
-// Encoding wire format to the underlying Wire writer.
+// Writing to chunkedWriter translates to writing in HTTP chunked Transfer
+// Encoding wire format to the underlying Wire chunkedWriter.
type chunkedWriter struct {
Wire io.Writer
}
@@ -51,7 +168,3 @@ func (cw *chunkedWriter) Close() error {
_, err := io.WriteString(cw.Wire, "0\r\n")
return err
}
-
-func newChunkedReader(r *bufio.Reader) io.Reader {
- return &chunkedReader{r: r}
-}
diff --git a/libgo/go/net/http/chunked_test.go b/libgo/go/net/http/chunked_test.go
new file mode 100644
index 00000000000..b77ee2ff26c
--- /dev/null
+++ b/libgo/go/net/http/chunked_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.
+
+// This code is duplicated in httputil/chunked_test.go.
+// Please make any changes in both files.
+
+package http
+
+import (
+ "bytes"
+ "io/ioutil"
+ "testing"
+)
+
+func TestChunk(t *testing.T) {
+ var b bytes.Buffer
+
+ w := newChunkedWriter(&b)
+ const chunk1 = "hello, "
+ const chunk2 = "world! 0123456789abcdef"
+ w.Write([]byte(chunk1))
+ w.Write([]byte(chunk2))
+ w.Close()
+
+ if g, e := b.String(), "7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n"; g != e {
+ t.Fatalf("chunk writer wrote %q; want %q", g, e)
+ }
+
+ r := newChunkedReader(&b)
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Logf(`data: "%s"`, data)
+ t.Fatalf("ReadAll from reader: %v", err)
+ }
+ if g, e := string(data), chunk1+chunk2; g != e {
+ t.Errorf("chunk reader read %q; want %q", g, e)
+ }
+}
diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go
index d224380298c..57a9dd9574d 100644
--- a/libgo/go/net/http/client_test.go
+++ b/libgo/go/net/http/client_test.go
@@ -26,6 +26,31 @@ var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})
+// pedanticReadAll works like ioutil.ReadAll but additionally
+// verifies that r obeys the documented io.Reader contract.
+func pedanticReadAll(r io.Reader) (b []byte, err error) {
+ var bufa [64]byte
+ buf := bufa[:]
+ for {
+ n, err := r.Read(buf)
+ if n == 0 && err == nil {
+ return nil, fmt.Errorf("Read: n=0 with err=nil")
+ }
+ b = append(b, buf[:n]...)
+ if err == io.EOF {
+ n, err := r.Read(buf)
+ if n != 0 || err != io.EOF {
+ return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err)
+ }
+ return b, nil
+ }
+ if err != nil {
+ return b, err
+ }
+ }
+ panic("unreachable")
+}
+
func TestClient(t *testing.T) {
ts := httptest.NewServer(robotsTxtHandler)
defer ts.Close()
@@ -33,7 +58,7 @@ func TestClient(t *testing.T) {
r, err := Get(ts.URL)
var b []byte
if err == nil {
- b, err = ioutil.ReadAll(r.Body)
+ b, err = pedanticReadAll(r.Body)
r.Body.Close()
}
if err != nil {
diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go
index 69350143248..cad852242e2 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 24adf202981..26bff93f643 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 3fe658641f8..13640ca85ee 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 7b563951ccf..c94b9a7b249 100644
--- a/libgo/go/net/http/fcgi/child.go
+++ b/libgo/go/net/http/fcgi/child.go
@@ -7,6 +7,7 @@ package fcgi
// This file implements FastCGI from the perspective of a child process.
import (
+ "errors"
"fmt"
"io"
"net"
@@ -102,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))
@@ -123,89 +124,101 @@ func (r *response) Close() error {
}
type child struct {
- conn *conn
- handler http.Handler
+ conn *conn
+ handler http.Handler
+ requests map[uint16]*request // keyed by request ID
}
-func newChild(rwc net.Conn, handler http.Handler) *child {
- return &child{newConn(rwc), handler}
+func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
+ return &child{
+ conn: newConn(rwc),
+ handler: handler,
+ requests: make(map[uint16]*request),
+ }
}
func (c *child) serve() {
- requests := map[uint16]*request{}
defer c.conn.Close()
var rec record
- var br beginRequest
for {
if err := rec.read(c.conn.rwc); err != nil {
return
}
-
- req, ok := requests[rec.h.Id]
- if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
- // The spec says to ignore unknown request IDs.
- continue
- }
- if ok && rec.h.Type == typeBeginRequest {
- // The server is trying to begin a request with the same ID
- // as an in-progress request. This is an error.
+ if err := c.handleRecord(&rec); err != nil {
return
}
+ }
+}
- switch rec.h.Type {
- case typeBeginRequest:
- if err := br.read(rec.content()); err != nil {
- return
- }
- if br.role != roleResponder {
- c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
- break
- }
- requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
- case typeParams:
- // NOTE(eds): Technically a key-value pair can straddle the boundary
- // between two packets. We buffer until we've received all parameters.
- if len(rec.content()) > 0 {
- req.rawParams = append(req.rawParams, rec.content()...)
- break
- }
- req.parseParams()
- case typeStdin:
- content := rec.content()
- if req.pw == nil {
- var body io.ReadCloser
- if len(content) > 0 {
- // body could be an io.LimitReader, but it shouldn't matter
- // as long as both sides are behaving.
- body, req.pw = io.Pipe()
- }
- go c.serveRequest(req, body)
- }
+var errCloseConn = errors.New("fcgi: connection should be closed")
+
+func (c *child) handleRecord(rec *record) error {
+ req, ok := c.requests[rec.h.Id]
+ if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
+ // The spec says to ignore unknown request IDs.
+ return nil
+ }
+ if ok && rec.h.Type == typeBeginRequest {
+ // The server is trying to begin a request with the same ID
+ // as an in-progress request. This is an error.
+ return errors.New("fcgi: received ID that is already in-flight")
+ }
+
+ switch rec.h.Type {
+ case typeBeginRequest:
+ var br beginRequest
+ if err := br.read(rec.content()); err != nil {
+ return err
+ }
+ if br.role != roleResponder {
+ c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
+ return nil
+ }
+ c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
+ case typeParams:
+ // NOTE(eds): Technically a key-value pair can straddle the boundary
+ // between two packets. We buffer until we've received all parameters.
+ if len(rec.content()) > 0 {
+ req.rawParams = append(req.rawParams, rec.content()...)
+ return nil
+ }
+ req.parseParams()
+ case typeStdin:
+ content := rec.content()
+ if req.pw == nil {
+ var body io.ReadCloser
if len(content) > 0 {
- // TODO(eds): This blocks until the handler reads from the pipe.
- // If the handler takes a long time, it might be a problem.
- req.pw.Write(content)
- } else if req.pw != nil {
- req.pw.Close()
- }
- case typeGetValues:
- values := map[string]string{"FCGI_MPXS_CONNS": "1"}
- c.conn.writePairs(0, typeGetValuesResult, values)
- case typeData:
- // If the filter role is implemented, read the data stream here.
- case typeAbortRequest:
- delete(requests, rec.h.Id)
- c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
- if !req.keepConn {
- // connection will close upon return
- return
+ // body could be an io.LimitReader, but it shouldn't matter
+ // as long as both sides are behaving.
+ body, req.pw = io.Pipe()
}
- default:
- b := make([]byte, 8)
- b[0] = rec.h.Type
- c.conn.writeRecord(typeUnknownType, 0, b)
+ go c.serveRequest(req, body)
+ }
+ if len(content) > 0 {
+ // TODO(eds): This blocks until the handler reads from the pipe.
+ // If the handler takes a long time, it might be a problem.
+ req.pw.Write(content)
+ } else if req.pw != nil {
+ req.pw.Close()
+ }
+ case typeGetValues:
+ values := map[string]string{"FCGI_MPXS_CONNS": "1"}
+ c.conn.writePairs(typeGetValuesResult, 0, values)
+ case typeData:
+ // If the filter role is implemented, read the data stream here.
+ case typeAbortRequest:
+ delete(c.requests, rec.h.Id)
+ c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
+ if !req.keepConn {
+ // connection will close upon return
+ return errCloseConn
}
+ default:
+ b := make([]byte, 8)
+ b[0] = byte(rec.h.Type)
+ c.conn.writeRecord(typeUnknownType, 0, b)
}
+ return nil
}
func (c *child) serveRequest(req *request, body io.ReadCloser) {
diff --git a/libgo/go/net/http/fcgi/fcgi.go b/libgo/go/net/http/fcgi/fcgi.go
index 70cf781e228..d35aa84d229 100644
--- a/libgo/go/net/http/fcgi/fcgi.go
+++ b/libgo/go/net/http/fcgi/fcgi.go
@@ -19,19 +19,22 @@ import (
"sync"
)
+// recType is a record type, as defined by
+// http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S8
+type recType uint8
+
const (
- // Packet Types
- typeBeginRequest = iota + 1
- typeAbortRequest
- typeEndRequest
- typeParams
- typeStdin
- typeStdout
- typeStderr
- typeData
- typeGetValues
- typeGetValuesResult
- typeUnknownType
+ typeBeginRequest recType = 1
+ typeAbortRequest recType = 2
+ typeEndRequest recType = 3
+ typeParams recType = 4
+ typeStdin recType = 5
+ typeStdout recType = 6
+ typeStderr recType = 7
+ typeData recType = 8
+ typeGetValues recType = 9
+ typeGetValuesResult recType = 10
+ typeUnknownType recType = 11
)
// keep the connection between web-server and responder open after request
@@ -59,7 +62,7 @@ const headerLen = 8
type header struct {
Version uint8
- Type uint8
+ Type recType
Id uint16
ContentLength uint16
PaddingLength uint8
@@ -85,7 +88,7 @@ func (br *beginRequest) read(content []byte) error {
// not synchronized because we don't care what the contents are
var pad [maxPad]byte
-func (h *header) init(recType uint8, reqId uint16, contentLength int) {
+func (h *header) init(recType recType, reqId uint16, contentLength int) {
h.Version = 1
h.Type = recType
h.Id = reqId
@@ -137,7 +140,7 @@ func (r *record) content() []byte {
}
// writeRecord writes and sends a single record.
-func (c *conn) writeRecord(recType uint8, reqId uint16, b []byte) error {
+func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error {
c.mutex.Lock()
defer c.mutex.Unlock()
c.buf.Reset()
@@ -167,12 +170,12 @@ func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8
return c.writeRecord(typeEndRequest, reqId, b)
}
-func (c *conn) writePairs(recType uint8, reqId uint16, pairs map[string]string) error {
+func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error {
w := newWriter(c, recType, reqId)
b := make([]byte, 8)
for k, v := range pairs {
n := encodeSize(b, uint32(len(k)))
- n += encodeSize(b[n:], uint32(len(k)))
+ n += encodeSize(b[n:], uint32(len(v)))
if _, err := w.Write(b[:n]); err != nil {
return err
}
@@ -235,7 +238,7 @@ func (w *bufWriter) Close() error {
return w.closer.Close()
}
-func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter {
+func newWriter(c *conn, recType recType, reqId uint16) *bufWriter {
s := &streamWriter{c: c, recType: recType, reqId: reqId}
w, _ := bufio.NewWriterSize(s, maxWrite)
return &bufWriter{s, w}
@@ -245,7 +248,7 @@ func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter {
// It only writes maxWrite bytes at a time.
type streamWriter struct {
c *conn
- recType uint8
+ recType recType
reqId uint16
}
diff --git a/libgo/go/net/http/fcgi/fcgi_test.go b/libgo/go/net/http/fcgi/fcgi_test.go
index e42f8efd658..6c7e1a9ce83 100644
--- a/libgo/go/net/http/fcgi/fcgi_test.go
+++ b/libgo/go/net/http/fcgi/fcgi_test.go
@@ -6,6 +6,7 @@ package fcgi
import (
"bytes"
+ "errors"
"io"
"testing"
)
@@ -40,25 +41,25 @@ func TestSize(t *testing.T) {
var streamTests = []struct {
desc string
- recType uint8
+ recType recType
reqId uint16
content []byte
raw []byte
}{
{"single record", typeStdout, 1, nil,
- []byte{1, typeStdout, 0, 1, 0, 0, 0, 0},
+ []byte{1, byte(typeStdout), 0, 1, 0, 0, 0, 0},
},
// this data will have to be split into two records
{"two records", typeStdin, 300, make([]byte, 66000),
bytes.Join([][]byte{
// header for the first record
- {1, typeStdin, 0x01, 0x2C, 0xFF, 0xFF, 1, 0},
+ {1, byte(typeStdin), 0x01, 0x2C, 0xFF, 0xFF, 1, 0},
make([]byte, 65536),
// header for the second
- {1, typeStdin, 0x01, 0x2C, 0x01, 0xD1, 7, 0},
+ {1, byte(typeStdin), 0x01, 0x2C, 0x01, 0xD1, 7, 0},
make([]byte, 472),
// header for the empty record
- {1, typeStdin, 0x01, 0x2C, 0, 0, 0, 0},
+ {1, byte(typeStdin), 0x01, 0x2C, 0, 0, 0, 0},
},
nil),
},
@@ -111,3 +112,39 @@ outer:
}
}
}
+
+type writeOnlyConn struct {
+ buf []byte
+}
+
+func (c *writeOnlyConn) Write(p []byte) (int, error) {
+ c.buf = append(c.buf, p...)
+ return len(p), nil
+}
+
+func (c *writeOnlyConn) Read(p []byte) (int, error) {
+ return 0, errors.New("conn is write-only")
+}
+
+func (c *writeOnlyConn) Close() error {
+ return nil
+}
+
+func TestGetValues(t *testing.T) {
+ var rec record
+ rec.h.Type = typeGetValues
+
+ wc := new(writeOnlyConn)
+ c := newChild(wc, nil)
+ err := c.handleRecord(&rec)
+ if err != nil {
+ t.Fatalf("handleRecord: %v", err)
+ }
+
+ const want = "\x01\n\x00\x00\x00\x12\x06\x00" +
+ "\x0f\x01FCGI_MPXS_CONNS1" +
+ "\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x00\x00\x00"
+ if got := string(wc.buf); got != want {
+ t.Errorf(" got: %q\nwant: %q\n", got, want)
+ }
+}
diff --git a/libgo/go/net/http/filetransport_test.go b/libgo/go/net/http/filetransport_test.go
index 265a3b903e9..039926b5382 100644
--- a/libgo/go/net/http/filetransport_test.go
+++ b/libgo/go/net/http/filetransport_test.go
@@ -7,6 +7,7 @@ package http_test
import (
"io/ioutil"
"net/http"
+ "os"
"path/filepath"
"testing"
)
@@ -28,6 +29,8 @@ func TestFileTransport(t *testing.T) {
fname := filepath.Join(dname, "foo.txt")
err = ioutil.WriteFile(fname, []byte("Bar"), 0644)
check("WriteFile", err)
+ defer os.Remove(dname)
+ defer os.Remove(fname)
tr := &http.Transport{}
tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname)))
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go
index 5f91ff5cbf6..70e7849f167 100644
--- a/libgo/go/net/http/fs.go
+++ b/libgo/go/net/http/fs.go
@@ -22,13 +22,19 @@ import (
// A Dir implements http.FileSystem using the native file
// system restricted to a specific directory tree.
+//
+// An empty Dir is treated as ".".
type Dir string
func (d Dir) Open(name string) (File, error) {
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 {
return nil, errors.New("http: invalid character in file path")
}
- f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name))))
+ dir := string(d)
+ if dir == "" {
+ dir = "."
+ }
+ f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
if err != nil {
return nil, err
}
@@ -46,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)
@@ -87,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
@@ -129,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
@@ -142,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 {
@@ -163,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.
@@ -209,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 e1a784c1f6d..976ee75c7dd 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")
@@ -208,6 +208,20 @@ func TestDirJoin(t *testing.T) {
test(Dir("/etc/hosts"), "../")
}
+func TestEmptyDirOpenCWD(t *testing.T) {
+ test := func(d Dir) {
+ name := "fs_test.go"
+ f, err := d.Open(name)
+ if err != nil {
+ t.Fatalf("open of %s: %v", name, err)
+ }
+ defer f.Close()
+ }
+ test(Dir(""))
+ test(Dir("."))
+ test(Dir("./"))
+}
+
func TestServeFileContentType(t *testing.T) {
const ctype = "icecream/chocolate"
override := false
@@ -247,6 +261,20 @@ func TestServeFileMimeType(t *testing.T) {
}
}
+func TestServeFileFromCWD(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ ServeFile(w, r, "fs_test.go")
+ }))
+ defer ts.Close()
+ r, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if r.StatusCode != 200 {
+ t.Fatalf("expected 200 OK, got %s", r.Status)
+ }
+}
+
func TestServeFileWithContentEncoding(t *testing.T) {
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Encoding", "foo")
diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go
index f09e826d9c9..5b02e143d4a 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/chunked.go b/libgo/go/net/http/httputil/chunked.go
index 34e47c796c1..69bcc0e816f 100644
--- a/libgo/go/net/http/httputil/chunked.go
+++ b/libgo/go/net/http/httputil/chunked.go
@@ -2,18 +2,126 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The wire protocol for HTTP's "chunked" Transfer-Encoding.
+
+// This code is a duplicate of ../chunked.go with these edits:
+// s/newChunked/NewChunked/g
+// s/package http/package httputil/
+// Please make any changes in both files.
+
package httputil
import (
"bufio"
+ "bytes"
+ "errors"
"io"
- "net/http"
"strconv"
- "strings"
)
-// NewChunkedWriter returns a new writer that translates writes into HTTP
-// "chunked" format before writing them to w. Closing the returned writer
+const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
+
+var ErrLineTooLong = errors.New("header line too long")
+
+// NewChunkedReader returns a new chunkedReader that translates the data read from r
+// out of HTTP "chunked" format before returning it.
+// The chunkedReader returns io.EOF when the final 0-length chunk is read.
+//
+// NewChunkedReader is not needed by normal applications. The http package
+// automatically decodes chunking when reading response bodies.
+func NewChunkedReader(r io.Reader) io.Reader {
+ br, ok := r.(*bufio.Reader)
+ if !ok {
+ br = bufio.NewReader(r)
+ }
+ return &chunkedReader{r: br}
+}
+
+type chunkedReader struct {
+ r *bufio.Reader
+ n uint64 // unread bytes in chunk
+ err error
+}
+
+func (cr *chunkedReader) beginChunk() {
+ // chunk-size CRLF
+ var line string
+ line, cr.err = readLine(cr.r)
+ if cr.err != nil {
+ return
+ }
+ cr.n, cr.err = strconv.Btoui64(line, 16)
+ if cr.err != nil {
+ return
+ }
+ if cr.n == 0 {
+ cr.err = io.EOF
+ }
+}
+
+func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
+ if cr.err != nil {
+ return 0, cr.err
+ }
+ if cr.n == 0 {
+ cr.beginChunk()
+ if cr.err != nil {
+ return 0, cr.err
+ }
+ }
+ if uint64(len(b)) > cr.n {
+ b = b[0:cr.n]
+ }
+ n, cr.err = cr.r.Read(b)
+ cr.n -= uint64(n)
+ if cr.n == 0 && cr.err == nil {
+ // end of chunk (CRLF)
+ b := make([]byte, 2)
+ if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
+ if b[0] != '\r' || b[1] != '\n' {
+ cr.err = errors.New("malformed chunked encoding")
+ }
+ }
+ }
+ return n, cr.err
+}
+
+// Read a line of bytes (up to \n) from b.
+// Give up if the line exceeds maxLineLength.
+// The returned bytes are a pointer into storage in
+// the bufio, so they are only valid until the next bufio read.
+func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+ if p, err = b.ReadSlice('\n'); err != nil {
+ // We always know when EOF is coming.
+ // If the caller asked for a line, there should be a line.
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ } else if err == bufio.ErrBufferFull {
+ err = ErrLineTooLong
+ }
+ return nil, err
+ }
+ if len(p) >= maxLineLength {
+ return nil, ErrLineTooLong
+ }
+
+ // Chop off trailing white space.
+ p = bytes.TrimRight(p, " \r\t\n")
+
+ return p, nil
+}
+
+// readLineBytes, but convert the bytes into a string.
+func readLine(b *bufio.Reader) (s string, err error) {
+ p, e := readLineBytes(b)
+ if e != nil {
+ return "", e
+ }
+ return string(p), nil
+}
+
+// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP
+// "chunked" format before writing them to w. Closing the returned chunkedWriter
// sends the final 0-length chunk that marks the end of the stream.
//
// NewChunkedWriter is not needed by normal applications. The http
@@ -25,8 +133,8 @@ func NewChunkedWriter(w io.Writer) io.WriteCloser {
return &chunkedWriter{w}
}
-// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer
-// Encoding wire format to the underlying Wire writer.
+// Writing to chunkedWriter translates to writing in HTTP chunked Transfer
+// Encoding wire format to the underlying Wire chunkedWriter.
type chunkedWriter struct {
Wire io.Writer
}
@@ -62,23 +170,3 @@ func (cw *chunkedWriter) Close() error {
_, err := io.WriteString(cw.Wire, "0\r\n")
return err
}
-
-// NewChunkedReader returns a new reader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
-// The reader returns io.EOF when the final 0-length chunk is read.
-//
-// NewChunkedReader is not needed by normal applications. The http package
-// automatically decodes chunking when reading response bodies.
-func NewChunkedReader(r io.Reader) io.Reader {
- // This is a bit of a hack so we don't have to copy chunkedReader into
- // httputil. It's a bit more complex than chunkedWriter, which is copied
- // above.
- req, err := http.ReadRequest(bufio.NewReader(io.MultiReader(
- strings.NewReader("POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"),
- r,
- strings.NewReader("\r\n"))))
- if err != nil {
- panic("bad fake request: " + err.Error())
- }
- return req.Body
-}
diff --git a/libgo/go/net/http/httputil/chunked_test.go b/libgo/go/net/http/httputil/chunked_test.go
index 258d39b93cb..155a32bdf9a 100644
--- a/libgo/go/net/http/httputil/chunked_test.go
+++ b/libgo/go/net/http/httputil/chunked_test.go
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// This code is a duplicate of ../chunked_test.go with these edits:
+// s/newChunked/NewChunked/g
+// s/package http/package httputil/
+// Please make any changes in both files.
+
package httputil
import (
@@ -27,7 +32,8 @@ func TestChunk(t *testing.T) {
r := NewChunkedReader(&b)
data, err := ioutil.ReadAll(r)
if err != nil {
- t.Fatalf("ReadAll from NewChunkedReader: %v", err)
+ t.Logf(`data: "%s"`, data)
+ t.Fatalf("ReadAll from reader: %v", err)
}
if g, e := string(data), chunk1+chunk2; g != e {
t.Errorf("chunk reader read %q; want %q", g, e)
diff --git a/libgo/go/net/http/httputil/persist.go b/libgo/go/net/http/httputil/persist.go
index d7b670110c4..1266bd3ad22 100644
--- a/libgo/go/net/http/httputil/persist.go
+++ b/libgo/go/net/http/httputil/persist.go
@@ -22,6 +22,10 @@ var (
ErrPipeline = &http.ProtocolError{"pipeline error"}
)
+// This is an API usage error - the local side is closed.
+// ErrPersistEOF (above) reports that the remote side is closed.
+var errClosed = errors.New("i/o operation on closed connection")
+
// A ServerConn reads requests and sends responses over an underlying
// connection, until the HTTP keepalive logic commands an end. ServerConn
// also allows hijacking the underlying connection by calling Hijack
@@ -108,7 +112,7 @@ func (sc *ServerConn) Read() (req *http.Request, err error) {
}
if sc.r == nil { // connection closed by user in the meantime
defer sc.lk.Unlock()
- return nil, os.EBADF
+ return nil, errClosed
}
r := sc.r
lastbody := sc.lastbody
@@ -313,7 +317,7 @@ func (cc *ClientConn) Write(req *http.Request) (err error) {
}
if cc.c == nil { // connection closed by user in the meantime
defer cc.lk.Unlock()
- return os.EBADF
+ return errClosed
}
c := cc.c
if req.Close {
@@ -369,7 +373,7 @@ func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) {
}
if cc.r == nil { // connection closed by user in the meantime
defer cc.lk.Unlock()
- return nil, os.EBADF
+ return nil, errClosed
}
r := cc.r
lastbody := cc.lastbody
diff --git a/libgo/go/net/http/httputil/reverseproxy.go b/libgo/go/net/http/httputil/reverseproxy.go
index bfcb3ca6b11..1dc83e7d032 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 c0327a94824..2de147579d1 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/readrequest_test.go b/libgo/go/net/http/readrequest_test.go
index 2219d433165..c64fff6109f 100644
--- a/libgo/go/net/http/readrequest_test.go
+++ b/libgo/go/net/http/readrequest_test.go
@@ -70,7 +70,6 @@ var reqTests = []reqTest{
Close: false,
ContentLength: 7,
Host: "www.techcrunch.com",
- Form: url.Values{},
},
"abcdef\n",
@@ -94,10 +93,10 @@ var reqTests = []reqTest{
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
+ Header: Header{},
Close: false,
ContentLength: 0,
Host: "foo.com",
- Form: url.Values{},
},
noBody,
@@ -131,7 +130,6 @@ var reqTests = []reqTest{
Close: false,
ContentLength: 0,
Host: "test",
- Form: url.Values{},
},
noBody,
@@ -180,9 +178,9 @@ var reqTests = []reqTest{
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
+ Header: Header{},
ContentLength: -1,
Host: "foo.com",
- Form: url.Values{},
},
"foobar",
diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go
index 4410ca1d11c..66178490e37 100644
--- a/libgo/go/net/http/request.go
+++ b/libgo/go/net/http/request.go
@@ -19,12 +19,10 @@ import (
"mime/multipart"
"net/textproto"
"net/url"
- "strconv"
"strings"
)
const (
- maxLineLength = 4096 // assumed <= bufio.defaultBufSize
maxValueLength = 4096
maxHeaderLines = 1024
chunkSize = 4 << 10 // 4 KB chunks
@@ -43,7 +41,6 @@ type ProtocolError struct {
func (err *ProtocolError) Error() string { return err.ErrorString }
var (
- ErrLineTooLong = &ProtocolError{"header line too long"}
ErrHeaderTooLong = &ProtocolError{"header too long"}
ErrShortBody = &ProtocolError{"entity body too short"}
ErrNotSupported = &ProtocolError{"feature not supported"}
@@ -375,44 +372,6 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
return nil
}
-// Read a line of bytes (up to \n) from b.
-// Give up if the line exceeds maxLineLength.
-// The returned bytes are a pointer into storage in
-// the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err error) {
- if p, err = b.ReadSlice('\n'); err != nil {
- // We always know when EOF is coming.
- // If the caller asked for a line, there should be a line.
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- } else if err == bufio.ErrBufferFull {
- err = ErrLineTooLong
- }
- return nil, err
- }
- if len(p) >= maxLineLength {
- return nil, ErrLineTooLong
- }
-
- // Chop off trailing white space.
- var i int
- for i = len(p); i > 0; i-- {
- if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' {
- break
- }
- }
- return p[0:i], nil
-}
-
-// readLineBytes, but convert the bytes into a string.
-func readLine(b *bufio.Reader) (s string, err error) {
- p, e := readLineBytes(b)
- if e != nil {
- return "", e
- }
- return string(p), nil
-}
-
// Convert decimal at s[i:len(s)] to integer,
// returning value, string position where the digits stopped,
// and whether there was a valid number (digits, not too big).
@@ -448,55 +407,6 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
return major, minor, true
}
-type chunkedReader struct {
- r *bufio.Reader
- n uint64 // unread bytes in chunk
- err error
-}
-
-func (cr *chunkedReader) beginChunk() {
- // chunk-size CRLF
- var line string
- line, cr.err = readLine(cr.r)
- if cr.err != nil {
- return
- }
- cr.n, cr.err = strconv.Btoui64(line, 16)
- if cr.err != nil {
- return
- }
- if cr.n == 0 {
- cr.err = io.EOF
- }
-}
-
-func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
- if cr.err != nil {
- return 0, cr.err
- }
- if cr.n == 0 {
- cr.beginChunk()
- if cr.err != nil {
- return 0, cr.err
- }
- }
- if uint64(len(b)) > cr.n {
- b = b[0:cr.n]
- }
- n, cr.err = cr.r.Read(b)
- cr.n -= uint64(n)
- if cr.n == 0 && cr.err == nil {
- // end of chunk (CRLF)
- b := make([]byte, 2)
- if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
- if b[0] != '\r' || b[1] != '\n' {
- cr.err = errors.New("malformed chunked encoding")
- }
- }
- }
- return n, cr.err
-}
-
// NewRequest returns a new Request given a method, URL, and optional body.
func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
u, err := url.Parse(urlStr)
diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go
index be717aa83c3..e5d01698e55 100644
--- a/libgo/go/net/http/response_test.go
+++ b/libgo/go/net/http/response_test.go
@@ -65,6 +65,7 @@ var respTests = []respTest{
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
+ Header: Header{},
Request: dummyReq("GET"),
Close: true,
ContentLength: -1,
@@ -85,6 +86,7 @@ var respTests = []respTest{
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
+ Header: Header{},
Request: dummyReq("GET"),
Close: false,
ContentLength: 0,
@@ -315,7 +317,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) {
}
var wr io.Writer = &buf
if test.chunked {
- wr = &chunkedWriter{wr}
+ wr = newChunkedWriter(wr)
}
if test.compressed {
buf.WriteString("Content-Encoding: gzip\r\n")
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index e278396091d..670b5418fcd 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)
@@ -1077,6 +1077,31 @@ func TestClientWriteShutdown(t *testing.T) {
}
}
+// Tests that chunked server responses that write 1 byte at a time are
+// buffered before chunk headers are added, not after chunk headers.
+func TestServerBufferedChunking(t *testing.T) {
+ if true {
+ t.Logf("Skipping known broken test; see Issue 2357")
+ return
+ }
+ conn := new(testConn)
+ conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n"))
+ done := make(chan bool)
+ ls := &oneConnListener{conn}
+ go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) {
+ defer close(done)
+ rw.Header().Set("Content-Type", "text/plain") // prevent sniffing, which buffers
+ rw.Write([]byte{'x'})
+ rw.Write([]byte{'y'})
+ rw.Write([]byte{'z'})
+ }))
+ <-done
+ if !bytes.HasSuffix(conn.writeBuf.Bytes(), []byte("\r\n\r\n3\r\nxyz\r\n0\r\n\r\n")) {
+ t.Errorf("response didn't end with a single 3 byte 'xyz' chunk; got:\n%q",
+ conn.writeBuf.Bytes())
+ }
+}
+
// goTimeout runs f, failing t if f takes more than ns to complete.
func goTimeout(t *testing.T, ns int64, f func()) {
ch := make(chan bool, 2)
@@ -1120,7 +1145,7 @@ func TestAcceptMaxFds(t *testing.T) {
ln := &errorListener{[]error{
&net.OpError{
Op: "accept",
- Err: os.Errno(syscall.EMFILE),
+ Err: syscall.EMFILE,
}}}
err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {})))
if err != io.EOF {
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go
index 8c4889436f1..125f3f214bb 100644
--- a/libgo/go/net/http/server.go
+++ b/libgo/go/net/http/server.go
@@ -149,11 +149,13 @@ type writerOnly struct {
}
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
- // Flush before checking w.chunking, as Flush will call
- // WriteHeader if it hasn't been called yet, and WriteHeader
- // is what sets w.chunking.
- w.Flush()
+ // Call WriteHeader before checking w.chunking if it hasn't
+ // been called yet, since WriteHeader is what sets w.chunking.
+ if !w.wroteHeader {
+ w.WriteHeader(StatusOK)
+ }
if !w.chunking && w.bodyAllowed() && !w.needSniff {
+ w.Flush()
if rf, ok := w.conn.rwc.(io.ReaderFrom); ok {
n, err = rf.ReadFrom(src)
w.written += n
@@ -345,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")
@@ -465,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)
}
@@ -1011,8 +1013,8 @@ func (srv *Server) Serve(l net.Listener) error {
// package main
//
// import (
-// "http"
// "io"
+// "net/http"
// "log"
// )
//
@@ -1042,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) {
@@ -1082,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"},
}
@@ -1110,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}
}
@@ -1123,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 5707c7f057f..c1c78e2417d 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 a414e6420db..6efa8ce1ca2 100644
--- a/libgo/go/net/http/sniff_test.go
+++ b/libgo/go/net/http/sniff_test.go
@@ -6,11 +6,14 @@ package http_test
import (
"bytes"
+ "fmt"
+ "io"
"io/ioutil"
"log"
. "net/http"
"net/http/httptest"
"strconv"
+ "strings"
"testing"
)
@@ -79,3 +82,56 @@ func TestServerContentType(t *testing.T) {
resp.Body.Close()
}
}
+
+func TestContentTypeWithCopy(t *testing.T) {
+ const (
+ input = "\n<html>\n\t<head>\n"
+ expected = "text/html; charset=utf-8"
+ )
+
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Use io.Copy from a bytes.Buffer to trigger ReadFrom.
+ buf := bytes.NewBuffer([]byte(input))
+ n, err := io.Copy(w, buf)
+ if int(n) != len(input) || err != nil {
+ t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
+ }
+ }))
+ defer ts.Close()
+
+ resp, err := Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ if ct := resp.Header.Get("Content-Type"); ct != expected {
+ t.Errorf("Content-Type = %q, want %q", ct, expected)
+ }
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.Errorf("reading body: %v", err)
+ } else if !bytes.Equal(data, []byte(input)) {
+ t.Errorf("data is %q, want %q", data, input)
+ }
+ 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/transfer.go b/libgo/go/net/http/transfer.go
index 2670d77ef00..d25c8fcde42 100644
--- a/libgo/go/net/http/transfer.go
+++ b/libgo/go/net/http/transfer.go
@@ -537,7 +537,9 @@ func (b *body) Read(p []byte) (n int, err error) {
// Read the final trailer once we hit EOF.
if err == io.EOF && b.hdr != nil {
- err = b.readTrailer()
+ if e := b.readTrailer(); e != nil {
+ err = e
+ }
b.hdr = nil
}
return n, err
diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go
index da5244b2c12..e622e41f0a2 100644
--- a/libgo/go/net/http/transport.go
+++ b/libgo/go/net/http/transport.go
@@ -504,7 +504,7 @@ func (pc *persistConn) expectingResponse() bool {
var remoteSideClosedFunc func(error) bool // or nil to use default
func remoteSideClosed(err error) bool {
- if err == io.EOF || err == os.EINVAL {
+ if err == io.EOF {
return true
}
if remoteSideClosedFunc != nil {
diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go
index 77297972449..6f50f6f2767 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/http/transport_windows.go b/libgo/go/net/http/transport_windows.go
index 2a20d2224ae..c9ef2c2ab6e 100644
--- a/libgo/go/net/http/transport_windows.go
+++ b/libgo/go/net/http/transport_windows.go
@@ -6,14 +6,14 @@ package http
import (
"net"
- "os"
+ "syscall"
)
func init() {
remoteSideClosedFunc = func(err error) (out bool) {
op, ok := err.(*net.OpError)
- if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == os.Errno(10058) {
- // TODO(bradfitz): find the symbol for 10058
+ if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == syscall.Errno(10058) {
+ // TODO(brainman,rsc): Fix whatever is generating this.
return true
}
return false
diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go
index b026e01104d..e896d43c321 100644
--- a/libgo/go/net/interface_bsd.go
+++ b/libgo/go/net/interface_bsd.go
@@ -20,18 +20,18 @@ import (
func interfaceTable(ifindex int) ([]Interface, error) {
var (
tab []byte
- e int
+ e error
msgs []syscall.RoutingMessage
ift []Interface
)
tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route rib", e)
}
msgs, e = syscall.ParseRoutingMessage(tab)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route message", e)
}
@@ -55,7 +55,7 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, error) {
var ift []Interface
sas, e := syscall.ParseRoutingSockaddr(m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route sockaddr", e)
}
@@ -110,18 +110,18 @@ func linkFlags(rawFlags int32) Flags {
func interfaceAddrTable(ifindex int) ([]Addr, error) {
var (
tab []byte
- e int
+ e error
msgs []syscall.RoutingMessage
ifat []Addr
)
tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route rib", e)
}
msgs, e = syscall.ParseRoutingMessage(tab)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route message", e)
}
@@ -145,7 +145,7 @@ func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, error) {
var ifat []Addr
sas, e := syscall.ParseRoutingSockaddr(m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route sockaddr", e)
}
diff --git a/libgo/go/net/interface_darwin.go b/libgo/go/net/interface_darwin.go
index 1472afb8846..2da447adc84 100644
--- a/libgo/go/net/interface_darwin.go
+++ b/libgo/go/net/interface_darwin.go
@@ -17,18 +17,18 @@ import (
func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
var (
tab []byte
- e int
+ e error
msgs []syscall.RoutingMessage
ifmat []Addr
)
tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route rib", e)
}
msgs, e = syscall.ParseRoutingMessage(tab)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route message", e)
}
@@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error)
var ifmat []Addr
sas, e := syscall.ParseRoutingSockaddr(m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route sockaddr", e)
}
diff --git a/libgo/go/net/interface_freebsd.go b/libgo/go/net/interface_freebsd.go
index b0274f68d75..a12877e251b 100644
--- a/libgo/go/net/interface_freebsd.go
+++ b/libgo/go/net/interface_freebsd.go
@@ -17,18 +17,18 @@ import (
func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
var (
tab []byte
- e int
+ e error
msgs []syscall.RoutingMessage
ifmat []Addr
)
tab, e = syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route rib", e)
}
msgs, e = syscall.ParseRoutingMessage(tab)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route message", e)
}
@@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error)
var ifmat []Addr
sas, e := syscall.ParseRoutingSockaddr(m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("route sockaddr", e)
}
diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go
index cd0339d61bd..96db7186af5 100644
--- a/libgo/go/net/interface_linux.go
+++ b/libgo/go/net/interface_linux.go
@@ -21,16 +21,16 @@ func interfaceTable(ifindex int) ([]Interface, error) {
ift []Interface
tab []byte
msgs []syscall.NetlinkMessage
- e int
+ e error
)
tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("netlink rib", e)
}
msgs, e = syscall.ParseNetlinkMessage(tab)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("netlink message", e)
}
@@ -42,7 +42,7 @@ func interfaceTable(ifindex int) ([]Interface, error) {
ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
if ifindex == 0 || ifindex == int(ifim.Index) {
attrs, e := syscall.ParseNetlinkRouteAttr(&m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("netlink routeattr", e)
}
ifi := newLink(attrs, ifim)
@@ -102,27 +102,19 @@ func linkFlags(rawFlags uint32) Flags {
// for all network interfaces. Otherwise it returns addresses
// for a specific interface.
func interfaceAddrTable(ifindex int) ([]Addr, error) {
- var (
- tab []byte
- e int
- err error
- ifat []Addr
- msgs []syscall.NetlinkMessage
- )
-
- tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
- if e != 0 {
+ tab, e := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
+ if e != nil {
return nil, os.NewSyscallError("netlink rib", e)
}
- msgs, e = syscall.ParseNetlinkMessage(tab)
- if e != 0 {
+ msgs, e := syscall.ParseNetlinkMessage(tab)
+ if e != nil {
return nil, os.NewSyscallError("netlink message", e)
}
- ifat, err = addrTable(msgs, ifindex)
- if err != nil {
- return nil, err
+ ifat, e := addrTable(msgs, ifindex)
+ if e != nil {
+ return nil, e
}
return ifat, nil
@@ -139,7 +131,7 @@ func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
if ifindex == 0 || ifindex == int(ifam.Index) {
attrs, e := syscall.ParseNetlinkRouteAttr(&m)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("netlink routeattr", e)
}
ifat = append(ifat, newAddr(attrs, int(ifam.Family))...)
diff --git a/libgo/go/net/interface_windows.go b/libgo/go/net/interface_windows.go
index a1c8d95161c..2ed66cdce37 100644
--- a/libgo/go/net/interface_windows.go
+++ b/libgo/go/net/interface_windows.go
@@ -39,7 +39,7 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) {
func getInterfaceList() ([]syscall.InterfaceInfo, error) {
s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("Socket", e)
}
defer syscall.Closesocket(s)
@@ -48,7 +48,7 @@ func getInterfaceList() ([]syscall.InterfaceInfo, error) {
ret := uint32(0)
size := uint32(unsafe.Sizeof(ii))
e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("WSAIoctl", e)
}
c := ret / uint32(unsafe.Sizeof(ii[0]))
diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go
index 716454d8a98..9234f5aff65 100644
--- a/libgo/go/net/ipsock.go
+++ b/libgo/go/net/ipsock.go
@@ -9,7 +9,7 @@ package net
var supportsIPv6, supportsIPv4map = probeIPv6Stack()
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
- if filter == anyaddr {
+ if filter == nil {
// We'll take any IP address, but since the dialing code
// does not yet try multiple addresses, prefer to use
// an IPv4 address if possible. This is especially relevant
@@ -113,7 +113,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
// Try as an IP address.
addr = ParseIP(host)
if addr == nil {
- filter := anyaddr
+ var filter func(IP) IP
if net != "" && net[len(net)-1] == '4' {
filter = ipv4only
}
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index d5b8f2189c4..f0ca7dad345 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -33,8 +33,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
}
for i := range probes {
- s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
- if errno != 0 {
+ s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ if err != nil {
continue
}
defer closesocket(s)
@@ -42,8 +42,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
if err != nil {
continue
}
- errno = syscall.Bind(s, sa)
- if errno != 0 {
+ err = syscall.Bind(s, sa)
+ if err != nil {
continue
}
probes[i].ok = true
diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go
index 61d8a8871e1..020871b46d6 100644
--- a/libgo/go/net/lookup_windows.go
+++ b/libgo/go/net/lookup_windows.go
@@ -22,7 +22,7 @@ func lookupProtocol(name string) (proto int, err error) {
protoentLock.Lock()
defer protoentLock.Unlock()
p, e := syscall.GetProtoByName(name)
- if e != 0 {
+ if e != nil {
return 0, os.NewSyscallError("GetProtoByName", e)
}
return int(p.Proto), nil
@@ -44,7 +44,7 @@ func LookupIP(name string) (addrs []IP, err error) {
hostentLock.Lock()
defer hostentLock.Unlock()
h, e := syscall.GetHostByName(name)
- if e != 0 {
+ if e != nil {
return nil, os.NewSyscallError("GetHostByName", e)
}
switch h.AddrType {
@@ -71,7 +71,7 @@ func LookupPort(network, service string) (port int, err error) {
serventLock.Lock()
defer serventLock.Unlock()
s, e := syscall.GetServByName(service, network)
- if e != 0 {
+ if e != nil {
return 0, os.NewSyscallError("GetServByName", e)
}
return int(syscall.Ntohs(s.Port)), nil
@@ -81,7 +81,7 @@ func LookupCNAME(name string) (cname string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
if e != 0 {
- return "", os.NewSyscallError("LookupCNAME", int(e))
+ return "", os.NewSyscallError("LookupCNAME", e)
}
defer syscall.DnsRecordListFree(r, 1)
if r != nil && r.Type == syscall.DNS_TYPE_CNAME {
@@ -110,7 +110,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
var r *syscall.DNSRecord
e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
if e != 0 {
- return "", nil, os.NewSyscallError("LookupSRV", int(e))
+ return "", nil, os.NewSyscallError("LookupSRV", e)
}
defer syscall.DnsRecordListFree(r, 1)
addrs = make([]*SRV, 0, 10)
@@ -126,7 +126,7 @@ func LookupMX(name string) (mx []*MX, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
if e != 0 {
- return nil, os.NewSyscallError("LookupMX", int(e))
+ return nil, os.NewSyscallError("LookupMX", e)
}
defer syscall.DnsRecordListFree(r, 1)
mx = make([]*MX, 0, 10)
@@ -142,7 +142,7 @@ func LookupTXT(name string) (txt []string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
if e != 0 {
- return nil, os.NewSyscallError("LookupTXT", int(e))
+ return nil, os.NewSyscallError("LookupTXT", e)
}
defer syscall.DnsRecordListFree(r, 1)
txt = make([]string, 0, 10)
@@ -164,7 +164,7 @@ func LookupAddr(addr string) (name []string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
if e != 0 {
- return nil, os.NewSyscallError("LookupAddr", int(e))
+ return nil, os.NewSyscallError("LookupAddr", e)
}
defer syscall.DnsRecordListFree(r, 1)
name = make([]string, 0, 10)
diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go
index 95246b2fa1d..e1afa32062f 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 5653647b8cc..1f71cc480af 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/newpollserver.go b/libgo/go/net/newpollserver.go
index 9ad6f7ba276..035df4a6ff1 100644
--- a/libgo/go/net/newpollserver.go
+++ b/libgo/go/net/newpollserver.go
@@ -18,11 +18,10 @@ func newPollServer() (s *pollServer, err error) {
if s.pr, s.pw, err = os.Pipe(); err != nil {
return nil, err
}
- var e int
- if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 {
+ if err = syscall.SetNonblock(s.pr.Fd(), true); err != nil {
goto Errno
}
- if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 {
+ if err = syscall.SetNonblock(s.pw.Fd(), true); err != nil {
goto Errno
}
if s.poll, err = newpollster(); err != nil {
@@ -37,7 +36,7 @@ func newPollServer() (s *pollServer, err error) {
return s, nil
Errno:
- err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)}
+ err = &os.PathError{"setnonblock", s.pr.Name(), err}
Error:
s.pr.Close()
s.pw.Close()
diff --git a/libgo/go/net/pipe.go b/libgo/go/net/pipe.go
index b99e6e658de..0ce7ccb9d7b 100644
--- a/libgo/go/net/pipe.go
+++ b/libgo/go/net/pipe.go
@@ -1,3 +1,7 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
package net
import (
diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go
index 36c75785789..350abe451f3 100644
--- a/libgo/go/net/sendfile_linux.go
+++ b/libgo/go/net/sendfile_linux.go
@@ -62,18 +62,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
written += int64(n)
remain -= int64(n)
}
- if n == 0 && errno == 0 {
+ if n == 0 && errno == nil {
break
}
if errno == syscall.EAGAIN && c.wdeadline >= 0 {
pollserver.WaitWrite(c)
continue
}
- if errno != 0 {
+ if errno != nil {
// This includes syscall.ENOSYS (no kernel
// support) and syscall.EINVAL (fd types which
// don't implement sendfile together)
- err = &OpError{"sendfile", c.net, c.raddr, os.Errno(errno)}
+ err = &OpError{"sendfile", c.net, c.raddr, errno}
break
}
}
diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go
index 0b31572771c..ee7ff8b98c2 100644
--- a/libgo/go/net/sendfile_windows.go
+++ b/libgo/go/net/sendfile_windows.go
@@ -16,7 +16,7 @@ type sendfileOp struct {
n uint32
}
-func (o *sendfileOp) Submit() (errno int) {
+func (o *sendfileOp) Submit() (err error) {
return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
}
diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go
index d9df02cd63f..33f11f219c9 100644
--- a/libgo/go/net/sock.go
+++ b/libgo/go/net/sock.go
@@ -28,9 +28,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
// See ../syscall/exec.go for description of ForkLock.
syscall.ForkLock.RLock()
s, e := syscall.Socket(f, p, t)
- if e != 0 {
+ if err != nil {
syscall.ForkLock.RUnlock()
- return nil, os.Errno(e)
+ return nil, err
}
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
@@ -39,9 +39,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
if la != nil {
e = syscall.Bind(s, la)
- if e != 0 {
+ if e != nil {
closesocket(s)
- return nil, os.Errno(e)
+ return nil, e
}
}
diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go
index a726b45c15a..44890ba66bb 100644
--- a/libgo/go/net/tcpsock_posix.go
+++ b/libgo/go/net/tcpsock_posix.go
@@ -250,9 +250,9 @@ func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) {
return nil, err
}
errno := syscall.Listen(fd.sysfd, listenBacklog())
- if errno != 0 {
+ if errno != nil {
closesocket(fd.sysfd)
- return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)}
+ return nil, &OpError{"listen", "tcp", laddr, errno}
}
l = new(TCPListener)
l.fd = fd
diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go
index 3c884ca7cfe..f6e5238c1b1 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/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 6ba692e5083..929f6409a4f 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -327,9 +327,9 @@ func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) {
return nil, err
}
e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog());
- if e1 != 0 {
+ if e1 != nil {
closesocket(fd.sysfd)
- return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: os.Errno(e1)}
+ return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: e1}
}
return &UnixListener{fd, laddr.Name}, nil
}
diff --git a/libgo/go/old/netchan/common.go b/libgo/go/old/netchan/common.go
index dfd1fd03427..03fa8ff6c41 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 d698dd53a90..d94c4b16b21 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 7243672ecd3..a6da8210b99 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/dir_largefile.go b/libgo/go/os/dir_largefile.go
index c723ec92400..d6b4239610e 100644
--- a/libgo/go/os/dir_largefile.go
+++ b/libgo/go/os/dir_largefile.go
@@ -9,4 +9,4 @@ package os
import "syscall"
-func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir64_r")
+func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir64_r")
diff --git a/libgo/go/os/dir_regfile.go b/libgo/go/os/dir_regfile.go
index 22fb5febbb2..7effdf7851a 100644
--- a/libgo/go/os/dir_regfile.go
+++ b/libgo/go/os/dir_regfile.go
@@ -9,4 +9,4 @@ package os
import "syscall"
-func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir_r")
+func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir_r")
diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go
index a16bcf63f41..e4dff835d89 100644
--- a/libgo/go/os/dir_unix.go
+++ b/libgo/go/os/dir_unix.go
@@ -47,9 +47,9 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
// Refill the buffer if necessary
if d.bufp >= d.nbuf {
d.bufp = 0
- var errno int
+ var errno error
d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf)
- if errno != 0 {
+ if errno != nil {
return names, NewSyscallError("readdirent", errno)
}
if d.nbuf <= 0 {
diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go
index 4844fa3e26d..7e3f52502e5 100644
--- a/libgo/go/os/env.go
+++ b/libgo/go/os/env.go
@@ -6,7 +6,10 @@
package os
-func setenv_c(k, v string)
+import (
+ "errors"
+ "syscall"
+)
// Expand replaces ${var} or $var in the string based on the mapping function.
// Invocations of undefined variables are replaced with the empty string.
@@ -73,3 +76,47 @@ func getShellName(s string) (string, int) {
}
return s[:i], i
}
+
+// ENOENV is the error indicating that an environment variable does not exist.
+var ENOENV = errors.New("no such environment variable")
+
+// Getenverror retrieves the value of the environment variable named by the key.
+// It returns the value and an error, if any.
+func Getenverror(key string) (value string, err error) {
+ if len(key) == 0 {
+ return "", EINVAL
+ }
+ val, found := syscall.Getenv(key)
+ if !found {
+ return "", ENOENV
+ }
+ return val, nil
+}
+
+// Getenv retrieves the value of the environment variable named by the key.
+// It returns the value, which will be empty if the variable is not present.
+func Getenv(key string) string {
+ v, _ := Getenverror(key)
+ return v
+}
+
+// Setenv sets the value of the environment variable named by the key.
+// It returns an error, if any.
+func Setenv(key, value string) error {
+ err := syscall.Setenv(key, value)
+ if err != nil {
+ return NewSyscallError("setenv", err)
+ }
+ return nil
+}
+
+// Clearenv deletes all environment variables.
+func Clearenv() {
+ syscall.Clearenv()
+}
+
+// Environ returns an array of strings representing the environment,
+// in the form "key=value".
+func Environ() []string {
+ return syscall.Environ()
+}
diff --git a/libgo/go/os/env_plan9.go b/libgo/go/os/env_plan9.go
deleted file mode 100644
index 9757aa902af..00000000000
--- a/libgo/go/os/env_plan9.go
+++ /dev/null
@@ -1,99 +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.
-
-// Plan 9 environment variables.
-
-package os
-
-import (
- "errors"
- "syscall"
-)
-
-// ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = errors.New("no such environment variable")
-
-// Getenverror retrieves the value of the environment variable named by the key.
-// It returns the value and an error, if any.
-func Getenverror(key string) (value string, err error) {
- if len(key) == 0 {
- return "", EINVAL
- }
- f, e := Open("/env/" + key)
- if iserror(e) {
- return "", ENOENV
- }
- defer f.Close()
-
- l, _ := f.Seek(0, 2)
- f.Seek(0, 0)
- buf := make([]byte, l)
- n, e := f.Read(buf)
- if iserror(e) {
- return "", ENOENV
- }
-
- if n > 0 && buf[n-1] == 0 {
- buf = buf[:n-1]
- }
- return string(buf), nil
-}
-
-// Getenv retrieves the value of the environment variable named by the key.
-// It returns the value, which will be empty if the variable is not present.
-func Getenv(key string) string {
- v, _ := Getenverror(key)
- return v
-}
-
-// Setenv sets the value of the environment variable named by the key.
-// It returns an error, if any.
-func Setenv(key, value string) error {
- if len(key) == 0 {
- return EINVAL
- }
-
- f, e := Create("/env/" + key)
- if iserror(e) {
- return e
- }
- defer f.Close()
-
- _, e = f.Write([]byte(value))
- return nil
-}
-
-// Clearenv deletes all environment variables.
-func Clearenv() {
- syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0)
-}
-
-// Environ returns an array of strings representing the environment,
-// in the form "key=value".
-func Environ() []string {
- env := make([]string, 0, 100)
-
- f, e := Open("/env")
- if iserror(e) {
- panic(e)
- }
- defer f.Close()
-
- names, e := f.Readdirnames(-1)
- if iserror(e) {
- panic(e)
- }
-
- for _, k := range names {
- if v, e := Getenverror(k); !iserror(e) {
- env = append(env, k+"="+v)
- }
- }
- return env[0:len(env)]
-}
-
-// TempDir returns the default directory to use for temporary files.
-func TempDir() string {
- return "/tmp"
-}
diff --git a/libgo/go/os/env_unix.go b/libgo/go/os/env_unix.go
deleted file mode 100644
index 01fd9d449f3..00000000000
--- a/libgo/go/os/env_unix.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin freebsd linux openbsd
-
-// Unix environment variables.
-
-package os
-
-import (
- "errors"
- "sync"
-)
-
-// ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = errors.New("no such environment variable")
-
-var env map[string]string
-var once sync.Once
-
-func copyenv() {
- env = make(map[string]string)
- for _, s := range Envs {
- for j := 0; j < len(s); j++ {
- if s[j] == '=' {
- env[s[0:j]] = s[j+1:]
- break
- }
- }
- }
-}
-
-var envLock sync.RWMutex
-
-// Getenverror retrieves the value of the environment variable named by the key.
-// It returns the value and an error, if any.
-func Getenverror(key string) (value string, err error) {
- once.Do(copyenv)
-
- if len(key) == 0 {
- return "", EINVAL
- }
-
- envLock.RLock()
- defer envLock.RUnlock()
-
- v, ok := env[key]
- if !ok {
- return "", ENOENV
- }
- return v, nil
-}
-
-// Getenv retrieves the value of the environment variable named by the key.
-// It returns the value, which will be empty if the variable is not present.
-func Getenv(key string) string {
- v, _ := Getenverror(key)
- return v
-}
-
-// Setenv sets the value of the environment variable named by the key.
-// It returns an error, if any.
-func Setenv(key, value string) error {
- once.Do(copyenv)
- if len(key) == 0 {
- return EINVAL
- }
-
- envLock.Lock()
- defer envLock.Unlock()
-
- env[key] = value
- setenv_c(key, value) // is a no-op if cgo isn't loaded
- return nil
-}
-
-// Clearenv deletes all environment variables.
-func Clearenv() {
- once.Do(copyenv) // prevent copyenv in Getenv/Setenv
-
- envLock.Lock()
- defer envLock.Unlock()
-
- env = make(map[string]string)
-
- // TODO(bradfitz): pass through to C
-}
-
-// Environ returns an array of strings representing the environment,
-// in the form "key=value".
-func Environ() []string {
- once.Do(copyenv)
- envLock.RLock()
- defer envLock.RUnlock()
- a := make([]string, len(env))
- i := 0
- for k, v := range env {
- a[i] = k + "=" + v
- i++
- }
- return a
-}
-
-// TempDir returns the default directory to use for temporary files.
-func TempDir() string {
- dir := Getenv("TMPDIR")
- if dir == "" {
- dir = "/tmp"
- }
- return dir
-}
diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go
deleted file mode 100644
index 4e90385da96..00000000000
--- a/libgo/go/os/env_windows.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Windows environment variables.
-
-package os
-
-import (
- "errors"
- "syscall"
- "unicode/utf16"
- "unsafe"
-)
-
-// ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = errors.New("no such environment variable")
-
-// Getenverror retrieves the value of the environment variable named by the key.
-// It returns the value and an error, if any.
-func Getenverror(key string) (value string, err error) {
- b := make([]uint16, 100)
- n, e := syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b)))
- if n == 0 && e == syscall.ERROR_ENVVAR_NOT_FOUND {
- return "", ENOENV
- }
- if n > uint32(len(b)) {
- b = make([]uint16, n)
- n, e = syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b)))
- if n > uint32(len(b)) {
- n = 0
- }
- }
- if n == 0 {
- return "", NewSyscallError("GetEnvironmentVariable", e)
- }
- return string(utf16.Decode(b[0:n])), nil
-}
-
-// Getenv retrieves the value of the environment variable named by the key.
-// It returns the value, which will be empty if the variable is not present.
-func Getenv(key string) string {
- v, _ := Getenverror(key)
- return v
-}
-
-// Setenv sets the value of the environment variable named by the key.
-// It returns an error, if any.
-func Setenv(key, value string) error {
- var v *uint16
- if len(value) > 0 {
- v = syscall.StringToUTF16Ptr(value)
- }
- e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v)
- if e != 0 {
- return NewSyscallError("SetEnvironmentVariable", e)
- }
- return nil
-}
-
-// Clearenv deletes all environment variables.
-func Clearenv() {
- for _, s := range Environ() {
- // Environment variables can begin with =
- // so start looking for the separator = at j=1.
- // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
- for j := 1; j < len(s); j++ {
- if s[j] == '=' {
- Setenv(s[0:j], "")
- break
- }
- }
- }
-}
-
-// Environ returns an array of strings representing the environment,
-// in the form "key=value".
-func Environ() []string {
- s, e := syscall.GetEnvironmentStrings()
- if e != 0 {
- return nil
- }
- defer syscall.FreeEnvironmentStrings(s)
- r := make([]string, 0, 50) // Empty with room to grow.
- for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ {
- if p[i] == 0 {
- // empty string marks the end
- if i <= from {
- break
- }
- r = append(r, string(utf16.Decode(p[from:i])))
- from = i + 1
- }
- }
- return r
-}
-
-// TempDir returns the default directory to use for temporary files.
-func TempDir() string {
- const pathSep = '\\'
- dirw := make([]uint16, syscall.MAX_PATH)
- n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
- if n > uint32(len(dirw)) {
- dirw = make([]uint16, n)
- n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
- if n > uint32(len(dirw)) {
- n = 0
- }
- }
- if n > 0 && dirw[n-1] == pathSep {
- n--
- }
- return string(utf16.Decode(dirw[0:n]))
-}
-
-func init() {
- var argc int32
- cmd := syscall.GetCommandLine()
- argv, e := syscall.CommandLineToArgv(cmd, &argc)
- if e != 0 {
- return
- }
- defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
- Args = make([]string, argc)
- for i, v := range (*argv)[:argc] {
- Args[i] = string(syscall.UTF16ToString((*v)[:]))
- }
-}
diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go
index e08707078ec..8f005efbe05 100644
--- a/libgo/go/os/error_plan9.go
+++ b/libgo/go/os/error_plan9.go
@@ -24,7 +24,7 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err }
// NewSyscallError returns, as an error, a new SyscallError
// with the given system call name and error details.
// As a convenience, if err is nil, NewSyscallError returns nil.
-func NewSyscallError(syscall string, err syscall.Error) error {
+func NewSyscallError(syscall string, err error) error {
if err == nil {
return nil
}
@@ -57,9 +57,3 @@ var (
EPIPE = errors.New("Broken Pipe")
EPLAN9 = errors.New("not supported by plan 9")
)
-
-func iserror(err syscall.Error) bool {
- return err != nil
-}
-
-func Errno(e syscall.Error) syscall.Error { return e }
diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go
index c3d79425062..dbe1b9a8d5e 100644
--- a/libgo/go/os/error_posix.go
+++ b/libgo/go/os/error_posix.go
@@ -8,67 +8,53 @@ package os
import syscall "syscall"
-// Errno is the Unix error number. Names such as EINVAL are simple
-// wrappers to convert the error number into an error.
-type Errno int64
-
-func (e Errno) Error() string { return syscall.Errstr(int(e)) }
-
-func (e Errno) Temporary() bool {
- return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout()
-}
-
-func (e Errno) Timeout() bool {
- return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT)
-}
-
// Commonly known Unix errors.
var (
- EPERM error = Errno(syscall.EPERM)
- ENOENT error = Errno(syscall.ENOENT)
- ESRCH error = Errno(syscall.ESRCH)
- EINTR error = Errno(syscall.EINTR)
- EIO error = Errno(syscall.EIO)
- ENXIO error = Errno(syscall.ENXIO)
- E2BIG error = Errno(syscall.E2BIG)
- ENOEXEC error = Errno(syscall.ENOEXEC)
- EBADF error = Errno(syscall.EBADF)
- ECHILD error = Errno(syscall.ECHILD)
- EDEADLK error = Errno(syscall.EDEADLK)
- ENOMEM error = Errno(syscall.ENOMEM)
- EACCES error = Errno(syscall.EACCES)
- EFAULT error = Errno(syscall.EFAULT)
- EBUSY error = Errno(syscall.EBUSY)
- EEXIST error = Errno(syscall.EEXIST)
- EXDEV error = Errno(syscall.EXDEV)
- ENODEV error = Errno(syscall.ENODEV)
- ENOTDIR error = Errno(syscall.ENOTDIR)
- EISDIR error = Errno(syscall.EISDIR)
- EINVAL error = Errno(syscall.EINVAL)
- ENFILE error = Errno(syscall.ENFILE)
- EMFILE error = Errno(syscall.EMFILE)
- ENOTTY error = Errno(syscall.ENOTTY)
- EFBIG error = Errno(syscall.EFBIG)
- ENOSPC error = Errno(syscall.ENOSPC)
- ESPIPE error = Errno(syscall.ESPIPE)
- EROFS error = Errno(syscall.EROFS)
- EMLINK error = Errno(syscall.EMLINK)
- EPIPE error = Errno(syscall.EPIPE)
- EAGAIN error = Errno(syscall.EAGAIN)
- EDOM error = Errno(syscall.EDOM)
- ERANGE error = Errno(syscall.ERANGE)
- EADDRINUSE error = Errno(syscall.EADDRINUSE)
- ECONNREFUSED error = Errno(syscall.ECONNREFUSED)
- ENAMETOOLONG error = Errno(syscall.ENAMETOOLONG)
- EAFNOSUPPORT error = Errno(syscall.EAFNOSUPPORT)
- ETIMEDOUT error = Errno(syscall.ETIMEDOUT)
- ENOTCONN error = Errno(syscall.ENOTCONN)
+ EPERM error = syscall.EPERM
+ ENOENT error = syscall.ENOENT
+ ESRCH error = syscall.ESRCH
+ EINTR error = syscall.EINTR
+ EIO error = syscall.EIO
+ ENXIO error = syscall.ENXIO
+ E2BIG error = syscall.E2BIG
+ ENOEXEC error = syscall.ENOEXEC
+ EBADF error = syscall.EBADF
+ ECHILD error = syscall.ECHILD
+ EDEADLK error = syscall.EDEADLK
+ ENOMEM error = syscall.ENOMEM
+ EACCES error = syscall.EACCES
+ EFAULT error = syscall.EFAULT
+ EBUSY error = syscall.EBUSY
+ EEXIST error = syscall.EEXIST
+ EXDEV error = syscall.EXDEV
+ ENODEV error = syscall.ENODEV
+ ENOTDIR error = syscall.ENOTDIR
+ EISDIR error = syscall.EISDIR
+ EINVAL error = syscall.EINVAL
+ ENFILE error = syscall.ENFILE
+ EMFILE error = syscall.EMFILE
+ ENOTTY error = syscall.ENOTTY
+ EFBIG error = syscall.EFBIG
+ ENOSPC error = syscall.ENOSPC
+ ESPIPE error = syscall.ESPIPE
+ EROFS error = syscall.EROFS
+ EMLINK error = syscall.EMLINK
+ EPIPE error = syscall.EPIPE
+ EAGAIN error = syscall.EAGAIN
+ EDOM error = syscall.EDOM
+ ERANGE error = syscall.ERANGE
+ EADDRINUSE error = syscall.EADDRINUSE
+ ECONNREFUSED error = syscall.ECONNREFUSED
+ ENAMETOOLONG error = syscall.ENAMETOOLONG
+ EAFNOSUPPORT error = syscall.EAFNOSUPPORT
+ ETIMEDOUT error = syscall.ETIMEDOUT
+ ENOTCONN error = syscall.ENOTCONN
)
// SyscallError records an error from a specific system call.
type SyscallError struct {
Syscall string
- Errno Errno
+ Errno error
}
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error() }
@@ -79,14 +65,10 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error(
// NewSyscallError returns, as an error, a new SyscallError
// with the given system call name and error details.
-// As a convenience, if errno is 0, NewSyscallError returns nil.
-func NewSyscallError(syscall string, errno int) error {
- if errno == 0 {
+// As a convenience, if err is nil, NewSyscallError returns nil.
+func NewSyscallError(syscall string, err error) error {
+ if err == nil {
return nil
}
- return &SyscallError{syscall, Errno(errno)}
-}
-
-func iserror(errno int) bool {
- return errno != 0
+ return &SyscallError{syscall, err}
}
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index ebdfd54a734..4c95c1b0dac 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -50,14 +50,14 @@ type Cmd struct {
// calling process's current directory.
Dir string
- // Stdin specifies the process's standard input.
- // If Stdin is nil, the process reads from DevNull.
+ // Stdin specifies the process's standard input. If Stdin is
+ // nil, the process reads from the null device (os.DevNull).
Stdin io.Reader
// Stdout and Stderr specify the process's standard output and error.
//
- // If either is nil, Run connects the
- // corresponding file descriptor to /dev/null.
+ // If either is nil, Run connects the corresponding file descriptor
+ // to the null device (os.DevNull).
//
// If Stdout and Stderr are are the same writer, at most one
// goroutine at a time will call Write.
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index d234641acc4..9665ea8f413 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 db326236ee8..ef5bd921668 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/exec_plan9.go b/libgo/go/os/exec_plan9.go
index a1a335359dc..9a0db6dd3ef 100644
--- a/libgo/go/os/exec_plan9.go
+++ b/libgo/go/os/exec_plan9.go
@@ -32,7 +32,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
sysattr.Files = intfd
pid, h, e := syscall.StartProcess(name, argv, sysattr)
- if iserror(e) {
+ if e != nil {
return nil, &PathError{"fork/exec", name, e}
}
@@ -52,7 +52,7 @@ func (p *Process) Signal(sig Signal) error {
}
f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0)
- if iserror(e) {
+ if e != nil {
return NewSyscallError("signal", e)
}
defer f.Close()
@@ -63,7 +63,7 @@ func (p *Process) Signal(sig Signal) error {
// Kill causes the Process to exit immediately.
func (p *Process) Kill() error {
f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0)
- if iserror(e) {
+ if e != nil {
return NewSyscallError("kill", e)
}
defer f.Close()
@@ -77,7 +77,7 @@ func (p *Process) Kill() error {
// ForkExec is almost always a better way to execute a program.
func Exec(name string, argv []string, envv []string) error {
e := syscall.Exec(name, argv, envv)
- if iserror(e) {
+ if e != nil {
return &PathError{"exec", name, e}
}
@@ -102,7 +102,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
for true {
err = syscall.Await(&waitmsg)
- if iserror(err) {
+ if err != nil {
return nil, NewSyscallError("wait", err)
}
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index 12b44e5f33b..8b08eebd0da 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -40,8 +40,8 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
}
pid, h, e := syscall.StartProcess(name, argv, sysattr)
- if iserror(e) {
- return nil, &PathError{"fork/exec", name, Errno(e)}
+ if e != nil {
+ return nil, &PathError{"fork/exec", name, e}
}
return newProcess(pid, h), nil
}
@@ -62,8 +62,8 @@ func Exec(name string, argv []string, envv []string) error {
envv = Environ()
}
e := syscall.Exec(name, argv, envv)
- if iserror(e) {
- return &PathError{"exec", name, Errno(e)}
+ if e != nil {
+ return &PathError{"exec", name, e}
}
return nil
}
diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go
index 242bda702c6..3dcac414c5a 100644
--- a/libgo/go/os/exec_unix.go
+++ b/libgo/go/os/exec_unix.go
@@ -38,7 +38,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
options ^= WRUSAGE
}
pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
- if e != 0 {
+ if e != nil {
return nil, NewSyscallError("wait", e)
}
// With WNOHANG pid is 0 if child has not exited.
@@ -57,8 +57,8 @@ func (p *Process) Signal(sig Signal) error {
if p.done {
return errors.New("os: process already finished")
}
- if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 {
- return Errno(e)
+ if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != nil {
+ return e
}
return nil
}
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 866757e3129..c4c9dcfe829 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -8,6 +8,7 @@ import (
"errors"
"runtime"
"syscall"
+ "unsafe"
)
func (p *Process) Wait(options int) (w *Waitmsg, err error) {
@@ -22,7 +23,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
}
var ec uint32
e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec)
- if e != 0 {
+ if e != nil {
return nil, NewSyscallError("GetExitCodeProcess", e)
}
p.done = true
@@ -39,7 +40,7 @@ func (p *Process) Signal(sig Signal) error {
e := syscall.TerminateProcess(syscall.Handle(p.handle), 1)
return NewSyscallError("TerminateProcess", e)
}
- return Errno(syscall.EWINDOWS)
+ return syscall.Errno(syscall.EWINDOWS)
}
func (p *Process) Release() error {
@@ -47,7 +48,7 @@ func (p *Process) Release() error {
return EINVAL
}
e := syscall.CloseHandle(syscall.Handle(p.handle))
- if e != 0 {
+ if e != nil {
return NewSyscallError("CloseHandle", e)
}
p.handle = -1
@@ -60,8 +61,22 @@ func FindProcess(pid int) (p *Process, err error) {
const da = syscall.STANDARD_RIGHTS_READ |
syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
h, e := syscall.OpenProcess(da, false, uint32(pid))
- if e != 0 {
+ if e != nil {
return nil, NewSyscallError("OpenProcess", e)
}
return newProcess(pid, int(h)), nil
}
+
+func init() {
+ var argc int32
+ cmd := syscall.GetCommandLine()
+ argv, e := syscall.CommandLineToArgv(cmd, &argc)
+ if e != nil {
+ return
+ }
+ defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
+ Args = make([]string, argc)
+ for i, v := range (*argv)[:argc] {
+ Args[i] = string(syscall.UTF16ToString((*v)[:]))
+ }
+}
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
new file mode 100644
index 00000000000..9c6ef429744
--- /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 0f3b2db7ea2..71845d3c9c9 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,38 +51,38 @@ 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
}
- if n == 0 && len(b) > 0 && !iserror(e) {
+ if n == 0 && len(b) > 0 && e == nil {
return 0, io.EOF
}
- if iserror(e) {
- err = &PathError{"read", file.name, Errno(e)}
+ if e != nil {
+ 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)
- if m == 0 && !iserror(e) {
+ m, e := f.pread(b, off)
+ if m == 0 && e == nil {
return n, io.EOF
}
- if iserror(e) {
- err = &PathError{"read", file.name, Errno(e)}
+ if e != nil {
+ 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 iserror(e) {
- err = &PathError{"write", file.name, Errno(e)}
+ if e != nil {
+ 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)
- if iserror(e) {
- err = &PathError{"write", file.name, Errno(e)}
+ m, e := f.pwrite(b, off)
+ if e != nil {
+ err = &PathError{"write", f.name, e}
break
}
n += m
@@ -136,40 +136,40 @@ 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 !iserror(e) && 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 iserror(e) {
- return 0, &PathError{"seek", file.name, Errno(e)}
+ if e != nil {
+ 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.
// It returns an error, if any.
func Mkdir(name string, perm uint32) error {
e := syscall.Mkdir(name, perm)
- if iserror(e) {
- return &PathError{"mkdir", name, Errno(e)}
+ if e != nil {
+ return &PathError{"mkdir", name, e}
}
return nil
}
// Chdir changes the current working directory to the named directory.
func Chdir(dir string) error {
- if e := syscall.Chdir(dir); iserror(e) {
- return &PathError{"chdir", dir, Errno(e)}
+ if e := syscall.Chdir(dir); e != nil {
+ return &PathError{"chdir", dir, e}
}
return nil
}
@@ -177,8 +177,8 @@ func Chdir(dir string) error {
// Chdir changes the current working directory to the file,
// which must be a directory.
func (f *File) Chdir() error {
- if e := syscall.Fchdir(f.fd); iserror(e) {
- return &PathError{"chdir", f.name, Errno(e)}
+ if e := syscall.Fchdir(f.fd); e != nil {
+ return &PathError{"chdir", f.name, e}
}
return nil
}
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 42332e157ef..42fefa96fe8 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -11,6 +11,14 @@ import (
// File represents an open file descriptor.
type File struct {
+ *file
+}
+
+// file is the real representation of *File.
+// The extra level of indirection ensures that no clients of os
+// can overwrite this data, which could cause the finalizer
+// to close the wrong file descriptor.
+type file struct {
fd int
name string
dirinfo *dirInfo // nil unless directory being read
@@ -29,8 +37,8 @@ func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
- f := &File{fd: fd, name: name}
- runtime.SetFinalizer(f, (*File).Close)
+ f := &File{&file{fd: fd, name: name}}
+ runtime.SetFinalizer(f.file, (*file).close)
return f
}
@@ -41,7 +49,7 @@ type dirInfo struct {
bufp int // location of next record in buf.
}
-func epipecheck(file *File, e syscall.Error) {
+func epipecheck(file *File, e error) {
}
// DevNull is the name of the operating system's ``null device.''
@@ -56,7 +64,7 @@ const DevNull = "/dev/null"
func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
var (
fd int
- e syscall.Error
+ e error
create bool
excl bool
trunc bool
@@ -85,7 +93,7 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
} else {
fd, e = syscall.Open(name, flag)
if e != nil && create {
- var e1 syscall.Error
+ var e1 error
fd, e1 = syscall.Create(name, flag, perm)
if e1 == nil {
e = nil
@@ -110,6 +118,10 @@ 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 (file *file) close() error {
if file == nil || file.fd < 0 {
return Ebadfd
}
@@ -130,7 +142,7 @@ func (file *File) Close() error {
// It returns the FileInfo and an error, if any.
func (f *File) Stat() (fi *FileInfo, err error) {
d, err := dirstat(f)
- if iserror(err) {
+ if err != nil {
return nil, err
}
return fileInfoFromStat(new(FileInfo), d), err
@@ -144,7 +156,7 @@ func (f *File) Truncate(size int64) error {
d.Length = uint64(size)
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
return &PathError{"truncate", f.name, e}
}
return nil
@@ -157,12 +169,12 @@ func (f *File) Chmod(mode uint32) error {
d.Null()
odir, e := dirstat(f)
- if iserror(e) {
+ if e != nil {
return &PathError{"chmod", f.name, e}
}
d.Mode = (odir.Mode & mask) | (mode &^ mask)
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
return &PathError{"chmod", f.name, e}
}
return nil
@@ -179,7 +191,7 @@ func (f *File) Sync() (err error) {
var d Dir
d.Null()
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
return NewSyscallError("fsync", e)
}
return nil
@@ -187,26 +199,26 @@ func (f *File) Sync() (err error) {
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
-func (f *File) read(b []byte) (n int, err syscall.Error) {
+func (f *File) read(b []byte) (n int, err error) {
return syscall.Read(f.fd, b)
}
// pread 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 nil.
-func (f *File) pread(b []byte, off int64) (n int, err syscall.Error) {
+func (f *File) pread(b []byte, off int64) (n int, err error) {
return syscall.Pread(f.fd, b, off)
}
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
-func (f *File) write(b []byte) (n int, err syscall.Error) {
+func (f *File) write(b []byte) (n int, err error) {
return syscall.Write(f.fd, b)
}
// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
-func (f *File) pwrite(b []byte, off int64) (n int, err syscall.Error) {
+func (f *File) pwrite(b []byte, off int64) (n int, err error) {
return syscall.Pwrite(f.fd, b, off)
}
@@ -214,7 +226,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err syscall.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 (f *File) seek(offset int64, whence int) (ret int64, err syscall.Error) {
+func (f *File) seek(offset int64, whence int) (ret int64, err error) {
return syscall.Seek(f.fd, offset, whence)
}
@@ -226,7 +238,7 @@ func Truncate(name string, size int64) error {
d.Length = uint64(size)
- if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
return &PathError{"truncate", name, e}
}
return nil
@@ -234,7 +246,7 @@ func Truncate(name string, size int64) error {
// Remove removes the named file or directory.
func Remove(name string) error {
- if e := syscall.Remove(name); iserror(e) {
+ if e := syscall.Remove(name); e != nil {
return &PathError{"remove", name, e}
}
return nil
@@ -247,7 +259,7 @@ func Rename(oldname, newname string) error {
d.Name = newname
- if e := syscall.Wstat(oldname, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil {
return &PathError{"rename", oldname, e}
}
return nil
@@ -260,12 +272,12 @@ func Chmod(name string, mode uint32) error {
d.Null()
odir, e := dirstat(name)
- if iserror(e) {
+ if e != nil {
return &PathError{"chmod", name, e}
}
d.Mode = (odir.Mode & mask) | (mode &^ mask)
- if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
@@ -284,7 +296,7 @@ func Chtimes(name string, atimeNs int64, mtimeNs int64) error {
d.Atime = uint32(atimeNs / 1e9)
d.Mtime = uint32(mtimeNs / 1e9)
- if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) {
+ if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
@@ -294,7 +306,7 @@ func Pipe() (r *File, w *File, err error) {
var p [2]int
syscall.ForkLock.RLock()
- if e := syscall.Pipe(p[0:]); iserror(e) {
+ if e := syscall.Pipe(p[0:]); e != nil {
syscall.ForkLock.RUnlock()
return nil, nil, NewSyscallError("pipe", e)
}
@@ -329,3 +341,8 @@ func Lchown(name string, uid, gid int) error {
func (f *File) Chown(uid, gid int) error {
return EPLAN9
}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ return "/tmp"
+}
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index c937b6d6266..a4ab5d6ae25 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -8,11 +8,12 @@ package os
import (
"syscall"
+ "time"
)
func sigpipe() // implemented in package runtime
-func epipecheck(file *File, e int) {
+func epipecheck(file *File, e error) {
if e == syscall.EPIPE {
file.nepipe++
if file.nepipe >= 10 {
@@ -30,11 +31,11 @@ func Remove(name string) error {
// Try both: it is cheaper on average than
// doing a Stat plus the right one.
e := syscall.Unlink(name)
- if !iserror(e) {
+ if e == nil {
return nil
}
e1 := syscall.Rmdir(name)
- if !iserror(e1) {
+ if e1 == nil {
return nil
}
@@ -53,7 +54,7 @@ func Remove(name string) error {
if e1 != syscall.ENOTDIR {
e = e1
}
- return &PathError{"remove", name, Errno(e)}
+ return &PathError{"remove", name, e}
}
// LinkError records an error during a link or symlink or rename
@@ -72,8 +73,8 @@ func (e *LinkError) Error() string {
// Link creates a hard link.
func Link(oldname, newname string) error {
e := syscall.Link(oldname, newname)
- if iserror(e) {
- return &LinkError{"link", oldname, newname, Errno(e)}
+ if e != nil {
+ return &LinkError{"link", oldname, newname, e}
}
return nil
}
@@ -81,8 +82,8 @@ func Link(oldname, newname string) error {
// Symlink creates a symbolic link.
func Symlink(oldname, newname string) error {
e := syscall.Symlink(oldname, newname)
- if iserror(e) {
- return &LinkError{"symlink", oldname, newname, Errno(e)}
+ if e != nil {
+ return &LinkError{"symlink", oldname, newname, e}
}
return nil
}
@@ -93,8 +94,8 @@ func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
n, e := syscall.Readlink(name, b)
- if iserror(e) {
- return "", &PathError{"readlink", name, Errno(e)}
+ if e != nil {
+ return "", &PathError{"readlink", name, e}
}
if n < len {
return string(b[0:n]), nil
@@ -107,8 +108,8 @@ func Readlink(name string) (string, error) {
// Rename renames a file.
func Rename(oldname, newname string) error {
e := syscall.Rename(oldname, newname)
- if iserror(e) {
- return &LinkError{"rename", oldname, newname, Errno(e)}
+ if e != nil {
+ return &LinkError{"rename", oldname, newname, e}
}
return nil
}
@@ -116,16 +117,16 @@ func Rename(oldname, newname string) error {
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
func Chmod(name string, mode uint32) error {
- if e := syscall.Chmod(name, mode); iserror(e) {
- return &PathError{"chmod", name, Errno(e)}
+ if e := syscall.Chmod(name, mode); e != nil {
+ return &PathError{"chmod", name, e}
}
return nil
}
// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode uint32) error {
- if e := syscall.Fchmod(f.fd, mode); iserror(e) {
- return &PathError{"chmod", f.name, Errno(e)}
+ if e := syscall.Fchmod(f.fd, mode); e != nil {
+ return &PathError{"chmod", f.name, e}
}
return nil
}
@@ -133,8 +134,8 @@ func (f *File) Chmod(mode uint32) error {
// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) error {
- if e := syscall.Chown(name, uid, gid); iserror(e) {
- return &PathError{"chown", name, Errno(e)}
+ if e := syscall.Chown(name, uid, gid); e != nil {
+ return &PathError{"chown", name, e}
}
return nil
}
@@ -142,16 +143,16 @@ func Chown(name string, uid, gid int) error {
// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) error {
- if e := syscall.Lchown(name, uid, gid); iserror(e) {
- return &PathError{"lchown", name, Errno(e)}
+ if e := syscall.Lchown(name, uid, gid); e != nil {
+ return &PathError{"lchown", name, e}
}
return nil
}
// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) error {
- if e := syscall.Fchown(f.fd, uid, gid); iserror(e) {
- return &PathError{"chown", f.name, Errno(e)}
+ if e := syscall.Fchown(f.fd, uid, gid); e != nil {
+ return &PathError{"chown", f.name, e}
}
return nil
}
@@ -159,8 +160,8 @@ func (f *File) Chown(uid, gid int) error {
// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) error {
- if e := syscall.Ftruncate(f.fd, size); iserror(e) {
- return &PathError{"truncate", f.name, Errno(e)}
+ if e := syscall.Ftruncate(f.fd, size); e != nil {
+ return &PathError{"truncate", f.name, e}
}
return nil
}
@@ -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); iserror(e) {
+ if e := syscall.Fsync(f.fd); e != nil {
return NewSyscallError("fsync", e)
}
return nil
@@ -181,15 +182,16 @@ 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:]); iserror(e) {
- return &PathError{"chtimes", name, Errno(e)}
+ if e := syscall.Utimes(name, utimes[0:]); e != nil {
+ return &PathError{"chtimes", name, e}
}
return nil
}
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index a7ccb337eb5..671d1a4cb62 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -13,6 +13,14 @@ import (
// File represents an open file descriptor.
type File struct {
+ *file
+}
+
+// file is the real representation of *File.
+// The extra level of indirection ensures that no clients of os
+// can overwrite this data, which could cause the finalizer
+// to close the wrong file descriptor.
+type file struct {
fd int
name string
dirinfo *dirInfo // nil unless directory being read
@@ -20,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.
@@ -32,8 +40,8 @@ func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
- f := &File{fd: fd, name: name}
- runtime.SetFinalizer(f, (*File).Close)
+ f := &File{&file{fd: fd, name: name}}
+ runtime.SetFinalizer(f.file, (*file).close)
return f
}
@@ -54,8 +62,8 @@ const DevNull = "/dev/null"
// It returns the File and an error, if any.
func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
- if e != 0 {
- return nil, &PathError{"open", name, Errno(e)}
+ if e != nil {
+ return nil, &PathError{"open", name, e}
}
// There's a race here with fork/exec, which we are
@@ -69,18 +77,22 @@ 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 {
+func (f *File) Close() error {
+ return f.file.close()
+}
+
+func (file *file) close() error {
if file == nil || file.fd < 0 {
return EINVAL
}
var err error
- if e := syscall.Close(file.fd); e != 0 {
- err = &PathError{"close", file.name, Errno(e)}
+ if e := syscall.Close(file.fd); e != nil {
+ err = &PathError{"close", file.name, e}
}
if file.dirinfo != nil {
if libc_closedir(file.dirinfo.dir) < 0 && err == nil {
- err = &PathError{"closedir", file.name, Errno(syscall.GetErrno())}
+ err = &PathError{"closedir", file.name, syscall.GetErrno()}
}
}
@@ -93,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 != 0 {
- return nil, &PathError{"stat", file.name, Errno(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 iserror(e) {
- return nil, &PathError{"stat", name, Errno(e)}
- }
- statp := &lstat
- if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
- e := syscall.Stat(name, &stat)
- if !iserror(e) {
- statp = &stat
- }
+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 iserror(e) {
- return nil, &PathError{"lstat", name, Errno(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.
//
@@ -150,47 +155,47 @@ 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.
// It returns the number of bytes read and an error, if any.
-func (f *File) read(b []byte) (n int, err int) {
+func (f *File) read(b []byte) (n int, err error) {
return syscall.Read(f.fd, b)
}
// pread 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 0.
-func (f *File) pread(b []byte, off int64) (n int, err int) {
+func (f *File) pread(b []byte, off int64) (n int, err error) {
return syscall.Pread(f.fd, b, off)
}
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
-func (f *File) write(b []byte) (n int, err int) {
+func (f *File) write(b []byte) (n int, err error) {
return syscall.Write(f.fd, b)
}
// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
-func (f *File) pwrite(b []byte, off int64) (n int, err int) {
+func (f *File) pwrite(b []byte, off int64) (n int, err error) {
return syscall.Pwrite(f.fd, b, off)
}
@@ -198,15 +203,15 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) {
// 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 (f *File) seek(offset int64, whence int) (ret int64, err int) {
+func (f *File) seek(offset int64, whence int) (ret int64, err error) {
return syscall.Seek(f.fd, offset, whence)
}
// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
func Truncate(name string, size int64) error {
- if e := syscall.Truncate(name, size); e != 0 {
- return &PathError{"truncate", name, Errno(e)}
+ if e := syscall.Truncate(name, size); e != nil {
+ return &PathError{"truncate", name, e}
}
return nil
}
@@ -237,7 +242,7 @@ func Pipe() (r *File, w *File, err error) {
// See ../syscall/exec.go for description of lock.
syscall.ForkLock.RLock()
e := syscall.Pipe(p[0:])
- if iserror(e) {
+ if e != nil {
syscall.ForkLock.RUnlock()
return nil, nil, NewSyscallError("pipe", e)
}
@@ -247,3 +252,12 @@ func Pipe() (r *File, w *File, err error) {
return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ dir := Getenv("TMPDIR")
+ if dir == "" {
+ dir = "/tmp"
+ }
+ return dir
+}
diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go
index d5f5ae6bed7..a0d3c99a503 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 1d6d872d5c7..299d2e86155 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -14,11 +14,12 @@ import (
"strings"
"syscall"
"testing"
+ "time"
)
var dot = []string{
"dir_unix.go",
- "env_unix.go",
+ "env.go",
"error.go",
"file.go",
"os_test.go",
@@ -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()
@@ -940,11 +890,6 @@ func TestHostname(t *testing.T) {
return
}
- // TODO(jsing): Fix nametomib() on OpenBSD
- if syscall.OS == "openbsd" {
- return
- }
-
// Check internal Hostname() against the output of /bin/hostname.
// Allow that the internal Hostname returns a Fully Qualified Domain Name
// and the /bin/hostname only returns the first component
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
new file mode 100644
index 00000000000..3109a8171a5
--- /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 82fade63ae2..bc14a783188 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/proc.go b/libgo/go/os/proc.go
index d21f849f210..61545f4456a 100644
--- a/libgo/go/os/proc.go
+++ b/libgo/go/os/proc.go
@@ -8,8 +8,8 @@ package os
import "syscall"
-var Args []string // provided by runtime
-var Envs []string // provided by runtime
+// Args hold the command-line arguments, starting with the program name.
+var Args []string
// Getuid returns the numeric user id of the caller.
func Getuid() int { return syscall.Getuid() }
diff --git a/libgo/go/os/stat.go b/libgo/go/os/stat.go
index 8eb4ab4391d..c664fc189b8 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 6d3a3813b09..66189a6b9ba 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/stat_plan9.go b/libgo/go/os/stat_plan9.go
index 8df9e580cc4..e4a1dbbaea3 100644
--- a/libgo/go/os/stat_plan9.go
+++ b/libgo/go/os/stat_plan9.go
@@ -34,7 +34,7 @@ func dirstat(arg interface{}) (d *Dir, err error) {
buf := make([]byte, nd)
var n int
- var e syscall.Error
+ var e error
switch syscallArg := arg.(type) {
case *File:
@@ -72,7 +72,7 @@ func dirstat(arg interface{}) (d *Dir, err error) {
// Stat returns a FileInfo structure describing the named file and an error, if any.
func Stat(name string) (fi *FileInfo, err error) {
d, err := dirstat(name)
- if iserror(err) {
+ if err != nil {
return nil, err
}
return fileInfoFromStat(new(FileInfo), d), err
@@ -83,7 +83,7 @@ func Stat(name string) (fi *FileInfo, err error) {
// the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link.
func Lstat(name string) (fi *FileInfo, err error) {
d, err := dirstat(name)
- if iserror(err) {
+ if err != nil {
return nil, err
}
return fileInfoFromStat(new(FileInfo), d), err
diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go
index 67133a1898b..c6a6de5c816 100644
--- a/libgo/go/os/sys_bsd.go
+++ b/libgo/go/os/sys_bsd.go
@@ -12,10 +12,9 @@ package os
import "syscall"
func Hostname() (name string, err error) {
- var errno int
- name, errno = syscall.Sysctl("kern.hostname")
- if errno != 0 {
- return "", NewSyscallError("sysctl kern.hostname", errno)
+ name, err = syscall.Sysctl("kern.hostname")
+ if err != nil {
+ return "", NewSyscallError("sysctl kern.hostname", err)
}
return name, nil
}
diff --git a/libgo/go/os/sys_uname.go b/libgo/go/os/sys_uname.go
index 0e68647acdc..313ee62aef6 100644
--- a/libgo/go/os/sys_uname.go
+++ b/libgo/go/os/sys_uname.go
@@ -10,7 +10,7 @@ import "syscall"
func Hostname() (name string, err error) {
var u syscall.Utsname
- if errno := syscall.Uname(&u); errno != 0 {
+ if errno := syscall.Uname(&u); errno != nil {
return "", NewSyscallError("uname", errno)
}
b := make([]byte, len(u.Nodename))
diff --git a/libgo/go/os/time.go b/libgo/go/os/time.go
index a4678ee2a16..eb564e57a6c 100644
--- a/libgo/go/os/time.go
+++ b/libgo/go/os/time.go
@@ -12,7 +12,7 @@ import "syscall"
// time is the Unix epoch.
func Time() (sec int64, nsec int64, err error) {
var tv syscall.Timeval
- if e := syscall.Gettimeofday(&tv); iserror(e) {
+ if e := syscall.Gettimeofday(&tv); e != nil {
return 0, 0, NewSyscallError("gettimeofday", e)
}
return int64(tv.Sec), int64(tv.Usec) * 1000, err
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index df57b59a388..2638153ddbe 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/lookup_unix.go b/libgo/go/os/user/lookup_unix.go
index ba4e1ac7991..89886cb03cf 100644
--- a/libgo/go/os/user/lookup_unix.go
+++ b/libgo/go/os/user/lookup_unix.go
@@ -8,7 +8,6 @@ package user
import (
"fmt"
- "os"
"strings"
"syscall"
"unsafe"
@@ -70,7 +69,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) {
bufSize,
&result)
if rv != 0 {
- return nil, fmt.Errorf("user: lookup username %s: %s", username, os.Errno(syscall.GetErrno()))
+ return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.GetErrno())
}
if result == nil {
return nil, UnknownUserError(username)
@@ -82,7 +81,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) {
bufSize,
&result)
if rv != 0 {
- return nil, fmt.Errorf("user: lookup userid %d: %s", uid, os.Errno(syscall.GetErrno()))
+ return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.GetErrno())
}
if result == nil {
return nil, UnknownUserIdError(uid)
diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go
index 59f15e4c675..f9f44af8a93 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 454eadececa..5c233fbaebf 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 8cf1f9ad107..c3678f541d4 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 1b5d6c36495..e3d6c342ca6 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 c81cbf0ddc7..2bd62d34aeb 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/reflect/all_test.go b/libgo/go/reflect/all_test.go
index 7c1022ecb48..cf31d1ba39d 100644
--- a/libgo/go/reflect/all_test.go
+++ b/libgo/go/reflect/all_test.go
@@ -16,6 +16,13 @@ import (
"unsafe"
)
+func TestBool(t *testing.T) {
+ v := ValueOf(true)
+ if v.Bool() != true {
+ t.Fatal("ValueOf(true).Bool() = false")
+ }
+}
+
type integer int
type T struct {
a int
@@ -215,7 +222,8 @@ func TestTypes(t *testing.T) {
func TestSet(t *testing.T) {
for i, tt := range valueTests {
- v := ValueOf(tt.i).Elem()
+ v := ValueOf(tt.i)
+ v = v.Elem()
switch v.Kind() {
case Int:
v.SetInt(132)
@@ -651,6 +659,14 @@ var deepEqualTests = []DeepEqualTest{
{nil, 1, false},
{1, nil, false},
+ // Nil vs empty: not the same.
+ {[]int{}, []int(nil), false},
+ {[]int{}, []int{}, true},
+ {[]int(nil), []int(nil), true},
+ {map[int]int{}, map[int]int(nil), false},
+ {map[int]int{}, map[int]int{}, true},
+ {map[int]int(nil), map[int]int(nil), true},
+
// Mismatched types
{1, 1.0, false},
{int32(1), int64(1), false},
@@ -1092,21 +1108,38 @@ func TestMethod(t *testing.T) {
}
// Curried method of value.
- i = ValueOf(p).Method(1).Call([]Value{ValueOf(10)})[0].Int()
+ tfunc := TypeOf(func(int) int(nil))
+ v := ValueOf(p).Method(1)
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Value Method Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Value Method returned %d; want 250", i)
}
- i = ValueOf(p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int()
+ v = ValueOf(p).MethodByName("Dist")
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Value MethodByName returned %d; want 250", i)
}
// Curried method of pointer.
- i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int()
+ v = ValueOf(&p).Method(1)
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Value Method returned %d; want 250", i)
}
- i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int()
+ v = ValueOf(&p).MethodByName("Dist")
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Value MethodByName returned %d; want 250", i)
}
@@ -1121,11 +1154,19 @@ func TestMethod(t *testing.T) {
}
}{p}
pv := ValueOf(s).Field(0)
- i = pv.Method(0).Call([]Value{ValueOf(10)})[0].Int()
+ v = pv.Method(0)
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Interface Method Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i)
}
- i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int()
+ v = pv.MethodByName("Dist")
+ if tt := v.Type(); tt != tfunc {
+ t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc)
+ }
+ i = v.Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Interface MethodByName returned %d; want 250", i)
}
diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go
index 63c28fe2024..df5ec0a6099 100644
--- a/libgo/go/reflect/deepequal.go
+++ b/libgo/go/reflect/deepequal.go
@@ -69,6 +69,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
}
return true
case Slice:
+ if v1.IsNil() != v2.IsNil() {
+ return false
+ }
if v1.Len() != v2.Len() {
return false
}
@@ -93,6 +96,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
}
return true
case Map:
+ if v1.IsNil() != v2.IsNil() {
+ return false
+ }
if v1.Len() != v2.Len() {
return false
}
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index 9be7f830875..07cc0f5ba40 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -188,7 +188,7 @@ type Type interface {
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
-type Kind uint8
+type Kind uint
const (
Invalid Kind = iota
@@ -455,15 +455,16 @@ func (t *uncommonType) Method(i int) (m Method) {
if p.name != nil {
m.Name = *p.name
}
- flag := uint32(0)
+ fl := flag(Func) << flagKindShift
if p.pkgPath != nil {
m.PkgPath = *p.pkgPath
- flag |= flagRO
+ fl |= flagRO
}
- m.Type = toType(p.typ)
+ mt := toCommonType(p.typ)
+ m.Type = mt.toType()
x := new(unsafe.Pointer)
*x = p.tfn
- m.Func = valueFromIword(flag, m.Type, iword(uintptr(unsafe.Pointer(x))))
+ m.Func = Value{mt, unsafe.Pointer(x), fl|flagIndir}
m.Index = i
return
}
@@ -769,7 +770,7 @@ func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
return
}
- p := t.fields[i]
+ p := &t.fields[i]
f.Type = toType(p.typ)
if p.name != nil {
f.Name = *p.name
@@ -868,10 +869,12 @@ L:
if n == 1 {
// Found matching field.
- if len(ff.Index) <= depth {
+ if depth >= len(ff.Index) {
ff.Index = make([]int, depth+1)
}
- ff.Index[depth] = fi
+ if len(ff.Index) > 1 {
+ ff.Index[depth] = fi
+ }
} else {
// None or more than one matching field found.
fd = inf
@@ -903,9 +906,6 @@ func toCommonType(p *runtime.Type) *commonType {
return nil
}
x := unsafe.Pointer(p)
- if uintptr(x)&reflectFlags != 0 {
- panic("reflect: invalid interface value")
- }
return (*commonType)(x)
}
@@ -967,10 +967,12 @@ func (t *commonType) runtimeType() *runtime.Type {
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
func PtrTo(t Type) Type {
- // If t records its pointer-to type, use it.
- ct := t.(*commonType)
+ return t.(*commonType).ptrTo()
+}
+
+func (ct *commonType) ptrTo() *commonType {
if p := ct.ptrToThis; p != nil {
- return toType(p)
+ return toCommonType(p)
}
// Otherwise, synthesize one.
@@ -982,7 +984,7 @@ func PtrTo(t Type) Type {
if m := ptrMap.m; m != nil {
if p := m[ct]; p != nil {
ptrMap.RUnlock()
- return p.commonType.toType()
+ return &p.commonType
}
}
ptrMap.RUnlock()
@@ -994,7 +996,7 @@ func PtrTo(t Type) Type {
if p != nil {
// some other goroutine won the race and created it
ptrMap.Unlock()
- return p
+ return &p.commonType
}
rt := (*runtime.Type)(unsafe.Pointer(ct))
@@ -1024,7 +1026,7 @@ func PtrTo(t Type) Type {
ptrMap.m[ct] = p
ptrMap.Unlock()
- return p.commonType.toType()
+ return &p.commonType
}
func (t *commonType) Implements(u Type) bool {
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index dbdfa09242f..b57ed462c84 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -11,6 +11,7 @@ import (
"unsafe"
)
+const bigEndian = false // can be smarter if we find a big-endian machine
const ptrSize = unsafe.Sizeof((*byte)(nil))
const cannotSet = "cannot set value obtained from unexported struct field"
@@ -53,14 +54,54 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) {
// its String method returns "<invalid Value>", and all other methods panic.
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
-//
-// The fields of Value are exported so that clients can copy and
-// pass Values around, but they should not be edited or inspected
-// directly. A future language change may make it possible not to
-// export these fields while still keeping Values usable as values.
type Value struct {
- Internal interface{}
- InternalMethod int
+ // typ holds the type of the value represented by a Value.
+ typ *commonType
+
+ // val holds the 1-word representation of the value.
+ // If flag's flagIndir bit is set, then val is a pointer to the data.
+ // Otherwise val is a word holding the actual data.
+ // When the data is smaller than a word, it begins at
+ // the first byte (in the memory address sense) of val.
+ // We use unsafe.Pointer so that the garbage collector
+ // knows that val could be a pointer.
+ val unsafe.Pointer
+
+ // flag holds metadata about the value.
+ // The lowest bits are flag bits:
+ // - flagRO: obtained via unexported field, so read-only
+ // - flagIndir: val holds a pointer to the data
+ // - flagAddr: v.CanAddr is true (implies flagIndir)
+ // - flagMethod: v is a method value.
+ // The next five bits give the Kind of the value.
+ // This repeats typ.Kind() except for method values.
+ // The remaining 23+ bits give a method number for method values.
+ // If flag.kind() != Func, code can assume that flagMethod is unset.
+ // If typ.size > ptrSize, code can assume that flagIndir is set.
+ flag
+
+ // A method value represents a curried method invocation
+ // like r.Read for some receiver r. The typ+val+flag bits describe
+ // the receiver r, but the flag's Kind bits say Func (methods are
+ // functions), and the top bits of the flag give the method number
+ // in r's type's method table.
+}
+
+type flag uintptr
+
+const (
+ flagRO flag = 1 << iota
+ flagIndir
+ flagAddr
+ flagMethod
+ flagKindShift = iota
+ flagKindWidth = 5 // there are 27 kinds
+ flagKindMask flag = 1<<flagKindWidth - 1
+ flagMethodShift = flagKindShift + flagKindWidth
+)
+
+func (f flag) kind() Kind {
+ return Kind((f >> flagKindShift) & flagKindMask)
}
// A ValueError occurs when a Value method is invoked on
@@ -92,17 +133,30 @@ func methodName() string {
// An iword is the word that would be stored in an
// interface to represent a given value v. Specifically, if v is
// bigger than a pointer, its word is a pointer to v's data.
-// Otherwise, its word is a zero uintptr with the data stored
-// in the leading bytes.
-type iword uintptr
+// Otherwise, its word holds the data stored
+// in its leading bytes (so is not a pointer).
+// Because the value sometimes holds a pointer, we use
+// unsafe.Pointer to represent it, so that if iword appears
+// in a struct, the garbage collector knows that might be
+// a pointer.
+type iword unsafe.Pointer
+
+func (v Value) iword() iword {
+ if v.flag&flagIndir != 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) {
+ // Have indirect but want direct word.
+ return loadIword(v.val, v.typ.size)
+ }
+ return iword(v.val)
+}
-func loadIword(p unsafe.Pointer, size uintptr) iword {
+// loadIword loads n bytes at p from memory into an iword.
+func loadIword(p unsafe.Pointer, n uintptr) iword {
// Run the copy ourselves instead of calling memmove
- // to avoid moving v to the heap.
- w := iword(0)
- switch size {
+ // to avoid moving w to the heap.
+ var w iword
+ switch n {
default:
- panic("reflect: internal error: loadIword of " + strconv.Itoa(int(size)) + "-byte value")
+ panic("reflect: internal error: loadIword of " + strconv.Itoa(int(n)) + "-byte value")
case 0:
case 1:
*(*uint8)(unsafe.Pointer(&w)) = *(*uint8)(p)
@@ -124,12 +178,13 @@ func loadIword(p unsafe.Pointer, size uintptr) iword {
return w
}
-func storeIword(p unsafe.Pointer, w iword, size uintptr) {
+// storeIword stores n bytes from w into p.
+func storeIword(p unsafe.Pointer, w iword, n uintptr) {
// Run the copy ourselves instead of calling memmove
- // to avoid moving v to the heap.
- switch size {
+ // to avoid moving w to the heap.
+ switch n {
default:
- panic("reflect: internal error: storeIword of " + strconv.Itoa(int(size)) + "-byte value")
+ panic("reflect: internal error: storeIword of " + strconv.Itoa(int(n)) + "-byte value")
case 0:
case 1:
*(*uint8)(p) = *(*uint8)(unsafe.Pointer(&w))
@@ -166,237 +221,42 @@ type nonEmptyInterface struct {
word iword
}
-// Regarding the implementation of Value:
-//
-// The Internal interface is a true interface value in the Go sense,
-// but it also serves as a (type, address) pair in which one cannot
-// be changed separately from the other. That is, it serves as a way
-// to prevent unsafe mutations of the Internal state even though
-// we cannot (yet?) hide the field while preserving the ability for
-// clients to make copies of Values.
-//
-// The internal method converts a Value into the expanded internalValue struct.
-// If we could avoid exporting fields we'd probably make internalValue the
-// definition of Value.
-//
-// If a Value is addressable (CanAddr returns true), then the Internal
-// interface value holds a pointer to the actual field data, and Set stores
-// through that pointer. If a Value is not addressable (CanAddr returns false),
-// then the Internal interface value holds the actual value.
-//
-// In addition to whether a value is addressable, we track whether it was
-// obtained by using an unexported struct field. Such values are allowed
-// to be read, mainly to make fmt.Print more useful, but they are not
-// allowed to be written. We call such values read-only.
-//
-// A Value can be set (via the Set, SetUint, etc. methods) only if it is both
-// addressable and not read-only.
-//
-// The two permission bits - addressable and read-only - are stored in
-// the bottom two bits of the type pointer in the interface value.
-//
-// ordinary value: Internal = value
-// addressable value: Internal = value, Internal.typ |= flagAddr
-// read-only value: Internal = value, Internal.typ |= flagRO
-// addressable, read-only value: Internal = value, Internal.typ |= flagAddr | flagRO
-//
-// It is important that the read-only values have the extra bit set
-// (as opposed to using the bit to mean writable), because client code
-// can grab the interface field and try to use it. Having the extra bit
-// set makes the type pointer compare not equal to any real type,
-// so that a client cannot, say, write through v.Internal.(*int).
-// The runtime routines that access interface types reject types with
-// low bits set.
-//
-// If a Value fv = v.Method(i), then fv = v with the InternalMethod
-// field set to i+1. Methods are never addressable.
-//
-// All in all, this is a lot of effort just to avoid making this new API
-// depend on a language change we'll probably do anyway, but
-// it's helpful to keep the two separate, and much of the logic is
-// necessary to implement the Interface method anyway.
-
-const (
- flagAddr uint32 = 1 << iota // holds address of value
- flagRO // read-only
-
- reflectFlags = 3
-)
-
-// An internalValue is the unpacked form of a Value.
-// The zero Value unpacks to a zero internalValue
-type internalValue struct {
- typ *commonType // type of value
- kind Kind // kind of value
- flag uint32
- word iword
- addr unsafe.Pointer
- rcvr iword
- method bool
- nilmethod bool
-}
-
-func (v Value) internal() internalValue {
- var iv internalValue
- eface := *(*emptyInterface)(unsafe.Pointer(&v.Internal))
- p := uintptr(unsafe.Pointer(eface.typ))
- iv.typ = toCommonType((*runtime.Type)(unsafe.Pointer(p &^ reflectFlags)))
- if iv.typ == nil {
- return iv
- }
- iv.flag = uint32(p & reflectFlags)
- iv.word = eface.word
- if iv.flag&flagAddr != 0 {
- iv.addr = unsafe.Pointer(uintptr(iv.word))
- iv.typ = iv.typ.Elem().common()
- if Kind(iv.typ.kind) == Ptr || Kind(iv.typ.kind) == UnsafePointer {
- iv.word = loadIword(iv.addr, iv.typ.size)
- }
- } else {
- if Kind(iv.typ.kind) != Ptr && Kind(iv.typ.kind) != UnsafePointer {
- iv.addr = unsafe.Pointer(uintptr(iv.word))
- }
+// mustBe panics if f's kind is not expected.
+// Making this a method on flag instead of on Value
+// (and embedding flag in Value) means that we can write
+// the very clear v.mustBe(Bool) and have it compile into
+// v.flag.mustBe(Bool), which will only bother to copy the
+// single important word for the receiver.
+func (f flag) mustBe(expected Kind) {
+ k := f.kind()
+ if k != expected {
+ panic(&ValueError{methodName(), k})
}
- iv.kind = iv.typ.Kind()
-
- // Is this a method? If so, iv describes the receiver.
- // Rewrite to describe the method function.
- if v.InternalMethod != 0 {
- // If this Value is a method value (x.Method(i) for some Value x)
- // then we will invoke it using the interface form of the method,
- // which always passes the receiver as a single word.
- // Record that information.
- i := v.InternalMethod - 1
- if iv.kind == Interface {
- it := (*interfaceType)(unsafe.Pointer(iv.typ))
- if i < 0 || i >= len(it.methods) {
- panic("reflect: broken Value")
- }
- m := &it.methods[i]
- if m.pkgPath != nil {
- iv.flag |= flagRO
- }
- iv.typ = toCommonType(m.typ)
- iface := (*nonEmptyInterface)(iv.addr)
- if iface.itab == nil {
- iv.word = 0
- iv.nilmethod = true
- } else {
- iv.word = iword(uintptr(iface.itab.fun[i]))
- }
- iv.rcvr = iface.word
- } else {
- ut := iv.typ.uncommon()
- if ut == nil || i < 0 || i >= len(ut.methods) {
- panic("reflect: broken Value")
- }
- m := &ut.methods[i]
- if m.pkgPath != nil {
- iv.flag |= flagRO
- }
- iv.typ = toCommonType(m.mtyp)
- iv.rcvr = iv.word
- iv.word = iword(uintptr(m.tfn))
- }
- if iv.word != 0 {
- p := new(iword)
- *p = iv.word
- iv.word = iword(uintptr(unsafe.Pointer(p)))
- }
- iv.kind = Func
- iv.method = true
- iv.flag &^= flagAddr
- iv.addr = unsafe.Pointer(uintptr(iv.word))
- }
-
- return iv
}
-// packValue returns a Value with the given flag bits, type, and interface word.
-func packValue(flag uint32, typ *runtime.Type, word iword) Value {
- if typ == nil {
- panic("packValue")
+// mustBeExported panics if f records that the value was obtained using
+// an unexported field.
+func (f flag) mustBeExported() {
+ if f == 0 {
+ panic(&ValueError{methodName(), 0})
}
- t := uintptr(unsafe.Pointer(typ))
- t |= uintptr(flag)
- eface := emptyInterface{(*runtime.Type)(unsafe.Pointer(t)), word}
- return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))}
-}
-
-var dummy struct {
- b bool
- x interface{}
-}
-
-// Dummy annotation marking that the value x escapes,
-// for use in cases where the reflect code is so clever that
-// the compiler cannot follow.
-func escapes(x interface{}) {
- if dummy.b {
- dummy.x = x
- }
-}
-
-// valueFromAddr returns a Value using the given type and address.
-func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value {
- // TODO(rsc): Eliminate this terrible hack.
- // The escape analysis knows that addr is a pointer
- // but it doesn't see addr get passed to anything
- // that keeps it. packValue keeps it, but packValue
- // takes a uintptr (iword(addr)), and integers (non-pointers)
- // are assumed not to matter. The escapes function works
- // because return values always escape (for now).
- escapes(addr)
-
- if flag&flagAddr != 0 {
- // Addressable, so the internal value is
- // an interface containing a pointer to the real value.
- return packValue(flag, PtrTo(typ).runtimeType(), iword(uintptr(addr)))
- }
-
- var w iword
- if k := typ.Kind(); k == Ptr || k == UnsafePointer {
- // In line, so the interface word is the actual value.
- w = loadIword(addr, typ.Size())
- } else {
- // Not in line: the interface word is the address.
- w = iword(uintptr(addr))
- }
- return packValue(flag, typ.runtimeType(), w)
-}
-
-// valueFromIword returns a Value using the given type and interface word.
-func valueFromIword(flag uint32, typ Type, w iword) Value {
- if flag&flagAddr != 0 {
- panic("reflect: internal error: valueFromIword addressable")
- }
- return packValue(flag, typ.runtimeType(), w)
-}
-
-func (iv internalValue) mustBe(want Kind) {
- if iv.kind != want {
- panic(&ValueError{methodName(), iv.kind})
- }
-}
-
-func (iv internalValue) mustBeExported() {
- if iv.kind == 0 {
- panic(&ValueError{methodName(), iv.kind})
- }
- if iv.flag&flagRO != 0 {
+ if f&flagRO != 0 {
panic(methodName() + " using value obtained using unexported field")
}
}
-func (iv internalValue) mustBeAssignable() {
- if iv.kind == 0 {
- panic(&ValueError{methodName(), iv.kind})
+// mustBeAssignable panics if f records that the value is not assignable,
+// which is to say that either it was obtained using an unexported field
+// or it is not addressable.
+func (f flag) mustBeAssignable() {
+ if f == 0 {
+ panic(&ValueError{methodName(), Invalid})
}
// Assignable if addressable and not read-only.
- if iv.flag&flagRO != 0 {
+ if f&flagRO != 0 {
panic(methodName() + " using value obtained using unexported field")
}
- if iv.flag&flagAddr == 0 {
+ if f&flagAddr == 0 {
panic(methodName() + " using unaddressable value")
}
}
@@ -407,31 +267,31 @@ func (iv internalValue) mustBeAssignable() {
// or slice element in order to call a method that requires a
// pointer receiver.
func (v Value) Addr() Value {
- iv := v.internal()
- if iv.flag&flagAddr == 0 {
+ if v.flag&flagAddr == 0 {
panic("reflect.Value.Addr of unaddressable value")
}
- return valueFromIword(iv.flag&flagRO, PtrTo(iv.typ.toType()), iword(uintptr(iv.addr)))
+ return Value{v.typ.ptrTo(), v.val, (v.flag & flagRO) | flag(Ptr)<<flagKindShift}
}
// Bool returns v's underlying value.
// It panics if v's kind is not Bool.
func (v Value) Bool() bool {
- iv := v.internal()
- iv.mustBe(Bool)
- return *(*bool)(unsafe.Pointer(iv.addr))
+ v.mustBe(Bool)
+ if v.flag&flagIndir != 0 {
+ return *(*bool)(v.val)
+ }
+ return *(*bool)(unsafe.Pointer(&v.val))
}
// Bytes returns v's underlying value.
// It panics if v's underlying value is not a slice of bytes.
func (v Value) Bytes() []byte {
- iv := v.internal()
- iv.mustBe(Slice)
- typ := iv.typ.toType()
- if typ.Elem().Kind() != Uint8 {
+ v.mustBe(Slice)
+ if v.typ.Elem().Kind() != Uint8 {
panic("reflect.Value.Bytes of non-byte slice")
}
- return *(*[]byte)(iv.addr)
+ // Slice is always bigger than a word; assume flagIndir.
+ return *(*[]byte)(v.val)
}
// CanAddr returns true if the value's address can be obtained with Addr.
@@ -440,8 +300,7 @@ func (v Value) Bytes() []byte {
// a field of an addressable struct, or the result of dereferencing a pointer.
// If CanAddr returns false, calling Addr will panic.
func (v Value) CanAddr() bool {
- iv := v.internal()
- return iv.flag&flagAddr != 0
+ return v.flag&flagAddr != 0
}
// CanSet returns true if the value of v can be changed.
@@ -450,8 +309,7 @@ func (v Value) CanAddr() bool {
// If CanSet returns false, calling Set or any type-specific
// setter (e.g., SetBool, SetInt64) will panic.
func (v Value) CanSet() bool {
- iv := v.internal()
- return iv.flag&(flagAddr|flagRO) == flagAddr
+ return v.flag&(flagAddr|flagRO) == flagAddr
}
// Call calls the function v with the input arguments in.
@@ -463,10 +321,9 @@ func (v Value) CanSet() bool {
// If v is a variadic function, Call creates the variadic slice parameter
// itself, copying in the corresponding values.
func (v Value) Call(in []Value) []Value {
- iv := v.internal()
- iv.mustBe(Func)
- iv.mustBeExported()
- return iv.call("Call", in)
+ v.mustBe(Func)
+ v.mustBeExported()
+ return v.call("Call", in)
}
// CallSlice calls the variadic function v with the input arguments in,
@@ -477,22 +334,60 @@ func (v Value) Call(in []Value) []Value {
// As in Go, each input argument must be assignable to the
// type of the function's corresponding input parameter.
func (v Value) CallSlice(in []Value) []Value {
- iv := v.internal()
- iv.mustBe(Func)
- iv.mustBeExported()
- return iv.call("CallSlice", in)
-}
-
-func (iv internalValue) call(method string, in []Value) []Value {
- if iv.word == 0 {
- if iv.nilmethod {
- panic("reflect.Value.Call: call of method on nil interface value")
+ v.mustBe(Func)
+ v.mustBeExported()
+ return v.call("CallSlice", in)
+}
+
+func (v Value) call(method string, in []Value) []Value {
+ // Get function pointer, type.
+ t := v.typ
+ var (
+ fn unsafe.Pointer
+ rcvr iword
+ )
+ if v.flag&flagMethod != 0 {
+ i := int(v.flag) >> flagMethodShift
+ if v.typ.Kind() == Interface {
+ tt := (*interfaceType)(unsafe.Pointer(v.typ))
+ if i < 0 || i >= len(tt.methods) {
+ panic("reflect: broken Value")
+ }
+ m := &tt.methods[i]
+ if m.pkgPath != nil {
+ panic(method + " of unexported method")
+ }
+ t = toCommonType(m.typ)
+ iface := (*nonEmptyInterface)(v.val)
+ if iface.itab == nil {
+ panic(method + " of method on nil interface value")
+ }
+ fn = iface.itab.fun[i]
+ rcvr = iface.word
+ } else {
+ ut := v.typ.uncommon()
+ if ut == nil || i < 0 || i >= len(ut.methods) {
+ panic("reflect: broken Value")
+ }
+ m := &ut.methods[i]
+ if m.pkgPath != nil {
+ panic(method + " of unexported method")
+ }
+ fn = m.tfn
+ t = toCommonType(m.mtyp)
+ rcvr = v.iword()
}
+ } else if v.flag&flagIndir != 0 {
+ fn = *(*unsafe.Pointer)(v.val)
+ } else {
+ fn = v.val
+ }
+
+ if fn == nil {
panic("reflect.Value.Call: call of nil function")
}
isSlice := method == "CallSlice"
- t := iv.typ
n := t.NumIn()
if isSlice {
if !t.IsVariadic() {
@@ -549,34 +444,32 @@ func (iv internalValue) call(method string, in []Value) []Value {
}
nout := t.NumOut()
- if iv.method {
+ if v.flag&flagMethod != 0 {
nin++
}
params := make([]unsafe.Pointer, nin)
delta := 0
off := 0
- if iv.method {
+ if v.flag&flagMethod != 0 {
// Hard-wired first argument.
p := new(iword)
- *p = iv.rcvr
+ *p = rcvr
params[0] = unsafe.Pointer(p)
off = 1
}
-
first_pointer := false
- for i, v := range in {
- siv := v.internal()
- siv.mustBeExported()
+ for i, pv := range in {
+ pv.mustBeExported()
targ := t.In(i).(*commonType)
- siv = convertForAssignment("reflect.Value.Call", nil, targ, siv)
- if siv.addr == nil {
+ pv = pv.assignTo("reflect.Value.Call", targ, nil)
+ if pv.flag&flagIndir == 0 {
p := new(unsafe.Pointer)
- *p = unsafe.Pointer(uintptr(siv.word))
+ *p = pv.val
params[off] = unsafe.Pointer(p)
} else {
- params[off] = siv.addr
+ params[off] = pv.val
}
- if i == 0 && Kind(targ.kind) != Ptr && !iv.method && isMethod(iv.typ) {
+ if i == 0 && Kind(targ.kind) != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) {
p := new(unsafe.Pointer)
*p = params[off]
params[off] = unsafe.Pointer(p)
@@ -602,7 +495,7 @@ func (iv internalValue) call(method string, in []Value) []Value {
pr = &results[0]
}
- call(t, *(*unsafe.Pointer)(iv.addr), iv.method, first_pointer, pp, pr)
+ call(t, fn, v.flag&flagMethod != 0, first_pointer, pp, pr)
return ret
}
@@ -635,39 +528,42 @@ func isMethod(t *commonType) bool {
// Cap returns v's capacity.
// It panics if v's Kind is not Array, Chan, or Slice.
func (v Value) Cap() int {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Array:
- return iv.typ.Len()
+ return v.typ.Len()
case Chan:
- return int(chancap(*(*iword)(iv.addr)))
+ return int(chancap(*(*iword)(v.iword())))
case Slice:
- return (*SliceHeader)(iv.addr).Cap
+ // Slice is always bigger than a word; assume flagIndir.
+ return (*SliceHeader)(v.val).Cap
}
- panic(&ValueError{"reflect.Value.Cap", iv.kind})
+ panic(&ValueError{"reflect.Value.Cap", k})
}
// Close closes the channel v.
// It panics if v's Kind is not Chan.
func (v Value) Close() {
- iv := v.internal()
- iv.mustBe(Chan)
- iv.mustBeExported()
- ch := *(*iword)(iv.addr)
- chanclose(ch)
+ v.mustBe(Chan)
+ v.mustBeExported()
+ chanclose(*(*iword)(v.iword()))
}
// Complex returns v's underlying value, as a complex128.
// It panics if v's Kind is not Complex64 or Complex128
func (v Value) Complex() complex128 {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Complex64:
- return complex128(*(*complex64)(iv.addr))
+ if v.flag&flagIndir != 0 {
+ return complex128(*(*complex64)(v.val))
+ }
+ return complex128(*(*complex64)(unsafe.Pointer(&v.val)))
case Complex128:
- return *(*complex128)(iv.addr)
+ // complex128 is always bigger than a word; assume flagIndir.
+ return *(*complex128)(v.val)
}
- panic(&ValueError{"reflect.Value.Complex", iv.kind})
+ panic(&ValueError{"reflect.Value.Complex", k})
}
// Elem returns the value that the interface v contains
@@ -675,90 +571,94 @@ func (v Value) Complex() complex128 {
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
func (v Value) Elem() Value {
- iv := v.internal()
- return iv.Elem()
-}
-
-func (iv internalValue) Elem() Value {
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Interface:
- // Empty interface and non-empty interface have different layouts.
- // Convert to empty interface.
- var eface emptyInterface
- if iv.typ.NumMethod() == 0 {
- eface = *(*emptyInterface)(iv.addr)
+ var (
+ typ *commonType
+ val unsafe.Pointer
+ )
+ if v.typ.NumMethod() == 0 {
+ eface := (*emptyInterface)(v.val)
+ if eface.typ == nil {
+ // nil interface value
+ return Value{}
+ }
+ typ = toCommonType(eface.typ)
+ val = unsafe.Pointer(eface.word)
} else {
- iface := (*nonEmptyInterface)(iv.addr)
- if iface.itab != nil {
- eface.typ = iface.itab.typ
+ iface := (*nonEmptyInterface)(v.val)
+ if iface.itab == nil {
+ // nil interface value
+ return Value{}
}
- eface.word = iface.word
+ typ = toCommonType(iface.itab.typ)
+ val = unsafe.Pointer(iface.word)
}
- if eface.typ == nil {
- return Value{}
+ fl := v.flag & flagRO
+ fl |= flag(typ.Kind()) << flagKindShift
+ if typ.Kind() != Ptr && typ.Kind() != UnsafePointer {
+ fl |= flagIndir
}
- return valueFromIword(iv.flag&flagRO, toType(eface.typ), eface.word)
+ return Value{typ, val, fl}
case Ptr:
+ val := v.val
+ if v.flag&flagIndir != 0 {
+ val = *(*unsafe.Pointer)(val)
+ }
// The returned value's address is v's value.
- if iv.word == 0 {
+ if val == nil {
return Value{}
}
- return valueFromAddr(iv.flag&flagRO|flagAddr, iv.typ.Elem(), unsafe.Pointer(uintptr(iv.word)))
+ tt := (*ptrType)(unsafe.Pointer(v.typ))
+ typ := toCommonType(tt.elem)
+ fl := v.flag&flagRO | flagIndir | flagAddr
+ fl |= flag(typ.Kind() << flagKindShift)
+ return Value{typ, val, fl}
}
- panic(&ValueError{"reflect.Value.Elem", iv.kind})
+ panic(&ValueError{"reflect.Value.Elem", k})
}
// Field returns the i'th field of the struct v.
// It panics if v's Kind is not Struct or i is out of range.
func (v Value) Field(i int) Value {
- iv := v.internal()
- iv.mustBe(Struct)
- t := iv.typ.toType()
- if i < 0 || i >= t.NumField() {
+ v.mustBe(Struct)
+ tt := (*structType)(unsafe.Pointer(v.typ))
+ if i < 0 || i >= len(tt.fields) {
panic("reflect: Field index out of range")
}
- f := t.Field(i)
+ field := &tt.fields[i]
+ typ := toCommonType(field.typ)
// Inherit permission bits from v.
- flag := iv.flag
+ fl := v.flag & (flagRO | flagIndir | flagAddr)
// Using an unexported field forces flagRO.
- if f.PkgPath != "" {
- flag |= flagRO
+ if field.pkgPath != nil {
+ fl |= flagRO
}
- return valueFromValueOffset(flag, f.Type, iv, f.Offset)
-}
+ fl |= flag(typ.Kind()) << flagKindShift
-// valueFromValueOffset returns a sub-value of outer
-// (outer is an array or a struct) with the given flag and type
-// starting at the given byte offset into outer.
-func valueFromValueOffset(flag uint32, typ Type, outer internalValue, offset uintptr) Value {
- if outer.addr != nil {
- return valueFromAddr(flag, typ, unsafe.Pointer(uintptr(outer.addr)+offset))
+ var val unsafe.Pointer
+ switch {
+ case fl&flagIndir != 0:
+ // Indirect. Just bump pointer.
+ val = unsafe.Pointer(uintptr(v.val) + field.offset)
+ case bigEndian:
+ // Direct. Discard leading bytes.
+ val = unsafe.Pointer(uintptr(v.val) << (field.offset * 8))
+ default:
+ // Direct. Discard leading bytes.
+ val = unsafe.Pointer(uintptr(v.val) >> (field.offset * 8))
}
- // outer is so tiny it is in line.
- // We have to use outer.word and derive
- // the new word (it cannot possibly be bigger).
- // In line, so not addressable.
- if flag&flagAddr != 0 {
- panic("reflect: internal error: misuse of valueFromValueOffset")
- }
- b := *(*[ptrSize]byte)(unsafe.Pointer(&outer.word))
- for i := uintptr(0); i < typ.Size(); i++ {
- b[i] = b[offset+i]
- }
- for i := typ.Size(); i < ptrSize; i++ {
- b[i] = 0
- }
- w := *(*iword)(unsafe.Pointer(&b))
- return valueFromIword(flag, typ, w)
+ return Value{typ, val, fl}
}
// FieldByIndex returns the nested field corresponding to index.
// It panics if v's Kind is not struct.
func (v Value) FieldByIndex(index []int) Value {
- v.internal().mustBe(Struct)
+ v.mustBe(Struct)
for i, x := range index {
if i > 0 {
if v.Kind() == Ptr && v.Elem().Kind() == Struct {
@@ -774,9 +674,8 @@ func (v Value) FieldByIndex(index []int) Value {
// It returns the zero Value if no field was found.
// It panics if v's Kind is not struct.
func (v Value) FieldByName(name string) Value {
- iv := v.internal()
- iv.mustBe(Struct)
- if f, ok := iv.typ.FieldByName(name); ok {
+ v.mustBe(Struct)
+ if f, ok := v.typ.FieldByName(name); ok {
return v.FieldByIndex(f.Index)
}
return Value{}
@@ -787,8 +686,8 @@ func (v Value) FieldByName(name string) Value {
// It panics if v's Kind is not struct.
// It returns the zero Value if no field was found.
func (v Value) FieldByNameFunc(match func(string) bool) Value {
- v.internal().mustBe(Struct)
- if f, ok := v.Type().FieldByNameFunc(match); ok {
+ v.mustBe(Struct)
+ if f, ok := v.typ.FieldByNameFunc(match); ok {
return v.FieldByIndex(f.Index)
}
return Value{}
@@ -797,74 +696,101 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
// Float returns v's underlying value, as an float64.
// It panics if v's Kind is not Float32 or Float64
func (v Value) Float() float64 {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Float32:
- return float64(*(*float32)(iv.addr))
+ if v.flag&flagIndir != 0 {
+ return float64(*(*float32)(v.val))
+ }
+ return float64(*(*float32)(unsafe.Pointer(&v.val)))
case Float64:
- return *(*float64)(iv.addr)
+ if v.flag&flagIndir != 0 {
+ return *(*float64)(v.val)
+ }
+ return *(*float64)(unsafe.Pointer(&v.val))
}
- panic(&ValueError{"reflect.Value.Float", iv.kind})
+ panic(&ValueError{"reflect.Value.Float", k})
}
// Index returns v's i'th element.
// It panics if v's Kind is not Array or Slice or i is out of range.
func (v Value) Index(i int) Value {
- iv := v.internal()
- switch iv.kind {
- default:
- panic(&ValueError{"reflect.Value.Index", iv.kind})
+ k := v.kind()
+ switch k {
case Array:
- flag := iv.flag // element flag same as overall array
- t := iv.typ.toType()
- if i < 0 || i > t.Len() {
+ tt := (*arrayType)(unsafe.Pointer(v.typ))
+ if i < 0 || i > int(tt.len) {
panic("reflect: array index out of range")
}
- typ := t.Elem()
- return valueFromValueOffset(flag, typ, iv, uintptr(i)*typ.Size())
+ typ := toCommonType(tt.elem)
+ fl := v.flag & (flagRO | flagIndir | flagAddr) // bits same as overall array
+ fl |= flag(typ.Kind()) << flagKindShift
+ offset := uintptr(i) * typ.size
+
+ var val unsafe.Pointer
+ switch {
+ case fl&flagIndir != 0:
+ // Indirect. Just bump pointer.
+ val = unsafe.Pointer(uintptr(v.val) + offset)
+ case bigEndian:
+ // Direct. Discard leading bytes.
+ val = unsafe.Pointer(uintptr(v.val) << (offset * 8))
+ default:
+ // Direct. Discard leading bytes.
+ val = unsafe.Pointer(uintptr(v.val) >> (offset * 8))
+ }
+ return Value{typ, val, fl}
case Slice:
// Element flag same as Elem of Ptr.
- // Addressable, possibly read-only.
- flag := iv.flag&flagRO | flagAddr
- s := (*SliceHeader)(iv.addr)
+ // Addressable, indirect, possibly read-only.
+ fl := flagAddr | flagIndir | v.flag&flagRO
+ s := (*SliceHeader)(v.val)
if i < 0 || i >= s.Len {
panic("reflect: slice index out of range")
}
- typ := iv.typ.Elem()
- addr := unsafe.Pointer(s.Data + uintptr(i)*typ.Size())
- return valueFromAddr(flag, typ, addr)
+ tt := (*sliceType)(unsafe.Pointer(v.typ))
+ typ := toCommonType(tt.elem)
+ fl |= flag(typ.Kind()) << flagKindShift
+ val := unsafe.Pointer(s.Data + uintptr(i)*typ.size)
+ return Value{typ, val, fl}
}
-
- panic("not reached")
+ panic(&ValueError{"reflect.Value.Index", k})
}
// Int returns v's underlying value, as an int64.
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) Int() int64 {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ var p unsafe.Pointer
+ if v.flag&flagIndir != 0 {
+ p = v.val
+ } else {
+ // The escape analysis is good enough that &v.val
+ // does not trigger a heap allocation.
+ p = unsafe.Pointer(&v.val)
+ }
+ switch k {
case Int:
- return int64(*(*int)(iv.addr))
+ return int64(*(*int)(p))
case Int8:
- return int64(*(*int8)(iv.addr))
+ return int64(*(*int8)(p))
case Int16:
- return int64(*(*int16)(iv.addr))
+ return int64(*(*int16)(p))
case Int32:
- return int64(*(*int32)(iv.addr))
+ return int64(*(*int32)(p))
case Int64:
- return *(*int64)(iv.addr)
+ return int64(*(*int64)(p))
}
- panic(&ValueError{"reflect.Value.Int", iv.kind})
+ panic(&ValueError{"reflect.Value.Int", k})
}
// CanInterface returns true if Interface can be used without panicking.
func (v Value) CanInterface() bool {
- iv := v.internal()
- if iv.kind == Invalid {
- panic(&ValueError{"reflect.Value.CanInterface", iv.kind})
+ if v.flag == 0 {
+ panic(&ValueError{"reflect.Value.CanInterface", Invalid})
}
- return v.InternalMethod == 0 && iv.flag&flagRO == 0
+ return v.flag&(flagMethod|flagRO) == 0
}
// Interface returns v's value as an interface{}.
@@ -876,75 +802,72 @@ func (v Value) Interface() interface{} {
}
func valueInterface(v Value, safe bool) interface{} {
- iv := v.internal()
- return iv.valueInterface(safe)
-}
-
-func (iv internalValue) valueInterface(safe bool) interface{} {
- if iv.kind == 0 {
- panic(&ValueError{"reflect.Value.Interface", iv.kind})
+ if v.flag == 0 {
+ panic(&ValueError{"reflect.Value.Interface", 0})
}
- if iv.method {
+ if v.flag&flagMethod != 0 {
panic("reflect.Value.Interface: cannot create interface value for method with bound receiver")
}
- if safe && iv.flag&flagRO != 0 {
+ if safe && v.flag&flagRO != 0 {
// Do not allow access to unexported values via Interface,
// because they might be pointers that should not be
// writable or methods or function that should not be callable.
panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
}
- if iv.kind == Interface {
+
+ k := v.kind()
+ if k == Interface {
// Special case: return the element inside the interface.
- // Won't recurse further because an interface cannot contain an interface.
- if iv.IsNil() {
- return nil
+ // Empty interface has one layout, all interfaces with
+ // methods have a second layout.
+ if v.NumMethod() == 0 {
+ return *(*interface{})(v.val)
}
- return iv.Elem().Interface()
+ return *(*interface {
+ M()
+ })(v.val)
}
// Non-interface value.
var eface emptyInterface
- eface.typ = iv.typ.runtimeType()
- eface.word = iv.word
+ eface.typ = v.typ.runtimeType()
+ eface.word = v.iword()
return *(*interface{})(unsafe.Pointer(&eface))
}
// InterfaceData returns the interface v's value as a uintptr pair.
// It panics if v's Kind is not Interface.
func (v Value) InterfaceData() [2]uintptr {
- iv := v.internal()
- iv.mustBe(Interface)
+ v.mustBe(Interface)
// We treat this as a read operation, so we allow
// it even for unexported data, because the caller
// has to import "unsafe" to turn it into something
// that can be abused.
- return *(*[2]uintptr)(iv.addr)
+ // Interface value is always bigger than a word; assume flagIndir.
+ return *(*[2]uintptr)(v.val)
}
// IsNil returns true if v is a nil value.
// It panics if v's Kind is not Chan, Func, Interface, Map, Ptr, or Slice.
func (v Value) IsNil() bool {
- return v.internal().IsNil()
-}
-
-func (iv internalValue) IsNil() bool {
- switch iv.kind {
- case Ptr:
- if iv.method {
+ k := v.kind()
+ switch k {
+ case Chan, Func, Map, Ptr:
+ if v.flag&flagMethod != 0 {
panic("reflect: IsNil of method Value")
}
- return iv.word == 0
- case Chan, Func, Map:
- if iv.method {
- panic("reflect: IsNil of method Value")
+ ptr := v.val
+ if v.flag&flagIndir != 0 {
+ ptr = *(*unsafe.Pointer)(ptr)
}
- return *(*uintptr)(iv.addr) == 0
+ return ptr == nil
case Interface, Slice:
// Both interface and slice are nil if first word is 0.
- return *(*uintptr)(iv.addr) == 0
+ // Both are always bigger than a word; assume flagIndir.
+ return *(*unsafe.Pointer)(v.val) == nil
}
- panic(&ValueError{"reflect.Value.IsNil", iv.kind})
+ panic(&ValueError{"reflect.Value.IsNil", k})
}
// IsValid returns true if v represents a value.
@@ -953,32 +876,35 @@ func (iv internalValue) IsNil() bool {
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
func (v Value) IsValid() bool {
- return v.Internal != nil
+ return v.flag != 0
}
// Kind returns v's Kind.
// If v is the zero Value (IsValid returns false), Kind returns Invalid.
func (v Value) Kind() Kind {
- return v.internal().kind
+ return v.kind()
}
// Len returns v's length.
// It panics if v's Kind is not Array, Chan, Map, Slice, or String.
func (v Value) Len() int {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Array:
- return iv.typ.Len()
+ tt := (*arrayType)(unsafe.Pointer(v.typ))
+ return int(tt.len)
case Chan:
- return int(chanlen(*(*iword)(iv.addr)))
+ return int(chanlen(*(*iword)(v.iword())))
case Map:
- return int(maplen(*(*iword)(iv.addr)))
+ return int(maplen(*(*iword)(v.iword())))
case Slice:
- return (*SliceHeader)(iv.addr).Len
+ // Slice is bigger than a word; assume flagIndir.
+ return (*SliceHeader)(v.val).Len
case String:
- return (*StringHeader)(iv.addr).Len
+ // String is bigger than a word; assume flagIndir.
+ return (*StringHeader)(v.val).Len
}
- panic(&ValueError{"reflect.Value.Len", iv.kind})
+ panic(&ValueError{"reflect.Value.Len", k})
}
// MapIndex returns the value associated with key in the map v.
@@ -986,29 +912,29 @@ func (v Value) Len() int {
// It returns the zero Value if key is not found in the map or if v represents a nil map.
// As in Go, the key's value must be assignable to the map's key type.
func (v Value) MapIndex(key Value) Value {
- iv := v.internal()
- iv.mustBe(Map)
- typ := iv.typ.toType()
+ v.mustBe(Map)
+ tt := (*mapType)(unsafe.Pointer(v.typ))
- // Do not require ikey to be exported, so that DeepEqual
+ // Do not require key to be exported, so that DeepEqual
// and other programs can use all the keys returned by
// MapKeys as arguments to MapIndex. If either the map
// or the key is unexported, though, the result will be
- // considered unexported.
-
- ikey := key.internal()
- ikey = convertForAssignment("reflect.Value.MapIndex", nil, typ.Key(), ikey)
- if iv.word == 0 {
- return Value{}
- }
+ // considered unexported. This is consistent with the
+ // behavior for structs, which allow read but not write
+ // of unexported fields.
+ key = key.assignTo("reflect.Value.MapIndex", toCommonType(tt.key), nil)
- flag := (iv.flag | ikey.flag) & flagRO
- elemType := typ.Elem()
- elemWord, ok := mapaccess(typ.runtimeType(), *(*iword)(iv.addr), ikey.word)
+ word, ok := mapaccess(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword())
if !ok {
return Value{}
}
- return valueFromIword(flag, elemType, elemWord)
+ typ := toCommonType(tt.elem)
+ fl := (v.flag | key.flag) & flagRO
+ if typ.Kind() != Ptr && typ.Kind() != UnsafePointer {
+ fl |= flagIndir
+ }
+ fl |= flag(typ.Kind()) << flagKindShift
+ return Value{typ, unsafe.Pointer(word), fl}
}
// MapKeys returns a slice containing all the keys present in the map,
@@ -1016,17 +942,22 @@ func (v Value) MapIndex(key Value) Value {
// It panics if v's Kind is not Map.
// It returns an empty slice if v represents a nil map.
func (v Value) MapKeys() []Value {
- iv := v.internal()
- iv.mustBe(Map)
- keyType := iv.typ.Key()
+ v.mustBe(Map)
+ tt := (*mapType)(unsafe.Pointer(v.typ))
+ keyType := toCommonType(tt.key)
+
+ fl := v.flag & flagRO
+ fl |= flag(keyType.Kind()) << flagKindShift
+ if keyType.Kind() != Ptr && keyType.Kind() != UnsafePointer {
+ fl |= flagIndir
+ }
- flag := iv.flag & flagRO
- m := *(*iword)(iv.addr)
+ m := *(*iword)(v.iword())
mlen := int32(0)
- if m != 0 {
+ if m != nil {
mlen = maplen(m)
}
- it := mapiterinit(iv.typ.runtimeType(), m)
+ it := mapiterinit(v.typ.runtimeType(), m)
a := make([]Value, mlen)
var i int
for i = 0; i < len(a); i++ {
@@ -1034,7 +965,7 @@ func (v Value) MapKeys() []Value {
if !ok {
break
}
- a[i] = valueFromIword(flag, keyType, keyWord)
+ a[i] = Value{keyType, unsafe.Pointer(keyWord), fl}
mapiternext(it)
}
return a[:i]
@@ -1045,23 +976,27 @@ func (v Value) MapKeys() []Value {
// a receiver; the returned function will always use v as the receiver.
// Method panics if i is out of range.
func (v Value) Method(i int) Value {
- iv := v.internal()
- if iv.kind == Invalid {
+ if v.typ == nil {
panic(&ValueError{"reflect.Value.Method", Invalid})
}
- if i < 0 || i >= iv.typ.NumMethod() {
+ if v.flag&flagMethod != 0 || i < 0 || i >= v.typ.NumMethod() {
panic("reflect: Method index out of range")
}
- return Value{v.Internal, i + 1}
+ fl := v.flag & (flagRO | flagAddr | flagIndir)
+ fl |= flag(Func) << flagKindShift
+ fl |= flag(i)<<flagMethodShift | flagMethod
+ return Value{v.typ, v.val, fl}
}
// NumMethod returns the number of methods in the value's method set.
func (v Value) NumMethod() int {
- iv := v.internal()
- if iv.kind == Invalid {
+ if v.typ == nil {
panic(&ValueError{"reflect.Value.NumMethod", Invalid})
}
- return iv.typ.NumMethod()
+ if v.flag&flagMethod != 0 {
+ return 0
+ }
+ return v.typ.NumMethod()
}
// MethodByName returns a function value corresponding to the method
@@ -1070,49 +1005,51 @@ func (v Value) NumMethod() int {
// a receiver; the returned function will always use v as the receiver.
// It returns the zero Value if no method was found.
func (v Value) MethodByName(name string) Value {
- iv := v.internal()
- if iv.kind == Invalid {
+ if v.typ == nil {
panic(&ValueError{"reflect.Value.MethodByName", Invalid})
}
- m, ok := iv.typ.MethodByName(name)
- if ok {
- return Value{v.Internal, m.Index + 1}
+ if v.flag&flagMethod != 0 {
+ return Value{}
}
- return Value{}
+ m, ok := v.typ.MethodByName(name)
+ if !ok {
+ return Value{}
+ }
+ return v.Method(m.Index)
}
// NumField returns the number of fields in the struct v.
// It panics if v's Kind is not Struct.
func (v Value) NumField() int {
- iv := v.internal()
- iv.mustBe(Struct)
- return iv.typ.NumField()
+ v.mustBe(Struct)
+ tt := (*structType)(unsafe.Pointer(v.typ))
+ return len(tt.fields)
}
// OverflowComplex returns true if the complex128 x cannot be represented by v's type.
// It panics if v's Kind is not Complex64 or Complex128.
func (v Value) OverflowComplex(x complex128) bool {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Complex64:
return overflowFloat32(real(x)) || overflowFloat32(imag(x))
case Complex128:
return false
}
- panic(&ValueError{"reflect.Value.OverflowComplex", iv.kind})
+ panic(&ValueError{"reflect.Value.OverflowComplex", k})
}
// OverflowFloat returns true if the float64 x cannot be represented by v's type.
// It panics if v's Kind is not Float32 or Float64.
func (v Value) OverflowFloat(x float64) bool {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Float32:
return overflowFloat32(x)
case Float64:
return false
}
- panic(&ValueError{"reflect.Value.OverflowFloat", iv.kind})
+ panic(&ValueError{"reflect.Value.OverflowFloat", k})
}
func overflowFloat32(x float64) bool {
@@ -1125,27 +1062,27 @@ func overflowFloat32(x float64) bool {
// OverflowInt returns true if the int64 x cannot be represented by v's type.
// It panics if v's Kind is not Int, Int8, int16, Int32, or Int64.
func (v Value) OverflowInt(x int64) bool {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Int, Int8, Int16, Int32, Int64:
- bitSize := iv.typ.size * 8
+ bitSize := v.typ.size * 8
trunc := (x << (64 - bitSize)) >> (64 - bitSize)
return x != trunc
}
- panic(&ValueError{"reflect.Value.OverflowInt", iv.kind})
+ panic(&ValueError{"reflect.Value.OverflowInt", k})
}
// OverflowUint returns true if the uint64 x cannot be represented by v's type.
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
func (v Value) OverflowUint(x uint64) bool {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ switch k {
case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64:
- bitSize := iv.typ.size * 8
+ bitSize := v.typ.size * 8
trunc := (x << (64 - bitSize)) >> (64 - bitSize)
return x != trunc
}
- panic(&ValueError{"reflect.Value.OverflowUint", iv.kind})
+ panic(&ValueError{"reflect.Value.OverflowUint", k})
}
// Pointer returns v's value as a uintptr.
@@ -1154,22 +1091,21 @@ func (v Value) OverflowUint(x uint64) bool {
// without importing the unsafe package explicitly.
// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
func (v Value) Pointer() uintptr {
- iv := v.internal()
- switch iv.kind {
- case Ptr, UnsafePointer:
- if iv.kind == Func && v.InternalMethod != 0 {
+ k := v.kind()
+ switch k {
+ case Chan, Func, Map, Ptr, UnsafePointer:
+ if k == Func && v.flag&flagMethod != 0 {
panic("reflect.Value.Pointer of method Value")
}
- return uintptr(iv.word)
- case Chan, Func, Map:
- if iv.kind == Func && v.InternalMethod != 0 {
- panic("reflect.Value.Pointer of method Value")
+ p := v.val
+ if v.flag&flagIndir != 0 {
+ p = *(*unsafe.Pointer)(p)
}
- return *(*uintptr)(iv.addr)
+ return uintptr(p)
case Slice:
- return (*SliceHeader)(iv.addr).Data
+ return (*SliceHeader)(v.val).Data
}
- panic(&ValueError{"reflect.Value.Pointer", iv.kind})
+ panic(&ValueError{"reflect.Value.Pointer", k})
}
// Recv receives and returns a value from the channel v.
@@ -1178,25 +1114,26 @@ func (v Value) Pointer() uintptr {
// The boolean value ok is true if the value x corresponds to a send
// on the channel, false if it is a zero value received because the channel is closed.
func (v Value) Recv() (x Value, ok bool) {
- iv := v.internal()
- iv.mustBe(Chan)
- iv.mustBeExported()
- return iv.recv(false)
+ v.mustBe(Chan)
+ v.mustBeExported()
+ return v.recv(false)
}
-// internal recv, possibly non-blocking (nb)
-func (iv internalValue) recv(nb bool) (val Value, ok bool) {
- t := iv.typ.toType()
- if t.ChanDir()&RecvDir == 0 {
+// internal recv, possibly non-blocking (nb).
+// v is known to be a channel.
+func (v Value) recv(nb bool) (val Value, ok bool) {
+ tt := (*chanType)(unsafe.Pointer(v.typ))
+ if ChanDir(tt.dir)&RecvDir == 0 {
panic("recv on send-only channel")
}
- ch := *(*iword)(iv.addr)
- if ch == 0 {
- panic("recv on nil channel")
- }
- valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb)
+ word, selected, ok := chanrecv(v.typ.runtimeType(), *(*iword)(v.iword()), nb)
if selected {
- val = valueFromIword(0, t.Elem(), valWord)
+ typ := toCommonType(tt.elem)
+ fl := flag(typ.Kind()) << flagKindShift
+ if typ.Kind() != Ptr && typ.Kind() != UnsafePointer {
+ fl |= flagIndir
+ }
+ val = Value{typ, unsafe.Pointer(word), fl}
}
return
}
@@ -1205,128 +1142,114 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) {
// It panics if v's kind is not Chan or if x's type is not the same type as v's element type.
// As in Go, x's value must be assignable to the channel's element type.
func (v Value) Send(x Value) {
- iv := v.internal()
- iv.mustBe(Chan)
- iv.mustBeExported()
- iv.send(x, false)
+ v.mustBe(Chan)
+ v.mustBeExported()
+ v.send(x, false)
}
-// internal send, possibly non-blocking
-func (iv internalValue) send(x Value, nb bool) (selected bool) {
- t := iv.typ.toType()
- if t.ChanDir()&SendDir == 0 {
+// internal send, possibly non-blocking.
+// v is known to be a channel.
+func (v Value) send(x Value, nb bool) (selected bool) {
+ tt := (*chanType)(unsafe.Pointer(v.typ))
+ if ChanDir(tt.dir)&SendDir == 0 {
panic("send on recv-only channel")
}
- ix := x.internal()
- ix.mustBeExported() // do not let unexported x leak
- ix = convertForAssignment("reflect.Value.Send", nil, t.Elem(), ix)
- ch := *(*iword)(iv.addr)
- if ch == 0 {
- panic("send on nil channel")
- }
- return chansend(iv.typ.runtimeType(), ch, ix.word, nb)
+ x.mustBeExported()
+ x = x.assignTo("reflect.Value.Send", toCommonType(tt.elem), nil)
+ return chansend(v.typ.runtimeType(), *(*iword)(v.iword()), x.iword(), nb)
}
// Set assigns x to the value v.
// It panics if CanSet returns false.
// As in Go, x's value must be assignable to v's type.
func (v Value) Set(x Value) {
- iv := v.internal()
- ix := x.internal()
-
- iv.mustBeAssignable()
- ix.mustBeExported() // do not let unexported x leak
-
- ix = convertForAssignment("reflect.Set", iv.addr, iv.typ, ix)
-
- n := ix.typ.size
- if Kind(ix.typ.kind) == Ptr || Kind(ix.typ.kind) == UnsafePointer {
- storeIword(iv.addr, ix.word, n)
+ v.mustBeAssignable()
+ x.mustBeExported() // do not let unexported x leak
+ var target *interface{}
+ if v.kind() == Interface {
+ target = (*interface{})(v.val)
+ }
+ x = x.assignTo("reflect.Set", v.typ, target)
+ if x.flag&flagIndir != 0 {
+ memmove(v.val, x.val, v.typ.size)
} else {
- memmove(iv.addr, ix.addr, n)
+ storeIword(v.val, iword(x.val), v.typ.size)
}
}
// SetBool sets v's underlying value.
// It panics if v's Kind is not Bool or if CanSet() is false.
func (v Value) SetBool(x bool) {
- iv := v.internal()
- iv.mustBeAssignable()
- iv.mustBe(Bool)
- *(*bool)(iv.addr) = x
+ v.mustBeAssignable()
+ v.mustBe(Bool)
+ *(*bool)(v.val) = x
}
// SetBytes sets v's underlying value.
// It panics if v's underlying value is not a slice of bytes.
func (v Value) SetBytes(x []byte) {
- iv := v.internal()
- iv.mustBeAssignable()
- iv.mustBe(Slice)
- typ := iv.typ.toType()
- if typ.Elem().Kind() != Uint8 {
+ v.mustBeAssignable()
+ v.mustBe(Slice)
+ if v.typ.Elem().Kind() != Uint8 {
panic("reflect.Value.SetBytes of non-byte slice")
}
- *(*[]byte)(iv.addr) = x
+ *(*[]byte)(v.val) = x
}
// SetComplex sets v's underlying value to x.
// It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false.
func (v Value) SetComplex(x complex128) {
- iv := v.internal()
- iv.mustBeAssignable()
- switch iv.kind {
+ v.mustBeAssignable()
+ switch k := v.kind(); k {
default:
- panic(&ValueError{"reflect.Value.SetComplex", iv.kind})
+ panic(&ValueError{"reflect.Value.SetComplex", k})
case Complex64:
- *(*complex64)(iv.addr) = complex64(x)
+ *(*complex64)(v.val) = complex64(x)
case Complex128:
- *(*complex128)(iv.addr) = x
+ *(*complex128)(v.val) = x
}
}
// SetFloat sets v's underlying value to x.
// It panics if v's Kind is not Float32 or Float64, or if CanSet() is false.
func (v Value) SetFloat(x float64) {
- iv := v.internal()
- iv.mustBeAssignable()
- switch iv.kind {
+ v.mustBeAssignable()
+ switch k := v.kind(); k {
default:
- panic(&ValueError{"reflect.Value.SetFloat", iv.kind})
+ panic(&ValueError{"reflect.Value.SetFloat", k})
case Float32:
- *(*float32)(iv.addr) = float32(x)
+ *(*float32)(v.val) = float32(x)
case Float64:
- *(*float64)(iv.addr) = x
+ *(*float64)(v.val) = x
}
}
// SetInt sets v's underlying value to x.
// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64, or if CanSet() is false.
func (v Value) SetInt(x int64) {
- iv := v.internal()
- iv.mustBeAssignable()
- switch iv.kind {
+ v.mustBeAssignable()
+ switch k := v.kind(); k {
default:
- panic(&ValueError{"reflect.Value.SetInt", iv.kind})
+ panic(&ValueError{"reflect.Value.SetInt", k})
case Int:
- *(*int)(iv.addr) = int(x)
+ *(*int)(v.val) = int(x)
case Int8:
- *(*int8)(iv.addr) = int8(x)
+ *(*int8)(v.val) = int8(x)
case Int16:
- *(*int16)(iv.addr) = int16(x)
+ *(*int16)(v.val) = int16(x)
case Int32:
- *(*int32)(iv.addr) = int32(x)
+ *(*int32)(v.val) = int32(x)
case Int64:
- *(*int64)(iv.addr) = x
+ *(*int64)(v.val) = x
}
}
// SetLen sets v's length to n.
// It panics if v's Kind is not Slice.
func (v Value) SetLen(n int) {
- iv := v.internal()
- iv.mustBeAssignable()
- iv.mustBe(Slice)
- s := (*SliceHeader)(iv.addr)
+ v.mustBeAssignable()
+ v.mustBe(Slice)
+ s := (*SliceHeader)(v.val)
if n < 0 || n > int(s.Cap) {
panic("reflect: slice length out of range in SetLen")
}
@@ -1339,88 +1262,84 @@ func (v Value) SetLen(n int) {
// As in Go, key's value must be assignable to the map's key type,
// and val's value must be assignable to the map's value type.
func (v Value) SetMapIndex(key, val Value) {
- iv := v.internal()
- ikey := key.internal()
- ival := val.internal()
-
- iv.mustBe(Map)
- iv.mustBeExported()
-
- ikey.mustBeExported()
- ikey = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Key(), ikey)
-
- if ival.kind != Invalid {
- ival.mustBeExported()
- ival = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Elem(), ival)
+ v.mustBe(Map)
+ v.mustBeExported()
+ key.mustBeExported()
+ tt := (*mapType)(unsafe.Pointer(v.typ))
+ key = key.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.key), nil)
+ if val.typ != nil {
+ val.mustBeExported()
+ val = val.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.elem), nil)
}
-
- mapassign(iv.typ.runtimeType(), *(*iword)(iv.addr), ikey.word, ival.word, ival.kind != Invalid)
+ mapassign(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword(), val.iword(), val.typ != nil)
}
// SetUint sets v's underlying value to x.
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64, or if CanSet() is false.
func (v Value) SetUint(x uint64) {
- iv := v.internal()
- iv.mustBeAssignable()
- switch iv.kind {
+ v.mustBeAssignable()
+ switch k := v.kind(); k {
default:
- panic(&ValueError{"reflect.Value.SetUint", iv.kind})
+ panic(&ValueError{"reflect.Value.SetUint", k})
case Uint:
- *(*uint)(iv.addr) = uint(x)
+ *(*uint)(v.val) = uint(x)
case Uint8:
- *(*uint8)(iv.addr) = uint8(x)
+ *(*uint8)(v.val) = uint8(x)
case Uint16:
- *(*uint16)(iv.addr) = uint16(x)
+ *(*uint16)(v.val) = uint16(x)
case Uint32:
- *(*uint32)(iv.addr) = uint32(x)
+ *(*uint32)(v.val) = uint32(x)
case Uint64:
- *(*uint64)(iv.addr) = x
+ *(*uint64)(v.val) = x
case Uintptr:
- *(*uintptr)(iv.addr) = uintptr(x)
+ *(*uintptr)(v.val) = uintptr(x)
}
}
// SetPointer sets the unsafe.Pointer value v to x.
// It panics if v's Kind is not UnsafePointer.
func (v Value) SetPointer(x unsafe.Pointer) {
- iv := v.internal()
- iv.mustBeAssignable()
- iv.mustBe(UnsafePointer)
- *(*unsafe.Pointer)(iv.addr) = x
+ v.mustBeAssignable()
+ v.mustBe(UnsafePointer)
+ *(*unsafe.Pointer)(v.val) = x
}
// SetString sets v's underlying value to x.
// It panics if v's Kind is not String or if CanSet() is false.
func (v Value) SetString(x string) {
- iv := v.internal()
- iv.mustBeAssignable()
- iv.mustBe(String)
- *(*string)(iv.addr) = x
+ v.mustBeAssignable()
+ v.mustBe(String)
+ *(*string)(v.val) = x
}
// Slice returns a slice of v.
// It panics if v's Kind is not Array or Slice.
func (v Value) Slice(beg, end int) Value {
- iv := v.internal()
- if iv.kind != Array && iv.kind != Slice {
- panic(&ValueError{"reflect.Value.Slice", iv.kind})
- }
- cap := v.Cap()
- if beg < 0 || end < beg || end > cap {
- panic("reflect.Value.Slice: slice index out of bounds")
- }
- var typ Type
- var base uintptr
- switch iv.kind {
+ var (
+ cap int
+ typ *sliceType
+ base unsafe.Pointer
+ )
+ switch k := v.kind(); k {
+ default:
+ panic(&ValueError{"reflect.Value.Slice", k})
case Array:
- if iv.flag&flagAddr == 0 {
+ if v.flag&flagAddr == 0 {
panic("reflect.Value.Slice: slice of unaddressable array")
}
- typ = toType((*arrayType)(unsafe.Pointer(iv.typ)).slice)
- base = uintptr(iv.addr)
+ tt := (*arrayType)(unsafe.Pointer(v.typ))
+ cap = int(tt.len)
+ typ = (*sliceType)(unsafe.Pointer(toCommonType(tt.slice)))
+ base = v.val
case Slice:
- typ = iv.typ.toType()
- base = (*SliceHeader)(iv.addr).Data
+ typ = (*sliceType)(unsafe.Pointer(v.typ))
+ s := (*SliceHeader)(v.val)
+ base = unsafe.Pointer(s.Data)
+ cap = s.Cap
+
+ }
+ if beg < 0 || end < beg || end > cap {
+ panic("reflect.Value.Slice: slice index out of bounds")
}
// Declare slice so that gc can see the base pointer in it.
@@ -1428,11 +1347,12 @@ func (v Value) Slice(beg, end int) Value {
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x))
- s.Data = base + uintptr(beg)*typ.Elem().Size()
+ s.Data = uintptr(base) + uintptr(beg)*toCommonType(typ.elem).Size()
s.Len = end - beg
s.Cap = end - beg
- return valueFromAddr(iv.flag&flagRO, typ, unsafe.Pointer(&x))
+ fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
+ return Value{typ.common(), unsafe.Pointer(&x), fl}
}
// String returns the string v's underlying value, as a string.
@@ -1440,16 +1360,15 @@ func (v Value) Slice(beg, end int) Value {
// Unlike the other getters, it does not panic if v's Kind is not String.
// Instead, it returns a string of the form "<T value>" where T is v's type.
func (v Value) String() string {
- iv := v.internal()
- switch iv.kind {
+ switch k := v.kind(); k {
case Invalid:
return "<invalid Value>"
case String:
- return *(*string)(iv.addr)
+ return *(*string)(v.val)
}
// If you call String on a reflect.Value of other type, it's better to
// print something than to panic. Useful in debugging.
- return "<" + iv.typ.String() + " Value>"
+ return "<" + v.typ.String() + " Value>"
}
// TryRecv attempts to receive a value from the channel v but will not block.
@@ -1458,10 +1377,9 @@ func (v Value) String() string {
// The boolean ok is true if the value x corresponds to a send
// on the channel, false if it is a zero value received because the channel is closed.
func (v Value) TryRecv() (x Value, ok bool) {
- iv := v.internal()
- iv.mustBe(Chan)
- iv.mustBeExported()
- return iv.recv(true)
+ v.mustBe(Chan)
+ v.mustBeExported()
+ return v.recv(true)
}
// TrySend attempts to send x on the channel v but will not block.
@@ -1469,54 +1387,83 @@ func (v Value) TryRecv() (x Value, ok bool) {
// It returns true if the value was sent, false otherwise.
// As in Go, x's value must be assignable to the channel's element type.
func (v Value) TrySend(x Value) bool {
- iv := v.internal()
- iv.mustBe(Chan)
- iv.mustBeExported()
- return iv.send(x, true)
+ v.mustBe(Chan)
+ v.mustBeExported()
+ return v.send(x, true)
}
// Type returns v's type.
func (v Value) Type() Type {
- t := v.internal().typ
- if t == nil {
+ f := v.flag
+ if f == 0 {
panic(&ValueError{"reflect.Value.Type", Invalid})
}
- return t.toType()
+ if f&flagMethod == 0 {
+ // Easy case
+ return v.typ.toType()
+ }
+
+ // Method value.
+ // v.typ describes the receiver, not the method type.
+ i := int(v.flag) >> flagMethodShift
+ if v.typ.Kind() == Interface {
+ // Method on interface.
+ tt := (*interfaceType)(unsafe.Pointer(v.typ))
+ if i < 0 || i >= len(tt.methods) {
+ panic("reflect: broken Value")
+ }
+ m := &tt.methods[i]
+ return toCommonType(m.typ).toType()
+ }
+ // Method on concrete type.
+ ut := v.typ.uncommon()
+ if ut == nil || i < 0 || i >= len(ut.methods) {
+ panic("reflect: broken Value")
+ }
+ m := &ut.methods[i]
+ return toCommonType(m.mtyp).toType()
}
// Uint returns v's underlying value, as a uint64.
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
func (v Value) Uint() uint64 {
- iv := v.internal()
- switch iv.kind {
+ k := v.kind()
+ var p unsafe.Pointer
+ if v.flag&flagIndir != 0 {
+ p = v.val
+ } else {
+ // The escape analysis is good enough that &v.val
+ // does not trigger a heap allocation.
+ p = unsafe.Pointer(&v.val)
+ }
+ switch k {
case Uint:
- return uint64(*(*uint)(iv.addr))
+ return uint64(*(*uint)(p))
case Uint8:
- return uint64(*(*uint8)(iv.addr))
+ return uint64(*(*uint8)(p))
case Uint16:
- return uint64(*(*uint16)(iv.addr))
+ return uint64(*(*uint16)(p))
case Uint32:
- return uint64(*(*uint32)(iv.addr))
- case Uintptr:
- return uint64(*(*uintptr)(iv.addr))
+ return uint64(*(*uint32)(p))
case Uint64:
- return *(*uint64)(iv.addr)
+ return uint64(*(*uint64)(p))
+ case Uintptr:
+ return uint64(*(*uintptr)(p))
}
- panic(&ValueError{"reflect.Value.Uint", iv.kind})
+ panic(&ValueError{"reflect.Value.Uint", k})
}
// UnsafeAddr returns a pointer to v's data.
// It is for advanced clients that also import the "unsafe" package.
// It panics if v is not addressable.
func (v Value) UnsafeAddr() uintptr {
- iv := v.internal()
- if iv.kind == Invalid {
- panic(&ValueError{"reflect.Value.UnsafeAddr", iv.kind})
+ if v.typ == nil {
+ panic(&ValueError{"reflect.Value.UnsafeAddr", Invalid})
}
- if iv.flag&flagAddr == 0 {
+ if v.flag&flagAddr == 0 {
panic("reflect.Value.UnsafeAddr of unaddressable value")
}
- return uintptr(iv.addr)
+ return uintptr(v.val)
}
// StringHeader is the runtime representation of a string.
@@ -1536,7 +1483,7 @@ type SliceHeader struct {
func typesMustMatch(what string, t1, t2 Type) {
if t1 != t2 {
- panic("reflect: " + what + ": " + t1.String() + " != " + t2.String())
+ panic(what + ": " + t1.String() + " != " + t2.String())
}
}
@@ -1571,7 +1518,7 @@ func grow(s Value, extra int) (Value, int, int) {
// Append appends the values x to a slice s and returns the resulting slice.
// As in Go, each x's value must be assignable to the slice's element type.
func Append(s Value, x ...Value) Value {
- s.internal().mustBe(Slice)
+ s.mustBe(Slice)
s, i0, i1 := grow(s, len(x))
for i, j := i0, 0; i < i1; i, j = i+1, j+1 {
s.Index(i).Set(x[j])
@@ -1582,8 +1529,8 @@ func Append(s Value, x ...Value) Value {
// AppendSlice appends a slice t to a slice s and returns the resulting slice.
// The slices s and t must have the same element type.
func AppendSlice(s, t Value) Value {
- s.internal().mustBe(Slice)
- t.internal().mustBe(Slice)
+ s.mustBe(Slice)
+ t.mustBe(Slice)
typesMustMatch("reflect.AppendSlice", s.Type().Elem(), t.Type().Elem())
s, i0, i1 := grow(s, t.Len())
Copy(s.Slice(i0, i1), t)
@@ -1596,23 +1543,23 @@ func AppendSlice(s, t Value) Value {
// Dst and src each must have kind Slice or Array, and
// dst and src must have the same element type.
func Copy(dst, src Value) int {
- idst := dst.internal()
- isrc := src.internal()
-
- if idst.kind != Array && idst.kind != Slice {
- panic(&ValueError{"reflect.Copy", idst.kind})
+ dk := dst.kind()
+ if dk != Array && dk != Slice {
+ panic(&ValueError{"reflect.Copy", dk})
}
- if idst.kind == Array {
- idst.mustBeAssignable()
+ if dk == Array {
+ dst.mustBeAssignable()
}
- idst.mustBeExported()
- if isrc.kind != Array && isrc.kind != Slice {
- panic(&ValueError{"reflect.Copy", isrc.kind})
+ dst.mustBeExported()
+
+ sk := src.kind()
+ if sk != Array && sk != Slice {
+ panic(&ValueError{"reflect.Copy", sk})
}
- isrc.mustBeExported()
+ src.mustBeExported()
- de := idst.typ.Elem()
- se := isrc.typ.Elem()
+ de := dst.typ.Elem()
+ se := src.typ.Elem()
typesMustMatch("reflect.Copy", de, se)
n := dst.Len()
@@ -1622,7 +1569,7 @@ func Copy(dst, src Value) int {
// If sk is an in-line array, cannot take its address.
// Instead, copy element by element.
- if isrc.addr == nil {
+ if src.flag&flagIndir == 0 {
for i := 0; i < n; i++ {
dst.Index(i).Set(src.Index(i))
}
@@ -1631,15 +1578,15 @@ func Copy(dst, src Value) int {
// Copy via memmove.
var da, sa unsafe.Pointer
- if idst.kind == Array {
- da = idst.addr
+ if dk == Array {
+ da = dst.val
} else {
- da = unsafe.Pointer((*SliceHeader)(idst.addr).Data)
+ da = unsafe.Pointer((*SliceHeader)(dst.val).Data)
}
- if isrc.kind == Array {
- sa = isrc.addr
+ if sk == Array {
+ sa = src.val
} else {
- sa = unsafe.Pointer((*SliceHeader)(isrc.addr).Data)
+ sa = unsafe.Pointer((*SliceHeader)(src.val).Data)
}
memmove(da, sa, uintptr(n)*de.Size())
return n
@@ -1653,7 +1600,7 @@ func Copy(dst, src Value) int {
// for the specified slice type, length, and capacity.
func MakeSlice(typ Type, len, cap int) Value {
if typ.Kind() != Slice {
- panic("reflect: MakeSlice of non-slice type")
+ panic("reflect.MakeSlice of non-slice type")
}
// Declare slice so that gc can see the base pointer in it.
@@ -1665,31 +1612,31 @@ func MakeSlice(typ Type, len, cap int) Value {
s.Len = len
s.Cap = cap
- return valueFromAddr(0, typ, unsafe.Pointer(&x))
+ return Value{typ.common(), unsafe.Pointer(&x), flagIndir | flag(Slice)<<flagKindShift}
}
// MakeChan creates a new channel with the specified type and buffer size.
func MakeChan(typ Type, buffer int) Value {
if typ.Kind() != Chan {
- panic("reflect: MakeChan of non-chan type")
+ panic("reflect.MakeChan of non-chan type")
}
if buffer < 0 {
- panic("MakeChan: negative buffer size")
+ panic("reflect.MakeChan: negative buffer size")
}
if typ.ChanDir() != BothDir {
- panic("MakeChan: unidirectional channel type")
+ panic("reflect.MakeChan: unidirectional channel type")
}
ch := makechan(typ.runtimeType(), uint32(buffer))
- return valueFromIword(0, typ, ch)
+ return Value{typ.common(), unsafe.Pointer(ch), flagIndir | (flag(Chan)<<flagKindShift)}
}
// MakeMap creates a new map of the specified type.
func MakeMap(typ Type) Value {
if typ.Kind() != Map {
- panic("reflect: MakeMap of non-map type")
+ panic("reflect.MakeMap of non-map type")
}
m := makemap(typ.runtimeType())
- return valueFromIword(0, typ, m)
+ return Value{typ.common(), unsafe.Pointer(m), flagIndir | (flag(Map)<<flagKindShift)}
}
// Indirect returns the value that v points to.
@@ -1719,7 +1666,12 @@ func ValueOf(i interface{}) Value {
// For an interface value with the noAddr bit set,
// the representation is identical to an empty interface.
eface := *(*emptyInterface)(unsafe.Pointer(&i))
- return packValue(0, eface.typ, eface.word)
+ typ := toCommonType(eface.typ)
+ fl := flag(typ.Kind()) << flagKindShift
+ if typ.Kind() != Ptr && typ.Kind() != UnsafePointer {
+ fl |= flagIndir
+ }
+ return Value{typ, unsafe.Pointer(eface.word), fl}
}
// Zero returns a Value representing a zero value for the specified type.
@@ -1730,10 +1682,12 @@ func Zero(typ Type) Value {
if typ == nil {
panic("reflect: Zero(nil)")
}
- if typ.Kind() == Ptr || typ.Kind() == UnsafePointer {
- return valueFromIword(0, typ, 0)
+ t := typ.common()
+ fl := flag(t.Kind()) << flagKindShift
+ if t.Kind() == Ptr || t.Kind() == UnsafePointer {
+ return Value{t, nil, fl}
}
- return valueFromAddr(0, typ, unsafe.New(typ))
+ return Value{t, unsafe.New(typ), fl | flagIndir}
}
// New returns a Value representing a pointer to a new zero value
@@ -1743,40 +1697,42 @@ func New(typ Type) Value {
panic("reflect: New(nil)")
}
ptr := unsafe.New(typ)
- return valueFromIword(0, PtrTo(typ), iword(uintptr(ptr)))
+ fl := flag(Ptr) << flagKindShift
+ return Value{typ.common().ptrTo(), ptr, fl}
}
-// convertForAssignment
-func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv internalValue) internalValue {
- if iv.method {
- panic(what + ": cannot assign method value to type " + dst.String())
+// assignTo returns a value v that can be assigned directly to typ.
+// It panics if v is not assignable to typ.
+// For a conversion to an interface type, target is a suggested scratch space to use.
+func (v Value) assignTo(context string, dst *commonType, target *interface{}) Value {
+ if v.flag&flagMethod != 0 {
+ panic(context + ": cannot assign method value to type " + dst.String())
}
- dst1 := dst.(*commonType)
- if directlyAssignable(dst1, iv.typ) {
+ switch {
+ case directlyAssignable(dst, v.typ):
// Overwrite type so that they match.
// Same memory layout, so no harm done.
- iv.typ = dst1
- return iv
- }
- if implements(dst1, iv.typ) {
- if addr == nil {
- addr = unsafe.Pointer(new(interface{}))
+ v.typ = dst
+ fl := v.flag & (flagRO | flagAddr | flagIndir)
+ fl |= flag(dst.Kind()) << flagKindShift
+ return Value{dst, v.val, fl}
+
+ case implements(dst, v.typ):
+ if target == nil {
+ target = new(interface{})
}
- x := iv.valueInterface(false)
+ x := valueInterface(v, false)
if dst.NumMethod() == 0 {
- *(*interface{})(addr) = x
+ *target = x
} else {
- ifaceE2I(dst1.runtimeType(), x, addr)
+ ifaceE2I(dst.runtimeType(), x, unsafe.Pointer(target))
}
- iv.addr = addr
- iv.word = iword(uintptr(addr))
- iv.typ = dst1
- return iv
+ return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)<<flagKindShift}
}
// Failed.
- panic(what + ": value of type " + iv.typ.String() + " is not assignable to type " + dst.String())
+ panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String())
}
// implemented in ../pkg/runtime
@@ -1787,7 +1743,7 @@ func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received
func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool
func makechan(typ *runtime.Type, size uint32) (ch iword)
-func makemap(t *runtime.Type) iword
+func makemap(t *runtime.Type) (m iword)
func mapaccess(t *runtime.Type, m iword, key iword) (val iword, ok bool)
func mapassign(t *runtime.Type, m iword, key, val iword, ok bool)
func mapiterinit(t *runtime.Type, m iword) *byte
@@ -1797,3 +1753,17 @@ func maplen(m iword) int32
func call(typ *commonType, fnaddr unsafe.Pointer, isInterface bool, isMethod bool, params *unsafe.Pointer, results *unsafe.Pointer)
func ifaceE2I(t *runtime.Type, src interface{}, dst unsafe.Pointer)
+
+// Dummy annotation marking that the value x escapes,
+// for use in cases where the reflect code is so clever that
+// the compiler cannot follow.
+func escapes(x interface{}) {
+ if dummy.b {
+ dummy.x = x
+ }
+}
+
+var dummy struct {
+ b bool
+ x interface{}
+}
diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go
index b906076f9eb..59f3be39d29 100644
--- a/libgo/go/regexp/regexp.go
+++ b/libgo/go/regexp/regexp.go
@@ -1,7 +1,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package regexp implements a simple regular expression library.
+// Package regexp implements regular expression search.
//
// The syntax of the regular expressions accepted is the same
// general syntax used by Perl, Python, and other languages.
diff --git a/libgo/go/runtime/gc_test.go b/libgo/go/runtime/gc_test.go
index fad60a36804..156d3bc7d30 100644
--- a/libgo/go/runtime/gc_test.go
+++ b/libgo/go/runtime/gc_test.go
@@ -6,16 +6,24 @@ import (
)
func TestGcSys(t *testing.T) {
+ runtime.GC()
+ runtime.UpdateMemStats()
+ sys := runtime.MemStats.Sys
+
for i := 0; i < 1000000; i++ {
workthegc()
}
// Should only be using a few MB.
runtime.UpdateMemStats()
- sys := runtime.MemStats.Sys
- t.Logf("using %d MB", sys>>20)
- if sys > 10e6 {
- t.Fatalf("using too much memory: %d MB", sys>>20)
+ if sys > runtime.MemStats.Sys {
+ sys = 0
+ } else {
+ sys = runtime.MemStats.Sys - sys
+ }
+ t.Logf("used %d extra bytes", sys)
+ if sys > 2<<20 {
+ t.Fatalf("using too much memory: %d bytes", sys)
}
}
diff --git a/libgo/go/strconv/decimal.go b/libgo/go/strconv/decimal.go
index f572ea4a22d..541553097bb 100644
--- a/libgo/go/strconv/decimal.go
+++ b/libgo/go/strconv/decimal.go
@@ -102,12 +102,6 @@ func (a *decimal) Assign(v uint64) {
trim(a)
}
-func newDecimal(i uint64) *decimal {
- a := new(decimal)
- a.Assign(i)
- return a
-}
-
// Maximum shift that we can do in one pass without overflow.
// Signed int has 31 bits, and we have to be able to accommodate 9<<k.
const maxShift = 27
@@ -303,32 +297,32 @@ func shouldRoundUp(a *decimal, nd int) bool {
// If nd is zero, it means we're rounding
// just to the left of the digits, as in
// 0.09 -> 0.1.
-func (a *decimal) Round(nd int) *decimal {
+func (a *decimal) Round(nd int) {
if nd < 0 || nd >= a.nd {
- return a
+ return
}
if shouldRoundUp(a, nd) {
- return a.RoundUp(nd)
+ a.RoundUp(nd)
+ } else {
+ a.RoundDown(nd)
}
- return a.RoundDown(nd)
}
// Round a down to nd digits (or fewer).
// Returns receiver for convenience.
-func (a *decimal) RoundDown(nd int) *decimal {
+func (a *decimal) RoundDown(nd int) {
if nd < 0 || nd >= a.nd {
- return a
+ return
}
a.nd = nd
trim(a)
- return a
}
// Round a up to nd digits (or fewer).
// Returns receiver for convenience.
-func (a *decimal) RoundUp(nd int) *decimal {
+func (a *decimal) RoundUp(nd int) {
if nd < 0 || nd >= a.nd {
- return a
+ return
}
// round up
@@ -337,7 +331,7 @@ func (a *decimal) RoundUp(nd int) *decimal {
if c < '9' { // can stop after this digit
a.d[i]++
a.nd = i + 1
- return a
+ return
}
}
@@ -346,7 +340,6 @@ func (a *decimal) RoundUp(nd int) *decimal {
a.d[0] = '1'
a.nd = 1
a.dp++
- return a
}
// Extract integer part, rounded appropriately.
diff --git a/libgo/go/strconv/decimal_test.go b/libgo/go/strconv/decimal_test.go
index deb2e02f610..13a127f5b2c 100644
--- a/libgo/go/strconv/decimal_test.go
+++ b/libgo/go/strconv/decimal_test.go
@@ -70,17 +70,23 @@ var roundtests = []roundTest{
func TestDecimalRound(t *testing.T) {
for i := 0; i < len(roundtests); i++ {
test := &roundtests[i]
- s := NewDecimal(test.i).RoundDown(test.nd).String()
+ d := NewDecimal(test.i)
+ d.RoundDown(test.nd)
+ s := d.String()
if s != test.down {
t.Errorf("Decimal %v RoundDown %d = %v, want %v",
test.i, test.nd, s, test.down)
}
- s = NewDecimal(test.i).Round(test.nd).String()
+ d = NewDecimal(test.i)
+ d.Round(test.nd)
+ s = d.String()
if s != test.round {
t.Errorf("Decimal %v Round %d = %v, want %v",
test.i, test.nd, s, test.down)
}
- s = NewDecimal(test.i).RoundUp(test.nd).String()
+ d = NewDecimal(test.i)
+ d.RoundUp(test.nd)
+ s = d.String()
if s != test.up {
t.Errorf("Decimal %v RoundUp %d = %v, want %v",
test.i, test.nd, s, test.up)
diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go
index 07fe806b97d..8342b6abe79 100644
--- a/libgo/go/strconv/ftoa.go
+++ b/libgo/go/strconv/ftoa.go
@@ -98,7 +98,8 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
// The shift is exp - flt.mantbits because mant is a 1-bit integer
// followed by a flt.mantbits fraction, and we are treating it as
// a 1+flt.mantbits-bit integer.
- d := newDecimal(mant)
+ d := new(decimal)
+ d.Assign(mant)
d.Shift(exp - int(flt.mantbits))
// Round appropriately.
@@ -184,7 +185,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// d = mant << (exp - mantbits)
// Next highest floating point number is mant+1 << exp-mantbits.
// Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
- upper := newDecimal(mant*2 + 1)
+ upper := new(decimal)
+ upper.Assign(mant*2 + 1)
upper.Shift(exp - int(flt.mantbits) - 1)
// d = mant << (exp - mantbits)
@@ -203,7 +205,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
mantlo = mant*2 - 1
explo = exp - 1
}
- lower := newDecimal(mantlo*2 + 1)
+ lower := new(decimal)
+ lower.Assign(mantlo*2 + 1)
lower.Shift(explo - int(flt.mantbits) - 1)
// The upper and lower bounds are possible outputs only if
diff --git a/libgo/go/strconv/ftoa_test.go b/libgo/go/strconv/ftoa_test.go
index 6d361a138ef..8bac5da4526 100644
--- a/libgo/go/strconv/ftoa_test.go
+++ b/libgo/go/strconv/ftoa_test.go
@@ -148,3 +148,27 @@ func TestFtoa(t *testing.T) {
}
}
}
+
+func BenchmarkFtoa64Decimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Ftoa64(33909, 'g', -1)
+ }
+}
+
+func BenchmarkFtoa64Float(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Ftoa64(339.7784, 'g', -1)
+ }
+}
+
+func BenchmarkFtoa64FloatExp(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Ftoa64(-5.09e75, 'g', -1)
+ }
+}
+
+func BenchmarkFtoa64Big(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Ftoa64(123456789123456789123456789, 'g', -1)
+ }
+}
diff --git a/libgo/go/strconv/internal_test.go b/libgo/go/strconv/internal_test.go
index 9a7f4f0867c..d0fa80edfb6 100644
--- a/libgo/go/strconv/internal_test.go
+++ b/libgo/go/strconv/internal_test.go
@@ -6,7 +6,11 @@
package strconv
-func NewDecimal(i uint64) *decimal { return newDecimal(i) }
+func NewDecimal(i uint64) *decimal {
+ d := new(decimal)
+ d.Assign(i)
+ return d
+}
func SetOptimize(b bool) bool {
old := optimize
diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go
index b4d920714ac..53fdeadf97b 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 304d69a19d7..957af67b2ba 100644
--- a/libgo/go/strings/strings_test.go
+++ b/libgo/go/strings/strings_test.go
@@ -489,46 +489,47 @@ func TestSpecialCase(t *testing.T) {
func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) }
var trimTests = []struct {
- f func(string, string) string
+ f string
in, cutset, out string
}{
- {Trim, "abba", "a", "bb"},
- {Trim, "abba", "ab", ""},
- {TrimLeft, "abba", "ab", ""},
- {TrimRight, "abba", "ab", ""},
- {TrimLeft, "abba", "a", "bba"},
- {TrimRight, "abba", "a", "abb"},
- {Trim, "<tag>", "<>", "tag"},
- {Trim, "* listitem", " *", "listitem"},
- {Trim, `"quote"`, `"`, "quote"},
- {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
+ {"Trim", "abba", "a", "bb"},
+ {"Trim", "abba", "ab", ""},
+ {"TrimLeft", "abba", "ab", ""},
+ {"TrimRight", "abba", "ab", ""},
+ {"TrimLeft", "abba", "a", "bba"},
+ {"TrimRight", "abba", "a", "abb"},
+ {"Trim", "<tag>", "<>", "tag"},
+ {"Trim", "* listitem", " *", "listitem"},
+ {"Trim", `"quote"`, `"`, "quote"},
+ {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
//empty string tests
- {Trim, "abba", "", "abba"},
- {Trim, "", "123", ""},
- {Trim, "", "", ""},
- {TrimLeft, "abba", "", "abba"},
- {TrimLeft, "", "123", ""},
- {TrimLeft, "", "", ""},
- {TrimRight, "abba", "", "abba"},
- {TrimRight, "", "123", ""},
- {TrimRight, "", "", ""},
- {TrimRight, "☺\xc0", "☺", "☺\xc0"},
+ {"Trim", "abba", "", "abba"},
+ {"Trim", "", "123", ""},
+ {"Trim", "", "", ""},
+ {"TrimLeft", "abba", "", "abba"},
+ {"TrimLeft", "", "123", ""},
+ {"TrimLeft", "", "", ""},
+ {"TrimRight", "abba", "", "abba"},
+ {"TrimRight", "", "123", ""},
+ {"TrimRight", "", "", ""},
+ {"TrimRight", "☺\xc0", "☺", "☺\xc0"},
}
func TestTrim(t *testing.T) {
for _, tc := range trimTests {
- actual := tc.f(tc.in, tc.cutset)
- var name string
- switch tc.f {
- case Trim:
- name = "Trim"
- case TrimLeft:
- name = "TrimLeft"
- case TrimRight:
- name = "TrimRight"
+ name := tc.f
+ var f func(string, string) string
+ switch name {
+ case "Trim":
+ f = Trim
+ case "TrimLeft":
+ f = TrimLeft
+ case "TrimRight":
+ f = TrimRight
default:
- t.Error("Undefined trim function")
+ t.Errorf("Undefined trim function %s", name)
}
+ actual := f(tc.in, tc.cutset)
if actual != tc.out {
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
}
@@ -907,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/sync/mutex.go b/libgo/go/sync/mutex.go
index 2d46c89948f..4fc02743c6e 100644
--- a/libgo/go/sync/mutex.go
+++ b/libgo/go/sync/mutex.go
@@ -6,6 +6,8 @@
// exclusion locks. Other than the Once and WaitGroup types, most are intended
// for use by low-level library routines. Higher-level synchronization is
// better done via channels and communication.
+//
+// Values containing the types defined in this package should not be copied.
package sync
import (
diff --git a/libgo/go/syscall/bpf_bsd.go b/libgo/go/syscall/bpf_bsd.go
index 06a2953e7fe..f94b7233b67 100644
--- a/libgo/go/syscall/bpf_bsd.go
+++ b/libgo/go/syscall/bpf_bsd.go
@@ -20,54 +20,54 @@ func BpfJump(code, k, jt, jf int) *BpfInsn {
return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)}
}
-func BpfBuflen(fd int) (int, int) {
+func BpfBuflen(fd int) (int, error) {
var l int
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l)))
- if e := int(ep); e != 0 {
- return 0, e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l)))
+ if err != 0 {
+ return 0, Errno(err)
}
- return l, 0
+ return l, nil
}
-func SetBpfBuflen(fd, l int) (int, int) {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l)))
- if e := int(ep); e != 0 {
- return 0, e
+func SetBpfBuflen(fd, l int) (int, error) {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l)))
+ if err != 0 {
+ return 0, Errno(err)
}
- return l, 0
+ return l, nil
}
-func BpfDatalink(fd int) (int, int) {
+func BpfDatalink(fd int) (int, error) {
var t int
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t)))
- if e := int(ep); e != 0 {
- return 0, e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t)))
+ if err != 0 {
+ return 0, Errno(err)
}
- return t, 0
+ return t, nil
}
-func SetBpfDatalink(fd, t int) (int, int) {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t)))
- if e := int(ep); e != 0 {
- return 0, e
+func SetBpfDatalink(fd, t int) (int, error) {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t)))
+ if err != 0 {
+ return 0, Errno(err)
}
- return t, 0
+ return t, nil
}
-func SetBpfPromisc(fd, m int) int {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m)))
- if e := int(ep); e != 0 {
- return e
+func SetBpfPromisc(fd, m int) error {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
-func FlushBpf(fd int) int {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0)
- if e := int(ep); e != 0 {
- return e
+func FlushBpf(fd int) error {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0)
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
type ivalue struct {
@@ -75,95 +75,95 @@ type ivalue struct {
value int16
}
-func BpfInterface(fd int, name string) (string, int) {
+func BpfInterface(fd int, name string) (string, error) {
var iv ivalue
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv)))
- if e := int(ep); e != 0 {
- return "", e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv)))
+ if err != 0 {
+ return "", Errno(err)
}
- return name, 0
+ return name, nil
}
-func SetBpfInterface(fd int, name string) int {
+func SetBpfInterface(fd int, name string) error {
var iv ivalue
copy(iv.name[:], []byte(name))
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv)))
- if e := int(ep); e != 0 {
- return e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
-func BpfTimeout(fd int) (*Timeval, int) {
+func BpfTimeout(fd int) (*Timeval, error) {
var tv Timeval
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv)))
- if e := int(ep); e != 0 {
- return nil, e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv)))
+ if err != 0 {
+ return nil, Errno(err)
}
- return &tv, 0
+ return &tv, nil
}
-func SetBpfTimeout(fd int, tv *Timeval) int {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv)))
- if e := int(ep); e != 0 {
- return e
+func SetBpfTimeout(fd int, tv *Timeval) error {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
-func BpfStats(fd int) (*BpfStat, int) {
+func BpfStats(fd int) (*BpfStat, error) {
var s BpfStat
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s)))
- if e := int(ep); e != 0 {
- return nil, e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s)))
+ if err != 0 {
+ return nil, Errno(err)
}
- return &s, 0
+ return &s, nil
}
-func SetBpfImmediate(fd, m int) int {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m)))
- if e := int(ep); e != 0 {
- return e
+func SetBpfImmediate(fd, m int) error {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
-func SetBpf(fd int, i []BpfInsn) int {
+func SetBpf(fd int, i []BpfInsn) error {
var p BpfProgram
p.Len = uint32(len(i))
p.Insns = (*BpfInsn)(unsafe.Pointer(&i[0]))
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p)))
- if e := int(ep); e != 0 {
- return e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
-func CheckBpfVersion(fd int) int {
+func CheckBpfVersion(fd int) error {
var v BpfVersion
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v)))
- if e := int(ep); e != 0 {
- return e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v)))
+ if err != 0 {
+ return Errno(err)
}
if v.Major != BPF_MAJOR_VERSION || v.Minor != BPF_MINOR_VERSION {
return EINVAL
}
- return 0
+ return nil
}
-func BpfHeadercmpl(fd int) (int, int) {
+func BpfHeadercmpl(fd int) (int, error) {
var f int
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f)))
- if e := int(ep); e != 0 {
- return 0, e
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f)))
+ if err != 0 {
+ return 0, Errno(err)
}
- return f, 0
+ return f, nil
}
-func SetBpfHeadercmpl(fd, f int) int {
- _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f)))
- if e := int(ep); e != 0 {
- return e
+func SetBpfHeadercmpl(fd, f int) error {
+ _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f)))
+ if err != 0 {
+ return Errno(err)
}
- return 0
+ return nil
}
diff --git a/libgo/go/syscall/env_plan9.go b/libgo/go/syscall/env_plan9.go
new file mode 100644
index 00000000000..518573318ef
--- /dev/null
+++ b/libgo/go/syscall/env_plan9.go
@@ -0,0 +1,74 @@
+// 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.
+
+// Plan 9 environment variables.
+
+package syscall
+
+import "errors"
+
+func Getenv(key string) (value string, found bool) {
+ if len(key) == 0 {
+ return "", false
+ }
+ f, e := Open("/env/"+key, O_RDONLY)
+ if e != nil {
+ return "", false
+ }
+ defer Close(f)
+
+ l, _ := Seek(f, 0, 2)
+ Seek(f, 0, 0)
+ buf := make([]byte, l)
+ n, e := Read(f, buf)
+ if e != nil {
+ return "", false
+ }
+
+ if n > 0 && buf[n-1] == 0 {
+ buf = buf[:n-1]
+ }
+ return string(buf), true
+}
+
+func Setenv(key, value string) error {
+ if len(key) == 0 {
+ return errors.New("bad arg in system call")
+ }
+
+ f, e := Create("/env/"+key, O_RDWR, 0666)
+ if e != nil {
+ return e
+ }
+ defer Close(f)
+
+ _, e = Write(f, []byte(value))
+ return nil
+}
+
+func Clearenv() {
+ RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
+}
+
+func Environ() []string {
+ env := make([]string, 0, 100)
+
+ f, e := Open("/env", O_RDONLY)
+ if e != nil {
+ panic(e)
+ }
+ defer Close(f)
+
+ names, e := readdirnames(f)
+ if e != nil {
+ panic(e)
+ }
+
+ for _, k := range names {
+ if v, ok := Getenv(k); ok {
+ env = append(env, k+"="+v)
+ }
+ }
+ return env[0:len(env)]
+}
diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go
new file mode 100644
index 00000000000..df259097c6a
--- /dev/null
+++ b/libgo/go/syscall/env_unix.go
@@ -0,0 +1,85 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux openbsd
+
+// Unix environment variables.
+
+package syscall
+
+import "sync"
+
+var env map[string]string
+var envOnce sync.Once
+var Envs []string // provided by runtime
+
+func setenv_c(k, v string)
+
+func copyenv() {
+ env = make(map[string]string)
+ for _, s := range Envs {
+ for j := 0; j < len(s); j++ {
+ if s[j] == '=' {
+ env[s[0:j]] = s[j+1:]
+ break
+ }
+ }
+ }
+}
+
+var envLock sync.RWMutex
+
+func Getenv(key string) (value string, found bool) {
+ envOnce.Do(copyenv)
+ if len(key) == 0 {
+ return "", false
+ }
+
+ envLock.RLock()
+ defer envLock.RUnlock()
+
+ v, ok := env[key]
+ if !ok {
+ return "", false
+ }
+ return v, true
+}
+
+func Setenv(key, value string) error {
+ envOnce.Do(copyenv)
+ if len(key) == 0 {
+ return EINVAL
+ }
+
+ envLock.Lock()
+ defer envLock.Unlock()
+
+ env[key] = value
+ setenv_c(key, value) // is a no-op if cgo isn't loaded
+ return nil
+}
+
+func Clearenv() {
+ envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
+
+ envLock.Lock()
+ defer envLock.Unlock()
+
+ env = make(map[string]string)
+
+ // TODO(bradfitz): pass through to C
+}
+
+func Environ() []string {
+ envOnce.Do(copyenv)
+ envLock.RLock()
+ defer envLock.RUnlock()
+ a := make([]string, len(env))
+ i := 0
+ for k, v := range env {
+ a[i] = k + "=" + v
+ i++
+ }
+ return a
+}
diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go
new file mode 100644
index 00000000000..8308f10a2dc
--- /dev/null
+++ b/libgo/go/syscall/env_windows.go
@@ -0,0 +1,77 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Windows environment variables.
+
+package syscall
+
+import (
+ "unicode/utf16"
+ "unsafe"
+)
+
+func Getenv(key string) (value string, found bool) {
+ b := make([]uint16, 100)
+ n, e := GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ if n == 0 && e == ERROR_ENVVAR_NOT_FOUND {
+ return "", false
+ }
+ if n > uint32(len(b)) {
+ b = make([]uint16, n)
+ n, e = GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ if n > uint32(len(b)) {
+ n = 0
+ }
+ }
+ if n == 0 {
+ return "", false
+ }
+ return string(utf16.Decode(b[0:n])), true
+}
+
+func Setenv(key, value string) error {
+ var v *uint16
+ if len(value) > 0 {
+ v = StringToUTF16Ptr(value)
+ }
+ e := SetEnvironmentVariable(StringToUTF16Ptr(key), v)
+ if e != nil {
+ return e
+ }
+ return nil
+}
+
+func Clearenv() {
+ for _, s := range Environ() {
+ // Environment variables can begin with =
+ // so start looking for the separator = at j=1.
+ // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
+ for j := 1; j < len(s); j++ {
+ if s[j] == '=' {
+ Setenv(s[0:j], "")
+ break
+ }
+ }
+ }
+}
+
+func Environ() []string {
+ s, e := GetEnvironmentStrings()
+ if e != nil {
+ return nil
+ }
+ defer FreeEnvironmentStrings(s)
+ r := make([]string, 0, 50) // Empty with room to grow.
+ for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ {
+ if p[i] == 0 {
+ // empty string marks the end
+ if i <= from {
+ break
+ }
+ r = append(r, string(utf16.Decode(p[from:i])))
+ from = i + 1
+ }
+ }
+ return r
+}
diff --git a/libgo/go/syscall/errno.c b/libgo/go/syscall/errno.c
index 854b5aaec28..8e57811151a 100644
--- a/libgo/go/syscall/errno.c
+++ b/libgo/go/syscall/errno.c
@@ -5,21 +5,22 @@
license that can be found in the LICENSE file. */
#include <errno.h>
+#include <stdint.h>
/* errno is typically a macro. These functions set
and get errno specific to the libc being used. */
-int GetErrno() asm ("libgo_syscall.syscall.GetErrno");
-void SetErrno(int) asm ("libgo_syscall.syscall.SetErrno");
+uintptr_t GetErrno() asm ("libgo_syscall.syscall.GetErrno");
+void SetErrno(uintptr_t) asm ("libgo_syscall.syscall.SetErrno");
-int
+uintptr_t
GetErrno()
{
- return errno;
+ return (uintptr_t) errno;
}
void
-SetErrno(int value)
+SetErrno(uintptr_t value)
{
- errno = value;
+ errno = (int) value;
}
diff --git a/libgo/go/syscall/errstr.go b/libgo/go/syscall/errstr.go
index d9f3fe82eb4..5ef10da1fd4 100644
--- a/libgo/go/syscall/errstr.go
+++ b/libgo/go/syscall/errstr.go
@@ -6,14 +6,14 @@
package syscall
-//sysnb strerror_r(errnum int, buf []byte) (errno int)
+//sysnb strerror_r(errnum int, buf []byte) (err error)
//strerror_r(errnum int, buf *byte, buflen Size_t) int
func Errstr(errnum int) string {
for len := 128; ; len *= 2 {
b := make([]byte, len)
err := strerror_r(errnum, b)
- if err == 0 {
+ if err == nil {
i := 0
for b[i] != 0 {
i++
diff --git a/libgo/go/syscall/exec_stubs.go b/libgo/go/syscall/exec_stubs.go
index 02b9ec34cf3..35bb17487b7 100644
--- a/libgo/go/syscall/exec_stubs.go
+++ b/libgo/go/syscall/exec_stubs.go
@@ -7,17 +7,27 @@
package syscall
func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) {
- return -1, ENOSYS;
+ return -1, ENOSYS
}
func Exec(argv0 string, argv []string, envv []string) (err int) {
- return ENOSYS;
+ return ENOSYS
}
-func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) {
- return -1, ENOSYS;
+func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
+ return -1, ENOSYS
}
-func raw_ptrace(request int, pid int, addr *byte, data *byte) int {
+func (w WaitStatus) Exited() bool { return false }
+func (w WaitStatus) Signaled() bool { return false }
+func (w WaitStatus) Stopped() bool { return false }
+func (w WaitStatus) Continued() bool { return false }
+func (w WaitStatus) CoreDump() bool { return false }
+func (w WaitStatus) ExitStatus() int { return 0 }
+func (w WaitStatus) Signal() int { return 0 }
+func (w WaitStatus) StopSignal() int { return 0 }
+func (w WaitStatus) TrapCause() int { return 0 }
+
+func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno {
return ENOSYS
}
diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go
index 60e9770ce23..c9814b7050e 100644
--- a/libgo/go/syscall/exec_unix.go
+++ b/libgo/go/syscall/exec_unix.go
@@ -11,39 +11,43 @@ import (
"unsafe"
)
-//sysnb raw_fork() (pid Pid_t, errno int)
+//sysnb raw_fork() (pid Pid_t, err Errno)
//fork() Pid_t
-//sysnb raw_setsid() (errno int)
+//sysnb raw_setsid() (err Errno)
//setsid() Pid_t
-//sysnb raw_chroot(path *byte) (errno int)
+//sysnb raw_setpgid(pid int, pgid int) (err Errno)
+//setpgid(pid Pid_t, pgid Pid_t) int
+
+//sysnb raw_chroot(path *byte) (err Errno)
//chroot(path *byte) int
-//sysnb raw_chdir(path *byte) (errno int)
+//sysnb raw_chdir(path *byte) (err Errno)
//chdir(path *byte) int
-//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, errno int)
+//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, err Errno)
//fcntl(fd int, cmd int, arg int) int
-//sysnb raw_close(fd int) (errno int)
+//sysnb raw_close(fd int) (err Errno)
//close(fd int) int
-//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, errno int)
+//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, err Errno)
//ioctl(fd int, cmd int, val int) int
-//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (errno int)
+//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (err Errno)
//execve(argv0 *byte, argv **byte, envv **byte) int
-//sysnb raw_read(fd int, p *byte, np int) (n int, errno int)
-//read(fd int, buf *byte, count Size_t) Ssize_t
-
-//sysnb raw_write(fd int, buf *byte, count int) int
+//sysnb raw_write(fd int, buf *byte, count int) (err Errno)
//write(fd int, buf *byte, count Size_t) Ssize_t
//sysnb raw_exit(status int)
//_exit(status int)
+// Note: not raw, returns error rather than Errno.
+//sys read(fd int, p *byte, np int) (n int, err error)
+//read(fd int, buf *byte, count Size_t) Ssize_t
+
// Lock synchronizing creation of new file descriptors with fork.
//
// We want the child in a fork/exec sequence to inherit only the
@@ -106,9 +110,9 @@ func StringSlicePtr(ss []string) []*byte {
func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) }
-func SetNonblock(fd int, nonblocking bool) (errno int) {
+func SetNonblock(fd int, nonblocking bool) (err error) {
flag, err := fcntl(fd, F_GETFL, 0)
- if err != 0 {
+ if err != nil {
return err
}
if nonblocking {
@@ -121,20 +125,22 @@ func SetNonblock(fd int, nonblocking bool) (errno int) {
}
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
-// If a dup or exec fails, write the errno int to pipe.
+// If a dup or exec fails, write the errno error to pipe.
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
// In the child, this function must not acquire any locks, because
// they might have been locked at the time of the fork. This means
// no rescheduling, no malloc calls, and no new stack segments.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
-func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) {
+func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
- var r1 Pid_t
- var err1 int
- var nextfd int
- var i int
+ var (
+ r1 Pid_t
+ err1 Errno
+ nextfd int
+ i int
+ )
// guard against side effects of shuffling fds below.
fd := append([]int(nil), attr.Files...)
@@ -143,7 +149,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// No more allocation or calls of non-assembly functions.
r1, err1 = raw_fork()
if err1 != 0 {
- return 0, int(err1)
+ return 0, err1
}
if r1 != 0 {
@@ -171,7 +177,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// Set process group
if sys.Setpgid {
- err1 = Setpgid(0, 0)
+ err1 = raw_setpgid(0, 0)
if err1 != 0 {
goto childerror
}
@@ -189,23 +195,35 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
if cred := sys.Credential; cred != nil {
ngroups := len(cred.Groups)
if ngroups == 0 {
- err1 = setgroups(0, nil)
+ err2 := setgroups(0, nil)
+ if err2 == nil {
+ err1 = 0
+ } else {
+ err1 = err2.(Errno)
+ }
} else {
groups := make([]Gid_t, ngroups)
for i, v := range cred.Groups {
groups[i] = Gid_t(v)
}
- err1 = setgroups(ngroups, &groups[0])
+ err2 := setgroups(ngroups, &groups[0])
+ if err2 == nil {
+ err1 = 0
+ } else {
+ err1 = err2.(Errno)
+ }
}
if err1 != 0 {
goto childerror
}
- err1 = Setgid(int(cred.Gid))
- if err1 != 0 {
+ err2 := Setgid(int(cred.Gid))
+ if err2 != nil {
+ err1 = err2.(Errno)
goto childerror
}
- err1 = Setuid(int(cred.Uid))
- if err1 != 0 {
+ err2 = Setuid(int(cred.Uid))
+ if err2 != nil {
+ err1 = err2.(Errno)
goto childerror
}
}
@@ -222,8 +240,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// so that pass 2 won't stomp on an fd it needs later.
nextfd = int(len(fd))
if pipe < nextfd {
- _, err1 = Dup2(pipe, nextfd)
- if err1 != 0 {
+ _, err2 := Dup2(pipe, nextfd)
+ if err2 != nil {
+ err1 = err2.(Errno)
goto childerror
}
raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC)
@@ -232,8 +251,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
- _, err1 = Dup2(fd[i], nextfd)
- if err1 != 0 {
+ _, err2 := Dup2(fd[i], nextfd)
+ if err2 != nil {
+ err1 = err2.(Errno)
goto childerror
}
raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC)
@@ -262,8 +282,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
// The new fd is created NOT close-on-exec,
// which is exactly what we want.
- _, err1 = Dup2(fd[i], i)
- if err1 != 0 {
+ _, err2 := Dup2(fd[i], i)
+ if err2 != nil {
+ err1 = err2.(Errno)
goto childerror
}
}
@@ -338,10 +359,10 @@ type SysProcAttr struct {
var zeroProcAttr ProcAttr
var zeroSysProcAttr SysProcAttr
-func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
+func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
var p [2]int
var n int
- var err1 uintptr
+ var err1 Errno
var wstatus WaitStatus
if attr == nil {
@@ -379,32 +400,32 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
ForkLock.Lock()
// Allocate child status pipe close on exec.
- if err = Pipe(p[0:]); err != 0 {
+ if err = Pipe(p[0:]); err != nil {
goto error
}
- if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != 0 {
+ if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
goto error
}
- if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != 0 {
+ if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil {
goto error
}
// Kick off child.
- pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
- if err != 0 {
+ pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
+ if err1 != 0 {
goto error
}
ForkLock.Unlock()
// Read child error status from pipe.
Close(p[1])
- n, err = raw_read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
+ n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
Close(p[0])
- if err != 0 || n != 0 {
+ if err != nil || n != 0 {
if n == int(unsafe.Sizeof(err1)) {
- err = int(err1)
+ err = Errno(err1)
}
- if err == 0 {
+ if err == nil {
err = EPIPE
}
@@ -418,7 +439,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
}
// Read got EOF, so pipe closed on exec, so exec succeeded.
- return pid, 0
+ return pid, nil
error:
if p[0] >= 0 {
@@ -430,20 +451,20 @@ error:
}
// Combination of fork and exec, careful to be thread safe.
-func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
+func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
return forkExec(argv0, argv, attr)
}
// StartProcess wraps ForkExec for package os.
-func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) {
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) {
pid, err = forkExec(argv0, argv, attr)
return pid, 0, err
}
// Ordinary exec.
-func Exec(argv0 string, argv []string, envv []string) (err int) {
+func Exec(argv0 string, argv []string, envv []string) (err error) {
err1 := raw_execve(StringBytePtr(argv0),
&StringSlicePtr(argv)[0],
&StringSlicePtr(envv)[0])
- return int(err1)
+ return Errno(err1)
}
diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go
index e8b540ad160..2826e2f35a6 100644
--- a/libgo/go/syscall/exec_windows.go
+++ b/libgo/go/syscall/exec_windows.go
@@ -8,8 +8,8 @@ package syscall
import (
"sync"
+ "unicode/utf16"
"unsafe"
- "utf16"
)
var ForkLock sync.RWMutex
@@ -100,7 +100,7 @@ func makeCmdLine(args []string) string {
// Last bytes are two UCS-2 NULs, or four NUL bytes.
func createEnvBlock(envv []string) *uint16 {
if len(envv) == 0 {
- return &utf16.Encode([]int("\x00\x00"))[0]
+ return &utf16.Encode([]rune("\x00\x00"))[0]
}
length := 0
for _, s := range envv {
@@ -118,54 +118,54 @@ func createEnvBlock(envv []string) *uint16 {
}
copy(b[i:i+1], []byte{0})
- return &utf16.Encode([]int(string(b)))[0]
+ return &utf16.Encode([]rune(string(b)))[0]
}
func CloseOnExec(fd Handle) {
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
}
-func SetNonblock(fd Handle, nonblocking bool) (errno int) {
- return 0
+func SetNonblock(fd Handle, nonblocking bool) (err error) {
+ return nil
}
// getFullPath retrieves the full path of the specified file.
// Just a wrapper for Windows GetFullPathName api.
-func getFullPath(name string) (path string, err int) {
+func getFullPath(name string) (path string, err error) {
p := StringToUTF16Ptr(name)
buf := make([]uint16, 100)
n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
- if err != 0 {
+ if err != nil {
return "", err
}
if n > uint32(len(buf)) {
// Windows is asking for bigger buffer.
buf = make([]uint16, n)
n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
- if err != 0 {
+ if err != nil {
return "", err
}
if n > uint32(len(buf)) {
return "", EINVAL
}
}
- return UTF16ToString(buf[:n]), 0
+ return UTF16ToString(buf[:n]), nil
}
func isSlash(c uint8) bool {
return c == '\\' || c == '/'
}
-func normalizeDir(dir string) (name string, err int) {
+func normalizeDir(dir string) (name string, err error) {
ndir, err := getFullPath(dir)
- if err != 0 {
+ if err != nil {
return "", err
}
if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
// dir cannot have \\server\share\path form
return "", EINVAL
}
- return ndir, 0
+ return ndir, nil
}
func volToUpper(ch int) int {
@@ -175,13 +175,13 @@ func volToUpper(ch int) int {
return ch
}
-func joinExeDirAndFName(dir, p string) (name string, err int) {
+func joinExeDirAndFName(dir, p string) (name string, err error) {
if len(p) == 0 {
return "", EINVAL
}
if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
// \\server\share\path form
- return p, 0
+ return p, nil
}
if len(p) > 1 && p[1] == ':' {
// has drive letter
@@ -189,10 +189,10 @@ func joinExeDirAndFName(dir, p string) (name string, err int) {
return "", EINVAL
}
if isSlash(p[2]) {
- return p, 0
+ return p, nil
} else {
d, err := normalizeDir(dir)
- if err != 0 {
+ if err != nil {
return "", err
}
if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
@@ -204,7 +204,7 @@ func joinExeDirAndFName(dir, p string) (name string, err int) {
} else {
// no drive letter
d, err := normalizeDir(dir)
- if err != 0 {
+ if err != nil {
return "", err
}
if isSlash(p[0]) {
@@ -232,7 +232,7 @@ type SysProcAttr struct {
var zeroProcAttr ProcAttr
var zeroSysProcAttr SysProcAttr
-func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) {
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) {
if len(argv0) == 0 {
return 0, 0, EWINDOWS
}
@@ -255,9 +255,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int,
// argv0 relative to the current directory, and, only once the new
// process is started, it does Chdir(attr.Dir). We are adjusting
// for that difference here by making argv0 absolute.
- var err int
+ var err error
argv0, err = joinExeDirAndFName(attr.Dir, argv0)
- if err != 0 {
+ if err != nil {
return 0, 0, err
}
}
@@ -294,7 +294,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int,
for i := range attr.Files {
if attr.Files[i] > 0 {
err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
- if err != 0 {
+ if err != nil {
return 0, 0, err
}
defer CloseHandle(Handle(fd[i]))
@@ -314,14 +314,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int,
pi := new(ProcessInformation)
err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi)
- if err != 0 {
+ if err != nil {
return 0, 0, err
}
defer CloseHandle(Handle(pi.Thread))
- return int(pi.ProcessId), int(pi.Process), 0
+ return int(pi.ProcessId), int(pi.Process), nil
}
-func Exec(argv0 string, argv []string, envv []string) (err int) {
+func Exec(argv0 string, argv []string, envv []string) (err error) {
return EWINDOWS
}
diff --git a/libgo/go/syscall/libcall_irix.go b/libgo/go/syscall/libcall_irix.go
index ae453559713..69e0db264bd 100644
--- a/libgo/go/syscall/libcall_irix.go
+++ b/libgo/go/syscall/libcall_irix.go
@@ -4,5 +4,5 @@
package syscall
-//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int)
+//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno)
//ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long
diff --git a/libgo/go/syscall/libcall_linux.go b/libgo/go/syscall/libcall_linux.go
index 3948e51ae28..79f5d48ae8b 100644
--- a/libgo/go/syscall/libcall_linux.go
+++ b/libgo/go/syscall/libcall_linux.go
@@ -8,31 +8,31 @@ package syscall
import "unsafe"
-//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int)
+//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
//openat(dirfd int, path *byte, flags int, mode Mode_t) int
-//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (errno int)
+//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (err error)
//futimesat(dirfd int, path *byte, times *[2]Timeval) int
-func Futimesat(dirfd int, path string, tv []Timeval) (errno int) {
+func Futimesat(dirfd int, path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
return futimesat(dirfd, StringBytePtr(path), (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
-func Futimes(fd int, tv []Timeval) (errno int) {
+func Futimes(fd int, tv []Timeval) (err error) {
// Believe it or not, this is the best we can do on GNU/Linux
// (and is what glibc does).
return Utimes("/proc/self/fd/"+itoa(fd), tv)
}
-//sys ptrace(request int, pid int, addr uintptr, data uintptr) (errno int)
+//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
//ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long
-//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int)
+//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno)
//ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long
-func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno int) {
+func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) {
// The peek requests are machine-size oriented, so we wrap it
// to retrieve arbitrary-length data.
@@ -48,9 +48,9 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in
// boundary.
n := 0
if addr%sizeofPtr != 0 {
- errno = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
- if errno != 0 {
- return 0, errno
+ err = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
+ if err != nil {
+ return 0, err
}
n += copy(out, buf[addr%sizeofPtr:])
out = out[n:]
@@ -60,27 +60,27 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in
for len(out) > 0 {
// We use an internal buffer to gaurantee alignment.
// It's not documented if this is necessary, but we're paranoid.
- errno = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
- if errno != 0 {
- return n, errno
+ err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
+ if err != nil {
+ return n, err
}
copied := copy(out, buf[0:])
n += copied
out = out[copied:]
}
- return n, 0
+ return n, nil
}
-func PtracePeekText(pid int, addr uintptr, out []byte) (count int, errno int) {
+func PtracePeekText(pid int, addr uintptr, out []byte) (count int, err error) {
return ptracePeek(PTRACE_PEEKTEXT, pid, addr, out)
}
-func PtracePeekData(pid int, addr uintptr, out []byte) (count int, errno int) {
+func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) {
return ptracePeek(PTRACE_PEEKDATA, pid, addr, out)
}
-func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, errno int) {
+func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) {
// As for ptracePeek, we need to align our accesses to deal
// with the possibility of straddling an invalid page.
@@ -88,15 +88,15 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
n := 0
if addr%sizeofPtr != 0 {
var buf [sizeofPtr]byte
- errno = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
- if errno != 0 {
- return 0, errno
+ err = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
+ if err != nil {
+ return 0, err
}
n += copy(buf[addr%sizeofPtr:], data)
word := *((*uintptr)(unsafe.Pointer(&buf[0])))
- errno = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word)
- if errno != 0 {
- return 0, errno
+ err = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word)
+ if err != nil {
+ return 0, err
}
data = data[n:]
}
@@ -104,9 +104,9 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
// Interior.
for len(data) > int(sizeofPtr) {
word := *((*uintptr)(unsafe.Pointer(&data[0])))
- errno = ptrace(pokeReq, pid, addr+uintptr(n), word)
- if errno != 0 {
- return n, errno
+ err = ptrace(pokeReq, pid, addr+uintptr(n), word)
+ if err != nil {
+ return n, err
}
n += int(sizeofPtr)
data = data[sizeofPtr:]
@@ -115,167 +115,167 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
// Trailing edge.
if len(data) > 0 {
var buf [sizeofPtr]byte
- errno = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
- if errno != 0 {
- return n, errno
+ err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
+ if err != nil {
+ return n, err
}
copy(buf[0:], data)
word := *((*uintptr)(unsafe.Pointer(&buf[0])))
- errno = ptrace(pokeReq, pid, addr+uintptr(n), word)
- if errno != 0 {
- return n, errno
+ err = ptrace(pokeReq, pid, addr+uintptr(n), word)
+ if err != nil {
+ return n, err
}
n += len(data)
}
- return n, 0
+ return n, nil
}
-func PtracePokeText(pid int, addr uintptr, data []byte) (count int, errno int) {
+func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) {
return ptracePoke(PTRACE_POKETEXT, PTRACE_PEEKTEXT, pid, addr, data)
}
-func PtracePokeData(pid int, addr uintptr, data []byte) (count int, errno int) {
+func PtracePokeData(pid int, addr uintptr, data []byte) (count int, err error) {
return ptracePoke(PTRACE_POKEDATA, PTRACE_PEEKDATA, pid, addr, data)
}
-func PtraceGetRegs(pid int, regsout *PtraceRegs) (errno int) {
+func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) {
return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout)))
}
-func PtraceSetRegs(pid int, regs *PtraceRegs) (errno int) {
+func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) {
return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs)))
}
-func PtraceSetOptions(pid int, options int) (errno int) {
+func PtraceSetOptions(pid int, options int) (err error) {
return ptrace(PTRACE_SETOPTIONS, pid, 0, uintptr(options))
}
-func PtraceGetEventMsg(pid int) (msg uint, errno int) {
+func PtraceGetEventMsg(pid int) (msg uint, err error) {
var data _C_long
- errno = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data)))
+ err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data)))
msg = uint(data)
return
}
-func PtraceCont(pid int, signal int) (errno int) {
+func PtraceCont(pid int, signal int) (err error) {
return ptrace(PTRACE_CONT, pid, 0, uintptr(signal))
}
-func PtraceSingleStep(pid int) (errno int) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) }
+func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) }
-func PtraceAttach(pid int) (errno int) { return ptrace(PTRACE_ATTACH, pid, 0, 0) }
+func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) }
-func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0) }
+func PtraceDetach(pid int) (err error) { return ptrace(PTRACE_DETACH, pid, 0, 0) }
// FIXME: mksysinfo needs to produce LINUX_REBOOT_MAGIC[12].
-// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int)
+// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error)
// //reboot(magic1 uint, magic2 uint, cmd int, arg *byte) int
-// func Reboot(cmd int) (errno int) {
+// func Reboot(cmd int) (err error) {
// return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
// }
-//sys Acct(path string) (errno int)
+//sys Acct(path string) (err error)
//acct(path *byte) int
// FIXME: mksysinfo Timex
-// //sys Adjtimex(buf *Timex) (state int, errno int)
+// //sys Adjtimex(buf *Timex) (state int, err error)
// //adjtimex(buf *Timex) int
-//sys Faccessat(dirfd int, path string, mode uint32, flags int) (errno int)
+//sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
//faccessat(dirfd int, pathname *byte, mode int, flags int) int
// FIXME: Only in glibc 2.10 and later.
-// //sys Fallocate(fd int, mode uint32, off int64, len int64) (errno int)
+// //sys Fallocate(fd int, mode uint32, off int64, len int64) (err error)
// //fallocate(fd int, mode int, offset Offset_t, len Offset_t) int
-//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (errno int)
+//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//fchmodat(dirfd int, pathname *byte, mode Mode_t, flags int) int
-//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int)
+//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//fchownat(dirfd int, path *byte, owner Uid_t, group Gid_t, flags int) int
-//sys Flock(fd int, how int) (errno int)
+//sys Flock(fd int, how int) (err error)
//flock(fd int, how int) int
// FIXME: mksysinfo statfs
-// //sys Fstatfs(fd int, buf *Statfs_t) (errno int)
+// //sys Fstatfs(fd int, buf *Statfs_t) (err error)
// //fstatfs(fd int, buf *Statfs_t) int
// FIXME: Only available as a syscall.
// //sysnb Gettid() (tid int)
// //gettid() Pid_t
-//sys Ioperm(from int, num int, on int) (errno int)
+//sys Ioperm(from int, num int, on int) (err error)
//ioperm(from _C_long, num _C_long, on int) int
-//sys Iopl(level int) (errno int)
+//sys Iopl(level int) (err error)
//iopl(level int) int
// FIXME: mksysinfo linux_dirent
// Or just abandon this function.
-// //sys Getdents(fd int, buf []byte) (n int, errno int)
+// //sys Getdents(fd int, buf []byte) (n int, err error)
// //getdents64(fd int, buf *byte, count uint)
-//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno int)
+//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
//inotify_add_watch(fd int, pathname *byte, mask uint32) int
-//sysnb InotifyInit() (fd int, errno int)
+//sysnb InotifyInit() (fd int, err error)
//inotify_init() int
// FIXME: Only in glibc 2.9 and later.
-// //sysnb InotifyInit1(flags int) (fd int, errno int)
+// //sysnb InotifyInit1(flags int) (fd int, err error)
// //inotify_init1(flags int) int
-//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int)
+//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error)
//inotify_rm_watch(fd int, wd uint32) int
-//sys Klogctl(typ int, buf []byte) (n int, errno int)
+//sys Klogctl(typ int, buf []byte) (n int, err error)
//klogctl(typ int, bufp *byte, len int) int
-//sys Mkdirat(dirfd int, path string, mode uint32) (errno int)
+//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//mkdirat(dirfd int, path *byte, mode Mode_t) int
-//sys Mknodat(dirfd int, path string, mode uint32, dev int) (errno int)
+//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
//mknodat(dirfd int, path *byte, mode Mode_t, dev _dev_t) int
-//sys PivotRoot(newroot string, putold string) (errno int)
+//sys PivotRoot(newroot string, putold string) (err error)
//pivot_root(newroot *byte, putold *byte) int
-//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int)
+//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//renameat(olddirfd int, oldpath *byte, newdirfd int, newpath *byte) int
-//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, errno int)
+//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, err error)
//sendfile64(outfd int, infd int, offset *Offset_t, count Size_t) Ssize_t
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) {
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
var soff Offset_t
var psoff *Offset_t
if offset != nil {
psoff = &soff
}
- written, errno = sendfile(outfd, infd, psoff, count)
+ written, err = sendfile(outfd, infd, psoff, count)
if offset != nil {
*offset = int64(soff)
}
return
}
-//sys Setfsgid(gid int) (errno int)
+//sys Setfsgid(gid int) (err error)
//setfsgid(gid Gid_t) int
-//sys Setfsuid(uid int) (errno int)
+//sys Setfsuid(uid int) (err error)
//setfsuid(uid Uid_t) int
-//sysnb Setresgid(rgid int, egid int, sgid int) (errno int)
+//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
//setresgid(rgid Gid_t, egid Gid_t, sgid Gid_t) int
-//sysnb Setresuid(ruid int, eguid int, suid int) (errno int)
+//sysnb Setresuid(ruid int, eguid int, suid int) (err error)
//setresuid(ruid Uid_t, euid Uid_t, suid Uid_t) int
-//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, errno int)
+//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, err error)
//splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len Size_t, flags uint) Ssize_t
-func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) {
+func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) {
var lroff _loff_t
var plroff *_loff_t
if (roff != nil) {
@@ -286,7 +286,7 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i
if (woff != nil) {
plwoff = &lwoff
}
- n, errno = splice(rfd, plroff, wfd, plwoff, len, flags)
+ n, err = splice(rfd, plroff, wfd, plwoff, len, flags)
if (roff != nil) {
*roff = int64(lroff)
}
@@ -297,37 +297,37 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i
}
// FIXME: mksysinfo statfs
-// //sys Statfs(path string, buf *Statfs_t) (errno int)
+// //sys Statfs(path string, buf *Statfs_t) (err error)
// //statfs(path *byte, buf *Statfs_t) int
// FIXME: Only in glibc 2.6 and later.
-// //sys SyncFileRange(fd int, off int64, n int64, flags int) (errno int)
+// //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
// //sync_file_range(fd int, off Offset_t, n Offset_t, flags uint) int
// FIXME: mksysinfo Sysinfo_t
-// //sysnb Sysinfo(info *Sysinfo_t) (errno int)
+// //sysnb Sysinfo(info *Sysinfo_t) (err error)
// //sysinfo(info *Sysinfo_t) int
-//sys Tee(rfd int, wfd int, len int, flags int) (n int64, errno int)
+//sys Tee(rfd int, wfd int, len int, flags int) (n int64, err error)
//tee(rfd int, wfd int, len Size_t, flags uint) Ssize_t
// FIXME: Only available as a syscall.
-// //sysnb Tgkill(tgid int, tid int, sig int) (errno int)
+// //sysnb Tgkill(tgid int, tid int, sig int) (err error)
// //tgkill(tgid int, tid int, sig int) int
-//sys unlinkat(dirfd int, path string, flags int) (errno int)
+//sys unlinkat(dirfd int, path string, flags int) (err error)
//unlinkat(dirfd int, path *byte, flags int) int
-func Unlinkat(dirfd int, path string) (errno int) {
+func Unlinkat(dirfd int, path string) (err error) {
return unlinkat(dirfd, path, 0)
}
-//sys Unmount(target string, flags int) (errno int) = SYS_UMOUNT2
+//sys Unmount(target string, flags int) (err error) = SYS_UMOUNT2
//umount2(target *byte, flags int) int
-//sys Unshare(flags int) (errno int)
+//sys Unshare(flags int) (err error)
//unshare(flags int) int
// FIXME: mksysinfo Ustat_t
-// //sys Ustat(dev int, ubuf *Ustat_t) (errno int)
+// //sys Ustat(dev int, ubuf *Ustat_t) (err error)
// //ustat(dev _dev_t, ubuf *Ustat_t) int
diff --git a/libgo/go/syscall/libcall_posix.go b/libgo/go/syscall/libcall_posix.go
index 87ed4e628fd..d90e595dba3 100644
--- a/libgo/go/syscall/libcall_posix.go
+++ b/libgo/go/syscall/libcall_posix.go
@@ -17,43 +17,43 @@ import "unsafe"
* Wrapped
*/
-//sysnb pipe(p *[2]int) (errno int)
+//sysnb pipe(p *[2]int) (err error)
//pipe(p *[2]int) int
-func Pipe(p []int) (errno int) {
+func Pipe(p []int) (err error) {
if len(p) != 2 {
return EINVAL
}
var pp [2]int
- errno = pipe(&pp)
+ err = pipe(&pp)
p[0] = pp[0]
p[1] = pp[1]
return
}
-//sys utimes(path string, times *[2]Timeval) (errno int)
+//sys utimes(path string, times *[2]Timeval) (err error)
//utimes(path *byte, times *[2]Timeval) int
-func Utimes(path string, tv []Timeval) (errno int) {
+func Utimes(path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
-//sys getcwd(buf *byte, size Size_t) (errno int)
+//sys getcwd(buf *byte, size Size_t) (err error)
//getcwd(buf *byte, size Size_t) *byte
const ImplementsGetwd = true
-func Getwd() (ret string, errno int) {
+func Getwd() (ret string, err error) {
for len := Size_t(4096); ; len *= 2 {
b := make([]byte, len)
err := getcwd(&b[0], len)
- if err == 0 {
- i := 0;
+ if err == nil {
+ i := 0
for b[i] != 0 {
- i++;
+ i++
}
- return string(b[0:i]), 0;
+ return string(b[0:i]), nil
}
if err != ERANGE {
return "", err
@@ -61,16 +61,16 @@ func Getwd() (ret string, errno int) {
}
}
-//sysnb getgroups(size int, list *Gid_t) (nn int, errno int)
+//sysnb getgroups(size int, list *Gid_t) (nn int, err error)
//getgroups(size int, list *Gid_t) int
-func Getgroups() (gids []int, errno int) {
+func Getgroups() (gids []int, err error) {
n, err := getgroups(0, nil)
- if err != 0 {
- return nil, errno
+ if err != nil {
+ return nil, err
}
if n == 0 {
- return nil, 0
+ return nil, nil
}
// Sanity check group count. Max is 1<<16 on GNU/Linux.
@@ -80,8 +80,8 @@ func Getgroups() (gids []int, errno int) {
a := make([]Gid_t, n)
n, err = getgroups(n, &a[0])
- if err != 0 {
- return nil, errno
+ if err != nil {
+ return nil, err
}
gids = make([]int, n)
for i, v := range a[0:n] {
@@ -90,10 +90,10 @@ func Getgroups() (gids []int, errno int) {
return
}
-//sysnb setgroups(n int, list *Gid_t) (errno int)
+//sysnb setgroups(n int, list *Gid_t) (err error)
//setgroups(n Size_t, list *Gid_t) int
-func Setgroups(gids []int) (errno int) {
+func Setgroups(gids []int) (err error) {
if len(gids) == 0 {
return setgroups(0, nil)
}
@@ -120,10 +120,10 @@ func (w WaitStatus) Signal() int
func (w WaitStatus) StopSignal() int
func (w WaitStatus) TrapCause() int
-//sys Mkfifo(path string, mode uint32) (errno int)
+//sys Mkfifo(path string, mode uint32) (err error)
//mkfifo(path *byte, mode Mode_t) int
-//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int)
+//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) int
const nfdbits = unsafe.Sizeof(fds_bits_type) * 8
@@ -154,52 +154,52 @@ func FDZero(set *FdSet) {
}
}
-//sys Access(path string, mode uint32) (errno int)
+//sys Access(path string, mode uint32) (err error)
//access(path *byte, mode int) int
-//sys Chdir(path string) (errno int)
+//sys Chdir(path string) (err error)
//chdir(path *byte) int
-//sys Chmod(path string, mode uint32) (errno int)
+//sys Chmod(path string, mode uint32) (err error)
//chmod(path *byte, mode Mode_t) int
-//sys Chown(path string, uid int, gid int) (errno int)
+//sys Chown(path string, uid int, gid int) (err error)
//chown(path *byte, uid Uid_t, gid Gid_t) int
-//sys Chroot(path string) (errno int)
+//sys Chroot(path string) (err error)
//chroot(path *byte) int
-//sys Close(fd int) (errno int)
+//sys Close(fd int) (err error)
//close(fd int) int
-//sys Creat(path string, mode uint32) (fd int, errno int)
+//sys Creat(path string, mode uint32) (fd int, err error)
//creat(path *byte, mode Mode_t) int
-//sysnb Dup(oldfd int) (fd int, errno int)
+//sysnb Dup(oldfd int) (fd int, err error)
//dup(oldfd int) int
-//sysnb Dup2(oldfd int, newfd int) (fd int, errno int)
+//sysnb Dup2(oldfd int, newfd int) (fd int, err error)
//dup2(oldfd int, newfd int) int
//sys Exit(code int)
//exit(code int)
-//sys Fchdir(fd int) (errno int)
+//sys Fchdir(fd int) (err error)
//fchdir(fd int) int
-//sys Fchmod(fd int, mode uint32) (errno int)
+//sys Fchmod(fd int, mode uint32) (err error)
//fchmod(fd int, mode Mode_t) int
-//sys Fchown(fd int, uid int, gid int) (errno int)
+//sys Fchown(fd int, uid int, gid int) (err error)
//fchown(fd int, uid Uid_t, gid Gid_t) int
-//sys fcntl(fd int, cmd int, arg int) (val int, errno int)
+//sys fcntl(fd int, cmd int, arg int) (val int, err error)
//fcntl(fd int, cmd int, arg int) int
-//sys Fdatasync(fd int) (errno int)
+//sys Fdatasync(fd int) (err error)
//fdatasync(fd int) int
-//sys Fsync(fd int) (errno int)
+//sys Fsync(fd int) (err error)
//fsync(fd int) int
//sysnb Getegid() (egid int)
@@ -214,7 +214,7 @@ func FDZero(set *FdSet) {
//sysnb Getpagesize() (pagesize int)
//getpagesize() int
-//sysnb Getpgid(pid int) (pgid int, errno int)
+//sysnb Getpgid(pid int) (pgid int, err error)
//getpgid(pid Pid_t) Pid_t
//sysnb Getpgrp() (pid int)
@@ -227,138 +227,138 @@ func FDZero(set *FdSet) {
//getppid() Pid_t
// FIXME: mksysinfo Rlimit
-// //sysnb Getrlimit(resource int, rlim *Rlimit) (errno int)
+// //sysnb Getrlimit(resource int, rlim *Rlimit) (err error)
// //getrlimit(resource int, rlim *Rlimit) int
-//sysnb Getrusage(who int, rusage *Rusage) (errno int)
+//sysnb Getrusage(who int, rusage *Rusage) (err error)
//getrusage(who int, rusage *Rusage) int
-//sysnb gettimeofday(tv *Timeval, tz *byte) (errno int)
+//sysnb gettimeofday(tv *Timeval, tz *byte) (err error)
//gettimeofday(tv *Timeval, tz *byte) int
-func Gettimeofday(tv *Timeval) (errno int) {
+func Gettimeofday(tv *Timeval) (err error) {
return gettimeofday(tv, nil)
}
//sysnb Getuid() (uid int)
//getuid() Uid_t
-//sysnb Kill(pid int, sig int) (errno int)
+//sysnb Kill(pid int, sig int) (err error)
//kill(pid Pid_t, sig int) int
-//sys Lchown(path string, uid int, gid int) (errno int)
+//sys Lchown(path string, uid int, gid int) (err error)
//lchown(path *byte, uid Uid_t, gid Gid_t) int
-//sys Link(oldpath string, newpath string) (errno int)
+//sys Link(oldpath string, newpath string) (err error)
//link(oldpath *byte, newpath *byte) int
-//sys Mkdir(path string, mode uint32) (errno int)
+//sys Mkdir(path string, mode uint32) (err error)
//mkdir(path *byte, mode Mode_t) int
-//sys Mknod(path string, mode uint32, dev int) (errno int)
+//sys Mknod(path string, mode uint32, dev int) (err error)
//mknod(path *byte, mode Mode_t, dev _dev_t) int
-//sys Mount(source string, target string, fstype string, flags int, data string) (errno int)
+//sys Mount(source string, target string, fstype string, flags int, data string) (err error)
//mount(source *byte, target *byte, fstype *byte, flags _C_long, data *byte) int
-//sys Nanosleep(time *Timespec, leftover *Timespec) (errno int)
+//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//nanosleep(time *Timespec, leftover *Timespec) int
-//sys Pause() (errno int)
+//sys Pause() (err error)
//pause() int
-//sys Read(fd int, p []byte) (n int, errno int)
+//sys Read(fd int, p []byte) (n int, err error)
//read(fd int, buf *byte, count Size_t) Ssize_t
-//sys Readlink(path string, buf []byte) (n int, errno int)
+//sys Readlink(path string, buf []byte) (n int, err error)
//readlink(path *byte, buf *byte, bufsiz Size_t) Ssize_t
-//sys Rename(oldpath string, newpath string) (errno int)
+//sys Rename(oldpath string, newpath string) (err error)
//rename(oldpath *byte, newpath *byte) int
-//sys Rmdir(path string) (errno int)
+//sys Rmdir(path string) (err error)
//rmdir(path *byte) int
-//sys Setdomainname(p []byte) (errno int)
+//sys Setdomainname(p []byte) (err error)
//setdomainname(name *byte, len Size_t) int
-//sys Sethostname(p []byte) (errno int)
+//sys Sethostname(p []byte) (err error)
//sethostname(name *byte, len Size_t) int
-//sysnb Setgid(gid int) (errno int)
+//sysnb Setgid(gid int) (err error)
//setgid(gid Gid_t) int
-//sysnb Setregid(rgid int, egid int) (errno int)
+//sysnb Setregid(rgid int, egid int) (err error)
//setregid(rgid Gid_t, egid Gid_t) int
-//sysnb Setpgid(pid int, pgid int) (errno int)
+//sysnb Setpgid(pid int, pgid int) (err error)
//setpgid(pid Pid_t, pgid Pid_t) int
-//sysnb Setreuid(ruid int, euid int) (errno int)
+//sysnb Setreuid(ruid int, euid int) (err error)
//setreuid(ruid Uid_t, euid Uid_t) int
// FIXME: mksysinfo Rlimit
-// //sysnb Setrlimit(resource int, rlim *Rlimit) (errno int)
+// //sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
// //setrlimit(resource int, rlim *Rlimit) int
-//sysnb Setsid() (pid int, errno int)
+//sysnb Setsid() (pid int, err error)
//setsid() Pid_t
-//sysnb settimeofday(tv *Timeval, tz *byte) (errno int)
+//sysnb settimeofday(tv *Timeval, tz *byte) (err error)
//settimeofday(tv *Timeval, tz *byte) int
-func Settimeofday(tv *Timeval) (errno int) {
+func Settimeofday(tv *Timeval) (err error) {
return settimeofday(tv, nil)
}
-//sysnb Setuid(uid int) (errno int)
+//sysnb Setuid(uid int) (err error)
//setuid(uid Uid_t) int
-//sys Symlink(oldpath string, newpath string) (errno int)
+//sys Symlink(oldpath string, newpath string) (err error)
//symlink(oldpath *byte, newpath *byte) int
//sys Sync()
//sync()
// FIXME: mksysinfo Time_t
-// //sysnb Time(t *Time_t) (tt Time_t, errno int)
+// //sysnb Time(t *Time_t) (tt Time_t, err error)
// //time(t *Time_t) Time_t
// FIXME: mksysinfo Tms
-// //sysnb Times(tms *Tms) (ticks uintptr, errno int)
+// //sysnb Times(tms *Tms) (ticks uintptr, err error)
// //times(tms *Tms) _clock_t
//sysnb Umask(mask int) (oldmask int)
//umask(mask Mode_t) Mode_t
-//sys Unlink(path string) (errno int)
+//sys Unlink(path string) (err error)
//unlink(path *byte) int
// FIXME: mksysinfo Utimbuf
-// //sys Utime(path string, buf *Utimbuf) (errno int)
+// //sys Utime(path string, buf *Utimbuf) (err error)
// //utime(path *byte, buf *Utimbuf) int
-//sys Write(fd int, p []byte) (n int, errno int)
+//sys Write(fd int, p []byte) (n int, err error)
//write(fd int, buf *byte, count Size_t) Ssize_t
-//sys munmap(addr uintptr, length uintptr) (errno int)
+//sys munmap(addr uintptr, length uintptr) (err error)
//munmap(addr *byte, length Size_t) int
-//sys Madvise(b []byte, advice int) (errno int)
+//sys Madvise(b []byte, advice int) (err error)
//madvise(addr *byte, len Size_t, advice int) int
-//sys Mprotect(b []byte, prot int) (errno int)
+//sys Mprotect(b []byte, prot int) (err error)
//mprotect(addr *byte, len Size_t, prot int) int
-//sys Mlock(b []byte) (errno int)
+//sys Mlock(b []byte) (err error)
//mlock(addr *byte, len Size_t) int
-//sys Munlock(b []byte) (errno int)
+//sys Munlock(b []byte) (err error)
//munlock(addr *byte, len Size_t) int
-//sys Mlockall(flags int) (errno int)
+//sys Mlockall(flags int) (err error)
//mlockall(flags int) int
-//sys Munlockall() (errno int)
+//sys Munlockall() (err error)
//munlockall() int
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -378,8 +378,8 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
return
}
-//sysnb Tcgetattr(fd int, p *Termios) (errno int)
+//sysnb Tcgetattr(fd int, p *Termios) (err error)
//tcgetattr(fd int, p *Termios) int
-//sys Tcsetattr(fd int, actions int, p *Termios) (errno int)
+//sys Tcsetattr(fd int, actions int, p *Termios) (err error)
//tcsetattr(fd int, actions int, p *Termios) int
diff --git a/libgo/go/syscall/libcall_posix_largefile.go b/libgo/go/syscall/libcall_posix_largefile.go
index acfafecc581..e898648157d 100644
--- a/libgo/go/syscall/libcall_posix_largefile.go
+++ b/libgo/go/syscall/libcall_posix_largefile.go
@@ -6,32 +6,32 @@
package syscall
-//sys Fstat(fd int, stat *Stat_t) (errno int)
+//sys Fstat(fd int, stat *Stat_t) (err error)
//fstat64(fd int, stat *Stat_t) int
-//sys Ftruncate(fd int, length int64) (errno int)
+//sys Ftruncate(fd int, length int64) (err error)
//ftruncate64(fd int, length Offset_t) int
-//sys Lstat(path string, stat *Stat_t) (errno int)
+//sys Lstat(path string, stat *Stat_t) (err error)
//lstat64(path *byte, stat *Stat_t) int
-//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int)
+//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)
//mmap64(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte
-//sys Open(path string, mode int, perm uint32) (fd int, errno int)
+//sys Open(path string, mode int, perm uint32) (fd int, err error)
//open64(path *byte, mode int, perm Mode_t) int
-//sys Pread(fd int, p []byte, offset int64) (n int, errno int)
+//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//pread64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t
-//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int)
+//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
//pwrite64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t
-//sys Seek(fd int, offset int64, whence int) (off int64, errno int)
+//sys Seek(fd int, offset int64, whence int) (off int64, err error)
//lseek64(fd int, offset Offset_t, whence int) Offset_t
-//sys Stat(path string, stat *Stat_t) (errno int)
+//sys Stat(path string, stat *Stat_t) (err error)
//stat64(path *byte, stat *Stat_t) int
-//sys Truncate(path string, length int64) (errno int)
+//sys Truncate(path string, length int64) (err error)
//truncate64(path *byte, length Offset_t) int
diff --git a/libgo/go/syscall/libcall_posix_regfile.go b/libgo/go/syscall/libcall_posix_regfile.go
index b71da0ceb48..97167013a18 100644
--- a/libgo/go/syscall/libcall_posix_regfile.go
+++ b/libgo/go/syscall/libcall_posix_regfile.go
@@ -7,32 +7,32 @@
package syscall
-//sys Fstat(fd int, stat *Stat_t) (errno int)
+//sys Fstat(fd int, stat *Stat_t) (err error)
//fstat(fd int, stat *Stat_t) int
-//sys Ftruncate(fd int, length int64) (errno int)
+//sys Ftruncate(fd int, length int64) (err error)
//ftruncate(fd int, length Offset_t) int
-//sys Lstat(path string, stat *Stat_t) (errno int)
+//sys Lstat(path string, stat *Stat_t) (err error)
//lstat(path *byte, stat *Stat_t) int
-//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int)
+//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)
//mmap(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte
-//sys Open(path string, mode int, perm uint32) (fd int, errno int)
+//sys Open(path string, mode int, perm uint32) (fd int, err error)
//open(path *byte, mode int, perm Mode_t) int
-//sys Pread(fd int, p []byte, offset int64) (n int, errno int)
+//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//pread(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t
-//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int)
+//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
//pwrite(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t
-//sys Seek(fd int, offset int64, whence int) (off int64, errno int)
+//sys Seek(fd int, offset int64, whence int) (off int64, err error)
//lseek(fd int, offset Offset_t, whence int) Offset_t
-//sys Stat(path string, stat *Stat_t) (errno int)
+//sys Stat(path string, stat *Stat_t) (err error)
//stat(path *byte, stat *Stat_t) int
-//sys Truncate(path string, length int64) (errno int)
+//sys Truncate(path string, length int64) (err error)
//truncate(path *byte, length Offset_t) int
diff --git a/libgo/go/syscall/libcall_solaris_386.go b/libgo/go/syscall/libcall_solaris_386.go
index 9c4e966f1ae..e94deecf8cd 100644
--- a/libgo/go/syscall/libcall_solaris_386.go
+++ b/libgo/go/syscall/libcall_solaris_386.go
@@ -5,8 +5,8 @@
package syscall
// 32-bit Solaris 2/x86 needs to use _nuname internally, cf. <sys/utsname.h>.
-//sysnb Uname(buf *Utsname) (errno int)
+//sysnb Uname(buf *Utsname) (err error)
//_nuname(buf *Utsname) int
-//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int)
+//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno)
//ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long
diff --git a/libgo/go/syscall/libcall_solaris_amd64.go b/libgo/go/syscall/libcall_solaris_amd64.go
index f0d335dbb67..69b11ba5ee6 100644
--- a/libgo/go/syscall/libcall_solaris_amd64.go
+++ b/libgo/go/syscall/libcall_solaris_amd64.go
@@ -5,6 +5,6 @@
package syscall
// 64-bit ptrace(3C) doesn't exist
-func raw_ptrace(request int, pid int, addr *byte, data *byte) int {
+func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno {
return ENOSYS
}
diff --git a/libgo/go/syscall/libcall_solaris_sparc.go b/libgo/go/syscall/libcall_solaris_sparc.go
index ae453559713..69e0db264bd 100644
--- a/libgo/go/syscall/libcall_solaris_sparc.go
+++ b/libgo/go/syscall/libcall_solaris_sparc.go
@@ -4,5 +4,5 @@
package syscall
-//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int)
+//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno)
//ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long
diff --git a/libgo/go/syscall/libcall_solaris_sparc64.go b/libgo/go/syscall/libcall_solaris_sparc64.go
index f0d335dbb67..69b11ba5ee6 100644
--- a/libgo/go/syscall/libcall_solaris_sparc64.go
+++ b/libgo/go/syscall/libcall_solaris_sparc64.go
@@ -5,6 +5,6 @@
package syscall
// 64-bit ptrace(3C) doesn't exist
-func raw_ptrace(request int, pid int, addr *byte, data *byte) int {
+func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno {
return ENOSYS
}
diff --git a/libgo/go/syscall/libcall_support.go b/libgo/go/syscall/libcall_support.go
index daed8073caf..7449a0adf79 100644
--- a/libgo/go/syscall/libcall_support.go
+++ b/libgo/go/syscall/libcall_support.go
@@ -8,5 +8,5 @@ package syscall
func entersyscall()
func exitsyscall()
-func GetErrno() int
-func SetErrno(int)
+func GetErrno() Errno
+func SetErrno(Errno)
diff --git a/libgo/go/syscall/libcall_uname.go b/libgo/go/syscall/libcall_uname.go
index e4c32b12d6a..519e6dc25d0 100644
--- a/libgo/go/syscall/libcall_uname.go
+++ b/libgo/go/syscall/libcall_uname.go
@@ -4,5 +4,5 @@
package syscall
-//sysnb Uname(buf *Utsname) (errno int)
+//sysnb Uname(buf *Utsname) (err error)
//uname(buf *Utsname) int
diff --git a/libgo/go/syscall/libcall_wait4.go b/libgo/go/syscall/libcall_wait4.go
index 7a63bc71e97..578686926f3 100644
--- a/libgo/go/syscall/libcall_wait4.go
+++ b/libgo/go/syscall/libcall_wait4.go
@@ -6,14 +6,13 @@
package syscall
-//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, errno int)
+//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, err error)
//wait4(pid Pid_t, status *int, options int, rusage *Rusage) Pid_t
-func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) {
+func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
var status int
r, err := wait4(Pid_t(pid), &status, options, rusage)
wpid = int(r)
- errno = err
if wstatus != nil {
*wstatus = WaitStatus(status)
}
diff --git a/libgo/go/syscall/libcall_waitpid.go b/libgo/go/syscall/libcall_waitpid.go
index 014446307e3..1c476d829d0 100644
--- a/libgo/go/syscall/libcall_waitpid.go
+++ b/libgo/go/syscall/libcall_waitpid.go
@@ -6,14 +6,13 @@
package syscall
-//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, errno int)
+//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, err error)
//waitpid(pid Pid_t, status *int, options int) Pid_t
-func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) {
+func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
var status int
r, err := waitpid(Pid_t(pid), &status, options)
wpid = int(r)
- errno = err
if wstatus != nil {
*wstatus = WaitStatus(status)
}
diff --git a/libgo/go/syscall/lsf_linux.go b/libgo/go/syscall/lsf_linux.go
index 0976688b9e7..05d653b4aa0 100644
--- a/libgo/go/syscall/lsf_linux.go
+++ b/libgo/go/syscall/lsf_linux.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.
-// GNU/Linux socket filter
+// Linux socket filter
package syscall
@@ -18,10 +18,10 @@ func LsfJump(code, k, jt, jf int) *SockFilter {
return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)}
}
-func LsfSocket(ifindex, proto int) (int, int) {
+func LsfSocket(ifindex, proto int) (int, error) {
var lsall SockaddrLinklayer
s, e := Socket(AF_PACKET, SOCK_RAW, proto)
- if e != 0 {
+ if e != nil {
return 0, e
}
p := (*[2]byte)(unsafe.Pointer(&lsall.Protocol))
@@ -29,11 +29,11 @@ func LsfSocket(ifindex, proto int) (int, int) {
p[1] = byte(proto)
lsall.Ifindex = ifindex
e = Bind(s, &lsall)
- if e != 0 {
+ if e != nil {
Close(s)
return 0, e
}
- return s, 0
+ return s, nil
}
type iflags struct {
@@ -41,17 +41,17 @@ type iflags struct {
flags uint16
}
-func SetLsfPromisc(name string, m bool) int {
+func SetLsfPromisc(name string, m bool) error {
s, e := Socket(AF_INET, SOCK_DGRAM, 0)
- if e != 0 {
+ if e != nil {
return e
}
defer Close(s)
var ifl iflags
copy(ifl.name[:], []byte(name))
_, _, ep := Syscall(SYS_IOCTL, uintptr(s), SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl)))
- if e := int(ep); e != 0 {
- return e
+ if ep != 0 {
+ return Errno(ep)
}
if m {
ifl.flags |= uint16(IFF_PROMISC)
@@ -59,20 +59,20 @@ func SetLsfPromisc(name string, m bool) int {
ifl.flags &= ^uint16(IFF_PROMISC)
}
_, _, ep = Syscall(SYS_IOCTL, uintptr(s), SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl)))
- if e := int(ep); e != 0 {
- return e
+ if ep != 0 {
+ return Errno(ep)
}
- return 0
+ return nil
}
-func AttachLsf(fd int, i []SockFilter) int {
+func AttachLsf(fd int, i []SockFilter) error {
var p SockFprog
p.Len = uint16(len(i))
p.Filter = (*SockFilter)(unsafe.Pointer(&i[0]))
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p))
}
-func DetachLsf(fd int) int {
+func DetachLsf(fd int) error {
var dummy int
return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, uintptr(unsafe.Pointer(&dummy)), unsafe.Sizeof(dummy))
}
diff --git a/libgo/go/syscall/mksyscall.awk b/libgo/go/syscall/mksyscall.awk
index 49828d94ce3..b02989cc323 100644
--- a/libgo/go/syscall/mksyscall.awk
+++ b/libgo/go/syscall/mksyscall.awk
@@ -12,7 +12,7 @@
# This includes return parameters.
# * The parameter lists must give a type for each argument:
# the (x, y, z int) shorthand is not allowed.
-# * If the return parameter is an error number, it must be named errno.
+# * If the return parameter is an error, it must be named err.
# A line beginning with //sysnb is like //sys, except that the
# goroutine will not be suspended during the execution of the library
@@ -217,13 +217,13 @@ BEGIN {
goname = goparam[1]
gotype = goparam[2]
- if (goname == "errno") {
+ if (goname == "err") {
if (cfnresult ~ /^\*/) {
print "\tif _r == nil {"
} else {
print "\tif _r < 0 {"
}
- print "\t\terrno = GetErrno()"
+ print "\t\terr = GetErrno()"
print "\t}"
} else if (gotype == "uintptr" && cfnresult ~ /^\*/) {
printf("\t%s = (%s)(unsafe.Pointer(_r))\n", goname, gotype)
diff --git a/libgo/go/syscall/netlink_linux.go b/libgo/go/syscall/netlink_linux.go
index 4ee78d62f25..8683bb3dacb 100644
--- a/libgo/go/syscall/netlink_linux.go
+++ b/libgo/go/syscall/netlink_linux.go
@@ -63,31 +63,28 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte {
// NetlinkRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
-func NetlinkRIB(proto, family int) ([]byte, int) {
+func NetlinkRIB(proto, family int) ([]byte, error) {
var (
- s int
- e int
lsanl SockaddrNetlink
- seq int
tab []byte
)
- s, e = Socket(AF_NETLINK, SOCK_RAW, 0)
- if e != 0 {
+ s, e := Socket(AF_NETLINK, SOCK_RAW, 0)
+ if e != nil {
return nil, e
}
defer Close(s)
lsanl.Family = AF_NETLINK
e = Bind(s, &lsanl)
- if e != 0 {
+ if e != nil {
return nil, e
}
- seq++
+ seq := 1
wb := newNetlinkRouteRequest(proto, seq, family)
e = Sendto(s, wb, 0, &lsanl)
- if e != 0 {
+ if e != nil {
return nil, e
}
@@ -100,7 +97,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) {
rb = make([]byte, Getpagesize())
nr, _, e = Recvfrom(s, rb, 0)
- if e != 0 {
+ if e != nil {
return nil, e
}
if nr < NLMSG_HDRLEN {
@@ -111,7 +108,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) {
msgs, _ := ParseNetlinkMessage(rb)
for _, m := range msgs {
- if lsa, e = Getsockname(s); e != 0 {
+ if lsa, e = Getsockname(s); e != nil {
return nil, e
}
switch v := lsa.(type) {
@@ -132,7 +129,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) {
}
done:
- return tab, 0
+ return tab, nil
}
// NetlinkMessage represents the netlink message.
@@ -143,18 +140,18 @@ type NetlinkMessage struct {
// ParseNetlinkMessage parses buf as netlink messages and returns
// the slice containing the NetlinkMessage structs.
-func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) {
+func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) {
var (
h *NlMsghdr
dbuf []byte
dlen int
- e int
+ e error
msgs []NetlinkMessage
)
for len(buf) >= NLMSG_HDRLEN {
h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf)
- if e != 0 {
+ if e != nil {
break
}
m := NetlinkMessage{}
@@ -167,12 +164,12 @@ func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) {
return msgs, e
}
-func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, int) {
+func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) {
h := (*NlMsghdr)(unsafe.Pointer(&buf[0]))
if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) {
return nil, nil, 0, EINVAL
}
- return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), 0
+ return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
}
// NetlinkRouteAttr represents the netlink route attribute.
@@ -184,13 +181,13 @@ type NetlinkRouteAttr struct {
// ParseNetlinkRouteAttr parses msg's payload as netlink route
// attributes and returns the slice containing the NetlinkRouteAttr
// structs.
-func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) {
+func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) {
var (
buf []byte
a *RtAttr
alen int
vbuf []byte
- e int
+ e error
attrs []NetlinkRouteAttr
)
@@ -207,7 +204,7 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) {
for len(buf) >= SizeofRtAttr {
a, vbuf, alen, e = netlinkRouteAttrAndValue(buf)
- if e != 0 {
+ if e != nil {
break
}
ra := NetlinkRouteAttr{}
@@ -217,13 +214,13 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) {
buf = buf[alen:]
}
- return attrs, 0
+ return attrs, nil
}
-func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, int) {
+func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) {
h := (*RtAttr)(unsafe.Pointer(&buf[0]))
if int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) {
return nil, nil, 0, EINVAL
}
- return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), 0
+ return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil
}
diff --git a/libgo/go/syscall/route_bsd.go b/libgo/go/syscall/route_bsd.go
index f6b124b64e4..bc4c15e950b 100644
--- a/libgo/go/syscall/route_bsd.go
+++ b/libgo/go/syscall/route_bsd.go
@@ -29,29 +29,24 @@ func rsaAlignOf(salen int) int {
// RouteRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
-func RouteRIB(facility, param int) ([]byte, int) {
- var (
- tab []byte
- e int
- )
-
+func RouteRIB(facility, param int) ([]byte, error) {
mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}
// Find size.
n := uintptr(0)
- if e = sysctl(mib, nil, &n, nil, 0); e != 0 {
- return nil, e
+ if err := sysctl(mib, nil, &n, nil, 0); err != nil {
+ return nil, err
}
if n == 0 {
- return nil, 0
+ return nil, nil
}
- tab = make([]byte, n)
- if e = sysctl(mib, &tab[0], &n, nil, 0); e != 0 {
- return nil, e
+ tab := make([]byte, n)
+ if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil {
+ return nil, err
}
- return tab[:n], 0
+ return tab[:n], nil
}
// RoutingMessage represents a routing message.
@@ -91,7 +86,7 @@ func (m *RouteMessage) sockaddr() []Sockaddr {
switch i {
case RTAX_DST, RTAX_GATEWAY:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
- if e != 0 {
+ if e != nil {
return nil
}
if i == RTAX_DST {
@@ -134,7 +129,7 @@ func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) {
return nil
}
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0])))
- if e != 0 {
+ if e != nil {
return nil
}
return append(sas, sa)
@@ -163,7 +158,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
switch i {
case RTAX_IFA:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
- if e != 0 {
+ if e != nil {
return nil
}
sas = append(sas, sa)
@@ -178,7 +173,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
// ParseRoutingMessage parses buf as routing messages and returns
// the slice containing the RoutingMessage interfaces.
-func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) {
+func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, err error) {
for len(buf) >= anyMessageLen {
any := (*anyMessage)(unsafe.Pointer(&buf[0]))
if any.Version != RTM_VERSION {
@@ -187,11 +182,11 @@ func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) {
msgs = append(msgs, any.toRoutingMessage(buf))
buf = buf[any.Msglen:]
}
- return msgs, 0
+ return msgs, nil
}
// ParseRoutingMessage parses msg's payload as raw sockaddrs and
// returns the slice containing the Sockaddr interfaces.
-func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, errno int) {
- return append(sas, msg.sockaddr()...), 0
+func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, err error) {
+ return append(sas, msg.sockaddr()...), nil
}
diff --git a/libgo/go/syscall/route_darwin.go b/libgo/go/syscall/route_darwin.go
index 9d3a701daba..410e70a138a 100644
--- a/libgo/go/syscall/route_darwin.go
+++ b/libgo/go/syscall/route_darwin.go
@@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
switch i {
case RTAX_IFA:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
- if e != 0 {
+ if e != nil {
return nil
}
sas = append(sas, sa)
diff --git a/libgo/go/syscall/route_freebsd.go b/libgo/go/syscall/route_freebsd.go
index 0d61d08b08c..094e17044db 100644
--- a/libgo/go/syscall/route_freebsd.go
+++ b/libgo/go/syscall/route_freebsd.go
@@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
switch i {
case RTAX_IFA:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
- if e != 0 {
+ if e != nil {
return nil
}
sas = append(sas, sa)
diff --git a/libgo/go/syscall/sleep_rtems.go b/libgo/go/syscall/sleep_rtems.go
index 8992eb53c3e..9d72203e8ef 100644
--- a/libgo/go/syscall/sleep_rtems.go
+++ b/libgo/go/syscall/sleep_rtems.go
@@ -6,8 +6,8 @@
package syscall
-func Sleep(nsec int64) (errno int) {
+func Sleep(nsec int64) (err error) {
ts := NsecToTimespec(nsec)
- errno = Nanosleep(&ts, nil)
+ err = Nanosleep(&ts, nil)
return
}
diff --git a/libgo/go/syscall/sleep_select.go b/libgo/go/syscall/sleep_select.go
index 3ebaf58f969..533f554da05 100644
--- a/libgo/go/syscall/sleep_select.go
+++ b/libgo/go/syscall/sleep_select.go
@@ -6,8 +6,8 @@
package syscall
-func Sleep(nsec int64) (errno int) {
+func Sleep(nsec int64) (err error) {
tv := NsecToTimeval(nsec);
- _, err := Select(0, nil, nil, nil, &tv);
+ _, err = Select(0, nil, nil, nil, &tv);
return err;
}
diff --git a/libgo/go/syscall/sockcmsg_linux.go b/libgo/go/syscall/sockcmsg_linux.go
index b025ca52101..0b4caa1d055 100644
--- a/libgo/go/syscall/sockcmsg_linux.go
+++ b/libgo/go/syscall/sockcmsg_linux.go
@@ -26,7 +26,7 @@ func UnixCredentials(ucred *Ucred) []byte {
// ParseUnixCredentials decodes a socket control message that contains
// credentials in a Ucred structure. To receive such a message, the
// SO_PASSCRED option must be enabled on the socket.
-func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) {
+func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) {
if msg.Header.Level != SOL_SOCKET {
return nil, EINVAL
}
@@ -34,5 +34,5 @@ func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) {
return nil, EINVAL
}
ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0]))
- return &ucred, 0
+ return &ucred, nil
}
diff --git a/libgo/go/syscall/sockcmsg_unix.go b/libgo/go/syscall/sockcmsg_unix.go
index c9872aeba31..84c1383d7e2 100644
--- a/libgo/go/syscall/sockcmsg_unix.go
+++ b/libgo/go/syscall/sockcmsg_unix.go
@@ -47,17 +47,17 @@ type SocketControlMessage struct {
Data []byte
}
-func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) {
+func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) {
var (
h *Cmsghdr
dbuf []byte
- e int
+ e error
cmsgs []SocketControlMessage
)
for len(buf) >= CmsgLen(0) {
h, dbuf, e = socketControlMessageHeaderAndData(buf)
- if e != 0 {
+ if e != nil {
break
}
m := SocketControlMessage{}
@@ -70,12 +70,12 @@ func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) {
return cmsgs, e
}
-func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, int) {
+func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) {
h := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) {
return nil, nil, EINVAL
}
- return h, buf[cmsgAlignOf(SizeofCmsghdr):], 0
+ return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil
}
// UnixRights encodes a set of open file descriptors into a socket
@@ -99,7 +99,7 @@ func UnixRights(fds ...int) []byte {
// ParseUnixRights decodes a socket control message that contains an
// integer array of open file descriptors from another process.
-func ParseUnixRights(msg *SocketControlMessage) ([]int, int) {
+func ParseUnixRights(msg *SocketControlMessage) ([]int, error) {
if msg.Header.Level != SOL_SOCKET {
return nil, EINVAL
}
@@ -111,5 +111,5 @@ func ParseUnixRights(msg *SocketControlMessage) ([]int, int) {
fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i])))
j++
}
- return fds, 0
+ return fds, nil
}
diff --git a/libgo/go/syscall/socket.go b/libgo/go/syscall/socket.go
index e0218ba359d..005fd843486 100644
--- a/libgo/go/syscall/socket.go
+++ b/libgo/go/syscall/socket.go
@@ -17,7 +17,7 @@ import "unsafe"
var SocketDisableIPv6 bool
type Sockaddr interface {
- sockaddr() (ptr *RawSockaddrAny, len Socklen_t, errno int) // lowercase; only we can define Sockaddrs
+ sockaddr() (ptr *RawSockaddrAny, len Socklen_t, err error) // lowercase; only we can define Sockaddrs
}
type RawSockaddrAny struct {
@@ -33,7 +33,7 @@ type SockaddrInet4 struct {
raw RawSockaddrInet4
}
-func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
+func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return nil, 0, EINVAL
}
@@ -45,7 +45,7 @@ func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
for i := 0; i < len(sa.Addr); i++ {
sa.raw.Addr[i] = sa.Addr[i]
}
- return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0
+ return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil
}
type SockaddrInet6 struct {
@@ -55,7 +55,7 @@ type SockaddrInet6 struct {
raw RawSockaddrInet6
}
-func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
+func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return nil, 0, EINVAL
}
@@ -68,7 +68,7 @@ func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
for i := 0; i < len(sa.Addr); i++ {
sa.raw.Addr[i] = sa.Addr[i]
}
- return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0
+ return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil
}
type SockaddrUnix struct {
@@ -76,7 +76,7 @@ type SockaddrUnix struct {
raw RawSockaddrUnix
}
-func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
+func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, error) {
name := sa.Name
n := len(name)
if n >= len(sa.raw.Path) || n == 0 {
@@ -92,186 +92,186 @@ func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
}
// length is family (uint16), name, NUL.
- return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, 0
+ return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, nil
}
-func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) {
+func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family {
case AF_UNIX:
pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa))
sa := new(SockaddrUnix)
n, err := pp.getLen()
- if err != 0 {
+ if err != nil {
return nil, err
}
- bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]));
- sa.Name = string(bytes[0:n]);
- return sa, 0;
+ bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))
+ sa.Name = string(bytes[0:n])
+ return sa, nil
case AF_INET:
- pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa));
- sa := new(SockaddrInet4);
- p := (*[2]byte)(unsafe.Pointer(&pp.Port));
- sa.Port = int(p[0])<<8 + int(p[1]);
+ pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
+ sa := new(SockaddrInet4)
+ p := (*[2]byte)(unsafe.Pointer(&pp.Port))
+ sa.Port = int(p[0])<<8 + int(p[1])
for i := 0; i < len(sa.Addr); i++ {
- sa.Addr[i] = pp.Addr[i];
+ sa.Addr[i] = pp.Addr[i]
}
- return sa, 0;
+ return sa, nil
case AF_INET6:
- pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa));
- sa := new(SockaddrInet6);
- p := (*[2]byte)(unsafe.Pointer(&pp.Port));
- sa.Port = int(p[0])<<8 + int(p[1]);
+ pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
+ sa := new(SockaddrInet6)
+ p := (*[2]byte)(unsafe.Pointer(&pp.Port))
+ sa.Port = int(p[0])<<8 + int(p[1])
for i := 0; i < len(sa.Addr); i++ {
- sa.Addr[i] = pp.Addr[i];
+ sa.Addr[i] = pp.Addr[i]
}
- return sa, 0;
+ return sa, nil
}
return anyToSockaddrOS(rsa)
}
-//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, errno int)
+//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, err error)
//accept(fd int, sa *RawSockaddrAny, len *Socklen_t) int
-func Accept(fd int) (nfd int, sa Sockaddr, errno int) {
+func Accept(fd int) (nfd int, sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len Socklen_t = SizeofSockaddrAny
- nfd, errno = accept(fd, &rsa, &len)
- if errno != 0 {
+ nfd, err = accept(fd, &rsa, &len)
+ if err != nil {
return
}
- sa, errno = anyToSockaddr(&rsa)
- if errno != 0 {
+ sa, err = anyToSockaddr(&rsa)
+ if err != nil {
Close(nfd)
nfd = 0
}
return
}
-//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int)
+//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error)
//getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) int
-func Getsockname(fd int) (sa Sockaddr, errno int) {
+func Getsockname(fd int) (sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len Socklen_t = SizeofSockaddrAny
- if errno = getsockname(fd, &rsa, &len); errno != 0 {
+ if err = getsockname(fd, &rsa, &len); err != nil {
return
}
return anyToSockaddr(&rsa)
}
-//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int)
+//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error)
//getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) int
-func Getpeername(fd int) (sa Sockaddr, errno int) {
+func Getpeername(fd int) (sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len Socklen_t = SizeofSockaddrAny
- if getpeername(fd, &rsa, &len); errno != 0 {
+ if err = getpeername(fd, &rsa, &len); err != nil {
return
}
return anyToSockaddr(&rsa)
}
-//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (errno int)
+//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (err error)
//bind(fd int, sa *RawSockaddrAny, len Socklen_t) int
-func Bind(fd int, sa Sockaddr) (errno int) {
+func Bind(fd int, sa Sockaddr) (err error) {
ptr, n, err := sa.sockaddr()
- if err != 0 {
+ if err != nil {
return err
}
return bind(fd, ptr, n)
}
-//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (errno int)
+//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (err error)
//connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) int
-func Connect(fd int, sa Sockaddr) (errno int) {
+func Connect(fd int, sa Sockaddr) (err error) {
ptr, n, err := sa.sockaddr()
- if err != 0 {
+ if err != nil {
return err
}
return connect(fd, ptr, n)
}
-//sysnb socket(domain int, typ int, proto int) (fd int, errno int)
+//sysnb socket(domain int, typ int, proto int) (fd int, err error)
//socket(domain int, typ int, protocol int) int
-func Socket(domain, typ, proto int) (fd, errno int) {
+func Socket(domain, typ, proto int) (fd int, err error) {
if domain == AF_INET6 && SocketDisableIPv6 {
return -1, EAFNOSUPPORT
}
- fd, errno = socket(domain, typ, proto)
+ fd, err = socket(domain, typ, proto)
return
}
-//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int)
+//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error)
//socketpair(domain int, typ int, protocol int, fd *[2]int) int
-func Socketpair(domain, typ, proto int) (fd [2]int, errno int) {
- errno = socketpair(domain, typ, proto, &fd)
+func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
+ err = socketpair(domain, typ, proto, &fd)
return
}
-//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (errno int)
+//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (err error)
//getsockopt(s int, level int, name int, val *byte, vallen *Socklen_t) int
-func GetsockoptInt(fd, level, opt int) (value, errno int) {
+func GetsockoptInt(fd, level, opt int) (value int, err error) {
var n int32
vallen := Socklen_t(4)
- errno = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen)
- return int(n), errno
+ err = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen)
+ return int(n), err
}
-func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, errno int) {
+func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) {
vallen := Socklen_t(4)
- errno = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen)
- return value, errno
+ err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen)
+ return value, err
}
-func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, int) {
+func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, error) {
var value IPMreq
vallen := Socklen_t(SizeofIPMreq)
- errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
- return &value, errno
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
/* FIXME: mksysinfo needs to support IPMreqn.
-func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, int) {
+func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) {
var value IPMreqn
vallen := Socklen_t(SizeofIPMreqn)
- errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
- return &value, errno
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
*/
/* FIXME: mksysinfo needs to support IPv6Mreq.
-func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, int) {
+func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
var value IPv6Mreq
vallen := Socklen_t(SizeofIPv6Mreq)
- errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
- return &value, errno
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
}
*/
-//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (errno int)
+//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (err error)
//setsockopt(s int, level int, optname int, val *byte, vallen Socklen_t) int
-func SetsockoptInt(fd, level, opt int, value int) (errno int) {
+func SetsockoptInt(fd, level, opt int, value int) (err error) {
var n = int32(value)
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&n)), 4)
}
-func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (errno int) {
+func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&value[0])), 4)
}
-func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) {
+func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(tv)), Socklen_t(unsafe.Sizeof(*tv)))
}
@@ -280,58 +280,58 @@ type Linger struct {
Linger int32;
}
-func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) {
+func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(l)), Socklen_t(unsafe.Sizeof(*l)));
}
-func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) {
+func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq)))
}
/* FIXME: mksysinfo needs to support IMPreqn.
-func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (errno int) {
+func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq)))
}
*/
-func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) {
+func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq)))
}
-func SetsockoptString(fd, level, opt int, s string) (errno int) {
+func SetsockoptString(fd, level, opt int, s string) (err error) {
return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&[]byte(s)[0])), Socklen_t(len(s)))
}
-//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, errno int)
+//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, err error)
//recvfrom(fd int, buf *byte, len Size_t, flags int, from *RawSockaddrAny, fromlen *Socklen_t) Ssize_t
-func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) {
+func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
var rsa RawSockaddrAny
var len Socklen_t = SizeofSockaddrAny
- if n, errno = recvfrom(fd, p, flags, &rsa, &len); errno != 0 {
+ if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
return
}
- from, errno = anyToSockaddr(&rsa)
+ from, err = anyToSockaddr(&rsa)
return
}
-//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (errno int)
+//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (err error)
//sendto(s int, buf *byte, len Size_t, flags int, to *RawSockaddrAny, tolen Socklen_t) Ssize_t
-func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) {
+func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
ptr, n, err := to.sockaddr()
- if err != 0 {
+ if err != nil {
return err
}
return sendto(fd, p, flags, ptr, n)
}
-//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int)
+//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
//recvmsg(s int, msg *Msghdr, flags int) Ssize_t
-func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, errno int) {
+func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
var msg Msghdr
var rsa RawSockaddrAny
msg.Name = (*byte)(unsafe.Pointer(&rsa))
@@ -353,28 +353,28 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
}
msg.Iov = &iov
msg.Iovlen = 1
- if n, errno = recvmsg(fd, &msg, flags); errno != 0 {
+ if n, err = recvmsg(fd, &msg, flags); err != nil {
return
}
oobn = int(msg.Controllen)
recvflags = int(msg.Flags)
// source address is only specified if the socket is unconnected
if rsa.Addr.Family != AF_UNSPEC {
- from, errno = anyToSockaddr(&rsa)
+ from, err = anyToSockaddr(&rsa)
}
return
}
-//sys sendmsg(s int, msg *Msghdr, flags int) (errno int)
+//sys sendmsg(s int, msg *Msghdr, flags int) (err error)
//sendmsg(s int, msg *Msghdr, flags int) Ssize_t
-func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) {
+func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
var ptr *RawSockaddrAny
var salen Socklen_t
if to != nil {
- var err int
+ var err error
ptr, salen, err = to.sockaddr()
- if err != 0 {
+ if err != nil {
return err
}
}
@@ -398,16 +398,16 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) {
}
msg.Iov = &iov
msg.Iovlen = 1
- if errno = sendmsg(fd, &msg, flags); errno != 0 {
+ if err = sendmsg(fd, &msg, flags); err != nil {
return
}
return
}
-//sys Listen(fd int, n int) (errno int)
+//sys Listen(fd int, n int) (err error)
//listen(fd int, n int) int
-//sys Shutdown(fd int, how int) (errno int)
+//sys Shutdown(fd int, how int) (err error)
//shutdown(fd int, how int) int
func (iov *Iovec) SetLen(length int) {
diff --git a/libgo/go/syscall/socket_bsd.go b/libgo/go/syscall/socket_bsd.go
index 735baf98684..be559915951 100644
--- a/libgo/go/syscall/socket_bsd.go
+++ b/libgo/go/syscall/socket_bsd.go
@@ -47,7 +47,7 @@ func (sa *RawSockaddrUnix) setLen(n int) {
sa.Len = uint8(3 + n) // 2 for Family, Len; 1 for NUL.
}
-func (sa *RawSockaddrUnix) getLen() (int, int) {
+func (sa *RawSockaddrUnix) getLen() (int, error) {
if sa.Len < 3 || sa.Len > SizeofSockaddrUnix {
return 0, EINVAL
}
@@ -59,7 +59,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) {
break
}
}
- return n, 0
+ return n, nil
}
type RawSockaddr struct {
@@ -69,10 +69,10 @@ type RawSockaddr struct {
}
// BindToDevice binds the socket associated with fd to device.
-func BindToDevice(fd int, device string) (errno int) {
+func BindToDevice(fd int, device string) (err error) {
return ENOSYS
}
-func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
- return nil, EAFNOSUPPORT;
+func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) {
+ return nil, EAFNOSUPPORT
}
diff --git a/libgo/go/syscall/socket_irix.go b/libgo/go/syscall/socket_irix.go
index 8bd55b8b4d9..c1fdc656ab0 100644
--- a/libgo/go/syscall/socket_irix.go
+++ b/libgo/go/syscall/socket_irix.go
@@ -41,7 +41,7 @@ type RawSockaddrUnix struct {
func (sa *RawSockaddrUnix) setLen(int) {
}
-func (sa *RawSockaddrUnix) getLen() (int, int) {
+func (sa *RawSockaddrUnix) getLen() (int, error) {
if sa.Path[0] == 0 {
// "Abstract" Unix domain socket.
// Rewrite leading NUL as @ for textual display.
@@ -61,7 +61,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) {
n++
}
- return n, 0
+ return n, nil
}
type RawSockaddr struct {
@@ -70,7 +70,7 @@ type RawSockaddr struct {
}
// BindToDevice binds the socket associated with fd to device.
-func BindToDevice(fd int, device string) (errno int) {
+func BindToDevice(fd int, device string) (err error) {
return ENOSYS
}
@@ -124,6 +124,6 @@ const (
EAI_MAX = 14
)
-func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
- return nil, EAFNOSUPPORT;
+func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) {
+ return nil, EAFNOSUPPORT
}
diff --git a/libgo/go/syscall/socket_linux.go b/libgo/go/syscall/socket_linux.go
index 49aac87bb7a..212e0b2d418 100644
--- a/libgo/go/syscall/socket_linux.go
+++ b/libgo/go/syscall/socket_linux.go
@@ -24,7 +24,7 @@ type SockaddrLinklayer struct {
raw RawSockaddrLinklayer
}
-func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
+func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, error) {
if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff {
return nil, 0, EINVAL
}
@@ -37,7 +37,7 @@ func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
for i := 0; i < len(sa.Addr); i++ {
sa.raw.Addr[i] = sa.Addr[i]
}
- return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, 0
+ return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, nil
}
type SockaddrNetlink struct {
@@ -48,12 +48,12 @@ type SockaddrNetlink struct {
raw RawSockaddrNetlink
}
-func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, int) {
+func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, error) {
sa.raw.Family = AF_NETLINK
sa.raw.Pad = sa.Pad
sa.raw.Pid = sa.Pid
sa.raw.Groups = sa.Groups
- return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, 0
+ return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, nil
}
type RawSockaddrInet4 struct {
@@ -87,7 +87,7 @@ type RawSockaddrUnix struct {
func (sa *RawSockaddrUnix) setLen(int) {
}
-func (sa *RawSockaddrUnix) getLen() (int, int) {
+func (sa *RawSockaddrUnix) getLen() (int, error) {
if sa.Path[0] == 0 {
// "Abstract" Unix domain socket.
// Rewrite leading NUL as @ for textual display.
@@ -107,7 +107,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) {
n++
}
- return n, 0
+ return n, nil
}
type RawSockaddrLinklayer struct {
@@ -133,11 +133,11 @@ type RawSockaddr struct {
}
// BindToDevice binds the socket associated with fd to device.
-func BindToDevice(fd int, device string) (errno int) {
+func BindToDevice(fd int, device string) (err error) {
return SetsockoptString(fd, SOL_SOCKET, SO_BINDTODEVICE, device)
}
-func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
+func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family {
case AF_NETLINK:
pp := (*RawSockaddrNetlink)(unsafe.Pointer(rsa))
@@ -146,7 +146,7 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
sa.Pad = pp.Pad
sa.Pid = pp.Pid
sa.Groups = pp.Groups
- return sa, 0
+ return sa, nil
case AF_PACKET:
pp := (*RawSockaddrLinklayer)(unsafe.Pointer(rsa))
@@ -159,16 +159,16 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
for i := 0; i < len(sa.Addr); i++ {
sa.Addr[i] = pp.Addr[i]
}
- return sa, 0
+ return sa, nil
}
return nil, EAFNOSUPPORT
}
-//sysnb EpollCreate(size int) (fd int, errno int)
+//sysnb EpollCreate(size int) (fd int, err error)
//epoll_create(size int) int
-//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int)
+//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error)
//epoll_ctl(epfd int, op int, fd int, event *EpollEvent) int
-//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int)
+//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//epoll_wait(epfd int, events *EpollEvent, maxevents int, timeout int) int
diff --git a/libgo/go/syscall/socket_solaris.go b/libgo/go/syscall/socket_solaris.go
index 0c3e6c9724e..0a03465a338 100644
--- a/libgo/go/syscall/socket_solaris.go
+++ b/libgo/go/syscall/socket_solaris.go
@@ -42,7 +42,7 @@ type RawSockaddrUnix struct {
func (sa *RawSockaddrUnix) setLen(int) {
}
-func (sa *RawSockaddrUnix) getLen() (int, int) {
+func (sa *RawSockaddrUnix) getLen() (int, error) {
if sa.Path[0] == 0 {
// "Abstract" Unix domain socket.
// Rewrite leading NUL as @ for textual display.
@@ -62,7 +62,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) {
n++
}
- return n, 0
+ return n, nil
}
type RawSockaddr struct {
@@ -71,10 +71,10 @@ type RawSockaddr struct {
}
// BindToDevice binds the socket associated with fd to device.
-func BindToDevice(fd int, device string) (errno int) {
+func BindToDevice(fd int, device string) (err error) {
return ENOSYS
}
-func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) {
- return nil, EAFNOSUPPORT;
+func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) {
+ return nil, EAFNOSUPPORT
}
diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go
index a802ba0bbf2..ad9df6cff98 100644
--- a/libgo/go/syscall/syscall.go
+++ b/libgo/go/syscall/syscall.go
@@ -9,8 +9,9 @@
// packages rather than this one if you can.
// For details of the functions and data types in this package consult
// the manuals for the appropriate operating system.
-// These calls return errno == 0 to indicate success; otherwise
-// errno is an operating system error number describing the failure.
+// These calls return err == nil to indicate success; otherwise
+// err is an operating system error describing the failure.
+// On most systems, that error has type syscall.Errno.
package syscall
import "unsafe"
diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go
index c734b2cf06a..899a65ca9b4 100644
--- a/libgo/go/syscall/syscall_unix.go
+++ b/libgo/go/syscall/syscall_unix.go
@@ -23,7 +23,7 @@ func c_syscall64(trap int64, a1, a2, a3, a4, a5, a6 int64) int64 __asm__ ("sysca
// Do a system call. We look at the size of uintptr to see how to pass
// the arguments, so that we don't pass a 64-bit value when the function
// expects a 32-bit one.
-func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
entersyscall()
var r uintptr
if unsafe.Sizeof(r) == 4 {
@@ -33,12 +33,12 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0)
r = uintptr(r1)
}
- errno := GetErrno()
+ err = GetErrno()
exitsyscall()
- return r, 0, uintptr(errno)
+ return r, 0, err
}
-func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
+func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
entersyscall()
var r uintptr
if unsafe.Sizeof(r) == 4 {
@@ -50,12 +50,12 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
int64(a4), int64(a5), int64(a6))
r = uintptr(r1)
}
- errno := GetErrno()
+ err = GetErrno()
exitsyscall()
- return r, 0, uintptr(errno)
+ return r, 0, err
}
-func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
+func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
var r uintptr
if unsafe.Sizeof(r) == 4 {
r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3), 0, 0, 0)
@@ -64,11 +64,11 @@ func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0)
r = uintptr(r1)
}
- errno := GetErrno()
- return r, 0, uintptr(errno)
+ err = GetErrno()
+ return r, 0, err
}
-func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
var r uintptr
if unsafe.Sizeof(r) == 4 {
r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3),
@@ -79,8 +79,8 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
int64(a4), int64(a5), int64(a6))
r = uintptr(r1)
}
- errno := GetErrno()
- return r, 0, uintptr(errno)
+ err = GetErrno()
+ return r, 0, err
}
// Mmap manager, for use by operating system-specific implementations.
@@ -89,18 +89,18 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
type mmapper struct {
sync.Mutex
active map[*byte][]byte // active mappings; key is last byte in mapping
- mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int)
- munmap func(addr uintptr, length uintptr) int
+ mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, error)
+ munmap func(addr uintptr, length uintptr) error
}
-func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) {
+func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
if length <= 0 {
return nil, EINVAL
}
// Map the requested memory.
addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset)
- if errno != 0 {
+ if errno != nil {
return nil, errno
}
@@ -119,10 +119,10 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d
m.Lock()
defer m.Unlock()
m.active[p] = b
- return b, 0
+ return b, nil
}
-func (m *mmapper) Munmap(data []byte) (errno int) {
+func (m *mmapper) Munmap(data []byte) (err error) {
if len(data) == 0 || len(data) != cap(data) {
return EINVAL
}
@@ -137,11 +137,11 @@ func (m *mmapper) Munmap(data []byte) (errno int) {
}
// Unmap the memory and update m.
- if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 {
+ if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != nil {
return errno
}
m.active[p] = nil, false
- return 0
+ return nil
}
var mapper = &mmapper{
@@ -150,10 +150,32 @@ var mapper = &mmapper{
munmap: munmap,
}
-func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) {
+func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
-func Munmap(b []byte) (errno int) {
+func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
+
+
+// An Errno is an unsigned number describing an error condition.
+// It implements the error interface. The zero Errno is by convention
+// a non-error, so code to convert from Errno to error should use:
+// err = nil
+// if errno != 0 {
+// err = errno
+// }
+type Errno uintptr
+
+func (e Errno) Error() string {
+ return Errstr(int(e))
+}
+
+func (e Errno) Temporary() bool {
+ return e == EINTR || e == EMFILE || e.Timeout()
+}
+
+func (e Errno) Timeout() bool {
+ return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
+}
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go
index df4c4a1a29b..e81e5c5845c 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)
@@ -205,7 +207,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
for _, Benchmark := range benchmarks {
matched, err := matchString(*matchBenchmarks, Benchmark.Name)
if err != nil {
- println("invalid regexp for -test.bench:", err.Error())
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err)
os.Exit(1)
}
if !matched {
@@ -218,11 +220,11 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
if procs != 1 {
benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
}
- print(fmt.Sprintf("%s\t", benchName))
+ fmt.Printf("%s\t", benchName)
r := b.run()
- print(fmt.Sprintf("%v\n", r))
+ fmt.Printf("%v\n", r)
if p := runtime.GOMAXPROCS(-1); p != procs {
- print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p))
+ fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
}
}
}
diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go
index 5b3e322b593..e23f13b6f16 100644
--- a/libgo/go/testing/example.go
+++ b/libgo/go/testing/example.go
@@ -21,24 +21,23 @@ type InternalExample struct {
func RunExamples(examples []InternalExample) (ok bool) {
ok = true
+ var eg InternalExample
+
stdout, stderr := os.Stdout, os.Stderr
defer func() {
os.Stdout, os.Stderr = stdout, stderr
if e := recover(); e != nil {
- if err, ok := e.(error); ok {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- panic(e)
+ fmt.Printf("--- FAIL: %s\npanic: %v\n", eg.Name, e)
+ os.Exit(1)
}
}()
- for _, eg := range examples {
+ for _, eg = range examples {
if *chatty {
- fmt.Fprintln(os.Stderr, "=== RUN:", eg.Name)
+ fmt.Printf("=== RUN: %s\n", eg.Name)
}
- // capture stdout and stderr for testing purposes
+ // capture stdout and stderr
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -50,16 +49,16 @@ func RunExamples(examples []InternalExample) (ok bool) {
buf := new(bytes.Buffer)
_, err := io.Copy(buf, r)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
os.Exit(1)
}
outC <- buf.String()
}()
// 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()
@@ -67,16 +66,15 @@ func RunExamples(examples []InternalExample) (ok bool) {
out := <-outC
// report any errors
+ tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
if out != eg.Output {
- fmt.Fprintf(
- os.Stderr,
- "--- FAIL: %s\ngot:\n%s\nwant:\n%s\n",
- eg.Name, out, eg.Output,
+ fmt.Printf(
+ "--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
+ eg.Name, tstr, out, eg.Output,
)
ok = false
} else if *chatty {
- tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
- fmt.Fprintln(os.Stderr, "--- PASS:", eg.Name, tstr)
+ fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr)
}
}
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 5869642c7e1..0b3a07108cc 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -75,8 +75,25 @@ func Short() bool {
return *short
}
-// Insert final newline if needed and tabs after internal newlines.
-func tabify(s string) string {
+// decorate inserts the a final newline if needed and indentation tabs for formatting.
+// If addFileLine is true, it also prefixes the string with the file and line of the call site.
+func decorate(s string, addFileLine bool) string {
+ if addFileLine {
+ _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
+ if ok {
+ // Truncate file name at last file name separator.
+ if index := strings.LastIndex(file, "/"); index >= 0 {
+ file = file[index+1:]
+ } else if index = strings.LastIndex(file, "\\"); index >= 0 {
+ file = file[index+1:]
+ }
+ } else {
+ file = "???"
+ line = 1
+ }
+ s = fmt.Sprintf("%s:%d: %s", file, line, s)
+ }
+ s = "\t" + s // Every line is indented at least one tab.
n := len(s)
if n > 0 && s[n-1] != '\n' {
s += "\n"
@@ -84,7 +101,8 @@ func tabify(s string) string {
}
for i := 0; i < n-1; i++ { // -1 to avoid final newline
if s[i] == '\n' {
- return s[0:i+1] + "\t" + tabify(s[i+1:n])
+ // Second and subsequent lines are indented an extra tab.
+ return s[0:i+1] + "\t" + decorate(s[i+1:n], false)
}
}
return s
@@ -93,12 +111,13 @@ func tabify(s string) 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.
@@ -110,43 +129,44 @@ 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()
}
+// log generates the output. It's always at the same stack depth.
+func (t *T) log(s string) { t.errors += decorate(s, true) }
+
// Log formats its arguments using default formatting, analogous to Print(),
// and records the text in the error log.
-func (t *T) Log(args ...interface{}) { t.errors += "\t" + tabify(fmt.Sprintln(args...)) }
+func (t *T) Log(args ...interface{}) { t.log(fmt.Sprintln(args...)) }
// Logf formats its arguments according to the format, analogous to Printf(),
// and records the text in the error log.
-func (t *T) Logf(format string, args ...interface{}) {
- t.errors += "\t" + tabify(fmt.Sprintf(format, args...))
-}
+func (t *T) Logf(format string, args ...interface{}) { t.log(fmt.Sprintf(format, args...)) }
// Error is equivalent to Log() followed by Fail().
func (t *T) Error(args ...interface{}) {
- t.Log(args...)
+ t.log(fmt.Sprintln(args...))
t.Fail()
}
// Errorf is equivalent to Logf() followed by Fail().
func (t *T) Errorf(format string, args ...interface{}) {
- t.Logf(format, args...)
+ t.log(fmt.Sprintf(format, args...))
t.Fail()
}
// Fatal is equivalent to Log() followed by FailNow().
func (t *T) Fatal(args ...interface{}) {
- t.Log(args...)
+ t.log(fmt.Sprintln(args...))
t.FailNow()
}
// Fatalf is equivalent to Logf() followed by FailNow().
func (t *T) Fatalf(format string, args ...interface{}) {
- t.Logf(format, args...)
+ t.log(fmt.Sprintf(format, args...))
t.FailNow()
}
@@ -165,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
}
@@ -182,22 +202,22 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
testOk := RunTests(matchString, tests)
exampleOk := RunExamples(examples)
if !testOk || !exampleOk {
- fmt.Fprintln(os.Stderr, "FAIL")
+ fmt.Println("FAIL")
os.Exit(1)
}
- fmt.Fprintln(os.Stderr, "PASS")
+ fmt.Println("PASS")
stopAlarm()
RunBenchmarks(matchString, benchmarks)
after()
}
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.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors)
+ fmt.Printf(format, "FAIL", t.name, tstr, t.errors)
} else if *chatty {
- fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors)
+ fmt.Printf(format, "PASS", t.name, tstr, t.errors)
}
}
@@ -217,7 +237,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
for i := 0; i < len(tests); i++ {
matched, err := matchString(*match, tests[i].Name)
if err != nil {
- println("invalid regexp for -test.run:", err.Error())
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
os.Exit(1)
}
if !matched {
@@ -229,7 +249,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
}
t := &T{ch: ch, name: testName, startParallel: startParallel}
if *chatty {
- println("=== RUN", t.name)
+ fmt.Printf("=== RUN %s\n", t.name)
}
go tRunner(t, &tests[i])
out := <-t.ch
@@ -325,7 +345,7 @@ func parseCpuList() {
for _, val := range strings.Split(*cpuListStr, ",") {
cpu, err := strconv.Atoi(val)
if err != nil || cpu <= 0 {
- println("invalid value for -test.cpu")
+ fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val)
os.Exit(1)
}
cpuList = append(cpuList, cpu)
diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go
index c136ca2a175..201a685c630 100644
--- a/libgo/go/text/tabwriter/tabwriter.go
+++ b/libgo/go/text/tabwriter/tabwriter.go
@@ -13,7 +13,6 @@ package tabwriter
import (
"bytes"
"io"
- "os"
"unicode/utf8"
)
@@ -221,7 +220,7 @@ type osError struct {
func (b *Writer) write0(buf []byte) {
n, err := b.output.Write(buf)
if n != len(buf) && err == nil {
- err = os.EIO
+ err = io.ErrShortWrite
}
if err != nil {
panic(osError{err})
diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go
index 42f9e560be1..4208d53a0a4 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 19108825d58..b74bc3b01c9 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 57216676410..cf3c4157281 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,8 +486,13 @@ func testExecute(execTests []execTest, set *Set, t *testing.T) {
"zeroArgs": zeroArgs,
}
for _, test := range execTests {
- tmpl := New(test.name).Funcs(funcs)
- _, err := tmpl.ParseInSet(test.input, 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)
+ }
if err != nil {
t.Errorf("%s: parse error: %s", test.name, err)
continue
@@ -659,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 2ca09a7c17f..d6e4bf1a216 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 a743a8326ec..3636fb54d69 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 00000000000..7b35d2633d5
--- /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 fa562141c29..00000000000
--- a/libgo/go/text/template/parse.go
+++ /dev/null
@@ -1,89 +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, 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. 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) {
- var setFuncs FuncMap
- if set != nil {
- setFuncs = set.parseFuncs
- }
- t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, setFuncs, builtins)
- if err != nil {
- return nil, err
- }
- if set != nil {
- 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 1b6ab3af4f0..346f613b048 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,32 +162,77 @@ 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.
-func (t *Tree) Parse(s, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree *Tree, err error) {
+// 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(true)
+ t.parse(treeSet)
+ t.add(treeSet)
t.stopParse()
return t, nil
}
-// parse is the helper for Parse.
-// It triggers an error if we expect EOF but don't reach it.
-func (t *Tree) parse(toEOF bool) (next Node) {
- t.Root, next = t.itemList(true)
- if toEOF && next != nil {
- t.errorf("unexpected %s", next)
+// 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.
+func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
+ t.Root = newList()
+ for t.peek().typ != itemEOF {
+ if t.peek().typ == itemLeftDelim {
+ delim := t.next()
+ if t.next().typ == itemDefine {
+ newT := New("definition") // name will be updated once we know it.
+ newT.startParse(t.funcs, t.lex)
+ newT.parseDefinition(treeSet)
+ continue
+ }
+ t.backup2(delim)
+ }
+ n := t.textOrAction()
+ if n.Type() == nodeEnd {
+ t.errorf("unexpected %s", n)
+ }
+ t.Root.append(n)
+ }
+ return nil
+}
+
+// parseDefinition parses a {{define}} ... {{end}} template definition and
+// installs the definition in the treeSet map. The "define" keyword has already
+// been scanned.
+func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
+ const context = "define clause"
+ name := t.expectOneOf(itemString, itemRawString, context)
+ var err error
+ t.Name, err = strconv.Unquote(name.val)
+ if err != nil {
+ t.error(err)
+ }
+ t.expect(itemRightDelim, context)
+ var end Node
+ t.Root, end = t.itemList()
+ if end.Type() != nodeEnd {
+ t.errorf("unexpected %s in %s", end, context)
}
- return next
+ t.stopParse()
+ t.add(treeSet)
}
// itemList:
// textOrAction*
-// Terminates at EOF and at {{end}} or {{else}}, which is returned separately.
-// The toEOF flag tells whether we expect to reach EOF.
-func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
+// Terminates at {{end}} or {{else}}, returned separately.
+func (t *Tree) itemList() (list *ListNode, next Node) {
list = newList()
for t.peek().typ != itemEOF {
n := t.textOrAction()
@@ -178,10 +242,8 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
}
list.append(n)
}
- if !toEOF {
- t.unexpected(t.next(), "input")
- }
- return list, nil
+ t.errorf("unexpected EOF")
+ return
}
// textOrAction:
@@ -276,11 +338,11 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list,
defer t.popVars(len(t.vars))
pipe = t.pipeline(context)
var next Node
- list, next = t.itemList(false)
+ list, next = t.itemList()
switch next.Type() {
case nodeEnd: //done
case nodeElse:
- elseList, next = t.itemList(false)
+ elseList, next = t.itemList()
if next.Type() != nodeEnd {
t.errorf("expected end; found %s", next)
}
diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go
index f05f6e3874c..fc93455ecbc 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, "", "", 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 d363eeff080..00000000000
--- a/libgo/go/text/template/parse/set.go
+++ /dev/null
@@ -1,49 +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
-
-import (
- "fmt"
- "strconv"
-)
-
-// 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)
- defer (*Tree)(nil).recover(&err)
- lex := lex("set", text, leftDelim, rightDelim)
- const context = "define clause"
- for {
- t := New("set") // name will be updated once we know it.
- t.startParse(funcs, lex)
- // Expect EOF or "{{ define name }}".
- if t.atEOF() {
- break
- }
- t.expect(itemLeftDelim, context)
- t.expect(itemDefine, context)
- name := t.expect(itemString, context)
- t.Name, err = strconv.Unquote(name.val)
- if err != nil {
- t.error(err)
- }
- t.expect(itemRightDelim, context)
- end := t.parse(false)
- if end == nil {
- t.errorf("unexpected EOF in %s", context)
- }
- if end.Type() != nodeEnd {
- t.errorf("unexpected %s in %s", end, context)
- }
- t.stopParse()
- if _, present := tree[t.Name]; present {
- return nil, fmt.Errorf("template: %q multiply defined", name)
- }
- tree[t.Name] = t
- }
- return
-}
diff --git a/libgo/go/text/template/set.go b/libgo/go/text/template/set.go
deleted file mode 100644
index 747cc7802b4..00000000000
--- a/libgo/go/text/template/set.go
+++ /dev/null
@@ -1,120 +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
- 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 f437bc779c2..00000000000
--- 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 00000000000..04fca407c10
--- /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 3d15b81735a..b72b3a340c7 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 a374d2fe7fc..16beba6e7dd 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 00000000000..153b1a3b660
--- /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 14b712ad086..082a51a1621 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 d7e7076539f..2c4df335f9b 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 314622d0dc9..1e23118f378 100644
--- a/libgo/go/time/sleep.go
+++ b/libgo/go/time/sleep.go
@@ -4,174 +4,89 @@
package time
-import (
- "container/heap"
- "sync"
-)
-
-// The Timer type represents a single event.
-// When the Timer expires, the current time will be sent on C
-// unless the Timer represents an AfterFunc event.
-type Timer struct {
- C <-chan int64
- t int64 // The absolute time that the event should fire.
- f func(int64) // The function to call when the event fires.
- i int // The event's index inside eventHeap.
+func nano() int64 {
+ sec, nsec := now()
+ return sec*1e9 + int64(nsec)
}
-type timerHeap []*Timer
-
-// forever is the absolute time (in ns) of an event that is forever away.
-const forever = 1 << 62
-
-// maxSleepTime is the maximum length of time that a sleeper
-// sleeps for before checking if it is defunct.
-const maxSleepTime = 1e9
-
-var (
- // timerMutex guards the variables inside this var group.
- timerMutex sync.Mutex
-
- // timers holds a binary heap of pending events, terminated with a sentinel.
- timers timerHeap
-
- // currentSleeper is an ever-incrementing counter which represents
- // the current sleeper. It allows older sleepers to detect that they are
- // defunct and exit.
- currentSleeper int64
-)
-
-func init() {
- timers.Push(&Timer{t: forever}) // sentinel
+// Interface to timers implemented in package runtime.
+// Must be in sync with ../runtime/runtime.h:/^struct.Timer$
+type runtimeTimer struct {
+ i int32
+ when int64
+ period int64
+ f func(int64, interface{})
+ arg interface{}
}
-// 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)
- e := after(ns, func(t int64) { c <- t })
- e.C = c
- return e
-}
+func startTimer(*runtimeTimer)
+func stopTimer(*runtimeTimer) bool
-// After waits at least ns nanoseconds before sending the current time
-// on the returned channel.
-// It is equivalent to NewTimer(ns).C.
-func After(ns int64) <-chan int64 {
- return NewTimer(ns).C
-}
-
-// AfterFunc waits at least ns nanoseconds before calling f
-// in its own goroutine. It returns a Timer that can
-// be used to cancel the call using its Stop method.
-func AfterFunc(ns int64, f func()) *Timer {
- return after(ns, func(_ int64) {
- go f()
- })
+// The Timer type represents a single event.
+// When the Timer expires, the current time will be sent on C,
+// unless the Timer was created by AfterFunc.
+type Timer struct {
+ C <-chan Time
+ r runtimeTimer
}
// Stop prevents the Timer from firing.
// It returns true if the call stops the timer, false if the timer has already
// expired or stopped.
-func (e *Timer) Stop() (ok bool) {
- timerMutex.Lock()
- // Avoid removing the first event in the queue so that
- // we don't start a new sleeper unnecessarily.
- if e.i > 0 {
- heap.Remove(timers, e.i)
- }
- ok = e.f != nil
- e.f = nil
- timerMutex.Unlock()
- return
+func (t *Timer) Stop() (ok bool) {
+ return stopTimer(&t.r)
}
-// after is the implementation of After and AfterFunc.
-// When the current time is after ns, it calls f with the current time.
-// It assumes that f will not block.
-func after(ns int64, f func(int64)) (e *Timer) {
- now := Nanoseconds()
- t := now + ns
- if ns > 0 && t < now {
- panic("time: time overflow")
- }
- timerMutex.Lock()
- t0 := timers[0].t
- e = &Timer{nil, t, f, -1}
- heap.Push(timers, e)
- // Start a new sleeper if the new event is before
- // the first event in the queue. If the length of time
- // until the new event is at least maxSleepTime,
- // then we're guaranteed that the sleeper will wake up
- // in time to service it, so no new sleeper is needed.
- if t0 > t && (t0 == forever || ns < maxSleepTime) {
- currentSleeper++
- go sleeper(currentSleeper)
+// NewTimer creates a new Timer that will send
+// the current time on its channel after at least ns nanoseconds.
+func NewTimer(d Duration) *Timer {
+ c := make(chan Time, 1)
+ t := &Timer{
+ C: c,
+ r: runtimeTimer{
+ when: nano() + int64(d),
+ f: sendTime,
+ arg: c,
+ },
}
- timerMutex.Unlock()
- return
+ startTimer(&t.r)
+ return t
}
-// sleeper continually looks at the earliest event in the queue, waits until it happens,
-// then removes any events in the queue that are due. It stops when the queue
-// is empty or when another sleeper has been started.
-func sleeper(sleeperId int64) {
- timerMutex.Lock()
- e := timers[0]
- t := Nanoseconds()
- for e.t != forever {
- if dt := e.t - t; dt > 0 {
- if dt > maxSleepTime {
- dt = maxSleepTime
- }
- timerMutex.Unlock()
- sysSleep(dt)
- timerMutex.Lock()
- if currentSleeper != sleeperId {
- // Another sleeper has been started, making this one redundant.
- break
- }
- }
- e = timers[0]
- t = Nanoseconds()
- for t >= e.t {
- if e.f != nil {
- e.f(t)
- e.f = nil
- }
- heap.Pop(timers)
- e = timers[0]
- }
+func sendTime(now int64, c interface{}) {
+ // Non-blocking send of time on c.
+ // Used in NewTimer, it cannot block anyway (buffer).
+ // Used in NewTicker, dropping sends on the floor is
+ // the desired behavior when the reader gets behind,
+ // because the sends are periodic.
+ select {
+ case c.(chan Time) <- Unix(0, now):
+ default:
}
- timerMutex.Unlock()
-}
-
-func (timerHeap) Len() int {
- return len(timers)
}
-func (timerHeap) Less(i, j int) bool {
- return timers[i].t < timers[j].t
-}
-
-func (timerHeap) Swap(i, j int) {
- timers[i], timers[j] = timers[j], timers[i]
- timers[i].i = i
- timers[j].i = j
+// 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(d Duration) <-chan Time {
+ return NewTimer(d).C
}
-func (timerHeap) Push(x interface{}) {
- e := x.(*Timer)
- e.i = len(timers)
- timers = append(timers, e)
+// AfterFunc waits at least ns nanoseconds before calling f
+// in its own goroutine. It returns a Timer that can
+// be used to cancel the call using its Stop method.
+func AfterFunc(ns int64, f func()) *Timer {
+ t := &Timer{
+ r: runtimeTimer{
+ when: nano() + ns,
+ f: goFunc,
+ arg: f,
+ },
+ }
+ startTimer(&t.r)
+ return t
}
-func (timerHeap) Pop() interface{} {
- // TODO: possibly shrink array.
- n := len(timers) - 1
- e := timers[n]
- timers[n] = nil
- timers = timers[0:n]
- e.i = -1
- return e
+func goFunc(now int64, arg interface{}) {
+ go arg.(func())()
}
diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go
index 0662e3359cf..cbcc897fd40 100644
--- a/libgo/go/time/sleep_test.go
+++ b/libgo/go/time/sleep_test.go
@@ -7,22 +7,24 @@ package time_test
import (
"errors"
"fmt"
+ "runtime"
"sort"
+ "sync/atomic"
"testing"
. "time"
)
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)
}
}
@@ -47,6 +49,23 @@ func TestAfterFunc(t *testing.T) {
<-c
}
+func TestAfterStress(t *testing.T) {
+ stop := uint32(0)
+ go func() {
+ for atomic.LoadUint32(&stop) == 0 {
+ runtime.GC()
+ // Need to yield, because otherwise
+ // the main goroutine will never set the stop flag.
+ runtime.Gosched()
+ }
+ }()
+ c := Tick(1)
+ for i := 0; i < 100; i++ {
+ <-c
+ }
+ atomic.StoreUint32(&stop, 1)
+}
+
func BenchmarkAfterFunc(b *testing.B) {
i := b.N
c := make(chan bool)
@@ -77,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)
}
}
@@ -152,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 ca1d334a5b7..fe6bc27d301 100644
--- a/libgo/go/time/sys.go
+++ b/libgo/go/time/sys.go
@@ -4,38 +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.
-
-// Nanoseconds reports the number of nanoseconds since the Unix epoch,
-// January 1, 1970 00:00:00 UTC.
-func Nanoseconds() int64
+import "syscall"
-// Sleep pauses the current goroutine for at least ns nanoseconds.
-// Higher resolution sleeping may be provided by syscall.Nanosleep
-// on some operating systems.
-func Sleep(ns int64) error {
- _, err := sleep(Nanoseconds(), ns)
- return err
-}
+// Sleep pauses the current goroutine for the duration d.
+func Sleep(d Duration)
-// sleep takes the current time and a duration,
-// pauses for at least ns nanoseconds, and
-// returns the current time and an error.
-func sleep(t, ns int64) (int64, error) {
- // TODO(cw): use monotonic-time once it's available
- end := t + ns
- for t < end {
- err := sysSleep(end - t)
- if err != nil {
- return 0, err
+// 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
}
- t = Nanoseconds()
}
- return t, nil
+ return ret, err
}
diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go
index a630b3ee030..e58fb519ea3 100644
--- a/libgo/go/time/sys_plan9.go
+++ b/libgo/go/time/sys_plan9.go
@@ -4,19 +4,6 @@
package time
-import (
- "os"
- "syscall"
-)
-
-func sysSleep(t int64) error {
- err := syscall.Sleep(t)
- if err != nil {
- return os.NewSyscallError("sleep", err)
- }
- return nil
-}
-
// for testing: whatever interrupts a sleep
func interrupt() {
// cannot predict pid, don't want to kill group
diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go
index 17a6a2d63e0..715d186be17 100644
--- a/libgo/go/time/sys_unix.go
+++ b/libgo/go/time/sys_unix.go
@@ -6,20 +6,9 @@
package time
-import (
- "os"
- "syscall"
-)
-
-func sysSleep(t int64) error {
- errno := syscall.Sleep(t)
- if errno != 0 && errno != syscall.EINTR {
- return os.NewSyscallError("sleep", errno)
- }
- return nil
-}
+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/sys_windows.go b/libgo/go/time/sys_windows.go
index f9d6e89281c..8c7242f4275 100644
--- a/libgo/go/time/sys_windows.go
+++ b/libgo/go/time/sys_windows.go
@@ -4,19 +4,6 @@
package time
-import (
- "os"
- "syscall"
-)
-
-func sysSleep(t int64) error {
- errno := syscall.Sleep(t)
- if errno != 0 && errno != syscall.EINTR {
- return os.NewSyscallError("sleep", errno)
- }
- return nil
-}
-
// for testing: whatever interrupts a sleep
func interrupt() {
}
diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go
index 92f9eb893e4..4440c2207b3 100644
--- a/libgo/go/time/tick.go
+++ b/libgo/go/time/tick.go
@@ -4,174 +4,50 @@
package time
-import (
- "errors"
- "sync"
-)
+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<- int64 // The same channel, but the end we use.
- ns int64
- shutdown chan bool // Buffered channel used to signal shutdown.
- nextTick int64
- next *Ticker
+ 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, 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 Time, 1)
+ t := &Ticker{
+ C: c,
+ r: runtimeTimer{
+ when: nano() + int64(d),
+ period: int64(d),
+ f: sendTime,
+ arg: c,
+ },
+ }
+ startTimer(&t.r)
+ return t
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
- select {
- case t.shutdown <- true:
- // ok
- default:
- // Stop in progress already
- }
+ stopTimer(&t.r)
}
// 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
-}
-
-type alarmer struct {
- wakeUp chan bool // wakeup signals sent/received here
- wakeMeAt chan int64
- wakeTime int64
-}
-
-// Set alarm to go off at time ns, if not already set earlier.
-func (a *alarmer) set(ns int64) {
- switch {
- case a.wakeTime > ns:
- // Next tick we expect is too late; shut down the late runner
- // and (after fallthrough) start a new wakeLoop.
- close(a.wakeMeAt)
- fallthrough
- case a.wakeMeAt == nil:
- // There's no wakeLoop, start one.
- a.wakeMeAt = make(chan int64)
- a.wakeUp = make(chan bool, 1)
- go wakeLoop(a.wakeMeAt, a.wakeUp)
- fallthrough
- case a.wakeTime == 0:
- // Nobody else is waiting; it's just us.
- a.wakeTime = ns
- a.wakeMeAt <- ns
- default:
- // There's already someone scheduled.
- }
-}
-
-// Channel to notify tickerLoop of new Tickers being created.
-var newTicker chan *Ticker
-
-func startTickerLoop() {
- newTicker = make(chan *Ticker)
- go tickerLoop()
-}
-
-// wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
-// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
-// wakeLoop and signal that this one is done by closing the wakeMeAt channel.
-func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
- for wakeAt := range wakeMeAt {
- Sleep(wakeAt - Nanoseconds())
- wakeUp <- true
- }
-}
-
-// A single tickerLoop serves all ticks to Tickers. It waits for two events:
-// either the creation of a new Ticker or a tick from the alarm,
-// signaling a time to wake up one or more Tickers.
-func tickerLoop() {
- // Represents the next alarm to be delivered.
- var alarm alarmer
- var now, wakeTime int64
- var tickers *Ticker
- for {
- select {
- case t := <-newTicker:
- // Add Ticker to list
- t.next = tickers
- tickers = t
- // Arrange for a new alarm if this one precedes the existing one.
- alarm.set(t.nextTick)
- case <-alarm.wakeUp:
- now = Nanoseconds()
- wakeTime = now + 1e15 // very long in the future
- var prev *Ticker = nil
- // Scan list of tickers, delivering updates to those
- // that need it and determining the next wake time.
- // TODO(r): list should be sorted in time order.
- for t := tickers; t != nil; t = t.next {
- select {
- case <-t.shutdown:
- // Ticker is done; remove it from list.
- if prev == nil {
- tickers = t.next
- } else {
- prev.next = t.next
- }
- continue
- default:
- }
- if t.nextTick <= now {
- if len(t.c) == 0 {
- // Only send if there's room. We must not block.
- // The channel is allocated with a one-element
- // buffer, which is sufficient: if he hasn't picked
- // up the last tick, no point in sending more.
- t.c <- now
- }
- t.nextTick += t.ns
- if t.nextTick <= now {
- // Still behind; advance in one big step.
- t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
- }
- }
- if t.nextTick < wakeTime {
- wakeTime = t.nextTick
- }
- prev = t
- }
- if tickers != nil {
- // Please send wakeup at earliest required time.
- // If there are no tickers, don't bother.
- alarm.wakeTime = wakeTime
- alarm.wakeMeAt <- wakeTime
- } else {
- alarm.wakeTime = 0
- }
- }
- }
-}
-
-var onceStartTickerLoop sync.Once
-
-// 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 {
- panic(errors.New("non-positive interval for NewTicker"))
- }
- c := make(chan int64, 1) // See comment on send in tickerLoop
- t := &Ticker{
- C: c,
- c: c,
- ns: ns,
- shutdown: make(chan bool, 1),
- nextTick: Nanoseconds() + ns,
- }
- onceStartTickerLoop.Do(startTickerLoop)
- // must be run in background so global Tickers can be created
- go func() { newTicker <- t }()
- return t
+ return NewTicker(d).C
}
diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go
index 4dcb63956b2..36349349ce0 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 859b3167278..04ed86cf25f 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,224 +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",
+}
+
+// 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.
+
+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
+ }
+ // 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))
+}
+
+// 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
+}
+
+// Year returns the year in which t occurs.
+func (t Time) Year() int {
+ year, _, _, _ := t.date(false)
+ return year
}
-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}
+// Month returns the month of the year specified by t.
+func (t Time) Month() Month {
+ _, month, _, _ := t.date(true)
+ return month
+}
-func months(year int64) []int {
- if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
- return leapyear
+// 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++
}
- return nonleapyear
+
+ // 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++
+ }
+ }
+
+ // 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
+
+// Common durations. There is no definition for units of Day or larger
+// to avoid confusion across daylight savings time zone transitions.
const (
- secondsPerDay = 24 * 60 * 60
- daysPer400Years = 365*400 + 97
- daysPer100Years = 365*100 + 24
- daysPer4Years = 365*4 + 1
- days1970To2001 = 31*365 + 8
+ Nanosecond Duration = 1
+ Microsecond = 1000 * Nanosecond
+ Millisecond = 1000 * Microsecond
+ Second = 1000 * Millisecond
+ Minute = 60 * Second
+ Hour = 60 * Minute
)
-// 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)
+// 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
+ }
+
+ 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)
+ }
+ }
+ }
- // Split into time and day.
- day := sec / secondsPerDay
- sec -= day * secondsPerDay
- if sec < 0 {
- day--
- sec += secondsPerDay
+ if neg {
+ w--
+ buf[w] = '-'
}
- // Time
- t.Hour = int(sec / 3600)
- t.Minute = int((sec / 60) % 60)
- t.Second = int(sec % 60)
+ return string(buf[w:])
+}
- // 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
+// 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
+}
- 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
+// 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
+ }
}
+ return w
+}
- // Cut off 400 year cycles.
- n := day / daysPer400Years
- year += 400 * n
- day -= daysPer400Years * n
+// 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
+}
+
+// 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)
+}
- // Cut off 100-year cycles
- n = day / daysPer100Years
- if n > 3 { // happens on last day of 400th year
- n = 3
+// 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)
+}
+
+// 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
}
- year += 100 * n
- day -= daysPer100Years * n
+ return t
+}
- // Cut off 4-year cycles
- n = day / daysPer4Years
- if n > 24 { // happens on last day of 100th year
- n = 24
+// 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)
+}
+
+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
+ }
+
+ 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
+ }
}
- 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
+ // 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])
}
- year += n
- day -= 365 * n
- t.Year = year
+ month++ // because January is 1
+ day = day - begin + 1
+ return
+}
- // If someone ever needs yearday,
- // tyearday = day (+1?)
+// 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,
+}
- months := months(year)
- var m int
- yday := int(day)
- for m = 0; m < 12 && yday >= months[m]; m++ {
- yday -= months[m]
+func daysIn(m Month, year int) int {
+ if m == February && isLeap(year) {
+ return 29
}
- t.Month = m + 1
- t.Day = yday + 1
- t.Zone = "UTC"
+ return int(daysBefore[m+1] - daysBefore[m])
+}
- return t
+// Provided by package runtime.
+func now() (sec int64, nsec int32)
+
+// Now returns the current local time.
+func Now() Time {
+ sec, nsec := now()
+ return Time{sec + unixToInternal, nsec, Local}
}
-// 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)
+// UTC returns t with the location set to UTC.
+func (t Time) UTC() Time {
+ t.loc = UTC
return t
}
-// UTC returns the current time as a parsed Time value in the UTC time zone.
-func UTC() *Time { return NanosecondsToUTC(Nanoseconds()) }
-
-// 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
+// Local returns t with the location set to local time.
+func (t Time) Local() Time {
+ t.loc = Local
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)
+// 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")
+ }
+ t.loc = loc
return t
}
-// LocalTime returns the current time as a parsed Time value in the local time zone.
-func LocalTime() *Time { return NanosecondsToLocalTime(Nanoseconds()) }
+// Location returns the time zone information associated with t.
+func (t Time) Location() *Location {
+ l := t.loc
+ if l == nil {
+ l = UTC
+ }
+ return l
+}
+
+// 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
+}
+
+// Unix returns the Unix time, the number of seconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) Unix() int64 {
+ return t.sec + internalToUnix
+}
-// 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)
+// 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)
+}
- // Rewrite year to be >= 2001.
- year := t.Year
- if year < 2001 {
- n := (2001-year)/400 + 1
- year += 400 * n
- day -= daysPer400Years * n
+// 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 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
+}
+
+// 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")
+ }
+
+ // 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 := (year - 2001) / 400
- year -= 400 * n
- day += daysPer400Years * n
+ n := y / 400
+ y -= 400 * n
+ d := daysPer400Years * n
// Add in 100-year cycles.
- n = (year - 2001) / 100
- year -= 100 * n
- day += daysPer100Years * n
+ n = y / 100
+ y -= 100 * n
+ d += daysPer100Years * n
// Add in 4-year cycles.
- n = (year - 2001) / 4
- year -= 4 * n
- day += daysPer4Years * n
+ n = y / 4
+ y -= 4 * n
+ d += daysPer4Years * n
// Add in non-leap years.
- n = year - 2001
- day += 365 * n
+ n = y
+ d += 365 * n
- // Add in days this year.
- months := months(t.Year)
- for m := 0; m < t.Month-1; m++ {
- day += int64(months[m])
+ // Add in days before this month.
+ d += uint64(daysBefore[month-1])
+ if isLeap(year) && month >= March {
+ d++ // February 29
}
- day += int64(t.Day - 1)
- // Convert days to seconds since January 1, 2001.
- sec := day * secondsPerDay
+ // Add in days before today.
+ d += uint64(day - 1)
// Add in time elapsed today.
- sec += int64(t.Hour) * 3600
- sec += int64(t.Minute) * 60
- sec += int64(t.Second)
-
- // Convert from seconds since 2001 to seconds since 1970.
- sec += days1970To2001 * secondsPerDay
-
- // Account for local time zone.
- sec -= int64(t.ZoneOffset)
- return sec
-}
-
-// 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)
-}
-
-// 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--
- }
- // Day 0 = January 1, 1970 was a Thursday
- weekday := int((day + Thursday) % 7)
- if weekday < 0 {
- weekday += 7
+ 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 weekday
+
+ return Time{unix + unixToInternal, int32(nsec), loc}
}
diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go
index 8b373a13bc2..9590e281a66 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,25 +494,152 @@ 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 int // year
+ month, day int // month and day
+ yex int // expected year
+ wex int // expected week
+}
+
+var isoWeekTests = []ISOWeekTest{
+ {1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52},
+ {1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1},
+ {1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52},
+ {1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1},
+ {1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1},
+ {1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2},
+ {1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53},
+ {2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1},
+ {2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53},
+ {2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1},
+ {2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53},
+ {2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1},
+ {2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1},
+ {2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1},
+ {2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23},
+ {2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52},
+ {2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52},
+ {2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52},
+ {2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1},
+ {2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52},
+ {2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1},
+ {2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51},
+ {2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1},
+ {2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2},
+ {2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52},
+ {2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1},
+ {2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52},
+ {2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1},
+ {2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1},
+ {2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1},
+ {2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1},
+ {2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53},
+ {2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52},
+}
+
+func TestISOWeek(t *testing.T) {
+ // Selected dates and corner cases
+ for _, wt := range isoWeekTests {
+ 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",
+ y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day)
+ }
+ }
+
+ // The only real invariant: Jan 04 is in week 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)
+ }
+ }
+ }
+}
+
+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 BenchmarkNanoseconds(b *testing.B) {
+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")
}
@@ -502,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 00000000000..aca56e746af
--- /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 577ef85bd68..38aefc7a977 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 b0fa6c33b65..00000000000
--- 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 0dc42353136..83d5b983c6e 100644
--- a/libgo/go/time/zoneinfo_unix.go
+++ b/libgo/go/time/zoneinfo_unix.go
@@ -12,8 +12,8 @@
package time
import (
- "io/ioutil"
- "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,84 +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
+ }
+ }
+
+ // Commited to succeed.
+ l = &Location{zone: zone, tx: tx}
+
+ // 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]
}
}
- return zt, true
+
+ return l, nil
}
-func readinfofile(name string) ([]zonetime, bool) {
- buf, err := ioutil.ReadFile(name)
+func loadZoneFile(name string) (l *Location, err error) {
+ buf, err := readFile(name)
if err != nil {
- return nil, false
+ return
}
- return parseinfo(buf)
+ return loadZoneData(buf)
+}
+
+func initTestingZone() {
+ syscall.Setenv("TZ", "America/Los_Angeles")
+ initLocal()
}
-func setupTestingZone() {
- os.Setenv("TZ", "America/Los_Angeles")
- 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 setupZone() {
+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 ba152e0882a..beef4de92b0 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 != 0 {
- 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 5dfd824e6e5..89cdcda71ae 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 4d5360ff4b9..ec7b7ae0a45 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 b17d9470bbc..ff386dc7f21 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 57dc4fd1dff..8320b032ead 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/go/websocket/websocket.go b/libgo/go/websocket/websocket.go
index 1e4036ce391..df4416e22ed 100644
--- a/libgo/go/websocket/websocket.go
+++ b/libgo/go/websocket/websocket.go
@@ -10,12 +10,12 @@ import (
"bufio"
"crypto/tls"
"encoding/json"
+ "errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
- "os"
"sync"
)
@@ -243,12 +243,14 @@ func (ws *Conn) RemoteAddr() net.Addr {
return &Addr{ws.config.Origin}
}
+var errSetTimeout = errors.New("websocket: cannot set timeout: not using a net.Conn")
+
// SetTimeout sets the connection's network timeout in nanoseconds.
func (ws *Conn) SetTimeout(nsec int64) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetTimeout(nsec)
}
- return os.EINVAL
+ return errSetTimeout
}
// SetReadTimeout sets the connection's network read timeout in nanoseconds.
@@ -256,7 +258,7 @@ func (ws *Conn) SetReadTimeout(nsec int64) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetReadTimeout(nsec)
}
- return os.EINVAL
+ return errSetTimeout
}
// SetWriteTimeout sets the connection's network write timeout in nanoseconds.
@@ -264,7 +266,7 @@ func (ws *Conn) SetWriteTimeout(nsec int64) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetWriteTimeout(nsec)
}
- return os.EINVAL
+ return errSetTimeout
}
// Config returns the WebSocket config.
diff --git a/libgo/merge.sh b/libgo/merge.sh
index e6d7898dfe1..496abe53ad5 100755
--- a/libgo/merge.sh
+++ b/libgo/merge.sh
@@ -137,9 +137,6 @@ merge_c() {
}
(cd ${NEWDIR}/src/pkg && find . -name '*.go' -print) | while read f; do
- if test `dirname $f` = "./syscall"; then
- continue
- fi
oldfile=${OLDDIR}/src/pkg/$f
newfile=${NEWDIR}/src/pkg/$f
libgofile=go/$f
diff --git a/libgo/mksysinfo.sh b/libgo/mksysinfo.sh
index e493ef072c1..a5e8144cd77 100755
--- a/libgo/mksysinfo.sh
+++ b/libgo/mksysinfo.sh
@@ -120,9 +120,10 @@ grep -v '^// ' gen-sysinfo.go | \
-e 's/\([^a-zA-Z0-9_]\)_timestruc_t\([^a-zA-Z0-9_]\)/\1Timestruc\2/g' \
>> ${OUT}
-# The errno constants.
-grep '^const _E' gen-sysinfo.go | \
- sed -e 's/^\(const \)_\(E[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
+# The errno constants. These get type Errno.
+echo '#include <errno.h>' | ${CC} -x c - -E -dM | \
+ egrep '#define E[A-Z0-9_]+ ' | \
+ sed -e 's/^#define \(E[A-Z0-9_]*\) .*$/const \1 = Errno(_\1)/' >> ${OUT}
# The O_xxx flags.
egrep '^const _(O|F|FD)_' gen-sysinfo.go | \
@@ -182,17 +183,21 @@ grep '^const _SOMAXCONN' gen-sysinfo.go |
grep '^const _SHUT_' gen-sysinfo.go |
sed -e 's/^\(const \)_\(SHUT[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
-# The net package requires a definition for IPV6ONLY.
-if ! grep '^const IPV6_V6ONLY ' ${OUT} >/dev/null 2>&1; then
- echo "const IPV6_V6ONLY = 0" >> ${OUT}
-fi
+# The net package requires some const definitions.
+for m in IPV6_V6ONLY IPPROTO_IPV6 IPV6_JOIN_GROUP IPV6_LEAVE_GROUP; do
+ if ! grep "^const $m " ${OUT} >/dev/null 2>&1; then
+ echo "const $m = 0" >> ${OUT}
+ fi
+done
# pathconf constants.
grep '^const __PC' gen-sysinfo.go |
sed -e 's/^\(const \)__\(PC[^= ]*\)\(.*\)$/\1\2 = __\2/' >> ${OUT}
-# The epoll constants were picked up by the errno constants, but we
-# need to be sure the EPOLLRDHUP is defined.
+# epoll constants.
+grep '^const _EPOLL' gen-sysinfo.go |
+ sed -e 's/^\(const \)_\(EPOLL[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
+# Make sure EPOLLRDHUP is defined.
if ! grep '^const EPOLLRDHUP' ${OUT} >/dev/null 2>&1; then
echo "const EPOLLRDHUP = 0x2000" >> ${OUT}
fi
@@ -471,11 +476,14 @@ grep '^type _ip_mreq ' gen-sysinfo.go | \
-e 's/_in_addr/[4]byte/g' \
>> ${OUT}
-# The size of the ip_mreq struct.
-if grep 'type IPMreq ' ${OUT} > /dev/null 2>&1; then
- echo 'var SizeofIPMreq = int(unsafe.Sizeof(IPMreq{}))' >> ${OUT}
+# We need IPMreq to compile the net package.
+if ! grep 'type IPMreq ' ${OUT} >/dev/null 2>&1; then
+ echo 'type IPMreq struct { Multiaddr [4]byte; Interface [4]byte; }' >> ${OUT}
fi
+# The size of the ip_mreq struct.
+echo 'var SizeofIPMreq = int(unsafe.Sizeof(IPMreq{}))' >> ${OUT}
+
# The ipv6_mreq struct.
grep '^type _ipv6_mreq ' gen-sysinfo.go | \
sed -e 's/_ipv6_mreq/IPv6Mreq/' \
@@ -484,6 +492,11 @@ grep '^type _ipv6_mreq ' gen-sysinfo.go | \
-e 's/_in6_addr/[16]byte/' \
>> ${OUT}
+# We need IPv6Mreq to compile the net package.
+if ! grep 'type IPv6Mreq ' ${OUT} >/dev/null 2>&1; then
+ echo 'type IPv6Mreq struct { Multiaddr [16]byte; Interface uint32; }' >> ${OUT}
+fi
+
# Try to guess the type to use for fd_set.
fd_set=`grep '^type _fd_set ' gen-sysinfo.go || true`
fds_bits_type="_C_long"
@@ -498,9 +511,11 @@ grep '^type _addrinfo ' gen-sysinfo.go | \
-e 's/ ai_/ Ai_/g' \
>> ${OUT}
-# The addrinfo flags.
+# The addrinfo flags and errors.
grep '^const _AI_' gen-sysinfo.go | \
sed -e 's/^\(const \)_\(AI_[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
+grep '^const _EAI_' gen-sysinfo.go | \
+ sed -e 's/^\(const \)_\(EAI_[^= ]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
# The passwd struct.
grep '^type _passwd ' gen-sysinfo.go | \
@@ -644,17 +659,18 @@ grep '^type _termios ' gen-sysinfo.go | \
-e 's/c_ospeed/Ospeed/' \
>> ${OUT}
-# The termios constants. The ones starting with 'E' were picked up above.
+# The termios constants.
for n in IGNBRK BRKINT IGNPAR PARMRK INPCK ISTRIP INLCR IGNCR ICRNL IUCLC \
IXON IXANY IXOFF IMAXBEL IUTF8 OPOST OLCUC ONLCR OCRNL ONOCR ONLRET \
OFILL OFDEL NLDLY NL0 NL1 CRDLY CR0 CR1 CR2 CR3 TABDLY BSDLY VTDLY \
FFDLY CBAUD CBAUDEX CSIZE CSTOPB CREAD PARENB PARODD HUPCL CLOCAL \
- LOBLK CIBAUD CMSPAR CRTSCTS ISIG ICANON XCASE DEFECHK FLUSHO NOFLSH \
- TOSTOP PENDIN IEXTEN VINTR VQUIT VERASE VKILL VEOF VMIN VEOL VTIME VEOL2 \
- VSWTCH VSTART VSTOP VSUSP VDSUSP VLNEXT VWERASE VREPRINT VDISCARD VSTATUS \
- TCSANOW TCSADRAIN, TCSAFLUSH TCIFLUSH TCOFLUSH TCIOFLUSH TCOOFF TCOON \
- TCIOFF TCION B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 \
- B4800 B9600 B19200 B38400 B57600 B115200 B230400; do
+ LOBLK CIBAUD CMSPAR CRTSCTS ISIG ICANON XCASE ECHO ECHOE ECHOK ECHONL \
+ ECHOCTL ECHOPRT ECHOKE DEFECHO FLUSHO NOFLSH TOSTOP PENDIN IEXTEN VINTR \
+ VQUIT VERASE VKILL VEOF VMIN VEOL VTIME VEOL2 VSWTCH VSTART VSTOP VSUSP \
+ VDSUSP VLNEXT VWERASE VREPRINT VDISCARD VSTATUS TCSANOW TCSADRAIN \
+ TCSAFLUSH TCIFLUSH TCOFLUSH TCIOFLUSH TCOOFF TCOON TCIOFF TCION B0 B50 \
+ B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 \
+ B38400 B57600 B115200 B230400; do
grep "^const _$n " gen-sysinfo.go | \
sed -e 's/^\(const \)_\([^=]*\)\(.*\)$/\1\2 = _\2/' >> ${OUT}
diff --git a/libgo/runtime/go-nanotime.c b/libgo/runtime/go-nanotime.c
index 197fb15d2a5..7e5e3e098a9 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 00000000000..5df8085c6a8
--- /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/go-setenv.c b/libgo/runtime/go-setenv.c
index 9c93f52668e..78717f4705a 100644
--- a/libgo/runtime/go-setenv.c
+++ b/libgo/runtime/go-setenv.c
@@ -12,10 +12,10 @@
#include "go-alloc.h"
#include "go-string.h"
-/* Set the C environment from Go. This is called by os.Setenv. */
+/* Set the C environment from Go. This is called by syscall.Setenv. */
void setenv_c (struct __go_string, struct __go_string)
- __asm__ ("libgo_os.os.setenv_c");
+ __asm__ ("libgo_syscall.syscall.setenv_c");
void
setenv_c (struct __go_string k, struct __go_string v)
diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c
index cd165f45112..eb624097b36 100644
--- a/libgo/runtime/go-signal.c
+++ b/libgo/runtime/go-signal.c
@@ -122,12 +122,14 @@ sighandler (int sig)
const char *msg;
int i;
+#ifdef SIGPROF
if (sig == SIGPROF)
{
/* FIXME. */
runtime_sigprof (0, 0, nil, nil);
return;
}
+#endif
/* FIXME: Should check siginfo for more information when
available. */
@@ -257,6 +259,7 @@ runtime_initsig (int32 queue)
void
runtime_resetcpuprofiler(int32 hz)
{
+#ifdef SIGPROF
struct itimerval it;
struct sigaction sa;
int i;
@@ -289,6 +292,7 @@ runtime_resetcpuprofiler(int32 hz)
i = setitimer (ITIMER_PROF, &it, NULL);
__go_assert (i == 0);
}
+#endif
runtime_m()->profilehz = hz;
}
diff --git a/libgo/runtime/mem_posix_memalign.c b/libgo/runtime/mem_posix_memalign.c
index 7d04f999f9f..8acdf070570 100644
--- a/libgo/runtime/mem_posix_memalign.c
+++ b/libgo/runtime/mem_posix_memalign.c
@@ -36,10 +36,13 @@ runtime_SysFree(void *v, uintptr n)
void*
runtime_SysReserve(void *v, uintptr n)
{
+ USED(v);
return runtime_SysAlloc(n);
}
void
runtime_SysMap(void *v, uintptr n)
{
+ USED(v);
+ USED(n);
}
diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c
index 0f1cb49e40f..c4ab1454c5b 100644
--- a/libgo/runtime/mgc0.c
+++ b/libgo/runtime/mgc0.c
@@ -741,6 +741,7 @@ mark(void (*scan)(byte*, int64))
scan((byte*)&runtime_allg, sizeof runtime_allg);
scan((byte*)&runtime_allm, sizeof runtime_allm);
runtime_MProf_Mark(scan);
+ runtime_time_scan(scan);
// mark stacks
for(gp=runtime_allg; gp!=nil; gp=gp->alllink) {
diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c
index 88831d41a67..34566fbf9a0 100644
--- a/libgo/runtime/proc.c
+++ b/libgo/runtime/proc.c
@@ -42,7 +42,6 @@ extern void *__splitstack_find(void *, void *, size_t *, void **, void **,
#endif
static void schedule(G*);
-static M *startm(void);
typedef struct Sched Sched;
@@ -128,7 +127,7 @@ struct Sched {
volatile uint32 atomic; // atomic scheduling word (see below)
int32 profilehz; // cpu profiling rate
-
+
bool init; // running initialization
bool lockmain; // init called runtime.LockOSThread
@@ -826,7 +825,7 @@ runtime_starttheworld(bool extra)
// but m is not running a specific goroutine,
// so set the helpgc flag as a signal to m's
// first schedule(nil) to mcpu-- and grunning--.
- m = startm();
+ m = runtime_newm();
m->helpgc = 1;
runtime_sched.grunning++;
}
@@ -876,8 +875,6 @@ struct CgoThreadStart
};
// Kick off new m's as needed (up to mcpumax).
-// There are already `other' other cpus that will
-// start looking for goroutines shortly.
// Sched is locked.
static void
matchmg(void)
@@ -895,13 +892,14 @@ matchmg(void)
// Find the m that will run gp.
if((mp = mget(gp)) == nil)
- mp = startm();
+ mp = runtime_newm();
mnextg(mp, gp);
}
}
-static M*
-startm(void)
+// Create a new m. It will start off with a call to runtime_mstart.
+M*
+runtime_newm(void)
{
M *m;
pthread_attr_t attr;
@@ -1135,6 +1133,7 @@ runtime_exitsyscall(void)
runtime_memclr(gp->gcregs, sizeof gp->gcregs);
}
+// Allocate a new g, with a stack big enough for stacksize bytes.
G*
runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize)
{
@@ -1283,6 +1282,7 @@ runtime_Gosched(void)
runtime_gosched();
}
+// Implementation of runtime.GOMAXPROCS.
// delete when scheduler is stronger
int32
runtime_gomaxprocsfunc(int32 n)
@@ -1390,6 +1390,7 @@ static struct {
uintptr pcbuf[100];
} prof;
+// Called if we receive a SIGPROF signal.
void
runtime_sigprof(uint8 *pc __attribute__ ((unused)),
uint8 *sp __attribute__ ((unused)),
@@ -1412,6 +1413,7 @@ runtime_sigprof(uint8 *pc __attribute__ ((unused)),
runtime_unlock(&prof);
}
+// Arrange to call fn with a traceback hz times a second.
void
runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
{
diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c
index 8e4433b0d6c..ec96f5b615f 100644
--- a/libgo/runtime/runtime.c
+++ b/libgo/runtime/runtime.c
@@ -87,7 +87,7 @@ static int32 argc;
static byte** argv;
extern Slice os_Args asm ("libgo_os.os.Args");
-extern Slice os_Envs asm ("libgo_os.os.Envs");
+extern Slice syscall_Envs asm ("libgo_syscall.syscall.Envs");
void
runtime_args(int32 c, byte **v)
@@ -126,9 +126,9 @@ runtime_goenvs(void)
s = runtime_malloc(n*sizeof s[0]);
for(i=0; i<n; i++)
s[i] = runtime_gostringnocopy(argv[argc+1+i]);
- os_Envs.__values = (void*)s;
- os_Envs.__count = n;
- os_Envs.__capacity = n;
+ syscall_Envs.__values = (void*)s;
+ syscall_Envs.__count = n;
+ syscall_Envs.__capacity = n;
}
const byte*
@@ -141,8 +141,8 @@ runtime_getenv(const char *s)
bs = (const byte*)s;
len = runtime_findnull(bs);
- envv = (String*)os_Envs.__values;
- envc = os_Envs.__count;
+ envv = (String*)syscall_Envs.__values;
+ envc = syscall_Envs.__count;
for(i=0; i<envc; i++){
if(envv[i].__length <= len)
continue;
diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h
index 00443197095..1c7ede92722 100644
--- a/libgo/runtime/runtime.h
+++ b/libgo/runtime/runtime.h
@@ -56,6 +56,8 @@ typedef union Note Note;
typedef struct MCache MCache;
typedef struct FixAlloc FixAlloc;
typedef struct Hchan Hchan;
+typedef struct Timers Timers;
+typedef struct Timer Timer;
typedef struct __go_open_array Slice;
typedef struct __go_string String;
@@ -190,6 +192,38 @@ enum {
};
#endif
+struct Timers
+{
+ Lock;
+ G *timerproc;
+ bool sleeping;
+ bool rescheduling;
+ Note waitnote;
+ Timer **t;
+ int32 len;
+ int32 cap;
+};
+
+// Package time knows the layout of this structure.
+// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
+struct Timer
+{
+ int32 i; // heap index
+
+ // Timer wakes up at when, and then at when+period, ... (period > 0 only)
+ // each time calling f(now, arg) in the timer goroutine, so f must be
+ // a well-behaved function and not block.
+ int64 when;
+ int64 period;
+ void (*f)(int64, Eface);
+ Eface arg;
+};
+
+/*
+ * defined macros
+ * you need super-gopher-guru privilege
+ * to add this list.
+ */
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
#define nil ((void*)0)
#define USED(v) ((void) v)
@@ -229,6 +263,8 @@ G* runtime_malg(int32, byte**, size_t*);
void runtime_minit(void);
void runtime_mallocinit(void);
void runtime_gosched(void);
+void runtime_tsleep(int64);
+M* runtime_newm(void);
void runtime_goexit(void);
void runtime_entersyscall(void) __asm__("libgo_syscall.syscall.entersyscall");
void runtime_exitsyscall(void) __asm__("libgo_syscall.syscall.exitsyscall");
@@ -341,3 +377,5 @@ void reflect_call(const struct __go_func_type *, const void *, _Bool, _Bool,
#ifdef __rtems__
void __wrap_rtems_task_variable_add(void **);
#endif
+
+void runtime_time_scan(void (*)(byte*, int64));
diff --git a/libgo/runtime/thread-linux.c b/libgo/runtime/thread-linux.c
index c08cc0dba29..a0ee3600650 100644
--- a/libgo/runtime/thread-linux.c
+++ b/libgo/runtime/thread-linux.c
@@ -30,7 +30,7 @@ runtime_futexsleep(uint32 *addr, uint32 val, int64 ns)
else {
ts.tv_sec = ns/1000000000LL;
ts.tv_nsec = ns%1000000000LL;
- // Avoid overflowdefs
+ // Avoid overflow
if(ts.tv_sec > 1<<30)
ts.tv_sec = 1<<30;
tsp = &ts;
diff --git a/libgo/runtime/time.goc b/libgo/runtime/time.goc
index f5a412a710f..cae15e5946a 100644
--- a/libgo/runtime/time.goc
+++ b/libgo/runtime/time.goc
@@ -2,12 +2,262 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Runtime implementations to help package time.
+// Time-related runtime and pieces of package time.
package time
#include "runtime.h"
+#include "defs.h"
+#include "arch.h"
+#include "malloc.h"
-func Nanoseconds() (ret int64) {
- ret = runtime_nanotime();
+static Timers timers;
+static void addtimer(Timer*);
+static bool deltimer(Timer*);
+
+// Package time APIs.
+// Godoc uses the comments in package time, not these.
+
+// time.now is implemented in assembly.
+
+// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
+func Sleep(ns int64) {
+ G *g;
+
+ g = runtime_g();
+ g->status = Gwaiting;
+ g->waitreason = "sleep";
+ runtime_tsleep(ns);
+}
+
+// startTimer adds t to the timer heap.
+func startTimer(t *Timer) {
+ addtimer(t);
+}
+
+// stopTimer removes t from the timer heap if it is there.
+// It returns true if t was removed, false if t wasn't even there.
+func stopTimer(t *Timer) (stopped bool) {
+ stopped = deltimer(t);
+}
+
+// C runtime.
+
+static void timerproc(void*);
+static void siftup(int32);
+static void siftdown(int32);
+
+// Ready the goroutine e.data.
+static void
+ready(int64 now, Eface e)
+{
+ USED(now);
+
+ runtime_ready(e.__object);
+}
+
+// Put the current goroutine to sleep for ns nanoseconds.
+// The caller must have set g->status and g->waitreason.
+void
+runtime_tsleep(int64 ns)
+{
+ Timer t;
+
+ if(ns <= 0)
+ return;
+
+ t.when = runtime_nanotime() + ns;
+ t.period = 0;
+ t.f = ready;
+ t.arg.__object = runtime_g();
+ addtimer(&t);
+ runtime_gosched();
+}
+
+// Add a timer to the heap and start or kick the timer proc
+// if the new timer is earlier than any of the others.
+static void
+addtimer(Timer *t)
+{
+ int32 n;
+ Timer **nt;
+
+ runtime_lock(&timers);
+ if(timers.len >= timers.cap) {
+ // Grow slice.
+ n = 16;
+ if(n <= timers.cap)
+ n = timers.cap*3 / 2;
+ nt = runtime_malloc(n*sizeof nt[0]);
+ runtime_memmove(nt, timers.t, timers.len*sizeof nt[0]);
+ runtime_free(timers.t);
+ timers.t = nt;
+ timers.cap = n;
+ }
+ t->i = timers.len++;
+ timers.t[t->i] = t;
+ siftup(t->i);
+ if(t->i == 0) {
+ // siftup moved to top: new earliest deadline.
+ if(timers.sleeping) {
+ timers.sleeping = false;
+ runtime_notewakeup(&timers.waitnote);
+ }
+ if(timers.rescheduling) {
+ timers.rescheduling = false;
+ runtime_ready(timers.timerproc);
+ }
+ }
+ if(timers.timerproc == nil)
+ timers.timerproc = __go_go(timerproc, nil);
+ runtime_unlock(&timers);
+}
+
+// Delete timer t from the heap.
+// Do not need to update the timerproc:
+// if it wakes up early, no big deal.
+static bool
+deltimer(Timer *t)
+{
+ int32 i;
+
+ runtime_lock(&timers);
+
+ // t may not be registered anymore and may have
+ // a bogus i (typically 0, if generated by Go).
+ // Verify it before proceeding.
+ i = t->i;
+ if(i < 0 || i >= timers.len || timers.t[i] != t) {
+ runtime_unlock(&timers);
+ return false;
+ }
+
+ timers.len--;
+ if(i == timers.len) {
+ timers.t[i] = nil;
+ } else {
+ timers.t[i] = timers.t[timers.len];
+ timers.t[timers.len] = nil;
+ timers.t[i]->i = i;
+ siftup(i);
+ siftdown(i);
+ }
+ runtime_unlock(&timers);
+ return true;
+}
+
+// Timerproc runs the time-driven events.
+// It sleeps until the next event in the timers heap.
+// If addtimer inserts a new earlier event, addtimer
+// wakes timerproc early.
+static void
+timerproc(void* dummy __attribute__ ((unused)))
+{
+ G *g;
+ int64 delta, now;
+ Timer *t;
+ void (*f)(int64, Eface);
+ Eface arg;
+
+ g = runtime_g();
+ for(;;) {
+ runtime_lock(&timers);
+ now = runtime_nanotime();
+ for(;;) {
+ if(timers.len == 0) {
+ delta = -1;
+ break;
+ }
+ t = timers.t[0];
+ delta = t->when - now;
+ if(delta > 0)
+ break;
+ if(t->period > 0) {
+ // leave in heap but adjust next time to fire
+ t->when += t->period * (1 + -delta/t->period);
+ siftdown(0);
+ } else {
+ // remove from heap
+ timers.t[0] = timers.t[--timers.len];
+ timers.t[0]->i = 0;
+ siftdown(0);
+ t->i = -1; // mark as removed
+ }
+ f = t->f;
+ arg = t->arg;
+ runtime_unlock(&timers);
+ f(now, arg);
+ runtime_lock(&timers);
+ }
+ if(delta < 0) {
+ // No timers left - put goroutine to sleep.
+ timers.rescheduling = true;
+ g->status = Gwaiting;
+ g->waitreason = "timer goroutine (idle)";
+ runtime_unlock(&timers);
+ runtime_gosched();
+ continue;
+ }
+ // At least one timer pending. Sleep until then.
+ timers.sleeping = true;
+ runtime_noteclear(&timers.waitnote);
+ runtime_unlock(&timers);
+ runtime_entersyscall();
+ runtime_notetsleep(&timers.waitnote, delta);
+ runtime_exitsyscall();
+ }
+}
+
+// heap maintenance algorithms.
+
+static void
+siftup(int32 i)
+{
+ int32 p;
+ Timer **t, *tmp;
+
+ t = timers.t;
+ while(i > 0) {
+ p = (i-1)/2; // parent
+ if(t[i]->when >= t[p]->when)
+ break;
+ tmp = t[i];
+ t[i] = t[p];
+ t[p] = tmp;
+ t[i]->i = i;
+ t[p]->i = p;
+ i = p;
+ }
+}
+
+static void
+siftdown(int32 i)
+{
+ int32 c, len;
+ Timer **t, *tmp;
+
+ t = timers.t;
+ len = timers.len;
+ for(;;) {
+ c = i*2 + 1; // left child
+ if(c >= len) {
+ break;
+ }
+ if(c+1 < len && t[c+1]->when < t[c]->when)
+ c++;
+ if(t[c]->when >= t[i]->when)
+ break;
+ tmp = t[i];
+ t[i] = t[c];
+ t[c] = tmp;
+ t[i]->i = i;
+ t[c]->i = c;
+ i = c;
+ }
+}
+
+void
+runtime_time_scan(void (*scan)(byte*, int64))
+{
+ scan((byte*)&timers, sizeof timers);
}