summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--CONTRIBUTORS6
-rw-r--r--doc/devel/release.html72
-rw-r--r--doc/devel/weekly.html108
-rw-r--r--doc/go_tutorial.html1049
-rw-r--r--doc/go_tutorial.txt108
-rw-r--r--doc/htmlgen.go167
-rw-r--r--doc/install.html6
-rwxr-xr-xdoc/makehtml16
-rwxr-xr-xdoc/prog.sh72
-rw-r--r--doc/progs/file_windows.go89
-rwxr-xr-xdoc/progs/run7
-rw-r--r--lib/codereview/codereview.py14
-rw-r--r--misc/dashboard/builder/http.go7
-rw-r--r--misc/dashboard/builder/main.go21
-rw-r--r--misc/dashboard/builder/package.go48
-rw-r--r--misc/dashboard/godashboard/auth.py13
-rw-r--r--misc/dashboard/godashboard/gobuild.py7
-rw-r--r--misc/dashboard/godashboard/index.yaml1
-rw-r--r--misc/dashboard/godashboard/package.html30
-rw-r--r--misc/dashboard/godashboard/package.py38
-rw-r--r--misc/dashboard/godashboard/static/style.css13
-rw-r--r--misc/emacs/go-mode.el4
-rw-r--r--misc/vim/ftdetect/gofiletype.vim2
-rw-r--r--src/cmd/5c/gc.h2
-rw-r--r--src/cmd/5l/asm.c31
-rw-r--r--src/cmd/5l/mkenam30
-rw-r--r--src/cmd/6c/gc.h1
-rw-r--r--src/cmd/6l/asm.c29
-rw-r--r--src/cmd/6l/mkenam30
-rw-r--r--src/cmd/8a/a.h4
-rw-r--r--src/cmd/8a/a.y2
-rw-r--r--src/cmd/8a/lex.c3
-rw-r--r--src/cmd/8c/gc.h2
-rw-r--r--src/cmd/8c/swt.c4
-rw-r--r--src/cmd/8l/asm.c29
-rw-r--r--src/cmd/8l/mkenam30
-rw-r--r--src/cmd/cc/acid.c1
-rw-r--r--src/cmd/cc/bits.c1
-rw-r--r--src/cmd/cc/cc.h4
-rw-r--r--src/cmd/cc/cc.y1
-rw-r--r--src/cmd/cc/com.c3
-rw-r--r--src/cmd/cc/com64.c1
-rw-r--r--src/cmd/cc/dcl.c1
-rw-r--r--src/cmd/cc/dpchk.c129
-rw-r--r--src/cmd/cc/funct.c1
-rw-r--r--src/cmd/cc/godefs.c3
-rw-r--r--src/cmd/cc/lex.c8
-rw-r--r--src/cmd/cc/lexbody6
-rw-r--r--src/cmd/cc/mac.c2
-rw-r--r--src/cmd/cc/macbody6
-rw-r--r--src/cmd/cc/omachcap.c2
-rw-r--r--src/cmd/cc/pgen.c2
-rw-r--r--src/cmd/cc/scon.c1
-rw-r--r--src/cmd/cc/sub.c1
-rw-r--r--src/cmd/cgo/gcc.go10
-rw-r--r--src/cmd/cgo/out.go2
-rw-r--r--src/cmd/ebnflint/ebnflint.go29
-rw-r--r--src/cmd/gc/go.h2
-rw-r--r--src/cmd/gc/go.y12
-rw-r--r--src/cmd/gc/subr.c11
-rw-r--r--src/cmd/godoc/codewalk.go10
-rw-r--r--src/cmd/godoc/dirtrees.go9
-rw-r--r--src/cmd/godoc/godoc.go15
-rw-r--r--src/cmd/godoc/index.go4
-rw-r--r--src/cmd/godoc/main.go6
-rw-r--r--src/cmd/godoc/mapping.go14
-rw-r--r--src/cmd/godoc/utils.go2
-rw-r--r--src/cmd/gofix/Makefile14
-rw-r--r--src/cmd/gofix/filepath.go53
-rw-r--r--src/cmd/gofix/filepath_test.go33
-rw-r--r--src/cmd/gofix/fix.go159
-rw-r--r--src/cmd/gofix/httpfs.go63
-rw-r--r--src/cmd/gofix/httpfs_test.go47
-rw-r--r--src/cmd/gofix/main.go2
-rw-r--r--src/cmd/gofix/signal.go49
-rw-r--r--src/cmd/gofix/signal_test.go96
-rw-r--r--src/cmd/gofix/sorthelpers.go47
-rw-r--r--src/cmd/gofix/sorthelpers_test.go45
-rw-r--r--src/cmd/gofix/sortslice.go50
-rw-r--r--src/cmd/gofix/sortslice_test.go35
-rw-r--r--src/cmd/gofix/stringssplit.go71
-rw-r--r--src/cmd/gofix/stringssplit_test.go51
-rw-r--r--src/cmd/gofix/testdata/reflect.template.go.in4
-rw-r--r--src/cmd/gofix/testdata/reflect.template.go.out4
-rw-r--r--src/cmd/gofmt/gofmt_test.go4
-rw-r--r--src/cmd/gofmt/rewrite.go2
-rw-r--r--src/cmd/goinstall/doc.go66
-rw-r--r--src/cmd/goinstall/download.go170
-rw-r--r--src/cmd/goinstall/main.go6
-rw-r--r--src/cmd/gotest/doc.go11
-rw-r--r--src/cmd/gotest/flag.go4
-rw-r--r--src/cmd/govet/Makefile3
-rw-r--r--src/cmd/govet/govet.go62
-rw-r--r--src/cmd/goyacc/goyacc.go2
-rw-r--r--src/cmd/hgpatch/main.go4
-rw-r--r--src/cmd/ld/data.c39
-rw-r--r--src/cmd/ld/lib.c2
-rw-r--r--src/cmd/ld/lib.h2
-rw-r--r--src/cmd/ld/symtab.c4
-rw-r--r--src/env.bash3
-rw-r--r--src/lib9/Makefile3
-rw-r--r--src/libmach/darwin.c13
-rw-r--r--src/pkg/Makefile3
-rw-r--r--src/pkg/asn1/asn1.go47
-rw-r--r--src/pkg/asn1/asn1_test.go6
-rw-r--r--src/pkg/asn1/common.go3
-rw-r--r--src/pkg/asn1/marshal.go2
-rw-r--r--src/pkg/asn1/marshal_test.go10
-rwxr-xr-xsrc/pkg/big/int.go58
-rwxr-xr-xsrc/pkg/big/int_test.go31
-rw-r--r--src/pkg/bufio/bufio.go22
-rw-r--r--src/pkg/bufio/bufio_test.go10
-rw-r--r--src/pkg/bytes/bytes.go26
-rw-r--r--src/pkg/bytes/bytes_test.go19
-rw-r--r--src/pkg/compress/lzw/reader_test.go2
-rw-r--r--src/pkg/crypto/aes/cipher.go4
-rw-r--r--src/pkg/crypto/blowfish/cipher.go4
-rw-r--r--src/pkg/crypto/ocsp/ocsp.go20
-rw-r--r--src/pkg/crypto/openpgp/keys.go67
-rw-r--r--src/pkg/crypto/openpgp/packet/public_key.go10
-rw-r--r--src/pkg/crypto/openpgp/packet/signature.go62
-rw-r--r--src/pkg/crypto/openpgp/packet/signature_test.go22
-rw-r--r--src/pkg/crypto/openpgp/read.go5
-rw-r--r--src/pkg/crypto/openpgp/read_test.go23
-rwxr-xr-xsrc/pkg/crypto/rand/rand_windows.go2
-rw-r--r--src/pkg/crypto/tls/generate_cert.go8
-rw-r--r--src/pkg/crypto/twofish/twofish.go2
-rw-r--r--src/pkg/crypto/x509/pkix/pkix.go12
-rw-r--r--src/pkg/crypto/x509/verify.go10
-rw-r--r--src/pkg/crypto/x509/verify_test.go11
-rw-r--r--src/pkg/crypto/x509/x509.go32
-rw-r--r--src/pkg/crypto/xtea/cipher.go4
-rw-r--r--src/pkg/csv/Makefile12
-rw-r--r--src/pkg/csv/reader.go373
-rw-r--r--src/pkg/csv/reader_test.go265
-rw-r--r--src/pkg/csv/writer.go123
-rw-r--r--src/pkg/csv/writer_test.go44
-rw-r--r--src/pkg/debug/proc/proc_linux.go2
-rw-r--r--src/pkg/exec/exec_test.go2
-rw-r--r--src/pkg/exec/lp_plan9.go2
-rw-r--r--src/pkg/exec/lp_unix.go2
-rw-r--r--src/pkg/exec/lp_windows.go4
-rw-r--r--src/pkg/exp/ogle/cmd.go2
-rw-r--r--src/pkg/exp/regexp/syntax/Makefile3
-rw-r--r--src/pkg/exp/regexp/syntax/compile.go264
-rw-r--r--src/pkg/exp/regexp/syntax/parse.go754
-rw-r--r--src/pkg/exp/regexp/syntax/parse_test.go310
-rw-r--r--src/pkg/exp/regexp/syntax/prog.go182
-rw-r--r--src/pkg/exp/regexp/syntax/prog_test.go91
-rw-r--r--src/pkg/exp/regexp/syntax/regexp.go78
-rw-r--r--src/pkg/exp/regexp/syntax/simplify.go151
-rw-r--r--src/pkg/exp/regexp/syntax/simplify_test.go151
-rw-r--r--src/pkg/exp/template/Makefile5
-rw-r--r--src/pkg/exp/template/exec.go508
-rw-r--r--src/pkg/exp/template/exec_test.go342
-rw-r--r--src/pkg/exp/template/funcs.go294
-rw-r--r--src/pkg/exp/template/lex.go180
-rw-r--r--src/pkg/exp/template/lex_test.go44
-rw-r--r--src/pkg/exp/template/parse.go467
-rw-r--r--src/pkg/exp/template/parse_test.go127
-rw-r--r--src/pkg/exp/template/set.go115
-rw-r--r--src/pkg/exp/template/set_test.go101
-rw-r--r--src/pkg/fmt/print.go33
-rw-r--r--src/pkg/fmt/scan.go12
-rw-r--r--src/pkg/go/ast/print_test.go2
-rw-r--r--src/pkg/go/build/dir.go2
-rw-r--r--src/pkg/go/build/path.go3
-rw-r--r--src/pkg/go/doc/comment.go2
-rw-r--r--src/pkg/go/doc/doc.go2
-rw-r--r--src/pkg/go/types/testdata/exports.go2
-rw-r--r--src/pkg/gob/decode.go4
-rw-r--r--src/pkg/gob/doc.go34
-rw-r--r--src/pkg/gob/encode.go15
-rw-r--r--src/pkg/gob/type.go5
-rw-r--r--src/pkg/html/parse.go6
-rw-r--r--src/pkg/html/token_test.go2
-rw-r--r--src/pkg/http/cgi/host.go26
-rw-r--r--src/pkg/http/cgi/host_test.go78
-rwxr-xr-xsrc/pkg/http/cgi/testdata/test.cgi51
-rw-r--r--src/pkg/http/chunked.go11
-rw-r--r--src/pkg/http/cookie.go4
-rw-r--r--src/pkg/http/fs.go68
-rw-r--r--src/pkg/http/fs_test.go66
-rw-r--r--src/pkg/http/header.go2
-rw-r--r--src/pkg/http/persist.go6
-rw-r--r--src/pkg/http/readrequest_test.go71
-rw-r--r--src/pkg/http/request.go19
-rw-r--r--src/pkg/http/requestwrite_test.go119
-rw-r--r--src/pkg/http/response.go2
-rw-r--r--src/pkg/http/reverseproxy_test.go3
-rw-r--r--src/pkg/http/serve_test.go76
-rw-r--r--src/pkg/http/server.go23
-rw-r--r--src/pkg/http/spdy/read.go2
-rw-r--r--src/pkg/http/transfer.go56
-rw-r--r--src/pkg/http/transport.go4
-rw-r--r--src/pkg/http/url.go4
-rw-r--r--src/pkg/image/draw/draw_test.go172
-rw-r--r--src/pkg/image/image.go17
-rw-r--r--src/pkg/image/image_test.go5
-rw-r--r--src/pkg/index/suffixarray/suffixarray.go4
-rw-r--r--src/pkg/index/suffixarray/suffixarray_test.go2
-rw-r--r--src/pkg/io/io.go8
-rw-r--r--src/pkg/json/decode.go2
-rw-r--r--src/pkg/json/decode_test.go9
-rw-r--r--src/pkg/json/encode.go16
-rw-r--r--src/pkg/json/scanner_test.go5
-rw-r--r--src/pkg/mail/message.go2
-rw-r--r--src/pkg/mime/mediatype.go13
-rw-r--r--src/pkg/mime/mediatype_test.go3
-rw-r--r--src/pkg/mime/multipart/multipart.go32
-rw-r--r--src/pkg/mime/multipart/multipart_test.go32
-rw-r--r--src/pkg/net/dial.go86
-rw-r--r--src/pkg/net/dnsmsg.go46
-rw-r--r--src/pkg/net/fd_windows.go48
-rw-r--r--src/pkg/net/hosts_test.go2
-rw-r--r--src/pkg/net/interface_windows.go4
-rw-r--r--src/pkg/net/ipsock.go12
-rw-r--r--src/pkg/net/sendfile_windows.go6
-rw-r--r--src/pkg/net/sock.go23
-rw-r--r--src/pkg/net/sock_windows.go2
-rw-r--r--src/pkg/os/Makefile2
-rw-r--r--src/pkg/os/env_windows.go2
-rw-r--r--src/pkg/os/exec_posix.go11
-rw-r--r--src/pkg/os/exec_windows.go8
-rw-r--r--src/pkg/os/file.go28
-rw-r--r--src/pkg/os/file_plan9.go26
-rw-r--r--src/pkg/os/file_posix.go22
-rw-r--r--src/pkg/os/file_unix.go45
-rw-r--r--src/pkg/os/file_windows.go63
-rwxr-xr-xsrc/pkg/os/mkunixsignals.sh2
-rw-r--r--src/pkg/os/types.go2
-rw-r--r--src/pkg/patch/patch.go2
-rw-r--r--src/pkg/path/filepath/match.go2
-rw-r--r--src/pkg/path/filepath/path.go2
-rw-r--r--src/pkg/path/filepath/path_test.go4
-rw-r--r--src/pkg/reflect/all_test.go76
-rw-r--r--src/pkg/reflect/type.go130
-rw-r--r--src/pkg/reflect/value.go21
-rw-r--r--src/pkg/regexp/regexp.go1
-rw-r--r--src/pkg/rpc/jsonrpc/all_test.go6
-rw-r--r--src/pkg/rpc/jsonrpc/client.go12
-rw-r--r--src/pkg/rpc/jsonrpc/server.go12
-rw-r--r--src/pkg/rpc/server.go2
-rw-r--r--src/pkg/runtime/386/atomic.c12
-rw-r--r--src/pkg/runtime/Makefile1
-rw-r--r--src/pkg/runtime/amd64/atomic.c12
-rw-r--r--src/pkg/runtime/arm/atomic.c12
-rw-r--r--src/pkg/runtime/cgo/darwin_386.c7
-rw-r--r--src/pkg/runtime/cgo/darwin_amd64.c7
-rw-r--r--src/pkg/runtime/cgo/freebsd_386.c7
-rw-r--r--src/pkg/runtime/cgo/freebsd_amd64.c7
-rw-r--r--src/pkg/runtime/cgo/linux_386.c7
-rw-r--r--src/pkg/runtime/cgo/linux_amd64.c7
-rwxr-xr-xsrc/pkg/runtime/cgo/windows_amd64.c18
-rw-r--r--src/pkg/runtime/debug/stack.go2
-rw-r--r--src/pkg/runtime/debug/stack_test.go2
-rwxr-xr-xsrc/pkg/runtime/mkasmh.sh25
-rw-r--r--src/pkg/runtime/proc.c2
-rw-r--r--src/pkg/runtime/runtime.h8
-rw-r--r--src/pkg/runtime/sema.goc187
-rw-r--r--src/pkg/runtime/sema_test.go100
-rw-r--r--src/pkg/runtime/windows/amd64/defs.h40
-rw-r--r--src/pkg/runtime/windows/amd64/rt0.s10
-rw-r--r--src/pkg/runtime/windows/amd64/signal.c20
-rw-r--r--src/pkg/runtime/windows/amd64/sys.s129
-rw-r--r--src/pkg/runtime/windows/mem.c10
-rw-r--r--src/pkg/runtime/windows/os.h3
-rw-r--r--src/pkg/runtime/windows/thread.c18
-rw-r--r--src/pkg/smtp/smtp.go6
-rw-r--r--src/pkg/smtp/smtp_test.go4
-rw-r--r--src/pkg/sort/sort.go12
-rw-r--r--src/pkg/sort/sort_test.go24
-rw-r--r--src/pkg/strconv/fp_test.go6
-rw-r--r--src/pkg/strings/strings.go26
-rw-r--r--src/pkg/strings/strings_test.go22
-rw-r--r--src/pkg/sync/mutex.go63
-rw-r--r--src/pkg/sync/mutex_test.go99
-rw-r--r--src/pkg/sync/once.go14
-rw-r--r--src/pkg/sync/once_test.go25
-rw-r--r--src/pkg/syscall/Makefile2
-rw-r--r--src/pkg/syscall/asm_windows_386.s2
-rw-r--r--src/pkg/syscall/asm_windows_amd64.s7
-rw-r--r--src/pkg/syscall/exec_unix.go31
-rw-r--r--src/pkg/syscall/exec_windows.go16
-rwxr-xr-xsrc/pkg/syscall/mkall.sh11
-rwxr-xr-xsrc/pkg/syscall/mkerrors.sh1
-rw-r--r--src/pkg/syscall/syscall_windows.go224
-rw-r--r--src/pkg/syscall/syscall_windows_386.go2
-rw-r--r--src/pkg/syscall/syscall_windows_amd64.go5
-rw-r--r--src/pkg/syscall/zerrors_darwin_386.go79
-rw-r--r--src/pkg/syscall/zerrors_darwin_amd64.go79
-rw-r--r--src/pkg/syscall/zerrors_freebsd_386.go72
-rw-r--r--src/pkg/syscall/zerrors_freebsd_amd64.go72
-rw-r--r--src/pkg/syscall/zerrors_linux_386.go63
-rw-r--r--src/pkg/syscall/zerrors_linux_amd64.go63
-rw-r--r--src/pkg/syscall/zerrors_linux_arm.go59
-rw-r--r--src/pkg/syscall/zerrors_windows.go283
-rw-r--r--src/pkg/syscall/zerrors_windows_386.go284
-rw-r--r--src/pkg/syscall/zerrors_windows_amd64.go5
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go132
-rw-r--r--src/pkg/syscall/zsyscall_windows_amd64.go1323
-rw-r--r--src/pkg/syscall/zsysnum_windows_amd64.go3
-rw-r--r--src/pkg/syscall/ztypes_windows.go656
-rw-r--r--src/pkg/syscall/ztypes_windows_386.go659
-rw-r--r--src/pkg/syscall/ztypes_windows_amd64.go5
-rw-r--r--src/pkg/template/execute.go2
-rw-r--r--src/pkg/template/parse.go2
-rw-r--r--src/pkg/testing/benchmark.go54
-rw-r--r--src/pkg/testing/iotest/reader.go21
-rw-r--r--src/pkg/testing/testing.go72
-rw-r--r--src/pkg/time/format.go2
-rw-r--r--src/pkg/time/sleep_test.go2
-rw-r--r--src/pkg/time/time_test.go17
-rw-r--r--src/pkg/unicode/maketables.go52
-rw-r--r--src/pkg/xml/Makefile1
-rw-r--r--src/pkg/xml/atom_test.go50
-rw-r--r--src/pkg/xml/embed_test.go10
-rw-r--r--src/pkg/xml/marshal.go228
-rw-r--r--src/pkg/xml/marshal_test.go299
-rw-r--r--src/pkg/xml/read.go41
-rw-r--r--src/pkg/xml/read_test.go42
-rwxr-xr-xsrc/run.bash2
-rwxr-xr-xsrc/version.bash9
-rw-r--r--test/fixedbugs/bug345.dir/io.go15
-rw-r--r--test/fixedbugs/bug345.dir/main.go28
-rw-r--r--test/fixedbugs/bug345.go7
327 files changed, 13435 insertions, 3614 deletions
diff --git a/AUTHORS b/AUTHORS
index 5471a8f80..f9af1a777 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,6 +16,7 @@ Alexander Orlov <alexander.orlov@loxal.net>
Alexey Borzenkov <snaury@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andrei Vieru <euvieru@gmail.com>
+Andrew Balholm <andybalholm@gmail.com>
Andrew Skiba <skibaa@gmail.com>
Andrey Mirtchovski <mirtchovski@gmail.com>
Andy Davis <andy@bigandian.com>
@@ -60,6 +61,7 @@ Fazlul Shahriar <fshahriar@gmail.com>
Firmansyah Adiputra <frm.adiputra@gmail.com>
Florian Uekermann <florian@uekermann-online.de>
Gary Burd <gary@beagledreams.com>
+Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com>
Giles Lean <giles.lean@pobox.com>
Google Inc.
Graham Miller <graham.miller@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index e5037753a..8bb57f9ab 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -40,6 +40,7 @@ Alexander Orlov <alexander.orlov@loxal.net>
Alexey Borzenkov <snaury@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andrei Vieru <euvieru@gmail.com>
+Andrew Balholm <andybalholm@gmail.com>
Andrew Gerrand <adg@golang.org>
Andrew Skiba <skibaa@gmail.com>
Andrey Mirtchovski <mirtchovski@gmail.com>
@@ -100,6 +101,7 @@ Firmansyah Adiputra <frm.adiputra@gmail.com>
Florian Uekermann <florian@uekermann-online.de> <f1@uekermann-online.de>
Fumitoshi Ukai <ukai@google.com>
Gary Burd <gary@beagledreams.com> <gary.burd@gmail.com>
+Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com>
Giles Lean <giles.lean@pobox.com>
Graham Miller <graham.miller@gmail.com>
Gustavo Niemeyer <gustavo@niemeyer.net> <n13m3y3r@gmail.com>
@@ -141,12 +143,13 @@ Ken Thompson <ken@golang.org>
Kevin Ballard <kevin@sb.org>
Kirklin McDonald <kirklin.mcdonald@gmail.com>
Kyle Consalus <consalus@gmail.com>
-Kyle Lemons <kyle@kylelemons.net> <etherealflaim@gmail.com>
+Kyle Lemons <kyle@kylelemons.net> <kevlar@google.com>
Larry Hosken <lahosken@golang.org>
Lorenzo Stoakes <lstoakes@gmail.com>
Lucio De Re <lucio.dere@gmail.com>
Luit van Drongelen <luitvd@gmail.com>
Luuk van Dijk <lvd@golang.org> <lvd@google.com>
+Marcel van Lohuizen <mpvl@golang.org>
Mark Zavislak <zavislak@google.com>
Markus Duft <markus.duft@salomon.at>
Martin Neubauer <m.ne@gmx.net>
@@ -171,6 +174,7 @@ Padraig Kitterick <padraigkitterick@gmail.com>
Paolo Giarrusso <p.giarrusso@gmail.com>
Pascal S. de Kloe <pascal@quies.net>
Patrick Gavlin <pgavlin@gmail.com>
+Paul Borman <borman@google.com>
Petar Maymounkov <petarm@gmail.com>
Peter Froehlich <peter.hans.froehlich@gmail.com>
Peter McKenzie <petermck@google.com>
diff --git a/doc/devel/release.html b/doc/devel/release.html
index 84ca622fa..d632200d3 100644
--- a/doc/devel/release.html
+++ b/doc/devel/release.html
@@ -14,6 +14,78 @@ hg pull
hg update release.r<i>NN</i>
</pre>
+<h2 id="r58">r58 (released 2011/06/29)</h2>
+
+<p>
+The r58 release corresponds to
+<code><a href="weekly.html#2011-06-09">weekly.2011-06-09</a></code>
+with additional bug fixes.
+This section highlights the most significant changes in this release.
+For a more detailed summary, see the
+<a href="weekly.html#2011-06-09">weekly release notes</a>.
+For complete information, see the
+<a href="http://code.google.com/p/go/source/list?r=release-branch.r58">Mercurial change list</a>.
+</p>
+
+<h3 id="r58.lang">Language</h3>
+
+<p>
+This release fixes a <a href="http://code.google.com/p/go/source/detail?r=b720749486e1">use of uninitialized memory in programs that misuse <code>goto</code></a>.
+</p>
+
+<h3 id="r58.pkg">Packages</h3>
+
+<p>
+As usual, <a href="/cmd/gofix/">gofix</a> will handle the bulk of the rewrites
+necessary for these changes to package APIs.
+</p>
+
+<p>
+<a href="/pkg/http/">Package http</a> drops the <code>finalURL</code> return
+value from the <a href="/pkg/http/#Client.Get">Client.Get</a> method. The value
+is now available via the new <code>Request</code> field on <a
+href="/pkg/http/#Response">http.Response</a>.
+Most instances of the type map[string][]string in have been
+replaced with the new <a href="/pkg/http/#Values">Values</a> type.
+</p>
+
+<p>
+<a href="/pkg/exec/">Package exec</a> has been redesigned with a more
+convenient and succinct API.
+</p>
+
+<p>
+<a href="/pkg/strconv/">Package strconv</a>'s <a href="/pkg/strconv/#Quote">Quote</a>
+function now escapes only those Unicode code points not classified as printable
+by <a href="/pkg/unicode/#IsPrint">unicode.IsPrint</a>.
+Previously Quote would escape all non-ASCII characters.
+This also affects the <a href="/pkg/fmt/">fmt</a> package's <code>"%q"</code>
+formatting directive. The previous quoting behavior is still available via
+strconv's new <a href="/pkg/strconv/#QuoteToASCII">QuoteToASCII</a> function.
+</p>
+
+<p>
+<a href="/pkg/os/signal/">Package os/signal</a>'s
+<a href="/pkg/os/#Signal">Signal</a> and
+<a href="/pkg/os/#UnixSignal">UnixSignal</a> types have been moved to the
+<a href="/pkg/os/">os</a> package.
+</p>
+
+<p>
+<a href="/pkg/image/draw/">Package image/draw</a> is the new name for
+<code>exp/draw</code>. The GUI-related code from <code>exp/draw</code> is now
+located in the <a href="/pkg/exp/gui/">exp/gui</a> package.
+</p>
+
+<h3 id="r58.cmd">Tools</h3>
+
+<p>
+<a href="/cmd/goinstall/">Goinstall</a> now observes the GOPATH environment
+variable to build and install your own code and external libraries outside of
+the Go tree (and avoid writing Makefiles).
+</p>
+
+
<h2 id="r57">r57 (released 2011/05/03)</h2>
<p>
diff --git a/doc/devel/weekly.html b/doc/devel/weekly.html
index 0a043d410..bf16c8370 100644
--- a/doc/devel/weekly.html
+++ b/doc/devel/weekly.html
@@ -14,6 +14,112 @@ hg pull
hg update weekly.<i>YYYY-MM-DD</i>
</pre>
+<h2 id="2011-07-07">2011-07-07</h2>
+
+<pre>
+This weekly snapshot includes changes to the strings, http, reflect, json, and
+xml packages. Code that uses these packages will need changes. Most of these
+changes can be made automatically with gofix.
+
+The strings package's Split function has itself been split into Split and
+SplitN. SplitN is the same as the old Split. The new Split is equivalent to
+SplitN with a final argument of -1.
+
+The http package has a new FileSystem interface that provides access to files.
+The FileServer helper now takes a FileSystem argument instead of an explicit
+file system root. By implementing your own FileSystem you can use the
+FileServer to serve arbitrary data.
+
+The reflect package supports a new struct tag scheme that enables sharing of
+struct tags between multiple packages.
+In this scheme, the tags must be of the form:
+ key:"value" key2:"value2"
+reflect.StructField's Tag field now has type StructTag (a string type), which
+has method Get(key string) string that returns the associated value.
+Clients of json and xml will need to be updated. Code that says
+ type T struct {
+ X int "name"
+ }
+should become
+ type T struct {
+ X int `json:"name"` // or `xml:"name"`
+ }
+Use govet to identify struct tags that need to be changed to use the new syntax.
+
+Other changes:
+* 5l, 6l, 8l: drop use of ed during build.
+* asn1: support T61 and UTF8 string.
+* bufio: do not cache Read errors (thanks Graham Miller).
+* build: make version.bash aware of branches.
+* cgi: don't depend on CGI.pm for tests.
+* codereview: make --ignore_hgpatch_failure work again,
+ restrict sync to default branch.
+* crypto/openpgp: add ability to reserialize keys,
+ bug fix (thanks Gideon Jan-Wessel Redelinghuys).
+* crypto/tls: fix generate_cert.go.
+* crypto/x509: prevent chain cycles in Verify.
+* csv: new package.
+* doc: remove ed from apt-get package list.
+* docs: fold the prog.sh scripting from makehtml into htmlgen itself.
+* ebnflint: better handling of stdin.
+* exp/regexp/syntax: new experimental RE2-based regexp implementation.
+* exp/template: a new experimental templating package.
+* fmt: add SkipSpace to fmt's ScanState interface.
+* fmt: rename errno and error to err for doc consistency.
+* gc: avoid package name ambiguity in error messages,
+ fix package quoting logic,
+ fixes for Plan 9 (thanks Lucio De Re).
+* go/build: evaluate symlinks before comparing path to GOPATH.
+* gob: use exported fields in structs in the package documentation.
+* godoc: ignore directories that begin with '.',
+ search GOPATH for documentation.
+* gofix: os/signal, path/filepath, and sort fixes (thanks Robert Hencke),
+* goinstall: add support for generic hosts (thanks Julian Phillips),
+ only report successfully-installed packages to the dashboard,
+ try to access via https (thanks Yasuhiro Matsumoto).
+* gotest: add -test.benchtime and -test.cpu flags.
+* html: fixes and improvements (thanks Yasuhiro Matsumoto).
+* http/cgi: add Handler.Dir to specify working directory (thanks Yasuhiro Matsumoto).
+* http: add StripPrefix handler wrapper,
+ assume ContentLength 0 on GET requests,
+ better handling of 0-length Request.Body,
+ do TLS handshake explicitly before copying TLS state,
+ document that ServerConn and ClientConn are low-level,
+ make NewChunkedReader public (thanks Andrew Balholm),
+ respect Handlers setting Connection: close in their response.
+* image: more tests, Paletted.Opaque optimization.
+* io.WriteString: if the object has a WriteString method, use it (thanks Evan Shaw).
+* ld: elide the Go symbol table when using -s (thanks Anthony Martin).
+* ld: fix ELF strip by removing overlap of sections (thanks Gustavo Niemeyer).
+* mime/multipart: parse LF-delimited messages, not just CRLF.
+* mime: permit lower-case media type parameters (thanks Pascal S. de Kloe).
+* misc/dashboard: new features and improvements (not yet deployed).
+* misc/emacs: update list of builtins (thanks Quan Yong Zhai).
+* misc/vim: allow only utf-8 for file encoding (thanks Yasuhiro Matsumoto).
+* os: fix documentation for FileInfo.Name,
+ simplify WriteString,
+ use a different symbol from syscall in mkunixsignals.sh.
+* path/filepath: enable TestWalk to run on windows (thanks Alex Brainman).
+* reflect: add MethodByName,
+ allow Len on String values.
+* regexp: document that Regexp is thread-safe.
+* runtime/cgo: check for errors from pthread_create (thanks Albert Strasheim).
+* runtime: add Semacquire/Semrelease benchmarks,
+ improved Semacquire/Semrelease implementation,
+ windows/amd64 port (thanks Wei Guangjing).
+* sync: add fast path to Once,
+ improve Mutex to allow successive acquisitions,
+ new and improved benchmarks.
+* syscall: regenerate zerrors for darwin/linux/freebsd,
+ support for tty options in StartProcess (thanks Ken Rockot).
+* testing: make ResetTimer not start/stop the timer,
+ scale benchmark precision to 0.01ns if needed.
+* time: zero-pad two-digit years.
+* unicode/maketables: update debugging data.
+* windows: define and use syscall.Handle (thanks Wei Guangjing).
+* xml: add Marshal and MarshalIndent.
+</pre>
+
<h2 id="2011-06-23">2011-06-23</h2>
<pre>
@@ -128,7 +234,7 @@ Other changes:
* xml: handle non-string attribute fields (thanks Maxim Ushakov).
</pre>
-<h2 id="2011-06-09">2011-06-09</h2>
+<h2 id="2011-06-09">2011-06-09 (<a href="release.html#r58">base for r58</a>)</h2>
<pre>
This release includes changes to the strconv, http, and exp/draw packages.
diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html
index 4f3f6b94b..822f9626e 100644
--- a/doc/go_tutorial.html
+++ b/doc/go_tutorial.html
@@ -26,14 +26,14 @@ cleanliness, blank lines remain blank.
<p>
Let's start in the usual way:
<p>
-<pre> <!-- progs/helloworld.go /package/ END -->
-05 package main
+<pre><!-- progs/helloworld.go /package/ $
+-->package main
-07 import fmt &quot;fmt&quot; // Package implementing formatted I/O.
+import fmt &#34;fmt&#34; // Package implementing formatted I/O.
-09 func main() {
-10 fmt.Printf(&quot;Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\n&quot;)
-11 }
+func main() {
+ fmt.Printf(&#34;Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\n&#34;)
+}
</pre>
<p>
Every Go source file declares, using a <code>package</code> statement, which package it's part of.
@@ -51,8 +51,8 @@ String constants can contain Unicode characters, encoded in UTF-8.
The comment convention is the same as in C++:
<p>
<pre>
- /* ... */
- // ...
+/* ... */
+// ...
</pre>
<p>
Later we'll have much more to say about printing.
@@ -96,67 +96,67 @@ a more robust run-time system although <code>gccgo</code> is catching up.
Here's how to compile and run our program. With <code>6g</code>, say,
<p>
<pre>
- $ 6g helloworld.go # compile; object goes into helloworld.6
- $ 6l helloworld.6 # link; output goes into 6.out
- $ 6.out
- Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
- $
+$ 6g helloworld.go # compile; object goes into helloworld.6
+$ 6l helloworld.6 # link; output goes into 6.out
+$ 6.out
+Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
+$
</pre>
<p>
With <code>gccgo</code> it looks a little more traditional.
<p>
<pre>
- $ gccgo helloworld.go
- $ a.out
- Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
- $
+$ gccgo helloworld.go
+$ a.out
+Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
+$
</pre>
<p>
<h2>Echo</h2>
<p>
Next up, here's a version of the Unix utility <code>echo(1)</code>:
<p>
-<pre> <!-- progs/echo.go /package/ END -->
-05 package main
+<pre><!-- progs/echo.go /package/ $
+-->package main
-07 import (
-08 &quot;os&quot;
-09 &quot;flag&quot; // command line option parser
-10 )
+import (
+ &#34;os&#34;
+ &#34;flag&#34; // command line option parser
+)
-12 var omitNewline = flag.Bool(&quot;n&quot;, false, &quot;don't print final newline&quot;)
+var omitNewline = flag.Bool(&#34;n&#34;, false, &#34;don&#39;t print final newline&#34;)
-14 const (
-15 Space = &quot; &quot;
-16 Newline = &quot;\n&quot;
-17 )
+const (
+ Space = &#34; &#34;
+ Newline = &#34;\n&#34;
+)
-19 func main() {
-20 flag.Parse() // Scans the arg list and sets up flags
-21 var s string = &quot;&quot;
-22 for i := 0; i &lt; flag.NArg(); i++ {
-23 if i &gt; 0 {
-24 s += Space
-25 }
-26 s += flag.Arg(i)
-27 }
-28 if !*omitNewline {
-29 s += Newline
-30 }
-31 os.Stdout.WriteString(s)
-32 }
+func main() {
+ flag.Parse() // Scans the arg list and sets up flags
+ var s string = &#34;&#34;
+ for i := 0; i &lt; flag.NArg(); i++ {
+ if i &gt; 0 {
+ s += Space
+ }
+ s += flag.Arg(i)
+ }
+ if !*omitNewline {
+ s += Newline
+ }
+ os.Stdout.WriteString(s)
+}
</pre>
<p>
This program is small but it's doing a number of new things. In the last example,
we saw <code>func</code> introduce a function. The keywords <code>var</code>, <code>const</code>, and <code>type</code>
(not used yet) also introduce declarations, as does <code>import</code>.
Notice that we can group declarations of the same sort into
-parenthesized lists, one item per line, as on lines 7-10 and 14-17.
+parenthesized lists, one item per line, as in the <code>import</code> and <code>const</code> clauses here.
But it's not necessary to do so; we could have said
<p>
<pre>
- const Space = " "
- const Newline = "\n"
+const Space = " "
+const Newline = "\n"
</pre>
<p>
This program imports the <code>&quot;os&quot;</code> package to access its <code>Stdout</code> variable, of type
@@ -186,7 +186,7 @@ string variable we will use to build the output.
The declaration statement has the form
<p>
<pre>
- var s string = ""
+var s string = ""
</pre>
<p>
This is the <code>var</code> keyword, followed by the name of the variable, followed by
@@ -197,20 +197,20 @@ string constant is of type string, we don't have to tell the compiler that.
We could write
<p>
<pre>
- var s = ""
+var s = ""
</pre>
<p>
or we could go even shorter and write the idiom
<p>
<pre>
- s := ""
+s := ""
</pre>
<p>
The <code>:=</code> operator is used a lot in Go to represent an initializing declaration.
There's one in the <code>for</code> clause on the next line:
<p>
-<pre> <!-- progs/echo.go /for/ -->
-22 for i := 0; i &lt; flag.NArg(); i++ {
+<pre><!-- progs/echo.go /for/
+--> for i := 0; i &lt; flag.NArg(); i++ {
</pre>
<p>
The <code>flag</code> package has parsed the arguments and left the non-flag arguments
@@ -231,7 +231,7 @@ It's defined that way. Falling off the end of <code>main.main</code> means
''success''; if you want to signal an erroneous return, call
<p>
<pre>
- os.Exit(1)
+os.Exit(1)
</pre>
<p>
The <code>os</code> package contains other essentials for getting
@@ -259,20 +259,20 @@ Once you've built a string <i>value</i>, you can't change it, although
of course you can change a string <i>variable</i> simply by
reassigning it. This snippet from <code>strings.go</code> is legal code:
<p>
-<pre> <!-- progs/strings.go /hello/ /ciao/ -->
-10 s := &quot;hello&quot;
-11 if s[1] != 'e' { os.Exit(1) }
-12 s = &quot;good bye&quot;
-13 var p *string = &amp;s
-14 *p = &quot;ciao&quot;
+<pre><!-- progs/strings.go /hello/ /ciao/
+--> s := &#34;hello&#34;
+ if s[1] != 'e' { os.Exit(1) }
+ s = &#34;good bye&#34;
+ var p *string = &amp;s
+ *p = &#34;ciao&#34;
</pre>
<p>
However the following statements are illegal because they would modify
a <code>string</code> value:
<p>
<pre>
- s[0] = 'x'
- (*p)[1] = 'y'
+s[0] = 'x'
+(*p)[1] = 'y'
</pre>
<p>
In C++ terms, Go strings are a bit like <code>const strings</code>, while pointers
@@ -284,7 +284,7 @@ read on.
Arrays are declared like this:
<p>
<pre>
- var arrayOfInt [10]int
+var arrayOfInt [10]int
</pre>
<p>
Arrays, like strings, are values, but they are mutable. This differs
@@ -315,7 +315,7 @@ expression formed
from a type followed by a brace-bounded expression like this:
<p>
<pre>
- [3]int{1,2,3}
+[3]int{1,2,3}
</pre>
<p>
In this case the constructor builds an array of 3 <code>ints</code>.
@@ -330,14 +330,14 @@ will slice the whole array.
<p>
Using slices one can write this function (from <code>sum.go</code>):
<p>
-<pre> <!-- progs/sum.go /sum/ /^}/ -->
-09 func sum(a []int) int { // returns an int
-10 s := 0
-11 for i := 0; i &lt; len(a); i++ {
-12 s += a[i]
-13 }
-14 return s
-15 }
+<pre><!-- progs/sum.go /sum/ /^}/
+-->func sum(a []int) int { // returns an int
+ s := 0
+ for i := 0; i &lt; len(a); i++ {
+ s += a[i]
+ }
+ return s
+}
</pre>
<p>
Note how the return type (<code>int</code>) is defined for <code>sum</code> by stating it
@@ -348,14 +348,14 @@ a simpler way in a moment) constructs
an array and slices it:
<p>
<pre>
- s := sum([3]int{1,2,3}[:])
+s := sum([3]int{1,2,3}[:])
</pre>
<p>
If you are creating a regular array but want the compiler to count the
elements for you, use <code>...</code> as the array size:
<p>
<pre>
- s := sum([...]int{1,2,3}[:])
+s := sum([...]int{1,2,3}[:])
</pre>
<p>
That's fussier than necessary, though.
@@ -363,13 +363,13 @@ In practice, unless you're meticulous about storage layout within a
data structure, a slice itself&mdash;using empty brackets with no size&mdash;is all you need:
<p>
<pre>
- s := sum([]int{1,2,3})
+s := sum([]int{1,2,3})
</pre>
<p>
There are also maps, which you can initialize like this:
<p>
<pre>
- m := map[string]int{"one":1 , "two":2}
+m := map[string]int{"one":1 , "two":2}
</pre>
<p>
The built-in function <code>len</code>, which returns number of elements,
@@ -380,13 +380,13 @@ By the way, another thing that works on strings, arrays, slices, maps
and channels is the <code>range</code> clause on <code>for</code> loops. Instead of writing
<p>
<pre>
- for i := 0; i &lt; len(a); i++ { ... }
+for i := 0; i &lt; len(a); i++ { ... }
</pre>
<p>
to loop over the elements of a slice (or map or ...) , we could write
<p>
<pre>
- for i, v := range a { ... }
+for i, v := range a { ... }
</pre>
<p>
This assigns <code>i</code> to the index and <code>v</code> to the value of the successive
@@ -404,14 +404,14 @@ To allocate a new variable, use the built-in function <code>new</code>, which
returns a pointer to the allocated storage.
<p>
<pre>
- type T struct { a, b int }
- var t *T = new(T)
+type T struct { a, b int }
+var t *T = new(T)
</pre>
<p>
or the more idiomatic
<p>
<pre>
- t := new(T)
+t := new(T)
</pre>
<p>
Some types&mdash;maps, slices, and channels (see below)&mdash;have reference semantics.
@@ -420,14 +420,14 @@ referencing the same underlying data will see the modification. For these three
types you want to use the built-in function <code>make</code>:
<p>
<pre>
- m := make(map[string]int)
+m := make(map[string]int)
</pre>
<p>
This statement initializes a new map ready to store entries.
If you just declare the map, as in
<p>
<pre>
- var m map[string]int
+var m map[string]int
</pre>
<p>
it creates a <code>nil</code> reference that cannot hold anything. To use the map,
@@ -448,20 +448,20 @@ can overflow only when they are assigned to an integer variable with
too little precision to represent the value.
<p>
<pre>
- const hardEight = (1 &lt;&lt; 100) &gt;&gt; 97 // legal
+const hardEight = (1 &lt;&lt; 100) &gt;&gt; 97 // legal
</pre>
<p>
There are nuances that deserve redirection to the legalese of the
language specification but here are some illustrative examples:
<p>
<pre>
- var a uint64 = 0 // a has type uint64, value 0
- a := uint64(0) // equivalent; uses a "conversion"
- i := 0x1234 // i gets default type: int
- var j int = 1e6 // legal - 1000000 is representable in an int
- x := 1.5 // a float64, the default type for floating constants
- i3div2 := 3/2 // integer division - result is 1
- f3div2 := 3./2. // floating-point division - result is 1.5
+var a uint64 = 0 // a has type uint64, value 0
+a := uint64(0) // equivalent; uses a "conversion"
+i := 0x1234 // i gets default type: int
+var j int = 1e6 // legal - 1000000 is representable in an int
+x := 1.5 // a float64, the default type for floating constants
+i3div2 := 3/2 // integer division - result is 1
+f3div2 := 3./2. // floating-point division - result is 1.5
</pre>
<p>
Conversions only work for simple cases such as converting <code>ints</code> of one
@@ -476,18 +476,18 @@ assigned to a variable.
Next we'll look at a simple package for doing file I/O with an
open/close/read/write interface. Here's the start of <code>file.go</code>:
<p>
-<pre> <!-- progs/file.go /package/ /^}/ -->
-05 package file
+<pre><!-- progs/file.go /package/ /^}/
+-->package file
-07 import (
-08 &quot;os&quot;
-09 &quot;syscall&quot;
-10 )
+import (
+ &#34;os&#34;
+ &#34;syscall&#34;
+)
-12 type File struct {
-13 fd int // file descriptor number
-14 name string // file name at Open time
-15 }
+type File struct {
+ fd int // file descriptor number
+ name string // file name at Open time
+}
</pre>
<p>
The first few lines declare the name of the
@@ -518,13 +518,13 @@ will soon give it some exported, upper-case methods.
<p>
First, though, here is a factory to create a <code>File</code>:
<p>
-<pre> <!-- progs/file.go /newFile/ /^}/ -->
-17 func newFile(fd int, name string) *File {
-18 if fd &lt; 0 {
-19 return nil
-20 }
-21 return &amp;File{fd, name}
-22 }
+<pre><!-- progs/file.go /newFile/ /^}/
+-->func newFile(fd int, name string) *File {
+ if fd &lt; 0 {
+ return nil
+ }
+ return &amp;File{fd, name}
+}
</pre>
<p>
This returns a pointer to a new <code>File</code> structure with the file descriptor and name
@@ -533,10 +533,10 @@ the ones used to build maps and arrays, to construct a new heap-allocated
object. We could write
<p>
<pre>
- n := new(File)
- n.fd = fd
- n.name = name
- return n
+n := new(File)
+n.fd = fd
+n.name = name
+return n
</pre>
<p>
but for simple structures like <code>File</code> it's easier to return the address of a
@@ -544,25 +544,26 @@ composite literal, as is done here on line 21.
<p>
We can use the factory to construct some familiar, exported variables of type <code>*File</code>:
<p>
-<pre> <!-- progs/file.go /var/ /^.$/ -->
-24 var (
-25 Stdin = newFile(syscall.Stdin, &quot;/dev/stdin&quot;)
-26 Stdout = newFile(syscall.Stdout, &quot;/dev/stdout&quot;)
-27 Stderr = newFile(syscall.Stderr, &quot;/dev/stderr&quot;)
-28 )
+<pre><!-- progs/file.go /var/ /^.$/
+-->var (
+ Stdin = newFile(syscall.Stdin, &#34;/dev/stdin&#34;)
+ Stdout = newFile(syscall.Stdout, &#34;/dev/stdout&#34;)
+ Stderr = newFile(syscall.Stderr, &#34;/dev/stderr&#34;)
+)
+
</pre>
<p>
The <code>newFile</code> function was not exported because it's internal. The proper,
exported factory to use is <code>OpenFile</code> (we'll explain that name in a moment):
<p>
-<pre> <!-- progs/file.go /func.OpenFile/ /^}/ -->
-30 func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
-31 r, e := syscall.Open(name, mode, perm)
-32 if e != 0 {
-33 err = os.Errno(e)
-34 }
-35 return newFile(r, name), err
-36 }
+<pre><!-- progs/file.go /func.OpenFile/ /^}/
+-->func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
+ r, e := syscall.Open(name, mode, perm)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return newFile(r, name), err
+}
</pre>
<p>
There are a number of new things in these few lines. First, <code>OpenFile</code> returns
@@ -593,23 +594,23 @@ the implementation of our <code>Open</code> and <code>Create</code>; they're tri
wrappers that eliminate common errors by capturing
the tricky standard arguments to open and, especially, to create a file:
<p>
-<pre> <!-- progs/file.go /^const/ /^}/ -->
-38 const (
-39 O_RDONLY = syscall.O_RDONLY
-40 O_RDWR = syscall.O_RDWR
-41 O_CREATE = syscall.O_CREAT
-42 O_TRUNC = syscall.O_TRUNC
-43 )
+<pre><!-- progs/file.go /^const/ /^}/
+-->const (
+ O_RDONLY = syscall.O_RDONLY
+ O_RDWR = syscall.O_RDWR
+ O_CREATE = syscall.O_CREAT
+ O_TRUNC = syscall.O_TRUNC
+)
-45 func Open(name string) (file *File, err os.Error) {
-46 return OpenFile(name, O_RDONLY, 0)
-47 }
+func Open(name string) (file *File, err os.Error) {
+ return OpenFile(name, O_RDONLY, 0)
+}
</pre>
<p>
-<pre> <!-- progs/file.go /func.Create/ /^}/ -->
-49 func Create(name string) (file *File, err os.Error) {
-50 return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
-51 }
+<pre><!-- progs/file.go /func.Create/ /^}/
+-->func Create(name string) (file *File, err os.Error) {
+ return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
+}
</pre>
<p>
Back to our main story.
@@ -619,44 +620,44 @@ of that type, placed
in parentheses before the function name. Here are some methods for <code>*File</code>,
each of which declares a receiver variable <code>file</code>.
<p>
-<pre> <!-- progs/file.go /Close/ END -->
-53 func (file *File) Close() os.Error {
-54 if file == nil {
-55 return os.EINVAL
-56 }
-57 e := syscall.Close(file.fd)
-58 file.fd = -1 // so it can't be closed again
-59 if e != 0 {
-60 return os.Errno(e)
-61 }
-62 return nil
-63 }
+<pre><!-- progs/file.go /Close/ $
+-->func (file *File) Close() os.Error {
+ if file == nil {
+ return os.EINVAL
+ }
+ e := syscall.Close(file.fd)
+ file.fd = -1 // so it can't be closed again
+ if e != 0 {
+ return os.Errno(e)
+ }
+ return nil
+}
-65 func (file *File) Read(b []byte) (ret int, err os.Error) {
-66 if file == nil {
-67 return -1, os.EINVAL
-68 }
-69 r, e := syscall.Read(file.fd, b)
-70 if e != 0 {
-71 err = os.Errno(e)
-72 }
-73 return int(r), err
-74 }
+func (file *File) Read(b []byte) (ret int, err os.Error) {
+ if file == nil {
+ return -1, os.EINVAL
+ }
+ r, e := syscall.Read(file.fd, b)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return int(r), err
+}
-76 func (file *File) Write(b []byte) (ret int, err os.Error) {
-77 if file == nil {
-78 return -1, os.EINVAL
-79 }
-80 r, e := syscall.Write(file.fd, b)
-81 if e != 0 {
-82 err = os.Errno(e)
-83 }
-84 return int(r), err
-85 }
+func (file *File) Write(b []byte) (ret int, err os.Error) {
+ if file == nil {
+ return -1, os.EINVAL
+ }
+ r, e := syscall.Write(file.fd, b)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return int(r), err
+}
-87 func (file *File) String() string {
-88 return file.name
-89 }
+func (file *File) String() string {
+ return file.name
+}
</pre>
<p>
There is no implicit <code>this</code> and the receiver variable must be used to access
@@ -674,24 +675,24 @@ set of such error values.
<p>
We can now use our new package:
<p>
-<pre> <!-- progs/helloworld3.go /package/ END -->
-05 package main
+<pre><!-- progs/helloworld3.go /package/ $
+-->package main
-07 import (
-08 &quot;./file&quot;
-09 &quot;fmt&quot;
-10 &quot;os&quot;
-11 )
+import (
+ &#34;./file&#34;
+ &#34;fmt&#34;
+ &#34;os&#34;
+)
-13 func main() {
-14 hello := []byte(&quot;hello, world\n&quot;)
-15 file.Stdout.Write(hello)
-16 f, err := file.Open(&quot;/does/not/exist&quot;)
-17 if f == nil {
-18 fmt.Printf(&quot;can't open file; err=%s\n&quot;, err.String())
-19 os.Exit(1)
-20 }
-21 }
+func main() {
+ hello := []byte(&#34;hello, world\n&#34;)
+ file.Stdout.Write(hello)
+ f, err := file.Open(&#34;/does/not/exist&#34;)
+ if f == nil {
+ fmt.Printf(&#34;can&#39;t open file; err=%s\n&#34;, err.String())
+ os.Exit(1)
+ }
+}
</pre>
<p>
The ''<code>./</code>'' in the import of ''<code>./file</code>'' tells the compiler
@@ -703,13 +704,13 @@ package.)
Now we can compile and run the program. On Unix, this would be the result:
<p>
<pre>
- $ 6g file.go # compile file package
- $ 6g helloworld3.go # compile main package
- $ 6l -o helloworld3 helloworld3.6 # link - no need to mention "file"
- $ helloworld3
- hello, world
- can't open file; err=No such file or directory
- $
+$ 6g file.go # compile file package
+$ 6g helloworld3.go # compile main package
+$ 6l -o helloworld3 helloworld3.6 # link - no need to mention "file"
+$ helloworld3
+hello, world
+can't open file; err=No such file or directory
+$
</pre>
<p>
<h2>Rotting cats</h2>
@@ -717,56 +718,56 @@ Now we can compile and run the program. On Unix, this would be the result:
Building on the <code>file</code> package, here's a simple version of the Unix utility <code>cat(1)</code>,
<code>progs/cat.go</code>:
<p>
-<pre> <!-- progs/cat.go /package/ END -->
-05 package main
+<pre><!-- progs/cat.go /package/ $
+-->package main
-07 import (
-08 &quot;./file&quot;
-09 &quot;flag&quot;
-10 &quot;fmt&quot;
-11 &quot;os&quot;
-12 )
+import (
+ &#34;./file&#34;
+ &#34;flag&#34;
+ &#34;fmt&#34;
+ &#34;os&#34;
+)
-14 func cat(f *file.File) {
-15 const NBUF = 512
-16 var buf [NBUF]byte
-17 for {
-18 switch nr, er := f.Read(buf[:]); true {
-19 case nr &lt; 0:
-20 fmt.Fprintf(os.Stderr, &quot;cat: error reading from %s: %s\n&quot;, f.String(), er.String())
-21 os.Exit(1)
-22 case nr == 0: // EOF
-23 return
-24 case nr &gt; 0:
-25 if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
-26 fmt.Fprintf(os.Stderr, &quot;cat: error writing from %s: %s\n&quot;, f.String(), ew.String())
-27 os.Exit(1)
-28 }
-29 }
-30 }
-31 }
+func cat(f *file.File) {
+ const NBUF = 512
+ var buf [NBUF]byte
+ for {
+ switch nr, er := f.Read(buf[:]); true {
+ case nr &lt; 0:
+ fmt.Fprintf(os.Stderr, &#34;cat: error reading from %s: %s\n&#34;, f.String(), er.String())
+ os.Exit(1)
+ case nr == 0: // EOF
+ return
+ case nr &gt; 0:
+ if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
+ fmt.Fprintf(os.Stderr, &#34;cat: error writing from %s: %s\n&#34;, f.String(), ew.String())
+ os.Exit(1)
+ }
+ }
+ }
+}
-33 func main() {
-34 flag.Parse() // Scans the arg list and sets up flags
-35 if flag.NArg() == 0 {
-36 cat(file.Stdin)
-37 }
-38 for i := 0; i &lt; flag.NArg(); i++ {
-39 f, err := file.Open(flag.Arg(i))
-40 if f == nil {
-41 fmt.Fprintf(os.Stderr, &quot;cat: can't open %s: error %s\n&quot;, flag.Arg(i), err)
-42 os.Exit(1)
-43 }
-44 cat(f)
-45 f.Close()
-46 }
-47 }
+func main() {
+ flag.Parse() // Scans the arg list and sets up flags
+ if flag.NArg() == 0 {
+ cat(file.Stdin)
+ }
+ for i := 0; i &lt; flag.NArg(); i++ {
+ f, err := file.Open(flag.Arg(i))
+ if f == nil {
+ fmt.Fprintf(os.Stderr, &#34;cat: can&#39;t open %s: error %s\n&#34;, flag.Arg(i), err)
+ os.Exit(1)
+ }
+ cat(f)
+ f.Close()
+ }
+}
</pre>
<p>
By now this should be easy to follow, but the <code>switch</code> statement introduces some
new features. Like a <code>for</code> loop, an <code>if</code> or <code>switch</code> can include an
-initialization statement. The <code>switch</code> on line 18 uses one to create variables
-<code>nr</code> and <code>er</code> to hold the return values from the call to <code>f.Read</code>. (The <code>if</code> on line 25
+initialization statement. The <code>switch</code> statement in <code>cat</code> uses one to create variables
+<code>nr</code> and <code>er</code> to hold the return values from the call to <code>f.Read</code>. (The <code>if</code> a few lines later
has the same idea.) The <code>switch</code> statement is general: it evaluates the cases
from top to bottom looking for the first case that matches the value; the
case expressions don't need to be constants or even integers, as long as
@@ -778,7 +779,7 @@ in a <code>for</code> statement, a missing value means <code>true</code>. In fa
is a form of <code>if-else</code> chain. While we're here, it should be mentioned that in
<code>switch</code> statements each <code>case</code> has an implicit <code>break</code>.
<p>
-Line 25 calls <code>Write</code> by slicing the incoming buffer, which is itself a slice.
+The argument to <code>file.Stdout.Write</code> is created by slicing the array <code>buf</code>.
Slices provide the standard Go way to handle I/O buffers.
<p>
Now let's make a variant of <code>cat</code> that optionally does <code>rot13</code> on its input.
@@ -789,11 +790,11 @@ The <code>cat</code> subroutine uses only two methods of <code>f</code>: <code>R
so let's start by defining an interface that has exactly those two methods.
Here is code from <code>progs/cat_rot13.go</code>:
<p>
-<pre> <!-- progs/cat_rot13.go /type.reader/ /^}/ -->
-26 type reader interface {
-27 Read(b []byte) (ret int, err os.Error)
-28 String() string
-29 }
+<pre><!-- progs/cat_rot13.go /type.reader/ /^}/
+-->type reader interface {
+ Read(b []byte) (ret int, err os.Error)
+ String() string
+}
</pre>
<p>
Any type that has the two methods of <code>reader</code>&mdash;regardless of whatever
@@ -806,68 +807,68 @@ existing <code>reader</code> and does <code>rot13</code> on the data. To do this
the type and implement the methods and with no other bookkeeping,
we have a second implementation of the <code>reader</code> interface.
<p>
-<pre> <!-- progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/ -->
-31 type rotate13 struct {
-32 source reader
-33 }
+<pre><!-- progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
+-->type rotate13 struct {
+ source reader
+}
-35 func newRotate13(source reader) *rotate13 {
-36 return &amp;rotate13{source}
-37 }
+func newRotate13(source reader) *rotate13 {
+ return &amp;rotate13{source}
+}
-39 func (r13 *rotate13) Read(b []byte) (ret int, err os.Error) {
-40 r, e := r13.source.Read(b)
-41 for i := 0; i &lt; r; i++ {
-42 b[i] = rot13(b[i])
-43 }
-44 return r, e
-45 }
+func (r13 *rotate13) Read(b []byte) (ret int, err os.Error) {
+ r, e := r13.source.Read(b)
+ for i := 0; i &lt; r; i++ {
+ b[i] = rot13(b[i])
+ }
+ return r, e
+}
-47 func (r13 *rotate13) String() string {
-48 return r13.source.String()
-49 }
-50 // end of rotate13 implementation
+func (r13 *rotate13) String() string {
+ return r13.source.String()
+}
+// end of rotate13 implementation
</pre>
<p>
-(The <code>rot13</code> function called on line 42 is trivial and not worth reproducing here.)
+(The <code>rot13</code> function called in <code>Read</code> is trivial and not worth reproducing here.)
<p>
To use the new feature, we define a flag:
<p>
-<pre> <!-- progs/cat_rot13.go /rot13Flag/ -->
-14 var rot13Flag = flag.Bool(&quot;rot13&quot;, false, &quot;rot13 the input&quot;)
+<pre><!-- progs/cat_rot13.go /rot13Flag/
+-->var rot13Flag = flag.Bool(&#34;rot13&#34;, false, &#34;rot13 the input&#34;)
</pre>
<p>
and use it from within a mostly unchanged <code>cat</code> function:
<p>
-<pre> <!-- progs/cat_rot13.go /func.cat/ /^}/ -->
-52 func cat(r reader) {
-53 const NBUF = 512
-54 var buf [NBUF]byte
+<pre><!-- progs/cat_rot13.go /func.cat/ /^}/
+-->func cat(r reader) {
+ const NBUF = 512
+ var buf [NBUF]byte
-56 if *rot13Flag {
-57 r = newRotate13(r)
-58 }
-59 for {
-60 switch nr, er := r.Read(buf[:]); {
-61 case nr &lt; 0:
-62 fmt.Fprintf(os.Stderr, &quot;cat: error reading from %s: %s\n&quot;, r.String(), er.String())
-63 os.Exit(1)
-64 case nr == 0: // EOF
-65 return
-66 case nr &gt; 0:
-67 nw, ew := file.Stdout.Write(buf[0:nr])
-68 if nw != nr {
-69 fmt.Fprintf(os.Stderr, &quot;cat: error writing from %s: %s\n&quot;, r.String(), ew.String())
-70 os.Exit(1)
-71 }
-72 }
-73 }
-74 }
+ if *rot13Flag {
+ r = newRotate13(r)
+ }
+ for {
+ switch nr, er := r.Read(buf[:]); {
+ case nr &lt; 0:
+ fmt.Fprintf(os.Stderr, &#34;cat: error reading from %s: %s\n&#34;, r.String(), er.String())
+ os.Exit(1)
+ case nr == 0: // EOF
+ return
+ case nr &gt; 0:
+ nw, ew := file.Stdout.Write(buf[0:nr])
+ if nw != nr {
+ fmt.Fprintf(os.Stderr, &#34;cat: error writing from %s: %s\n&#34;, r.String(), ew.String())
+ os.Exit(1)
+ }
+ }
+ }
+}
</pre>
<p>
(We could also do the wrapping in <code>main</code> and leave <code>cat</code> mostly alone, except
for changing the type of the argument; consider that an exercise.)
-Lines 56 through 58 set it all up: If the <code>rot13</code> flag is true, wrap the <code>reader</code>
+The <code>if</code> at the top of <code>cat</code> sets it all up: If the <code>rot13</code> flag is true, wrap the <code>reader</code>
we received into a <code>rotate13</code> and proceed. Note that the interface variables
are values, not pointers: the argument is of type <code>reader</code>, not <code>*reader</code>,
even though under the covers it holds a pointer to a <code>struct</code>.
@@ -875,11 +876,11 @@ even though under the covers it holds a pointer to a <code>struct</code>.
Here it is in action:
<p>
<pre>
- $ echo abcdefghijklmnopqrstuvwxyz | ./cat
- abcdefghijklmnopqrstuvwxyz
- $ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
- nopqrstuvwxyzabcdefghijklm
- $
+$ echo abcdefghijklmnopqrstuvwxyz | ./cat
+abcdefghijklmnopqrstuvwxyz
+$ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
+nopqrstuvwxyzabcdefghijklm
+$
</pre>
<p>
Fans of dependency injection may take cheer from how easily interfaces
@@ -895,7 +896,7 @@ implement a <code>writer</code>, or any other interface built from its methods t
fits the current situation. Consider the <i>empty interface</i>
<p>
<pre>
- type Empty interface {}
+type Empty interface {}
</pre>
<p>
<i>Every</i> type implements the empty interface, which makes it
@@ -910,36 +911,36 @@ same interface variable.
<p>
As an example, consider this simple sort algorithm taken from <code>progs/sort.go</code>:
<p>
-<pre> <!-- progs/sort.go /func.Sort/ /^}/ -->
-13 func Sort(data Interface) {
-14 for i := 1; i &lt; data.Len(); i++ {
-15 for j := i; j &gt; 0 &amp;&amp; data.Less(j, j-1); j-- {
-16 data.Swap(j, j-1)
-17 }
-18 }
-19 }
+<pre><!-- progs/sort.go /func.Sort/ /^}/
+-->func Sort(data Interface) {
+ for i := 1; i &lt; data.Len(); i++ {
+ for j := i; j &gt; 0 &amp;&amp; data.Less(j, j-1); j-- {
+ data.Swap(j, j-1)
+ }
+ }
+}
</pre>
<p>
The code needs only three methods, which we wrap into sort's <code>Interface</code>:
<p>
-<pre> <!-- progs/sort.go /interface/ /^}/ -->
-07 type Interface interface {
-08 Len() int
-09 Less(i, j int) bool
-10 Swap(i, j int)
-11 }
+<pre><!-- progs/sort.go /interface/ /^}/
+-->type Interface interface {
+ Len() int
+ Less(i, j int) bool
+ Swap(i, j int)
+}
</pre>
<p>
We can apply <code>Sort</code> to any type that implements <code>Len</code>, <code>Less</code>, and <code>Swap</code>.
The <code>sort</code> package includes the necessary methods to allow sorting of
arrays of integers, strings, etc.; here's the code for arrays of <code>int</code>
<p>
-<pre> <!-- progs/sort.go /type.*IntSlice/ /Swap/ -->
-33 type IntSlice []int
+<pre><!-- progs/sort.go /type.*IntSlice/ /Swap/
+-->type IntSlice []int
-35 func (p IntSlice) Len() int { return len(p) }
-36 func (p IntSlice) Less(i, j int) bool { return p[i] &lt; p[j] }
-37 func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p IntSlice) Len() int { return len(p) }
+func (p IntSlice) Less(i, j int) bool { return p[i] &lt; p[j] }
+func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
</pre>
<p>
Here we see methods defined for non-<code>struct</code> types. You can define methods
@@ -949,34 +950,34 @@ And now a routine to test it out, from <code>progs/sortmain.go</code>. This
uses a function in the <code>sort</code> package, omitted here for brevity,
to test that the result is sorted.
<p>
-<pre> <!-- progs/sortmain.go /func.ints/ /^}/ -->
-12 func ints() {
-13 data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
-14 a := sort.IntSlice(data)
-15 sort.Sort(a)
-16 if !sort.IsSorted(a) {
-17 panic(&quot;fail&quot;)
-18 }
-19 }
+<pre><!-- progs/sortmain.go /func.ints/ /^}/
+-->func ints() {
+ data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
+ a := sort.IntSlice(data)
+ sort.Sort(a)
+ if !sort.IsSorted(a) {
+ panic(&#34;fail&#34;)
+ }
+}
</pre>
<p>
If we have a new type we want to be able to sort, all we need to do is
to implement the three methods for that type, like this:
<p>
-<pre> <!-- progs/sortmain.go /type.day/ /Swap/ -->
-30 type day struct {
-31 num int
-32 shortName string
-33 longName string
-34 }
+<pre><!-- progs/sortmain.go /type.day/ /Swap/
+-->type day struct {
+ num int
+ shortName string
+ longName string
+}
-36 type dayArray struct {
-37 data []*day
-38 }
+type dayArray struct {
+ data []*day
+}
-40 func (p *dayArray) Len() int { return len(p.data) }
-41 func (p *dayArray) Less(i, j int) bool { return p.data[i].num &lt; p.data[j].num }
-42 func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] }
+func (p *dayArray) Len() int { return len(p.data) }
+func (p *dayArray) Less(i, j int) bool { return p.data[i].num &lt; p.data[j].num }
+func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] }
</pre>
<p>
<p>
@@ -990,7 +991,7 @@ implements <code>Printf</code>, <code>Fprintf</code>, and so on.
Within the <code>fmt</code> package, <code>Printf</code> is declared with this signature:
<p>
<pre>
- Printf(format string, v ...interface{}) (n int, errno os.Error)
+Printf(format string, v ...interface{}) (n int, errno os.Error)
</pre>
<p>
The token <code>...</code> introduces a variable-length argument list that in C would
@@ -1011,34 +1012,34 @@ argument. It's easier in many cases in Go. Instead of <code>%llud</code> you
can just say <code>%d</code>; <code>Printf</code> knows the size and signedness of the
integer and can do the right thing for you. The snippet
<p>
-<pre> <!-- progs/print.go NR==10 NR==11 -->
-10 var u64 uint64 = 1&lt;&lt;64-1
-11 fmt.Printf(&quot;%d %d\n&quot;, u64, int64(u64))
+<pre><!-- progs/print.go 10 11
+--> var u64 uint64 = 1&lt;&lt;64-1
+ fmt.Printf(&#34;%d %d\n&#34;, u64, int64(u64))
</pre>
<p>
prints
<p>
<pre>
- 18446744073709551615 -1
+18446744073709551615 -1
</pre>
<p>
In fact, if you're lazy the format <code>%v</code> will print, in a simple
appropriate style, any value, even an array or structure. The output of
<p>
-<pre> <!-- progs/print.go NR==14 NR==20 -->
-14 type T struct {
-15 a int
-16 b string
-17 }
-18 t := T{77, &quot;Sunset Strip&quot;}
-19 a := []int{1, 2, 3, 4}
-20 fmt.Printf(&quot;%v %v %v\n&quot;, u64, t, a)
+<pre><!-- progs/print.go 14 20
+--> type T struct {
+ a int
+ b string
+ }
+ t := T{77, &#34;Sunset Strip&#34;}
+ a := []int{1, 2, 3, 4}
+ fmt.Printf(&#34;%v %v %v\n&#34;, u64, t, a)
</pre>
<p>
is
<p>
<pre>
- 18446744073709551615 {77 Sunset Strip} [1 2 3 4]
+18446744073709551615 {77 Sunset Strip} [1 2 3 4]
</pre>
<p>
You can drop the formatting altogether if you use <code>Print</code> or <code>Println</code>
@@ -1048,9 +1049,9 @@ of <code>%v</code> while <code>Println</code> inserts spaces between arguments
and adds a newline. The output of each of these two lines is identical
to that of the <code>Printf</code> call above.
<p>
-<pre> <!-- progs/print.go NR==21 NR==22 -->
-21 fmt.Print(u64, &quot; &quot;, t, &quot; &quot;, a, &quot;\n&quot;)
-22 fmt.Println(u64, t, a)
+<pre><!-- progs/print.go 21 22
+--> fmt.Print(u64, &#34; &#34;, t, &#34; &#34;, a, &#34;\n&#34;)
+ fmt.Println(u64, t, a)
</pre>
<p>
If you have your own type you'd like <code>Printf</code> or <code>Print</code> to format,
@@ -1059,27 +1060,27 @@ routines will examine the value to inquire whether it implements
the method and if so, use it rather than some other formatting.
Here's a simple example.
<p>
-<pre> <!-- progs/print_string.go NR==9 END -->
-09 type testType struct {
-10 a int
-11 b string
-12 }
+<pre><!-- progs/print_string.go 9 $
+-->type testType struct {
+ a int
+ b string
+}
-14 func (t *testType) String() string {
-15 return fmt.Sprint(t.a) + &quot; &quot; + t.b
-16 }
+func (t *testType) String() string {
+ return fmt.Sprint(t.a) + &#34; &#34; + t.b
+}
-18 func main() {
-19 t := &amp;testType{77, &quot;Sunset Strip&quot;}
-20 fmt.Println(t)
-21 }
+func main() {
+ t := &amp;testType{77, &#34;Sunset Strip&#34;}
+ fmt.Println(t)
+}
</pre>
<p>
Since <code>*testType</code> has a <code>String</code> method, the
default formatter for that type will use it and produce the output
<p>
<pre>
- 77 Sunset Strip
+77 Sunset Strip
</pre>
<p>
Observe that the <code>String</code> method calls <code>Sprint</code> (the obvious Go
@@ -1101,18 +1102,18 @@ Schematically, given a value <code>v</code>, it does this:
<p>
<p>
<pre>
- type Stringer interface {
- String() string
- }
+type Stringer interface {
+ String() string
+}
</pre>
<p>
<pre>
- s, ok := v.(Stringer) // Test whether v implements "String()"
- if ok {
- result = s.String()
- } else {
- result = defaultOutput(v)
- }
+s, ok := v.(Stringer) // Test whether v implements "String()"
+if ok {
+ result = s.String()
+} else {
+ result = defaultOutput(v)
+}
</pre>
<p>
The code uses a ``type assertion'' (<code>v.(Stringer)</code>) to test if the value stored in
@@ -1133,9 +1134,9 @@ not a file. Instead, it is a variable of type <code>io.Writer</code>, which is
interface type defined in the <code>io</code> library:
<p>
<pre>
- type Writer interface {
- Write(p []byte) (n int, err os.Error)
- }
+type Writer interface {
+ Write(p []byte) (n int, err os.Error)
+}
</pre>
<p>
(This interface is another conventional name, this time for <code>Write</code>; there are also
@@ -1178,13 +1179,13 @@ coordinates the communication; as with maps and slices, use
<p>
Here is the first function in <code>progs/sieve.go</code>:
<p>
-<pre> <!-- progs/sieve.go /Send/ /^}/ -->
-09 // Send the sequence 2, 3, 4, ... to channel 'ch'.
-10 func generate(ch chan int) {
-11 for i := 2; ; i++ {
-12 ch &lt;- i // Send 'i' to channel 'ch'.
-13 }
-14 }
+<pre><!-- progs/sieve.go /Send/ /^}/
+-->// Send the sequence 2, 3, 4, ... to channel 'ch'.
+func generate(ch chan int) {
+ for i := 2; ; i++ {
+ ch &lt;- i // Send &#39;i&#39; to channel &#39;ch&#39;.
+ }
+}
</pre>
<p>
The <code>generate</code> function sends the sequence 2, 3, 4, 5, ... to its
@@ -1197,17 +1198,17 @@ channel, and a prime number. It copies values from the input to the
output, discarding anything divisible by the prime. The unary communications
operator <code>&lt;-</code> (receive) retrieves the next value on the channel.
<p>
-<pre> <!-- progs/sieve.go /Copy.the/ /^}/ -->
-16 // Copy the values from channel 'in' to channel 'out',
-17 // removing those divisible by 'prime'.
-18 func filter(in, out chan int, prime int) {
-19 for {
-20 i := &lt;-in // Receive value of new variable 'i' from 'in'.
-21 if i % prime != 0 {
-22 out &lt;- i // Send 'i' to channel 'out'.
-23 }
-24 }
-25 }
+<pre><!-- progs/sieve.go /Copy.the/ /^}/
+-->// Copy the values from channel 'in' to channel 'out',
+// removing those divisible by 'prime'.
+func filter(in, out chan int, prime int) {
+ for {
+ i := &lt;-in // Receive value of new variable &#39;i&#39; from &#39;in&#39;.
+ if i % prime != 0 {
+ out &lt;- i // Send &#39;i&#39; to channel &#39;out&#39;.
+ }
+ }
+}
</pre>
<p>
The generator and filters execute concurrently. Go has
@@ -1219,37 +1220,37 @@ this starts the function running in parallel with the current
computation but in the same address space:
<p>
<pre>
- go sum(hugeArray) // calculate sum in the background
+go sum(hugeArray) // calculate sum in the background
</pre>
<p>
If you want to know when the calculation is done, pass a channel
on which it can report back:
<p>
<pre>
- ch := make(chan int)
- go sum(hugeArray, ch)
- // ... do something else for a while
- result := &lt;-ch // wait for, and retrieve, result
+ch := make(chan int)
+go sum(hugeArray, ch)
+// ... do something else for a while
+result := &lt;-ch // wait for, and retrieve, result
</pre>
<p>
Back to our prime sieve. Here's how the sieve pipeline is stitched
together:
<p>
-<pre> <!-- progs/sieve.go /func.main/ /^}/ -->
-28 func main() {
-29 ch := make(chan int) // Create a new channel.
-30 go generate(ch) // Start generate() as a goroutine.
-31 for i := 0; i &lt; 100; i++ { // Print the first hundred primes.
-32 prime := &lt;-ch
-33 fmt.Println(prime)
-34 ch1 := make(chan int)
-35 go filter(ch, ch1, prime)
-36 ch = ch1
-37 }
-38 }
-</pre>
-<p>
-Line 29 creates the initial channel to pass to <code>generate</code>, which it
+<pre><!-- progs/sieve.go /func.main/ /^}/
+-->func main() {
+ ch := make(chan int) // Create a new channel.
+ go generate(ch) // Start generate() as a goroutine.
+ for i := 0; i &lt; 100; i++ { // Print the first hundred primes.
+ prime := &lt;-ch
+ fmt.Println(prime)
+ ch1 := make(chan int)
+ go filter(ch, ch1, prime)
+ ch = ch1
+ }
+}
+</pre>
+<p>
+The first line of <code>main</code> creates the initial channel to pass to <code>generate</code>, which it
then starts up. As each prime pops out of the channel, a new <code>filter</code>
is added to the pipeline and <i>its</i> output becomes the new value
of <code>ch</code>.
@@ -1258,16 +1259,16 @@ The sieve program can be tweaked to use a pattern common
in this style of programming. Here is a variant version
of <code>generate</code>, from <code>progs/sieve1.go</code>:
<p>
-<pre> <!-- progs/sieve1.go /func.generate/ /^}/ -->
-10 func generate() chan int {
-11 ch := make(chan int)
-12 go func(){
-13 for i := 2; ; i++ {
-14 ch &lt;- i
-15 }
-16 }()
-17 return ch
-18 }
+<pre><!-- progs/sieve1.go /func.generate/ /^}/
+-->func generate() chan int {
+ ch := make(chan int)
+ go func(){
+ for i := 2; ; i++ {
+ ch &lt;- i
+ }
+ }()
+ return ch
+}
</pre>
<p>
This version does all the setup internally. It creates the output
@@ -1275,54 +1276,54 @@ channel, launches a goroutine running a function literal, and
returns the channel to the caller. It is a factory for concurrent
execution, starting the goroutine and returning its connection.
<p>
-The function literal notation (lines 12-16) allows us to construct an
+The function literal notation used in the <code>go</code> statement allows us to construct an
anonymous function and invoke it on the spot. Notice that the local
variable <code>ch</code> is available to the function literal and lives on even
after <code>generate</code> returns.
<p>
The same change can be made to <code>filter</code>:
<p>
-<pre> <!-- progs/sieve1.go /func.filter/ /^}/ -->
-21 func filter(in chan int, prime int) chan int {
-22 out := make(chan int)
-23 go func() {
-24 for {
-25 if i := &lt;-in; i % prime != 0 {
-26 out &lt;- i
-27 }
-28 }
-29 }()
-30 return out
-31 }
+<pre><!-- progs/sieve1.go /func.filter/ /^}/
+-->func filter(in chan int, prime int) chan int {
+ out := make(chan int)
+ go func() {
+ for {
+ if i := &lt;-in; i % prime != 0 {
+ out &lt;- i
+ }
+ }
+ }()
+ return out
+}
</pre>
<p>
The <code>sieve</code> function's main loop becomes simpler and clearer as a
result, and while we're at it let's turn it into a factory too:
<p>
-<pre> <!-- progs/sieve1.go /func.sieve/ /^}/ -->
-33 func sieve() chan int {
-34 out := make(chan int)
-35 go func() {
-36 ch := generate()
-37 for {
-38 prime := &lt;-ch
-39 out &lt;- prime
-40 ch = filter(ch, prime)
-41 }
-42 }()
-43 return out
-44 }
+<pre><!-- progs/sieve1.go /func.sieve/ /^}/
+-->func sieve() chan int {
+ out := make(chan int)
+ go func() {
+ ch := generate()
+ for {
+ prime := &lt;-ch
+ out &lt;- prime
+ ch = filter(ch, prime)
+ }
+ }()
+ return out
+}
</pre>
<p>
Now <code>main</code>'s interface to the prime sieve is a channel of primes:
<p>
-<pre> <!-- progs/sieve1.go /func.main/ /^}/ -->
-46 func main() {
-47 primes := sieve()
-48 for i := 0; i &lt; 100; i++ { // Print the first hundred primes.
-49 fmt.Println(&lt;-primes)
-50 }
-51 }
+<pre><!-- progs/sieve1.go /func.main/ /^}/
+-->func main() {
+ primes := sieve()
+ for i := 0; i &lt; 100; i++ { // Print the first hundred primes.
+ fmt.Println(&lt;-primes)
+ }
+}
</pre>
<p>
<h2>Multiplexing</h2>
@@ -1334,102 +1335,102 @@ A realistic client-server program is a lot of code, so here is a very simple sub
to illustrate the idea. It starts by defining a <code>request</code> type, which embeds a channel
that will be used for the reply.
<p>
-<pre> <!-- progs/server.go /type.request/ /^}/ -->
-09 type request struct {
-10 a, b int
-11 replyc chan int
-12 }
+<pre><!-- progs/server.go /type.request/ /^}/
+-->type request struct {
+ a, b int
+ replyc chan int
+}
</pre>
<p>
The server will be trivial: it will do simple binary operations on integers. Here's the
code that invokes the operation and responds to the request:
<p>
-<pre> <!-- progs/server.go /type.binOp/ /^}/ -->
-14 type binOp func(a, b int) int
+<pre><!-- progs/server.go /type.binOp/ /^}/
+-->type binOp func(a, b int) int
-16 func run(op binOp, req *request) {
-17 reply := op(req.a, req.b)
-18 req.replyc &lt;- reply
-19 }
+func run(op binOp, req *request) {
+ reply := op(req.a, req.b)
+ req.replyc &lt;- reply
+}
</pre>
<p>
-Line 14 defines the name <code>binOp</code> to be a function taking two integers and
+The type declaration makes <code>binOp</code> represent a function taking two integers and
returning a third.
<p>
The <code>server</code> routine loops forever, receiving requests and, to avoid blocking due to
a long-running operation, starting a goroutine to do the actual work.
<p>
-<pre> <!-- progs/server.go /func.server/ /^}/ -->
-21 func server(op binOp, service chan *request) {
-22 for {
-23 req := &lt;-service
-24 go run(op, req) // don't wait for it
-25 }
-26 }
+<pre><!-- progs/server.go /func.server/ /^}/
+-->func server(op binOp, service chan *request) {
+ for {
+ req := &lt;-service
+ go run(op, req) // don't wait for it
+ }
+}
</pre>
<p>
We construct a server in a familiar way, starting it and returning a channel
connected to it:
<p>
-<pre> <!-- progs/server.go /func.startServer/ /^}/ -->
-28 func startServer(op binOp) chan *request {
-29 req := make(chan *request)
-30 go server(op, req)
-31 return req
-32 }
+<pre><!-- progs/server.go /func.startServer/ /^}/
+-->func startServer(op binOp) chan *request {
+ req := make(chan *request)
+ go server(op, req)
+ return req
+}
</pre>
<p>
Here's a simple test. It starts a server with an addition operator and sends out
<code>N</code> requests without waiting for the replies. Only after all the requests are sent
does it check the results.
<p>
-<pre> <!-- progs/server.go /func.main/ /^}/ -->
-34 func main() {
-35 adder := startServer(func(a, b int) int { return a + b })
-36 const N = 100
-37 var reqs [N]request
-38 for i := 0; i &lt; N; i++ {
-39 req := &amp;reqs[i]
-40 req.a = i
-41 req.b = i + N
-42 req.replyc = make(chan int)
-43 adder &lt;- req
-44 }
-45 for i := N-1; i &gt;= 0; i-- { // doesn't matter what order
-46 if &lt;-reqs[i].replyc != N + 2*i {
-47 fmt.Println(&quot;fail at&quot;, i)
-48 }
-49 }
-50 fmt.Println(&quot;done&quot;)
-51 }
+<pre><!-- progs/server.go /func.main/ /^}/
+-->func main() {
+ adder := startServer(func(a, b int) int { return a + b })
+ const N = 100
+ var reqs [N]request
+ for i := 0; i &lt; N; i++ {
+ req := &amp;reqs[i]
+ req.a = i
+ req.b = i + N
+ req.replyc = make(chan int)
+ adder &lt;- req
+ }
+ for i := N-1; i &gt;= 0; i-- { // doesn&#39;t matter what order
+ if &lt;-reqs[i].replyc != N + 2*i {
+ fmt.Println(&#34;fail at&#34;, i)
+ }
+ }
+ fmt.Println(&#34;done&#34;)
+}
</pre>
<p>
One annoyance with this program is that it doesn't shut down the server cleanly; when <code>main</code> returns
there are a number of lingering goroutines blocked on communication. To solve this,
we can provide a second, <code>quit</code> channel to the server:
<p>
-<pre> <!-- progs/server1.go /func.startServer/ /^}/ -->
-32 func startServer(op binOp) (service chan *request, quit chan bool) {
-33 service = make(chan *request)
-34 quit = make(chan bool)
-35 go server(op, service, quit)
-36 return service, quit
-37 }
+<pre><!-- progs/server1.go /func.startServer/ /^}/
+-->func startServer(op binOp) (service chan *request, quit chan bool) {
+ service = make(chan *request)
+ quit = make(chan bool)
+ go server(op, service, quit)
+ return service, quit
+}
</pre>
<p>
It passes the quit channel to the <code>server</code> function, which uses it like this:
<p>
-<pre> <!-- progs/server1.go /func.server/ /^}/ -->
-21 func server(op binOp, service chan *request, quit chan bool) {
-22 for {
-23 select {
-24 case req := &lt;-service:
-25 go run(op, req) // don't wait for it
-26 case &lt;-quit:
-27 return
-28 }
-29 }
-30 }
+<pre><!-- progs/server1.go /func.server/ /^}/
+-->func server(op binOp, service chan *request, quit chan bool) {
+ for {
+ select {
+ case req := &lt;-service:
+ go run(op, req) // don't wait for it
+ case &lt;-quit:
+ return
+ }
+ }
+}
</pre>
<p>
Inside <code>server</code>, the <code>select</code> statement chooses which of the multiple communications
@@ -1442,12 +1443,12 @@ returns, terminating its execution.
All that's left is to strobe the <code>quit</code> channel
at the end of main:
<p>
-<pre> <!-- progs/server1.go /adder,.quit/ -->
-40 adder, quit := startServer(func(a, b int) int { return a + b })
+<pre><!-- progs/server1.go /adder,.quit/
+--> adder, quit := startServer(func(a, b int) int { return a + b })
</pre>
...
-<pre> <!-- progs/server1.go /quit....true/ -->
-55 quit &lt;- true
+<pre><!-- progs/server1.go /quit....true/
+--> quit &lt;- true
</pre>
<p>
There's a lot more to Go programming and concurrent programming in general but this
diff --git a/doc/go_tutorial.txt b/doc/go_tutorial.txt
index 7e2bc7c4b..17ef6eee9 100644
--- a/doc/go_tutorial.txt
+++ b/doc/go_tutorial.txt
@@ -28,7 +28,7 @@ Hello, World
Let's start in the usual way:
---PROG progs/helloworld.go /package/ END
+!src progs/helloworld.go /package/ $
Every Go source file declares, using a "package" statement, which package it's part of.
It may also import other packages to use their facilities.
@@ -107,13 +107,13 @@ Echo
Next up, here's a version of the Unix utility "echo(1)":
---PROG progs/echo.go /package/ END
+!src progs/echo.go /package/ $
This program is small but it's doing a number of new things. In the last example,
we saw "func" introduce a function. The keywords "var", "const", and "type"
(not used yet) also introduce declarations, as does "import".
Notice that we can group declarations of the same sort into
-parenthesized lists, one item per line, as on lines 7-10 and 14-17.
+parenthesized lists, one item per line, as in the "import" and "const" clauses here.
But it's not necessary to do so; we could have said
const Space = " "
@@ -163,7 +163,7 @@ or we could go even shorter and write the idiom
The ":=" operator is used a lot in Go to represent an initializing declaration.
There's one in the "for" clause on the next line:
---PROG progs/echo.go /for/
+!src progs/echo.go /for/
The "flag" package has parsed the arguments and left the non-flag arguments
in a list that can be iterated over in the obvious way.
@@ -210,7 +210,7 @@ Once you've built a string <i>value</i>, you can't change it, although
of course you can change a string <i>variable</i> simply by
reassigning it. This snippet from "strings.go" is legal code:
---PROG progs/strings.go /hello/ /ciao/
+!src progs/strings.go /hello/ /ciao/
However the following statements are illegal because they would modify
a "string" value:
@@ -269,7 +269,7 @@ will slice the whole array.
Using slices one can write this function (from "sum.go"):
---PROG progs/sum.go /sum/ /^}/
+!src progs/sum.go /sum/ /^}/
Note how the return type ("int") is defined for "sum" by stating it
after the parameter list.
@@ -386,7 +386,7 @@ An I/O Package
Next we'll look at a simple package for doing file I/O with an
open/close/read/write interface. Here's the start of "file.go":
---PROG progs/file.go /package/ /^}/
+!src progs/file.go /package/ /^}/
The first few lines declare the name of the
package&mdash;"file"&mdash;and then import two packages. The "os"
@@ -416,7 +416,7 @@ will soon give it some exported, upper-case methods.
First, though, here is a factory to create a "File":
---PROG progs/file.go /newFile/ /^}/
+!src progs/file.go /newFile/ /^}/
This returns a pointer to a new "File" structure with the file descriptor and name
filled in. This code uses Go's notion of a ''composite literal'', analogous to
@@ -433,12 +433,12 @@ composite literal, as is done here on line 21.
We can use the factory to construct some familiar, exported variables of type "*File":
---PROG progs/file.go /var/ /^.$/
+!src progs/file.go /var/ /^.$/
The "newFile" function was not exported because it's internal. The proper,
exported factory to use is "OpenFile" (we'll explain that name in a moment):
---PROG progs/file.go /func.OpenFile/ /^}/
+!src progs/file.go /func.OpenFile/ /^}/
There are a number of new things in these few lines. First, "OpenFile" returns
multiple values, a "File" and an error (more about errors in a moment).
@@ -468,9 +468,9 @@ the implementation of our "Open" and "Create"; they're trivial
wrappers that eliminate common errors by capturing
the tricky standard arguments to open and, especially, to create a file:
---PROG progs/file.go /^const/ /^}/
+!src progs/file.go /^const/ /^}/
---PROG progs/file.go /func.Create/ /^}/
+!src progs/file.go /func.Create/ /^}/
Back to our main story.
Now that we can build "Files", we can write methods for them. To declare
@@ -479,7 +479,7 @@ of that type, placed
in parentheses before the function name. Here are some methods for "*File",
each of which declares a receiver variable "file".
---PROG progs/file.go /Close/ END
+!src progs/file.go /Close/ $
There is no implicit "this" and the receiver variable must be used to access
members of the structure. Methods are not declared within
@@ -496,7 +496,7 @@ set of such error values.
We can now use our new package:
---PROG progs/helloworld3.go /package/ END
+!src progs/helloworld3.go /package/ $
The ''"./"'' in the import of ''"./file"'' tells the compiler
to use our own package rather than
@@ -520,12 +520,12 @@ Rotting cats
Building on the "file" package, here's a simple version of the Unix utility "cat(1)",
"progs/cat.go":
---PROG progs/cat.go /package/ END
+!src progs/cat.go /package/ $
By now this should be easy to follow, but the "switch" statement introduces some
new features. Like a "for" loop, an "if" or "switch" can include an
-initialization statement. The "switch" on line 18 uses one to create variables
-"nr" and "er" to hold the return values from the call to "f.Read". (The "if" on line 25
+initialization statement. The "switch" statement in "cat" uses one to create variables
+"nr" and "er" to hold the return values from the call to "f.Read". (The "if" a few lines later
has the same idea.) The "switch" statement is general: it evaluates the cases
from top to bottom looking for the first case that matches the value; the
case expressions don't need to be constants or even integers, as long as
@@ -537,7 +537,7 @@ in a "for" statement, a missing value means "true". In fact, such a "switch"
is a form of "if-else" chain. While we're here, it should be mentioned that in
"switch" statements each "case" has an implicit "break".
-Line 25 calls "Write" by slicing the incoming buffer, which is itself a slice.
+The argument to "file.Stdout.Write" is created by slicing the array "buf".
Slices provide the standard Go way to handle I/O buffers.
Now let's make a variant of "cat" that optionally does "rot13" on its input.
@@ -548,7 +548,7 @@ The "cat" subroutine uses only two methods of "f": "Read" and "String",
so let's start by defining an interface that has exactly those two methods.
Here is code from "progs/cat_rot13.go":
---PROG progs/cat_rot13.go /type.reader/ /^}/
+!src progs/cat_rot13.go /type.reader/ /^}/
Any type that has the two methods of "reader"&mdash;regardless of whatever
other methods the type may also have&mdash;is said to <i>implement</i> the
@@ -560,34 +560,32 @@ existing "reader" and does "rot13" on the data. To do this, we just define
the type and implement the methods and with no other bookkeeping,
we have a second implementation of the "reader" interface.
---PROG progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
+!src progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
-(The "rot13" function called on line 42 is trivial and not worth reproducing here.)
+(The "rot13" function called in "Read" is trivial and not worth reproducing here.)
To use the new feature, we define a flag:
---PROG progs/cat_rot13.go /rot13Flag/
+!src progs/cat_rot13.go /rot13Flag/
and use it from within a mostly unchanged "cat" function:
---PROG progs/cat_rot13.go /func.cat/ /^}/
+!src progs/cat_rot13.go /func.cat/ /^}/
(We could also do the wrapping in "main" and leave "cat" mostly alone, except
for changing the type of the argument; consider that an exercise.)
-Lines 56 through 58 set it all up: If the "rot13" flag is true, wrap the "reader"
+The "if" at the top of "cat" sets it all up: If the "rot13" flag is true, wrap the "reader"
we received into a "rotate13" and proceed. Note that the interface variables
are values, not pointers: the argument is of type "reader", not "*reader",
even though under the covers it holds a pointer to a "struct".
Here it is in action:
-<pre>
$ echo abcdefghijklmnopqrstuvwxyz | ./cat
abcdefghijklmnopqrstuvwxyz
$ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
nopqrstuvwxyzabcdefghijklm
$
-</pre>
Fans of dependency injection may take cheer from how easily interfaces
allow us to substitute the implementation of a file descriptor.
@@ -601,9 +599,7 @@ as we saw with "rot13". The type "file.File" implements "reader"; it could also
implement a "writer", or any other interface built from its methods that
fits the current situation. Consider the <i>empty interface</i>
-<pre>
type Empty interface {}
-</pre>
<i>Every</i> type implements the empty interface, which makes it
useful for things like containers.
@@ -618,17 +614,17 @@ same interface variable.
As an example, consider this simple sort algorithm taken from "progs/sort.go":
---PROG progs/sort.go /func.Sort/ /^}/
+!src progs/sort.go /func.Sort/ /^}/
The code needs only three methods, which we wrap into sort's "Interface":
---PROG progs/sort.go /interface/ /^}/
+!src progs/sort.go /interface/ /^}/
We can apply "Sort" to any type that implements "Len", "Less", and "Swap".
The "sort" package includes the necessary methods to allow sorting of
arrays of integers, strings, etc.; here's the code for arrays of "int"
---PROG progs/sort.go /type.*IntSlice/ /Swap/
+!src progs/sort.go /type.*IntSlice/ /Swap/
Here we see methods defined for non-"struct" types. You can define methods
for any type you define and name in your package.
@@ -637,12 +633,12 @@ And now a routine to test it out, from "progs/sortmain.go". This
uses a function in the "sort" package, omitted here for brevity,
to test that the result is sorted.
---PROG progs/sortmain.go /func.ints/ /^}/
+!src progs/sortmain.go /func.ints/ /^}/
If we have a new type we want to be able to sort, all we need to do is
to implement the three methods for that type, like this:
---PROG progs/sortmain.go /type.day/ /Swap/
+!src progs/sortmain.go /type.day/ /Swap/
Printing
@@ -675,7 +671,7 @@ argument. It's easier in many cases in Go. Instead of "%llud" you
can just say "%d"; "Printf" knows the size and signedness of the
integer and can do the right thing for you. The snippet
---PROG progs/print.go 'NR==10' 'NR==11'
+!src progs/print.go 10 11
prints
@@ -684,7 +680,7 @@ prints
In fact, if you're lazy the format "%v" will print, in a simple
appropriate style, any value, even an array or structure. The output of
---PROG progs/print.go 'NR==14' 'NR==20'
+!src progs/print.go 14 20
is
@@ -697,7 +693,7 @@ of "%v" while "Println" inserts spaces between arguments
and adds a newline. The output of each of these two lines is identical
to that of the "Printf" call above.
---PROG progs/print.go 'NR==21' 'NR==22'
+!src progs/print.go 21 22
If you have your own type you'd like "Printf" or "Print" to format,
just give it a "String" method that returns a string. The print
@@ -705,7 +701,7 @@ routines will examine the value to inquire whether it implements
the method and if so, use it rather than some other formatting.
Here's a simple example.
---PROG progs/print_string.go 'NR==9' END
+!src progs/print_string.go 9 $
Since "*testType" has a "String" method, the
default formatter for that type will use it and produce the output
@@ -803,7 +799,7 @@ coordinates the communication; as with maps and slices, use
Here is the first function in "progs/sieve.go":
---PROG progs/sieve.go /Send/ /^}/
+!src progs/sieve.go /Send/ /^}/
The "generate" function sends the sequence 2, 3, 4, 5, ... to its
argument channel, "ch", using the binary communications operator "&lt;-".
@@ -815,7 +811,7 @@ channel, and a prime number. It copies values from the input to the
output, discarding anything divisible by the prime. The unary communications
operator "&lt;-" (receive) retrieves the next value on the channel.
---PROG progs/sieve.go /Copy.the/ /^}/
+!src progs/sieve.go /Copy.the/ /^}/
The generator and filters execute concurrently. Go has
its own model of process/threads/light-weight processes/coroutines,
@@ -838,9 +834,9 @@ on which it can report back:
Back to our prime sieve. Here's how the sieve pipeline is stitched
together:
---PROG progs/sieve.go /func.main/ /^}/
+!src progs/sieve.go /func.main/ /^}/
-Line 29 creates the initial channel to pass to "generate", which it
+The first line of "main" creates the initial channel to pass to "generate", which it
then starts up. As each prime pops out of the channel, a new "filter"
is added to the pipeline and <i>its</i> output becomes the new value
of "ch".
@@ -849,30 +845,30 @@ The sieve program can be tweaked to use a pattern common
in this style of programming. Here is a variant version
of "generate", from "progs/sieve1.go":
---PROG progs/sieve1.go /func.generate/ /^}/
+!src progs/sieve1.go /func.generate/ /^}/
This version does all the setup internally. It creates the output
channel, launches a goroutine running a function literal, and
returns the channel to the caller. It is a factory for concurrent
execution, starting the goroutine and returning its connection.
-The function literal notation (lines 12-16) allows us to construct an
+The function literal notation used in the "go" statement allows us to construct an
anonymous function and invoke it on the spot. Notice that the local
variable "ch" is available to the function literal and lives on even
after "generate" returns.
The same change can be made to "filter":
---PROG progs/sieve1.go /func.filter/ /^}/
+!src progs/sieve1.go /func.filter/ /^}/
The "sieve" function's main loop becomes simpler and clearer as a
result, and while we're at it let's turn it into a factory too:
---PROG progs/sieve1.go /func.sieve/ /^}/
+!src progs/sieve1.go /func.sieve/ /^}/
Now "main"'s interface to the prime sieve is a channel of primes:
---PROG progs/sieve1.go /func.main/ /^}/
+!src progs/sieve1.go /func.main/ /^}/
Multiplexing
----
@@ -884,41 +880,41 @@ A realistic client-server program is a lot of code, so here is a very simple sub
to illustrate the idea. It starts by defining a "request" type, which embeds a channel
that will be used for the reply.
---PROG progs/server.go /type.request/ /^}/
+!src progs/server.go /type.request/ /^}/
The server will be trivial: it will do simple binary operations on integers. Here's the
code that invokes the operation and responds to the request:
---PROG progs/server.go /type.binOp/ /^}/
+!src progs/server.go /type.binOp/ /^}/
-Line 14 defines the name "binOp" to be a function taking two integers and
+The type declaration makes "binOp" represent a function taking two integers and
returning a third.
The "server" routine loops forever, receiving requests and, to avoid blocking due to
a long-running operation, starting a goroutine to do the actual work.
---PROG progs/server.go /func.server/ /^}/
+!src progs/server.go /func.server/ /^}/
We construct a server in a familiar way, starting it and returning a channel
connected to it:
---PROG progs/server.go /func.startServer/ /^}/
+!src progs/server.go /func.startServer/ /^}/
Here's a simple test. It starts a server with an addition operator and sends out
"N" requests without waiting for the replies. Only after all the requests are sent
does it check the results.
---PROG progs/server.go /func.main/ /^}/
+!src progs/server.go /func.main/ /^}/
One annoyance with this program is that it doesn't shut down the server cleanly; when "main" returns
there are a number of lingering goroutines blocked on communication. To solve this,
we can provide a second, "quit" channel to the server:
---PROG progs/server1.go /func.startServer/ /^}/
+!src progs/server1.go /func.startServer/ /^}/
It passes the quit channel to the "server" function, which uses it like this:
---PROG progs/server1.go /func.server/ /^}/
+!src progs/server1.go /func.server/ /^}/
Inside "server", the "select" statement chooses which of the multiple communications
listed by its cases can proceed. If all are blocked, it waits until one can proceed; if
@@ -930,9 +926,9 @@ returns, terminating its execution.
All that's left is to strobe the "quit" channel
at the end of main:
---PROG progs/server1.go /adder,.quit/
+!src progs/server1.go /adder,.quit/
...
---PROG progs/server1.go /quit....true/
+!src progs/server1.go /quit....true/
There's a lot more to Go programming and concurrent programming in general but this
quick tour should give you some of the basics.
diff --git a/doc/htmlgen.go b/doc/htmlgen.go
index 3a8feb8bc..5318a07dc 100644
--- a/doc/htmlgen.go
+++ b/doc/htmlgen.go
@@ -2,46 +2,80 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Process plain text into HTML.
+// If --html is set, process plain text into HTML.
// - h2's are made from lines followed by a line "----\n"
-// - tab-indented blocks become <pre> blocks
+// - tab-indented blocks become <pre> blocks with the first tab deleted
// - blank lines become <p> marks (except inside <pre> tags)
// - "quoted strings" become <code>quoted strings</code>
+// Lines beginning !src define pieces of program source to be
+// extracted from other files and injected as <pre> blocks.
+// The syntax is simple: 1, 2, or 3 space-separated arguments:
+//
+// Whole file:
+// !src foo.go
+// One line (here the signature of main):
+// !src foo.go /^func.main/
+// Block of text, determined by start and end (here the body of main):
+// !src foo.go /^func.main/ /^}/
+//
+// Patterns can be /regular.expression/, a decimal number, or $
+// to signify the end of the file.
+// TODO: the regular expression cannot contain spaces; does this matter?
+
package main
import (
"bufio"
"bytes"
+ "flag"
+ "fmt"
+ "io/ioutil"
"log"
"os"
+ "regexp"
+ "strconv"
+ "strings"
+ "template"
)
var (
- lines = make([][]byte, 0, 2000) // probably big enough; grows if not
+ html = flag.Bool("html", true, "process text into HTML")
+)
+
+var (
+ // lines holds the input and is reworked in place during processing.
+ lines = make([][]byte, 0, 20000)
empty = []byte("")
newline = []byte("\n")
tab = []byte("\t")
quote = []byte(`"`)
- indent = []byte{' ', ' ', ' ', ' '}
+ indent = []byte(" ")
sectionMarker = []byte("----\n")
preStart = []byte("<pre>")
preEnd = []byte("</pre>\n")
pp = []byte("<p>\n")
+
+ srcPrefix = []byte("!src")
)
func main() {
+ flag.Parse()
read()
- headings()
- coalesce(preStart, foldPre)
- coalesce(tab, foldTabs)
- paragraphs()
- quotes()
+ programs()
+ if *html {
+ headings()
+ coalesce(preStart, foldPre)
+ coalesce(tab, foldTabs)
+ paragraphs()
+ quotes()
+ }
write()
}
+// read turns standard input into a slice of lines.
func read() {
b := bufio.NewReader(os.Stdin)
for {
@@ -56,6 +90,7 @@ func read() {
}
}
+// write puts the result on standard output.
func write() {
b := bufio.NewWriter(os.Stdout)
for _, line := range lines {
@@ -64,8 +99,104 @@ func write() {
b.Flush()
}
-// each time prefix is found on a line, call fold and replace
-// line with return value from fold.
+// programs injects source code from !src invocations.
+func programs() {
+ nlines := make([][]byte, 0, len(lines)*3/2)
+ for _, line := range lines {
+ if bytes.HasPrefix(line, srcPrefix) {
+ line = trim(line)[len(srcPrefix):]
+ prog := srcCommand(string(line))
+ if *html {
+ nlines = append(nlines, []byte(fmt.Sprintf("<pre><!--%s\n-->", line)))
+ }
+ for _, l := range prog {
+ nlines = append(nlines, htmlEscape(l))
+ }
+ if *html {
+ nlines = append(nlines, preEnd)
+ }
+ } else {
+ nlines = append(nlines, line)
+ }
+ }
+ lines = nlines
+}
+
+// srcCommand processes one !src invocation.
+func srcCommand(command string) [][]byte {
+ // TODO: quoted args so we can have 'a b'?
+ args := strings.Fields(command)
+ if len(args) == 0 || len(args) > 3 {
+ log.Fatal("bad syntax for src command: %s", command)
+ }
+ file := args[0]
+ lines := bytes.SplitAfter(readFile(file), newline)
+ // File plus zero args: whole file:
+ // !src file.go
+ if len(args) == 1 {
+ return lines
+ }
+ start := match(file, 0, lines, string(args[1]))
+ // File plus one arg: one line:
+ // !src file.go /foo/
+ if len(args) == 2 {
+ return [][]byte{lines[start]}
+ }
+ // File plus two args: range:
+ // !src file.go /foo/ /^}/
+ end := match(file, start, lines, string(args[2]))
+ return lines[start : end+1] // +1 to include matched line.
+}
+
+// htmlEscape makes sure input is HTML clean, if necessary.
+func htmlEscape(input []byte) []byte {
+ if !*html || bytes.IndexAny(input, `&"<>`) < 0 {
+ return input
+ }
+ var b bytes.Buffer
+ template.HTMLEscape(&b, input)
+ return b.Bytes()
+}
+
+// readFile reads and returns a file as part of !src processing.
+func readFile(name string) []byte {
+ file, err := ioutil.ReadFile(name)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return file
+}
+
+// match identifies the input line that matches the pattern in a !src invocation.
+// If start>0, match lines starting there rather than at the beginning.
+func match(file string, start int, lines [][]byte, pattern string) int {
+ // $ matches the end of the file.
+ if pattern == "$" {
+ return len(lines) - 1
+ }
+ // Number matches the line.
+ if i, err := strconv.Atoi(pattern); err == nil {
+ return i - 1 // Lines are 1-indexed.
+ }
+ // /regexp/ matches the line that matches the regexp.
+ if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
+ re, err := regexp.Compile(pattern[1 : len(pattern)-1])
+ if err != nil {
+ log.Fatal(err)
+ }
+ for i := start; i < len(lines); i++ {
+ if re.Match(lines[i]) {
+ return i
+ }
+ }
+ log.Fatalf("%s: no match for %s", file, pattern)
+ }
+ log.Fatalf("unrecognized pattern: %s", pattern)
+ return 0
+}
+
+// coalesce combines lines. Each time prefix is found on a line,
+// it calls fold and replaces the line with return value from fold.
func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
j := 0 // output line number goes up by one each loop
for i := 0; i < len(lines); {
@@ -82,7 +213,7 @@ func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
lines = lines[0:j]
}
-// return the <pre> block as a single slice
+// foldPre returns the <pre> block as a single slice.
func foldPre(i int) (n int, line []byte) {
buf := new(bytes.Buffer)
for i < len(lines) {
@@ -96,7 +227,7 @@ func foldPre(i int) (n int, line []byte) {
return n, buf.Bytes()
}
-// return the tab-indented block as a single <pre>-bounded slice
+// foldTabs returns the tab-indented block as a single <pre>-bounded slice.
func foldTabs(i int) (n int, line []byte) {
buf := new(bytes.Buffer)
buf.WriteString("<pre>\n")
@@ -104,7 +235,7 @@ func foldTabs(i int) (n int, line []byte) {
if !bytes.HasPrefix(lines[i], tab) {
break
}
- buf.Write(lines[i])
+ buf.Write(lines[i][1:]) // delete leading tab.
n++
i++
}
@@ -112,6 +243,7 @@ func foldTabs(i int) (n int, line []byte) {
return n, buf.Bytes()
}
+// headings turns sections into HTML sections.
func headings() {
b := bufio.NewWriter(os.Stdout)
for i, l := range lines {
@@ -123,6 +255,7 @@ func headings() {
b.Flush()
}
+// paragraphs turns blank lines into paragraph marks.
func paragraphs() {
for i, l := range lines {
if bytes.Equal(l, newline) {
@@ -131,12 +264,14 @@ func paragraphs() {
}
}
+// quotes turns "x" in the file into <code>x</code>.
func quotes() {
for i, l := range lines {
lines[i] = codeQuotes(l)
}
}
+// quotes turns "x" in the line into <code>x</code>.
func codeQuotes(l []byte) []byte {
if bytes.HasPrefix(l, preStart) {
return l
@@ -162,7 +297,7 @@ func codeQuotes(l []byte) []byte {
return buf.Bytes()
}
-// drop trailing newline
+// trim drops the trailing newline, if present.
func trim(l []byte) []byte {
n := len(l)
if n > 0 && l[n-1] == '\n' {
@@ -171,7 +306,7 @@ func trim(l []byte) []byte {
return l
}
-// expand tabs to spaces. don't worry about columns.
+// expandTabs expands tabs to spaces. It doesn't worry about columns.
func expandTabs(l []byte) []byte {
return bytes.Replace(l, tab, indent, -1)
}
diff --git a/doc/install.html b/doc/install.html
index 2256123ec..a1bc89982 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -81,8 +81,8 @@ To build it, you need these programs installed:
<li>the standard C libraries,
<li>the parser generator Bison,
<li>GNU <tt>make</tt> (version 3.81 or later),
-<li><tt>awk</tt>, and
-<li>the text editor <tt>ed</tt>.
+and
+<li><tt>awk</tt>.
</ul>
</p>
@@ -91,7 +91,7 @@ installed as part of
<a href="http://developer.apple.com/TOOLS/Xcode/">Xcode</a>.
</p>
-<p>On Ubuntu/Debian, use <code>sudo apt-get install bison ed gawk gcc libc6-dev
+<p>On Ubuntu/Debian, use <code>sudo apt-get install bison gawk gcc libc6-dev
make</code>. If you want to build 32-bit binaries on a 64-bit system you'll
also need the <code>libc6-dev-i386</code> package.
</p>
diff --git a/doc/makehtml b/doc/makehtml
index c9ac0c8e8..1b8caed69 100755
--- a/doc/makehtml
+++ b/doc/makehtml
@@ -7,7 +7,6 @@ set -e
TXT=${1:-go_tutorial.txt} # input file
HTML=$(basename $TXT .txt).html # output file (basename)
-TMP=TEMP.txt # input to htmlgen
if ! test -w $HTML
then
@@ -15,17 +14,4 @@ then
exit 1
fi
-if grep -q '^--PROG' $TXT
-then
- echo >&2 makehtml: processing PROG sections
- <$TXT >$TMP awk '
- /^--PROG/ { system("sh ./prog.sh "$2" "$3" "$4" "); getline }
- /^/ {print}
- '
-else
- cp $TXT $TMP
-fi
-
-make htmlgen && ./htmlgen < $TMP > $HTML
-
-rm -f $TMP
+make htmlgen && ./htmlgen < $TXT > $HTML
diff --git a/doc/prog.sh b/doc/prog.sh
deleted file mode 100755
index 6a540980a..000000000
--- a/doc/prog.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/bin/sh
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# generate HTML for a program excerpt.
-# first arg is file name
-# second arg is awk pattern to match start line
-# third arg is awk pattern to stop processing
-#
-# missing third arg means print one line
-# third arg "END" means proces rest of file
-# missing second arg means process whole file
-#
-# examples:
-#
-# prog.sh foo.go # whole file
-# prog.sh foo.go "/^func.main/" # signature of main
-# prog.sh foo.go "/^func.main/" "/^}/ # body of main
-#
-# non-blank lines are annotated with line number in file
-
-# line numbers are printed %.2d to make them equal-width for nice formatting.
-# the format gives a leading 0. the format %2d gives a leading space but
-# that appears to confuse sanjay's makehtml formatter into bungling quotes
-# because it makes some lines look indented.
-
-echo "<pre> <!-- $* -->"
-
-case $# in
-3)
- if test "$3" = "END" # $2 to end of file
- then
- awk '
- function LINE() { printf("%.2d\t%s\n", NR, $0) }
- BEGIN { printing = 0 }
- '$2' { printing = 1; LINE(); getline }
- printing { if($0 ~ /./) { LINE() } else { print "" } }
- '
- else # $2 through $3
- awk '
- function LINE() { printf("%.2d\t%s\n", NR, $0) }
- BEGIN { printing = 0 }
- '$2' { printing = 1; LINE(); getline }
- '$3' && printing { if(printing) {printing = 0; LINE(); exit} }
- printing { if($0 ~ /./) { LINE() } else { print "" } }
- '
- fi
- ;;
-2) # one line
- awk '
- function LINE() { printf("%.2d\t%s\n", NR, $0) }
- '$2' { LINE(); getline; exit }
- '
- ;;
-1) # whole file
- awk '
- function LINE() { printf("%.2d\t%s\n", NR, $0) }
- { if($0 ~ /./) { LINE() } else { print "" } }
- '
- ;;
-*)
- echo >&2 usage: prog.sh file.go /func.main/ /^}/
-esac <$1 |
-sed '
- s/&/\&amp;/g
- s/"/\&quot;/g
- s/</\&lt;/g
- s/>/\&gt;/g
-'
-
-echo '</pre>'
diff --git a/doc/progs/file_windows.go b/doc/progs/file_windows.go
new file mode 100644
index 000000000..d5e7c00d3
--- /dev/null
+++ b/doc/progs/file_windows.go
@@ -0,0 +1,89 @@
+// 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 file
+
+import (
+ "os"
+ "syscall"
+)
+
+type File struct {
+ fd syscall.Handle // file descriptor number
+ name string // file name at Open time
+}
+
+func newFile(fd syscall.Handle, name string) *File {
+ if fd < 0 {
+ return nil
+ }
+ return &File{fd, name}
+}
+
+var (
+ Stdin = newFile(syscall.Stdin, "/dev/stdin")
+ Stdout = newFile(syscall.Stdout, "/dev/stdout")
+ Stderr = newFile(syscall.Stderr, "/dev/stderr")
+)
+
+func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
+ r, e := syscall.Open(name, mode, perm)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return newFile(r, name), err
+}
+
+const (
+ O_RDONLY = syscall.O_RDONLY
+ O_RDWR = syscall.O_RDWR
+ O_CREATE = syscall.O_CREAT
+ O_TRUNC = syscall.O_TRUNC
+)
+
+func Open(name string) (file *File, err os.Error) {
+ return OpenFile(name, O_RDONLY, 0)
+}
+
+func Create(name string) (file *File, err os.Error) {
+ return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
+}
+
+func (file *File) Close() os.Error {
+ if file == nil {
+ return os.EINVAL
+ }
+ e := syscall.Close(file.fd)
+ file.fd = syscall.InvalidHandle // so it can't be closed again
+ if e != 0 {
+ return os.Errno(e)
+ }
+ return nil
+}
+
+func (file *File) Read(b []byte) (ret int, err os.Error) {
+ if file == nil {
+ return -1, os.EINVAL
+ }
+ r, e := syscall.Read(file.fd, b)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return int(r), err
+}
+
+func (file *File) Write(b []byte) (ret int, err os.Error) {
+ if file == nil {
+ return -1, os.EINVAL
+ }
+ r, e := syscall.Write(file.fd, b)
+ if e != 0 {
+ err = os.Errno(e)
+ }
+ return int(r), err
+}
+
+func (file *File) String() string {
+ return file.name
+}
diff --git a/doc/progs/run b/doc/progs/run
index 241e65dfa..81781c9d2 100755
--- a/doc/progs/run
+++ b/doc/progs/run
@@ -14,8 +14,13 @@ fi
rm -f *.$O
+if [ "$GOOS" = "windows" ];then
+ $GC -o file.8 file_windows.go
+else
+ $GC file.go
+fi
+
for i in \
- file.go \
helloworld.go \
helloworld3.go \
echo.go \
diff --git a/lib/codereview/codereview.py b/lib/codereview/codereview.py
index a222919d8..385ac2c06 100644
--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -1316,7 +1316,7 @@ def clpatch_or_undo(ui, repo, clname, opts, mode):
# Create fresh CL and start with patch that would reverse the change.
vers = short(rev.node())
cl = CL("new")
- desc = rev.description()
+ desc = str(rev.description())
if mode == "undo":
cl.desc = (undoHeader % (clname, vers)) + desc + undoFooter
else:
@@ -1352,10 +1352,12 @@ def clpatch_or_undo(ui, repo, clname, opts, mode):
repo[vers].description()
except:
return "local repository is out of date; sync to get %s" % (vers)
- patch, err = portPatch(repo, patch, vers, id)
+ patch1, err = portPatch(repo, patch, vers, id)
if err != "":
- return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id)
-
+ if not opts["ignore_hgpatch_failure"]:
+ return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id)
+ else:
+ patch = patch1
argv = ["hgpatch"]
if opts["no_incoming"] or mode == "backport":
argv += ["--checksync=false"]
@@ -1369,7 +1371,7 @@ def clpatch_or_undo(ui, repo, clname, opts, mode):
return "hgpatch failed"
cl.local = True
cl.files = out.strip().split()
- if not cl.files:
+ if not cl.files and not opts["ignore_hgpatch_failure"]:
return "codereview issue %s has no changed files" % clname
files = ChangedFiles(ui, repo, [], opts)
extra = Sub(cl.files, files)
@@ -1781,7 +1783,7 @@ def sync(ui, repo, **opts):
err = commands.postincoming(ui, repo, modheads, True, "tip")
if err:
return err
- commands.update(ui, repo)
+ commands.update(ui, repo, rev="default")
sync_changes(ui, repo)
def sync_note(msg):
diff --git a/misc/dashboard/builder/http.go b/misc/dashboard/builder/http.go
index 5e1da0c87..98400c51a 100644
--- a/misc/dashboard/builder/http.go
+++ b/misc/dashboard/builder/http.go
@@ -112,16 +112,15 @@ func packages() (pkgs []string, err os.Error) {
return
}
-// updatePackage sends package build results and info to the dashboard
-func (b *Builder) updatePackage(pkg string, state bool, buildLog, info string, hash string) os.Error {
+// updatePackage sends package build results and info dashboard
+func (b *Builder) updatePackage(pkg string, ok bool, buildLog, info string) os.Error {
return dash("POST", "package", nil, param{
"builder": b.name,
"key": b.key,
"path": pkg,
- "state": strconv.Btoa(state),
+ "ok": strconv.Btoa(ok),
"log": buildLog,
"info": info,
- "go_rev": hash[:12],
})
}
diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go
index 9a714fe79..989965bc4 100644
--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -60,8 +60,9 @@ var (
)
var (
- goroot string
- releaseRegexp = regexp.MustCompile(`^(release|weekly)\.[0-9\-.]+`)
+ goroot string
+ binaryTagRe = regexp.MustCompile(`^(release\.r|weekly\.)[0-9\-.]+`)
+ releaseRe = regexp.MustCompile(`^release\.r[0-9\-.]+`)
)
func main() {
@@ -161,7 +162,7 @@ func NewBuilder(builder string) (*Builder, os.Error) {
b := &Builder{name: builder}
// get goos/goarch from builder string
- s := strings.Split(builder, "-", 3)
+ s := strings.SplitN(builder, "-", 3)
if len(s) >= 2 {
b.goos, b.goarch = s[0], s[1]
} else {
@@ -177,7 +178,7 @@ func NewBuilder(builder string) (*Builder, os.Error) {
if err != nil {
return nil, fmt.Errorf("readKeys %s (%s): %s", b.name, fn, err)
}
- v := strings.Split(string(c), "\n", -1)
+ v := strings.Split(string(c), "\n")
b.key = v[0]
if len(v) >= 3 {
b.codeUsername, b.codePassword = v[1], v[2]
@@ -200,7 +201,7 @@ func (b *Builder) buildExternal() {
log.Println("hg pull failed:", err)
continue
}
- hash, tag, err := firstTag(releaseRegexp)
+ hash, tag, err := firstTag(releaseRe)
if err != nil {
log.Println(err)
continue
@@ -321,7 +322,7 @@ func (b *Builder) buildHash(hash string) (err os.Error) {
}
// if this is a release, create tgz and upload to google code
- releaseHash, release, err := firstTag(releaseRegexp)
+ releaseHash, release, err := firstTag(binaryTagRe)
if hash == releaseHash {
// clean out build state
err = run(b.envv(), srcDir, "./clean.bash", "--nopkg")
@@ -392,7 +393,7 @@ func (b *Builder) envvWindows() []string {
skip[name] = true
}
for _, kv := range os.Environ() {
- s := strings.Split(kv, "=", 2)
+ s := strings.SplitN(kv, "=", 2)
name := strings.ToUpper(s[0])
switch {
case name == "":
@@ -591,7 +592,7 @@ func fullHash(rev string) (hash string, err os.Error) {
if s == "" {
return "", fmt.Errorf("cannot find revision")
}
- if len(s) != 20 {
+ if len(s) != 40 {
return "", fmt.Errorf("hg returned invalid hash " + s)
}
return s, nil
@@ -602,7 +603,7 @@ var revisionRe = regexp.MustCompile(`^([^ ]+) +[0-9]+:([0-9a-f]+)$`)
// firstTag returns the hash and tag of the most recent tag matching re.
func firstTag(re *regexp.Regexp) (hash string, tag string, err os.Error) {
o, _, err := runLog(nil, "", goroot, "hg", "tags")
- for _, l := range strings.Split(o, "\n", -1) {
+ for _, l := range strings.Split(o, "\n") {
if l == "" {
continue
}
@@ -615,7 +616,7 @@ func firstTag(re *regexp.Regexp) (hash string, tag string, err os.Error) {
continue
}
tag = s[1]
- hash, err = fullHash(s[3])
+ hash, err = fullHash(s[2])
return
}
err = os.NewError("no matching tag found")
diff --git a/misc/dashboard/builder/package.go b/misc/dashboard/builder/package.go
index ee65d7669..b6674428d 100644
--- a/misc/dashboard/builder/package.go
+++ b/misc/dashboard/builder/package.go
@@ -10,35 +10,47 @@ import (
"go/token"
"log"
"os"
- "path"
+ "path/filepath"
+ "strings"
)
+const MaxCommentLength = 500 // App Engine won't store more in a StringProperty.
+
func (b *Builder) buildPackages(workpath string, hash string) os.Error {
pkgs, err := packages()
if err != nil {
return err
}
for _, p := range pkgs {
- goroot := path.Join(workpath, "go")
- goinstall := path.Join(goroot, "bin", "goinstall")
+ goroot := filepath.Join(workpath, "go")
+ gobin := filepath.Join(goroot, "bin")
+ goinstall := filepath.Join(gobin, "goinstall")
envv := append(b.envv(), "GOROOT="+goroot)
+ // add GOBIN to path
+ for i, v := range envv {
+ if strings.HasPrefix(v, "PATH=") {
+ p := filepath.SplitList(v[5:])
+ p = append([]string{gobin}, p...)
+ s := strings.Join(p, string(filepath.ListSeparator))
+ envv[i] = "PATH=" + s
+ }
+ }
+
// goinstall
- buildLog, code, err := runLog(envv, "", goroot, goinstall, p)
+ buildLog, code, err := runLog(envv, "", goroot, goinstall, "-log=false", p)
if err != nil {
log.Printf("goinstall %v: %v", p, err)
- continue
}
- built := code != 0
// get doc comment from package source
- info, err := packageComment(p, path.Join(goroot, "pkg", p))
+ info, err := packageComment(p, filepath.Join(goroot, "src", "pkg", p))
if err != nil {
- log.Printf("goinstall %v: %v", p, err)
+ log.Printf("packageComment %v: %v", p, err)
}
// update dashboard with build state + info
- err = b.updatePackage(p, built, buildLog, info, hash)
+ err = b.updatePackage(p, code == 0, buildLog, info)
if err != nil {
log.Printf("updatePackage %v: %v", p, err)
}
@@ -46,9 +58,15 @@ func (b *Builder) buildPackages(workpath string, hash string) os.Error {
return nil
}
+func isGoFile(fi *os.FileInfo) bool {
+ return fi.IsRegular() && // exclude directories
+ !strings.HasPrefix(fi.Name, ".") && // ignore .files
+ filepath.Ext(fi.Name) == ".go"
+}
+
func packageComment(pkg, pkgpath string) (info string, err os.Error) {
fset := token.NewFileSet()
- pkgs, err := parser.ParseDir(fset, pkgpath, nil, parser.PackageClauseOnly|parser.ParseComments)
+ pkgs, err := parser.ParseDir(fset, pkgpath, isGoFile, parser.PackageClauseOnly|parser.ParseComments)
if err != nil {
return
}
@@ -62,5 +80,15 @@ func packageComment(pkg, pkgpath string) (info string, err os.Error) {
pdoc := doc.NewPackageDoc(pkgs[name], pkg)
info = pdoc.Doc
}
+ // grab only first paragraph
+ if parts := strings.SplitN(info, "\n\n", 2); len(parts) > 1 {
+ info = parts[0]
+ }
+ // replace newlines with spaces
+ info = strings.Replace(info, "\n", " ", -1)
+ // truncate
+ if len(info) > MaxCommentLength {
+ info = info[:MaxCommentLength]
+ }
return
}
diff --git a/misc/dashboard/godashboard/auth.py b/misc/dashboard/godashboard/auth.py
new file mode 100644
index 000000000..73a54c0d4
--- /dev/null
+++ b/misc/dashboard/godashboard/auth.py
@@ -0,0 +1,13 @@
+# 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.
+
+import hmac
+
+# local imports
+import key
+
+def auth(req):
+ k = req.get('key')
+ return k == hmac.new(key.accessKey, req.get('builder')).hexdigest() or k == key.accessKey
+
diff --git a/misc/dashboard/godashboard/gobuild.py b/misc/dashboard/godashboard/gobuild.py
index 5678f2e1b..685dc83a9 100644
--- a/misc/dashboard/godashboard/gobuild.py
+++ b/misc/dashboard/godashboard/gobuild.py
@@ -14,14 +14,13 @@ from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
import datetime
import hashlib
-import hmac
import logging
import os
import re
import bz2
# local imports
-import key
+from auth import auth
import const
# The majority of our state are commit objects. One of these exists for each of
@@ -142,10 +141,6 @@ class DashboardHandler(webapp.RequestHandler):
simplejson.dump(obj, self.response.out)
return
-def auth(req):
- k = req.get('key')
- return k == hmac.new(key.accessKey, req.get('builder')).hexdigest() or k == key.accessKey
-
# Todo serves /todo. It tells the builder which commits need to be built.
class Todo(DashboardHandler):
def get(self):
diff --git a/misc/dashboard/godashboard/index.yaml b/misc/dashboard/godashboard/index.yaml
index 4a00c4a6f..f39299d5d 100644
--- a/misc/dashboard/godashboard/index.yaml
+++ b/misc/dashboard/godashboard/index.yaml
@@ -49,4 +49,3 @@ indexes:
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.
-
diff --git a/misc/dashboard/godashboard/package.html b/misc/dashboard/godashboard/package.html
index 9332b5a79..043080b5b 100644
--- a/misc/dashboard/godashboard/package.html
+++ b/misc/dashboard/godashboard/package.html
@@ -19,37 +19,43 @@
Packages listed on this page are written by third parties and
may or may not build or be safe to use.
</p>
+
+ <p>
+ An "ok" in the <b>build</b> column indicates that the package is
+ <a href="http://golang.org/cmd/goinstall/">goinstallable</a>
+ with the latest
+ <a href="http://golang.org/doc/devel/release.html">release</a> of Go.
+ </p>
+
+ <p>
+ The <b>info</b> column shows the first paragraph from the
+ <a href="http://blog.golang.org/2011/03/godoc-documenting-go-code.html">package doc comment</a>.
+ </p>
<h2>Recently Installed Packages</h2>
<table class="alternate" cellpadding="0" cellspacing="0">
- <tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr>
+ <tr><th>last install</th><th>count</th><th>build</th><th>path</th><th>info</th></tr>
{% for r in by_time %}
<tr>
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
<td class="count">{{r.count}}</td>
+ <td class="ok">{% if r.ok %}<a title="{{r.last_ok|date:"Y-M-d H:i"}}">ok</a>{% else %}&nbsp;{% endif %}</td>
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
- <td class="project">
- {% for p in r.project_set %}
- <a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}}
- {% endfor %}
- </td>
+ <td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
</tr>
{% endfor %}
</table>
<h2>Most Installed Packages</h2>
<table class="alternate" cellpadding="0" cellspacing="0">
- <tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr>
+ <tr><th>last install</th><th>count</th><th>build</th><th>path</th><th>info</th></tr>
{% for r in by_count %}
<tr>
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
<td class="count">{{r.count}}</td>
+ <td class="ok">{% if r.ok %}<a title="{{r.last_ok|date:"Y-M-d H:i"}}">ok</a>{% else %}&nbsp;{% endif %}</td>
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
- <td class="project">
- {% for p in r.project_set %}
- <a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}}
- {% endfor %}
- </td>
+ <td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
</tr>
{% endfor %}
</table>
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py
index dd09593ac..316f3867f 100644
--- a/misc/dashboard/godashboard/package.py
+++ b/misc/dashboard/godashboard/package.py
@@ -23,6 +23,7 @@ import sets
# local imports
import toutf8
import const
+from auth import auth
template.register_template_library('toutf8')
@@ -34,6 +35,11 @@ class Package(db.Model):
count = db.IntegerProperty()
last_install = db.DateTimeProperty()
+ # data contributed by gobuilder
+ info = db.StringProperty()
+ ok = db.BooleanProperty()
+ last_ok = db.DateTimeProperty()
+
class Project(db.Model):
name = db.StringProperty(indexed=True)
descr = db.StringProperty()
@@ -43,22 +49,25 @@ class Project(db.Model):
tags = db.ListProperty(str)
approved = db.BooleanProperty(indexed=True)
-re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
-re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$')
+re_bitbucket = re.compile(r'^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-zA-Z0-9_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$')
+re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)(/[a-z0-9A-Z_.\-/]+)?$')
re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)+$')
re_launchpad = re.compile(r'^launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$')
-
def vc_to_web(path):
if re_bitbucket.match(path):
- check_url = 'http://' + path + '/?cmd=heads'
- web = 'http://' + path + '/'
+ m = re_bitbucket.match(path)
+ check_url = 'http://' + m.group(1) + '/?cmd=heads'
+ web = 'http://' + m.group(1) + '/'
elif re_github.match(path):
m = re_github_web.match(path)
check_url = 'https://raw.github.com/' + m.group(1) + '/' + m.group(2) + '/master/'
- web = 'http://github.com/' + m.group(1) + '/' + m.group(2)
+ web = 'http://github.com/' + m.group(1) + '/' + m.group(2) + '/'
elif re_googlecode.match(path):
+ m = re_googlecode.match(path)
check_url = 'http://'+path
+ if not m.group(2): # append / after bare '/hg'
+ check_url += '/'
web = 'http://code.google.com/p/' + path[:path.index('.')]
elif re_launchpad.match(path):
check_url = web = 'https://'+path
@@ -142,8 +151,7 @@ class PackagePage(webapp.RequestHandler):
def can_get_url(self, url):
try:
- req = urllib2.Request(url)
- response = urllib2.urlopen(req)
+ urllib2.urlopen(urllib2.Request(url))
return True
except:
return False
@@ -173,15 +181,23 @@ class PackagePage(webapp.RequestHandler):
return False
p = Package(key_name = key, path = path, count = 0, web_url = web)
+ # is this the builder updating package metadata?
+ if auth(self.request):
+ p.info = self.request.get('info')
+ p.ok = self.request.get('ok') == "true"
+ if p.ok:
+ p.last_ok = datetime.datetime.utcnow()
+ else:
+ p.count += 1
+ p.last_install = datetime.datetime.utcnow()
+
# update package object
- p.count += 1
- p.last_install = datetime.datetime.utcnow()
p.put()
return True
def post(self):
path = self.request.get('path')
- ok = self.record_pkg(path)
+ ok = db.run_in_transaction(self.record_pkg, path)
if ok:
self.response.set_status(200)
self.response.out.write('ok')
diff --git a/misc/dashboard/godashboard/static/style.css b/misc/dashboard/godashboard/static/style.css
index 481af36d7..a7e61dda5 100644
--- a/misc/dashboard/godashboard/static/style.css
+++ b/misc/dashboard/godashboard/static/style.css
@@ -52,7 +52,7 @@ table.alternate tr td:last-child {
padding-right: 0;
}
table.alternate tr:nth-child(2n) {
- background-color: #f8f8f8;
+ background-color: #f0f0f0;
}
span.hash {
font-family: monospace;
@@ -62,10 +62,19 @@ span.hash {
td.date {
color: #aaa;
}
-td.result {
+td.ok {
text-align: center;
+ color: #060;
+ font-weight: bold;
+}
+td.ok a {
+ cursor: help;
+}
+th {
+ text-align: left;
}
th.builder {
+ text-align: center;
font-weight: bold;
}
a.fail {
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index 03f0a2a8b..ba7f72397 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -69,8 +69,8 @@
some syntax analysis.")
(defvar go-mode-font-lock-keywords
- (let ((builtins '("cap" "close" "closed" "len" "make" "new"
- "panic" "panicln" "print" "println"))
+ (let ((builtins '("append" "cap" "close" "complex" "copy" "imag" "len"
+ "make" "new" "panic" "print" "println" "real" "recover"))
(constants '("nil" "true" "false" "iota"))
(type-name "\\s *\\(?:[*(]\\s *\\)*\\(?:\\w+\\s *\\.\\s *\\)?\\(\\w+\\)")
)
diff --git a/misc/vim/ftdetect/gofiletype.vim b/misc/vim/ftdetect/gofiletype.vim
index 884312160..f03a1d8dc 100644
--- a/misc/vim/ftdetect/gofiletype.vim
+++ b/misc/vim/ftdetect/gofiletype.vim
@@ -1 +1 @@
-au BufRead,BufNewFile *.go set filetype=go
+au BufReadPre,BufNewFile *.go set filetype=go fileencoding=utf-8 fileencodings=utf-8
diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h
index 549e0c88a..ff6d51916 100644
--- a/src/cmd/5c/gc.h
+++ b/src/cmd/5c/gc.h
@@ -28,7 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-
+#include <u.h>
#include "../cc/cc.h"
#include "../5l/5.out.h"
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index 4afed2b80..2c9e50d00 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -68,9 +68,6 @@ enum {
ElfStrText,
ElfStrData,
ElfStrBss,
- ElfStrGosymcounts,
- ElfStrGosymtab,
- ElfStrGopclntab,
ElfStrSymtab,
ElfStrStrtab,
ElfStrShstrtab,
@@ -160,12 +157,11 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
- addstring(shstrtab, ".rodata");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
+ addstring(shstrtab, ".rodata");
+ addstring(shstrtab, ".gosymtab");
+ addstring(shstrtab, ".gopclntab");
if(!debug['s']) {
- elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts");
- elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab");
- elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab");
elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab");
elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab");
}
@@ -307,10 +303,11 @@ asmb(void)
seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
codeblk(sect->vaddr, sect->len);
- /* output read-only data in text segment */
- sect = segtext.sect->next;
- seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
- datblk(sect->vaddr, sect->len);
+ /* output read-only data in text segment (rodata, gosymtab and pclntab) */
+ for(sect = sect->next; sect != nil; sect = sect->next) {
+ seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
+ datblk(sect->vaddr, sect->len);
+ }
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
@@ -572,18 +569,6 @@ asmb(void)
elfshbits(sect);
if (!debug['s']) {
- sh = newElfShdr(elfstr[ElfStrGosymtab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("symtab", 0));
-
- sh = newElfShdr(elfstr[ElfStrGopclntab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("pclntab", 0));
-
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
diff --git a/src/cmd/5l/mkenam b/src/cmd/5l/mkenam
index 265cb9988..6cccb0263 100644
--- a/src/cmd/5l/mkenam
+++ b/src/cmd/5l/mkenam
@@ -28,18 +28,18 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-ed - ../5l/5.out.h <<'!'
-v/^ A/d
-,s/^ A/ "/
-g/ .*$/s///
-,s/,*$/",/
-1i
-char* anames[] =
-{
-.
-$a
-};
-.
-w enam.c
-Q
-!
+awk '
+BEGIN {
+ print "char* anames[] ="
+ print "{"
+}
+
+/^ A/ {
+ name=$1
+ sub(/,/, "", name)
+ sub(/^A/, "", name)
+ print "\t\"" name "\","
+}
+
+END { print "};" }
+' ../5l/5.out.h >enam.c
diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h
index 735cd8909..775d97281 100644
--- a/src/cmd/6c/gc.h
+++ b/src/cmd/6c/gc.h
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "../cc/cc.h"
#include "../6l/6.out.h"
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 4c04112b7..9136e0379 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -87,9 +87,6 @@ enum {
ElfStrText,
ElfStrData,
ElfStrBss,
- ElfStrGosymcounts,
- ElfStrGosymtab,
- ElfStrGopclntab,
ElfStrShstrtab,
ElfStrSymtab,
ElfStrStrtab,
@@ -571,10 +568,9 @@ doelf(void)
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
addstring(shstrtab, ".elfdata");
addstring(shstrtab, ".rodata");
+ addstring(shstrtab, ".gosymtab");
+ addstring(shstrtab, ".gopclntab");
if(!debug['s']) {
- elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts");
- elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab");
- elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab");
elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab");
elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab");
dwarfaddshstrings(shstrtab);
@@ -718,10 +714,11 @@ asmb(void)
seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
codeblk(sect->vaddr, sect->len);
- /* output read-only data in text segment */
- sect = segtext.sect->next;
- seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
- datblk(sect->vaddr, sect->len);
+ /* output read-only data in text segment (rodata, gosymtab and pclntab) */
+ for(sect = sect->next; sect != nil; sect = sect->next) {
+ seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
+ datblk(sect->vaddr, sect->len);
+ }
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
@@ -1013,18 +1010,6 @@ asmb(void)
elfshbits(sect);
if (!debug['s']) {
- sh = newElfShdr(elfstr[ElfStrGosymtab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("symtab", 0));
-
- sh = newElfShdr(elfstr[ElfStrGopclntab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("pclntab", 0));
-
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
diff --git a/src/cmd/6l/mkenam b/src/cmd/6l/mkenam
index 5cabb2633..3001dbe93 100644
--- a/src/cmd/6l/mkenam
+++ b/src/cmd/6l/mkenam
@@ -28,18 +28,18 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-/bin/ed - ../6l/6.out.h <<'!'
-v/^ A/d
-,s/^ A/ "/
-g/ .*$/s///
-,s/,*$/",/
-1i
-char* anames[] =
-{
-.
-$a
-};
-.
-w enam.c
-Q
-!
+awk '
+BEGIN {
+ print "char* anames[] ="
+ print "{"
+}
+
+/^ A/ {
+ name=$1
+ sub(/,/, "", name)
+ sub(/^A/, "", name)
+ print "\t\"" name "\","
+}
+
+END { print "};" }
+' ../6l/6.out.h >enam.c
diff --git a/src/cmd/8a/a.h b/src/cmd/8a/a.h
index 3cb30f4c2..c5c22d7ba 100644
--- a/src/cmd/8a/a.h
+++ b/src/cmd/8a/a.h
@@ -28,8 +28,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include <u.h>
-#include <libc.h>
#include <bio.h>
#include "../8l/8.out.h"
@@ -57,7 +55,9 @@ typedef struct Gen2 Gen2;
#define NSYMB 500
#define BUFSIZ 8192
#define HISTSZ 20
+#ifndef EOF
#define EOF (-1)
+#endif
#define IGN (-2)
#define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
#define NHASH 503
diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y
index 04662f83d..a8ac773da 100644
--- a/src/cmd/8a/a.y
+++ b/src/cmd/8a/a.y
@@ -29,7 +29,9 @@
// THE SOFTWARE.
%{
+#include <u.h>
#include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */
+#include <libc.h>
#include "a.h"
%}
%union {
diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c
index 078861877..ab4de417a 100644
--- a/src/cmd/8a/lex.c
+++ b/src/cmd/8a/lex.c
@@ -29,9 +29,10 @@
// THE SOFTWARE.
#define EXTERN
+#include <u.h>
+#include <libc.h>
#include "a.h"
#include "y.tab.h"
-#include <ctype.h>
enum
{
diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h
index 9fead60e4..32b80e995 100644
--- a/src/cmd/8c/gc.h
+++ b/src/cmd/8c/gc.h
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "../cc/cc.h"
#include "../8l/8.out.h"
@@ -400,6 +401,7 @@ void shiftit(Type*, Node*, Node*);
#pragma varargck type "A" int
#pragma varargck type "B" Bits
#pragma varargck type "D" Adr*
+#pragma varargck type "lD" Adr*
#pragma varargck type "P" Prog*
#pragma varargck type "R" int
#pragma varargck type "S" char*
diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c
index d07a5439c..769ef2c66 100644
--- a/src/cmd/8c/swt.c
+++ b/src/cmd/8c/swt.c
@@ -237,10 +237,10 @@ outcode(void)
Bprint(&b, "\n");
Bprint(&b, "$$ // exports\n\n");
Bprint(&b, "$$ // local types\n\n");
- Bprint(&b, "$$ // dynimport\n", thestring);
+ Bprint(&b, "$$ // dynimport\n");
for(i=0; i<ndynimp; i++)
Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path);
- Bprint(&b, "\n$$ // dynexport\n", thestring);
+ Bprint(&b, "\n$$ // dynexport\n");
for(i=0; i<ndynexp; i++)
Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote);
Bprint(&b, "\n$$\n\n");
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index a9a720af1..e1ccfb8a3 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -83,9 +83,6 @@ enum {
ElfStrText,
ElfStrData,
ElfStrBss,
- ElfStrGosymcounts,
- ElfStrGosymtab,
- ElfStrGopclntab,
ElfStrShstrtab,
ElfStrSymtab,
ElfStrStrtab,
@@ -531,10 +528,9 @@ doelf(void)
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
addstring(shstrtab, ".elfdata");
addstring(shstrtab, ".rodata");
+ addstring(shstrtab, ".gosymtab");
+ addstring(shstrtab, ".gopclntab");
if(!debug['s']) {
- elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts");
- elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab");
- elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab");
elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab");
elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab");
dwarfaddshstrings(shstrtab);
@@ -679,10 +675,11 @@ asmb(void)
seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
codeblk(sect->vaddr, sect->len);
- /* output read-only data in text segment */
- sect = segtext.sect->next;
- seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
- datblk(sect->vaddr, sect->len);
+ /* output read-only data in text segment (rodata, gosymtab and pclntab) */
+ for(sect = sect->next; sect != nil; sect = sect->next) {
+ seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0);
+ datblk(sect->vaddr, sect->len);
+ }
if(debug['v'])
Bprint(&bso, "%5.2f datblk\n", cputime());
@@ -1083,18 +1080,6 @@ asmb(void)
elfshbits(sect);
if (!debug['s']) {
- sh = newElfShdr(elfstr[ElfStrGosymtab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("symtab", 0));
-
- sh = newElfShdr(elfstr[ElfStrGopclntab]);
- sh->type = SHT_PROGBITS;
- sh->flags = SHF_ALLOC;
- sh->addralign = 1;
- shsym(sh, lookup("pclntab", 0));
-
sh = newElfShdr(elfstr[ElfStrSymtab]);
sh->type = SHT_SYMTAB;
sh->off = symo;
diff --git a/src/cmd/8l/mkenam b/src/cmd/8l/mkenam
index b33fec7cc..992aa3160 100644
--- a/src/cmd/8l/mkenam
+++ b/src/cmd/8l/mkenam
@@ -28,18 +28,18 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-ed - ../8l/8.out.h <<'!'
-v/^ A/d
-,s/^ A/ "/
-g/ .*$/s///
-,s/,*$/",/
-1i
-char* anames[] =
-{
-.
-$a
-};
-.
-w enam.c
-Q
-!
+awk '
+BEGIN {
+ print "char* anames[] ="
+ print "{"
+}
+
+/^ A/ {
+ name=$1
+ sub(/,/, "", name)
+ sub(/^A/, "", name)
+ print "\t\"" name "\","
+}
+
+END { print "};" }
+' ../8l/8.out.h >enam.c
diff --git a/src/cmd/cc/acid.c b/src/cmd/cc/acid.c
index c6a6722bd..23147e519 100644
--- a/src/cmd/cc/acid.c
+++ b/src/cmd/cc/acid.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
static char *kwd[] =
diff --git a/src/cmd/cc/bits.c b/src/cmd/cc/bits.c
index aef4449e8..4496d65e7 100644
--- a/src/cmd/cc/bits.c
+++ b/src/cmd/cc/bits.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
Bits
diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h
index 8e8f6af44..a38e658ce 100644
--- a/src/cmd/cc/cc.h
+++ b/src/cmd/cc/cc.h
@@ -28,10 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#include <u.h>
#include <libc.h>
#include <bio.h>
-#include <ctype.h>
#pragma lib "../cc/cc.a$O"
@@ -816,7 +814,9 @@ int machcap(Node*);
#pragma varargck type "L" int32
#pragma varargck type "Q" int32
#pragma varargck type "O" int
+#pragma varargck type "O" uint
#pragma varargck type "T" Type*
+#pragma varargck type "U" char*
#pragma varargck type "|" int
enum
diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y
index 470fdae26..515a80372 100644
--- a/src/cmd/cc/cc.y
+++ b/src/cmd/cc/cc.y
@@ -29,6 +29,7 @@
// THE SOFTWARE.
%{
+#include <u.h>
#include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */
#include "cc.h"
%}
diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c
index b1a8a4704..6e470ee64 100644
--- a/src/cmd/cc/com.c
+++ b/src/cmd/cc/com.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
int compar(Node*, int);
@@ -127,7 +128,7 @@ tcomo(Node *n, int f)
case ORETURN:
if(l == Z) {
if(n->type->etype != TVOID)
- warn(n, "null return of a typed function");
+ diag(n, "null return of a typed function");
break;
}
if(tcom(l))
diff --git a/src/cmd/cc/com64.c b/src/cmd/cc/com64.c
index 8d6e07d1b..fb7a3f750 100644
--- a/src/cmd/cc/com64.c
+++ b/src/cmd/cc/com64.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
/*
diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c
index 6f1b8a9a9..d624bf247 100644
--- a/src/cmd/cc/dcl.c
+++ b/src/cmd/cc/dcl.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
Node*
diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c
index 0e51101f1..42c245b56 100644
--- a/src/cmd/cc/dpchk.c
+++ b/src/cmd/cc/dpchk.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <ctype.h>
#include "cc.h"
#include "y.tab.h"
@@ -56,7 +58,9 @@ struct Tname
{
char* name;
int param;
+ int count;
Tname* link;
+ Tprot* prot;
};
static Type* indchar;
@@ -131,8 +135,8 @@ getflag(char *s)
return flag;
}
-void
-newprot(Sym *m, Type *t, char *s)
+static void
+newprot(Sym *m, Type *t, char *s, Tprot **prot)
{
Bits flag;
Tprot *l;
@@ -142,32 +146,37 @@ newprot(Sym *m, Type *t, char *s)
return;
}
flag = getflag(s);
- for(l=tprot; l; l=l->link)
+ for(l=*prot; l; l=l->link)
if(beq(flag, l->flag) && sametype(t, l->type))
return;
l = alloc(sizeof(*l));
l->type = t;
l->flag = flag;
- l->link = tprot;
- tprot = l;
+ l->link = *prot;
+ *prot = l;
}
-void
-newname(char *s, int p)
+static Tname*
+newname(char *s, int p, int count)
{
Tname *l;
for(l=tname; l; l=l->link)
if(strcmp(l->name, s) == 0) {
- if(l->param != p)
+ if(p >= 0 && l->param != p)
yyerror("vargck %s already defined\n", s);
- return;
+ return l;
}
+ if(p < 0)
+ return nil;
+
l = alloc(sizeof(*l));
l->name = s;
l->param = p;
l->link = tname;
+ l->count = count;
tname = l;
+ return l;
}
void
@@ -234,6 +243,7 @@ pragvararg(void)
int n, c;
char *t;
Type *ty;
+ Tname *l;
if(!debug['F'])
goto out;
@@ -244,6 +254,8 @@ pragvararg(void)
goto cktype;
if(s && strcmp(s->name, "flag") == 0)
goto ckflag;
+ if(s && strcmp(s->name, "countpos") == 0)
+ goto ckcount;
yyerror("syntax in #pragma varargck");
goto out;
@@ -255,7 +267,18 @@ ckpos:
n = getnsn();
if(n < 0)
goto bad;
- newname(s->name, n);
+ newname(s->name, n, 0);
+ goto out;
+
+ckcount:
+/*#pragma varargck countpos name 2*/
+ s = getsym();
+ if(s == S)
+ goto bad;
+ n = getnsn();
+ if(n < 0)
+ goto bad;
+ newname(s->name, 0, n);
goto out;
ckflag:
@@ -276,6 +299,25 @@ ckflag:
goto out;
cktype:
+ c = getnsc();
+ unget(c);
+ if(c != '"') {
+/*#pragma varargck type name int*/
+ s = getsym();
+ if(s == S)
+ goto bad;
+ l = newname(s->name, -1, -1);
+ s = getsym();
+ if(s == S)
+ goto bad;
+ ty = s->type;
+ while((c = getnsc()) == '*')
+ ty = typ(TIND, ty);
+ unget(c);
+ newprot(s, ty, "a", &l->prot);
+ goto out;
+ }
+
/*#pragma varargck type O int*/
t = getquoted();
if(t == nil)
@@ -287,7 +329,7 @@ cktype:
while((c = getnsc()) == '*')
ty = typ(TIND, ty);
unget(c);
- newprot(s, ty, t);
+ newprot(s, ty, t, &tprot);
goto out;
bad:
@@ -384,7 +426,8 @@ dpcheck(Node *n)
char *s;
Node *a, *b;
Tname *l;
- int i;
+ Tprot *tl;
+ int i, j;
if(n == Z)
return;
@@ -398,20 +441,76 @@ dpcheck(Node *n)
if(l == 0)
return;
+ if(l->count > 0) {
+ // fetch count, then check remaining length
+ i = l->count;
+ a = nil;
+ b = n->right;
+ while(i > 0) {
+ b = nextarg(b, &a);
+ i--;
+ }
+ if(a == Z) {
+ diag(n, "can't find count arg");
+ return;
+ }
+ if(a->op != OCONST || !typechl[a->type->etype]) {
+ diag(n, "count is invalid constant");
+ return;
+ }
+ j = a->vconst;
+ i = 0;
+ while(b != Z) {
+ b = nextarg(b, &a);
+ i++;
+ }
+ if(i != j)
+ diag(n, "found %d argument%s after count %d", i, i == 1 ? "" : "s", j);
+ }
+
+ if(l->prot != nil) {
+ // check that all arguments after param or count
+ // are listed in type list.
+ i = l->count;
+ if(i == 0)
+ i = l->param;
+ if(i == 0)
+ return;
+ a = nil;
+ b = n->right;
+ while(i > 0) {
+ b = nextarg(b, &a);
+ i--;
+ }
+ if(a == Z) {
+ diag(n, "can't find count/param arg");
+ return;
+ }
+ while(b != Z) {
+ b = nextarg(b, &a);
+ for(tl=l->prot; tl; tl=tl->link)
+ if(sametype(a->type, tl->type))
+ break;
+ if(tl == nil)
+ diag(a, "invalid type %T in call to %s", a->type, s);
+ }
+ }
+
+ if(l->param <= 0)
+ return;
i = l->param;
a = nil;
b = n->right;
- a = Z;
while(i > 0) {
b = nextarg(b, &a);
i--;
}
if(a == Z) {
- warn(n, "cant find format arg");
+ diag(n, "can't find format arg");
return;
}
if(!sametype(indchar, a->type)) {
- warn(n, "format arg type %T", a->type);
+ diag(n, "format arg type %T", a->type);
return;
}
if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) {
diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c
index 21d86258f..99477b2b2 100644
--- a/src/cmd/cc/funct.c
+++ b/src/cmd/cc/funct.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
typedef struct Ftab Ftab;
diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c
index 9503cb2f2..3ba979c8a 100644
--- a/src/cmd/cc/godefs.c
+++ b/src/cmd/cc/godefs.c
@@ -29,6 +29,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
static int upper;
@@ -238,7 +239,7 @@ printtypename(Type *t)
Bprint(&outbuf, "%U", n);
break;
case TFUNC:
- Bprint(&outbuf, "func(", t);
+ Bprint(&outbuf, "func(");
for(t1 = t->down; t1 != T; t1 = t1->down) {
if(t1->etype == TVOID)
break;
diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c
index 71cc89bf0..15f2d374d 100644
--- a/src/cmd/cc/lex.c
+++ b/src/cmd/cc/lex.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <ctype.h>
#include "cc.h"
#include "y.tab.h"
@@ -384,7 +386,7 @@ lookup(void)
}else
*w++ = *r;
}
- *w++ = '\0';
+ *w = '\0';
h = 0;
for(p=symb; *p;) {
@@ -1524,7 +1526,7 @@ alloc(int32 n)
p = malloc(n);
if(p == nil) {
print("alloc out of mem\n");
- exit(1);
+ exits("alloc: out of mem");
}
memset(p, 0, n);
return p;
@@ -1538,7 +1540,7 @@ allocn(void *p, int32 n, int32 d)
p = realloc(p, n+d);
if(p == nil) {
print("allocn out of mem\n");
- exit(1);
+ exits("allocn: out of mem");
}
if(d > 0)
memset((char*)p+n, 0, d);
diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody
index 24f9bdc85..f4cc19c2e 100644
--- a/src/cmd/cc/lexbody
+++ b/src/cmd/cc/lexbody
@@ -96,7 +96,7 @@ alloc(int32 n)
p = malloc(n);
if(p == nil) {
print("alloc out of mem\n");
- exit(1);
+ exits("alloc: out of mem");
}
memset(p, 0, n);
return p;
@@ -110,7 +110,7 @@ allocn(void *p, int32 n, int32 d)
p = realloc(p, n+d);
if(p == nil) {
print("allocn out of mem\n");
- exit(1);
+ exits("allocn: out of mem");
}
if(d > 0)
memset((char*)p+n, 0, d);
@@ -245,7 +245,7 @@ lookup(void)
}else
*w++ = *r;
}
- *w++ = '\0';
+ *w = '\0';
h = 0;
for(p=symb; c = *p; p++)
diff --git a/src/cmd/cc/mac.c b/src/cmd/cc/mac.c
index c08cd9c97..43ae214d7 100644
--- a/src/cmd/cc/mac.c
+++ b/src/cmd/cc/mac.c
@@ -28,6 +28,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
+#include <ctype.h>
#include "cc.h"
#include "macbody"
diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody
index ca8a54c0b..ed66361f1 100644
--- a/src/cmd/cc/macbody
+++ b/src/cmd/cc/macbody
@@ -830,11 +830,11 @@ linehist(char *f, int offset)
if(debug['f'])
if(f) {
if(offset)
- print("%4ld: %s (#line %d)\n", lineno, f, offset);
+ print("%4d: %s (#line %d)\n", lineno, f, offset);
else
- print("%4ld: %s\n", lineno, f);
+ print("%4d: %s\n", lineno, f);
} else
- print("%4ld: <pop>\n", lineno);
+ print("%4d: <pop>\n", lineno);
newflag = 0;
h = alloc(sizeof(Hist));
diff --git a/src/cmd/cc/omachcap.c b/src/cmd/cc/omachcap.c
index ec5aa86e9..f8fc1d88b 100644
--- a/src/cmd/cc/omachcap.c
+++ b/src/cmd/cc/omachcap.c
@@ -28,11 +28,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
/* default, like old cc */
int
machcap(Node *n)
{
+ USED(n);
return 0;
}
diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c
index 5d17cafc9..0e5e8c059 100644
--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -112,7 +112,7 @@ codgen(Node *n, Node *nn)
warnreach = 1;
gen(n);
if(canreach && thisfn->link->etype != TVOID)
- warn(Z, "no return at end of function: %s", n1->sym->name);
+ diag(Z, "no return at end of function: %s", n1->sym->name);
noretval(3);
gbranch(ORETURN);
diff --git a/src/cmd/cc/scon.c b/src/cmd/cc/scon.c
index 3047ca44f..193331f77 100644
--- a/src/cmd/cc/scon.c
+++ b/src/cmd/cc/scon.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
static Node*
diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c
index e0d5df719..e5992e213 100644
--- a/src/cmd/cc/sub.c
+++ b/src/cmd/cc/sub.c
@@ -28,6 +28,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#include <u.h>
#include "cc.h"
Node*
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 6b930f151..a4d83f1e7 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -66,7 +66,7 @@ func cname(s string) string {
// preamble. Multiple occurrences are concatenated with a separating space,
// even across files.
func (p *Package) ParseFlags(f *File, srcfile string) {
- linesIn := strings.Split(f.Preamble, "\n", -1)
+ linesIn := strings.Split(f.Preamble, "\n")
linesOut := make([]string, 0, len(linesIn))
NextLine:
@@ -78,7 +78,7 @@ NextLine:
}
l = strings.TrimSpace(l[4:])
- fields := strings.Split(l, ":", 2)
+ fields := strings.SplitN(l, ":", 2)
if len(fields) != 2 {
fatalf("%s: bad #cgo line: %s", srcfile, line)
}
@@ -275,7 +275,7 @@ func (p *Package) loadDefines(f *File) {
b.WriteString(f.Preamble)
stdout := p.gccDefines(b.Bytes())
- for _, line := range strings.Split(stdout, "\n", -1) {
+ for _, line := range strings.Split(stdout, "\n") {
if len(line) < 9 || line[0:7] != "#define" {
continue
}
@@ -397,7 +397,7 @@ func (p *Package) guessKinds(f *File) []*Name {
isConst[i] = true // until proven otherwise
}
- for _, line := range strings.Split(stderr, "\n", -1) {
+ for _, line := range strings.Split(stderr, "\n") {
if len(line) < 9 || line[0:9] != "cgo-test:" {
// the user will see any compiler errors when the code is compiled later.
continue
@@ -1188,7 +1188,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
if ss, ok := dwarfToName[s]; ok {
s = ss
}
- s = strings.Join(strings.Split(s, " ", -1), "") // strip spaces
+ s = strings.Join(strings.Split(s, " "), "") // strip spaces
name := c.Ident("_Ctype_" + s)
typedef[name.Name] = t.Go
t.Go = name
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 7eecb3437..6802dd1cf 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -148,7 +148,7 @@ func dynimport(obj string) {
fatalf("cannot load imported symbols from PE file %s: %v", obj, err)
}
for _, s := range sym {
- ss := strings.Split(s, ":", -1)
+ ss := strings.Split(s, ":")
fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
}
return
diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go
index cac39179f..0b0443156 100644
--- a/src/cmd/ebnflint/ebnflint.go
+++ b/src/cmd/ebnflint/ebnflint.go
@@ -35,6 +35,12 @@ var (
)
+func report(err os.Error) {
+ scanner.PrintError(os.Stderr, err)
+ os.Exit(1)
+}
+
+
func extractEBNF(src []byte) []byte {
var buf bytes.Buffer
@@ -75,34 +81,35 @@ func extractEBNF(src []byte) []byte {
func main() {
flag.Parse()
- var filename string
+ var (
+ filename string
+ src []byte
+ err os.Error
+ )
switch flag.NArg() {
case 0:
- filename = "/dev/stdin"
+ filename = "<stdin>"
+ src, err = ioutil.ReadAll(os.Stdin)
case 1:
filename = flag.Arg(0)
+ src, err = ioutil.ReadFile(filename)
default:
usage()
}
-
- src, err := ioutil.ReadFile(filename)
if err != nil {
- scanner.PrintError(os.Stderr, err)
- os.Exit(1)
+ report(err)
}
- if filepath.Ext(filename) == ".html" {
+ if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 {
src = extractEBNF(src)
}
grammar, err := ebnf.Parse(fset, filename, src)
if err != nil {
- scanner.PrintError(os.Stderr, err)
- os.Exit(1)
+ report(err)
}
if err = ebnf.Verify(fset, grammar, *start); err != nil {
- scanner.PrintError(os.Stderr, err)
- os.Exit(1)
+ report(err)
}
}
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index b68768165..8ca086ee0 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -302,6 +302,7 @@ struct Sym
uchar flags;
uchar sym; // huffman encoding in object file
Sym* link;
+ int32 npkg; // number of imported packages with this name
// saved and restored by dcopy
Pkg* pkg;
@@ -777,6 +778,7 @@ EXTERN int32 nhunk;
EXTERN int32 thunk;
EXTERN int exporting;
+EXTERN int erroring;
EXTERN int noargnames;
EXTERN int funcdepth;
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index 5d28c0e3b..01a4e822f 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -237,7 +237,11 @@ import_here:
import_package:
LPACKAGE sym import_safety ';'
{
- importpkg->name = $2->name;
+ if(importpkg->name == nil) {
+ importpkg->name = $2->name;
+ pkglookup($2->name, nil)->npkg++;
+ } else if(strcmp(importpkg->name, $2->name) != 0)
+ yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path);
importpkg->direct = 1;
if(safemode && !curio.importsafe)
@@ -1657,7 +1661,11 @@ hidden_import:
Pkg *p;
p = mkpkg($3.u.sval);
- p->name = $2->name;
+ if(p->name == nil) {
+ p->name = $2->name;
+ pkglookup($2->name, nil)->npkg++;
+ } else if(strcmp(p->name, $2->name) != 0)
+ yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path);
}
| LVAR hidden_pkg_importsym hidden_type ';'
{
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index 8eb60de31..40b0c4fd1 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -45,10 +45,12 @@ adderr(int line, char *fmt, va_list arg)
Fmt f;
Error *p;
+ erroring++;
fmtstrinit(&f);
fmtprint(&f, "%L: ", line);
fmtvprint(&f, fmt, arg);
fmtprint(&f, "\n");
+ erroring--;
if(nerr >= merr) {
if(merr == 0)
@@ -1122,7 +1124,14 @@ Sconv(Fmt *fp)
return 0;
}
- if(s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
+ if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
+ // This one is for the user. If the package name
+ // was used by multiple packages, give the full
+ // import path to disambiguate.
+ if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
+ fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+ return 0;
+ }
fmtprint(fp, "%s.%s", s->pkg->name, s->name);
return 0;
}
diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go
index 54bebe854..50043e2ab 100644
--- a/src/cmd/godoc/codewalk.go
+++ b/src/cmd/godoc/codewalk.go
@@ -74,7 +74,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) {
// A Codewalk represents a single codewalk read from an XML file.
type Codewalk struct {
- Title string "attr"
+ Title string `xml:"attr"`
File []string
Step []*Codestep
}
@@ -83,9 +83,9 @@ type Codewalk struct {
// A Codestep is a single step in a codewalk.
type Codestep struct {
// Filled in from XML
- Src string "attr"
- Title string "attr"
- XML string "innerxml"
+ Src string `xml:"attr"`
+ Title string `xml:"attr"`
+ XML string `xml:"innerxml"`
// Derived from Src; not in XML.
Err os.Error
@@ -168,7 +168,7 @@ func loadCodewalk(filename string) (*Codewalk, os.Error) {
cw.File[i] = f
i++
}
- sort.SortStrings(cw.File)
+ sort.Strings(cw.File)
return cw, nil
}
diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go
index af44fa16a..e98e93a46 100644
--- a/src/cmd/godoc/dirtrees.go
+++ b/src/cmd/godoc/dirtrees.go
@@ -30,7 +30,7 @@ type Directory struct {
func isGoFile(fi FileInfo) bool {
name := fi.Name()
return fi.IsRegular() &&
- !strings.HasPrefix(name, ".") && // ignore .files
+ len(name) > 0 && name[0] != '.' && // ignore .files
filepath.Ext(name) == ".go"
}
@@ -43,7 +43,8 @@ func isPkgFile(fi FileInfo) bool {
func isPkgDir(fi FileInfo) bool {
name := fi.Name()
- return fi.IsDirectory() && len(name) > 0 && name[0] != '_'
+ return fi.IsDirectory() && len(name) > 0 &&
+ name[0] != '_' && name[0] != '.' // ignore _files and .files
}
@@ -267,8 +268,8 @@ func (dir *Directory) lookupLocal(name string) *Directory {
// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
- d := strings.Split(dir.Path, string(filepath.Separator), -1)
- p := strings.Split(path, string(filepath.Separator), -1)
+ d := strings.Split(dir.Path, string(filepath.Separator))
+ p := strings.Split(path, string(filepath.Separator))
i := 0
for i < len(d) {
if i >= len(p) || d[i] != p[i] {
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 6987d911b..20ebd3183 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -9,6 +9,7 @@ import (
"flag"
"fmt"
"go/ast"
+ "go/build"
"go/doc"
"go/printer"
"go/token"
@@ -83,8 +84,16 @@ var (
func initHandlers() {
- fsMap.Init(*pkgPath)
- fileServer = http.FileServer(*goroot, "")
+ paths := filepath.SplitList(*pkgPath)
+ for _, t := range build.Path {
+ if t.Goroot {
+ continue
+ }
+ paths = append(paths, t.SrcDir())
+ }
+ fsMap.Init(paths)
+
+ fileServer = http.FileServer(http.Dir(*goroot))
cmdHandler = httpHandler{"/cmd/", filepath.Join(*goroot, "src", "cmd"), false}
pkgHandler = httpHandler{"/pkg/", filepath.Join(*goroot, "src", "pkg"), true}
}
@@ -160,7 +169,7 @@ func readDirList(filename string) ([]string, os.Error) {
}
return e == nil && isPkgDir(d)
}
- list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter)
+ list := canonicalizePaths(strings.Split(string(contents), "\n"), filter)
// for each parent path, remove all it's children q
// (requirement for binary search to work when filtering)
i := 0
diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go
index 61caee101..e0c89e794 100644
--- a/src/cmd/godoc/index.go
+++ b/src/cmd/godoc/index.go
@@ -901,7 +901,7 @@ func isIdentifier(s string) bool {
// identifier, Lookup returns a LookupResult, and a list of alternative
// spellings, if any. If the query syntax is wrong, an error is reported.
func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os.Error) {
- ss := strings.Split(query, ".", -1)
+ ss := strings.Split(query, ".")
// check query syntax
for _, s := range ss {
@@ -954,7 +954,7 @@ func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[
// unique returns the list sorted and with duplicate entries removed
func unique(list []int) []int {
- sort.SortInts(list)
+ sort.Ints(list)
var last int
i := 0
for _, x := range list {
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 55f6031bc..51fcf8dd0 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -31,6 +31,7 @@ import (
"flag"
"fmt"
"go/ast"
+ "go/build"
"http"
_ "http/pprof" // to serve /debug/pprof/*
"io"
@@ -332,7 +333,10 @@ func main() {
}
relpath := path
abspath := path
- if !filepath.IsAbs(path) {
+ if t, pkg, err := build.FindTree(path); err == nil {
+ relpath = pkg
+ abspath = filepath.Join(t.SrcDir(), pkg)
+ } else if !filepath.IsAbs(path) {
abspath = absolutePath(path, pkgHandler.fsRoot)
} else {
relpath = relativeURL(path)
diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go
index 73f1881a2..92614e83e 100644
--- a/src/cmd/godoc/mapping.go
+++ b/src/cmd/godoc/mapping.go
@@ -59,10 +59,10 @@ type mapping struct {
}
-// Init initializes the Mapping from a list of paths separated by
-// filepath.ListSeparator. Empty paths are ignored; relative paths
-// are assumed to be relative to the current working directory and
-// converted to absolute paths. For each path of the form:
+// Init initializes the Mapping from a list of paths.
+// Empty paths are ignored; relative paths are assumed to be relative to
+// the current working directory and converted to absolute paths.
+// For each path of the form:
//
// dirname/localname
//
@@ -80,8 +80,8 @@ type mapping struct {
// user -> /home/user
// public -> /home/build/public
//
-func (m *Mapping) Init(paths string) {
- pathlist := canonicalizePaths(filepath.SplitList(paths), nil)
+func (m *Mapping) Init(paths []string) {
+ pathlist := canonicalizePaths(paths, nil)
list := make([]mapping, len(pathlist))
// create mapping list
@@ -120,7 +120,7 @@ func (m *Mapping) PrefixList() []string {
}
// sort the list and remove duplicate entries
- sort.SortStrings(list)
+ sort.Strings(list)
i := 0
prev := ""
for _, path := range list {
diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go
index 660bf6d04..e2637ab3d 100644
--- a/src/cmd/godoc/utils.go
+++ b/src/cmd/godoc/utils.go
@@ -80,7 +80,7 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string {
list = list[0:i]
// sort the list and remove duplicate entries
- sort.SortStrings(list)
+ sort.Strings(list)
i = 0
prev := ""
for _, path := range list {
diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile
index b157649e8..7ce21e8aa 100644
--- a/src/cmd/gofix/Makefile
+++ b/src/cmd/gofix/Makefile
@@ -6,16 +6,22 @@ include ../../Make.inc
TARG=gofix
GOFILES=\
+ filepath.go\
fix.go\
- netdial.go\
- main.go\
- oserrorstring.go\
- osopen.go\
httpfinalurl.go\
+ httpfs.go\
httpheaders.go\
httpserver.go\
+ main.go\
+ netdial.go\
+ oserrorstring.go\
+ osopen.go\
procattr.go\
reflect.go\
+ signal.go\
+ sorthelpers.go\
+ sortslice.go\
+ stringssplit.go\
typecheck.go\
include ../../Make.cmd
diff --git a/src/cmd/gofix/filepath.go b/src/cmd/gofix/filepath.go
new file mode 100644
index 000000000..1d0ad6879
--- /dev/null
+++ b/src/cmd/gofix/filepath.go
@@ -0,0 +1,53 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(fix{
+ "filepath",
+ filepathFunc,
+ `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator).
+
+http://codereview.appspot.com/4527090
+`,
+ })
+}
+
+func filepathFunc(f *ast.File) (fixed bool) {
+ if !imports(f, "path/filepath") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ e, ok := n.(*ast.Expr)
+ if !ok {
+ return
+ }
+
+ var ident string
+ switch {
+ case isPkgDot(*e, "filepath", "SeparatorString"):
+ ident = "filepath.Separator"
+ case isPkgDot(*e, "filepath", "ListSeparatorString"):
+ ident = "filepath.ListSeparator"
+ default:
+ return
+ }
+
+ // string(filepath.[List]Separator)
+ *e = &ast.CallExpr{
+ Fun: ast.NewIdent("string"),
+ Args: []ast.Expr{ast.NewIdent(ident)},
+ }
+
+ fixed = true
+ })
+
+ return
+}
diff --git a/src/cmd/gofix/filepath_test.go b/src/cmd/gofix/filepath_test.go
new file mode 100644
index 000000000..d170c3ae3
--- /dev/null
+++ b/src/cmd/gofix/filepath_test.go
@@ -0,0 +1,33 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(filepathTests)
+}
+
+var filepathTests = []testCase{
+ {
+ Name: "filepath.0",
+ In: `package main
+
+import (
+ "path/filepath"
+)
+
+var _ = filepath.SeparatorString
+var _ = filepath.ListSeparatorString
+`,
+ Out: `package main
+
+import (
+ "path/filepath"
+)
+
+var _ = string(filepath.Separator)
+var _ = string(filepath.ListSeparator)
+`,
+ },
+}
diff --git a/src/cmd/gofix/fix.go b/src/cmd/gofix/fix.go
index 0852ce21e..c1c5a746c 100644
--- a/src/cmd/gofix/fix.go
+++ b/src/cmd/gofix/fix.go
@@ -10,6 +10,7 @@ import (
"go/token"
"os"
"strconv"
+ "strings"
)
type fix struct {
@@ -258,13 +259,28 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
// imports returns true if f imports path.
func imports(f *ast.File, path string) bool {
+ return importSpec(f, path) != nil
+}
+
+// importSpec returns the import spec if f imports path,
+// or nil otherwise.
+func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
- t, err := strconv.Unquote(s.Path.Value)
- if err == nil && t == path {
- return true
+ if importPath(s) == path {
+ return s
}
}
- return false
+ return nil
+}
+
+// importPath returns the unquoted import path of s,
+// or "" if the path is not properly quoted.
+func importPath(s *ast.ImportSpec) string {
+ t, err := strconv.Unquote(s.Path.Value)
+ if err == nil {
+ return t
+ }
+ return ""
}
// isPkgDot returns true if t is the expression "pkg.name"
@@ -420,3 +436,138 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
},
}
}
+
+// addImport adds the import path to the file f, if absent.
+func addImport(f *ast.File, path string) {
+ if imports(f, path) {
+ return
+ }
+
+ newImport := &ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(path),
+ },
+ }
+
+ var impdecl *ast.GenDecl
+
+ // Find an import decl to add to.
+ for _, decl := range f.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+
+ if ok && gen.Tok == token.IMPORT {
+ impdecl = gen
+ break
+ }
+ }
+
+ // No import decl found. Add one.
+ if impdecl == nil {
+ impdecl = &ast.GenDecl{
+ Tok: token.IMPORT,
+ }
+ f.Decls = append(f.Decls, nil)
+ copy(f.Decls[1:], f.Decls)
+ f.Decls[0] = impdecl
+ }
+
+ // Ensure the import decl has parentheses, if needed.
+ if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() {
+ impdecl.Lparen = impdecl.Pos()
+ }
+
+ // Assume the import paths are alphabetically ordered.
+ // If they are not, the result is ugly, but legal.
+ insertAt := len(impdecl.Specs) // default to end of specs
+ for i, spec := range impdecl.Specs {
+ impspec := spec.(*ast.ImportSpec)
+ if importPath(impspec) > path {
+ insertAt = i
+ break
+ }
+ }
+
+ impdecl.Specs = append(impdecl.Specs, nil)
+ copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:])
+ impdecl.Specs[insertAt] = newImport
+
+ f.Imports = append(f.Imports, newImport)
+}
+
+// deleteImport deletes the import path from the file f, if present.
+func deleteImport(f *ast.File, path string) {
+ oldImport := importSpec(f, path)
+
+ // Find the import node that imports path, if any.
+ for i, decl := range f.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ continue
+ }
+ for j, spec := range gen.Specs {
+ impspec := spec.(*ast.ImportSpec)
+
+ if oldImport != impspec {
+ continue
+ }
+
+ // We found an import spec that imports path.
+ // Delete it.
+ copy(gen.Specs[j:], gen.Specs[j+1:])
+ gen.Specs = gen.Specs[:len(gen.Specs)-1]
+
+ // If this was the last import spec in this decl,
+ // delete the decl, too.
+ if len(gen.Specs) == 0 {
+ copy(f.Decls[i:], f.Decls[i+1:])
+ f.Decls = f.Decls[:len(f.Decls)-1]
+ } else if len(gen.Specs) == 1 {
+ gen.Lparen = token.NoPos // drop parens
+ }
+
+ break
+ }
+ }
+
+ // Delete it from f.Imports.
+ for i, imp := range f.Imports {
+ if imp == oldImport {
+ copy(f.Imports[i:], f.Imports[i+1:])
+ f.Imports = f.Imports[:len(f.Imports)-1]
+ break
+ }
+ }
+}
+
+func usesImport(f *ast.File, path string) (used bool) {
+ spec := importSpec(f, path)
+ if spec == nil {
+ return
+ }
+
+ name := spec.Name.String()
+ switch name {
+ case "<nil>":
+ // If the package name is not explicitly specified,
+ // make an educated guess. This is not guaranteed to be correct.
+ lastSlash := strings.LastIndex(path, "/")
+ if lastSlash == -1 {
+ name = path
+ } else {
+ name = path[lastSlash+1:]
+ }
+ case "_", ".":
+ // Not sure if this import is used - err on the side of caution.
+ return true
+ }
+
+ walk(f, func(n interface{}) {
+ sel, ok := n.(*ast.SelectorExpr)
+ if ok && isTopName(sel.X, name) {
+ used = true
+ }
+ })
+
+ return
+}
diff --git a/src/cmd/gofix/httpfs.go b/src/cmd/gofix/httpfs.go
new file mode 100644
index 000000000..7f2765680
--- /dev/null
+++ b/src/cmd/gofix/httpfs.go
@@ -0,0 +1,63 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+var httpFileSystemFix = fix{
+ "httpfs",
+ httpfs,
+ `Adapt http FileServer to take a FileSystem.
+
+http://codereview.appspot.com/4629047 http FileSystem interface
+`,
+}
+
+func init() {
+ register(httpFileSystemFix)
+}
+
+func httpfs(f *ast.File) bool {
+ if !imports(f, "http") {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ call, ok := n.(*ast.CallExpr)
+ if !ok || !isPkgDot(call.Fun, "http", "FileServer") {
+ return
+ }
+ if len(call.Args) != 2 {
+ return
+ }
+ dir, prefix := call.Args[0], call.Args[1]
+ call.Args = []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("Dir")},
+ Args: []ast.Expr{dir},
+ }}
+ wrapInStripHandler := true
+ if prefixLit, ok := prefix.(*ast.BasicLit); ok {
+ if prefixLit.Kind == token.STRING && (prefixLit.Value == `"/"` || prefixLit.Value == `""`) {
+ wrapInStripHandler = false
+ }
+ }
+ if wrapInStripHandler {
+ call.Fun.(*ast.SelectorExpr).Sel = ast.NewIdent("StripPrefix")
+ call.Args = []ast.Expr{
+ prefix,
+ &ast.CallExpr{
+ Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("FileServer")},
+ Args: call.Args,
+ },
+ }
+ }
+ fixed = true
+ })
+ return fixed
+}
diff --git a/src/cmd/gofix/httpfs_test.go b/src/cmd/gofix/httpfs_test.go
new file mode 100644
index 000000000..d1804e93b
--- /dev/null
+++ b/src/cmd/gofix/httpfs_test.go
@@ -0,0 +1,47 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(httpFileSystemTests)
+}
+
+var httpFileSystemTests = []testCase{
+ {
+ Name: "httpfs.0",
+ In: `package httpfs
+
+import (
+ "http"
+)
+
+func f() {
+ _ = http.FileServer("/var/www/foo", "/")
+ _ = http.FileServer("/var/www/foo", "")
+ _ = http.FileServer("/var/www/foo/bar", "/bar")
+ s := "/foo"
+ _ = http.FileServer(s, "/")
+ prefix := "/p"
+ _ = http.FileServer(s, prefix)
+}
+`,
+ Out: `package httpfs
+
+import (
+ "http"
+)
+
+func f() {
+ _ = http.FileServer(http.Dir("/var/www/foo"))
+ _ = http.FileServer(http.Dir("/var/www/foo"))
+ _ = http.StripPrefix("/bar", http.FileServer(http.Dir("/var/www/foo/bar")))
+ s := "/foo"
+ _ = http.FileServer(http.Dir(s))
+ prefix := "/p"
+ _ = http.StripPrefix(prefix, http.FileServer(http.Dir(s)))
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go
index 05495bc0d..e7e7013c5 100644
--- a/src/cmd/gofix/main.go
+++ b/src/cmd/gofix/main.go
@@ -53,7 +53,7 @@ func main() {
if *allowedRewrites != "" {
allowed = make(map[string]bool)
- for _, f := range strings.Split(*allowedRewrites, ",", -1) {
+ for _, f := range strings.Split(*allowedRewrites, ",") {
allowed[f] = true
}
}
diff --git a/src/cmd/gofix/signal.go b/src/cmd/gofix/signal.go
new file mode 100644
index 000000000..53c338851
--- /dev/null
+++ b/src/cmd/gofix/signal.go
@@ -0,0 +1,49 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "strings"
+)
+
+func init() {
+ register(fix{
+ "signal",
+ signal,
+ `Adapt code to types moved from os/signal to signal.
+
+http://codereview.appspot.com/4437091
+`,
+ })
+}
+
+func signal(f *ast.File) (fixed bool) {
+ if !imports(f, "os/signal") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+
+ if !ok || !isTopName(s.X, "signal") {
+ return
+ }
+
+ sel := s.Sel.String()
+ if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") {
+ s.X = &ast.Ident{Name: "os"}
+ fixed = true
+ }
+ })
+
+ if fixed {
+ addImport(f, "os")
+ if !usesImport(f, "os/signal") {
+ deleteImport(f, "os/signal")
+ }
+ }
+ return
+}
diff --git a/src/cmd/gofix/signal_test.go b/src/cmd/gofix/signal_test.go
new file mode 100644
index 000000000..2500e9cee
--- /dev/null
+++ b/src/cmd/gofix/signal_test.go
@@ -0,0 +1,96 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(signalTests)
+}
+
+var signalTests = []testCase{
+ {
+ Name: "signal.0",
+ In: `package main
+
+import (
+ _ "a"
+ "os/signal"
+ _ "z"
+)
+
+type T1 signal.UnixSignal
+type T2 signal.Signal
+
+func f() {
+ _ = signal.SIGHUP
+ _ = signal.Incoming
+}
+`,
+ Out: `package main
+
+import (
+ _ "a"
+ "os"
+ "os/signal"
+ _ "z"
+)
+
+type T1 os.UnixSignal
+type T2 os.Signal
+
+func f() {
+ _ = os.SIGHUP
+ _ = signal.Incoming
+}
+`,
+ },
+ {
+ Name: "signal.1",
+ In: `package main
+
+import (
+ "os"
+ "os/signal"
+)
+
+func f() {
+ var _ os.Error
+ _ = signal.SIGHUP
+}
+`,
+ Out: `package main
+
+import "os"
+
+
+func f() {
+ var _ os.Error
+ _ = os.SIGHUP
+}
+`,
+ },
+ {
+ Name: "signal.2",
+ In: `package main
+
+import "os"
+import "os/signal"
+
+func f() {
+ var _ os.Error
+ _ = signal.SIGHUP
+}
+`,
+ Out: `package main
+
+import "os"
+
+
+func f() {
+ var _ os.Error
+ _ = os.SIGHUP
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/sorthelpers.go b/src/cmd/gofix/sorthelpers.go
new file mode 100644
index 000000000..4d0bee6e7
--- /dev/null
+++ b/src/cmd/gofix/sorthelpers.go
@@ -0,0 +1,47 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(fix{
+ "sorthelpers",
+ sorthelpers,
+ `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings].
+`,
+ })
+}
+
+
+func sorthelpers(f *ast.File) (fixed bool) {
+ if !imports(f, "sort") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+ if !ok || !isTopName(s.X, "sort") {
+ return
+ }
+
+ switch s.Sel.String() {
+ case "SortFloat64s":
+ s.Sel.Name = "Float64s"
+ case "SortInts":
+ s.Sel.Name = "Ints"
+ case "SortStrings":
+ s.Sel.Name = "Strings"
+ default:
+ return
+ }
+
+ fixed = true
+ })
+
+ return
+}
diff --git a/src/cmd/gofix/sorthelpers_test.go b/src/cmd/gofix/sorthelpers_test.go
new file mode 100644
index 000000000..6c37858fd
--- /dev/null
+++ b/src/cmd/gofix/sorthelpers_test.go
@@ -0,0 +1,45 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(sorthelpersTests)
+}
+
+var sorthelpersTests = []testCase{
+ {
+ Name: "sortslice.0",
+ In: `package main
+
+import (
+ "sort"
+)
+
+func main() {
+ var s []string
+ sort.SortStrings(s)
+ var i []ints
+ sort.SortInts(i)
+ var f []float64
+ sort.SortFloat64s(f)
+}
+`,
+ Out: `package main
+
+import (
+ "sort"
+)
+
+func main() {
+ var s []string
+ sort.Strings(s)
+ var i []ints
+ sort.Ints(i)
+ var f []float64
+ sort.Float64s(f)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/sortslice.go b/src/cmd/gofix/sortslice.go
new file mode 100644
index 000000000..b9c108b5a
--- /dev/null
+++ b/src/cmd/gofix/sortslice.go
@@ -0,0 +1,50 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+)
+
+func init() {
+ register(fix{
+ "sortslice",
+ sortslice,
+ `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice.
+
+http://codereview.appspot.com/4602054
+http://codereview.appspot.com/4639041
+`,
+ })
+}
+
+
+func sortslice(f *ast.File) (fixed bool) {
+ if !imports(f, "sort") {
+ return
+ }
+
+ walk(f, func(n interface{}) {
+ s, ok := n.(*ast.SelectorExpr)
+ if !ok || !isTopName(s.X, "sort") {
+ return
+ }
+
+ switch s.Sel.String() {
+ case "Float64Array":
+ s.Sel.Name = "Float64Slice"
+ case "IntArray":
+ s.Sel.Name = "IntSlice"
+ case "StringArray":
+ s.Sel.Name = "StringSlice"
+ default:
+ return
+ }
+
+ fixed = true
+ })
+
+ return
+}
diff --git a/src/cmd/gofix/sortslice_test.go b/src/cmd/gofix/sortslice_test.go
new file mode 100644
index 000000000..404feb26f
--- /dev/null
+++ b/src/cmd/gofix/sortslice_test.go
@@ -0,0 +1,35 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(sortsliceTests)
+}
+
+var sortsliceTests = []testCase{
+ {
+ Name: "sortslice.0",
+ In: `package main
+
+import (
+ "sort"
+)
+
+var _ = sort.Float64Array
+var _ = sort.IntArray
+var _ = sort.StringArray
+`,
+ Out: `package main
+
+import (
+ "sort"
+)
+
+var _ = sort.Float64Slice
+var _ = sort.IntSlice
+var _ = sort.StringSlice
+`,
+ },
+}
diff --git a/src/cmd/gofix/stringssplit.go b/src/cmd/gofix/stringssplit.go
new file mode 100644
index 000000000..4a1fe93d3
--- /dev/null
+++ b/src/cmd/gofix/stringssplit.go
@@ -0,0 +1,71 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+var stringssplitFix = fix{
+ "stringssplit",
+ stringssplit,
+ `Restore strings.Split to its original meaning and add strings.SplitN. Bytes too.
+
+http://codereview.appspot.com/4661051
+`,
+}
+
+func init() {
+ register(stringssplitFix)
+}
+
+func stringssplit(f *ast.File) bool {
+ if !imports(f, "bytes") && !imports(f, "strings") {
+ return false
+ }
+
+ fixed := false
+ walk(f, func(n interface{}) {
+ call, ok := n.(*ast.CallExpr)
+ // func Split(s, sep string, n int) []string
+ // func SplitAfter(s, sep string, n int) []string
+ if !ok || len(call.Args) != 3 {
+ return
+ }
+ // Is this our function?
+ switch {
+ case isPkgDot(call.Fun, "bytes", "Split"):
+ case isPkgDot(call.Fun, "bytes", "SplitAfter"):
+ case isPkgDot(call.Fun, "strings", "Split"):
+ case isPkgDot(call.Fun, "strings", "SplitAfter"):
+ default:
+ return
+ }
+
+ sel := call.Fun.(*ast.SelectorExpr)
+ args := call.Args
+ fixed = true // We're committed.
+
+ // Is the last argument -1? If so, drop the arg.
+ // (Actually we just look for a negative integer literal.)
+ // Otherwise, Split->SplitN and keep the arg.
+ final := args[2]
+ if unary, ok := final.(*ast.UnaryExpr); ok && unary.Op == token.SUB {
+ if lit, ok := unary.X.(*ast.BasicLit); ok {
+ // Is it an integer? If so, it's a negative integer and that's what we're after.
+ if lit.Kind == token.INT {
+ // drop the last arg.
+ call.Args = args[0:2]
+ return
+ }
+ }
+ }
+
+ // If not, rename and keep the argument list.
+ sel.Sel.Name += "N"
+ })
+ return fixed
+}
diff --git a/src/cmd/gofix/stringssplit_test.go b/src/cmd/gofix/stringssplit_test.go
new file mode 100644
index 000000000..b925722af
--- /dev/null
+++ b/src/cmd/gofix/stringssplit_test.go
@@ -0,0 +1,51 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(stringssplitTests)
+}
+
+var stringssplitTests = []testCase{
+ {
+ Name: "stringssplit.0",
+ In: `package main
+
+import (
+ "bytes"
+ "strings"
+)
+
+func f() {
+ bytes.Split(a, b, c)
+ bytes.Split(a, b, -1)
+ bytes.SplitAfter(a, b, c)
+ bytes.SplitAfter(a, b, -1)
+ strings.Split(a, b, c)
+ strings.Split(a, b, -1)
+ strings.SplitAfter(a, b, c)
+ strings.SplitAfter(a, b, -1)
+}
+`,
+ Out: `package main
+
+import (
+ "bytes"
+ "strings"
+)
+
+func f() {
+ bytes.SplitN(a, b, c)
+ bytes.Split(a, b)
+ bytes.SplitAfterN(a, b, c)
+ bytes.SplitAfter(a, b)
+ strings.SplitN(a, b, c)
+ strings.Split(a, b)
+ strings.SplitAfterN(a, b, c)
+ strings.SplitAfter(a, b)
+}
+`,
+ },
+}
diff --git a/src/cmd/gofix/testdata/reflect.template.go.in b/src/cmd/gofix/testdata/reflect.template.go.in
index ba06de4e3..1f5a8128f 100644
--- a/src/cmd/gofix/testdata/reflect.template.go.in
+++ b/src/cmd/gofix/testdata/reflect.template.go.in
@@ -444,7 +444,7 @@ func (t *Template) newVariable(words []string) *variableElement {
bar := strings.IndexRune(lastWord, '|')
if bar >= 0 {
words[len(words)-1] = lastWord[0:bar]
- formatters = strings.Split(lastWord[bar+1:], "|", -1)
+ formatters = strings.Split(lastWord[bar+1:], "|")
}
// We could remember the function address here and avoid the lookup later,
@@ -705,7 +705,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
if s == "@" {
return indirectPtr(data, numStars)
}
- for _, elem := range strings.Split(s, ".", -1) {
+ for _, elem := range strings.Split(s, ".") {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
if data == nil {
diff --git a/src/cmd/gofix/testdata/reflect.template.go.out b/src/cmd/gofix/testdata/reflect.template.go.out
index c36288455..f2f56ef3c 100644
--- a/src/cmd/gofix/testdata/reflect.template.go.out
+++ b/src/cmd/gofix/testdata/reflect.template.go.out
@@ -444,7 +444,7 @@ func (t *Template) newVariable(words []string) *variableElement {
bar := strings.IndexRune(lastWord, '|')
if bar >= 0 {
words[len(words)-1] = lastWord[0:bar]
- formatters = strings.Split(lastWord[bar+1:], "|", -1)
+ formatters = strings.Split(lastWord[bar+1:], "|")
}
// We could remember the function address here and avoid the lookup later,
@@ -705,7 +705,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
if s == "@" {
return indirectPtr(data, numStars)
}
- for _, elem := range strings.Split(s, ".", -1) {
+ for _, elem := range strings.Split(s, ".") {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
if !data.IsValid() {
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index a72530307..70700554b 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -20,8 +20,8 @@ func runTest(t *testing.T, dirname, in, out, flags string) {
// process flags
*simplifyAST = false
*rewriteRule = ""
- for _, flag := range strings.Split(flags, " ", -1) {
- elts := strings.Split(flag, "=", 2)
+ for _, flag := range strings.Split(flags, " ") {
+ elts := strings.SplitN(flag, "=", 2)
name := elts[0]
value := ""
if len(elts) == 2 {
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index 4c24282f3..f7f1fe824 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -22,7 +22,7 @@ func initRewrite() {
rewrite = nil // disable any previous rewrite
return
}
- f := strings.Split(*rewriteRule, "->", -1)
+ f := strings.Split(*rewriteRule, "->")
if len(f) != 2 {
fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
os.Exit(2)
diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go
index 52b09d37e..a5df7b3bd 100644
--- a/src/cmd/goinstall/doc.go
+++ b/src/cmd/goinstall/doc.go
@@ -5,7 +5,8 @@
/*
Goinstall is an experiment in automatic package installation.
It installs packages, possibly downloading them from the internet.
-It maintains a list of public Go packages at http://godashboard.appspot.com/package.
+It maintains a list of public Go packages at
+http://godashboard.appspot.com/package.
Usage:
goinstall [flags] importpath...
@@ -41,9 +42,22 @@ Another common idiom is to use
to update, recompile, and reinstall all goinstalled packages.
The source code for a package with import path foo/bar is expected
-to be in the directory $GOROOT/src/pkg/foo/bar/. If the import
-path refers to a code hosting site, goinstall will download the code
-if necessary. The recognized code hosting sites are:
+to be in the directory $GOROOT/src/pkg/foo/bar/ or $GOPATH/src/foo/bar/.
+See "The GOPATH Environment Variable" for more about GOPATH.
+
+By default, goinstall prints output only when it encounters an error.
+The -v flag causes goinstall to print information about packages
+being considered and installed.
+
+Goinstall ignores Makefiles.
+
+
+Remote Repositories
+
+If a package import path refers to a remote repository, goinstall will
+download the code if necessary.
+
+Goinstall recognizes packages from a few common code hosting sites:
BitBucket (Mercurial)
@@ -72,7 +86,6 @@ if necessary. The recognized code hosting sites are:
import "launchpad.net/~user/project/branch"
import "launchpad.net/~user/project/branch/sub/directory"
-
If the destination directory (e.g., $GOROOT/src/pkg/bitbucket.org/user/project)
already exists and contains an appropriate checkout, goinstall will not
attempt to fetch updates. The -u flag changes this behavior,
@@ -84,19 +97,42 @@ named "release". If there is one, it uses that version of the code.
Otherwise it uses the default version selected by the version control
system, typically HEAD for git, tip for Mercurial.
-After a successful download and installation of a publicly accessible
-remote package, goinstall reports the installation to godashboard.appspot.com,
-which increments a count associated with the package and the time
-of its most recent installation. This mechanism powers the package list
-at http://godashboard.appspot.com/package, allowing Go programmers
-to learn about popular packages that might be worth looking at.
+After a successful download and installation of one of these import paths,
+goinstall reports the installation to godashboard.appspot.com, which
+increments a count associated with the package and the time of its most
+recent installation. This mechanism powers the package list at
+http://godashboard.appspot.com/package, allowing Go programmers to learn about
+popular packages that might be worth looking at.
The -dashboard=false flag disables this reporting.
-By default, goinstall prints output only when it encounters an error.
-The -v flag causes goinstall to print information about packages
-being considered and installed.
+For code hosted on other servers, goinstall recognizes the general form
+
+ repository.vcs/path
+
+as denoting the given repository, with or without the .vcs suffix, using
+the named version control system, and then the path inside that repository.
+The supported version control systems are:
+
+ Bazaar .bzr
+ Git .git
+ Mercurial .hg
+ Subversion .svn
+
+For example,
+
+ import "example.org/user/foo.hg"
+
+denotes the root directory of the Mercurial repository at example.org/user/foo
+or foo.hg, and
+
+ import "example.org/repo.git/foo/bar"
+
+denotes the foo/bar directory of the Git repository at example.com/repo or
+repo.git.
-Goinstall does not use make. Makefiles are ignored by goinstall.
+When a version control system supports multiple protocols, goinstall tries each
+in turn.
+For example, for Git it tries git://, then https://, then http://.
The GOPATH Environment Variable
diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go
index d209fa82b..da892a69d 100644
--- a/src/cmd/goinstall/download.go
+++ b/src/cmd/goinstall/download.go
@@ -7,6 +7,8 @@
package main
import (
+ "exec"
+ "fmt"
"http"
"os"
"path/filepath"
@@ -31,11 +33,6 @@ func maybeReportToDashboard(path string) {
}
}
-type host struct {
- pattern *regexp.Regexp
- protocol string
-}
-
// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
@@ -57,9 +54,10 @@ type vcs struct {
defaultHosts []host
}
-type vcsMatch struct {
- *vcs
- prefix, repo string
+type host struct {
+ pattern *regexp.Regexp
+ protocol string
+ suffix string
}
var hg = vcs{
@@ -75,10 +73,11 @@ var hg = vcs{
logLimitFlag: "-l1",
logReleaseFlag: "-rrelease",
check: "identify",
- protocols: []string{"http"},
+ protocols: []string{"https", "http"},
+ suffix: ".hg",
defaultHosts: []host{
- {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https"},
- {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"},
+ {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
+ {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
},
}
@@ -94,11 +93,11 @@ var git = vcs{
log: "show-ref",
logLimitFlag: "",
logReleaseFlag: "release",
- check: "peek-remote",
- protocols: []string{"git", "http"},
+ check: "ls-remote",
+ protocols: []string{"git", "https", "http"},
suffix: ".git",
defaultHosts: []host{
- {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"},
+ {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
},
}
@@ -114,9 +113,10 @@ var svn = vcs{
logLimitFlag: "-l1",
logReleaseFlag: "release",
check: "info",
- protocols: []string{"http", "svn"},
+ protocols: []string{"https", "http", "svn"},
+ suffix: ".svn",
defaultHosts: []host{
- {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https"},
+ {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
},
}
@@ -134,23 +134,79 @@ var bzr = vcs{
logLimitFlag: "-l1",
logReleaseFlag: "-rrelease",
check: "info",
- protocols: []string{"http", "bzr"},
+ protocols: []string{"https", "http", "bzr"},
+ suffix: ".bzr",
defaultHosts: []host{
- {regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https"},
+ {regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
},
}
var vcsList = []*vcs{&git, &hg, &bzr, &svn}
+type vcsMatch struct {
+ *vcs
+ prefix, repo string
+}
+
+// findHostedRepo checks whether pkg is located at one of
+// the supported code hosting sites and, if so, returns a match.
+func findHostedRepo(pkg string) (*vcsMatch, os.Error) {
+ for _, v := range vcsList {
+ for _, host := range v.defaultHosts {
+ if hm := host.pattern.FindStringSubmatch(pkg); hm != nil {
+ if host.suffix != "" && strings.HasSuffix(hm[1], host.suffix) {
+ return nil, os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix")
+ }
+ repo := host.protocol + "://" + hm[1] + host.suffix
+ return &vcsMatch{v, hm[1], repo}, nil
+ }
+ }
+ }
+ return nil, nil
+}
+
+// findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match.
+func findAnyRepo(pkg string) (*vcsMatch, os.Error) {
+ for _, v := range vcsList {
+ i := strings.Index(pkg+"/", v.suffix+"/")
+ if i < 0 {
+ continue
+ }
+ if !strings.Contains(pkg[:i], "/") {
+ continue // don't match vcs suffix in the host name
+ }
+ if m := v.find(pkg[:i]); m != nil {
+ return m, nil
+ }
+ return nil, fmt.Errorf("couldn't find %s repository", v.name)
+ }
+ return nil, nil
+}
+
+func (v *vcs) find(pkg string) *vcsMatch {
+ for _, proto := range v.protocols {
+ for _, suffix := range []string{"", v.suffix} {
+ repo := proto + "://" + pkg + suffix
+ out, err := exec.Command(v.cmd, v.check, repo).CombinedOutput()
+ if err == nil {
+ printf("find %s: found %s\n", pkg, repo)
+ return &vcsMatch{v, pkg + v.suffix, repo}
+ }
+ printf("find %s: %s %s %s: %v\n%s\n", pkg, v.cmd, v.check, repo, err, out)
+ }
+ }
+ return nil
+}
+
// isRemote returns true if the first part of the package name looks like a
// hostname - i.e. contains at least one '.' and the last part is at least 2
// characters.
func isRemote(pkg string) bool {
- parts := strings.Split(pkg, "/", 2)
+ parts := strings.SplitN(pkg, "/", 2)
if len(parts) != 2 {
return false
}
- parts = strings.Split(parts[0], ".", -1)
+ parts = strings.Split(parts[0], ".")
if len(parts) < 2 || len(parts[len(parts)-1]) < 2 {
return false
}
@@ -158,26 +214,35 @@ func isRemote(pkg string) bool {
}
// download checks out or updates pkg from the remote server.
-func download(pkg, srcDir string) os.Error {
+func download(pkg, srcDir string) (dashReport bool, err os.Error) {
if strings.Contains(pkg, "..") {
- return os.NewError("invalid path (contains ..)")
+ err = os.NewError("invalid path (contains ..)")
+ return
}
- var m *vcsMatch
- for _, v := range vcsList {
- for _, host := range v.defaultHosts {
- if hm := host.pattern.FindStringSubmatch(pkg); hm != nil {
- if v.suffix != "" && strings.HasSuffix(hm[1], v.suffix) {
- return os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix")
- }
- repo := host.protocol + "://" + hm[1] + v.suffix
- m = &vcsMatch{v, hm[1], repo}
- }
+ m, err := findHostedRepo(pkg)
+ if err != nil {
+ return
+ }
+ if m != nil {
+ dashReport = true // only report public code hosting sites
+ } else {
+ m, err = findAnyRepo(pkg)
+ if err != nil {
+ return
}
}
if m == nil {
- return os.NewError("cannot download: " + pkg)
+ err = os.NewError("cannot download: " + pkg)
+ return
+ }
+ installed, err := m.checkoutRepo(srcDir, m.prefix, m.repo)
+ if err != nil {
+ return
+ }
+ if !installed {
+ dashReport = false
}
- return vcsCheckout(m.vcs, srcDir, m.prefix, m.repo, pkg)
+ return
}
// Try to detect if a "release" tag exists. If it does, update
@@ -196,47 +261,46 @@ func (v *vcs) updateRepo(dst string) os.Error {
return nil
}
-// vcsCheckout checks out repo into dst using vcs.
+// checkoutRepo checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release". If there is no
// such tag or branch, it falls back to the repository tip.
-func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error {
+func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) (installed bool, err os.Error) {
dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix))
dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
if err == nil && !dir.IsDirectory() {
- return os.NewError("not a directory: " + dst)
+ err = os.NewError("not a directory: " + dst)
+ return
}
if err != nil {
parent, _ := filepath.Split(dst)
- if err := os.MkdirAll(parent, 0777); err != nil {
- return err
+ if err = os.MkdirAll(parent, 0777); err != nil {
+ return
}
- if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
- return err
+ if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
+ return
}
- if err := vcs.updateRepo(dst); err != nil {
- return err
+ if err = vcs.updateRepo(dst); err != nil {
+ return
}
- // success on first installation - report
- maybeReportToDashboard(dashpath)
+ installed = true
} else if *update {
// Retrieve new revisions from the remote branch, if the VCS
// supports this operation independently (e.g. svn doesn't)
if vcs.pull != "" {
if vcs.pullForceFlag != "" {
- if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
- return err
+ if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
+ return
}
- } else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
- return err
+ } else if err = run(dst, nil, vcs.cmd, vcs.pull); err != nil {
+ return
}
}
-
// Update to release or latest revision
- if err := vcs.updateRepo(dst); err != nil {
- return err
+ if err = vcs.updateRepo(dst); err != nil {
+ return
}
}
- return nil
+ return
}
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
index bdf8469a0..5cdf0f18e 100644
--- a/src/cmd/goinstall/main.go
+++ b/src/cmd/goinstall/main.go
@@ -182,9 +182,10 @@ func install(pkg, parent string) {
}
// Download remote packages if not found or forced with -u flag.
remote := isRemote(pkg)
+ dashReport := false
if remote && (err == build.ErrNotFound || (err == nil && *update)) {
printf("%s: download\n", pkg)
- err = download(pkg, tree.SrcDir())
+ dashReport, err = download(pkg, tree.SrcDir())
}
if err != nil {
errorf("%s: %v\n", pkg, err)
@@ -243,6 +244,9 @@ func install(pkg, parent string) {
}
}
}
+ if dashReport {
+ maybeReportToDashboard(pkg)
+ }
if remote {
// mark package as installed in $GOROOT/goinstall.log
logPackage(pkg)
diff --git a/src/cmd/gotest/doc.go b/src/cmd/gotest/doc.go
index 9dba390c1..5be06f817 100644
--- a/src/cmd/gotest/doc.go
+++ b/src/cmd/gotest/doc.go
@@ -53,7 +53,9 @@ The resulting test binary, called (for amd64) 6.out, has several flags.
Usage:
6.out [-test.v] [-test.run pattern] [-test.bench pattern] \
[-test.cpuprofile=cpu.out] \
- [-test.memprofile=mem.out] [-test.memprofilerate=1]
+ [-test.memprofile=mem.out] [-test.memprofilerate=1] \
+ [-test.timeout=10] [-test.short] \
+ [-test.benchtime=3] [-test.cpu=1,2,3,4]
The -test.v flag causes the tests to be logged as they run. The
-test.run flag causes only those tests whose names match the regular
@@ -93,6 +95,13 @@ The -test.timeout flag sets a timeout for the test in seconds. If the
test runs for longer than that, it will panic, dumping a stack trace
of all existing goroutines.
+The -test.benchtime flag specifies the number of seconds to run each benchmark.
+The default is one second.
+
+The -test.cpu flag specifies a list of GOMAXPROCS values for which
+the tests or benchmarks are executed. The default is the current
+value of GOMAXPROCS.
+
For convenience, each of these -test.X flags of the test binary is
also available as the flag -X in gotest itself. Flags not listed here
are unaffected. For instance, the command
diff --git a/src/cmd/gotest/flag.go b/src/cmd/gotest/flag.go
index 780c78b9c..c3a28f9a3 100644
--- a/src/cmd/gotest/flag.go
+++ b/src/cmd/gotest/flag.go
@@ -23,6 +23,8 @@ var usageMessage = `Usage of %s:
// These flags can be passed with or without a "test." prefix: -v or -test.v.
-bench="": passes -test.bench to test
+ -benchtime=1: passes -test.benchtime to test
+ -cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test
-memprofilerate=0: passes -test.memprofilerate to test
@@ -56,6 +58,8 @@ var flagDefn = []*flagSpec{
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
&flagSpec{name: "bench", passToTest: true},
+ &flagSpec{name: "benchtime", passToTest: true},
+ &flagSpec{name: "cpu", passToTest: true},
&flagSpec{name: "cpuprofile", passToTest: true},
&flagSpec{name: "memprofile", passToTest: true},
&flagSpec{name: "memprofilerate", passToTest: true},
diff --git a/src/cmd/govet/Makefile b/src/cmd/govet/Makefile
index 291b27197..f565b78f5 100644
--- a/src/cmd/govet/Makefile
+++ b/src/cmd/govet/Makefile
@@ -9,3 +9,6 @@ GOFILES=\
govet.go\
include ../../Make.cmd
+
+test testshort: $(TARG)
+ ../../../test/errchk $(TARG) -printfuncs='Warn:1,Warnf:1' govet.go
diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go
index b811c61a2..5b24d2ff0 100644
--- a/src/cmd/govet/govet.go
+++ b/src/cmd/govet/govet.go
@@ -15,6 +15,7 @@ import (
"go/token"
"os"
"path/filepath"
+ "reflect"
"strconv"
"strings"
"utf8"
@@ -50,7 +51,7 @@ func main() {
flag.Parse()
if *printfuncs != "" {
- for _, name := range strings.Split(*printfuncs, ",", -1) {
+ for _, name := range strings.Split(*printfuncs, ",") {
if len(name) == 0 {
flag.Usage()
}
@@ -59,7 +60,7 @@ func main() {
var err os.Error
skip, err = strconv.Atoi(name[colon+1:])
if err != nil {
- error(`illegal format for "Func:N" argument %q; %s`, name, err)
+ errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
}
name = name[:colon]
}
@@ -93,7 +94,7 @@ func doFile(name string, reader io.Reader) {
fs := token.NewFileSet()
parsedFile, err := parser.ParseFile(fs, name, reader, 0)
if err != nil {
- error("%s: %s", name, err)
+ errorf("%s: %s", name, err)
return
}
file := &File{fs.File(parsedFile.Pos())}
@@ -121,7 +122,7 @@ func walkDir(root string) {
done := make(chan bool)
go func() {
for e := range errors {
- error("walk error: %s", e)
+ errorf("walk error: %s", e)
}
done <- true
}()
@@ -132,7 +133,7 @@ func walkDir(root string) {
// error formats the error to standard error, adding program
// identification and a newline
-func error(format string, args ...interface{}) {
+func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...)
setExit(2)
}
@@ -185,15 +186,35 @@ func (f *File) checkFile(name string, file *ast.File) {
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
- // TODO: could return nil for nodes that cannot contain a CallExpr -
- // will shortcut traversal. Worthwhile?
switch n := node.(type) {
case *ast.CallExpr:
f.checkCallExpr(n)
+ case *ast.Field:
+ f.checkFieldTag(n)
}
return f
}
+// checkField checks a struct field tag.
+func (f *File) checkFieldTag(field *ast.Field) {
+ if field.Tag == nil {
+ return
+ }
+
+ tag, err := strconv.Unquote(field.Tag.Value)
+ if err != nil {
+ f.Warnf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
+ return
+ }
+
+ // Check tag for validity by appending
+ // new key:value to end and checking that
+ // the tag parsing code can find it.
+ if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
+ f.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
+ return
+ }
+}
// checkCallExpr checks a call expression.
func (f *File) checkCallExpr(call *ast.CallExpr) {
@@ -358,19 +379,24 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) {
}
// This function never executes, but it serves as a simple test for the program.
-// Test with govet -printfuncs="Bad:1,Badf:1,Warn:1,Warnf:1" govet.go
+// Test with make test.
func BadFunctionUsedInTests() {
- fmt.Println() // niladic call
- fmt.Println("%s", "hi") // % in call to Println
- fmt.Printf("%s", "hi", 3) // wrong # percents
- fmt.Printf("%s%%%d", "hi", 3) // right # percents
- fmt.Printf("%.*d", 3, 3) // right # percents, with a *
- fmt.Printf("%.*d", 3, 3, 3) // wrong # percents, with a *
- printf("now is the time", "buddy") // no %s
- Printf("now is the time", "buddy") // no %s
+ fmt.Println() // not an error
+ fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
+ fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args in Printf call"
+ fmt.Printf("%s%%%d", "hi", 3) // correct
+ fmt.Printf("%.*d", 3, 3) // correct
+ fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args in Printf call"
+ printf("now is the time", "buddy") // ERROR "no formatting directive"
+ Printf("now is the time", "buddy") // ERROR "no formatting directive"
+ Printf("hi") // ok
f := new(File)
- f.Warn(0, "%s", "hello", 3) // % in call to added function
- f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function
+ f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
+ f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args in Warnf call"
+}
+
+type BadTypeUsedInTests struct {
+ X int "hello" // ERROR "struct field tag"
}
// printf is used by the test.
diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/goyacc/goyacc.go
index 220c99492..543f8b1e8 100644
--- a/src/cmd/goyacc/goyacc.go
+++ b/src/cmd/goyacc/goyacc.go
@@ -2834,7 +2834,7 @@ func others() {
// copy yaccpar
fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
- parts := strings.Split(yaccpar, prefix+"run()", 2)
+ parts := strings.SplitN(yaccpar, prefix+"run()", 2)
fmt.Fprintf(ftable, "%v", parts[0])
ftable.Write(fcode.Bytes())
fmt.Fprintf(ftable, "%v", parts[1])
diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go
index 1f3e5e736..4f7aec22b 100644
--- a/src/cmd/hgpatch/main.go
+++ b/src/cmd/hgpatch/main.go
@@ -176,7 +176,7 @@ func main() {
list[i] = f
i++
}
- sort.SortStrings(list)
+ sort.Strings(list)
for _, f := range list {
fmt.Printf("%s\n", f)
}
@@ -282,7 +282,7 @@ func hgModified() ([]string, os.Error) {
if err != nil {
return nil, err
}
- return strings.Split(strings.TrimSpace(out), "\n", -1), nil
+ return strings.Split(strings.TrimSpace(out), "\n"), nil
}
// hgAdd adds name to the repository.
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index bdad58ff9..f1132fc8b 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -789,14 +789,34 @@ dodata(void)
sect->vaddr = 0;
datsize = 0;
s = datap;
- for(; s != nil && s->type < SDATA; s = s->next) {
+ for(; s != nil && s->type < SSYMTAB; s = s->next) {
s->type = SRODATA;
t = rnd(s->size, PtrSize);
s->value = datsize;
datsize += t;
}
sect->len = datsize - sect->vaddr;
-
+
+ /* gosymtab */
+ sect = addsection(&segtext, ".gosymtab", 04);
+ sect->vaddr = datsize;
+ for(; s != nil && s->type < SPCLNTAB; s = s->next) {
+ s->type = SRODATA;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize - sect->vaddr;
+
+ /* gopclntab */
+ sect = addsection(&segtext, ".gopclntab", 04);
+ sect->vaddr = datsize;
+ for(; s != nil && s->type < SDATA; s = s->next) {
+ s->type = SRODATA;
+ s->value = datsize;
+ datsize += s->size;
+ }
+ sect->len = datsize - sect->vaddr;
+
/* data */
datsize = 0;
sect = addsection(&segdata, ".data", 06);
@@ -890,7 +910,7 @@ textaddress(void)
void
address(void)
{
- Section *s, *text, *data, *rodata;
+ Section *s, *text, *data, *rodata, *symtab, *pclntab;
Sym *sym, *sub;
uvlong va;
@@ -921,7 +941,9 @@ address(void)
segdata.filelen = segdata.sect->len; // assume .data is first
text = segtext.sect;
- rodata = segtext.sect->next;
+ rodata = text->next;
+ symtab = rodata->next;
+ pclntab = symtab->next;
data = segdata.sect;
for(sym = datap; sym != nil; sym = sym->next) {
@@ -938,12 +960,11 @@ address(void)
xdefine("etext", STEXT, text->vaddr + text->len);
xdefine("rodata", SRODATA, rodata->vaddr);
xdefine("erodata", SRODATA, rodata->vaddr + rodata->len);
+ xdefine("symtab", SRODATA, symtab->vaddr);
+ xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len);
+ xdefine("pclntab", SRODATA, pclntab->vaddr);
+ xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len);
xdefine("data", SBSS, data->vaddr);
xdefine("edata", SBSS, data->vaddr + data->len);
xdefine("end", SBSS, segdata.vaddr + segdata.len);
-
- sym = lookup("pclntab", 0);
- xdefine("epclntab", SRODATA, sym->value + sym->size);
- sym = lookup("symtab", 0);
- xdefine("esymtab", SRODATA, sym->value + sym->size);
}
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 04ee790a4..77a62f5de 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -956,7 +956,7 @@ pclntab(void)
uchar *bp;
sym = lookup("pclntab", 0);
- sym->type = SRODATA;
+ sym->type = SPCLNTAB;
sym->reachable = 1;
if(debug['s'])
return;
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index 463713143..347987195 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -40,6 +40,8 @@ enum
SSTRING,
SGOSTRING,
SRODATA,
+ SSYMTAB,
+ SPCLNTAB,
SDATA,
SMACHO, /* Mach-O __nl_symbol_ptr */
SMACHOGOT,
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index c66eca148..60e146b35 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -351,7 +351,7 @@ symtab(void)
s->reachable = 1;
symt = lookup("symtab", 0);
- symt->type = SRODATA;
+ symt->type = SSYMTAB;
symt->size = 0;
symt->reachable = 1;
@@ -372,5 +372,7 @@ symtab(void)
}
}
+ if(debug['s'])
+ return;
genasmsym(putsymb);
}
diff --git a/src/env.bash b/src/env.bash
index 19402f306..f83012a26 100644
--- a/src/env.bash
+++ b/src/env.bash
@@ -55,7 +55,6 @@ PROGS="
cp
cut
echo
- ed
egrep
gcc
grep
@@ -74,7 +73,7 @@ PROGS="
uniq
"
-for i in bison ed awk gcc $MAKE; do
+for i in $PROGS; do
if ! which $i >/dev/null 2>&1; then
echo "Cannot find '$i' on search path." 1>&2
echo "See http://golang.org/doc/install.html#ctools" 1>&2
diff --git a/src/lib9/Makefile b/src/lib9/Makefile
index d222e2f53..28c97c9b4 100644
--- a/src/lib9/Makefile
+++ b/src/lib9/Makefile
@@ -116,5 +116,6 @@ GOROOT_FINAL?=$(GOROOT)
$(HOST_CC) -c $(HOST_CFLAGS) $<
goos.$O: goos.c
- $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$(../version.bash)"'"' $<
+ GOVERSION=`../version.bash` && \
+ $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$GOVERSION"'"' $<
diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c
index c443a4fba..63abde313 100644
--- a/src/libmach/darwin.c
+++ b/src/libmach/darwin.c
@@ -222,12 +222,21 @@ addpid(int pid, int force)
// The excthread reads that port and signals
// us if we are waiting on that thread.
pthread_t p;
+ int err;
excport = mach_reply_port();
pthread_mutex_init(&mu, nil);
pthread_cond_init(&cond, nil);
- pthread_create(&p, nil, excthread, nil);
- pthread_create(&p, nil, waitthread, (void*)(uintptr)pid);
+ err = pthread_create(&p, nil, excthread, nil);
+ if (err != 0) {
+ fprint(2, "pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+ err = pthread_create(&p, nil, waitthread, (void*)(uintptr)pid);
+ if (err != 0) {
+ fprint(2, "pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
first = 0;
}
diff --git a/src/pkg/Makefile b/src/pkg/Makefile
index f18dc1f9b..7338399c2 100644
--- a/src/pkg/Makefile
+++ b/src/pkg/Makefile
@@ -62,6 +62,7 @@ DIRS=\
crypto/x509\
crypto/x509/pkix\
crypto/xtea\
+ csv\
debug/dwarf\
debug/macho\
debug/elf\
@@ -82,6 +83,7 @@ DIRS=\
exp/gui\
exp/gui/x11\
exp/regexp/syntax\
+ exp/template\
expvar\
flag\
fmt\
@@ -240,7 +242,6 @@ NOTEST+=\
../cmd/godoc\
../cmd/goinstall\
../cmd/gotest\
- ../cmd/govet\
../cmd/goyacc\
../cmd/hgpatch\
diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go
index 2650ef2a2..655772931 100644
--- a/src/pkg/asn1/asn1.go
+++ b/src/pkg/asn1/asn1.go
@@ -149,7 +149,7 @@ func (b BitString) RightAlign() []byte {
return a
}
-// parseBitString parses an ASN.1 bit string from the given byte array and returns it.
+// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
func parseBitString(bytes []byte) (ret BitString, err os.Error) {
if len(bytes) == 0 {
err = SyntaxError{"zero length BIT STRING"}
@@ -227,7 +227,7 @@ type Enumerated int
type Flag bool
// parseBase128Int parses a base-128 encoded int from the given offset in the
-// given byte array. It returns the value and the new offset.
+// given byte slice. It returns the value and the new offset.
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) {
offset = initOffset
for shifted := 0; offset < len(bytes); shifted++ {
@@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) {
return
}
-// parseGeneralizedTime parses the GeneralizedTime from the given byte array
+// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// and returns the resulting time.
func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) {
return time.Parse("20060102150405Z0700", string(bytes))
@@ -300,7 +300,7 @@ func isPrintable(b byte) bool {
// IA5String
// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
-// byte array and returns it.
+// byte slice and returns it.
func parseIA5String(bytes []byte) (ret string, err os.Error) {
for _, b := range bytes {
if b >= 0x80 {
@@ -315,11 +315,19 @@ func parseIA5String(bytes []byte) (ret string, err os.Error) {
// T61String
// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
-// byte array and returns it.
+// byte slice and returns it.
func parseT61String(bytes []byte) (ret string, err os.Error) {
return string(bytes), nil
}
+// UTF8String
+
+// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
+// array and returns it.
+func parseUTF8String(bytes []byte) (ret string, err os.Error) {
+ return string(bytes), nil
+}
+
// A RawValue represents an undecoded ASN.1 object.
type RawValue struct {
Class, Tag int
@@ -336,7 +344,7 @@ type RawContent []byte
// Tagging
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
-// into a byte array. It returns the parsed data and the new offset. SET and
+// into a byte slice. It returns the parsed data and the new offset. SET and
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
// don't distinguish between ordered and unordered objects in this code.
func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) {
@@ -393,7 +401,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
}
// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
-// a number of ASN.1 values from the given byte array and returns them as a
+// a number of ASN.1 values from the given byte slice and returns them as a
// slice of Go values of the given type.
func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) {
expectedTag, compoundType, ok := getUniversalType(elemType)
@@ -456,7 +464,7 @@ func invalidLength(offset, length, sliceLength int) bool {
return offset+length < offset || offset+length > sliceLength
}
-// parseField is the main parsing function. Given a byte array and an offset
+// parseField is the main parsing function. Given a byte slice and an offset
// into the array, it will try to parse a suitable ASN.1 value out and store it
// in the given Value.
func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) {
@@ -573,16 +581,15 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
}
- // Special case for strings: PrintableString and IA5String both map to
- // the Go type string. getUniversalType returns the tag for
- // PrintableString when it sees a string so, if we see an IA5String on
- // the wire, we change the universal type to match.
- if universalTag == tagPrintableString && t.tag == tagIA5String {
- universalTag = tagIA5String
- }
- // Likewise for GeneralString
- if universalTag == tagPrintableString && t.tag == tagGeneralString {
- universalTag = tagGeneralString
+ // Special case for strings: all the ASN.1 string types map to the Go
+ // type string. getUniversalType returns the tag for PrintableString
+ // when it sees a string, so if we see a different string type on the
+ // wire, we change the universal type to match.
+ if universalTag == tagPrintableString {
+ switch t.tag {
+ case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
+ universalTag = t.tag
+ }
}
// Special case for time: UTCTime and GeneralizedTime both map to the
@@ -707,7 +714,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
if i == 0 && field.Type == rawContentsType {
continue
}
- innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag))
+ innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
if err != nil {
return
}
@@ -738,6 +745,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
v, err = parseIA5String(innerBytes)
case tagT61String:
v, err = parseT61String(innerBytes)
+ case tagUTF8String:
+ v, err = parseUTF8String(innerBytes)
case tagGeneralString:
// GeneralString is specified in ISO-2022/ECMA-35,
// A brief review suggests that it includes structures
diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go
index 463dbe026..3c9478618 100644
--- a/src/pkg/asn1/asn1_test.go
+++ b/src/pkg/asn1/asn1_test.go
@@ -299,11 +299,11 @@ type TestObjectIdentifierStruct struct {
}
type TestContextSpecificTags struct {
- A int "tag:1"
+ A int `asn1:"tag:1"`
}
type TestContextSpecificTags2 struct {
- A int "explicit,tag:1"
+ A int `asn1:"explicit,tag:1"`
B int
}
@@ -353,7 +353,7 @@ type Certificate struct {
}
type TBSCertificate struct {
- Version int "optional,explicit,default:0,tag:0"
+ Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber RawValue
SignatureAlgorithm AlgorithmIdentifier
Issuer RDNSequence
diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go
index 9db887e25..01f4f7b6e 100644
--- a/src/pkg/asn1/common.go
+++ b/src/pkg/asn1/common.go
@@ -25,6 +25,7 @@ const (
tagOctetString = 4
tagOID = 6
tagEnum = 10
+ tagUTF8String = 12
tagSequence = 16
tagSet = 17
tagPrintableString = 19
@@ -83,7 +84,7 @@ type fieldParameters struct {
// parseFieldParameters will parse it into a fieldParameters structure,
// ignoring unknown parts of the string.
func parseFieldParameters(str string) (ret fieldParameters) {
- for _, part := range strings.Split(str, ",", -1) {
+ for _, part := range strings.Split(str, ",") {
switch {
case part == "optional":
ret.optional = true
diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go
index 7212c91ef..d7eb63bf8 100644
--- a/src/pkg/asn1/marshal.go
+++ b/src/pkg/asn1/marshal.go
@@ -413,7 +413,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
for i := startingField; i < t.NumField(); i++ {
var pre *forkableWriter
pre, out = out.fork()
- err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag))
+ err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
if err != nil {
return
}
diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go
index a9517634d..03df5f1e1 100644
--- a/src/pkg/asn1/marshal_test.go
+++ b/src/pkg/asn1/marshal_test.go
@@ -30,23 +30,23 @@ type rawContentsStruct struct {
}
type implicitTagTest struct {
- A int "implicit,tag:5"
+ A int `asn1:"implicit,tag:5"`
}
type explicitTagTest struct {
- A int "explicit,tag:5"
+ A int `asn1:"explicit,tag:5"`
}
type ia5StringTest struct {
- A string "ia5"
+ A string `asn1:"ia5"`
}
type printableStringTest struct {
- A string "printable"
+ A string `asn1:"printable"`
}
type optionalRawValueTest struct {
- A RawValue "optional"
+ A RawValue `asn1:"optional"`
}
type testSET []int
diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go
index 4d47a82d5..0948919cd 100755
--- a/src/pkg/big/int.go
+++ b/src/pkg/big/int.go
@@ -368,11 +368,60 @@ func (x *Int) Format(s fmt.State, ch int) {
format = "0X%s"
}
}
- if x.neg {
- format = "-" + format
+ t := fmt.Sprintf(format, x.abs.string(cs))
+
+ // insert spaces in hexadecimal formats if needed
+ if len(t) > 0 && s.Flag(' ') && (ch == 'x' || ch == 'X') {
+ spaces := (len(t)+1)/2 - 1
+ spaced := make([]byte, len(t)+spaces)
+ var i, j int
+ spaced[i] = t[j]
+ i++
+ j++
+ if len(t)&1 == 0 {
+ spaced[i] = t[j]
+ i++
+ j++
+ }
+ for j < len(t) {
+ spaced[i] = ' '
+ i++
+ spaced[i] = t[j]
+ i++
+ j++
+ spaced[i] = t[j]
+ i++
+ j++
+ }
+ t = string(spaced)
+ }
+
+ // determine sign prefix
+ prefix := ""
+ switch {
+ case x.neg:
+ prefix = "-"
+ case s.Flag('+'):
+ prefix = "+"
+ case s.Flag(' ') && ch != 'x' && ch != 'X':
+ prefix = " "
+ }
+
+ // fill to minimum width and prepend sign prefix
+ if width, ok := s.Width(); ok && len(t)+len(prefix) < width {
+ if s.Flag('0') {
+ t = fmt.Sprintf("%s%0*d%s", prefix, width-len(t)-len(prefix), 0, t)
+ } else {
+ if s.Flag('-') {
+ width = -width
+ }
+ t = fmt.Sprintf("%*s", width, prefix+t)
+ }
+ } else if prefix != "" {
+ t = prefix + t
}
- fmt.Fprintf(s, format, x.abs.string(cs))
+ fmt.Fprint(s, t)
}
@@ -417,6 +466,7 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, os.Error) {
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
func (z *Int) Scan(s fmt.ScanState, ch int) os.Error {
+ s.SkipSpace() // skip leading space characters
base := 0
switch ch {
case 'b':
@@ -585,7 +635,7 @@ func ProbablyPrime(z *Int, n int) bool {
}
-// Rand sets z to a pseudo-random number in [0, n) and returns z.
+// Rand sets z to a pseudo-random number in [0, n) and returns z.
func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
z.neg = false
if n.neg == true || len(n.abs) == 0 {
diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go
index 58a55030d..7f33c9522 100755
--- a/src/pkg/big/int_test.go
+++ b/src/pkg/big/int_test.go
@@ -376,6 +376,35 @@ var formatTests = []struct {
{"-10", "%#X", "-0XA"},
{"10", "%#y", "%!y(big.Int=10)"},
{"-10", "%#y", "%!y(big.Int=-10)"},
+
+ {"1234", "%d", "1234"},
+ {"1234", "%3d", "1234"},
+ {"1234", "%4d", "1234"},
+ {"-1234", "%d", "-1234"},
+ {"1234", "% 5d", " 1234"},
+ {"1234", "%+5d", "+1234"},
+ {"1234", "%-5d", "1234 "},
+ {"1234", "%x", "4d2"},
+ {"1234", "%X", "4D2"},
+ {"1234", "% x", "4 d2"},
+ {"-1234", "%3x", "-4d2"},
+ {"-1234", "%4x", "-4d2"},
+ {"-1234", "%5x", " -4d2"},
+ {"-1234", "%-5x", "-4d2 "},
+ {"-1234", "% x", "-4 d2"},
+ {"1234", "%03d", "1234"},
+ {"1234", "%04d", "1234"},
+ {"1234", "%05d", "01234"},
+ {"1234", "%06d", "001234"},
+ {"-1234", "%06d", "-01234"},
+ {"1234", "%+06d", "+01234"},
+ {"1234", "% 06d", " 01234"},
+ {"1234", "%-6d", "1234 "},
+ {"1234", "%-06d", "001234"},
+ {"-1234", "%-06d", "-01234"},
+ {"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // 10**100
+ "% x",
+ "12 49 ad 25 94 c3 7c eb 0b 27 84 c4 ce 0b f3 8a ce 40 8e 21 1a 7c aa b2 43 08 a8 2e 8f 10 00 00 00 00 00 00 00 00 00 00 00 00"},
}
@@ -391,7 +420,7 @@ func TestFormat(t *testing.T) {
}
output := fmt.Sprintf(test.format, x)
if output != test.output {
- t.Errorf("#%d got %s; want %s", i, output, test.output)
+ t.Errorf("#%d got %q; want %q", i, output, test.output)
}
}
}
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index 497e770fb..cb2667b28 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -103,6 +103,12 @@ func (b *Reader) fill() {
}
}
+func (b *Reader) readErr() os.Error {
+ err := b.err
+ b.err = nil
+ return err
+}
+
// Peek returns the next n bytes without advancing the reader. The bytes stop
// being valid at the next read call. If Peek returns fewer than n bytes, it
// also returns an error explaining why the read is short. The error is
@@ -121,7 +127,7 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) {
if m > n {
m = n
}
- err := b.err
+ err := b.readErr()
if m < n && err == nil {
err = ErrBufferFull
}
@@ -136,11 +142,11 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) {
func (b *Reader) Read(p []byte) (n int, err os.Error) {
n = len(p)
if n == 0 {
- return 0, b.err
+ return 0, b.readErr()
}
if b.w == b.r {
if b.err != nil {
- return 0, b.err
+ return 0, b.readErr()
}
if len(p) >= len(b.buf) {
// Large read, empty buffer.
@@ -150,11 +156,11 @@ func (b *Reader) Read(p []byte) (n int, err os.Error) {
b.lastByte = int(p[n-1])
b.lastRuneSize = -1
}
- return n, b.err
+ return n, b.readErr()
}
b.fill()
if b.w == b.r {
- return 0, b.err
+ return 0, b.readErr()
}
}
@@ -174,7 +180,7 @@ func (b *Reader) ReadByte() (c byte, err os.Error) {
b.lastRuneSize = -1
for b.w == b.r {
if b.err != nil {
- return 0, b.err
+ return 0, b.readErr()
}
b.fill()
}
@@ -210,7 +216,7 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) {
}
b.lastRuneSize = -1
if b.r == b.w {
- return 0, 0, b.err
+ return 0, 0, b.readErr()
}
rune, size = int(b.buf[b.r]), 1
if rune >= 0x80 {
@@ -262,7 +268,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
if b.err != nil {
line := b.buf[b.r:b.w]
b.r = b.w
- return line, b.err
+ return line, b.readErr()
}
n := b.Buffered()
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 123adac29..5709213c8 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -53,11 +53,12 @@ func readBytes(buf *Reader) string {
if e == os.EOF {
break
}
- if e != nil {
+ if e == nil {
+ b[nb] = c
+ nb++
+ } else if e != iotest.ErrTimeout {
panic("Data: " + e.String())
}
- b[nb] = c
- nb++
}
return string(b[0:nb])
}
@@ -86,6 +87,7 @@ var readMakers = []readMaker{
{"byte", iotest.OneByteReader},
{"half", iotest.HalfReader},
{"data+err", iotest.DataErrReader},
+ {"timeout", iotest.TimeoutReader},
}
// Call ReadString (which ends up calling everything else)
@@ -97,7 +99,7 @@ func readLines(b *Reader) string {
if e == os.EOF {
break
}
- if e != nil {
+ if e != nil && e != iotest.ErrTimeout {
panic("GetLines: " + e.String())
}
s += s1
diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go
index 0f9ac9863..3cec60f96 100644
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -212,26 +212,40 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte {
return a[0 : na+1]
}
-// Split slices s into subslices separated by sep and returns a slice of
+// SplitN slices s into subslices separated by sep and returns a slice of
// the subslices between those separators.
-// If sep is empty, Split splits after each UTF-8 sequence.
+// If sep is empty, SplitN splits after each UTF-8 sequence.
// The count determines the number of subslices to return:
// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
// n == 0: the result is nil (zero subslices)
// n < 0: all subslices
-func Split(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
+func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
-// SplitAfter slices s into subslices after each instance of sep and
+// SplitAfterN slices s into subslices after each instance of sep and
// returns a slice of those subslices.
-// If sep is empty, Split splits after each UTF-8 sequence.
+// If sep is empty, SplitAfterN splits after each UTF-8 sequence.
// The count determines the number of subslices to return:
// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
// n == 0: the result is nil (zero subslices)
// n < 0: all subslices
-func SplitAfter(s, sep []byte, n int) [][]byte {
+func SplitAfterN(s, sep []byte, n int) [][]byte {
return genSplit(s, sep, len(sep), n)
}
+// Split slices s into all subslices separated by sep and returns a slice of
+// the subslices between those separators.
+// If sep is empty, Split splits after each UTF-8 sequence.
+// It is equivalent to SplitN with a count of -1.
+func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) }
+
+// SplitAfter slices s into all subslices after each instance of sep and
+// returns a slice of those subslices.
+// If sep is empty, SplitAfter splits after each UTF-8 sequence.
+// It is equivalent to SplitAfterN with a count of -1.
+func SplitAfter(s, sep []byte) [][]byte {
+ return genSplit(s, sep, len(sep), -1)
+}
+
// Fields splits the array s around each instance of one or more consecutive white space
// characters, returning a slice of subarrays of s or an empty list if s contains only white space.
func Fields(s []byte) [][]byte {
diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go
index 4ce291a4f..753935309 100644
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -6,6 +6,7 @@ package bytes_test
import (
. "bytes"
+ "reflect"
"testing"
"unicode"
"utf8"
@@ -315,7 +316,7 @@ var explodetests = []ExplodeTest{
func TestExplode(t *testing.T) {
for _, tt := range explodetests {
- a := Split([]byte(tt.s), nil, tt.n)
+ a := SplitN([]byte(tt.s), nil, tt.n)
result := arrayOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a)
@@ -354,7 +355,7 @@ var splittests = []SplitTest{
func TestSplit(t *testing.T) {
for _, tt := range splittests {
- a := Split([]byte(tt.s), []byte(tt.sep), tt.n)
+ a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n)
result := arrayOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
@@ -367,6 +368,12 @@ func TestSplit(t *testing.T) {
if string(s) != tt.s {
t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
}
+ if tt.n < 0 {
+ b := Split([]byte(tt.s), []byte(tt.sep))
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
+ }
+ }
}
}
@@ -388,7 +395,7 @@ var splitaftertests = []SplitTest{
func TestSplitAfter(t *testing.T) {
for _, tt := range splitaftertests {
- a := SplitAfter([]byte(tt.s), []byte(tt.sep), tt.n)
+ a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n)
result := arrayOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
@@ -398,6 +405,12 @@ func TestSplitAfter(t *testing.T) {
if string(s) != tt.s {
t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
}
+ if tt.n < 0 {
+ b := SplitAfter([]byte(tt.s), []byte(tt.sep))
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("SplitAfter disagrees withSplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
+ }
+ }
}
}
diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go
index 72121a6b5..f8042b0d1 100644
--- a/src/pkg/compress/lzw/reader_test.go
+++ b/src/pkg/compress/lzw/reader_test.go
@@ -84,7 +84,7 @@ var lzwTests = []lzwTest{
func TestReader(t *testing.T) {
b := bytes.NewBuffer(nil)
for _, tt := range lzwTests {
- d := strings.Split(tt.desc, ";", -1)
+ d := strings.Split(tt.desc, ";")
var order Order
switch d[1] {
case "LSB":
diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go
index 3a9d02318..73223531e 100644
--- a/src/pkg/crypto/aes/cipher.go
+++ b/src/pkg/crypto/aes/cipher.go
@@ -45,14 +45,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) {
// BlockSize returns the AES block size, 16 bytes.
// It is necessary to satisfy the Cipher interface in the
-// package "crypto/block".
+// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
// Encrypt encrypts the 16-byte buffer src using the key k
// and stores the result in dst.
// Note that for amounts of data larger than a block,
// it is not safe to just call Encrypt on successive blocks;
-// instead, use an encryption mode like CBC (see crypto/block/cbc.go).
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) }
// Decrypt decrypts the 16-byte buffer src using the key k
diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go
index f3c5175ac..6c37dfe94 100644
--- a/src/pkg/crypto/blowfish/cipher.go
+++ b/src/pkg/crypto/blowfish/cipher.go
@@ -42,14 +42,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) {
// BlockSize returns the Blowfish block size, 8 bytes.
// It is necessary to satisfy the Cipher interface in the
-// package "crypto/block".
+// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
// Encrypt encrypts the 8-byte buffer src using the key k
// and stores the result in dst.
// Note that for amounts of data larger than a block,
// it is not safe to just call Encrypt on successive blocks;
-// instead, use an encryption mode like CBC (see crypto/block/cbc.go).
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
func (c *Cipher) Encrypt(dst, src []byte) {
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go
index 57dbe7d2d..e725bded8 100644
--- a/src/pkg/crypto/ocsp/ocsp.go
+++ b/src/pkg/crypto/ocsp/ocsp.go
@@ -43,7 +43,7 @@ type certID struct {
type responseASN1 struct {
Status asn1.Enumerated
- Response responseBytes "explicit,tag:0"
+ Response responseBytes `asn1:"explicit,tag:0"`
}
type responseBytes struct {
@@ -55,30 +55,30 @@ type basicResponse struct {
TBSResponseData responseData
SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString
- Certificates []asn1.RawValue "explicit,tag:0,optional"
+ Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
}
type responseData struct {
Raw asn1.RawContent
- Version int "optional,default:1,explicit,tag:0"
- RequestorName pkix.RDNSequence "optional,explicit,tag:1"
- KeyHash []byte "optional,explicit,tag:2"
+ 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
Responses []singleResponse
}
type singleResponse struct {
CertID certID
- Good asn1.Flag "explicit,tag:0,optional"
- Revoked revokedInfo "explicit,tag:1,optional"
- Unknown asn1.Flag "explicit,tag:2,optional"
+ 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 "explicit,tag:0,optional"
+ NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
}
type revokedInfo struct {
RevocationTime *time.Time
- Reason int "explicit,tag:0,optional"
+ Reason int `asn1:"explicit,tag:0,optional"`
}
// This is the exposed reflection of the internal OCSP structures.
diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go
index d12d07d7e..c70fb7927 100644
--- a/src/pkg/crypto/openpgp/keys.go
+++ b/src/pkg/crypto/openpgp/keys.go
@@ -12,6 +12,7 @@ import (
"crypto/rsa"
"io"
"os"
+ "time"
)
// PublicKeyType is the armor type for a PGP public key.
@@ -476,3 +477,69 @@ func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) {
}
return nil
}
+
+// Serialize writes the public part of the given Entity to w. (No private
+// key material will be output).
+func (e *Entity) Serialize(w io.Writer) os.Error {
+ err := e.PrimaryKey.Serialize(w)
+ if err != nil {
+ return err
+ }
+ for _, ident := range e.Identities {
+ err = ident.UserId.Serialize(w)
+ if err != nil {
+ return err
+ }
+ err = ident.SelfSignature.Serialize(w)
+ if err != nil {
+ return err
+ }
+ for _, sig := range ident.Signatures {
+ err = sig.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ for _, subkey := range e.Subkeys {
+ err = subkey.PublicKey.Serialize(w)
+ if err != nil {
+ return err
+ }
+ err = subkey.Sig.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SignIdentity adds a signature to e, from signer, attesting that identity is
+// associated with e. The provided identity must already be an element of
+// e.Identities and the private key of signer must have been decrypted if
+// necessary.
+func (e *Entity) SignIdentity(identity string, signer *Entity) os.Error {
+ if signer.PrivateKey == nil {
+ return error.InvalidArgumentError("signing Entity must have a private key")
+ }
+ if signer.PrivateKey.Encrypted {
+ return error.InvalidArgumentError("signing Entity's private key must be decrypted")
+ }
+ ident, ok := e.Identities[identity]
+ if !ok {
+ return error.InvalidArgumentError("given identity string not found in Entity")
+ }
+
+ sig := &packet.Signature{
+ SigType: packet.SigTypeGenericCert,
+ PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
+ Hash: crypto.SHA256,
+ CreationTime: uint32(time.Seconds()),
+ IssuerKeyId: &signer.PrivateKey.KeyId,
+ }
+ if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil {
+ return err
+ }
+ ident.Signatures = append(ident.Signatures, sig)
+ return nil
+}
diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go
index ba4d481f0..e6b0ae5f3 100644
--- a/src/pkg/crypto/openpgp/packet/public_key.go
+++ b/src/pkg/crypto/openpgp/packet/public_key.go
@@ -219,7 +219,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) {
panic("unknown public key algorithm")
}
- err = serializeHeader(w, packetTypePublicKey, length)
+ packetType := packetTypePublicKey
+ if pk.IsSubkey {
+ packetType = packetTypePublicSubkey
+ }
+ err = serializeHeader(w, packetType, length)
if err != nil {
return
}
@@ -279,14 +283,14 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
- err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature)
+ err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes)
if err != nil {
return error.SignatureError("RSA verification failure")
}
return nil
case PubKeyAlgoDSA:
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
- if !dsa.Verify(dsaPublicKey, hashBytes, sig.DSASigR, sig.DSASigS) {
+ if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) {
return error.SignatureError("DSA verification failure")
}
return nil
diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go
index 123c99fb2..7577e2875 100644
--- a/src/pkg/crypto/openpgp/packet/signature.go
+++ b/src/pkg/crypto/openpgp/packet/signature.go
@@ -5,7 +5,6 @@
package packet
import (
- "big"
"crypto"
"crypto/dsa"
"crypto/openpgp/error"
@@ -32,8 +31,11 @@ type Signature struct {
HashTag [2]byte
CreationTime uint32 // Unix epoch time
- RSASignature []byte
- DSASigR, DSASigS *big.Int
+ RSASignature parsedMPI
+ DSASigR, DSASigS parsedMPI
+
+ // rawSubpackets contains the unparsed subpackets, in order.
+ rawSubpackets []outputSubpacket
// The following are optional so are nil when not included in the
// signature.
@@ -128,14 +130,11 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) {
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sig.RSASignature, _, err = readMPI(r)
+ sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
case PubKeyAlgoDSA:
- var rBytes, sBytes []byte
- rBytes, _, err = readMPI(r)
- sig.DSASigR = new(big.Int).SetBytes(rBytes)
+ sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r)
if err == nil {
- sBytes, _, err = readMPI(r)
- sig.DSASigS = new(big.Int).SetBytes(sBytes)
+ sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
}
default:
panic("unreachable")
@@ -179,7 +178,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
// RFC 4880, section 5.2.3.1
var (
length uint32
- packetType byte
+ packetType signatureSubpacketType
isCritical bool
)
switch {
@@ -211,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
err = error.StructuralError("zero length signature subpacket")
return
}
- packetType = subpacket[0] & 0x7f
+ packetType = signatureSubpacketType(subpacket[0] & 0x7f)
isCritical = subpacket[0]&0x80 == 0x80
subpacket = subpacket[1:]
- switch signatureSubpacketType(packetType) {
+ sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket})
+ switch packetType {
case creationTimeSubpacket:
if !isHashed {
err = error.StructuralError("signature creation time in non-hashed area")
@@ -385,7 +385,6 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix() (err os.Error) {
- sig.outSubpackets = sig.buildSubpackets()
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)
var ok bool
@@ -428,6 +427,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error)
// the hash of the message to be signed and will be mutated by this function.
// On success, the signature is stored in sig. Call Serialize to write it out.
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) {
+ sig.outSubpackets = sig.buildSubpackets()
digest, err := sig.signPrepareHash(h)
if err != nil {
return
@@ -435,9 +435,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) {
switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
+ sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
+ sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes))
case PubKeyAlgoDSA:
- sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest)
+ r, s, err := dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest)
+ if err == nil {
+ sig.DSASigR.bytes = r.Bytes()
+ sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes))
+ sig.DSASigS.bytes = s.Bytes()
+ sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
+ }
default:
err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
}
@@ -468,17 +475,20 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey) os.Error {
// Serialize marshals sig to w. SignRSA or SignDSA must have been called first.
func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
- if sig.RSASignature == nil && sig.DSASigR == nil {
+ if len(sig.outSubpackets) == 0 {
+ sig.outSubpackets = sig.rawSubpackets
+ }
+ if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil {
return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize")
}
sigLength := 0
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sigLength = len(sig.RSASignature)
+ sigLength = 2 + len(sig.RSASignature.bytes)
case PubKeyAlgoDSA:
- sigLength = mpiLength(sig.DSASigR)
- sigLength += mpiLength(sig.DSASigS)
+ sigLength = 2 + len(sig.DSASigR.bytes)
+ sigLength += 2 + len(sig.DSASigS.bytes)
default:
panic("impossible")
}
@@ -486,7 +496,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
length := len(sig.HashSuffix) - 6 /* trailer not included */ +
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
- 2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength
+ 2 /* hash tag */ + sigLength
err = serializeHeader(w, packetTypeSignature, length)
if err != nil {
return
@@ -513,12 +523,9 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature)
+ err = writeMPIs(w, sig.RSASignature)
case PubKeyAlgoDSA:
- err = writeBig(w, sig.DSASigR)
- if err == nil {
- err = writeBig(w, sig.DSASigS)
- }
+ err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
default:
panic("impossible")
}
@@ -529,6 +536,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) {
type outputSubpacket struct {
hashed bool // true if this subpacket is in the hashed area.
subpacketType signatureSubpacketType
+ isCritical bool
contents []byte
}
@@ -538,12 +546,12 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
creationTime[1] = byte(sig.CreationTime >> 16)
creationTime[2] = byte(sig.CreationTime >> 8)
creationTime[3] = byte(sig.CreationTime)
- subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, creationTime})
+ subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
if sig.IssuerKeyId != nil {
keyId := make([]byte, 8)
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
- subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, keyId})
+ subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId})
}
return
diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go
index 1305548b2..c1bbde8b0 100644
--- a/src/pkg/crypto/openpgp/packet/signature_test.go
+++ b/src/pkg/crypto/openpgp/packet/signature_test.go
@@ -12,9 +12,7 @@ import (
)
func TestSignatureRead(t *testing.T) {
- signatureData, _ := hex.DecodeString(signatureDataHex)
- buf := bytes.NewBuffer(signatureData)
- packet, err := Read(buf)
+ packet, err := Read(readerFromHex(signatureDataHex))
if err != nil {
t.Error(err)
return
@@ -25,4 +23,20 @@ func TestSignatureRead(t *testing.T) {
}
}
-const signatureDataHex = "89011c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e"
+func TestSignatureReserialize(t *testing.T) {
+ packet, _ := Read(readerFromHex(signatureDataHex))
+ sig := packet.(*Signature)
+ out := new(bytes.Buffer)
+ err := sig.Serialize(out)
+ if err != nil {
+ t.Errorf("error reserializing: %s", err)
+ return
+ }
+
+ expected, _ := hex.DecodeString(signatureDataHex)
+ if !bytes.Equal(expected, out.Bytes()) {
+ t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected))
+ }
+}
+
+const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e"
diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go
index 683014752..d95f613c6 100644
--- a/src/pkg/crypto/openpgp/read.go
+++ b/src/pkg/crypto/openpgp/read.go
@@ -250,11 +250,12 @@ FindLiteralData:
md.IsSigned = true
md.SignedByKeyId = p.KeyId
keys := keyring.KeysById(p.KeyId)
- for _, key := range keys {
+ for i, key := range keys {
if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign {
continue
}
- md.SignedBy = &key
+ md.SignedBy = &keys[i]
+ break
}
case *packet.LiteralData:
md.LiteralData = p
diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go
index 7182e94b5..4dc290ef2 100644
--- a/src/pkg/crypto/openpgp/read_test.go
+++ b/src/pkg/crypto/openpgp/read_test.go
@@ -33,6 +33,29 @@ func TestReadKeyRing(t *testing.T) {
}
}
+func TestRereadKeyRing(t *testing.T) {
+ kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex))
+ if err != nil {
+ t.Errorf("error in initial parse: %s", err)
+ return
+ }
+ out := new(bytes.Buffer)
+ err = kring[0].Serialize(out)
+ if err != nil {
+ t.Errorf("error in serialization: %s", err)
+ return
+ }
+ kring, err = ReadKeyRing(out)
+ if err != nil {
+ t.Errorf("error in second parse: %s", err)
+ return
+ }
+
+ if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB {
+ t.Errorf("bad keyring: %#v", kring)
+ }
+}
+
func TestReadPrivateKeyRing(t *testing.T) {
kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
if err != nil {
diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go
index 281d6dc6a..0eab6b213 100755
--- a/src/pkg/crypto/rand/rand_windows.go
+++ b/src/pkg/crypto/rand/rand_windows.go
@@ -19,7 +19,7 @@ func init() { Reader = &rngReader{} }
// A rngReader satisfies reads by reading from the Windows CryptGenRandom API.
type rngReader struct {
- prov uint32
+ prov syscall.Handle
mu sync.Mutex
}
diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go
index f46188879..41206e276 100644
--- a/src/pkg/crypto/tls/generate_cert.go
+++ b/src/pkg/crypto/tls/generate_cert.go
@@ -8,8 +8,10 @@
package main
import (
- "crypto/rsa"
+ "big"
+ "crypto/x509/pkix"
"crypto/rand"
+ "crypto/rsa"
"crypto/x509"
"encoding/pem"
"flag"
@@ -32,8 +34,8 @@ func main() {
now := time.Seconds()
template := x509.Certificate{
- SerialNumber: []byte{0},
- Subject: x509.Name{
+ SerialNumber: new(big.Int).SetInt64(0),
+ Subject: pkix.Name{
CommonName: *hostName,
Organization: []string{"Acme Co"},
},
diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go
index 1a1aac9b9..2e537c606 100644
--- a/src/pkg/crypto/twofish/twofish.go
+++ b/src/pkg/crypto/twofish/twofish.go
@@ -269,7 +269,7 @@ func h(in, key []byte, offset int) uint32 {
// Encrypt encrypts a 16-byte block from src to dst, which may overlap.
// Note that for amounts of data larger than a block,
// it is not safe to just call Encrypt on successive blocks;
-// instead, use an encryption mode like CBC (see crypto/block/cbc.go).
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
func (c *Cipher) Encrypt(dst, src []byte) {
S1 := c.s[0]
S2 := c.s[1]
diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go
index 7806b2a2e..266fd557a 100644
--- a/src/pkg/crypto/x509/pkix/pkix.go
+++ b/src/pkg/crypto/x509/pkix/pkix.go
@@ -16,7 +16,7 @@ import (
// 5280, section 4.1.1.2.
type AlgorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
- Parameters asn1.RawValue "optional"
+ Parameters asn1.RawValue `asn1:"optional"`
}
type RDNSequence []RelativeDistinguishedNameSET
@@ -32,7 +32,7 @@ type AttributeTypeAndValue struct {
// 5280, section 4.2.
type Extension struct {
Id asn1.ObjectIdentifier
- Critical bool "optional"
+ Critical bool `asn1:"optional"`
Value []byte
}
@@ -149,13 +149,13 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
// 5280, section 5.1.
type TBSCertificateList struct {
Raw asn1.RawContent
- Version int "optional,default:2"
+ Version int `asn1:"optional,default:2"`
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate *time.Time
NextUpdate *time.Time
- RevokedCertificates []RevokedCertificate "optional"
- Extensions []Extension "tag:0,optional,explicit"
+ RevokedCertificates []RevokedCertificate `asn1:"optional"`
+ Extensions []Extension `asn1:"tag:0,optional,explicit"`
}
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
@@ -163,5 +163,5 @@ type TBSCertificateList struct {
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime *time.Time
- Extensions []Extension "optional"
+ Extensions []Extension `asn1:"optional"`
}
diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go
index 9145880a2..cad863db8 100644
--- a/src/pkg/crypto/x509/verify.go
+++ b/src/pkg/crypto/x509/verify.go
@@ -171,8 +171,14 @@ func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain [
chains = append(chains, appendToFreshChain(currentChain, root))
}
+nextIntermediate:
for _, intermediateNum := range opts.Intermediates.findVerifiedParents(c) {
intermediate := opts.Intermediates.certs[intermediateNum]
+ for _, cert := range currentChain {
+ if cert == intermediate {
+ continue nextIntermediate
+ }
+ }
err = intermediate.isValid(intermediateCertificate, opts)
if err != nil {
continue
@@ -202,8 +208,8 @@ func matchHostnames(pattern, host string) bool {
return false
}
- patternParts := strings.Split(pattern, ".", -1)
- hostParts := strings.Split(host, ".", -1)
+ patternParts := strings.Split(pattern, ".")
+ hostParts := strings.Split(host, ".")
if len(patternParts) != len(hostParts) {
return false
diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go
index 7a631186a..111f60eb1 100644
--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -72,23 +72,24 @@ var verifyTests = []verifyTest{
},
},
{
- leaf: googleLeaf,
- intermediates: []string{verisignRoot, thawteIntermediate},
- roots: []string{verisignRoot},
+ leaf: dnssecExpLeaf,
+ intermediates: []string{startComIntermediate},
+ roots: []string{startComRoot},
currentTime: 1302726541,
expectedChains: [][]string{
- []string{"Google", "Thawte", "VeriSign"},
+ []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
},
},
{
leaf: dnssecExpLeaf,
- intermediates: []string{startComIntermediate},
+ intermediates: []string{startComIntermediate, startComRoot},
roots: []string{startComRoot},
currentTime: 1302726541,
expectedChains: [][]string{
[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
+ []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
},
},
}
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index 8bafeda5c..348727a26 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -30,11 +30,11 @@ type pkcs1PrivateKey struct {
P *big.Int
Q *big.Int
// We ignore these values, if present, because rsa will calculate them.
- Dp *big.Int "optional"
- Dq *big.Int "optional"
- Qinv *big.Int "optional"
+ Dp *big.Int `asn1:"optional"`
+ Dq *big.Int `asn1:"optional"`
+ Qinv *big.Int `asn1:"optional"`
- AdditionalPrimes []pkcs1AdditionalRSAPrime "optional"
+ AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
}
type pkcs1AdditionalRSAPrime struct {
@@ -136,16 +136,16 @@ type certificate struct {
type tbsCertificate struct {
Raw asn1.RawContent
- Version int "optional,explicit,default:1,tag:0"
+ Version int `asn1:"optional,explicit,default:1,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer pkix.RDNSequence
Validity validity
Subject pkix.RDNSequence
PublicKey publicKeyInfo
- UniqueId asn1.BitString "optional,tag:1"
- SubjectUniqueId asn1.BitString "optional,tag:2"
- Extensions []pkix.Extension "optional,explicit,tag:3"
+ UniqueId asn1.BitString `asn1:"optional,tag:1"`
+ SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
+ Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
type dsaAlgorithmParameters struct {
@@ -168,7 +168,7 @@ type publicKeyInfo struct {
// RFC 5280, 4.2.1.1
type authKeyId struct {
- Id []byte "optional,tag:0"
+ Id []byte `asn1:"optional,tag:0"`
}
type SignatureAlgorithm int
@@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string {
}
type basicConstraints struct {
- IsCA bool "optional"
- MaxPathLen int "optional"
+ IsCA bool `asn1:"optional"`
+ MaxPathLen int `asn1:"optional"`
}
type rsaPublicKey struct {
@@ -497,14 +497,14 @@ type policyInformation struct {
// RFC 5280, 4.2.1.10
type nameConstraints struct {
- Permitted []generalSubtree "optional,tag:0"
- Excluded []generalSubtree "optional,tag:1"
+ Permitted []generalSubtree `asn1:"optional,tag:0"`
+ Excluded []generalSubtree `asn1:"optional,tag:1"`
}
type generalSubtree struct {
- Name string "tag:2,optional,ia5"
- Min int "optional,tag:0"
- Max int "optional,tag:1"
+ Name string `asn1:"tag:2,optional,ia5"`
+ Min int `asn1:"optional,tag:0"`
+ Max int `asn1:"optional,tag:1"`
}
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) {
diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go
index f2a5da003..b3fba3c84 100644
--- a/src/pkg/crypto/xtea/cipher.go
+++ b/src/pkg/crypto/xtea/cipher.go
@@ -48,13 +48,13 @@ func NewCipher(key []byte) (*Cipher, os.Error) {
// BlockSize returns the XTEA block size, 8 bytes.
// It is necessary to satisfy the Cipher interface in the
-// package "crypto/block".
+// package "crypto/cipher".
func (c *Cipher) BlockSize() int { return BlockSize }
// Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst.
// Note that for amounts of data larger than a block,
// it is not safe to just call Encrypt on successive blocks;
-// instead, use an encryption mode like CBC (see crypto/block/cbc.go).
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) }
// Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst.
diff --git a/src/pkg/csv/Makefile b/src/pkg/csv/Makefile
new file mode 100644
index 000000000..e364d51d2
--- /dev/null
+++ b/src/pkg/csv/Makefile
@@ -0,0 +1,12 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.inc
+
+TARG=csv
+GOFILES=\
+ reader.go\
+ writer.go\
+
+include ../../Make.pkg
diff --git a/src/pkg/csv/reader.go b/src/pkg/csv/reader.go
new file mode 100644
index 000000000..1f4b61cf9
--- /dev/null
+++ b/src/pkg/csv/reader.go
@@ -0,0 +1,373 @@
+// 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 csv reads and writes comma-separated values (CSV) files.
+//
+// A csv file contains zero or more records of one or more fields per record.
+// Each record is separated by the newline character. The final record may
+// optionally be followed by a newline character.
+//
+// field1,field2,field3
+//
+// White space is considered part of a field.
+//
+// Carriage returns before newline characters are silently removed.
+//
+// Blank lines are ignored. A line with only whitespace characters (excluding
+// the ending newline character) is not considered a blank line.
+//
+// Fields which start and stop with the quote character " are called
+// quoted-fields. The beginning and ending quote are not part of the
+// field.
+//
+// The source:
+//
+// normal string,"quoted-field"
+//
+// results in the fields
+//
+// {`normal string`, `quoted-field`}
+//
+// Within a quoted-field a quote character followed by a second quote
+// character is considered a single quote.
+//
+// "the ""word"" is true","a ""quoted-field"""
+//
+// results in
+//
+// {`the "word" is true`, `a "quoted-field"`}
+//
+// Newlines and commas may be included in a quoted-field
+//
+// "Multi-line
+// field","comma is ,"
+//
+// results in
+//
+// {`Multi-line
+// field`, `comma is ,`}
+package csv
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "unicode"
+)
+
+// A ParseError is returned for parsing errors.
+// The first line is 1. The first column is 0.
+type ParseError struct {
+ Line int // Line where the error occurred
+ Column int // Column (rune index) where the error occurred
+ Error os.Error // The actual error
+}
+
+func (e *ParseError) String() string {
+ return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Error)
+}
+
+// These are the errors that can be returned in ParseError.Error
+var (
+ ErrTrailingComma = os.NewError("extra delimiter at end of line")
+ ErrBareQuote = os.NewError("bare \" in non-quoted-field")
+ ErrQuote = os.NewError("extraneous \" in field")
+ ErrFieldCount = os.NewError("wrong number of fields in line")
+)
+
+// A Reader reads records from a CSV-encoded file.
+//
+// As returned by NewReader, a Reader expects input conforming to RFC 4180.
+// The exported fields can be changed to customize the details before the
+// first call to Read or ReadAll.
+//
+// Comma is the field delimiter. It defaults to ','.
+//
+// Comment, if not 0, is the comment character. Lines beginning with the
+// Comment character is ignored.
+//
+// If FieldsPerRecord is positive, Read requires each record to
+// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
+// the number of fields in the first record, so that future records must
+// have the same field count.
+//
+// If LazyQuotes is true, a quote may appear in an unquoted field and a
+// non-doubled quote may appear in a quoted field.
+//
+// If TrailingComma is true, the last field may be a unquoted empty field.
+//
+// If TrimLeadingSpace is true, leading white space in a field is ignored.
+type Reader struct {
+ Comma int // Field delimiter (set to ',' by NewReader)
+ Comment int // Comment character for start of line
+ FieldsPerRecord int // Number of expected fields per record
+ LazyQuotes bool // Allow lazy quotes
+ TrailingComma bool // Allow trailing comma
+ TrimLeadingSpace bool // Trim leading space
+ line int
+ column int
+ r *bufio.Reader
+ field bytes.Buffer
+}
+
+// NewReader returns a new Reader that reads from r.
+func NewReader(r io.Reader) *Reader {
+ return &Reader{
+ Comma: ',',
+ r: bufio.NewReader(r),
+ }
+}
+
+// error creates a new ParseError based on err.
+func (r *Reader) error(err os.Error) os.Error {
+ return &ParseError{
+ Line: r.line,
+ Column: r.column,
+ Error: err,
+ }
+}
+
+// Read reads one record from r. The record is a slice of strings with each
+// string representing one field.
+func (r *Reader) Read() (record []string, err os.Error) {
+ for {
+ record, err = r.parseRecord()
+ if record != nil {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if r.FieldsPerRecord > 0 {
+ if len(record) != r.FieldsPerRecord {
+ r.column = 0 // report at start of record
+ return record, r.error(ErrFieldCount)
+ }
+ } else if r.FieldsPerRecord == 0 {
+ r.FieldsPerRecord = len(record)
+ }
+ return record, nil
+}
+
+// ReadAll reads all the remaining records from r.
+// Each record is a slice of fields.
+func (r *Reader) ReadAll() (records [][]string, err os.Error) {
+ for {
+ record, err := r.Read()
+ if err == os.EOF {
+ return records, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+ records = append(records, record)
+ }
+ panic("unreachable")
+}
+
+// readRune reads one rune from r, folding \r\n to \n and keeping track
+// of our far into the line we have read. r.column will point to the start
+// of this rune, not the end of this rune.
+func (r *Reader) readRune() (int, os.Error) {
+ rune, _, err := r.r.ReadRune()
+
+ // Handle \r\n here. We make the simplifying assumption that
+ // anytime \r is followed by \n that it can be folded to \n.
+ // We will not detect files which contain both \r\n and bare \n.
+ if rune == '\r' {
+ rune, _, err = r.r.ReadRune()
+ if err == nil {
+ if rune != '\n' {
+ r.r.UnreadRune()
+ rune = '\r'
+ }
+ }
+ }
+ r.column++
+ return rune, err
+}
+
+// unreadRune puts the last rune read from r back.
+func (r *Reader) unreadRune() {
+ r.r.UnreadRune()
+ r.column--
+}
+
+// skip reads runes up to and including the rune delim or until error.
+func (r *Reader) skip(delim int) os.Error {
+ for {
+ rune, err := r.readRune()
+ if err != nil {
+ return err
+ }
+ if rune == delim {
+ return nil
+ }
+ }
+ panic("unreachable")
+}
+
+// parseRecord reads and parses a single csv record from r.
+func (r *Reader) parseRecord() (fields []string, err os.Error) {
+ // Each record starts on a new line. We increment our line
+ // number (lines start at 1, not 0) and set column to -1
+ // so as we increment in readRune it points to the character we read.
+ r.line++
+ r.column = -1
+
+ // Peek at the first rune. If it is an error we are done.
+ // If we are support comments and it is the comment character
+ // the skip to the end of line.
+
+ rune, _, err := r.r.ReadRune()
+ if err != nil {
+ return nil, err
+ }
+
+ if r.Comment != 0 && rune == r.Comment {
+ return nil, r.skip('\n')
+ }
+ r.r.UnreadRune()
+
+ // At this point we have at least one field.
+ for {
+ haveField, delim, err := r.parseField()
+ if haveField {
+ fields = append(fields, r.field.String())
+ }
+ if delim == '\n' || err == os.EOF {
+ return fields, err
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ panic("unreachable")
+}
+
+
+// parseField parses the next field in the record. The read field is
+// located in r.field. Delim is the first character not part of the field
+// (r.Comma or '\n').
+func (r *Reader) parseField() (haveField bool, delim int, err os.Error) {
+ r.field.Reset()
+
+ rune, err := r.readRune()
+ if err != nil {
+ // If we have EOF and are not at the start of a line
+ // then we return the empty field. We have already
+ // checked for trailing commas if needed.
+ if err == os.EOF && r.column != 0 {
+ return true, 0, err
+ }
+ return false, 0, err
+ }
+
+ if r.TrimLeadingSpace {
+ for unicode.IsSpace(rune) {
+ rune, err = r.readRune()
+ if err != nil {
+ return false, 0, err
+ }
+ }
+ }
+
+ switch rune {
+ case r.Comma:
+ // will check below
+
+ case '\n':
+ // We are a trailing empty field or a blank linke
+ if r.column == 0 {
+ return false, rune, nil
+ }
+ return true, rune, nil
+
+ case '"':
+ // quoted field
+ Quoted:
+ for {
+ rune, err = r.readRune()
+ if err != nil {
+ if err == os.EOF {
+ if r.LazyQuotes {
+ return true, 0, err
+ }
+ return false, 0, r.error(ErrQuote)
+ }
+ return false, 0, err
+ }
+ switch rune {
+ case '"':
+ rune, err = r.readRune()
+ if err != nil || rune == r.Comma {
+ break Quoted
+ }
+ if rune == '\n' {
+ return true, rune, nil
+ }
+ if rune != '"' {
+ if !r.LazyQuotes {
+ r.column--
+ return false, 0, r.error(ErrQuote)
+ }
+ // accept the bare quote
+ r.field.WriteRune('"')
+ }
+ case '\n':
+ r.line++
+ r.column = -1
+ }
+ r.field.WriteRune(rune)
+ }
+
+ default:
+ // unquoted field
+ for {
+ r.field.WriteRune(rune)
+ rune, err = r.readRune()
+ if err != nil || rune == r.Comma {
+ break
+ }
+ if rune == '\n' {
+ return true, rune, nil
+ }
+ if !r.LazyQuotes && rune == '"' {
+ return false, 0, r.error(ErrBareQuote)
+ }
+ }
+ }
+
+ if err != nil {
+ if err == os.EOF {
+ return true, 0, err
+ }
+ return false, 0, err
+ }
+
+ if !r.TrailingComma {
+ // We don't allow trailing commas. See if we
+ // are at the end of the line (being mindful
+ // of triming spaces
+ c := r.column
+ rune, err = r.readRune()
+ if r.TrimLeadingSpace {
+ for unicode.IsSpace(rune) {
+ rune, err = r.readRune()
+ if err != nil {
+ break
+ }
+ }
+ }
+ if err == os.EOF || rune == '\n' {
+ r.column = c // report the comma
+ return false, 0, r.error(ErrTrailingComma)
+ }
+ r.unreadRune()
+ }
+ return true, rune, nil
+}
diff --git a/src/pkg/csv/reader_test.go b/src/pkg/csv/reader_test.go
new file mode 100644
index 000000000..0068bad1d
--- /dev/null
+++ b/src/pkg/csv/reader_test.go
@@ -0,0 +1,265 @@
+// 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 csv
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+var readTests = []struct {
+ Name string
+ Input string
+ Output [][]string
+ UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1
+
+ // These fields are copied into the Reader
+ Comma int
+ Comment int
+ FieldsPerRecord int
+ LazyQuotes bool
+ TrailingComma bool
+ TrimLeadingSpace bool
+
+ Error string
+ Line int // Expected error line if != 0
+ Column int // Expected error column if line != 0
+}{
+ {
+ Name: "Simple",
+ Input: "a,b,c\n",
+ Output: [][]string{{"a", "b", "c"}},
+ },
+ {
+ Name: "CRLF",
+ Input: "a,b\r\nc,d\r\n",
+ Output: [][]string{{"a", "b"}, {"c", "d"}},
+ },
+ {
+ Name: "BareCR",
+ Input: "a,b\rc,d\r\n",
+ Output: [][]string{{"a", "b\rc", "d"}},
+ },
+ {
+ Name: "RFC4180test",
+ UseFieldsPerRecord: true,
+ Input: `#field1,field2,field3
+"aaa","bb
+b","ccc"
+"a,a","b""bb","ccc"
+zzz,yyy,xxx
+`,
+ Output: [][]string{
+ {"#field1", "field2", "field3"},
+ {"aaa", "bb\nb", "ccc"},
+ {"a,a", `b"bb`, "ccc"},
+ {"zzz", "yyy", "xxx"},
+ },
+ },
+ {
+ Name: "NoEOLTest",
+ Input: "a,b,c",
+ Output: [][]string{{"a", "b", "c"}},
+ },
+ {
+ Name: "Semicolon",
+ Comma: ';',
+ Input: "a;b;c\n",
+ Output: [][]string{{"a", "b", "c"}},
+ },
+ {
+ Name: "MultiLine",
+ Input: `"two
+line","one line","three
+line
+field"`,
+ Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}},
+ },
+ {
+ Name: "BlankLine",
+ Input: "a,b,c\n\nd,e,f\n\n",
+ Output: [][]string{
+ {"a", "b", "c"},
+ {"d", "e", "f"},
+ },
+ },
+ {
+ Name: "TrimSpace",
+ Input: " a, b, c\n",
+ TrimLeadingSpace: true,
+ Output: [][]string{{"a", "b", "c"}},
+ },
+ {
+ Name: "LeadingSpace",
+ Input: " a, b, c\n",
+ Output: [][]string{{" a", " b", " c"}},
+ },
+ {
+ Name: "Comment",
+ Comment: '#',
+ Input: "#1,2,3\na,b,c\n#comment",
+ Output: [][]string{{"a", "b", "c"}},
+ },
+ {
+ Name: "NoComment",
+ Input: "#1,2,3\na,b,c",
+ Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}},
+ },
+ {
+ Name: "LazyQuotes",
+ LazyQuotes: true,
+ Input: `a "word","1"2",a","b`,
+ Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}},
+ },
+ {
+ Name: "BareQuotes",
+ LazyQuotes: true,
+ Input: `a "word","1"2",a"`,
+ Output: [][]string{{`a "word"`, `1"2`, `a"`}},
+ },
+ {
+ Name: "BareDoubleQuotes",
+ LazyQuotes: true,
+ Input: `a""b,c`,
+ Output: [][]string{{`a""b`, `c`}},
+ },
+ {
+ Name: "BadDoubleQuotes",
+ Input: `a""b,c`,
+ Output: [][]string{{`a""b`, `c`}},
+ Error: `bare " in non-quoted-field`, Line: 1, Column: 1,
+ },
+ {
+ Name: "TrimQuote",
+ Input: ` "a"," b",c`,
+ TrimLeadingSpace: true,
+ Output: [][]string{{"a", " b", "c"}},
+ },
+ {
+ Name: "BadBareQuote",
+ Input: `a "word","b"`,
+ Error: `bare " in non-quoted-field`, Line: 1, Column: 2,
+ },
+ {
+ Name: "BadTrailingQuote",
+ Input: `"a word",b"`,
+ Error: `bare " in non-quoted-field`, Line: 1, Column: 10,
+ },
+ {
+ Name: "ExtraneousQuote",
+ Input: `"a "word","b"`,
+ Error: `extraneous " in field`, Line: 1, Column: 3,
+ },
+ {
+ Name: "BadFieldCount",
+ UseFieldsPerRecord: true,
+ Input: "a,b,c\nd,e",
+ Error: "wrong number of fields", Line: 2,
+ },
+ {
+ Name: "BadFieldCount1",
+ UseFieldsPerRecord: true,
+ FieldsPerRecord: 2,
+ Input: `a,b,c`,
+ Error: "wrong number of fields", Line: 1,
+ },
+ {
+ Name: "FieldCount",
+ Input: "a,b,c\nd,e",
+ Output: [][]string{{"a", "b", "c"}, {"d", "e"}},
+ },
+ {
+ Name: "BadTrailingCommaEOF",
+ Input: "a,b,c,",
+ Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ },
+ {
+ Name: "BadTrailingCommaEOL",
+ Input: "a,b,c,\n",
+ Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ },
+ {
+ Name: "BadTrailingCommaSpaceEOF",
+ TrimLeadingSpace: true,
+ Input: "a,b,c, ",
+ Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ },
+ {
+ Name: "BadTrailingCommaSpaceEOL",
+ TrimLeadingSpace: true,
+ Input: "a,b,c, \n",
+ Error: "extra delimiter at end of line", Line: 1, Column: 5,
+ },
+ {
+ Name: "BadTrailingCommaLine3",
+ TrimLeadingSpace: true,
+ Input: "a,b,c\nd,e,f\ng,hi,",
+ Error: "extra delimiter at end of line", Line: 3, Column: 4,
+ },
+ {
+ Name: "NotTrailingComma3",
+ Input: "a,b,c, \n",
+ Output: [][]string{{"a", "b", "c", " "}},
+ },
+ {
+ Name: "CommaFieldTest",
+ TrailingComma: true,
+ Input: `x,y,z,w
+x,y,z,
+x,y,,
+x,,,
+,,,
+"x","y","z","w"
+"x","y","z",""
+"x","y","",""
+"x","","",""
+"","","",""
+`,
+ Output: [][]string{
+ {"x", "y", "z", "w"},
+ {"x", "y", "z", ""},
+ {"x", "y", "", ""},
+ {"x", "", "", ""},
+ {"", "", "", ""},
+ {"x", "y", "z", "w"},
+ {"x", "y", "z", ""},
+ {"x", "y", "", ""},
+ {"x", "", "", ""},
+ {"", "", "", ""},
+ },
+ },
+}
+
+func TestRead(t *testing.T) {
+ for _, tt := range readTests {
+ r := NewReader(strings.NewReader(tt.Input))
+ r.Comment = tt.Comment
+ if tt.UseFieldsPerRecord {
+ r.FieldsPerRecord = tt.FieldsPerRecord
+ } else {
+ r.FieldsPerRecord = -1
+ }
+ r.LazyQuotes = tt.LazyQuotes
+ r.TrailingComma = tt.TrailingComma
+ r.TrimLeadingSpace = tt.TrimLeadingSpace
+ if tt.Comma != 0 {
+ r.Comma = tt.Comma
+ }
+ out, err := r.ReadAll()
+ perr, _ := err.(*ParseError)
+ if tt.Error != "" {
+ if err == nil || !strings.Contains(err.String(), tt.Error) {
+ t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error)
+ } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) {
+ t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column)
+ }
+ } else if err != nil {
+ t.Errorf("%s: unexpected error %v", tt.Name, err)
+ } else if !reflect.DeepEqual(out, tt.Output) {
+ t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output)
+ }
+ }
+}
diff --git a/src/pkg/csv/writer.go b/src/pkg/csv/writer.go
new file mode 100644
index 000000000..01386da19
--- /dev/null
+++ b/src/pkg/csv/writer.go
@@ -0,0 +1,123 @@
+// 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 csv
+
+import (
+ "bufio"
+ "io"
+ "os"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// A Writer writes records to a CSV encoded file.
+//
+// As returned by NewWriter, a Writer writes records terminated by a
+// newline and uses ',' as the field delimiter. The exported fields can be
+// changed to customize the details before the first call to Write or WriteAll.
+//
+// Comma is the field delimiter.
+//
+// If UseCRLF is true, the Writer ends each record with \r\n instead of \n.
+// just \n is written.
+type Writer struct {
+ Comma int // Field delimiter (set to to ',' by NewWriter)
+ UseCRLF bool // True to use \r\n as the line terminator
+ w *bufio.Writer
+}
+
+// NewWriter returns a new Writer that writes to w.
+func NewWriter(w io.Writer) *Writer {
+ return &Writer{
+ Comma: ',',
+ w: bufio.NewWriter(w),
+ }
+}
+
+// Writer writes a single CSV record to w along with any necessary quoting.
+// A record is a slice of strings with each string being one field.
+func (w *Writer) Write(record []string) (err os.Error) {
+ for n, field := range record {
+ if n > 0 {
+ if _, err = w.w.WriteRune(w.Comma); err != nil {
+ return
+ }
+ }
+
+ // If we don't have to have a quoted field then just
+ // write out the field and continue to the next field.
+ if !w.fieldNeedsQuotes(field) {
+ if _, err = w.w.WriteString(field); err != nil {
+ return
+ }
+ continue
+ }
+ if err = w.w.WriteByte('"'); err != nil {
+ return
+ }
+
+ for _, rune := range field {
+ switch rune {
+ case '"':
+ _, err = w.w.WriteString(`""`)
+ case '\r':
+ if !w.UseCRLF {
+ err = w.w.WriteByte('\r')
+ }
+ case '\n':
+ if w.UseCRLF {
+ _, err = w.w.WriteString("\r\n")
+ } else {
+ err = w.w.WriteByte('\n')
+ }
+ default:
+ _, err = w.w.WriteRune(rune)
+ }
+ if err != nil {
+ return
+ }
+ }
+
+ if err = w.w.WriteByte('"'); err != nil {
+ return
+ }
+ }
+ if w.UseCRLF {
+ _, err = w.w.WriteString("\r\n")
+ } else {
+ err = w.w.WriteByte('\n')
+ }
+ return
+}
+
+// Flush writes any buffered data to the underlying io.Writer.
+func (w *Writer) Flush() {
+ w.w.Flush()
+}
+
+// WriteAll writes multiple CSV records to w using Write and then calls Flush.
+func (w *Writer) WriteAll(records [][]string) (err os.Error) {
+ for _, record := range records {
+ err = w.Write(record)
+ if err != nil {
+ break
+ }
+ }
+ w.Flush()
+ return nil
+}
+
+// fieldNeedsQuotes returns true if our field must be enclosed in quotes.
+// Empty fields, files with a Comma, fields with a quote or newline, and
+// fields which start with a space must be enclosed in quotes.
+func (w *Writer) fieldNeedsQuotes(field string) bool {
+ if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 {
+ return true
+ }
+
+ rune, _ := utf8.DecodeRuneInString(field)
+ return unicode.IsSpace(rune)
+}
diff --git a/src/pkg/csv/writer_test.go b/src/pkg/csv/writer_test.go
new file mode 100644
index 000000000..578959007
--- /dev/null
+++ b/src/pkg/csv/writer_test.go
@@ -0,0 +1,44 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package csv
+
+import (
+ "bytes"
+ "testing"
+)
+
+var writeTests = []struct {
+ Input [][]string
+ Output string
+ UseCRLF bool
+}{
+ {Input: [][]string{{"abc"}}, Output: "abc\n"},
+ {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true},
+ {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"},
+ {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"},
+ {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"},
+ {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"},
+ {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"},
+ {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"},
+ {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"},
+ {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"},
+ {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true},
+}
+
+func TestWrite(t *testing.T) {
+ for n, tt := range writeTests {
+ b := &bytes.Buffer{}
+ f := NewWriter(b)
+ f.UseCRLF = tt.UseCRLF
+ err := f.WriteAll(tt.Input)
+ if err != nil {
+ t.Errorf("Unexpected error: %s\n", err)
+ }
+ out := b.String()
+ if out != tt.Output {
+ t.Errorf("#%d: out=%q want %q", n, out, tt.Output)
+ }
+ }
+}
diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go
index 5831b0e97..7ec797114 100644
--- a/src/pkg/debug/proc/proc_linux.go
+++ b/src/pkg/debug/proc/proc_linux.go
@@ -1229,7 +1229,7 @@ func (p *process) attachAllThreads() os.Error {
return err
}
- statParts := strings.Split(string(statFile), " ", 4)
+ statParts := strings.SplitN(string(statFile), " ", 4)
if len(statParts) > 2 && statParts[2] == "Z" {
// tid is a zombie
p.logTrace("thread %d is a zombie", tid)
diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go
index c45a7d70a..f6cebb905 100644
--- a/src/pkg/exec/exec_test.go
+++ b/src/pkg/exec/exec_test.go
@@ -55,7 +55,7 @@ func TestCatGoodAndBadFile(t *testing.T) {
t.Errorf("expected Waitmsg from cat combined; got %T: %v", err, err)
}
s := string(bs)
- sp := strings.Split(s, "\n", 2)
+ sp := strings.SplitN(s, "\n", 2)
if len(sp) != 2 {
t.Fatalf("expected two lines from cat; got %q", s)
}
diff --git a/src/pkg/exec/lp_plan9.go b/src/pkg/exec/lp_plan9.go
index c4e2a7a0f..e4751a4df 100644
--- a/src/pkg/exec/lp_plan9.go
+++ b/src/pkg/exec/lp_plan9.go
@@ -42,7 +42,7 @@ func LookPath(file string) (string, os.Error) {
}
path := os.Getenv("path")
- for _, dir := range strings.Split(path, "\000", -1) {
+ for _, dir := range strings.Split(path, "\000") {
if err := findExecutable(dir + "/" + file); err == nil {
return dir + "/" + file, nil
}
diff --git a/src/pkg/exec/lp_unix.go b/src/pkg/exec/lp_unix.go
index cdf720768..008fb11a8 100644
--- a/src/pkg/exec/lp_unix.go
+++ b/src/pkg/exec/lp_unix.go
@@ -39,7 +39,7 @@ func LookPath(file string) (string, os.Error) {
return "", &Error{file, err}
}
pathenv := os.Getenv("PATH")
- for _, dir := range strings.Split(pathenv, ":", -1) {
+ for _, dir := range strings.Split(pathenv, ":") {
if dir == "" {
// Unix shell semantics: path element "" means "."
dir = "."
diff --git a/src/pkg/exec/lp_windows.go b/src/pkg/exec/lp_windows.go
index 47763458f..7581088eb 100644
--- a/src/pkg/exec/lp_windows.go
+++ b/src/pkg/exec/lp_windows.go
@@ -47,7 +47,7 @@ func LookPath(file string) (f string, err os.Error) {
x = `.COM;.EXE;.BAT;.CMD`
}
exts := []string{}
- for _, e := range strings.Split(strings.ToLower(x), `;`, -1) {
+ for _, e := range strings.Split(strings.ToLower(x), `;`) {
if e == "" {
continue
}
@@ -67,7 +67,7 @@ func LookPath(file string) (f string, err os.Error) {
return
}
} else {
- for _, dir := range strings.Split(pathenv, `;`, -1) {
+ for _, dir := range strings.Split(pathenv, `;`) {
if f, err = findExecutable(dir+`\`+file, exts); err == nil {
return
}
diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go
index a8db523ea..ff0d24c69 100644
--- a/src/pkg/exp/ogle/cmd.go
+++ b/src/pkg/exp/ogle/cmd.go
@@ -154,7 +154,7 @@ func cmdLoad(args []byte) os.Error {
}
println("Attached to", pid)
} else {
- parts := strings.Split(path, " ", -1)
+ parts := strings.Split(path, " ")
if len(parts) == 0 {
fname = ""
} else {
diff --git a/src/pkg/exp/regexp/syntax/Makefile b/src/pkg/exp/regexp/syntax/Makefile
index 8e0b4c1e6..97d4ad6ca 100644
--- a/src/pkg/exp/regexp/syntax/Makefile
+++ b/src/pkg/exp/regexp/syntax/Makefile
@@ -6,8 +6,11 @@ include ../../../../Make.inc
TARG=exp/regexp/syntax
GOFILES=\
+ compile.go\
parse.go\
perl_groups.go\
+ prog.go\
regexp.go\
+ simplify.go\
include ../../../../Make.pkg
diff --git a/src/pkg/exp/regexp/syntax/compile.go b/src/pkg/exp/regexp/syntax/compile.go
new file mode 100644
index 000000000..ec9556fde
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/compile.go
@@ -0,0 +1,264 @@
+package syntax
+
+import (
+ "os"
+ "unicode"
+)
+
+// A patchList is a list of instruction pointers that need to be filled in (patched).
+// Because the pointers haven't been filled in yet, we can reuse their storage
+// to hold the list. It's kind of sleazy, but works well in practice.
+// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration.
+//
+// These aren't really pointers: they're integers, so we can reinterpret them
+// this way without using package unsafe. A value l denotes
+// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1).
+// l == 0 denotes the empty list, okay because we start every program
+// with a fail instruction, so we'll never want to point at its output link.
+type patchList uint32
+
+func (l patchList) next(p *Prog) patchList {
+ i := &p.Inst[l>>1]
+ if l&1 == 0 {
+ return patchList(i.Out)
+ }
+ return patchList(i.Arg)
+}
+
+func (l patchList) patch(p *Prog, val uint32) {
+ for l != 0 {
+ i := &p.Inst[l>>1]
+ if l&1 == 0 {
+ l = patchList(i.Out)
+ i.Out = val
+ } else {
+ l = patchList(i.Arg)
+ i.Arg = val
+ }
+ }
+}
+
+func (l1 patchList) append(p *Prog, l2 patchList) patchList {
+ if l1 == 0 {
+ return l2
+ }
+ if l2 == 0 {
+ return l1
+ }
+
+ last := l1
+ for {
+ next := last.next(p)
+ if next == 0 {
+ break
+ }
+ last = next
+ }
+
+ i := &p.Inst[last>>1]
+ if last&1 == 0 {
+ i.Out = uint32(l2)
+ } else {
+ i.Arg = uint32(l2)
+ }
+ return l1
+}
+
+// A frag represents a compiled program fragment.
+type frag struct {
+ i uint32 // index of first instruction
+ out patchList // where to record end instruction
+}
+
+type compiler struct {
+ p *Prog
+}
+
+// Compile compiles the regexp into a program to be executed.
+func Compile(re *Regexp) (*Prog, os.Error) {
+ var c compiler
+ c.init()
+ f := c.compile(re)
+ f.out.patch(c.p, c.inst(InstMatch).i)
+ c.p.Start = int(f.i)
+ return c.p, nil
+}
+
+func (c *compiler) init() {
+ c.p = new(Prog)
+ c.inst(InstFail)
+}
+
+var anyRuneNotNL = []int{0, '\n' - 1, '\n' - 1, unicode.MaxRune}
+var anyRune = []int{0, unicode.MaxRune}
+
+func (c *compiler) compile(re *Regexp) frag {
+ switch re.Op {
+ case OpNoMatch:
+ return c.fail()
+ case OpEmptyMatch:
+ return c.nop()
+ case OpLiteral:
+ if len(re.Rune) == 0 {
+ return c.nop()
+ }
+ var f frag
+ for j := range re.Rune {
+ f1 := c.rune(re.Rune[j : j+1])
+ if j == 0 {
+ f = f1
+ } else {
+ f = c.cat(f, f1)
+ }
+ }
+ return f
+ case OpCharClass:
+ return c.rune(re.Rune)
+ case OpAnyCharNotNL:
+ return c.rune(anyRuneNotNL)
+ case OpAnyChar:
+ return c.rune(anyRune)
+ case OpBeginLine:
+ return c.empty(EmptyBeginLine)
+ case OpEndLine:
+ return c.empty(EmptyEndLine)
+ case OpBeginText:
+ return c.empty(EmptyBeginText)
+ case OpEndText:
+ return c.empty(EmptyEndText)
+ case OpWordBoundary:
+ return c.empty(EmptyWordBoundary)
+ case OpNoWordBoundary:
+ return c.empty(EmptyNoWordBoundary)
+ case OpCapture:
+ bra := c.cap(uint32(re.Cap << 1))
+ sub := c.compile(re.Sub[0])
+ ket := c.cap(uint32(re.Cap<<1 | 1))
+ return c.cat(c.cat(bra, sub), ket)
+ case OpStar:
+ return c.star(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
+ case OpPlus:
+ return c.plus(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
+ case OpQuest:
+ return c.quest(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
+ case OpConcat:
+ if len(re.Sub) == 0 {
+ return c.nop()
+ }
+ var f frag
+ for i, sub := range re.Sub {
+ if i == 0 {
+ f = c.compile(sub)
+ } else {
+ f = c.cat(f, c.compile(sub))
+ }
+ }
+ return f
+ case OpAlternate:
+ var f frag
+ for _, sub := range re.Sub {
+ f = c.alt(f, c.compile(sub))
+ }
+ return f
+ }
+ panic("regexp: unhandled case in compile")
+}
+
+func (c *compiler) inst(op InstOp) frag {
+ // TODO: impose length limit
+ f := frag{i: uint32(len(c.p.Inst))}
+ c.p.Inst = append(c.p.Inst, Inst{Op: op})
+ return f
+}
+
+func (c *compiler) nop() frag {
+ f := c.inst(InstNop)
+ f.out = patchList(f.i << 1)
+ return f
+}
+
+func (c *compiler) fail() frag {
+ return frag{}
+}
+
+func (c *compiler) cap(arg uint32) frag {
+ f := c.inst(InstCapture)
+ f.out = patchList(f.i << 1)
+ c.p.Inst[f.i].Arg = arg
+ return f
+}
+
+func (c *compiler) cat(f1, f2 frag) frag {
+ // concat of failure is failure
+ if f1.i == 0 || f2.i == 0 {
+ return frag{}
+ }
+
+ // TODO: elide nop
+
+ f1.out.patch(c.p, f2.i)
+ return frag{f1.i, f2.out}
+}
+
+func (c *compiler) alt(f1, f2 frag) frag {
+ // alt of failure is other
+ if f1.i == 0 {
+ return f2
+ }
+ if f2.i == 0 {
+ return f1
+ }
+
+ f := c.inst(InstAlt)
+ i := &c.p.Inst[f.i]
+ i.Out = f1.i
+ i.Arg = f2.i
+ f.out = f1.out.append(c.p, f2.out)
+ return f
+}
+
+func (c *compiler) quest(f1 frag, nongreedy bool) frag {
+ f := c.inst(InstAlt)
+ i := &c.p.Inst[f.i]
+ if nongreedy {
+ i.Arg = f1.i
+ f.out = patchList(f.i << 1)
+ } else {
+ i.Out = f1.i
+ f.out = patchList(f.i<<1 | 1)
+ }
+ f.out = f.out.append(c.p, f1.out)
+ return f
+}
+
+func (c *compiler) star(f1 frag, nongreedy bool) frag {
+ f := c.inst(InstAlt)
+ i := &c.p.Inst[f.i]
+ if nongreedy {
+ i.Arg = f1.i
+ f.out = patchList(f.i << 1)
+ } else {
+ i.Out = f1.i
+ f.out = patchList(f.i<<1 | 1)
+ }
+ f1.out.patch(c.p, f.i)
+ return f
+}
+
+func (c *compiler) plus(f1 frag, nongreedy bool) frag {
+ return frag{f1.i, c.star(f1, nongreedy).out}
+}
+
+func (c *compiler) empty(op EmptyOp) frag {
+ f := c.inst(InstEmptyWidth)
+ c.p.Inst[f.i].Arg = uint32(op)
+ f.out = patchList(f.i << 1)
+ return f
+}
+
+func (c *compiler) rune(rune []int) frag {
+ f := c.inst(InstRune)
+ c.p.Inst[f.i].Rune = rune
+ f.out = patchList(f.i << 1)
+ return f
+}
diff --git a/src/pkg/exp/regexp/syntax/parse.go b/src/pkg/exp/regexp/syntax/parse.go
index d04f25097..b6c91f7e1 100644
--- a/src/pkg/exp/regexp/syntax/parse.go
+++ b/src/pkg/exp/regexp/syntax/parse.go
@@ -79,28 +79,108 @@ const (
type parser struct {
flags Flags // parse mode flags
stack []*Regexp // stack of parsed expressions
- numCap int // number of capturing groups seen
+ free *Regexp
+ numCap int // number of capturing groups seen
wholeRegexp string
+ tmpClass []int // temporary char class work space
+}
+
+func (p *parser) newRegexp(op Op) *Regexp {
+ re := p.free
+ if re != nil {
+ p.free = re.Sub0[0]
+ *re = Regexp{}
+ } else {
+ re = new(Regexp)
+ }
+ re.Op = op
+ return re
+}
+
+func (p *parser) reuse(re *Regexp) {
+ re.Sub0[0] = p.free
+ p.free = re
}
// Parse stack manipulation.
// push pushes the regexp re onto the parse stack and returns the regexp.
func (p *parser) push(re *Regexp) *Regexp {
- // TODO: automatic concatenation
- // TODO: turn character class into literal
- // TODO: compute simple
+ if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] {
+ // Single rune.
+ if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) {
+ return nil
+ }
+ re.Op = OpLiteral
+ re.Rune = re.Rune[:1]
+ re.Flags = p.flags &^ FoldCase
+ } else if re.Op == OpCharClass && len(re.Rune) == 4 &&
+ re.Rune[0] == re.Rune[1] && re.Rune[2] == re.Rune[3] &&
+ unicode.SimpleFold(re.Rune[0]) == re.Rune[2] &&
+ unicode.SimpleFold(re.Rune[2]) == re.Rune[0] ||
+ re.Op == OpCharClass && len(re.Rune) == 2 &&
+ re.Rune[0]+1 == re.Rune[1] &&
+ unicode.SimpleFold(re.Rune[0]) == re.Rune[1] &&
+ unicode.SimpleFold(re.Rune[1]) == re.Rune[0] {
+ // Case-insensitive rune like [Aa] or [Δδ].
+ if p.maybeConcat(re.Rune[0], p.flags|FoldCase) {
+ return nil
+ }
+
+ // Rewrite as (case-insensitive) literal.
+ re.Op = OpLiteral
+ re.Rune = re.Rune[:1]
+ re.Flags = p.flags | FoldCase
+ } else {
+ // Incremental concatenation.
+ p.maybeConcat(-1, 0)
+ }
p.stack = append(p.stack, re)
return re
}
-// newLiteral returns a new OpLiteral Regexp with the given flags
-func newLiteral(r int, flags Flags) *Regexp {
- re := &Regexp{
- Op: OpLiteral,
- Flags: flags,
+// maybeConcat implements incremental concatenation
+// of literal runes into string nodes. The parser calls this
+// before each push, so only the top fragment of the stack
+// might need processing. Since this is called before a push,
+// the topmost literal is no longer subject to operators like *
+// (Otherwise ab* would turn into (ab)*.)
+// If r >= 0 and there's a node left over, maybeConcat uses it
+// to push r with the given flags.
+// maybeConcat reports whether r was pushed.
+func (p *parser) maybeConcat(r int, flags Flags) bool {
+ n := len(p.stack)
+ if n < 2 {
+ return false
}
+
+ re1 := p.stack[n-1]
+ re2 := p.stack[n-2]
+ if re1.Op != OpLiteral || re2.Op != OpLiteral || re1.Flags&FoldCase != re2.Flags&FoldCase {
+ return false
+ }
+
+ // Push re1 into re2.
+ re2.Rune = append(re2.Rune, re1.Rune...)
+
+ // Reuse re1 if possible.
+ if r >= 0 {
+ re1.Rune = re1.Rune0[:1]
+ re1.Rune[0] = r
+ re1.Flags = flags
+ return true
+ }
+
+ p.stack = p.stack[:n-1]
+ p.reuse(re1)
+ return false // did not push r
+}
+
+// newLiteral returns a new OpLiteral Regexp with the given flags
+func (p *parser) newLiteral(r int, flags Flags) *Regexp {
+ re := p.newRegexp(OpLiteral)
+ re.Flags = flags
re.Rune0[0] = r
re.Rune = re.Rune0[:1]
return re
@@ -108,14 +188,16 @@ func newLiteral(r int, flags Flags) *Regexp {
// literal pushes a literal regexp for the rune r on the stack
// and returns that regexp.
-func (p *parser) literal(r int) *Regexp {
- return p.push(newLiteral(r, p.flags))
+func (p *parser) literal(r int) {
+ p.push(p.newLiteral(r, p.flags))
}
// op pushes a regexp with the given op onto the stack
// and returns that regexp.
func (p *parser) op(op Op) *Regexp {
- return p.push(&Regexp{Op: op, Flags: p.flags})
+ re := p.newRegexp(op)
+ re.Flags = p.flags
+ return p.push(re)
}
// repeat replaces the top stack element with itself repeated
@@ -139,12 +221,10 @@ func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (strin
return "", &Error{ErrMissingRepeatArgument, opstr}
}
sub := p.stack[n-1]
- re := &Regexp{
- Op: op,
- Min: min,
- Max: max,
- Flags: flags,
- }
+ re := p.newRegexp(op)
+ re.Min = min
+ re.Max = max
+ re.Flags = flags
re.Sub = re.Sub0[:1]
re.Sub[0] = sub
p.stack[n-1] = re
@@ -153,60 +233,385 @@ func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (strin
// concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation.
func (p *parser) concat() *Regexp {
- // TODO: Flatten concats.
+ p.maybeConcat(-1, 0)
// Scan down to find pseudo-operator | or (.
i := len(p.stack)
for i > 0 && p.stack[i-1].Op < opPseudo {
i--
}
- sub := p.stack[i:]
+ subs := p.stack[i:]
p.stack = p.stack[:i]
- var re *Regexp
- switch len(sub) {
- case 0:
- re = &Regexp{Op: OpEmptyMatch}
- case 1:
- re = sub[0]
- default:
- re = &Regexp{Op: OpConcat}
- re.Sub = append(re.Sub0[:0], sub...)
+ // Empty concatenation is special case.
+ if len(subs) == 0 {
+ return p.push(p.newRegexp(OpEmptyMatch))
}
- return p.push(re)
+
+ return p.push(p.collapse(subs, OpConcat))
}
// alternate replaces the top of the stack (above the topmost '(') with its alternation.
func (p *parser) alternate() *Regexp {
- // TODO: Flatten alternates.
-
// Scan down to find pseudo-operator (.
// There are no | above (.
i := len(p.stack)
for i > 0 && p.stack[i-1].Op < opPseudo {
i--
}
- sub := p.stack[i:]
+ subs := p.stack[i:]
p.stack = p.stack[:i]
- var re *Regexp
- switch len(sub) {
- case 0:
- re = &Regexp{Op: OpNoMatch}
- case 1:
- re = sub[0]
- default:
- re = &Regexp{Op: OpAlternate}
- re.Sub = append(re.Sub0[:0], sub...)
+ // Make sure top class is clean.
+ // All the others already are (see swapVerticalBar).
+ if len(subs) > 0 {
+ cleanAlt(subs[len(subs)-1])
}
- return p.push(re)
+
+ // Empty alternate is special case
+ // (shouldn't happen but easy to handle).
+ if len(subs) == 0 {
+ return p.push(p.newRegexp(OpNoMatch))
+ }
+
+ return p.push(p.collapse(subs, OpAlternate))
}
-func literalRegexp(s string, flags Flags) *Regexp {
- re := &Regexp{
- Op: OpLiteral,
- Flags: flags,
+// cleanAlt cleans re for eventual inclusion in an alternation.
+func cleanAlt(re *Regexp) {
+ switch re.Op {
+ case OpCharClass:
+ re.Rune = cleanClass(&re.Rune)
+ if len(re.Rune) == 2 && re.Rune[0] == 0 && re.Rune[1] == unicode.MaxRune {
+ re.Rune = nil
+ re.Op = OpAnyChar
+ return
+ }
+ if len(re.Rune) == 4 && re.Rune[0] == 0 && re.Rune[1] == '\n'-1 && re.Rune[2] == '\n'+1 && re.Rune[3] == unicode.MaxRune {
+ re.Rune = nil
+ re.Op = OpAnyCharNotNL
+ return
+ }
+ if cap(re.Rune)-len(re.Rune) > 100 {
+ // re.Rune will not grow any more.
+ // Make a copy or inline to reclaim storage.
+ re.Rune = append(re.Rune0[:0], re.Rune...)
+ }
+ }
+}
+
+// collapse returns the result of applying op to sub.
+// If sub contains op nodes, they all get hoisted up
+// so that there is never a concat of a concat or an
+// alternate of an alternate.
+func (p *parser) collapse(subs []*Regexp, op Op) *Regexp {
+ if len(subs) == 1 {
+ return subs[0]
+ }
+ re := p.newRegexp(op)
+ re.Sub = re.Sub0[:0]
+ for _, sub := range subs {
+ if sub.Op == op {
+ re.Sub = append(re.Sub, sub.Sub...)
+ p.reuse(sub)
+ } else {
+ re.Sub = append(re.Sub, sub)
+ }
+ }
+ if op == OpAlternate {
+ re.Sub = p.factor(re.Sub, re.Flags)
+ if len(re.Sub) == 1 {
+ old := re
+ re = re.Sub[0]
+ p.reuse(old)
+ }
+ }
+ return re
+}
+
+// factor factors common prefixes from the alternation list sub.
+// It returns a replacement list that reuses the same storage and
+// frees (passes to p.reuse) any removed *Regexps.
+//
+// For example,
+// ABC|ABD|AEF|BCX|BCY
+// simplifies by literal prefix extraction to
+// A(B(C|D)|EF)|BC(X|Y)
+// which simplifies by character class introduction to
+// A(B[CD]|EF)|BC[XY]
+//
+func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp {
+ if len(sub) < 2 {
+ return sub
+ }
+
+ // Round 1: Factor out common literal prefixes.
+ var str []int
+ var strflags Flags
+ start := 0
+ out := sub[:0]
+ for i := 0; i <= len(sub); i++ {
+ // Invariant: the Regexps that were in sub[0:start] have been
+ // used or marked for reuse, and the slice space has been reused
+ // for out (len(out) <= start).
+ //
+ // Invariant: sub[start:i] consists of regexps that all begin
+ // with str as modified by strflags.
+ var istr []int
+ var iflags Flags
+ if i < len(sub) {
+ istr, iflags = p.leadingString(sub[i])
+ if iflags == strflags {
+ same := 0
+ for same < len(str) && same < len(istr) && str[same] == istr[same] {
+ same++
+ }
+ if same > 0 {
+ // Matches at least one rune in current range.
+ // Keep going around.
+ str = str[:same]
+ continue
+ }
+ }
+ }
+
+ // Found end of a run with common leading literal string:
+ // sub[start:i] all begin with str[0:len(str)], but sub[i]
+ // does not even begin with str[0].
+ //
+ // Factor out common string and append factored expression to out.
+ if i == start {
+ // Nothing to do - run of length 0.
+ } else if i == start+1 {
+ // Just one: don't bother factoring.
+ out = append(out, sub[start])
+ } else {
+ // Construct factored form: prefix(suffix1|suffix2|...)
+ prefix := p.newRegexp(OpLiteral)
+ prefix.Flags = strflags
+ prefix.Rune = append(prefix.Rune[:0], str...)
+
+ for j := start; j < i; j++ {
+ sub[j] = p.removeLeadingString(sub[j], len(str))
+ }
+ suffix := p.collapse(sub[start:i], OpAlternate) // recurse
+
+ re := p.newRegexp(OpConcat)
+ re.Sub = append(re.Sub[:0], prefix, suffix)
+ out = append(out, re)
+ }
+
+ // Prepare for next iteration.
+ start = i
+ str = istr
+ strflags = iflags
+ }
+ sub = out
+
+ // Round 2: Factor out common complex prefixes,
+ // just the first piece of each concatenation,
+ // whatever it is. This is good enough a lot of the time.
+ start = 0
+ out = sub[:0]
+ var first *Regexp
+ for i := 0; i <= len(sub); i++ {
+ // Invariant: the Regexps that were in sub[0:start] have been
+ // used or marked for reuse, and the slice space has been reused
+ // for out (len(out) <= start).
+ //
+ // Invariant: sub[start:i] consists of regexps that all begin
+ // with str as modified by strflags.
+ var ifirst *Regexp
+ if i < len(sub) {
+ ifirst = p.leadingRegexp(sub[i])
+ if first != nil && first.Equal(ifirst) {
+ continue
+ }
+ }
+
+ // Found end of a run with common leading regexp:
+ // sub[start:i] all begin with first but sub[i] does not.
+ //
+ // Factor out common regexp and append factored expression to out.
+ if i == start {
+ // Nothing to do - run of length 0.
+ } else if i == start+1 {
+ // Just one: don't bother factoring.
+ out = append(out, sub[start])
+ } else {
+ // Construct factored form: prefix(suffix1|suffix2|...)
+ prefix := first
+
+ for j := start; j < i; j++ {
+ reuse := j != start // prefix came from sub[start]
+ sub[j] = p.removeLeadingRegexp(sub[j], reuse)
+ }
+ suffix := p.collapse(sub[start:i], OpAlternate) // recurse
+
+ re := p.newRegexp(OpConcat)
+ re.Sub = append(re.Sub[:0], prefix, suffix)
+ out = append(out, re)
+ }
+
+ // Prepare for next iteration.
+ start = i
+ first = ifirst
+ }
+ sub = out
+
+ // Round 3: Collapse runs of single literals into character classes.
+ start = 0
+ out = sub[:0]
+ for i := 0; i <= len(sub); i++ {
+ // Invariant: the Regexps that were in sub[0:start] have been
+ // used or marked for reuse, and the slice space has been reused
+ // for out (len(out) <= start).
+ //
+ // Invariant: sub[start:i] consists of regexps that are either
+ // literal runes or character classes.
+ if i < len(sub) && isCharClass(sub[i]) {
+ continue
+ }
+
+ // sub[i] is not a char or char class;
+ // emit char class for sub[start:i]...
+ if i == start {
+ // Nothing to do - run of length 0.
+ } else if i == start+1 {
+ out = append(out, sub[start])
+ } else {
+ // Make new char class.
+ // Start with most complex regexp in sub[start].
+ max := start
+ for j := start + 1; j < i; j++ {
+ if sub[max].Op < sub[j].Op || sub[max].Op == sub[j].Op && len(sub[max].Rune) < len(sub[j].Rune) {
+ max = j
+ }
+ }
+ sub[start], sub[max] = sub[max], sub[start]
+
+ for j := start + 1; j < i; j++ {
+ mergeCharClass(sub[start], sub[j])
+ p.reuse(sub[j])
+ }
+ cleanAlt(sub[start])
+ out = append(out, sub[start])
+ }
+
+ // ... and then emit sub[i].
+ if i < len(sub) {
+ out = append(out, sub[i])
+ }
+ start = i + 1
+ }
+ sub = out
+
+ // Round 4: Collapse runs of empty matches into a single empty match.
+ start = 0
+ out = sub[:0]
+ for i := range sub {
+ if i+1 < len(sub) && sub[i].Op == OpEmptyMatch && sub[i+1].Op == OpEmptyMatch {
+ continue
+ }
+ out = append(out, sub[i])
+ }
+ sub = out
+
+ return sub
+}
+
+// leadingString returns the leading literal string that re begins with.
+// The string refers to storage in re or its children.
+func (p *parser) leadingString(re *Regexp) ([]int, Flags) {
+ if re.Op == OpConcat && len(re.Sub) > 0 {
+ re = re.Sub[0]
+ }
+ if re.Op != OpLiteral {
+ return nil, 0
+ }
+ return re.Rune, re.Flags & FoldCase
+}
+
+// removeLeadingString removes the first n leading runes
+// from the beginning of re. It returns the replacement for re.
+func (p *parser) removeLeadingString(re *Regexp, n int) *Regexp {
+ if re.Op == OpConcat && len(re.Sub) > 0 {
+ // Removing a leading string in a concatenation
+ // might simplify the concatenation.
+ sub := re.Sub[0]
+ sub = p.removeLeadingString(sub, n)
+ re.Sub[0] = sub
+ if sub.Op == OpEmptyMatch {
+ p.reuse(sub)
+ switch len(re.Sub) {
+ case 0, 1:
+ // Impossible but handle.
+ re.Op = OpEmptyMatch
+ re.Sub = nil
+ case 2:
+ old := re
+ re = re.Sub[1]
+ p.reuse(old)
+ default:
+ copy(re.Sub, re.Sub[1:])
+ re.Sub = re.Sub[:len(re.Sub)-1]
+ }
+ }
+ return re
}
+
+ if re.Op == OpLiteral {
+ re.Rune = re.Rune[:copy(re.Rune, re.Rune[n:])]
+ if len(re.Rune) == 0 {
+ re.Op = OpEmptyMatch
+ }
+ }
+ return re
+}
+
+// leadingRegexp returns the leading regexp that re begins with.
+// The regexp refers to storage in re or its children.
+func (p *parser) leadingRegexp(re *Regexp) *Regexp {
+ if re.Op == OpEmptyMatch {
+ return nil
+ }
+ if re.Op == OpConcat && len(re.Sub) > 0 {
+ sub := re.Sub[0]
+ if sub.Op == OpEmptyMatch {
+ return nil
+ }
+ return sub
+ }
+ return re
+}
+
+// removeLeadingRegexp removes the leading regexp in re.
+// It returns the replacement for re.
+// If reuse is true, it passes the removed regexp (if no longer needed) to p.reuse.
+func (p *parser) removeLeadingRegexp(re *Regexp, reuse bool) *Regexp {
+ if re.Op == OpConcat && len(re.Sub) > 0 {
+ if reuse {
+ p.reuse(re.Sub[0])
+ }
+ re.Sub = re.Sub[:copy(re.Sub, re.Sub[1:])]
+ switch len(re.Sub) {
+ case 0:
+ re.Op = OpEmptyMatch
+ re.Sub = nil
+ case 1:
+ old := re
+ re = re.Sub[0]
+ p.reuse(old)
+ }
+ return re
+ }
+ re.Op = OpEmptyMatch
+ return re
+}
+
+func literalRegexp(s string, flags Flags) *Regexp {
+ re := &Regexp{Op: OpLiteral}
+ re.Flags = flags
re.Rune = re.Rune0[:0] // use local storage for small strings
for _, c := range s {
if len(re.Rune) >= cap(re.Rune) {
@@ -264,7 +669,6 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) {
p.op(opLeftParen).Cap = p.numCap
t = t[1:]
case '|':
- p.concat()
if err = p.parseVerticalBar(); err != nil {
return nil, err
}
@@ -360,7 +764,8 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) {
}
}
- re := &Regexp{Op: OpCharClass, Flags: p.flags}
+ re := p.newRegexp(OpCharClass)
+ re.Flags = p.flags
// Look for Unicode character group like \p{Han}
if len(t) >= 2 && (t[1] == 'p' || t[1] == 'P') {
@@ -371,7 +776,6 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) {
if r != nil {
re.Rune = r
t = rest
- // TODO: Handle FoldCase flag.
p.push(re)
break BigSwitch
}
@@ -381,12 +785,10 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) {
if r, rest := p.parsePerlClassEscape(t, re.Rune0[:0]); r != nil {
re.Rune = r
t = rest
- // TODO: Handle FoldCase flag.
p.push(re)
break BigSwitch
}
-
- // TODO: Give re back to parser's pool.
+ p.reuse(re)
// Ordinary single-character escape.
if c, t, err = p.parseEscape(t); err != nil {
@@ -592,6 +994,35 @@ func (p *parser) parseInt(s string) (n int, rest string, ok bool) {
return
}
+// can this be represented as a character class?
+// single-rune literal string, char class, ., and .|\n.
+func isCharClass(re *Regexp) bool {
+ return re.Op == OpLiteral && len(re.Rune) == 1 ||
+ re.Op == OpCharClass ||
+ re.Op == OpAnyCharNotNL ||
+ re.Op == OpAnyChar
+}
+
+// does re match r?
+func matchRune(re *Regexp, r int) bool {
+ switch re.Op {
+ case OpLiteral:
+ return len(re.Rune) == 1 && re.Rune[0] == r
+ case OpCharClass:
+ for i := 0; i < len(re.Rune); i += 2 {
+ if re.Rune[i] <= r && r <= re.Rune[i+1] {
+ return true
+ }
+ }
+ return false
+ case OpAnyCharNotNL:
+ return r != '\n'
+ case OpAnyChar:
+ return true
+ }
+ return false
+}
+
// parseVerticalBar handles a | in the input.
func (p *parser) parseVerticalBar() os.Error {
p.concat()
@@ -607,14 +1038,66 @@ func (p *parser) parseVerticalBar() os.Error {
return nil
}
+// mergeCharClass makes dst = dst|src.
+// The caller must ensure that dst.Op >= src.Op,
+// to reduce the amount of copying.
+func mergeCharClass(dst, src *Regexp) {
+ switch dst.Op {
+ case OpAnyChar:
+ // src doesn't add anything.
+ case OpAnyCharNotNL:
+ // src might add \n
+ if matchRune(src, '\n') {
+ dst.Op = OpAnyChar
+ }
+ case OpCharClass:
+ // src is simpler, so either literal or char class
+ if src.Op == OpLiteral {
+ dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0])
+ } else {
+ dst.Rune = appendClass(dst.Rune, src.Rune)
+ }
+ case OpLiteral:
+ // both literal
+ if src.Rune[0] == dst.Rune[0] {
+ break
+ }
+ dst.Op = OpCharClass
+ dst.Rune = append(dst.Rune, dst.Rune[0])
+ dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0])
+ }
+}
+
// If the top of the stack is an element followed by an opVerticalBar
// swapVerticalBar swaps the two and returns true.
// Otherwise it returns false.
func (p *parser) swapVerticalBar() bool {
- if n := len(p.stack); n >= 2 {
+ // If above and below vertical bar are literal or char class,
+ // can merge into a single char class.
+ n := len(p.stack)
+ if n >= 3 && p.stack[n-2].Op == opVerticalBar && isCharClass(p.stack[n-1]) && isCharClass(p.stack[n-3]) {
+ re1 := p.stack[n-1]
+ re3 := p.stack[n-3]
+ // Make re3 the more complex of the two.
+ if re1.Op > re3.Op {
+ re1, re3 = re3, re1
+ p.stack[n-3] = re3
+ }
+ mergeCharClass(re3, re1)
+ p.reuse(re1)
+ p.stack = p.stack[:n-1]
+ return true
+ }
+
+ if n >= 2 {
re1 := p.stack[n-1]
re2 := p.stack[n-2]
if re2.Op == opVerticalBar {
+ if n >= 3 {
+ // Now out of reach.
+ // Clean opportunistically.
+ cleanAlt(p.stack[n-3])
+ }
p.stack[n-2] = re1
p.stack[n-1] = re2
return true
@@ -729,6 +1212,7 @@ Switch:
if r > unicode.MaxRune {
break Switch
}
+ nhex++
}
if nhex == 0 {
break Switch
@@ -801,12 +1285,7 @@ func (p *parser) parsePerlClassEscape(s string, r []int) (out []int, rest string
if g.sign == 0 {
return
}
- if g.sign < 0 {
- r = appendNegatedClass(r, g.class)
- } else {
- r = appendClass(r, g.class)
- }
- return r, s[2:]
+ return p.appendGroup(r, g), s[2:]
}
// parseNamedClass parses a leading POSIX named character class like [:alnum:]
@@ -827,23 +1306,40 @@ func (p *parser) parseNamedClass(s string, r []int) (out []int, rest string, err
if g.sign == 0 {
return nil, "", &Error{ErrInvalidCharRange, name}
}
- if g.sign < 0 {
- r = appendNegatedClass(r, g.class)
+ return p.appendGroup(r, g), s, nil
+}
+
+func (p *parser) appendGroup(r []int, g charGroup) []int {
+ if p.flags&FoldCase == 0 {
+ if g.sign < 0 {
+ r = appendNegatedClass(r, g.class)
+ } else {
+ r = appendClass(r, g.class)
+ }
} else {
- r = appendClass(r, g.class)
+ tmp := p.tmpClass[:0]
+ tmp = appendFoldedClass(tmp, g.class)
+ p.tmpClass = tmp
+ tmp = cleanClass(&p.tmpClass)
+ if g.sign < 0 {
+ r = appendNegatedClass(r, tmp)
+ } else {
+ r = appendClass(r, tmp)
+ }
}
- return r, s, nil
+ return r
}
-// unicodeTable returns the unicode.RangeTable identified by name.
-func unicodeTable(name string) *unicode.RangeTable {
+// unicodeTable returns the unicode.RangeTable identified by name
+// and the table of additional fold-equivalent code points.
+func unicodeTable(name string) (*unicode.RangeTable, *unicode.RangeTable) {
if t := unicode.Categories[name]; t != nil {
- return t
+ return t, unicode.FoldCategory[name]
}
if t := unicode.Scripts[name]; t != nil {
- return t
+ return t, unicode.FoldScript[name]
}
- return nil
+ return nil, nil
}
// parseUnicodeClass parses a leading Unicode character class like \p{Han}
@@ -891,14 +1387,31 @@ func (p *parser) parseUnicodeClass(s string, r []int) (out []int, rest string, e
name = name[1:]
}
- tab := unicodeTable(name)
+ tab, fold := unicodeTable(name)
if tab == nil {
return nil, "", &Error{ErrInvalidCharRange, seq}
}
- if sign > 0 {
- r = appendTable(r, tab)
+
+ if p.flags&FoldCase == 0 || fold == nil {
+ if sign > 0 {
+ r = appendTable(r, tab)
+ } else {
+ r = appendNegatedTable(r, tab)
+ }
} else {
- r = appendNegatedTable(r, tab)
+ // Merge and clean tab and fold in a temporary buffer.
+ // This is necessary for the negative case and just tidy
+ // for the positive case.
+ tmp := p.tmpClass[:0]
+ tmp = appendTable(tmp, tab)
+ tmp = appendTable(tmp, fold)
+ p.tmpClass = tmp
+ tmp = cleanClass(&p.tmpClass)
+ if sign > 0 {
+ r = appendClass(r, tmp)
+ } else {
+ r = appendNegatedClass(r, tmp)
+ }
}
return r, t, nil
}
@@ -907,7 +1420,8 @@ func (p *parser) parseUnicodeClass(s string, r []int) (out []int, rest string, e
// and pushes it onto the parse stack.
func (p *parser) parseClass(s string) (rest string, err os.Error) {
t := s[1:] // chop [
- re := &Regexp{Op: OpCharClass, Flags: p.flags}
+ re := p.newRegexp(OpCharClass)
+ re.Flags = p.flags
re.Rune = re.Rune0[:0]
sign := +1
@@ -979,12 +1493,14 @@ func (p *parser) parseClass(s string) (rest string, err os.Error) {
return "", &Error{Code: ErrInvalidCharRange, Expr: rng}
}
}
- class = appendRange(class, lo, hi)
+ if p.flags&FoldCase == 0 {
+ class = appendRange(class, lo, hi)
+ } else {
+ class = appendFoldedRange(class, lo, hi)
+ }
}
t = t[1:] // chop ]
- // TODO: Handle FoldCase flag.
-
// Use &re.Rune instead of &class to avoid allocation.
re.Rune = class
class = cleanClass(&re.Rune)
@@ -999,10 +1515,15 @@ func (p *parser) parseClass(s string) (rest string, err os.Error) {
// cleanClass sorts the ranges (pairs of elements of r),
// merges them, and eliminates duplicates.
func cleanClass(rp *[]int) []int {
+
// Sort by lo increasing, hi decreasing to break ties.
sort.Sort(ranges{rp})
r := *rp
+ if len(r) < 2 {
+ return r
+ }
+
// Merge abutting, overlapping.
w := 2 // write index
for i := 2; i < len(r); i += 2 {
@@ -1025,23 +1546,71 @@ func cleanClass(rp *[]int) []int {
// appendRange returns the result of appending the range lo-hi to the class r.
func appendRange(r []int, lo, hi int) []int {
- // Expand last range if overlaps or abuts.
- if n := len(r); n > 0 {
- rlo, rhi := r[n-2], r[n-1]
- if lo <= rhi+1 && rlo <= hi+1 {
- if lo < rlo {
- r[n-2] = lo
- }
- if hi > rhi {
- r[n-1] = hi
+ // Expand last range or next to last range if it overlaps or abuts.
+ // Checking two ranges helps when appending case-folded
+ // alphabets, so that one range can be expanding A-Z and the
+ // other expanding a-z.
+ n := len(r)
+ for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4
+ if n >= i {
+ rlo, rhi := r[n-i], r[n-i+1]
+ if lo <= rhi+1 && rlo <= hi+1 {
+ if lo < rlo {
+ r[n-i] = lo
+ }
+ if hi > rhi {
+ r[n-i+1] = hi
+ }
+ return r
}
- return r
}
}
return append(r, lo, hi)
}
+const (
+ // minimum and maximum runes involved in folding.
+ // checked during test.
+ minFold = 0x0041
+ maxFold = 0x1044f
+)
+
+// appendFoldedRange returns the result of appending the range lo-hi
+// and its case folding-equivalent runes to the class r.
+func appendFoldedRange(r []int, lo, hi int) []int {
+ // Optimizations.
+ if lo <= minFold && hi >= maxFold {
+ // Range is full: folding can't add more.
+ return appendRange(r, lo, hi)
+ }
+ if hi < minFold || lo > maxFold {
+ // Range is outside folding possibilities.
+ return appendRange(r, lo, hi)
+ }
+ if lo < minFold {
+ // [lo, minFold-1] needs no folding.
+ r = appendRange(r, lo, minFold-1)
+ lo = minFold
+ }
+ if hi > maxFold {
+ // [maxFold+1, hi] needs no folding.
+ r = appendRange(r, maxFold+1, hi)
+ hi = maxFold
+ }
+
+ // Brute force. Depend on appendRange to coalesce ranges on the fly.
+ for c := lo; c <= hi; c++ {
+ r = appendRange(r, c, c)
+ f := unicode.SimpleFold(c)
+ for f != c {
+ r = appendRange(r, f, f)
+ f = unicode.SimpleFold(f)
+ }
+ }
+ return r
+}
+
// appendClass returns the result of appending the class x to the class r.
// It assume x is clean.
func appendClass(r []int, x []int) []int {
@@ -1051,6 +1620,14 @@ func appendClass(r []int, x []int) []int {
return r
}
+// appendFolded returns the result of appending the case folding of the class x to the class r.
+func appendFoldedClass(r []int, x []int) []int {
+ for i := 0; i < len(x); i += 2 {
+ r = appendFoldedRange(r, x[i], x[i+1])
+ }
+ return r
+}
+
// appendNegatedClass returns the result of appending the negation of the class x to the class r.
// It assumes x is clean.
func appendNegatedClass(r []int, x []int) []int {
@@ -1148,10 +1725,11 @@ func negateClass(r []int) []int {
}
nextLo = hi + 1
}
+ r = r[:w]
if nextLo <= unicode.MaxRune {
// It's possible for the negation to have one more
// range - this one - than the original class, so use append.
- r = append(r[:w], nextLo, unicode.MaxRune)
+ r = append(r, nextLo, unicode.MaxRune)
}
return r
}
diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go
index b52cab1a1..779b9afde 100644
--- a/src/pkg/exp/regexp/syntax/parse_test.go
+++ b/src/pkg/exp/regexp/syntax/parse_test.go
@@ -16,141 +16,152 @@ var parseTests = []struct {
Dump string
}{
// Base cases
- {"a", "lit{a}"},
- {"a.", "cat{lit{a}dot{}}"},
- {"a.b", "cat{lit{a}dot{}lit{b}}"},
- // { "ab", "str{ab}" },
- {"ab", "cat{lit{a}lit{b}}"},
- {"a.b.c", "cat{lit{a}dot{}lit{b}dot{}lit{c}}"},
- // { "abc", "str{abc}" },
- {"abc", "cat{lit{a}lit{b}lit{c}}"},
- {"a|^", "alt{lit{a}bol{}}"},
- // { "a|b", "cc{0x61-0x62}" },
- {"a|b", "alt{lit{a}lit{b}}"},
- {"(a)", "cap{lit{a}}"},
- {"(a)|b", "alt{cap{lit{a}}lit{b}}"},
- {"a*", "star{lit{a}}"},
- {"a+", "plus{lit{a}}"},
- {"a?", "que{lit{a}}"},
- {"a{2}", "rep{2,2 lit{a}}"},
- {"a{2,3}", "rep{2,3 lit{a}}"},
- {"a{2,}", "rep{2,-1 lit{a}}"},
- {"a*?", "nstar{lit{a}}"},
- {"a+?", "nplus{lit{a}}"},
- {"a??", "nque{lit{a}}"},
- {"a{2}?", "nrep{2,2 lit{a}}"},
- {"a{2,3}?", "nrep{2,3 lit{a}}"},
- {"a{2,}?", "nrep{2,-1 lit{a}}"},
- {"", "emp{}"},
- // { "|", "emp{}" }, // alt{emp{}emp{}} but got factored
- {"|", "alt{emp{}emp{}}"},
- {"|x|", "alt{emp{}lit{x}emp{}}"},
- {".", "dot{}"},
- {"^", "bol{}"},
- {"$", "eol{}"},
- {"\\|", "lit{|}"},
- {"\\(", "lit{(}"},
- {"\\)", "lit{)}"},
- {"\\*", "lit{*}"},
- {"\\+", "lit{+}"},
- {"\\?", "lit{?}"},
- {"{", "lit{{}"},
- {"}", "lit{}}"},
- {"\\.", "lit{.}"},
- {"\\^", "lit{^}"},
- {"\\$", "lit{$}"},
- {"\\\\", "lit{\\}"},
- {"[ace]", "cc{0x61 0x63 0x65}"},
- {"[abc]", "cc{0x61-0x63}"},
- {"[a-z]", "cc{0x61-0x7a}"},
- // { "[a]", "lit{a}" },
- {"[a]", "cc{0x61}"},
- {"\\-", "lit{-}"},
- {"-", "lit{-}"},
- {"\\_", "lit{_}"},
+ {`a`, `lit{a}`},
+ {`a.`, `cat{lit{a}dot{}}`},
+ {`a.b`, `cat{lit{a}dot{}lit{b}}`},
+ {`ab`, `str{ab}`},
+ {`a.b.c`, `cat{lit{a}dot{}lit{b}dot{}lit{c}}`},
+ {`abc`, `str{abc}`},
+ {`a|^`, `alt{lit{a}bol{}}`},
+ {`a|b`, `cc{0x61-0x62}`},
+ {`(a)`, `cap{lit{a}}`},
+ {`(a)|b`, `alt{cap{lit{a}}lit{b}}`},
+ {`a*`, `star{lit{a}}`},
+ {`a+`, `plus{lit{a}}`},
+ {`a?`, `que{lit{a}}`},
+ {`a{2}`, `rep{2,2 lit{a}}`},
+ {`a{2,3}`, `rep{2,3 lit{a}}`},
+ {`a{2,}`, `rep{2,-1 lit{a}}`},
+ {`a*?`, `nstar{lit{a}}`},
+ {`a+?`, `nplus{lit{a}}`},
+ {`a??`, `nque{lit{a}}`},
+ {`a{2}?`, `nrep{2,2 lit{a}}`},
+ {`a{2,3}?`, `nrep{2,3 lit{a}}`},
+ {`a{2,}?`, `nrep{2,-1 lit{a}}`},
+ {``, `emp{}`},
+ {`|`, `emp{}`}, // alt{emp{}emp{}} but got factored
+ {`|x|`, `alt{emp{}lit{x}emp{}}`},
+ {`.`, `dot{}`},
+ {`^`, `bol{}`},
+ {`$`, `eol{}`},
+ {`\|`, `lit{|}`},
+ {`\(`, `lit{(}`},
+ {`\)`, `lit{)}`},
+ {`\*`, `lit{*}`},
+ {`\+`, `lit{+}`},
+ {`\?`, `lit{?}`},
+ {`{`, `lit{{}`},
+ {`}`, `lit{}}`},
+ {`\.`, `lit{.}`},
+ {`\^`, `lit{^}`},
+ {`\$`, `lit{$}`},
+ {`\\`, `lit{\}`},
+ {`[ace]`, `cc{0x61 0x63 0x65}`},
+ {`[abc]`, `cc{0x61-0x63}`},
+ {`[a-z]`, `cc{0x61-0x7a}`},
+ {`[a]`, `lit{a}`},
+ {`\-`, `lit{-}`},
+ {`-`, `lit{-}`},
+ {`\_`, `lit{_}`},
+ {`abc`, `str{abc}`},
+ {`abc|def`, `alt{str{abc}str{def}}`},
+ {`abc|def|ghi`, `alt{str{abc}str{def}str{ghi}}`},
// Posix and Perl extensions
- {"[[:lower:]]", "cc{0x61-0x7a}"},
- {"[a-z]", "cc{0x61-0x7a}"},
- {"[^[:lower:]]", "cc{0x0-0x60 0x7b-0x10ffff}"},
- {"[[:^lower:]]", "cc{0x0-0x60 0x7b-0x10ffff}"},
- // { "(?i)[[:lower:]]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" },
- // { "(?i)[a-z]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" },
- // { "(?i)[^[:lower:]]", "cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
- // { "(?i)[[:^lower:]]", "cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
- {"\\d", "cc{0x30-0x39}"},
- {"\\D", "cc{0x0-0x2f 0x3a-0x10ffff}"},
- {"\\s", "cc{0x9-0xa 0xc-0xd 0x20}"},
- {"\\S", "cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}"},
- {"\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}"},
- {"\\W", "cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}"},
- // { "(?i)\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}" },
- // { "(?i)\\W", "cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
- {"[^\\\\]", "cc{0x0-0x5b 0x5d-0x10ffff}"},
- // { "\\C", "byte{}" },
+ {`[[:lower:]]`, `cc{0x61-0x7a}`},
+ {`[a-z]`, `cc{0x61-0x7a}`},
+ {`[^[:lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`},
+ {`[[:^lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`},
+ {`(?i)[[:lower:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`},
+ {`(?i)[a-z]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`},
+ {`(?i)[^[:lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`},
+ {`(?i)[[:^lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`},
+ {`\d`, `cc{0x30-0x39}`},
+ {`\D`, `cc{0x0-0x2f 0x3a-0x10ffff}`},
+ {`\s`, `cc{0x9-0xa 0xc-0xd 0x20}`},
+ {`\S`, `cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}`},
+ {`\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}`},
+ {`\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}`},
+ {`(?i)\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}`},
+ {`(?i)\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`},
+ {`[^\\]`, `cc{0x0-0x5b 0x5d-0x10ffff}`},
+ // { `\C`, `byte{}` }, // probably never
// Unicode, negatives, and a double negative.
- {"\\p{Braille}", "cc{0x2800-0x28ff}"},
- {"\\P{Braille}", "cc{0x0-0x27ff 0x2900-0x10ffff}"},
- {"\\p{^Braille}", "cc{0x0-0x27ff 0x2900-0x10ffff}"},
- {"\\P{^Braille}", "cc{0x2800-0x28ff}"},
- {"\\pZ", "cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}"},
- {"[\\p{Braille}]", "cc{0x2800-0x28ff}"},
- {"[\\P{Braille}]", "cc{0x0-0x27ff 0x2900-0x10ffff}"},
- {"[\\p{^Braille}]", "cc{0x0-0x27ff 0x2900-0x10ffff}"},
- {"[\\P{^Braille}]", "cc{0x2800-0x28ff}"},
- {"[\\pZ]", "cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}"},
+ {`\p{Braille}`, `cc{0x2800-0x28ff}`},
+ {`\P{Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`},
+ {`\p{^Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`},
+ {`\P{^Braille}`, `cc{0x2800-0x28ff}`},
+ {`\pZ`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`},
+ {`[\p{Braille}]`, `cc{0x2800-0x28ff}`},
+ {`[\P{Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`},
+ {`[\p{^Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`},
+ {`[\P{^Braille}]`, `cc{0x2800-0x28ff}`},
+ {`[\pZ]`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`},
+ {`\p{Lu}`, mkCharClass(unicode.IsUpper)},
+ {`[\p{Lu}]`, mkCharClass(unicode.IsUpper)},
+ {`(?i)[\p{Lu}]`, mkCharClass(isUpperFold)},
+
+ // Hex, octal.
+ {`[\012-\234]\141`, `cat{cc{0xa-0x9c}lit{a}}`},
+ {`[\x{41}-\x7a]\x61`, `cat{cc{0x41-0x7a}lit{a}}`},
// More interesting regular expressions.
- // { "a{,2}", "str{a{,2}}" },
- // { "\\.\\^\\$\\\\", "str{.^$\\}" },
- {"[a-zABC]", "cc{0x41-0x43 0x61-0x7a}"},
- {"[^a]", "cc{0x0-0x60 0x62-0x10ffff}"},
- {"[\xce\xb1-\xce\xb5\xe2\x98\xba]", "cc{0x3b1-0x3b5 0x263a}"}, // utf-8
- {"a*{", "cat{star{lit{a}}lit{{}}"},
+ {`a{,2}`, `str{a{,2}}`},
+ {`\.\^\$\\`, `str{.^$\}`},
+ {`[a-zABC]`, `cc{0x41-0x43 0x61-0x7a}`},
+ {`[^a]`, `cc{0x0-0x60 0x62-0x10ffff}`},
+ {`[α-ε☺]`, `cc{0x3b1-0x3b5 0x263a}`}, // utf-8
+ {`a*{`, `cat{star{lit{a}}lit{{}}`},
// Test precedences
- // { "(?:ab)*", "star{str{ab}}" },
- // { "(ab)*", "star{cap{str{ab}}}" },
- // { "ab|cd", "alt{str{ab}str{cd}}" },
- // { "a(b|c)d", "cat{lit{a}cap{cc{0x62-0x63}}lit{d}}" },
- {"(?:ab)*", "star{cat{lit{a}lit{b}}}"},
- {"(ab)*", "star{cap{cat{lit{a}lit{b}}}}"},
- {"ab|cd", "alt{cat{lit{a}lit{b}}cat{lit{c}lit{d}}}"},
- {"a(b|c)d", "cat{lit{a}cap{alt{lit{b}lit{c}}}lit{d}}"},
+ {`(?:ab)*`, `star{str{ab}}`},
+ {`(ab)*`, `star{cap{str{ab}}}`},
+ {`ab|cd`, `alt{str{ab}str{cd}}`},
+ {`a(b|c)d`, `cat{lit{a}cap{cc{0x62-0x63}}lit{d}}`},
// Test flattening.
- {"(?:a)", "lit{a}"},
- // { "(?:ab)(?:cd)", "str{abcd}" },
- // { "(?:a|b)|(?:c|d)", "cc{0x61-0x64}" },
- // { "a|.", "dot{}" },
- // { ".|a", "dot{}" },
+ {`(?:a)`, `lit{a}`},
+ {`(?:ab)(?:cd)`, `str{abcd}`},
+ {`(?:a+b+)(?:c+d+)`, `cat{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`},
+ {`(?:a+|b+)|(?:c+|d+)`, `alt{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`},
+ {`(?:a|b)|(?:c|d)`, `cc{0x61-0x64}`},
+ {`a|.`, `dot{}`},
+ {`.|a`, `dot{}`},
+ {`(?:[abc]|A|Z|hello|world)`, `alt{cc{0x41 0x5a 0x61-0x63}str{hello}str{world}}`},
+ {`(?:[abc]|A|Z)`, `cc{0x41 0x5a 0x61-0x63}`},
// Test Perl quoted literals
- {"\\Q+|*?{[\\E", "str{+|*?{[}"},
- {"\\Q+\\E+", "plus{lit{+}}"},
- {"\\Q\\\\E", "lit{\\}"},
- {"\\Q\\\\\\E", "str{\\\\}"},
+ {`\Q+|*?{[\E`, `str{+|*?{[}`},
+ {`\Q+\E+`, `plus{lit{+}}`},
+ {`\Q\\E`, `lit{\}`},
+ {`\Q\\\E`, `str{\\}`},
// Test Perl \A and \z
- {"(?m)^", "bol{}"},
- {"(?m)$", "eol{}"},
- {"(?-m)^", "bot{}"},
- {"(?-m)$", "eot{}"},
- {"(?m)\\A", "bot{}"},
- {"(?m)\\z", "eot{\\z}"},
- {"(?-m)\\A", "bot{}"},
- {"(?-m)\\z", "eot{\\z}"},
+ {`(?m)^`, `bol{}`},
+ {`(?m)$`, `eol{}`},
+ {`(?-m)^`, `bot{}`},
+ {`(?-m)$`, `eot{}`},
+ {`(?m)\A`, `bot{}`},
+ {`(?m)\z`, `eot{\z}`},
+ {`(?-m)\A`, `bot{}`},
+ {`(?-m)\z`, `eot{\z}`},
// Test named captures
- {"(?P<name>a)", "cap{name:lit{a}}"},
+ {`(?P<name>a)`, `cap{name:lit{a}}`},
// Case-folded literals
- // { "[Aa]", "litfold{a}" },
+ {`[Aa]`, `litfold{A}`},
+ {`[\x{100}\x{101}]`, `litfold{Ā}`},
+ {`[Δδ]`, `litfold{Δ}`},
// Strings
- // { "abcde", "str{abcde}" },
- // { "[Aa][Bb]cd", "cat{strfold{ab}str{cd}}" },
+ {`abcde`, `str{abcde}`},
+ {`[Aa][Bb]cd`, `cat{strfold{AB}str{cd}}`},
+
+ // Factoring.
+ {`abc|abd|aef|bcx|bcy`, `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}cat{str{bc}cc{0x78-0x79}}}`},
+ {`ax+y|ax+z|ay+w`, `cat{lit{a}alt{cat{plus{lit{x}}cc{0x79-0x7a}}cat{plus{lit{y}}lit{w}}}}`},
}
const testFlags = MatchNL | PerlX | UnicodeGroups
@@ -223,8 +234,9 @@ func dumpRegexp(b *bytes.Buffer, re *Regexp) {
}
if re.Flags&FoldCase != 0 {
for _, r := range re.Rune {
- if unicode.ToUpper(r) != r {
+ if unicode.SimpleFold(r) != r {
b.WriteString("fold")
+ break
}
}
}
@@ -270,3 +282,69 @@ func dumpRegexp(b *bytes.Buffer, re *Regexp) {
}
b.WriteByte('}')
}
+
+func mkCharClass(f func(int) bool) string {
+ re := &Regexp{Op: OpCharClass}
+ lo := -1
+ for i := 0; i <= unicode.MaxRune; i++ {
+ if f(i) {
+ if lo < 0 {
+ lo = i
+ }
+ } else {
+ if lo >= 0 {
+ re.Rune = append(re.Rune, lo, i-1)
+ lo = -1
+ }
+ }
+ }
+ if lo >= 0 {
+ re.Rune = append(re.Rune, lo, unicode.MaxRune)
+ }
+ return dump(re)
+}
+
+func isUpperFold(rune int) bool {
+ if unicode.IsUpper(rune) {
+ return true
+ }
+ c := unicode.SimpleFold(rune)
+ for c != rune {
+ if unicode.IsUpper(c) {
+ return true
+ }
+ c = unicode.SimpleFold(c)
+ }
+ return false
+}
+
+func TestFoldConstants(t *testing.T) {
+ last := -1
+ for i := 0; i <= unicode.MaxRune; i++ {
+ if unicode.SimpleFold(i) == i {
+ continue
+ }
+ if last == -1 && minFold != i {
+ t.Errorf("minFold=%#U should be %#U", minFold, i)
+ }
+ last = i
+ }
+ if maxFold != last {
+ t.Errorf("maxFold=%#U should be %#U", maxFold, last)
+ }
+}
+
+func TestAppendRangeCollapse(t *testing.T) {
+ // AppendRange should collapse each of the new ranges
+ // into the earlier ones (it looks back two ranges), so that
+ // the slice never grows very large.
+ // Note that we are not calling cleanClass.
+ var r []int
+ for i := 'A'; i <= 'Z'; i++ {
+ r = appendRange(r, i, i)
+ r = appendRange(r, i+'a'-'A', i+'a'-'A')
+ }
+ if string(r) != "AZaz" {
+ t.Errorf("appendRange interlaced A-Z a-z = %s, want AZaz", string(r))
+ }
+}
diff --git a/src/pkg/exp/regexp/syntax/prog.go b/src/pkg/exp/regexp/syntax/prog.go
new file mode 100644
index 000000000..6eeb3da0c
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/prog.go
@@ -0,0 +1,182 @@
+package syntax
+
+import (
+ "bytes"
+ "strconv"
+)
+
+// Compiled program.
+// May not belong in this package, but convenient for now.
+
+// A Prog is a compiled regular expression program.
+type Prog struct {
+ Inst []Inst
+ Start int // index of start instruction
+}
+
+// An InstOp is an instruction opcode.
+type InstOp uint8
+
+const (
+ InstAlt InstOp = iota
+ InstAltMatch
+ InstCapture
+ InstEmptyWidth
+ InstMatch
+ InstFail
+ InstNop
+ InstRune
+)
+
+// An EmptyOp specifies a kind or mixture of zero-width assertions.
+type EmptyOp uint8
+
+const (
+ EmptyBeginLine EmptyOp = 1 << iota
+ EmptyEndLine
+ EmptyBeginText
+ EmptyEndText
+ EmptyWordBoundary
+ EmptyNoWordBoundary
+)
+
+// An Inst is a single instruction in a regular expression program.
+type Inst struct {
+ Op InstOp
+ Out uint32 // all but InstMatch, InstFail
+ Arg uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth
+ Rune []int
+}
+
+func (p *Prog) String() string {
+ var b bytes.Buffer
+ dumpProg(&b, p)
+ return b.String()
+}
+
+// MatchRune returns true if the instruction matches (and consumes) r.
+// It should only be called when i.Op == InstRune.
+func (i *Inst) MatchRune(r int) bool {
+ rune := i.Rune
+
+ // Special case: single-rune slice is from literal string, not char class.
+ // TODO: Case folding.
+ if len(rune) == 1 {
+ return r == rune[0]
+ }
+
+ // Peek at the first few pairs.
+ // Should handle ASCII well.
+ for j := 0; j < len(rune) && j <= 8; j += 2 {
+ if r < rune[j] {
+ return false
+ }
+ if r <= rune[j+1] {
+ return true
+ }
+ }
+
+ // Otherwise binary search.
+ lo := 0
+ hi := len(rune) / 2
+ for lo < hi {
+ m := lo + (hi-lo)/2
+ if c := rune[2*m]; c <= r {
+ if r <= rune[2*m+1] {
+ return true
+ }
+ lo = m + 1
+ } else {
+ hi = m
+ }
+ }
+ return false
+}
+
+// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char.
+// Since we act on runes, it would be easy to support Unicode here.
+func wordRune(rune int) bool {
+ return rune == '_' ||
+ ('A' <= rune && rune <= 'Z') ||
+ ('a' <= rune && rune <= 'z') ||
+ ('0' <= rune && rune <= '9')
+}
+
+// MatchEmptyWidth returns true if the instruction matches
+// an empty string between the runes before and after.
+// It should only be called when i.Op == InstEmptyWidth.
+func (i *Inst) MatchEmptyWidth(before int, after int) bool {
+ switch EmptyOp(i.Arg) {
+ case EmptyBeginLine:
+ return before == '\n' || before == -1
+ case EmptyEndLine:
+ return after == '\n' || after == -1
+ case EmptyBeginText:
+ return before == -1
+ case EmptyEndText:
+ return after == -1
+ case EmptyWordBoundary:
+ return wordRune(before) != wordRune(after)
+ case EmptyNoWordBoundary:
+ return wordRune(before) == wordRune(after)
+ }
+ panic("unknown empty width arg")
+}
+
+
+func (i *Inst) String() string {
+ var b bytes.Buffer
+ dumpInst(&b, i)
+ return b.String()
+}
+
+func bw(b *bytes.Buffer, args ...string) {
+ for _, s := range args {
+ b.WriteString(s)
+ }
+}
+
+func dumpProg(b *bytes.Buffer, p *Prog) {
+ for j := range p.Inst {
+ i := &p.Inst[j]
+ pc := strconv.Itoa(j)
+ if len(pc) < 3 {
+ b.WriteString(" "[len(pc):])
+ }
+ if j == p.Start {
+ pc += "*"
+ }
+ bw(b, pc, "\t")
+ dumpInst(b, i)
+ bw(b, "\n")
+ }
+}
+
+func u32(i uint32) string {
+ return strconv.Uitoa64(uint64(i))
+}
+
+func dumpInst(b *bytes.Buffer, i *Inst) {
+ switch i.Op {
+ case InstAlt:
+ bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg))
+ case InstAltMatch:
+ bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg))
+ case InstCapture:
+ bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out))
+ case InstEmptyWidth:
+ bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out))
+ case InstMatch:
+ bw(b, "match")
+ case InstFail:
+ bw(b, "fail")
+ case InstNop:
+ bw(b, "nop -> ", u32(i.Out))
+ case InstRune:
+ if i.Rune == nil {
+ // shouldn't happen
+ bw(b, "rune <nil>")
+ }
+ bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out))
+ }
+}
diff --git a/src/pkg/exp/regexp/syntax/prog_test.go b/src/pkg/exp/regexp/syntax/prog_test.go
new file mode 100644
index 000000000..7be4281c2
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/prog_test.go
@@ -0,0 +1,91 @@
+package syntax
+
+import (
+ "testing"
+)
+
+var compileTests = []struct {
+ Regexp string
+ Prog string
+}{
+ {"a", ` 0 fail
+ 1* rune "a" -> 2
+ 2 match
+`},
+ {"[A-M][n-z]", ` 0 fail
+ 1* rune "AM" -> 2
+ 2 rune "nz" -> 3
+ 3 match
+`},
+ {"", ` 0 fail
+ 1* nop -> 2
+ 2 match
+`},
+ {"a?", ` 0 fail
+ 1 rune "a" -> 3
+ 2* alt -> 1, 3
+ 3 match
+`},
+ {"a??", ` 0 fail
+ 1 rune "a" -> 3
+ 2* alt -> 3, 1
+ 3 match
+`},
+ {"a+", ` 0 fail
+ 1* rune "a" -> 2
+ 2 alt -> 1, 3
+ 3 match
+`},
+ {"a+?", ` 0 fail
+ 1* rune "a" -> 2
+ 2 alt -> 3, 1
+ 3 match
+`},
+ {"a*", ` 0 fail
+ 1 rune "a" -> 2
+ 2* alt -> 1, 3
+ 3 match
+`},
+ {"a*?", ` 0 fail
+ 1 rune "a" -> 2
+ 2* alt -> 3, 1
+ 3 match
+`},
+ {"a+b+", ` 0 fail
+ 1* rune "a" -> 2
+ 2 alt -> 1, 3
+ 3 rune "b" -> 4
+ 4 alt -> 3, 5
+ 5 match
+`},
+ {"(a+)(b+)", ` 0 fail
+ 1* cap 2 -> 2
+ 2 rune "a" -> 3
+ 3 alt -> 2, 4
+ 4 cap 3 -> 5
+ 5 cap 4 -> 6
+ 6 rune "b" -> 7
+ 7 alt -> 6, 8
+ 8 cap 5 -> 9
+ 9 match
+`},
+ {"a+|b+", ` 0 fail
+ 1 rune "a" -> 2
+ 2 alt -> 1, 6
+ 3 rune "b" -> 4
+ 4 alt -> 3, 6
+ 5* alt -> 1, 3
+ 6 match
+`},
+}
+
+func TestCompile(t *testing.T) {
+ for _, tt := range compileTests {
+ re, _ := Parse(tt.Regexp, Perl)
+ p, _ := Compile(re)
+ s := p.String()
+ if s != tt.Prog {
+ t.Errorf("compiled %#q:\n--- have\n%s---\n--- want\n%s---", tt.Regexp, s, tt.Prog)
+ }
+ }
+}
diff --git a/src/pkg/exp/regexp/syntax/regexp.go b/src/pkg/exp/regexp/syntax/regexp.go
index a0c465967..00a4addef 100644
--- a/src/pkg/exp/regexp/syntax/regexp.go
+++ b/src/pkg/exp/regexp/syntax/regexp.go
@@ -33,6 +33,8 @@ type Regexp struct {
type Op uint8
// Operators are listed in precedence order, tightest binding to weakest.
+// Character class operators are listed simplest to most complex
+// (OpLiteral, OpCharClass, OpAnyCharNotNL, OpAnyChar).
const (
OpNoMatch Op = 1 + iota // matches no strings
@@ -58,6 +60,59 @@ const (
const opPseudo Op = 128 // where pseudo-ops start
+// Equal returns true if x and y have identical structure.
+func (x *Regexp) Equal(y *Regexp) bool {
+ if x == nil || y == nil {
+ return x == y
+ }
+ if x.Op != y.Op {
+ return false
+ }
+ switch x.Op {
+ case OpEndText:
+ // The parse flags remember whether this is \z or \Z.
+ if x.Flags&WasDollar != y.Flags&WasDollar {
+ return false
+ }
+
+ case OpLiteral, OpCharClass:
+ if len(x.Rune) != len(y.Rune) {
+ return false
+ }
+ for i, r := range x.Rune {
+ if r != y.Rune[i] {
+ return false
+ }
+ }
+
+ case OpAlternate, OpConcat:
+ if len(x.Sub) != len(y.Sub) {
+ return false
+ }
+ for i, sub := range x.Sub {
+ if !sub.Equal(y.Sub[i]) {
+ return false
+ }
+ }
+
+ case OpStar, OpPlus, OpQuest:
+ if x.Flags&NonGreedy != y.Flags&NonGreedy || !x.Sub[0].Equal(y.Sub[0]) {
+ return false
+ }
+
+ case OpRepeat:
+ if x.Flags&NonGreedy != y.Flags&NonGreedy || x.Min != y.Min || x.Max != y.Max || !x.Sub[0].Equal(y.Sub[0]) {
+ return false
+ }
+
+ case OpCapture:
+ if x.Cap != y.Cap || x.Name != y.Name || !x.Sub[0].Equal(y.Sub[0]) {
+ return false
+ }
+ }
+ return true
+}
+
// writeRegexp writes the Perl syntax for the regular expression re to b.
func writeRegexp(b *bytes.Buffer, re *Regexp) {
switch re.Op {
@@ -68,16 +123,24 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) {
case OpEmptyMatch:
b.WriteString(`(?:)`)
case OpLiteral:
+ if re.Flags&FoldCase != 0 {
+ b.WriteString(`(?i:`)
+ }
for _, r := range re.Rune {
escape(b, r, false)
}
+ if re.Flags&FoldCase != 0 {
+ b.WriteString(`)`)
+ }
case OpCharClass:
if len(re.Rune)%2 != 0 {
b.WriteString(`[invalid char class]`)
break
}
b.WriteRune('[')
- if len(re.Rune) > 0 && re.Rune[0] == 0 && re.Rune[len(re.Rune)-1] == unicode.MaxRune {
+ if len(re.Rune) == 0 {
+ b.WriteString(`^\x00-\x{10FFFF}`)
+ } else if re.Rune[0] == 0 && re.Rune[len(re.Rune)-1] == unicode.MaxRune {
// Contains 0 and MaxRune. Probably a negated class.
// Print the gaps.
b.WriteRune('^')
@@ -124,7 +187,9 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) {
} else {
b.WriteRune('(')
}
- writeRegexp(b, re.Sub[0])
+ if re.Sub[0].Op != OpEmptyMatch {
+ writeRegexp(b, re.Sub[0])
+ }
b.WriteRune(')')
case OpStar, OpPlus, OpQuest, OpRepeat:
if sub := re.Sub[0]; sub.Op > OpCapture {
@@ -203,6 +268,15 @@ func escape(b *bytes.Buffer, r int, force bool) {
case '\v':
b.WriteString(`\v`)
default:
+ if r < 0x100 {
+ b.WriteString(`\x`)
+ s := strconv.Itob(r, 16)
+ if len(s) == 1 {
+ b.WriteRune('0')
+ }
+ b.WriteString(s)
+ break
+ }
b.WriteString(`\x{`)
b.WriteString(strconv.Itob(r, 16))
b.WriteString(`}`)
diff --git a/src/pkg/exp/regexp/syntax/simplify.go b/src/pkg/exp/regexp/syntax/simplify.go
new file mode 100644
index 000000000..72390417b
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/simplify.go
@@ -0,0 +1,151 @@
+// 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 syntax
+
+// Simplify returns a regexp equivalent to re but without counted repetitions
+// and with various other simplifications, such as rewriting /(?:a+)+/ to /a+/.
+// The resulting regexp will execute correctly but its string representation
+// will not produce the same parse tree, because capturing parentheses
+// may have been duplicated or removed. For example, the simplified form
+// for /(x){1,2}/ is /(x)(x)?/ but both parentheses capture as $1.
+// The returned regexp may share structure with or be the original.
+func (re *Regexp) Simplify() *Regexp {
+ if re == nil {
+ return nil
+ }
+ switch re.Op {
+ case OpCapture, OpConcat, OpAlternate:
+ // Simplify children, building new Regexp if children change.
+ nre := re
+ for i, sub := range re.Sub {
+ nsub := sub.Simplify()
+ if nre == re && nsub != sub {
+ // Start a copy.
+ nre = new(Regexp)
+ *nre = *re
+ nre.Rune = nil
+ nre.Sub = append(nre.Sub0[:0], re.Sub[:i]...)
+ }
+ if nre != re {
+ nre.Sub = append(nre.Sub, nsub)
+ }
+ }
+ return nre
+
+ case OpStar, OpPlus, OpQuest:
+ sub := re.Sub[0].Simplify()
+ return simplify1(re.Op, re.Flags, sub, re)
+
+ case OpRepeat:
+ // Special special case: x{0} matches the empty string
+ // and doesn't even need to consider x.
+ if re.Min == 0 && re.Max == 0 {
+ return &Regexp{Op: OpEmptyMatch}
+ }
+
+ // The fun begins.
+ sub := re.Sub[0].Simplify()
+
+ // x{n,} means at least n matches of x.
+ if re.Max == -1 {
+ // Special case: x{0,} is x*.
+ if re.Min == 0 {
+ return simplify1(OpStar, re.Flags, sub, nil)
+ }
+
+ // Special case: x{1,} is x+.
+ if re.Min == 1 {
+ return simplify1(OpPlus, re.Flags, sub, nil)
+ }
+
+ // General case: x{4,} is xxxx+.
+ nre := &Regexp{Op: OpConcat}
+ nre.Sub = nre.Sub0[:0]
+ for i := 0; i < re.Min-1; i++ {
+ nre.Sub = append(nre.Sub, sub)
+ }
+ nre.Sub = append(nre.Sub, simplify1(OpPlus, re.Flags, sub, nil))
+ return nre
+ }
+
+ // Special case x{0} handled above.
+
+ // Special case: x{1} is just x.
+ if re.Min == 1 && re.Max == 1 {
+ return sub
+ }
+
+ // General case: x{n,m} means n copies of x and m copies of x?
+ // The machine will do less work if we nest the final m copies,
+ // so that x{2,5} = xx(x(x(x)?)?)?
+
+ // Build leading prefix: xx.
+ var prefix *Regexp
+ if re.Min > 0 {
+ prefix = &Regexp{Op: OpConcat}
+ prefix.Sub = prefix.Sub0[:0]
+ for i := 0; i < re.Min; i++ {
+ prefix.Sub = append(prefix.Sub, sub)
+ }
+ }
+
+ // Build and attach suffix: (x(x(x)?)?)?
+ if re.Max > re.Min {
+ suffix := simplify1(OpQuest, re.Flags, sub, nil)
+ for i := re.Min + 1; i < re.Max; i++ {
+ nre2 := &Regexp{Op: OpConcat}
+ nre2.Sub = append(nre2.Sub0[:0], sub, suffix)
+ suffix = simplify1(OpQuest, re.Flags, nre2, nil)
+ }
+ if prefix == nil {
+ return suffix
+ }
+ prefix.Sub = append(prefix.Sub, suffix)
+ }
+ if prefix != nil {
+ return prefix
+ }
+
+ // Some degenerate case like min > max or min < max < 0.
+ // Handle as impossible match.
+ return &Regexp{Op: OpNoMatch}
+ }
+
+ return re
+}
+
+// simplify1 implements Simplify for the unary OpStar,
+// OpPlus, and OpQuest operators. It returns the simple regexp
+// equivalent to
+//
+// Regexp{Op: op, Flags: flags, Sub: {sub}}
+//
+// under the assumption that sub is already simple, and
+// without first allocating that structure. If the regexp
+// to be returned turns out to be equivalent to re, simplify1
+// returns re instead.
+//
+// simplify1 is factored out of Simplify because the implementation
+// for other operators generates these unary expressions.
+// Letting them call simplify1 makes sure the expressions they
+// generate are simple.
+func simplify1(op Op, flags Flags, sub, re *Regexp) *Regexp {
+ // Special case: repeat the empty string as much as
+ // you want, but it's still the empty string.
+ if sub.Op == OpEmptyMatch {
+ return sub
+ }
+ // The operators are idempotent if the flags match.
+ if op == sub.Op && flags&NonGreedy == sub.Flags&NonGreedy {
+ return sub
+ }
+ if re != nil && re.Op == op && re.Flags&NonGreedy == flags&NonGreedy && sub == re.Sub[0] {
+ return re
+ }
+
+ re = &Regexp{Op: op, Flags: flags}
+ re.Sub = append(re.Sub0[:0], sub)
+ return re
+}
diff --git a/src/pkg/exp/regexp/syntax/simplify_test.go b/src/pkg/exp/regexp/syntax/simplify_test.go
new file mode 100644
index 000000000..c8cec2183
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/simplify_test.go
@@ -0,0 +1,151 @@
+// 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 syntax
+
+import "testing"
+
+var simplifyTests = []struct {
+ Regexp string
+ Simple string
+}{
+ // Already-simple constructs
+ {`a`, `a`},
+ {`ab`, `ab`},
+ {`a|b`, `[a-b]`},
+ {`ab|cd`, `ab|cd`},
+ {`(ab)*`, `(ab)*`},
+ {`(ab)+`, `(ab)+`},
+ {`(ab)?`, `(ab)?`},
+ {`.`, `.`},
+ {`^`, `^`},
+ {`$`, `$`},
+ {`[ac]`, `[ac]`},
+ {`[^ac]`, `[^ac]`},
+
+ // Posix character classes
+ {`[[:alnum:]]`, `[0-9A-Za-z]`},
+ {`[[:alpha:]]`, `[A-Za-z]`},
+ {`[[:blank:]]`, `[\t ]`},
+ {`[[:cntrl:]]`, `[\x00-\x1f\x7f]`},
+ {`[[:digit:]]`, `[0-9]`},
+ {`[[:graph:]]`, `[!-~]`},
+ {`[[:lower:]]`, `[a-z]`},
+ {`[[:print:]]`, `[ -~]`},
+ {`[[:punct:]]`, "[!-/:-@\\[-`\\{-~]"},
+ {`[[:space:]]`, `[\t-\r ]`},
+ {`[[:upper:]]`, `[A-Z]`},
+ {`[[:xdigit:]]`, `[0-9A-Fa-f]`},
+
+ // Perl character classes
+ {`\d`, `[0-9]`},
+ {`\s`, `[\t-\n\f-\r ]`},
+ {`\w`, `[0-9A-Z_a-z]`},
+ {`\D`, `[^0-9]`},
+ {`\S`, `[^\t-\n\f-\r ]`},
+ {`\W`, `[^0-9A-Z_a-z]`},
+ {`[\d]`, `[0-9]`},
+ {`[\s]`, `[\t-\n\f-\r ]`},
+ {`[\w]`, `[0-9A-Z_a-z]`},
+ {`[\D]`, `[^0-9]`},
+ {`[\S]`, `[^\t-\n\f-\r ]`},
+ {`[\W]`, `[^0-9A-Z_a-z]`},
+
+ // Posix repetitions
+ {`a{1}`, `a`},
+ {`a{2}`, `aa`},
+ {`a{5}`, `aaaaa`},
+ {`a{0,1}`, `a?`},
+ // The next three are illegible because Simplify inserts (?:)
+ // parens instead of () parens to avoid creating extra
+ // captured subexpressions. The comments show a version with fewer parens.
+ {`(a){0,2}`, `(?:(a)(a)?)?`}, // (aa?)?
+ {`(a){0,4}`, `(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // (a(a(aa?)?)?)?
+ {`(a){2,6}`, `(a)(a)(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // aa(a(a(aa?)?)?)?
+ {`a{0,2}`, `(?:aa?)?`}, // (aa?)?
+ {`a{0,4}`, `(?:a(?:a(?:aa?)?)?)?`}, // (a(a(aa?)?)?)?
+ {`a{2,6}`, `aa(?:a(?:a(?:aa?)?)?)?`}, // aa(a(a(aa?)?)?)?
+ {`a{0,}`, `a*`},
+ {`a{1,}`, `a+`},
+ {`a{2,}`, `aa+`},
+ {`a{5,}`, `aaaaa+`},
+
+ // Test that operators simplify their arguments.
+ {`(?:a{1,}){1,}`, `a+`},
+ {`(a{1,}b{1,})`, `(a+b+)`},
+ {`a{1,}|b{1,}`, `a+|b+`},
+ {`(?:a{1,})*`, `(?:a+)*`},
+ {`(?:a{1,})+`, `a+`},
+ {`(?:a{1,})?`, `(?:a+)?`},
+ {``, `(?:)`},
+ {`a{0}`, `(?:)`},
+
+ // Character class simplification
+ {`[ab]`, `[a-b]`},
+ {`[a-za-za-z]`, `[a-z]`},
+ {`[A-Za-zA-Za-z]`, `[A-Za-z]`},
+ {`[ABCDEFGH]`, `[A-H]`},
+ {`[AB-CD-EF-GH]`, `[A-H]`},
+ {`[W-ZP-XE-R]`, `[E-Z]`},
+ {`[a-ee-gg-m]`, `[a-m]`},
+ {`[a-ea-ha-m]`, `[a-m]`},
+ {`[a-ma-ha-e]`, `[a-m]`},
+ {`[a-zA-Z0-9 -~]`, `[ -~]`},
+
+ // Empty character classes
+ {`[^[:cntrl:][:^cntrl:]]`, `[^\x00-\x{10FFFF}]`},
+
+ // Full character classes
+ {`[[:cntrl:][:^cntrl:]]`, `.`},
+
+ // Unicode case folding.
+ {`(?i)A`, `(?i:A)`},
+ {`(?i)a`, `(?i:a)`},
+ {`(?i)[A]`, `(?i:A)`},
+ {`(?i)[a]`, `(?i:A)`},
+ {`(?i)K`, `(?i:K)`},
+ {`(?i)k`, `(?i:k)`},
+ {`(?i)\x{212a}`, "(?i:\u212A)"},
+ {`(?i)[K]`, "[Kk\u212A]"},
+ {`(?i)[k]`, "[Kk\u212A]"},
+ {`(?i)[\x{212a}]`, "[Kk\u212A]"},
+ {`(?i)[a-z]`, "[A-Za-z\u017F\u212A]"},
+ {`(?i)[\x00-\x{FFFD}]`, "[\\x00-\uFFFD]"},
+ {`(?i)[\x00-\x{10FFFF}]`, `.`},
+
+ // Empty string as a regular expression.
+ // The empty string must be preserved inside parens in order
+ // to make submatches work right, so these tests are less
+ // interesting than they might otherwise be. String inserts
+ // explicit (?:) in place of non-parenthesized empty strings,
+ // to make them easier to spot for other parsers.
+ {`(a|b|)`, `([a-b]|(?:))`},
+ {`(|)`, `()`},
+ {`a()`, `a()`},
+ {`(()|())`, `(()|())`},
+ {`(a|)`, `(a|(?:))`},
+ {`ab()cd()`, `ab()cd()`},
+ {`()`, `()`},
+ {`()*`, `()*`},
+ {`()+`, `()+`},
+ {`()?`, `()?`},
+ {`(){0}`, `(?:)`},
+ {`(){1}`, `()`},
+ {`(){1,}`, `()+`},
+ {`(){0,2}`, `(?:()()?)?`},
+}
+
+func TestSimplify(t *testing.T) {
+ for _, tt := range simplifyTests {
+ re, err := Parse(tt.Regexp, MatchNL|Perl&^OneLine)
+ if err != nil {
+ t.Errorf("Parse(%#q) = error %v", tt.Regexp, err)
+ continue
+ }
+ s := re.Simplify().String()
+ if s != tt.Simple {
+ t.Errorf("Simplify(%#q) = %#q, want %#q", tt.Regexp, s, tt.Simple)
+ }
+ }
+}
diff --git a/src/pkg/exp/template/Makefile b/src/pkg/exp/template/Makefile
index ab9832f61..8550b0d52 100644
--- a/src/pkg/exp/template/Makefile
+++ b/src/pkg/exp/template/Makefile
@@ -4,9 +4,12 @@
include ../../../Make.inc
-TARG=template
+TARG=exp/template
GOFILES=\
+ exec.go\
+ funcs.go\
lex.go\
parse.go\
+ set.go\
include ../../../Make.pkg
diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go
new file mode 100644
index 000000000..fb0a9e621
--- /dev/null
+++ b/src/pkg/exp/template/exec.go
@@ -0,0 +1,508 @@
+// 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"
+ "os"
+ "reflect"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// state represents the state of an execution. It's not part of the
+// template so that multiple executions of the same template
+// can execute in parallel.
+type state struct {
+ tmpl *Template
+ wr io.Writer
+ set *Set
+ line int // line number for errors
+}
+
+// errorf formats the error and terminates processing.
+func (s *state) errorf(format string, args ...interface{}) {
+ format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.name, s.line, format)
+ panic(fmt.Errorf(format, args...))
+}
+
+// error terminates processing.
+func (s *state) error(err os.Error) {
+ s.errorf("%s", err)
+}
+
+// Execute applies a parsed template to the specified data object,
+// writing the output to wr.
+func (t *Template) Execute(wr io.Writer, data interface{}) os.Error {
+ return t.ExecuteInSet(wr, data, nil)
+}
+
+// ExecuteInSet applies a parsed template to the specified data object,
+// writing the output to wr. Nested template invocations will be resolved
+// from the specified set.
+func (t *Template) ExecuteInSet(wr io.Writer, data interface{}, set *Set) (err os.Error) {
+ defer t.recover(&err)
+ state := &state{
+ tmpl: t,
+ wr: wr,
+ set: set,
+ line: 1,
+ }
+ if t.root == nil {
+ state.errorf("must be parsed before execution")
+ }
+ state.walk(reflect.ValueOf(data), t.root)
+ return
+}
+
+// Walk functions step through the major pieces of the template structure,
+// generating output as they go.
+func (s *state) walk(data reflect.Value, n node) {
+ switch n := n.(type) {
+ case *actionNode:
+ s.line = n.line
+ s.printValue(n, s.evalPipeline(data, n.pipeline))
+ case *listNode:
+ for _, node := range n.nodes {
+ s.walk(data, node)
+ }
+ case *ifNode:
+ s.line = n.line
+ s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList)
+ case *rangeNode:
+ s.line = n.line
+ s.walkRange(data, n)
+ case *textNode:
+ if _, err := s.wr.Write(n.text); err != nil {
+ s.error(err)
+ }
+ case *templateNode:
+ s.line = n.line
+ s.walkTemplate(data, n)
+ case *withNode:
+ s.line = n.line
+ s.walkIfOrWith(nodeWith, data, n.pipeline, n.list, n.elseList)
+ default:
+ s.errorf("unknown node: %s", n)
+ }
+}
+
+// walkIfOrWith walks an 'if' or 'with' node. The two control structures
+// are identical in behavior except that 'with' sets dot.
+func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) {
+ val := s.evalPipeline(data, pipe)
+ truth, ok := isTrue(val)
+ if !ok {
+ s.errorf("if/with can't use value of type %T", val.Interface())
+ }
+ if truth {
+ if typ == nodeWith {
+ data = val
+ }
+ s.walk(data, list)
+ } else if elseList != nil {
+ s.walk(data, elseList)
+ }
+}
+
+// isTrue returns whether the value is 'true', in the sense of not the zero of its type,
+// and whether the value has a meaningful truth value.
+func isTrue(val reflect.Value) (truth, ok bool) {
+ switch val.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ truth = val.Len() > 0
+ case reflect.Bool:
+ truth = val.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ truth = val.Int() != 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ truth = val.Uint() != 0
+ case reflect.Float32, reflect.Float64:
+ truth = val.Float() != 0
+ case reflect.Complex64, reflect.Complex128:
+ truth = val.Complex() != 0
+ case reflect.Chan, reflect.Func, reflect.Ptr:
+ truth = !val.IsNil()
+ default:
+ return
+ }
+ return truth, true
+}
+
+func (s *state) walkRange(data reflect.Value, r *rangeNode) {
+ val := s.evalPipeline(data, r.pipeline)
+ switch val.Kind() {
+ case reflect.Array, reflect.Slice:
+ if val.Len() == 0 {
+ break
+ }
+ for i := 0; i < val.Len(); i++ {
+ s.walk(val.Index(i), r.list)
+ }
+ return
+ case reflect.Map:
+ if val.Len() == 0 {
+ break
+ }
+ for _, key := range val.MapKeys() {
+ s.walk(val.MapIndex(key), r.list)
+ }
+ return
+ default:
+ s.errorf("range can't iterate over value of type %T", val.Interface())
+ }
+ if r.elseList != nil {
+ s.walk(data, r.elseList)
+ }
+}
+
+func (s *state) walkTemplate(data reflect.Value, t *templateNode) {
+ name := s.evalArg(data, reflect.TypeOf("string"), t.name).String()
+ if s.set == nil {
+ s.errorf("no set defined in which to invoke template named %q", name)
+ }
+ tmpl := s.set.tmpl[name]
+ if tmpl == nil {
+ s.errorf("template %q not in set", name)
+ }
+ data = s.evalPipeline(data, t.pipeline)
+ newState := *s
+ newState.tmpl = tmpl
+ newState.walk(data, tmpl.root)
+}
+
+// Eval functions evaluate pipelines, commands, and their elements and extract
+// values from the data structure by examining fields, calling methods, and so on.
+// The printing of those values happens only through walk functions.
+
+func (s *state) evalPipeline(data reflect.Value, pipe []*commandNode) reflect.Value {
+ value := reflect.Value{}
+ for _, cmd := range pipe {
+ value = s.evalCommand(data, cmd, value) // previous value is this one's final arg.
+ // If the object has type interface{}, dig down one level to the thing inside.
+ if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
+ value = reflect.ValueOf(value.Interface()) // lovely!
+ }
+ }
+ return value
+}
+
+func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value {
+ firstWord := cmd.args[0]
+ switch n := firstWord.(type) {
+ case *fieldNode:
+ return s.evalFieldNode(data, n, cmd.args, final)
+ case *identifierNode:
+ return s.evalFieldOrCall(data, n.ident, cmd.args, final)
+ }
+ if len(cmd.args) > 1 || final.IsValid() {
+ s.errorf("can't give argument to non-function %s", cmd.args[0])
+ }
+ switch word := cmd.args[0].(type) {
+ case *dotNode:
+ return data
+ case *boolNode:
+ return reflect.ValueOf(word.true)
+ case *numberNode:
+ // These are ideal constants but we don't know the type
+ // and we have no context. (If it was a method argument,
+ // we'd know what we need.) The syntax guides us to some extent.
+ switch {
+ case word.isComplex:
+ return reflect.ValueOf(word.complex128) // incontrovertible.
+ case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0:
+ return reflect.ValueOf(word.float64)
+ case word.isInt:
+ return reflect.ValueOf(word.int64)
+ case word.isUint:
+ return reflect.ValueOf(word.uint64)
+ }
+ case *stringNode:
+ return reflect.ValueOf(word.text)
+ }
+ s.errorf("can't handle command %q", firstWord)
+ panic("not reached")
+}
+
+func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value {
+ // Up to the last entry, it must be a field.
+ n := len(field.ident)
+ for i := 0; i < n-1; i++ {
+ data = s.evalField(data, field.ident[i])
+ }
+ // Now it can be a field or method and if a method, gets arguments.
+ return s.evalFieldOrCall(data, field.ident[n-1], args, final)
+}
+
+// Is this an exported - upper case - name?
+func isExported(name string) bool {
+ rune, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(rune)
+}
+
+func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value {
+ var isNil bool
+ if data, isNil = indirect(data); isNil {
+ s.errorf("%s is nil pointer", fieldName)
+ }
+ switch data.Kind() {
+ case reflect.Struct:
+ // Is it a field?
+ field := data.FieldByName(fieldName)
+ // TODO: look higher up the tree if we can't find it here. Also unexported fields
+ // might succeed higher up, as map keys.
+ if field.IsValid() && isExported(fieldName) { // valid and exported
+ return field
+ }
+ s.errorf("%s has no exported field %q", data.Type(), fieldName)
+ default:
+ s.errorf("can't evaluate field %s of type %s", fieldName, data.Type())
+ }
+ panic("not reached")
+}
+
+func (s *state) evalFieldOrCall(data reflect.Value, fieldName string, args []node, final reflect.Value) reflect.Value {
+ // Is it a function?
+ if function, ok := findFunction(fieldName, s.tmpl, s.set); ok {
+ return s.evalCall(data, function, fieldName, false, args, final)
+ }
+ ptr := data
+ for data.Kind() == reflect.Ptr && !data.IsNil() {
+ ptr, data = data, reflect.Indirect(data)
+ }
+ // Is it a method? We use the pointer because it has value methods too.
+ if method, ok := methodByName(ptr.Type(), fieldName); ok {
+ return s.evalCall(ptr, method.Func, fieldName, true, args, final)
+ }
+ if len(args) > 1 || final.IsValid() {
+ s.errorf("%s is not a method but has arguments", fieldName)
+ }
+ switch data.Kind() {
+ case reflect.Struct:
+ return s.evalField(data, fieldName)
+ default:
+ s.errorf("can't handle evaluation of field %s of type %s", fieldName, data.Type())
+ }
+ panic("not reached")
+}
+
+// TODO: delete when reflect's own MethodByName is released.
+func methodByName(typ reflect.Type, name string) (reflect.Method, bool) {
+ for i := 0; i < typ.NumMethod(); i++ {
+ if typ.Method(i).Name == name {
+ return typ.Method(i), true
+ }
+ }
+ return reflect.Method{}, false
+}
+
+var (
+ osErrorType = reflect.TypeOf(new(os.Error)).Elem()
+)
+
+func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args []node, final reflect.Value) reflect.Value {
+ typ := fun.Type()
+ if !isMethod && len(args) > 0 { // Args will be nil if it's a niladic call in an argument list
+ args = args[1:] // first arg is name of function; not used in call.
+ }
+ numIn := len(args)
+ if final.IsValid() {
+ numIn++
+ }
+ numFixed := len(args)
+ if typ.IsVariadic() {
+ numFixed = typ.NumIn() - 1 // last arg is the variadic one.
+ if numIn < numFixed {
+ s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
+ }
+ } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
+ s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
+ }
+ if !goodFunc(typ) {
+ s.errorf("can't handle multiple results from method/function %q", name)
+ }
+ // Build the arg list.
+ argv := make([]reflect.Value, numIn)
+ // First arg is the receiver.
+ i := 0
+ if isMethod {
+ argv[0] = v
+ i++
+ }
+ // Others must be evaluated. Fixed args first.
+ for ; i < numFixed; i++ {
+ argv[i] = s.evalArg(v, typ.In(i), args[i])
+ }
+ // And now the ... args.
+ if typ.IsVariadic() {
+ argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
+ for ; i < len(args); i++ {
+ argv[i] = s.evalArg(v, argType, args[i])
+ }
+ }
+ // Add final value if necessary.
+ if final.IsValid() {
+ argv[len(args)] = final
+ }
+ result := fun.Call(argv)
+ // If we have an os.Error that is not nil, stop execution and return that error to the caller.
+ if len(result) == 2 && !result[1].IsNil() {
+ s.error(result[1].Interface().(os.Error))
+ }
+ return result[0]
+}
+
+func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
+ if field, ok := n.(*fieldNode); ok {
+ value := s.evalFieldNode(data, field, []node{n}, reflect.Value{})
+ if !value.Type().AssignableTo(typ) {
+ s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+ }
+ return value
+ }
+ switch typ.Kind() {
+ case reflect.Bool:
+ return s.evalBool(typ, n)
+ case reflect.String:
+ return s.evalString(typ, n)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return s.evalInteger(typ, n)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return s.evalUnsignedInteger(typ, n)
+ case reflect.Float32, reflect.Float64:
+ return s.evalFloat(typ, n)
+ case reflect.Complex64, reflect.Complex128:
+ return s.evalComplex(typ, n)
+ case reflect.Interface:
+ if typ.NumMethod() == 0 {
+ return s.evalEmptyInterface(data, typ, n)
+ }
+ }
+ s.errorf("can't handle %s for arg of type %s", n, typ)
+ panic("not reached")
+}
+
+func (s *state) evalBool(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*boolNode); ok {
+ value := reflect.New(typ).Elem()
+ value.SetBool(n.true)
+ return value
+ }
+ s.errorf("expected bool; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalString(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*stringNode); ok {
+ value := reflect.New(typ).Elem()
+ value.SetString(n.text)
+ return value
+ }
+ s.errorf("expected string; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalInteger(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*numberNode); ok && n.isInt {
+ value := reflect.New(typ).Elem()
+ value.SetInt(n.int64)
+ return value
+ }
+ s.errorf("expected integer; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalUnsignedInteger(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*numberNode); ok && n.isUint {
+ value := reflect.New(typ).Elem()
+ value.SetUint(n.uint64)
+ return value
+ }
+ s.errorf("expected unsigned integer; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalFloat(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*numberNode); ok && n.isFloat {
+ value := reflect.New(typ).Elem()
+ value.SetFloat(n.float64)
+ return value
+ }
+ s.errorf("expected float; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalComplex(typ reflect.Type, n node) reflect.Value {
+ if n, ok := n.(*numberNode); ok && n.isComplex {
+ value := reflect.New(typ).Elem()
+ value.SetComplex(n.complex128)
+ return value
+ }
+ s.errorf("expected complex; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value {
+ switch n := n.(type) {
+ case *boolNode:
+ return reflect.ValueOf(n.true)
+ case *dotNode:
+ return data
+ case *fieldNode:
+ return s.evalFieldNode(data, n, nil, reflect.Value{})
+ case *identifierNode:
+ return s.evalFieldOrCall(data, n.ident, nil, reflect.Value{})
+ case *numberNode:
+ if n.isComplex {
+ return reflect.ValueOf(n.complex128)
+ }
+ if n.isInt {
+ return reflect.ValueOf(n.int64)
+ }
+ if n.isUint {
+ return reflect.ValueOf(n.uint64)
+ }
+ if n.isFloat {
+ return reflect.ValueOf(n.float64)
+ }
+ case *stringNode:
+ return reflect.ValueOf(n.text)
+ }
+ s.errorf("can't handle assignment of %s to empty interface argument", n)
+ panic("not reached")
+}
+
+// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
+func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
+ for v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return v, true
+ }
+ v = v.Elem()
+ }
+ return v, false
+}
+
+// printValue writes the textual representation of the value to the output of
+// the template.
+func (s *state) printValue(n node, v reflect.Value) {
+ if !v.IsValid() {
+ fmt.Fprint(s.wr, "<no value>")
+ return
+ }
+ switch v.Kind() {
+ case reflect.Ptr:
+ var isNil bool
+ if v, isNil = indirect(v); isNil {
+ fmt.Fprint(s.wr, "<nil>")
+ return
+ }
+ case reflect.Chan, reflect.Func, reflect.Interface:
+ s.errorf("can't print %s of type %s", n, v.Type())
+ }
+ fmt.Fprint(s.wr, v.Interface())
+}
diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go
new file mode 100644
index 000000000..86b958e84
--- /dev/null
+++ b/src/pkg/exp/template/exec_test.go
@@ -0,0 +1,342 @@
+// 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"
+ "os"
+ "sort"
+ "strings"
+ "testing"
+)
+
+// T has lots of interesting pieces to use to test execution.
+type T struct {
+ // Basics
+ I int
+ U16 uint16
+ X string
+ FloatZero float64
+ ComplexZero float64
+ // Nested structs.
+ U *U
+ // Slices
+ SI []int
+ SIEmpty []int
+ SB []bool
+ // Maps
+ MSI map[string]int
+ MSIone map[string]int // one element, for deterministic output
+ MSIEmpty map[string]int
+ SMSI []map[string]int
+ // Empty interfaces; used to see if we can dig inside one.
+ Empty0 interface{} // nil
+ Empty1 interface{}
+ Empty2 interface{}
+ Empty3 interface{}
+ Empty4 interface{}
+ // Pointers
+ PI *int
+ PSI *[]int
+ NIL *int
+}
+
+var tVal = &T{
+ I: 17,
+ U16: 16,
+ X: "x",
+ U: &U{"v"},
+ SI: []int{3, 4, 5},
+ SB: []bool{true, false},
+ MSI: map[string]int{"one": 1, "two": 2, "three": 3},
+ MSIone: map[string]int{"one": 1},
+ SMSI: []map[string]int{
+ {"one": 1, "two": 2},
+ {"eleven": 11, "twelve": 12},
+ },
+ Empty1: 3,
+ Empty2: "empty2",
+ Empty3: []int{7, 8},
+ Empty4: &U{"v"},
+ PI: newInt(23),
+ PSI: newIntSlice(21, 22, 23),
+}
+
+// Helpers for creation.
+func newInt(n int) *int {
+ p := new(int)
+ *p = n
+ return p
+}
+
+func newIntSlice(n ...int) *[]int {
+ p := new([]int)
+ *p = make([]int, len(n))
+ copy(*p, n)
+ return p
+}
+
+// Simple methods with and without arguments.
+func (t *T) Method0() string {
+ return "resultOfMethod0"
+}
+
+func (t *T) Method1(a int) int {
+ return a
+}
+
+func (t *T) Method2(a uint16, b string) string {
+ return fmt.Sprintf("Method2: %d %s", a, b)
+}
+
+func (t *T) MAdd(a int, b []int) []int {
+ v := make([]int, len(b))
+ for i, x := range b {
+ v[i] = x + a
+ }
+ return v
+}
+
+// MSort is used to sort map keys for stable output. (Nice trick!)
+func (t *T) MSort(m map[string]int) []string {
+ keys := make([]string, len(m))
+ i := 0
+ for k := range m {
+ keys[i] = k
+ i++
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+// EPERM returns a value and an os.Error according to its argument.
+func (t *T) EPERM(error bool) (bool, os.Error) {
+ if error {
+ return true, os.EPERM
+ }
+ return false, nil
+}
+
+type U struct {
+ V string
+}
+
+type execTest struct {
+ name string
+ input string
+ output string
+ data interface{}
+ ok bool
+}
+
+var execTests = []execTest{
+ // Trivial cases.
+ {"empty", "", "", nil, true},
+ {"text", "some text", "some text", nil, true},
+
+ // Fields of structs.
+ {".X", "-{{.X}}-", "-x-", tVal, true},
+ {".U.V", "-{{.U.V}}-", "-v-", tVal, true},
+
+ // Dots of all kinds to test basic evaluation.
+ {"dot int", "<{{.}}>", "<13>", 13, true},
+ {"dot uint", "<{{.}}>", "<14>", uint(14), true},
+ {"dot float", "<{{.}}>", "<15.1>", 15.1, true},
+ {"dot bool", "<{{.}}>", "<true>", true, true},
+ {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true},
+ {"dot string", "<{{.}}>", "<hello>", "hello", true},
+ {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true},
+ {"dot map", "<{{.}}>", "<map[two:22 one:11]>", map[string]int{"one": 11, "two": 22}, true},
+ {"dot struct", "<{{.}}>", "<{7 seven}>", struct {
+ a int
+ b string
+ }{7, "seven"}, true},
+
+ // Pointers.
+ {"*int", "{{.PI}}", "23", tVal, true},
+ {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
+ {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
+ {"NIL", "{{.NIL}}", "<nil>", tVal, true},
+
+ // Emtpy interfaces holding values.
+ {"empty nil", "{{.Empty0}}", "<no value>", tVal, true},
+ {"empty with int", "{{.Empty1}}", "3", tVal, true},
+ {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
+ {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true},
+ {"empty with struct", "{{.Empty4}}", "{v}", tVal, true},
+
+ // Method calls.
+ {".Method0", "-{{.Method0}}-", "-resultOfMethod0-", tVal, true},
+ {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
+ {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
+ {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
+ {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
+
+ // Pipelines.
+ {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 resultOfMethod0-", tVal, true},
+
+ // If.
+ {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
+ {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
+ {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
+ {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+ {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+ {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
+
+ // Printf.
+ {"printf", `{{printf "hello, printf"}}`, "hello, printf", tVal, true},
+ {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
+ {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
+ {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true},
+ {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true},
+ {"printf function", `{{printf "%#q" gopher}}`, "`gopher`", tVal, true},
+ {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
+ {"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true},
+ {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true},
+
+ // HTML.
+ {"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
+ "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
+ {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
+ "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
+
+ // JavaScript.
+ {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true},
+
+ // Booleans
+ {"not", "{{not true}} {{not false}}", "false true", nil, true},
+ {"and", "{{and 0 0}} {{and 1 0}} {{and 0 1}} {{and 1 1}}", "false false false true", nil, true},
+ {"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true},
+ {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true},
+ {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
+
+ // Indexing.
+ {"slice[0]", "{{index .SI 0}}", "3", tVal, true},
+ {"slice[1]", "{{index .SI 1}}", "4", tVal, true},
+ {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false},
+ {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
+ {"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
+ {"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
+ {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, false},
+ {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
+ {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
+
+ // With.
+ {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
+ {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
+ {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true},
+ {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true},
+ {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true},
+ {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
+ {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true},
+ {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true},
+ {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
+ {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
+
+ // Range.
+ {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
+ {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
+ {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
+ {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
+ {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
+ {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true},
+ {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
+ {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true},
+ {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
+ {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
+
+ // Error handling.
+ {"error method, error", "{{.EPERM true}}", "", tVal, false},
+ {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
+}
+
+func gopher() string {
+ return "gopher"
+}
+
+func testExecute(execTests []execTest, set *Set, t *testing.T) {
+ b := new(bytes.Buffer)
+ funcs := FuncMap{"gopher": gopher}
+ for _, test := range execTests {
+ tmpl := New(test.name).Funcs(funcs)
+ err := tmpl.Parse(test.input)
+ if err != nil {
+ t.Errorf("%s: parse error: %s", test.name, err)
+ continue
+ }
+ b.Reset()
+ err = tmpl.ExecuteInSet(b, test.data, set)
+ switch {
+ case !test.ok && err == nil:
+ t.Errorf("%s: expected error; got none", test.name)
+ continue
+ case test.ok && err != nil:
+ t.Errorf("%s: unexpected execute error: %s", test.name, err)
+ continue
+ case !test.ok && err != nil:
+ // expected error, got one
+ if *debug {
+ fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
+ }
+ }
+ result := b.String()
+ if result != test.output {
+ t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result)
+ }
+ }
+}
+
+func TestExecute(t *testing.T) {
+ testExecute(execTests, nil, t)
+}
+
+// Check that an error from a method flows back to the top.
+func TestExecuteError(t *testing.T) {
+ b := new(bytes.Buffer)
+ tmpl := New("error")
+ err := tmpl.Parse("{{.EPERM true}}")
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+ err = tmpl.Execute(b, tVal)
+ if err == nil {
+ t.Errorf("expected error; got none")
+ } else if !strings.Contains(err.String(), os.EPERM.String()) {
+ t.Errorf("expected os.EPERM; got %s", err)
+ }
+}
+
+func TestJSEscaping(t *testing.T) {
+ testCases := []struct {
+ in, exp string
+ }{
+ {`a`, `a`},
+ {`'foo`, `\'foo`},
+ {`Go "jump" \`, `Go \"jump\" \\`},
+ {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
+ {"unprintable \uFDFF", `unprintable \uFDFF`},
+ }
+ for _, tc := range testCases {
+ s := JSEscapeString(tc.in)
+ if s != tc.exp {
+ t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp)
+ }
+ }
+}
diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go
new file mode 100644
index 000000000..66be40fd4
--- /dev/null
+++ b/src/pkg/exp/template/funcs.go
@@ -0,0 +1,294 @@
+// 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"
+ "io"
+ "os"
+ "reflect"
+ "strings"
+ "unicode"
+ "utf8"
+)
+
+// 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 os.Error.
+type FuncMap map[string]interface{}
+
+var funcs = map[string]reflect.Value{
+ "and": reflect.ValueOf(and),
+ "html": reflect.ValueOf(HTMLEscaper),
+ "index": reflect.ValueOf(index),
+ "js": reflect.ValueOf(JSEscaper),
+ "not": reflect.ValueOf(not),
+ "or": reflect.ValueOf(or),
+ "printf": reflect.ValueOf(fmt.Sprintf),
+}
+
+// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
+func addFuncs(values map[string]reflect.Value, funcMap FuncMap) {
+ for name, fn := range funcMap {
+ v := reflect.ValueOf(fn)
+ if v.Kind() != reflect.Func {
+ panic("value for " + name + " not a function")
+ }
+ if !goodFunc(v.Type()) {
+ panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
+ }
+ values[name] = v
+ }
+}
+
+// goodFunc checks that the function or method has the right result signature.
+func goodFunc(typ reflect.Type) bool {
+ // We allow functions with 1 result or 2 results where the second is an os.Error.
+ switch {
+ case typ.NumOut() == 1:
+ return true
+ case typ.NumOut() == 2 && typ.Out(1) == osErrorType:
+ return true
+ }
+ 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 {
+ if fn := tmpl.funcs[name]; fn.IsValid() {
+ return fn, true
+ }
+ }
+ if set != nil {
+ if fn := set.funcs[name]; fn.IsValid() {
+ return fn, true
+ }
+ }
+ if fn := funcs[name]; fn.IsValid() {
+ return fn, true
+ }
+ return reflect.Value{}, false
+}
+
+// Indexing.
+
+// index returns the result of indexing its first argument by the following
+// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
+// indexed item must be a map, slice, or array.
+func index(item interface{}, indices ...interface{}) (interface{}, os.Error) {
+ v := reflect.ValueOf(item)
+ for _, i := range indices {
+ index := reflect.ValueOf(i)
+ var isNil bool
+ if v, isNil = indirect(v); isNil {
+ return nil, fmt.Errorf("index of nil pointer")
+ }
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice:
+ var x int64
+ switch index.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x = index.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ x = int64(index.Uint())
+ default:
+ return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
+ }
+ if x < 0 || x >= int64(v.Len()) {
+ return nil, fmt.Errorf("index out of range: %d", x)
+ }
+ v = v.Index(int(x))
+ case reflect.Map:
+ if !index.Type().AssignableTo(v.Type().Key()) {
+ return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
+ }
+ v = v.MapIndex(index)
+ if !v.IsValid() {
+ return nil, fmt.Errorf("index %v not present in map", index.Interface())
+ }
+ default:
+ return nil, fmt.Errorf("can't index item of type %s", index.Type())
+ }
+ }
+ return v.Interface(), nil
+}
+
+// Boolean logic.
+
+// and returns the Boolean AND of its arguments.
+func and(arg0 interface{}, args ...interface{}) (truth bool) {
+ truth, _ = isTrue(reflect.ValueOf(arg0))
+ for i := 0; truth && i < len(args); i++ {
+ truth, _ = isTrue(reflect.ValueOf(args[i]))
+ }
+ return
+}
+
+// or returns the Boolean OR of its arguments.
+func or(arg0 interface{}, args ...interface{}) (truth bool) {
+ truth, _ = isTrue(reflect.ValueOf(arg0))
+ for i := 0; !truth && i < len(args); i++ {
+ truth, _ = isTrue(reflect.ValueOf(args[i]))
+ }
+ return
+}
+
+// not returns the Boolean negation of its argument.
+func not(arg interface{}) (truth bool) {
+ truth, _ = isTrue(reflect.ValueOf(arg))
+ return !truth
+}
+
+// HTML escaping.
+
+var (
+ htmlQuot = []byte("&#34;") // shorter than "&quot;"
+ htmlApos = []byte("&#39;") // shorter than "&apos;"
+ htmlAmp = []byte("&amp;")
+ htmlLt = []byte("&lt;")
+ htmlGt = []byte("&gt;")
+)
+
+// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
+func HTMLEscape(w io.Writer, b []byte) {
+ last := 0
+ for i, c := range b {
+ var html []byte
+ switch c {
+ case '"':
+ html = htmlQuot
+ case '\'':
+ html = htmlApos
+ case '&':
+ html = htmlAmp
+ case '<':
+ html = htmlLt
+ case '>':
+ html = htmlGt
+ default:
+ continue
+ }
+ w.Write(b[last:i])
+ w.Write(html)
+ last = i + 1
+ }
+ w.Write(b[last:])
+}
+
+// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
+func HTMLEscapeString(s string) string {
+ // Avoid allocation if we can.
+ if strings.IndexAny(s, `'"&<>`) < 0 {
+ return s
+ }
+ var b bytes.Buffer
+ HTMLEscape(&b, []byte(s))
+ return b.String()
+}
+
+// HTMLEscaper returns the escaped HTML equivalent of the textual
+// representation of its arguments.
+func HTMLEscaper(args ...interface{}) string {
+ ok := false
+ var s string
+ if len(args) == 1 {
+ s, ok = args[0].(string)
+ }
+ if !ok {
+ s = fmt.Sprint(args...)
+ }
+ return HTMLEscapeString(s)
+}
+
+// JavaScript escaping.
+
+var (
+ jsLowUni = []byte(`\u00`)
+ hex = []byte("0123456789ABCDEF")
+
+ jsBackslash = []byte(`\\`)
+ jsApos = []byte(`\'`)
+ jsQuot = []byte(`\"`)
+)
+
+
+// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
+func JSEscape(w io.Writer, b []byte) {
+ last := 0
+ for i := 0; i < len(b); i++ {
+ c := b[i]
+
+ if ' ' <= c && c < utf8.RuneSelf && c != '\\' && c != '"' && c != '\'' {
+ // fast path: nothing to do
+ continue
+ }
+ w.Write(b[last:i])
+
+ if c < utf8.RuneSelf {
+ // Quotes and slashes get quoted.
+ // Control characters get written as \u00XX.
+ switch c {
+ case '\\':
+ w.Write(jsBackslash)
+ case '\'':
+ w.Write(jsApos)
+ case '"':
+ w.Write(jsQuot)
+ default:
+ w.Write(jsLowUni)
+ t, b := c>>4, c&0x0f
+ w.Write(hex[t : t+1])
+ w.Write(hex[b : b+1])
+ }
+ } else {
+ // Unicode rune.
+ rune, size := utf8.DecodeRune(b[i:])
+ if unicode.IsPrint(rune) {
+ w.Write(b[i : i+size])
+ } else {
+ // TODO(dsymonds): Do this without fmt?
+ fmt.Fprintf(w, "\\u%04X", rune)
+ }
+ i += size - 1
+ }
+ last = i + 1
+ }
+ w.Write(b[last:])
+}
+
+// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
+func JSEscapeString(s string) string {
+ // Avoid allocation if we can.
+ if strings.IndexFunc(s, jsIsSpecial) < 0 {
+ return s
+ }
+ var b bytes.Buffer
+ JSEscape(&b, []byte(s))
+ return b.String()
+}
+
+func jsIsSpecial(rune int) bool {
+ switch rune {
+ case '\\', '\'', '"':
+ return true
+ }
+ return rune < ' ' || utf8.RuneSelf <= rune
+}
+
+// JSEscaper returns the escaped JavaScript equivalent of the textual
+// representation of its arguments.
+func JSEscaper(args ...interface{}) string {
+ ok := false
+ var s string
+ if len(args) == 1 {
+ s, ok = args[0].(string)
+ }
+ if !ok {
+ s = fmt.Sprint(args...)
+ }
+ return JSEscapeString(s)
+}
diff --git a/src/pkg/exp/template/lex.go b/src/pkg/exp/template/lex.go
index 826d3eb88..d78152979 100644
--- a/src/pkg/exp/template/lex.go
+++ b/src/pkg/exp/template/lex.go
@@ -18,58 +18,71 @@ type item struct {
}
func (i item) String() string {
- switch i.typ {
- case itemEOF:
+ switch {
+ case i.typ == itemEOF:
return "EOF"
- case itemError:
+ case i.typ == itemError:
return i.val
- }
- if len(i.val) > 10 {
+ case i.typ > itemKeyword:
+ return fmt.Sprintf("<%s>", i.val)
+ case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
-// itemType identifies the type of lex item.
+// itemType identifies the type of lex items.
type itemType int
const (
- itemError itemType = iota // error occurred; value is text of error
- itemDot // the cursor, spelled '.'.
+ itemError itemType = iota // error occurred; value is text of error
+ itemBool // boolean constant
+ itemComplex // complex constant (1+2i); imaginary is just a number
itemEOF
- itemElse // else keyword
- itemEnd // end keyword
- itemField // alphanumeric identifier, starting with '.'.
+ itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
itemIdentifier // alphanumeric identifier
- itemIf // if keyword
- itemLeftMeta // left meta-string
- itemNumber // number
+ itemLeftDelim // left action delimiter
+ itemNumber // simple number, including imaginary
itemPipe // pipe symbol
- itemRange // range keyword
itemRawString // raw quoted string (includes quotes)
- itemRightMeta // right meta-string
+ itemRightDelim // right action delimiter
itemString // quoted string (includes quotes)
itemText // plain text
+ // Keywords appear after all the rest.
+ itemKeyword // used only to delimit the keywords
+ itemDot // the cursor, spelled '.'.
+ itemDefine // define keyword
+ itemElse // else keyword
+ itemEnd // end keyword
+ itemIf // if keyword
+ itemRange // range keyword
+ itemTemplate // template keyword
+ itemWith // with keyword
)
// Make the types prettyprint.
var itemName = map[itemType]string{
itemError: "error",
- itemDot: ".",
+ itemBool: "bool",
+ itemComplex: "complex",
itemEOF: "EOF",
- itemElse: "else",
- itemEnd: "end",
itemField: "field",
itemIdentifier: "identifier",
- itemIf: "if",
- itemLeftMeta: "left meta",
+ itemLeftDelim: "left delim",
itemNumber: "number",
itemPipe: "pipe",
- itemRange: "range",
itemRawString: "raw string",
- itemRightMeta: "rightMeta",
+ itemRightDelim: "right delim",
itemString: "string",
- itemText: "text",
+ // keywords
+ itemDot: ".",
+ itemDefine: "define",
+ itemElse: "else",
+ itemIf: "if",
+ itemEnd: "end",
+ itemRange: "range",
+ itemTemplate: "template",
+ itemWith: "with",
}
func (i itemType) String() string {
@@ -81,11 +94,14 @@ func (i itemType) String() string {
}
var key = map[string]itemType{
- ".": itemDot,
- "else": itemElse,
- "end": itemEnd,
- "if": itemIf,
- "range": itemRange,
+ ".": itemDot,
+ "define": itemDefine,
+ "else": itemElse,
+ "end": itemEnd,
+ "if": itemIf,
+ "range": itemRange,
+ "template": itemTemplate,
+ "with": itemWith,
}
const eof = -1
@@ -97,6 +113,7 @@ type stateFn func(*lexer) stateFn
type lexer struct {
name string // the name of the input; used only for error reports.
input string // the string being scanned.
+ state stateFn // the next lexing function to enter
pos int // current position in the input.
start int // start position of this item.
width int // width of last rune read from input.
@@ -166,38 +183,47 @@ func (l *lexer) errorf(format string, args ...interface{}) stateFn {
return nil
}
-// run lexes the input by executing state functions until nil.
-func (l *lexer) run() {
- for state := lexText; state != nil; {
- state = state(l)
+// nextItem returns the next item from the input.
+func (l *lexer) nextItem() item {
+ for {
+ select {
+ case item := <-l.items:
+ return item
+ default:
+ l.state = l.state(l)
+ }
}
- close(l.items)
+ panic("not reached")
}
-// lex launches a new scanner and returns the channel of items.
-func lex(name, input string) (*lexer, chan item) {
+// lex creates a new scanner for the input string.
+func lex(name, input string) *lexer {
l := &lexer{
name: name,
input: input,
- items: make(chan item),
+ state: lexText,
+ items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
}
- go l.run()
- return l, l.items
+ return l
}
// state functions
-const leftMeta = "{{"
-const rightMeta = "}}"
+const (
+ leftDelim = "{{"
+ rightDelim = "}}"
+ leftComment = "{{/*"
+ rightComment = "*/}}"
+)
-// lexText scans until a metacharacter
+// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
for {
- if strings.HasPrefix(l.input[l.pos:], leftMeta) {
+ if strings.HasPrefix(l.input[l.pos:], leftDelim) {
if l.pos > l.start {
l.emit(itemText)
}
- return lexLeftMeta
+ return lexLeftDelim
}
if l.next() == eof {
break
@@ -211,28 +237,42 @@ func lexText(l *lexer) stateFn {
return nil
}
-// leftMeta scans the left "metacharacter", which is known to be present.
-func lexLeftMeta(l *lexer) stateFn {
- l.pos += len(leftMeta)
- l.emit(itemLeftMeta)
+// lexLeftDelim scans the left delimiter, which is known to be present.
+func lexLeftDelim(l *lexer) stateFn {
+ if strings.HasPrefix(l.input[l.pos:], leftComment) {
+ return lexComment
+ }
+ l.pos += len(leftDelim)
+ l.emit(itemLeftDelim)
return lexInsideAction
}
-// rightMeta scans the right "metacharacter", which is known to be present.
-func lexRightMeta(l *lexer) stateFn {
- l.pos += len(rightMeta)
- l.emit(itemRightMeta)
+// lexComment scans a comment. The left comment marker is known to be present.
+func lexComment(l *lexer) stateFn {
+ i := strings.Index(l.input[l.pos:], rightComment)
+ if i < 0 {
+ return l.errorf("unclosed comment")
+ }
+ l.pos += i + len(rightComment)
+ l.ignore()
+ return lexText
+}
+
+// lexRightDelim scans the right delimiter, which is known to be present.
+func lexRightDelim(l *lexer) stateFn {
+ l.pos += len(rightDelim)
+ l.emit(itemRightDelim)
return lexText
}
-// lexInsideAction scans the elements inside "metacharacters".
+// lexInsideAction scans the elements inside action delimiters.
func lexInsideAction(l *lexer) stateFn {
// Either number, quoted string, or identifier.
// Spaces separate and are ignored.
// Pipe symbols separate and are emitted.
for {
- if strings.HasPrefix(l.input[l.pos:], rightMeta) {
- return lexRightMeta
+ if strings.HasPrefix(l.input[l.pos:], rightDelim) {
+ return lexRightDelim
}
switch r := l.next(); {
case r == eof || r == '\n':
@@ -273,15 +313,19 @@ Loop:
for {
switch r := l.next(); {
case isAlphaNumeric(r):
- // absorb
+ // absorb.
+ case r == '.' && l.input[l.start] == '.':
+ // field chaining; absorb into one token.
default:
l.backup()
word := l.input[l.start:l.pos]
switch {
- case key[word] != itemError:
+ case key[word] > itemKeyword:
l.emit(key[word])
case word[0] == '.':
l.emit(itemField)
+ case word == "true", word == "false":
+ l.emit(itemBool)
default:
l.emit(itemIdentifier)
}
@@ -295,8 +339,23 @@ Loop:
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
-// TODO: without expressions you can do imaginary but not complex.
func lexNumber(l *lexer) stateFn {
+ if !l.scanNumber() {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ if sign := l.peek(); sign == '+' || sign == '-' {
+ // Complex: 1+2i. No spaces, must end in 'i'.
+ if !l.scanNumber() || l.input[l.pos-1] != 'i' {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ l.emit(itemComplex)
+ } else {
+ l.emit(itemNumber)
+ }
+ return lexInsideAction
+}
+
+func (l *lexer) scanNumber() bool {
// Optional leading sign.
l.accept("+-")
// Is it hex?
@@ -317,10 +376,9 @@ func lexNumber(l *lexer) stateFn {
// Next thing mustn't be alphanumeric.
if isAlphaNumeric(l.peek()) {
l.next()
- return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ return false
}
- l.emit(itemNumber)
- return lexInsideAction
+ return true
}
// lexQuote scans a quoted string.
diff --git a/src/pkg/exp/template/lex_test.go b/src/pkg/exp/template/lex_test.go
index 184e833ef..256ec04d8 100644
--- a/src/pkg/exp/template/lex_test.go
+++ b/src/pkg/exp/template/lex_test.go
@@ -17,8 +17,8 @@ type lexTest struct {
var (
tEOF = item{itemEOF, ""}
- tLeft = item{itemLeftMeta, "{{"}
- tRight = item{itemRightMeta, "}}"}
+ tLeft = item{itemLeftDelim, "{{"}
+ tRight = item{itemRightDelim, "}}"}
tRange = item{itemRange, "range"}
tPipe = item{itemPipe, "|"}
tFor = item{itemIdentifier, "for"}
@@ -31,11 +31,16 @@ var lexTests = []lexTest{
{"empty", "", []item{tEOF}},
{"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
{"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
+ {"text with comment", "hello-{{/* this is a comment */}}-world", []item{
+ {itemText, "hello-"},
+ {itemText, "-world"},
+ tEOF,
+ }},
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
- {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4}}", []item{
+ {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
tLeft,
{itemNumber, "1"},
{itemNumber, "02"},
@@ -43,25 +48,40 @@ var lexTests = []lexTest{
{itemNumber, "-7.2i"},
{itemNumber, "1e3"},
{itemNumber, "+1.2e-4"},
+ {itemNumber, "4.2i"},
+ {itemComplex, "1+2i"},
tRight,
tEOF,
}},
- {"dots", "{{.x . .2 .x.y }}", []item{
+ {"bools", "{{true false}}", []item{
+ tLeft,
+ {itemBool, "true"},
+ {itemBool, "false"},
+ tRight,
+ tEOF,
+ }},
+ {"dot", "{{.}}", []item{
+ tLeft,
+ {itemDot, "."},
+ tRight,
+ tEOF,
+ }},
+ {"dots", "{{.x . .2 .x.y}}", []item{
tLeft,
{itemField, ".x"},
{itemDot, "."},
{itemNumber, ".2"},
- {itemField, ".x"},
- {itemField, ".y"},
+ {itemField, ".x.y"},
tRight,
tEOF,
}},
- {"keywords", "{{range if else end}}", []item{
+ {"keywords", "{{range if else end with}}", []item{
tLeft,
{itemRange, "range"},
{itemIf, "if"},
{itemElse, "else"},
{itemEnd, "end"},
+ {itemWith, "with"},
tRight,
tEOF,
}},
@@ -112,9 +132,13 @@ var lexTests = []lexTest{
// collect gathers the emitted items into a slice.
func collect(t *lexTest) (items []item) {
- _, tokens := lex(t.name, t.input)
- for i := range tokens {
- items = append(items, i)
+ l := lex(t.name, t.input)
+ for {
+ item := l.nextItem()
+ items = append(items, item)
+ if item.typ == itemEOF || item.typ == itemError {
+ break
+ }
}
return
}
diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go
index 57ddb0084..8b2d60207 100644
--- a/src/pkg/exp/template/parse.go
+++ b/src/pkg/exp/template/parse.go
@@ -8,17 +8,21 @@ import (
"bytes"
"fmt"
"os"
+ "reflect"
"runtime"
"strconv"
+ "strings"
+ "unicode"
)
// Template is the representation of a parsed template.
type Template struct {
- // TODO: At the moment, these are all internal to parsing.
- name string
- root *listNode
+ name string
+ root *listNode
+ funcs map[string]reflect.Value
+ // Parsing only; cleared after parse.
+ set *Set
lex *lexer
- tokens chan item
token item // token lookahead for parser
havePeek bool
}
@@ -28,7 +32,7 @@ func (t *Template) next() item {
if t.havePeek {
t.havePeek = false
} else {
- t.token = <-t.tokens
+ t.token = t.lex.nextItem()
}
return t.token
}
@@ -43,7 +47,7 @@ func (t *Template) peek() item {
if t.havePeek {
return t.token
}
- t.token = <-t.tokens
+ t.token = t.lex.nextItem()
t.havePeek = true
return t.token
}
@@ -64,14 +68,18 @@ const (
nodeText nodeType = iota
nodeAction
nodeCommand
+ nodeDot
nodeElse
nodeEnd
nodeField
nodeIdentifier
+ nodeIf
nodeList
nodeNumber
nodeRange
nodeString
+ nodeTemplate
+ nodeWith
)
// Nodes.
@@ -103,25 +111,26 @@ func (l *listNode) String() string {
// textNode holds plain text.
type textNode struct {
nodeType
- text string
+ text []byte
}
func newText(text string) *textNode {
- return &textNode{nodeType: nodeText, text: text}
+ return &textNode{nodeType: nodeText, text: []byte(text)}
}
func (t *textNode) String() string {
return fmt.Sprintf("(text: %q)", t.text)
}
-// actionNode holds an action (something bounded by metacharacters).
+// actionNode holds an action (something bounded by delimiters).
type actionNode struct {
nodeType
+ line int
pipeline []*commandNode
}
-func newAction() *actionNode {
- return &actionNode{nodeType: nodeAction}
+func newAction(line int, pipeline []*commandNode) *actionNode {
+ return &actionNode{nodeType: nodeAction, line: line, pipeline: pipeline}
}
func (a *actionNode) append(command *commandNode) {
@@ -164,45 +173,88 @@ func (i *identifierNode) String() string {
return fmt.Sprintf("I=%s", i.ident)
}
-// fieldNode holds a field (identifier starting with '.'). The period is dropped from the ident.
+// dotNode holds the special identifier '.'. It is represented by a nil pointer.
+type dotNode bool
+
+func newDot() *dotNode {
+ return nil
+}
+
+func (d *dotNode) typ() nodeType {
+ return nodeDot
+}
+
+func (d *dotNode) String() string {
+ return "{{<.>}}"
+}
+
+// fieldNode holds a field (identifier starting with '.').
+// The names may be chained ('.x.y').
+// The period is dropped from each ident.
type fieldNode struct {
nodeType
- ident string
+ ident []string
}
func newField(ident string) *fieldNode {
- return &fieldNode{nodeType: nodeField, ident: ident[1:]} //drop period
+ return &fieldNode{nodeType: nodeField, ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
}
func (f *fieldNode) String() string {
- return fmt.Sprintf("F=.%s", f.ident)
+ return fmt.Sprintf("F=%s", f.ident)
+}
+
+// boolNode holds a boolean constant.
+type boolNode struct {
+ nodeType
+ true bool
+}
+
+func newBool(true bool) *boolNode {
+ return &boolNode{nodeType: nodeString, true: true}
+}
+
+func (b *boolNode) String() string {
+ if b.true {
+ return fmt.Sprintf("B=true")
+ }
+ return fmt.Sprintf("B=false")
}
-// numberNode holds a number, signed or unsigned, integer, floating, or imaginary.
+// numberNode holds a number, signed or unsigned integer, floating, or complex.
// The value is parsed and stored under all the types that can represent the value.
// This simulates in a small amount of code the behavior of Go's ideal constants.
-// TODO: booleans, complex numbers.
type numberNode struct {
nodeType
- isInt bool // number has an integral value
- isUint bool // number has an unsigned integral value
- isFloat bool // number has a floating-point value
- imaginary bool // number is imaginary
- int64 // the signed integer value
- uint64 // the unsigned integer value
- float64 // the positive floating-point value
- text string
-}
-
-func newNumber(text string) (*numberNode, os.Error) {
+ isInt bool // number has an integral value
+ isUint bool // number has an unsigned integral value
+ isFloat bool // number has a floating-point value
+ isComplex bool // number is complex
+ int64 // the signed integer value
+ uint64 // the unsigned integer value
+ float64 // the floating-point value
+ complex128 // the complex value
+ text string
+}
+
+func newNumber(text string, isComplex bool) (*numberNode, os.Error) {
n := &numberNode{nodeType: nodeNumber, text: text}
- // Imaginary constants can only be floating-point.
+ if isComplex {
+ // fmt.Sscan can parse the pair, so let it do the work.
+ if _, err := fmt.Sscan(text, &n.complex128); err != nil {
+ return nil, err
+ }
+ n.isComplex = true
+ n.simplifyComplex()
+ return n, nil
+ }
+ // Imaginary constants can only be complex unless they are zero.
if len(text) > 0 && text[len(text)-1] == 'i' {
f, err := strconv.Atof64(text[:len(text)-1])
if err == nil {
- n.imaginary = true
- n.isFloat = true
- n.float64 = f
+ n.isComplex = true
+ n.complex128 = complex(0, f)
+ n.simplifyComplex()
return n, nil
}
}
@@ -250,6 +302,23 @@ func newNumber(text string) (*numberNode, os.Error) {
return n, nil
}
+// simplifyComplex pulls out any other types that are represented by the complex number.
+// These all require that the imaginary part be zero.
+func (n *numberNode) simplifyComplex() {
+ n.isFloat = imag(n.complex128) == 0
+ if n.isFloat {
+ n.float64 = real(n.complex128)
+ n.isInt = float64(int64(n.float64)) == n.float64
+ if n.isInt {
+ n.int64 = int64(n.float64)
+ }
+ n.isUint = float64(uint64(n.float64)) == n.float64
+ if n.isUint {
+ n.uint64 = uint64(n.float64)
+ }
+ }
+}
+
func (n *numberNode) String() string {
return fmt.Sprintf("N=%s", n.text)
}
@@ -283,11 +352,14 @@ func (e *endNode) String() string {
return "{{end}}"
}
-// elseNode represents an {{else}} action. It is represented by a nil pointer.
-type elseNode bool
+// elseNode represents an {{else}} action.
+type elseNode struct {
+ nodeType
+ line int
+}
-func newElse() *elseNode {
- return nil
+func newElse(line int) *elseNode {
+ return &elseNode{nodeType: nodeElse, line: line}
}
func (e *elseNode) typ() nodeType {
@@ -297,37 +369,106 @@ func (e *elseNode) typ() nodeType {
func (e *elseNode) String() string {
return "{{else}}"
}
+// ifNode represents an {{if}} action and its commands.
+// TODO: what should evaluation look like? is a pipeline enough?
+type ifNode struct {
+ nodeType
+ line int
+ pipeline []*commandNode
+ list *listNode
+ elseList *listNode
+}
+
+func newIf(line int, pipeline []*commandNode, list, elseList *listNode) *ifNode {
+ return &ifNode{nodeType: nodeIf, line: line, pipeline: pipeline, list: list, elseList: elseList}
+}
+
+func (i *ifNode) String() string {
+ if i.elseList != nil {
+ return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.pipeline, i.list, i.elseList)
+ }
+ return fmt.Sprintf("({{if %s}} %s)", i.pipeline, i.list)
+}
-// rangeNode represents an {{range}} action and its commands.
+// rangeNode represents a {{range}} action and its commands.
type rangeNode struct {
nodeType
- field node
+ line int
+ pipeline []*commandNode
list *listNode
elseList *listNode
}
-func newRange(field node, list *listNode) *rangeNode {
- return &rangeNode{nodeType: nodeRange, field: field, list: list}
+func newRange(line int, pipeline []*commandNode, list, elseList *listNode) *rangeNode {
+ return &rangeNode{nodeType: nodeRange, line: line, pipeline: pipeline, list: list, elseList: elseList}
}
func (r *rangeNode) String() string {
if r.elseList != nil {
- return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.field, r.list, r.elseList)
+ return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.pipeline, r.list, r.elseList)
}
- return fmt.Sprintf("({{range %s}} %s)", r.field, r.list)
+ return fmt.Sprintf("({{range %s}} %s)", r.pipeline, r.list)
+}
+
+// templateNode represents a {{template}} action.
+type templateNode struct {
+ nodeType
+ line int
+ name node
+ pipeline []*commandNode
}
+func newTemplate(line int, name node, pipeline []*commandNode) *templateNode {
+ return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipeline: pipeline}
+}
+
+func (t *templateNode) String() string {
+ return fmt.Sprintf("{{template %s %s}}", t.name, t.pipeline)
+}
+
+// withNode represents a {{with}} action and its commands.
+type withNode struct {
+ nodeType
+ line int
+ pipeline []*commandNode
+ list *listNode
+ elseList *listNode
+}
+
+func newWith(line int, pipeline []*commandNode, list, elseList *listNode) *withNode {
+ return &withNode{nodeType: nodeWith, line: line, pipeline: pipeline, list: list, elseList: elseList}
+}
+
+func (w *withNode) String() string {
+ if w.elseList != nil {
+ return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.pipeline, w.list, w.elseList)
+ }
+ return fmt.Sprintf("({{with %s}} %s)", w.pipeline, w.list)
+}
+
+
// Parsing.
// New allocates a new template with the given name.
func New(name string) *Template {
return &Template{
- name: name,
+ name: name,
+ funcs: make(map[string]reflect.Value),
}
}
+// Funcs adds to the template's function map the elements of the
+// argument 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 {
+ addFuncs(t.funcs, funcMap)
+ return t
+}
+
// errorf formats the error and terminates processing.
func (t *Template) errorf(format string, args ...interface{}) {
+ t.root = nil
format = fmt.Sprintf("template: %s:%d: %s", t.name, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...))
}
@@ -351,25 +492,80 @@ func (t *Template) unexpected(token item, context string) {
t.errorf("unexpected %s in %s", token, context)
}
-// Parse parses the template definition string and constructs an efficient representation of the template.
-func (t *Template) Parse(s string) (err os.Error) {
- t.lex, t.tokens = lex(t.name, s)
- defer func() {
- e := recover()
- if e != nil {
- if _, ok := e.(runtime.Error); ok {
- panic(e)
+// recover is the handler that turns panics into returns from the top
+// level of Parse or Execute.
+func (t *Template) recover(errp *os.Error) {
+ e := recover()
+ if e != nil {
+ if _, ok := e.(runtime.Error); ok {
+ panic(e)
+ }
+ t.stopParse()
+ *errp = e.(os.Error)
+ }
+ return
+}
+
+// startParse starts the template parsing from the lexer.
+func (t *Template) startParse(set *Set, lex *lexer) {
+ t.root = nil
+ t.set = set
+ t.lex = lex
+}
+
+// stopParse terminates parsing.
+func (t *Template) stopParse() {
+ t.set, t.lex = nil, nil
+}
+
+// atEOF returns true if, possibly after spaces, we're at EOF.
+func (t *Template) atEOF() bool {
+ for {
+ token := t.peek()
+ switch token.typ {
+ case itemEOF:
+ return true
+ case itemText:
+ for _, r := range token.val {
+ if !unicode.IsSpace(r) {
+ return false
+ }
}
- err = e.(os.Error)
+ t.next() // skip spaces.
+ continue
}
- return
- }()
- var next node
+ break
+ }
+ return false
+}
+
+// Parse parses the template definition string to construct an internal representation
+// of the template for execution.
+func (t *Template) Parse(s string) (err os.Error) {
+ t.startParse(nil, lex(t.name, s))
+ defer t.recover(&err)
+ t.parse(true)
+ t.stopParse()
+ return
+}
+
+// ParseInSet parses the template definition string to construct an internal representation
+// of the template for execution. Function bindings are checked against those in the set.
+func (t *Template) ParseInSet(s string, set *Set) (err os.Error) {
+ t.startParse(set, lex(t.name, s))
+ defer t.recover(&err)
+ t.parse(true)
+ t.stopParse()
+ return
+}
+
+// parse is the helper for Parse. It triggers an error if we expect EOF but don't reach it.
+func (t *Template) parse(toEOF bool) (next node) {
t.root, next = t.itemList(true)
- if next != nil {
+ if toEOF && next != nil {
t.errorf("unexpected %s", next)
}
- return nil
+ return next
}
// itemList:
@@ -398,7 +594,7 @@ func (t *Template) textOrAction() node {
switch token := t.next(); token.typ {
case itemText:
return newText(token.val)
- case itemLeftMeta:
+ case itemLeftDelim:
return t.action()
default:
t.unexpected(token, "input")
@@ -409,63 +605,95 @@ func (t *Template) textOrAction() node {
// Action:
// control
// command ("|" command)*
-// Left meta is past. Now get actions.
+// Left delim is past. Now get actions.
+// First word could be a keyword such as range.
func (t *Template) action() (n node) {
- action := newAction()
switch token := t.next(); token.typ {
- case itemRange:
- return t.rangeControl()
case itemElse:
return t.elseControl()
case itemEnd:
return t.endControl()
+ case itemIf:
+ return t.ifControl()
+ case itemRange:
+ return t.rangeControl()
+ case itemTemplate:
+ return t.templateControl()
+ case itemWith:
+ return t.withControl()
}
t.backup()
-Loop:
+ return newAction(t.lex.lineNumber(), t.pipeline("command"))
+}
+
+// Pipeline:
+// field or command
+// pipeline "|" pipeline
+func (t *Template) pipeline(context string) (pipe []*commandNode) {
for {
switch token := t.next(); token.typ {
- case itemRightMeta:
- break Loop
- case itemIdentifier, itemField:
- t.backup()
- cmd, err := t.command()
- if err != nil {
- t.error(err)
+ case itemRightDelim:
+ if len(pipe) == 0 {
+ t.errorf("missing value for %s", context)
}
- action.append(cmd)
+ return
+ case itemBool, itemComplex, itemDot, itemField, itemIdentifier, itemNumber, itemRawString, itemString:
+ t.backup()
+ pipe = append(pipe, t.command())
default:
- t.unexpected(token, "command")
+ t.unexpected(token, context)
}
}
- return action
+ return
}
-// Range:
-// {{range field}} itemList {{end}}
-// {{range field}} itemList {{else}} itemList {{end}}
-// Range keyword is past.
-func (t *Template) rangeControl() node {
- field := t.expect(itemField, "range")
- t.expect(itemRightMeta, "range")
- list, next := t.itemList(false)
- r := newRange(newField(field.val), list)
+func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) {
+ lineNum = t.lex.lineNumber()
+ pipe = t.pipeline(context)
+ var next node
+ list, next = t.itemList(false)
switch next.typ() {
case nodeEnd: //done
case nodeElse:
- elseList, next := t.itemList(false)
+ elseList, next = t.itemList(false)
if next.typ() != nodeEnd {
t.errorf("expected end; found %s", next)
}
- r.elseList = elseList
+ elseList = elseList
}
- return r
+ return lineNum, pipe, list, elseList
+}
+
+// If:
+// {{if pipeline}} itemList {{end}}
+// {{if pipeline}} itemList {{else}} itemList {{end}}
+// If keyword is past.
+func (t *Template) ifControl() node {
+ return newIf(t.parseControl("if"))
+}
+
+// Range:
+// {{range pipeline}} itemList {{end}}
+// {{range pipeline}} itemList {{else}} itemList {{end}}
+// Range keyword is past.
+func (t *Template) rangeControl() node {
+ return newRange(t.parseControl("range"))
}
+// With:
+// {{with pipeline}} itemList {{end}}
+// {{with pipeline}} itemList {{else}} itemList {{end}}
+// If keyword is past.
+func (t *Template) withControl() node {
+ return newWith(t.parseControl("with"))
+}
+
+
// End:
// {{end}}
// End keyword is past.
func (t *Template) endControl() node {
- t.expect(itemRightMeta, "end")
+ t.expect(itemRightDelim, "end")
return newEnd()
}
@@ -473,50 +701,83 @@ func (t *Template) endControl() node {
// {{else}}
// Else keyword is past.
func (t *Template) elseControl() node {
- t.expect(itemRightMeta, "else")
- return newElse()
+ t.expect(itemRightDelim, "else")
+ return newElse(t.lex.lineNumber())
+}
+
+// Template:
+// {{template stringValue pipeline}}
+// Template keyword is past. The name must be something that can evaluate
+// to a string.
+func (t *Template) templateControl() node {
+ var name node
+ switch token := t.next(); token.typ {
+ case itemIdentifier:
+ if _, ok := findFunction(token.val, t, t.set); !ok {
+ t.errorf("function %q not defined", token.val)
+ }
+ name = newIdentifier(token.val)
+ case itemDot:
+ name = newDot()
+ case itemField:
+ name = newField(token.val)
+ case itemString, itemRawString:
+ s, err := strconv.Unquote(token.val)
+ if err != nil {
+ t.error(err)
+ }
+ name = newString(s)
+ default:
+ t.unexpected(token, "template invocation")
+ }
+ pipeline := t.pipeline("template")
+ return newTemplate(t.lex.lineNumber(), name, pipeline)
}
// command:
-// space-separated arguments up to a pipeline character or right metacharacter.
-// we consume the pipe character but leave the right meta to terminate the action.
-func (t *Template) command() (*commandNode, os.Error) {
+// space-separated arguments up to a pipeline character or right delimiter.
+// we consume the pipe character but leave the right delim to terminate the action.
+func (t *Template) command() *commandNode {
cmd := newCommand()
Loop:
for {
switch token := t.next(); token.typ {
- case itemRightMeta:
+ case itemRightDelim:
t.backup()
break Loop
case itemPipe:
break Loop
case itemError:
- return nil, os.NewError(token.val)
+ t.errorf("%s", token.val)
case itemIdentifier:
+ if _, ok := findFunction(token.val, t, t.set); !ok {
+ t.errorf("function %q not defined", token.val)
+ }
cmd.append(newIdentifier(token.val))
+ case itemDot:
+ cmd.append(newDot())
case itemField:
cmd.append(newField(token.val))
- case itemNumber:
- if len(cmd.args) == 0 {
- t.errorf("command cannot be %q", token.val)
- }
- number, err := newNumber(token.val)
+ case itemBool:
+ cmd.append(newBool(token.val == "true"))
+ case itemComplex, itemNumber:
+ number, err := newNumber(token.val, token.typ == itemComplex)
if err != nil {
t.error(err)
}
cmd.append(number)
case itemString, itemRawString:
- if len(cmd.args) == 0 {
- t.errorf("command cannot be %q", token.val)
- }
s, err := strconv.Unquote(token.val)
if err != nil {
- return nil, err
+ t.error(err)
}
cmd.append(newString(s))
default:
t.unexpected(token, "command")
}
}
- return cmd, nil
+ if len(cmd.args) == 0 {
+ t.errorf("empty command")
+ }
+ return cmd
}
diff --git a/src/pkg/exp/template/parse_test.go b/src/pkg/exp/template/parse_test.go
index f89eaa6ce..71580f8b6 100644
--- a/src/pkg/exp/template/parse_test.go
+++ b/src/pkg/exp/template/parse_test.go
@@ -5,52 +5,65 @@
package template
import (
+ "flag"
"fmt"
"testing"
)
-const dumpErrors = true
+var debug = flag.Bool("debug", false, "show the errors produced by the tests")
type numberTest struct {
text string
isInt bool
isUint bool
isFloat bool
- imaginary bool
+ isComplex bool
int64
uint64
float64
+ complex128
}
var numberTests = []numberTest{
// basics
- {"0", true, true, true, false, 0, 0, 0},
- {"-0", true, true, true, false, 0, 0, 0}, // check that -0 is a uint.
- {"73", true, true, true, false, 73, 73, 73},
- {"-73", true, false, true, false, -73, 0, -73},
- {"+73", true, false, true, false, 73, 0, 73},
- {"100", true, true, true, false, 100, 100, 100},
- {"1e9", true, true, true, false, 1e9, 1e9, 1e9},
- {"-1e9", true, false, true, false, -1e9, 0, -1e9},
- {"-1.2", false, false, true, false, 0, 0, -1.2},
- {"1e19", false, true, true, false, 0, 1e19, 1e19},
- {"-1e19", false, false, true, false, 0, 0, -1e19},
- {"4i", false, false, true, true, 0, 0, 4},
+ {"0", true, true, true, false, 0, 0, 0, 0},
+ {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
+ {"73", true, true, true, false, 73, 73, 73, 0},
+ {"-73", true, false, true, false, -73, 0, -73, 0},
+ {"+73", true, false, true, false, 73, 0, 73, 0},
+ {"100", true, true, true, false, 100, 100, 100, 0},
+ {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
+ {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
+ {"-1.2", false, false, true, false, 0, 0, -1.2, 0},
+ {"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
+ {"-1e19", false, false, true, false, 0, 0, -1e19, 0},
+ {"4i", false, false, false, true, 0, 0, 0, 4i},
+ {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
+ // complex with 0 imaginary are float (and maybe integer)
+ {"0i", true, true, true, true, 0, 0, 0, 0},
+ {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
+ {"-12+0i", true, false, true, true, -12, 0, -12, -12},
+ {"13+0i", true, true, true, true, 13, 13, 13, 13},
// funny bases
- {"0123", true, true, true, false, 0123, 0123, 0123},
- {"-0x0", true, true, true, false, 0, 0, 0},
- {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef},
+ {"0123", true, true, true, false, 0123, 0123, 0123, 0},
+ {"-0x0", true, true, true, false, 0, 0, 0, 0},
+ {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
// some broken syntax
{text: "+-2"},
{text: "0x123."},
{text: "1e."},
{text: "0xi."},
+ {text: "1+2."},
}
func TestNumberParse(t *testing.T) {
for _, test := range numberTests {
- n, err := newNumber(test.text)
- ok := test.isInt || test.isUint || test.isFloat
+ // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
+ // because imaginary comes out as a number.
+ var c complex128
+ _, err := fmt.Sscan(test.text, &c)
+ n, err := newNumber(test.text, err == nil)
+ ok := test.isInt || test.isUint || test.isFloat || test.isComplex
if ok && err != nil {
t.Errorf("unexpected error for %q", test.text)
continue
@@ -62,8 +75,8 @@ func TestNumberParse(t *testing.T) {
if !ok {
continue
}
- if n.imaginary != test.imaginary {
- t.Errorf("imaginary incorrect for %q; should be %t", test.text, test.imaginary)
+ if n.isComplex != test.isComplex {
+ t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
}
if test.isInt {
if !n.isInt {
@@ -95,17 +108,19 @@ func TestNumberParse(t *testing.T) {
} else if n.isFloat {
t.Errorf("did not expect float for %q", test.text)
}
+ if test.isComplex {
+ if !n.isComplex {
+ t.Errorf("expected complex for %q", test.text)
+ }
+ if n.complex128 != test.complex128 {
+ t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128)
+ }
+ } else if n.isComplex {
+ t.Errorf("did not expect complex for %q", test.text)
+ }
}
}
-func num(s string) *numberNode {
- n, err := newNumber(s)
- if err != nil {
- panic(err)
- }
- return n
-}
-
type parseTest struct {
name string
input string
@@ -125,29 +140,45 @@ var parseTests = []parseTest{
`[(text: " \t\n")]`},
{"text", "some text", noError,
`[(text: "some text")]`},
- {"emptyMeta", "{{}}", noError,
+ {"emptyAction", "{{}}", hasError,
`[(action: [])]`},
- {"simple command", "{{hello}}", noError,
- `[(action: [(command: [I=hello])])]`},
- {"multi-word command", "{{hello world}}", noError,
- `[(action: [(command: [I=hello I=world])])]`},
- {"multi-word command with number", "{{hello 80}}", noError,
- `[(action: [(command: [I=hello N=80])])]`},
- {"multi-word command with string", "{{hello `quoted text`}}", noError,
- "[(action: [(command: [I=hello S=`quoted text`])])]"},
- {"pipeline", "{{hello|world}}", noError,
- `[(action: [(command: [I=hello]) (command: [I=world])])]`},
- {"simple range", "{{range .x}}hello{{end}}", noError,
- `[({{range F=.x}} [(text: "hello")])]`},
- {"nested range", "{{range .x}}hello{{range .y}}goodbye{{end}}{{end}}", noError,
- `[({{range F=.x}} [(text: "hello")({{range F=.y}} [(text: "goodbye")])])]`},
- {"range with else", "{{range .x}}true{{else}}false{{end}}", noError,
- `[({{range F=.x}} [(text: "true")] {{else}} [(text: "false")])]`},
+ {"field", "{{.X}}", noError,
+ `[(action: [(command: [F=[X]])])]`},
+ {"simple command", "{{printf}}", noError,
+ `[(action: [(command: [I=printf])])]`},
+ {"multi-word command", "{{printf `%d` 23}}", noError,
+ "[(action: [(command: [I=printf S=`%d` N=23])])]"},
+ {"pipeline", "{{.X|.Y}}", noError,
+ `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
+ {"simple if", "{{if .X}}hello{{end}}", noError,
+ `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`},
+ {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
+ `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
+ {"simple range", "{{range .X}}hello{{end}}", noError,
+ `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`},
+ {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
+ `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`},
+ {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
+ `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`},
+ {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
+ `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
+ {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
+ `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
+ {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
+ `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
+ {"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError,
+ `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`},
+ {"template", "{{template `x` .Y}}", noError,
+ "[{{template S=`x` [(command: [F=[Y]])]}}]"},
+ {"with", "{{with .X}}hello{{end}}", noError,
+ `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
+ {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
+ `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
// Errors.
{"unclosed action", "hello{{range", hasError, ""},
- {"not a field", "hello{{range x}}{{end}}", hasError, ""},
{"missing end", "hello{{range .x}}", hasError, ""},
{"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
+ {"undefined function", "hello{{undefined}}", hasError, ""},
}
func TestParse(t *testing.T) {
@@ -163,7 +194,7 @@ func TestParse(t *testing.T) {
continue
case err != nil && !test.ok:
// expected error, got one
- if dumpErrors {
+ if *debug {
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
}
continue
diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go
new file mode 100644
index 000000000..492e270e1
--- /dev/null
+++ b/src/pkg/exp/template/set.go
@@ -0,0 +1,115 @@
+// 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"
+ "os"
+ "reflect"
+ "runtime"
+ "strconv"
+)
+
+// Set holds a set of related templates that can refer to one another by name.
+// A template may be a member of multiple sets.
+type Set struct {
+ tmpl map[string]*Template
+ funcs map[string]reflect.Value
+}
+
+// NewSet allocates a new, empty template set.
+func NewSet() *Set {
+ return &Set{
+ tmpl: make(map[string]*Template),
+ funcs: make(map[string]reflect.Value),
+ }
+}
+
+// Funcs adds to the set's function map the elements of the
+// argument 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 {
+ addFuncs(s.funcs, funcMap)
+ return s
+}
+
+// Add adds the argument templates to the set. It panics if the call
+// attempts to reuse a name defined in the template.
+// The return value is the set, so calls can be chained.
+func (s *Set) Add(templates ...*Template) *Set {
+ for _, t := range templates {
+ if _, ok := s.tmpl[t.name]; ok {
+ panic(fmt.Errorf("template: %q already defined in set", t.name))
+ }
+ s.tmpl[t.name] = t
+ }
+ return s
+}
+
+// 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]
+}
+
+// Execute looks for the named template in the set and then applies that
+// template to the specified data object, writing the output to wr. Nested
+// template invocations will be resolved from the set.
+func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error {
+ tmpl := s.tmpl[name]
+ if tmpl == nil {
+ return fmt.Errorf("template: no template %q in set", name)
+ }
+ return tmpl.ExecuteInSet(wr, data, s)
+}
+
+// recover is the handler that turns panics into returns from the top
+// level of Parse.
+func (s *Set) recover(errp *os.Error) {
+ e := recover()
+ if e != nil {
+ if _, ok := e.(runtime.Error); ok {
+ panic(e)
+ }
+ s.tmpl = nil
+ *errp = e.(os.Error)
+ }
+ return
+}
+
+// Parse parses the file into a set of named templates.
+func (s *Set) Parse(text string) (err os.Error) {
+ defer s.recover(&err)
+ lex := lex("set", text)
+ const context = "define clause"
+ for {
+ t := New("set") // name will be updated once we know it.
+ t.startParse(s, lex)
+ // Expect EOF or "{{ define name }}".
+ if t.atEOF() {
+ return
+ }
+ 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.typ() != nodeEnd {
+ t.errorf("unexpected %s in %s", end, context)
+ }
+ t.stopParse()
+ s.tmpl[t.name] = t
+ }
+ return nil
+}
diff --git a/src/pkg/exp/template/set_test.go b/src/pkg/exp/template/set_test.go
new file mode 100644
index 000000000..c0115ec0a
--- /dev/null
+++ b/src/pkg/exp/template/set_test.go
@@ -0,0 +1,101 @@
+// 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"
+)
+
+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 := NewSet()
+ err := 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 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 text", `{{template "text" .SI}}`, "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},
+}
+
+const setText = `
+ {{define "text"}}TEXT{{end}}
+ {{define "dotV"}}{{.V}}{{end}}
+ {{define "dot"}}{{.}}{{end}}
+ {{define "nested"}}{{template "dot" .}}{{end}}
+`
+
+func TestSetExecute(t *testing.T) {
+ // Declare a set with a couple of templates first.
+ set := NewSet()
+ err := set.Parse(setText)
+ if err != nil {
+ t.Fatalf("error parsing set: %s", err)
+ }
+ testExecute(setExecTests, set, t)
+}
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 438e0ae26..5c083e5e9 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -162,19 +162,18 @@ func (p *pp) Write(b []byte) (ret int, err os.Error) {
// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
-func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) {
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err os.Error) {
p := newPrinter()
p.doPrintf(format, a)
- n64, error := p.buf.WriteTo(w)
+ n64, err := p.buf.WriteTo(w)
p.free()
- return int(n64), error
+ return int(n64), err
}
// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
-func Printf(format string, a ...interface{}) (n int, errno os.Error) {
- n, errno = Fprintf(os.Stdout, format, a...)
- return n, errno
+func Printf(format string, a ...interface{}) (n int, err os.Error) {
+ return Fprintf(os.Stdout, format, a...)
}
// Sprintf formats according to a format specifier and returns the resulting string.
@@ -197,20 +196,19 @@ func Errorf(format string, a ...interface{}) os.Error {
// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
-func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) {
+func Fprint(w io.Writer, a ...interface{}) (n int, err os.Error) {
p := newPrinter()
p.doPrint(a, false, false)
- n64, error := p.buf.WriteTo(w)
+ n64, err := p.buf.WriteTo(w)
p.free()
- return int(n64), error
+ return int(n64), err
}
// Print formats using the default formats for its operands and writes to standard output.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
-func Print(a ...interface{}) (n int, errno os.Error) {
- n, errno = Fprint(os.Stdout, a...)
- return n, errno
+func Print(a ...interface{}) (n int, err os.Error) {
+ return Fprint(os.Stdout, a...)
}
// Sprint formats using the default formats for its operands and returns the resulting string.
@@ -230,20 +228,19 @@ func Sprint(a ...interface{}) string {
// Fprintln formats using the default formats for its operands and writes to w.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
-func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) {
+func Fprintln(w io.Writer, a ...interface{}) (n int, err os.Error) {
p := newPrinter()
p.doPrint(a, true, true)
- n64, error := p.buf.WriteTo(w)
+ n64, err := p.buf.WriteTo(w)
p.free()
- return int(n64), error
+ return int(n64), err
}
// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
-func Println(a ...interface{}) (n int, errno os.Error) {
- n, errno = Fprintln(os.Stdout, a...)
- return n, errno
+func Println(a ...interface{}) (n int, err os.Error) {
+ return Fprintln(os.Stdout, a...)
}
// Sprintln formats using the default formats for its operands and returns the resulting string.
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index f48fcbb44..d93a8c1da 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -35,6 +35,10 @@ type ScanState interface {
ReadRune() (rune int, size int, err os.Error)
// UnreadRune causes the next call to ReadRune to return the same rune.
UnreadRune() os.Error
+ // SkipSpace skips space in the input. Newlines are treated as space
+ // unless the scan operation is Scanln, Fscanln or Sscanln, in which case
+ // a newline is treated as EOF.
+ SkipSpace()
// Token skips space in the input if skipSpace is true, then returns the
// run of Unicode code points c satisfying f(c). If f is nil,
// !unicode.IsSpace(c) is used; that is, the token will hold non-space
@@ -267,6 +271,14 @@ func notSpace(r int) bool {
return !unicode.IsSpace(r)
}
+
+// skipSpace provides Scan() methods the ability to skip space and newline characters
+// in keeping with the current scanning mode set by format strings and Scan()/Scanln().
+func (s *ss) SkipSpace() {
+ s.skipSpace(false)
+}
+
+
// readRune is a structure to enable reading UTF-8 encoded code points
// from an io.Reader. It is used if the Reader given to the scanner does
// not already implement io.RuneReader.
diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go
index 0820dcfce..30b396fcf 100644
--- a/src/pkg/go/ast/print_test.go
+++ b/src/pkg/go/ast/print_test.go
@@ -53,7 +53,7 @@ var tests = []struct {
// Split s into lines, trim whitespace from all lines, and return
// the concatenated non-empty lines.
func trim(s string) string {
- lines := strings.Split(s, "\n", -1)
+ lines := strings.Split(s, "\n")
i := 0
for _, line := range lines {
line = strings.TrimSpace(line)
diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go
index 20f8f2913..e0000b534 100644
--- a/src/pkg/go/build/dir.go
+++ b/src/pkg/go/build/dir.go
@@ -139,7 +139,7 @@ func goodOSArch(filename string) bool {
if dot := strings.Index(filename, "."); dot != -1 {
filename = filename[:dot]
}
- l := strings.Split(filename, "_", -1)
+ l := strings.Split(filename, "_")
n := len(l)
if n == 0 {
return true
diff --git a/src/pkg/go/build/path.go b/src/pkg/go/build/path.go
index 8ad39fb0f..ea588abbd 100644
--- a/src/pkg/go/build/path.go
+++ b/src/pkg/go/build/path.go
@@ -88,6 +88,9 @@ func FindTree(path string) (tree *Tree, pkg string, err os.Error) {
if path, err = filepath.Abs(path); err != nil {
return
}
+ if path, err = filepath.EvalSymlinks(path); err != nil {
+ return
+ }
for _, t := range Path {
tpath := t.SrcDir() + string(filepath.Separator)
if !strings.HasPrefix(path, tpath) {
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index f1ebfa97b..85640af79 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -58,7 +58,7 @@ func CommentText(comment *ast.CommentGroup) string {
}
// Split on newlines.
- cl := strings.Split(c, "\n", -1)
+ cl := strings.Split(c, "\n")
// Walk lines, stripping trailing white space and adding to list.
for _, l := range cl {
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index a7a7e0a32..b26cd2bed 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -551,7 +551,7 @@ func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc
p := new(PackageDoc)
p.PackageName = doc.pkgName
p.ImportPath = importpath
- sort.SortStrings(filenames)
+ sort.Strings(filenames)
p.Filenames = filenames
p.Doc = CommentText(doc.doc)
// makeTypeDocs may extend the list of doc.values and
diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go
index 1de2e00ad..035a13fb7 100644
--- a/src/pkg/go/types/testdata/exports.go
+++ b/src/pkg/go/types/testdata/exports.go
@@ -38,7 +38,7 @@ type (
T9 struct {
a int
b, c float32
- d []string "tag"
+ d []string `go:"tag"`
}
T10 struct {
T8
diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go
index 415b30825..bf7cb95f2 100644
--- a/src/pkg/gob/decode.go
+++ b/src/pkg/gob/decode.go
@@ -741,7 +741,7 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
// decodeGobDecoder decodes something implementing the GobDecoder interface.
// The data is encoded as a byte slice.
-func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value, index int) {
+func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) {
// Read the bytes for the value.
b := make([]byte, state.decodeUint())
_, err := state.b.Read(b)
@@ -969,7 +969,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) {
} else {
v = reflect.ValueOf(unsafe.Unreflect(rcvrType, p))
}
- state.dec.decodeGobDecoder(state, v, methodIndex(rcvrType, gobDecodeMethodName))
+ state.dec.decodeGobDecoder(state, v)
}
return &op, int(ut.indir)
diff --git a/src/pkg/gob/doc.go b/src/pkg/gob/doc.go
index 850759bbd..aaf429c43 100644
--- a/src/pkg/gob/doc.go
+++ b/src/pkg/gob/doc.go
@@ -29,29 +29,29 @@ receiver and transmitter will do all necessary indirection and dereferencing to
convert between gobs and actual Go values. For instance, a gob type that is
schematically,
- struct { a, b int }
+ struct { A, B int }
can be sent from or received into any of these Go types:
- struct { a, b int } // the same
- *struct { a, b int } // extra indirection of the struct
- struct { *a, **b int } // extra indirection of the fields
- struct { a, b int64 } // different concrete value type; see below
+ struct { A, B int } // the same
+ *struct { A, B int } // extra indirection of the struct
+ struct { *A, **B int } // extra indirection of the fields
+ struct { A, B int64 } // different concrete value type; see below
It may also be received into any of these:
- struct { a, b int } // the same
- struct { b, a int } // ordering doesn't matter; matching is by name
- struct { a, b, c int } // extra field (c) ignored
- struct { b int } // missing field (a) ignored; data will be dropped
- struct { b, c int } // missing field (a) ignored; extra field (c) ignored.
+ struct { A, B int } // the same
+ struct { B, A int } // ordering doesn't matter; matching is by name
+ struct { A, B, C int } // extra field (C) ignored
+ struct { B int } // missing field (A) ignored; data will be dropped
+ struct { B, C int } // missing field (A) ignored; extra field (C) ignored.
Attempting to receive into these types will draw a decode error:
- struct { a int; b uint } // change of signedness for b
- struct { a int; b float } // change of type for b
+ struct { A int; B uint } // change of signedness for B
+ struct { A int; B float } // change of type for B
struct { } // no field names in common
- struct { c, d int } // no field names in common
+ struct { C, D int } // no field names in common
Integers are transmitted two ways: arbitrary precision signed integers or
arbitrary precision unsigned integers. There is no int8, int16 etc.
@@ -269,12 +269,12 @@ StructValue:
/*
For implementers and the curious, here is an encoded example. Given
- type Point struct {x, y int}
+ type Point struct {X, Y int}
and the value
p := Point{22, 33}
the bytes transmitted that encode p will be:
1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00
- 01 02 01 01 78 01 04 00 01 01 79 01 04 00 00 00
+ 01 02 01 01 58 01 04 00 01 01 59 01 04 00 00 00
07 ff 82 01 2c 01 42 00
They are determined as follows.
@@ -310,13 +310,13 @@ reserved).
02 // There are two fields in the type (len(structType.field))
01 // Start of first field structure; add 1 to get field number 0: field[0].name
01 // 1 byte
- 78 // structType.field[0].name = "x"
+ 58 // structType.field[0].name = "X"
01 // Add 1 to get field number 1: field[0].id
04 // structType.field[0].typeId is 2 (signed int).
00 // End of structType.field[0]; start structType.field[1]; set field number to -1.
01 // Add 1 to get field number 0: field[1].name
01 // 1 byte
- 79 // structType.field[1].name = "y"
+ 59 // structType.field[1].name = "Y"
01 // Add 1 to get field number 1: field[0].id
04 // struct.Type.field[1].typeId is 2 (signed int).
00 // End of structType.field[1]; end of structType.field.
diff --git a/src/pkg/gob/encode.go b/src/pkg/gob/encode.go
index 743e853e9..941e26052 100644
--- a/src/pkg/gob/encode.go
+++ b/src/pkg/gob/encode.go
@@ -468,7 +468,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
// encGobEncoder encodes a value that implements the GobEncoder interface.
// The data is sent as a byte array.
-func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int) {
+func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) {
// TODO: should we catch panics from the called method?
// We know it's a GobEncoder, so just call the method directly.
data, err := v.Interface().(GobEncoder).GobEncode()
@@ -592,17 +592,6 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp
return &op, indir
}
-// methodIndex returns which method of rt implements the method.
-func methodIndex(rt reflect.Type, method string) int {
- for i := 0; i < rt.NumMethod(); i++ {
- if rt.Method(i).Name == method {
- return i
- }
- }
- errorf("internal error: can't find method %s", method)
- return 0
-}
-
// gobEncodeOpFor returns the op for a type that is known to implement
// GobEncoder.
func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) {
@@ -624,7 +613,7 @@ func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) {
v = reflect.ValueOf(unsafe.Unreflect(rt, p))
}
state.update(i)
- state.enc.encodeGobEncoder(state.b, v, methodIndex(rt, gobEncodeMethodName))
+ state.enc.encodeGobEncoder(state.b, v)
}
return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver.
}
diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go
index f8e3843a7..552faa4d6 100644
--- a/src/pkg/gob/type.go
+++ b/src/pkg/gob/type.go
@@ -80,11 +80,6 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) {
return
}
-const (
- gobEncodeMethodName = "GobEncode"
- gobDecodeMethodName = "GobDecode"
-)
-
var (
gobEncoderInterfaceType = reflect.TypeOf(new(GobEncoder)).Elem()
gobDecoderInterfaceType = reflect.TypeOf(new(GobDecoder)).Elem()
diff --git a/src/pkg/html/parse.go b/src/pkg/html/parse.go
index 2ef90a873..6a2bc1ea6 100644
--- a/src/pkg/html/parse.go
+++ b/src/pkg/html/parse.go
@@ -400,6 +400,7 @@ func inBodyIM(p *parser) (insertionMode, bool) {
p.framesetOK = false
default:
// TODO.
+ p.addElement(p.tok.Data, p.tok.Attr)
}
case EndTagToken:
switch p.tok.Data {
@@ -413,7 +414,10 @@ func inBodyIM(p *parser) (insertionMode, bool) {
p.pop()
}
default:
- // TODO.
+ // TODO: any other end tag
+ if p.tok.Data == p.top().Data {
+ p.pop()
+ }
}
}
if endP {
diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go
index c17b436aa..c794612ab 100644
--- a/src/pkg/html/token_test.go
+++ b/src/pkg/html/token_test.go
@@ -161,7 +161,7 @@ func TestTokenizer(t *testing.T) {
loop:
for _, tt := range tokenTests {
z := NewTokenizer(bytes.NewBuffer([]byte(tt.html)))
- for i, s := range strings.Split(tt.golden, "$", -1) {
+ for i, s := range strings.Split(tt.golden, "$") {
if z.Next() == ErrorToken {
t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Error())
continue loop
diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go
index 2be3ede77..059fc758e 100644
--- a/src/pkg/http/cgi/host.go
+++ b/src/pkg/http/cgi/host.go
@@ -46,6 +46,12 @@ type Handler struct {
Path string // path to the CGI executable
Root string // root URI prefix of handler or empty for "/"
+ // Dir specifies the CGI executable's working directory.
+ // If Dir is empty, the base directory of Path is used.
+ // If Path has no base directory, the current working
+ // directory is used.
+ Dir string
+
Env []string // extra environment variables to set, if any, as "key=value"
InheritEnv []string // environment variables to inherit from host, as "key"
Logger *log.Logger // optional log for errors or nil to use log.Print
@@ -125,11 +131,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
env = append(env, h.Env...)
}
- path := os.Getenv("PATH")
- if path == "" {
- path = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
+ envPath := os.Getenv("PATH")
+ if envPath == "" {
+ envPath = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
}
- env = append(env, "PATH="+path)
+ env = append(env, "PATH="+envPath)
for _, e := range h.InheritEnv {
if v := os.Getenv(e); v != "" {
@@ -143,7 +149,13 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}
- cwd, pathBase := filepath.Split(h.Path)
+ var cwd, path string
+ if h.Dir != "" {
+ path = h.Path
+ cwd = h.Dir
+ } else {
+ cwd, path = filepath.Split(h.Path)
+ }
if cwd == "" {
cwd = "."
}
@@ -154,7 +166,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
cmd := &exec.Cmd{
- Path: pathBase,
+ Path: path,
Args: append([]string{h.Path}, h.Args...),
Dir: cwd,
Env: env,
@@ -197,7 +209,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if len(line) == 0 {
break
}
- parts := strings.Split(string(line), ":", 2)
+ parts := strings.SplitN(string(line), ":", 2)
if len(parts) < 2 {
h.printf("cgi: bogus header line: %s", string(line))
continue
diff --git a/src/pkg/http/cgi/host_test.go b/src/pkg/http/cgi/host_test.go
index bbdb715cf..b08d8bbf6 100644
--- a/src/pkg/http/cgi/host_test.go
+++ b/src/pkg/http/cgi/host_test.go
@@ -13,8 +13,10 @@ import (
"http"
"http/httptest"
"os"
+ "path/filepath"
"strings"
"testing"
+ "runtime"
)
func newRequest(httpreq string) *http.Request {
@@ -46,7 +48,7 @@ readlines:
}
linesRead++
trimmedLine := strings.TrimRight(line, "\r\n")
- split := strings.Split(trimmedLine, "=", 2)
+ split := strings.SplitN(trimmedLine, "=", 2)
if len(split) != 2 {
t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v",
len(split), linesRead, line, m)
@@ -301,3 +303,77 @@ func TestInternalRedirect(t *testing.T) {
}
runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap)
}
+
+func TestDirUnix(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+
+ cwd, _ := os.Getwd()
+ h := &Handler{
+ Path: "testdata/test.cgi",
+ Root: "/test.cgi",
+ Dir: cwd,
+ }
+ expectedMap := map[string]string{
+ "cwd": cwd,
+ }
+ runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
+
+ cwd, _ = os.Getwd()
+ cwd = filepath.Join(cwd, "testdata")
+ h = &Handler{
+ Path: "testdata/test.cgi",
+ Root: "/test.cgi",
+ }
+ expectedMap = map[string]string{
+ "cwd": cwd,
+ }
+ runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
+}
+
+func TestDirWindows(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ return
+ }
+
+ cgifile, _ := filepath.Abs("testdata/test.cgi")
+
+ var perl string
+ var err os.Error
+ perl, err = exec.LookPath("perl")
+ if err != nil {
+ return
+ }
+ perl, _ = filepath.Abs(perl)
+
+ cwd, _ := os.Getwd()
+ h := &Handler{
+ Path: perl,
+ Root: "/test.cgi",
+ Dir: cwd,
+ Args: []string{cgifile},
+ Env: []string{"SCRIPT_FILENAME=" + cgifile},
+ }
+ expectedMap := map[string]string{
+ "cwd": cwd,
+ }
+ runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
+
+ // If not specify Dir on windows, working directory should be
+ // base directory of perl.
+ cwd, _ = filepath.Split(perl)
+ if cwd != "" && cwd[len(cwd)-1] == filepath.Separator {
+ cwd = cwd[:len(cwd)-1]
+ }
+ h = &Handler{
+ Path: perl,
+ Root: "/test.cgi",
+ Args: []string{cgifile},
+ Env: []string{"SCRIPT_FILENAME=" + cgifile},
+ }
+ expectedMap = map[string]string{
+ "cwd": cwd,
+ }
+ runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
+}
diff --git a/src/pkg/http/cgi/testdata/test.cgi b/src/pkg/http/cgi/testdata/test.cgi
index a1b2ff893..36c107f76 100755
--- a/src/pkg/http/cgi/testdata/test.cgi
+++ b/src/pkg/http/cgi/testdata/test.cgi
@@ -6,9 +6,9 @@
# Test script run as a child process under cgi_test.go
use strict;
-use CGI;
+use Cwd;
-my $q = CGI->new;
+my $q = MiniCGI->new;
my $params = $q->Vars;
if ($params->{"loc"}) {
@@ -39,3 +39,50 @@ foreach my $k (sort keys %ENV) {
$clean_env =~ s/[\n\r]//g;
print "env-$k=$clean_env\n";
}
+
+# NOTE: don't call getcwd() for windows.
+# msys return /c/go/src/... not C:\go\...
+my $dir;
+if ($^O eq 'MSWin32' || $^O eq 'msys') {
+ my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
+ $cmd =~ s!\\!/!g;
+ $dir = `$cmd /c cd`;
+ chomp $dir;
+} else {
+ $dir = getcwd();
+}
+print "cwd=$dir\n";
+
+
+# A minimal version of CGI.pm, for people without the perl-modules
+# package installed. (CGI.pm used to be part of the Perl core, but
+# some distros now bundle perl-base and perl-modules separately...)
+package MiniCGI;
+
+sub new {
+ my $class = shift;
+ return bless {}, $class;
+}
+
+sub Vars {
+ my $self = shift;
+ my $pairs;
+ if ($ENV{CONTENT_LENGTH}) {
+ $pairs = do { local $/; <STDIN> };
+ } else {
+ $pairs = $ENV{QUERY_STRING};
+ }
+ my $vars = {};
+ foreach my $kv (split(/&/, $pairs)) {
+ my ($k, $v) = split(/=/, $kv, 2);
+ $vars->{_urldecode($k)} = _urldecode($v);
+ }
+ return $vars;
+}
+
+sub _urldecode {
+ my $v = shift;
+ $v =~ tr/+/ /;
+ $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+ return $v;
+}
diff --git a/src/pkg/http/chunked.go b/src/pkg/http/chunked.go
index 59121c5a2..6c23e691f 100644
--- a/src/pkg/http/chunked.go
+++ b/src/pkg/http/chunked.go
@@ -9,6 +9,7 @@ import (
"log"
"os"
"strconv"
+ "bufio"
)
// NewChunkedWriter returns a new writer that translates writes into HTTP
@@ -64,3 +65,13 @@ func (cw *chunkedWriter) Close() os.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 os.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 *bufio.Reader) io.Reader {
+ return &chunkedReader{r: r}
+}
diff --git a/src/pkg/http/cookie.go b/src/pkg/http/cookie.go
index 79c239b46..fe70431bb 100644
--- a/src/pkg/http/cookie.go
+++ b/src/pkg/http/cookie.go
@@ -41,7 +41,7 @@ type Cookie struct {
func readSetCookies(h Header) []*Cookie {
cookies := []*Cookie{}
for _, line := range h["Set-Cookie"] {
- parts := strings.Split(strings.TrimSpace(line), ";", -1)
+ parts := strings.Split(strings.TrimSpace(line), ";")
if len(parts) == 1 && parts[0] == "" {
continue
}
@@ -175,7 +175,7 @@ func readCookies(h Header, filter string) []*Cookie {
}
for _, line := range lines {
- parts := strings.Split(strings.TrimSpace(line), ";", -1)
+ parts := strings.Split(strings.TrimSpace(line), ";")
if len(parts) == 1 && parts[0] == "" {
continue
}
diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go
index 56512980c..0b830053a 100644
--- a/src/pkg/http/fs.go
+++ b/src/pkg/http/fs.go
@@ -11,6 +11,7 @@ import (
"io"
"mime"
"os"
+ "path"
"path/filepath"
"strconv"
"strings"
@@ -18,6 +19,38 @@ import (
"utf8"
)
+// A Dir implements http.FileSystem using the native file
+// system restricted to a specific directory tree.
+type Dir string
+
+func (d Dir) Open(name string) (File, os.Error) {
+ if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 {
+ return nil, os.NewError("http: invalid character in file path")
+ }
+ f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name))))
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// A FileSystem implements access to a collection of named files.
+// The elements in a file path are separated by slash ('/', U+002F)
+// characters, regardless of host operating system convention.
+type FileSystem interface {
+ Open(name string) (File, os.Error)
+}
+
+// A File is returned by a FileSystem's Open method and can be
+// served by the FileServer implementation.
+type File interface {
+ Close() os.Error
+ Stat() (*os.FileInfo, os.Error)
+ Readdir(count int) ([]os.FileInfo, os.Error)
+ Read([]byte) (int, os.Error)
+ Seek(offset int64, whence int) (int64, os.Error)
+}
+
// Heuristic: b is text if it is valid UTF-8 and doesn't
// contain any unprintable ASCII or Unicode characters.
func isText(b []byte) bool {
@@ -44,7 +77,7 @@ func isText(b []byte) bool {
return true
}
-func dirList(w ResponseWriter, f *os.File) {
+func dirList(w ResponseWriter, f File) {
fmt.Fprintf(w, "<pre>\n")
for {
dirs, err := f.Readdir(100)
@@ -63,7 +96,8 @@ func dirList(w ResponseWriter, f *os.File) {
fmt.Fprintf(w, "</pre>\n")
}
-func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
+// name is '/'-separated, not filepath.Separator.
+func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
const indexPage = "/index.html"
// redirect .../index.html to .../
@@ -72,7 +106,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
return
}
- f, err := os.Open(name)
+ f, err := fs.Open(name)
if err != nil {
// TODO expose actual error?
NotFound(w, r)
@@ -113,7 +147,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
// use contents of index.html for directory, if present
if d.IsDirectory() {
index := name + filepath.FromSlash(indexPage)
- ff, err := os.Open(index)
+ ff, err := fs.Open(index)
if err == nil {
defer ff.Close()
dd, err := ff.Stat()
@@ -188,28 +222,26 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w ResponseWriter, r *Request, name string) {
- serveFile(w, r, name, false)
+ serveFile(w, r, Dir(name), "", false)
}
type fileHandler struct {
- root string
- prefix string
+ root FileSystem
}
// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
-// It strips prefix from the incoming requests before
-// looking up the file name in the file system.
-func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} }
+//
+// To use the operating system's file system implementation,
+// use http.Dir:
+//
+// http.Handle("/", http.FileServer(http.Dir("/tmp")))
+func FileServer(root FileSystem) Handler {
+ return &fileHandler{root}
+}
func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
- path := r.URL.Path
- if !strings.HasPrefix(path, f.prefix) {
- NotFound(w, r)
- return
- }
- path = path[len(f.prefix):]
- serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true)
+ serveFile(w, r, f.root, path.Clean(r.URL.Path), true)
}
// httpRange specifies the byte range to be sent to the client.
@@ -227,7 +259,7 @@ func parseRange(s string, size int64) ([]httpRange, os.Error) {
return nil, os.NewError("invalid range")
}
var ranges []httpRange
- for _, ra := range strings.Split(s[len(b):], ",", -1) {
+ for _, ra := range strings.Split(s[len(b):], ",") {
i := strings.Index(ra, "-")
if i < 0 {
return nil, os.NewError("invalid range")
diff --git a/src/pkg/http/fs_test.go b/src/pkg/http/fs_test.go
index 554053449..dbbdf05bd 100644
--- a/src/pkg/http/fs_test.go
+++ b/src/pkg/http/fs_test.go
@@ -85,6 +85,72 @@ func TestServeFile(t *testing.T) {
}
}
+type testFileSystem struct {
+ open func(name string) (File, os.Error)
+}
+
+func (fs *testFileSystem) Open(name string) (File, os.Error) {
+ return fs.open(name)
+}
+
+func TestFileServerCleans(t *testing.T) {
+ ch := make(chan string, 1)
+ fs := FileServer(&testFileSystem{func(name string) (File, os.Error) {
+ ch <- name
+ return nil, os.ENOENT
+ }})
+ tests := []struct {
+ reqPath, openArg string
+ }{
+ {"/foo.txt", "/foo.txt"},
+ {"//foo.txt", "/foo.txt"},
+ {"/../foo.txt", "/foo.txt"},
+ }
+ req, _ := NewRequest("GET", "http://example.com", nil)
+ for n, test := range tests {
+ rec := httptest.NewRecorder()
+ req.URL.Path = test.reqPath
+ fs.ServeHTTP(rec, req)
+ if got := <-ch; got != test.openArg {
+ t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
+ }
+ }
+}
+
+func TestDirJoin(t *testing.T) {
+ wfi, err := os.Stat("/etc/hosts")
+ if err != nil {
+ t.Logf("skipping test; no /etc/hosts file")
+ return
+ }
+ test := func(d Dir, name string) {
+ f, err := d.Open(name)
+ if err != nil {
+ t.Fatalf("open of %s: %v", name, err)
+ }
+ defer f.Close()
+ gfi, err := f.Stat()
+ if err != nil {
+ t.Fatalf("stat of %s: %v", err)
+ }
+ if gfi.Ino != wfi.Ino {
+ t.Errorf("%s got different inode")
+ }
+ }
+ test(Dir("/etc/"), "/hosts")
+ test(Dir("/etc/"), "hosts")
+ test(Dir("/etc/"), "../../../../hosts")
+ test(Dir("/etc"), "/hosts")
+ test(Dir("/etc"), "hosts")
+ test(Dir("/etc"), "../../../../hosts")
+
+ // Not really directories, but since we use this trick in
+ // ServeFile, test it:
+ test(Dir("/etc/hosts"), "")
+ test(Dir("/etc/hosts"), "/")
+ test(Dir("/etc/hosts"), "../")
+}
+
func TestServeFileContentType(t *testing.T) {
const ctype = "icecream/chocolate"
override := false
diff --git a/src/pkg/http/header.go b/src/pkg/http/header.go
index 95a25a814..08b077130 100644
--- a/src/pkg/http/header.go
+++ b/src/pkg/http/header.go
@@ -56,7 +56,7 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) os.Error {
keys = append(keys, k)
}
}
- sort.SortStrings(keys)
+ sort.Strings(keys)
for _, k := range keys {
for _, v := range h[k] {
v = strings.Replace(v, "\n", " ", -1)
diff --git a/src/pkg/http/persist.go b/src/pkg/http/persist.go
index 62f9ff1b5..78bf9058f 100644
--- a/src/pkg/http/persist.go
+++ b/src/pkg/http/persist.go
@@ -24,6 +24,9 @@ var (
// to regain control over the connection. ServerConn supports pipe-lining,
// i.e. requests can be read out of sync (but in the same order) while the
// respective responses are sent.
+//
+// ServerConn is low-level and should not be needed by most applications.
+// See Server.
type ServerConn struct {
lk sync.Mutex // read-write protects the following fields
c net.Conn
@@ -211,6 +214,9 @@ func (sc *ServerConn) Write(req *Request, resp *Response) os.Error {
// connection, while respecting the HTTP keepalive logic. ClientConn
// supports hijacking the connection calling Hijack to
// regain control of the underlying net.Conn and deal with it as desired.
+//
+// ClientConn is low-level and should not be needed by most applications.
+// See Client.
type ClientConn struct {
lk sync.Mutex // read-write protects the following fields
c net.Conn
diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go
index 0b92b7942..79f8de70d 100644
--- a/src/pkg/http/readrequest_test.go
+++ b/src/pkg/http/readrequest_test.go
@@ -13,11 +13,15 @@ import (
)
type reqTest struct {
- Raw string
- Req Request
- Body string
+ Raw string
+ Req *Request
+ Body string
+ Error string
}
+var noError = ""
+var noBody = ""
+
var reqTests = []reqTest{
// Baseline test; All Request fields included for template use
{
@@ -33,7 +37,7 @@ var reqTests = []reqTest{
"Proxy-Connection: keep-alive\r\n\r\n" +
"abcdef\n???",
- Request{
+ &Request{
Method: "GET",
RawURL: "http://www.techcrunch.com/",
URL: &URL{
@@ -67,6 +71,34 @@ var reqTests = []reqTest{
},
"abcdef\n",
+
+ noError,
+ },
+
+ // GET request with no body (the normal case)
+ {
+ "GET / HTTP/1.1\r\n" +
+ "Host: foo.com\r\n\r\n",
+
+ &Request{
+ Method: "GET",
+ RawURL: "/",
+ URL: &URL{
+ Raw: "/",
+ Path: "/",
+ RawPath: "/",
+ },
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Close: false,
+ ContentLength: 0,
+ Host: "foo.com",
+ Form: Values{},
+ },
+
+ noBody,
+ noError,
},
// Tests that we don't parse a path that looks like a
@@ -75,7 +107,7 @@ var reqTests = []reqTest{
"GET //user@host/is/actually/a/path/ HTTP/1.1\r\n" +
"Host: test\r\n\r\n",
- Request{
+ &Request{
Method: "GET",
RawURL: "//user@host/is/actually/a/path/",
URL: &URL{
@@ -94,12 +126,31 @@ var reqTests = []reqTest{
ProtoMinor: 1,
Header: Header{},
Close: false,
- ContentLength: -1,
+ ContentLength: 0,
Host: "test",
Form: Values{},
},
- "",
+ noBody,
+ noError,
+ },
+
+ // Tests a bogus abs_path on the Request-Line (RFC 2616 section 5.1.2)
+ {
+ "GET ../../../../etc/passwd HTTP/1.1\r\n" +
+ "Host: test\r\n\r\n",
+ nil,
+ noBody,
+ "parse ../../../../etc/passwd: invalid URI for request",
+ },
+
+ // Tests missing URL:
+ {
+ "GET HTTP/1.1\r\n" +
+ "Host: test\r\n\r\n",
+ nil,
+ noBody,
+ "parse : empty url",
},
}
@@ -110,12 +161,14 @@ func TestReadRequest(t *testing.T) {
braw.WriteString(tt.Raw)
req, err := ReadRequest(bufio.NewReader(&braw))
if err != nil {
- t.Errorf("#%d: %s", i, err)
+ if err.String() != tt.Error {
+ t.Errorf("#%d: error %q, want error %q", i, err.String(), tt.Error)
+ }
continue
}
rbody := req.Body
req.Body = nil
- diff(t, fmt.Sprintf("#%d Request", i), req, &tt.Req)
+ diff(t, fmt.Sprintf("#%d Request", i), req, tt.Req)
var bout bytes.Buffer
if rbody != nil {
io.Copy(&bout, rbody)
diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go
index 183a35c71..2917cc1e6 100644
--- a/src/pkg/http/request.go
+++ b/src/pkg/http/request.go
@@ -428,10 +428,6 @@ type chunkedReader struct {
err os.Error
}
-func newChunkedReader(r *bufio.Reader) *chunkedReader {
- return &chunkedReader{r: r}
-}
-
func (cr *chunkedReader) beginChunk() {
// chunk-size CRLF
var line string
@@ -511,13 +507,6 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) {
req.ContentLength = int64(v.Len())
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
- default:
- req.ContentLength = -1 // chunked
- }
- if req.ContentLength == 0 {
- // To prevent chunking and disambiguate this
- // from the default ContentLength zero value.
- req.TransferEncoding = []string{"identity"}
}
}
@@ -550,7 +539,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) {
}
var f []string
- if f = strings.Split(s, " ", 3); len(f) < 3 {
+ if f = strings.SplitN(s, " ", 3); len(f) < 3 {
return nil, &badStringError{"malformed HTTP request", s}
}
req.Method, req.RawURL, req.Proto = f[0], f[1], f[2]
@@ -669,11 +658,11 @@ func ParseQuery(query string) (m Values, err os.Error) {
}
func parseQuery(m Values, query string) (err os.Error) {
- for _, kv := range strings.Split(query, "&", -1) {
+ for _, kv := range strings.Split(query, "&") {
if len(kv) == 0 {
continue
}
- kvPair := strings.Split(kv, "=", 2)
+ kvPair := strings.SplitN(kv, "=", 2)
var key, value string
var e os.Error
@@ -710,7 +699,7 @@ func (r *Request) ParseForm() (err os.Error) {
return os.NewError("missing form body")
}
ct := r.Header.Get("Content-Type")
- switch strings.Split(ct, ";", 2)[0] {
+ switch strings.SplitN(ct, ";", 2)[0] {
case "text/plain", "application/x-www-form-urlencoded", "":
const maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1))
diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go
index 43ad5252d..0052c0cfc 100644
--- a/src/pkg/http/requestwrite_test.go
+++ b/src/pkg/http/requestwrite_test.go
@@ -6,6 +6,7 @@ package http
import (
"bytes"
+ "fmt"
"io"
"io/ioutil"
"os"
@@ -15,7 +16,7 @@ import (
type reqWriteTest struct {
Req Request
- Body []byte
+ Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body
Raw string
RawProxy string
}
@@ -98,13 +99,13 @@ var reqWriteTests = []reqWriteTest{
"Host: www.google.com\r\n" +
"User-Agent: Go http package\r\n" +
"Transfer-Encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
+ chunk("abcdef") + chunk(""),
"GET http://www.google.com/search HTTP/1.1\r\n" +
"Host: www.google.com\r\n" +
"User-Agent: Go http package\r\n" +
"Transfer-Encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
+ chunk("abcdef") + chunk(""),
},
// HTTP/1.1 POST => chunked coding; body; empty trailer
{
@@ -129,14 +130,14 @@ var reqWriteTests = []reqWriteTest{
"User-Agent: Go http package\r\n" +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
+ chunk("abcdef") + chunk(""),
"POST http://www.google.com/search HTTP/1.1\r\n" +
"Host: www.google.com\r\n" +
"User-Agent: Go http package\r\n" +
"Connection: close\r\n" +
"Transfer-Encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
+ chunk("abcdef") + chunk(""),
},
// HTTP/1.1 POST with Content-Length, no chunking
@@ -224,13 +225,72 @@ var reqWriteTests = []reqWriteTest{
"User-Agent: Go http package\r\n" +
"\r\n",
},
+
+ // Request with a 0 ContentLength and a 0 byte body.
+ {
+ Request{
+ Method: "POST",
+ RawURL: "/",
+ Host: "example.com",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ ContentLength: 0, // as if unset by user
+ },
+
+ func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) },
+
+ "POST / HTTP/1.1\r\n" +
+ "Host: example.com\r\n" +
+ "User-Agent: Go http package\r\n" +
+ "\r\n",
+
+ "POST / HTTP/1.1\r\n" +
+ "Host: example.com\r\n" +
+ "User-Agent: Go http package\r\n" +
+ "\r\n",
+ },
+
+ // Request with a 0 ContentLength and a 1 byte body.
+ {
+ Request{
+ Method: "POST",
+ RawURL: "/",
+ Host: "example.com",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ ContentLength: 0, // as if unset by user
+ },
+
+ func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) },
+
+ "POST / HTTP/1.1\r\n" +
+ "Host: example.com\r\n" +
+ "User-Agent: Go http package\r\n" +
+ "Transfer-Encoding: chunked\r\n\r\n" +
+ chunk("x") + chunk(""),
+
+ "POST / HTTP/1.1\r\n" +
+ "Host: example.com\r\n" +
+ "User-Agent: Go http package\r\n" +
+ "Transfer-Encoding: chunked\r\n\r\n" +
+ chunk("x") + chunk(""),
+ },
}
func TestRequestWrite(t *testing.T) {
for i := range reqWriteTests {
tt := &reqWriteTests[i]
+
+ setBody := func() {
+ switch b := tt.Body.(type) {
+ case []byte:
+ tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
+ case func() io.ReadCloser:
+ tt.Req.Body = b()
+ }
+ }
if tt.Body != nil {
- tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body))
+ setBody()
}
if tt.Req.Header == nil {
tt.Req.Header = make(Header)
@@ -248,7 +308,7 @@ func TestRequestWrite(t *testing.T) {
}
if tt.Body != nil {
- tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body))
+ setBody()
}
var praw bytes.Buffer
err = tt.Req.WriteProxy(&praw)
@@ -280,41 +340,30 @@ func (rc *closeChecker) Close() os.Error {
func TestRequestWriteClosesBody(t *testing.T) {
rc := &closeChecker{Reader: strings.NewReader("my body")}
req, _ := NewRequest("POST", "http://foo.com/", rc)
- if g, e := req.ContentLength, int64(-1); g != e {
- t.Errorf("got req.ContentLength %d, want %d", g, e)
+ if req.ContentLength != 0 {
+ t.Errorf("got req.ContentLength %d, want 0", req.ContentLength)
}
buf := new(bytes.Buffer)
req.Write(buf)
if !rc.closed {
t.Error("body not closed after write")
}
- if g, e := buf.String(), "POST / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n7\r\nmy body\r\n0\r\n\r\n"; g != e {
- t.Errorf("write:\n got: %s\nwant: %s", g, e)
+ expected := "POST / HTTP/1.1\r\n" +
+ "Host: foo.com\r\n" +
+ "User-Agent: Go http package\r\n" +
+ "Transfer-Encoding: chunked\r\n\r\n" +
+ // TODO: currently we don't buffer before chunking, so we get a
+ // single "m" chunk before the other chunks, as this was the 1-byte
+ // read from our MultiReader where we stiched the Body back together
+ // after sniffing whether the Body was 0 bytes or not.
+ chunk("m") +
+ chunk("y body") +
+ chunk("")
+ if buf.String() != expected {
+ t.Errorf("write:\n got: %s\nwant: %s", buf.String(), expected)
}
}
-func TestZeroLengthNewRequest(t *testing.T) {
- var buf bytes.Buffer
-
- // Writing with default identity encoding
- req, _ := NewRequest("PUT", "http://foo.com/", strings.NewReader(""))
- if len(req.TransferEncoding) == 0 || req.TransferEncoding[0] != "identity" {
- t.Fatalf("got req.TransferEncoding of %v, want %v", req.TransferEncoding, []string{"identity"})
- }
- if g, e := req.ContentLength, int64(0); g != e {
- t.Errorf("got req.ContentLength %d, want %d", g, e)
- }
- req.Write(&buf)
- if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nContent-Length: 0\r\n\r\n"; g != e {
- t.Errorf("identity write:\n got: %s\nwant: %s", g, e)
- }
-
- // Overriding identity encoding and forcing chunked.
- req, _ = NewRequest("PUT", "http://foo.com/", strings.NewReader(""))
- req.TransferEncoding = nil
- buf.Reset()
- req.Write(&buf)
- if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"; g != e {
- t.Errorf("chunked write:\n got: %s\nwant: %s", g, e)
- }
+func chunk(s string) string {
+ return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
}
diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go
index 6c0c441a9..915327a69 100644
--- a/src/pkg/http/response.go
+++ b/src/pkg/http/response.go
@@ -95,7 +95,7 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err os.Error)
}
return nil, err
}
- f := strings.Split(line, " ", 3)
+ f := strings.SplitN(line, " ", 3)
if len(f) < 2 {
return nil, &badStringError{"malformed HTTP response", line}
}
diff --git a/src/pkg/http/reverseproxy_test.go b/src/pkg/http/reverseproxy_test.go
index bc0861481..b2dd24633 100644
--- a/src/pkg/http/reverseproxy_test.go
+++ b/src/pkg/http/reverseproxy_test.go
@@ -17,6 +17,9 @@ func TestReverseProxy(t *testing.T) {
const backendResponse = "I am the backend"
const backendStatus = 404
backend := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if len(r.TransferEncoding) > 0 {
+ t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding)
+ }
if r.Header.Get("X-Forwarded-For") == "" {
t.Errorf("didn't get X-Forwarded-For header")
}
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index 40de54747..55a9cbf70 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -373,11 +373,8 @@ func TestIdentityResponse(t *testing.T) {
}
}
-// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
-func TestServeHTTP10Close(t *testing.T) {
- s := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- ServeFile(w, r, "testdata/file")
- }))
+func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
+ s := httptest.NewServer(h)
defer s.Close()
conn, err := net.Dial("tcp", s.Listener.Addr().String())
@@ -386,7 +383,7 @@ func TestServeHTTP10Close(t *testing.T) {
}
defer conn.Close()
- _, err = fmt.Fprint(conn, "GET / HTTP/1.0\r\n\r\n")
+ _, err = fmt.Fprint(conn, req)
if err != nil {
t.Fatal("print error:", err)
}
@@ -414,6 +411,27 @@ func TestServeHTTP10Close(t *testing.T) {
success <- true
}
+// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
+func TestServeHTTP10Close(t *testing.T) {
+ testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ ServeFile(w, r, "testdata/file")
+ }))
+}
+
+// TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close,
+// even for HTTP/1.1 requests.
+func TestHandlersCanSetConnectionClose11(t *testing.T) {
+ testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Connection", "close")
+ }))
+}
+
+func TestHandlersCanSetConnectionClose10(t *testing.T) {
+ testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Connection", "close")
+ }))
+}
+
func TestSetsRemoteAddr(t *testing.T) {
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
fmt.Fprintf(w, "%s", r.RemoteAddr)
@@ -522,7 +540,12 @@ func TestHeadResponses(t *testing.T) {
func TestTLSServer(t *testing.T) {
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- fmt.Fprintf(w, "tls=%v", r.TLS != nil)
+ if r.TLS != nil {
+ w.Header().Set("X-TLS-Set", "true")
+ if r.TLS.HandshakeComplete {
+ w.Header().Set("X-TLS-HandshakeComplete", "true")
+ }
+ }
}))
defer ts.Close()
if !strings.HasPrefix(ts.URL, "https://") {
@@ -530,20 +553,17 @@ func TestTLSServer(t *testing.T) {
}
res, err := Get(ts.URL)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
if res == nil {
t.Fatalf("got nil Response")
}
- if res.Body == nil {
- t.Fatalf("got nil Response.Body")
- }
- body, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Error(err)
+ defer res.Body.Close()
+ if res.Header.Get("X-TLS-Set") != "true" {
+ t.Errorf("expected X-TLS-Set response header")
}
- if e, g := "tls=true", string(body); e != g {
- t.Errorf("expected body %q; got %q", e, g)
+ if res.Header.Get("X-TLS-HandshakeComplete") != "true" {
+ t.Errorf("expected X-TLS-HandshakeComplete header")
}
}
@@ -796,6 +816,30 @@ func TestNoDate(t *testing.T) {
}
}
+func TestStripPrefix(t *testing.T) {
+ h := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("X-Path", r.URL.Path)
+ })
+ ts := httptest.NewServer(StripPrefix("/foo", h))
+ defer ts.Close()
+
+ res, err := Get(ts.URL + "/foo/bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, e := res.Header.Get("X-Path"), "/bar"; g != e {
+ t.Errorf("test 1: got %s, want %s", g, e)
+ }
+
+ res, err = Get(ts.URL + "/bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, e := res.StatusCode, 404; g != e {
+ t.Errorf("test 2: got status %v, want %v", g, e)
+ }
+}
+
type errorListener struct {
errs []os.Error
}
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index 7f1b8a2bc..ab960f4f0 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -152,6 +152,7 @@ func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) {
c.buf = bufio.NewReadWriter(br, bw)
if tlsConn, ok := rwc.(*tls.Conn); ok {
+ tlsConn.Handshake()
c.tlsState = new(tls.ConnectionState)
*c.tlsState = tlsConn.ConnectionState()
}
@@ -314,6 +315,10 @@ func (w *response) WriteHeader(code int) {
w.closeAfterReply = true
}
+ if w.header.Get("Connection") == "close" {
+ w.closeAfterReply = true
+ }
+
// Cannot use Content-Length with non-identity Transfer-Encoding.
if w.chunking {
w.header.Del("Content-Length")
@@ -416,7 +421,7 @@ func errorKludge(w *response) {
msg += " would ignore this error page if this text weren't here.\n"
// Is it text? ("Content-Type" is always in the map)
- baseType := strings.Split(w.header.Get("Content-Type"), ";", 2)[0]
+ baseType := strings.SplitN(w.header.Get("Content-Type"), ";", 2)[0]
switch baseType {
case "text/html":
io.WriteString(w, "<!-- ")
@@ -591,6 +596,22 @@ func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", Sta
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
+// StripPrefix returns a handler that serves HTTP requests
+// by removing the given prefix from the request URL's Path
+// and invoking the handler h. StripPrefix handles a
+// request for a path that doesn't begin with prefix by
+// replying with an HTTP 404 not found error.
+func StripPrefix(prefix string, h Handler) Handler {
+ return HandlerFunc(func(w ResponseWriter, r *Request) {
+ if !strings.HasPrefix(r.URL.Path, prefix) {
+ NotFound(w, r)
+ return
+ }
+ r.URL.Path = r.URL.Path[len(prefix):]
+ h.ServeHTTP(w, r)
+ })
+}
+
// Redirect replies to the request with a redirect to url,
// which may be a path relative to the request path.
func Redirect(w ResponseWriter, r *Request, url string, code int) {
diff --git a/src/pkg/http/spdy/read.go b/src/pkg/http/spdy/read.go
index 8adec7bd4..c6b6ab3af 100644
--- a/src/pkg/http/spdy/read.go
+++ b/src/pkg/http/spdy/read.go
@@ -174,7 +174,7 @@ func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, os.Error)
if _, err := io.ReadFull(r, value); err != nil {
return nil, err
}
- valueList := strings.Split(string(value), "\x00", -1)
+ valueList := strings.Split(string(value), "\x00")
for _, v := range valueList {
h.Add(name, v)
}
diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go
index b54508e7a..b65d99a6f 100644
--- a/src/pkg/http/transfer.go
+++ b/src/pkg/http/transfer.go
@@ -5,6 +5,7 @@
package http
import (
+ "bytes"
"bufio"
"io"
"io/ioutil"
@@ -17,7 +18,8 @@ import (
// sanitizes them without changing the user object and provides methods for
// writing the respective header, body and trailer in wire format.
type transferWriter struct {
- Body io.ReadCloser
+ Body io.Reader
+ BodyCloser io.Closer
ResponseToHEAD bool
ContentLength int64
Close bool
@@ -33,16 +35,37 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) {
switch rr := r.(type) {
case *Request:
t.Body = rr.Body
+ t.BodyCloser = rr.Body
t.ContentLength = rr.ContentLength
t.Close = rr.Close
t.TransferEncoding = rr.TransferEncoding
t.Trailer = rr.Trailer
atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
- if t.Body != nil && t.ContentLength <= 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
- t.TransferEncoding = []string{"chunked"}
+ if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
+ if t.ContentLength == 0 {
+ // Test to see if it's actually zero or just unset.
+ var buf [1]byte
+ n, _ := io.ReadFull(t.Body, buf[:])
+ if n == 1 {
+ // Oh, guess there is data in this Body Reader after all.
+ // The ContentLength field just wasn't set.
+ // Stich the Body back together again, re-attaching our
+ // consumed byte.
+ t.ContentLength = -1
+ t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body)
+ } else {
+ // Body is actually empty.
+ t.Body = nil
+ t.BodyCloser = nil
+ }
+ }
+ if t.ContentLength < 0 {
+ t.TransferEncoding = []string{"chunked"}
+ }
}
case *Response:
t.Body = rr.Body
+ t.BodyCloser = rr.Body
t.ContentLength = rr.ContentLength
t.Close = rr.Close
t.TransferEncoding = rr.TransferEncoding
@@ -147,7 +170,7 @@ func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) {
if err != nil {
return err
}
- if err = t.Body.Close(); err != nil {
+ if err = t.BodyCloser.Close(); err != nil {
return err
}
}
@@ -195,6 +218,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
t := &transferReader{}
// Unify input
+ isResponse := false
switch rr := msg.(type) {
case *Response:
t.Header = rr.Header
@@ -203,6 +227,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
t.ProtoMajor = rr.ProtoMajor
t.ProtoMinor = rr.ProtoMinor
t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header)
+ isResponse = true
case *Request:
t.Header = rr.Header
t.ProtoMajor = rr.ProtoMajor
@@ -211,6 +236,8 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
// Responses with status code 200, responding to a GET method
t.StatusCode = 200
t.RequestMethod = "GET"
+ default:
+ panic("unexpected type")
}
// Default to HTTP/1.1
@@ -224,7 +251,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
return err
}
- t.ContentLength, err = fixLength(t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
+ t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
if err != nil {
return err
}
@@ -252,7 +279,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
// or close connection when finished, since multipart is not supported yet
switch {
case chunked(t.TransferEncoding):
- t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
+ t.Body = &body{Reader: NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
case t.ContentLength >= 0:
// TODO: limit the Content-Length. This is an easy DoS vector.
t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
@@ -265,9 +292,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) {
// Persistent connection (i.e. HTTP/1.1)
t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
}
- // TODO(petar): It may be a good idea, for extra robustness, to
- // assume ContentLength=0 for GET requests (and other special
- // cases?). This logic should be in fixLength().
}
// Unify output
@@ -310,7 +334,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Erro
return nil, nil
}
- encodings := strings.Split(raw[0], ",", -1)
+ encodings := strings.Split(raw[0], ",")
te := make([]string, 0, len(encodings))
// TODO: Even though we only support "identity" and "chunked"
// encodings, the loop below is designed with foresight. One
@@ -345,7 +369,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Erro
// Determine the expected body length, using RFC 2616 Section 4.4. This
// function is not a method, because ultimately it should be shared by
// ReadResponse and ReadRequest.
-func fixLength(status int, requestMethod string, header Header, te []string) (int64, os.Error) {
+func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, os.Error) {
// Logic based on response type or status
if noBodyExpected(requestMethod) {
@@ -376,6 +400,14 @@ func fixLength(status int, requestMethod string, header Header, te []string) (in
header.Del("Content-Length")
}
+ if !isResponse && requestMethod == "GET" {
+ // RFC 2616 doesn't explicitly permit nor forbid an
+ // entity-body on a GET request so we permit one if
+ // declared, but we default to 0 here (not -1 below)
+ // if there's no mention of a body.
+ return 0, nil
+ }
+
// Logic based on media type. The purpose of the following code is just
// to detect whether the unsupported "multipart/byteranges" is being
// used. A proper Content-Type parser is needed in the future.
@@ -418,7 +450,7 @@ func fixTrailer(header Header, te []string) (Header, os.Error) {
header.Del("Trailer")
trailer := make(Header)
- keys := strings.Split(raw, ",", -1)
+ keys := strings.Split(raw, ",")
for _, key := range keys {
key = CanonicalHeaderKey(strings.TrimSpace(key))
switch key {
diff --git a/src/pkg/http/transport.go b/src/pkg/http/transport.go
index 9ad159010..3c16c880d 100644
--- a/src/pkg/http/transport.go
+++ b/src/pkg/http/transport.go
@@ -329,7 +329,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) {
return nil, err
}
if resp.StatusCode != 200 {
- f := strings.Split(resp.Status, " ", 2)
+ f := strings.SplitN(resp.Status, " ", 2)
conn.Close()
return nil, os.NewError(f[1])
}
@@ -383,7 +383,7 @@ func useProxy(addr string) bool {
addr = addr[:strings.LastIndex(addr, ":")]
}
- for _, p := range strings.Split(no_proxy, ",", -1) {
+ for _, p := range strings.Split(no_proxy, ",") {
p = strings.ToLower(strings.TrimSpace(p))
if len(p) == 0 {
continue
diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go
index beb0b8200..e934b27c4 100644
--- a/src/pkg/http/url.go
+++ b/src/pkg/http/url.go
@@ -508,8 +508,8 @@ func (v Values) Encode() string {
// resolvePath applies special path segments from refs and applies
// them to base, per RFC 2396.
func resolvePath(basepath string, refpath string) string {
- base := strings.Split(basepath, "/", -1)
- refs := strings.Split(refpath, "/", -1)
+ base := strings.Split(basepath, "/")
+ refs := strings.Split(refpath, "/")
if len(base) == 0 {
base = []string{""}
}
diff --git a/src/pkg/image/draw/draw_test.go b/src/pkg/image/draw/draw_test.go
index 6db567231..55435cc27 100644
--- a/src/pkg/image/draw/draw_test.go
+++ b/src/pkg/image/draw/draw_test.go
@@ -154,22 +154,32 @@ var drawTests = []drawTest{
{"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}},
}
-func makeGolden(dst, src, mask image.Image, op Op) image.Image {
+func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
// Since golden is a newly allocated image, we don't have to check if the
// input source and mask images and the output golden image overlap.
b := dst.Bounds()
- sx0 := src.Bounds().Min.X - b.Min.X
- sy0 := src.Bounds().Min.Y - b.Min.Y
- var mx0, my0 int
+ sb := src.Bounds()
+ mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
if mask != nil {
- mx0 = mask.Bounds().Min.X - b.Min.X
- my0 = mask.Bounds().Min.Y - b.Min.Y
+ mb = mask.Bounds()
}
golden := image.NewRGBA(b.Max.X, b.Max.Y)
- for y := b.Min.Y; y < b.Max.Y; y++ {
- my, sy := my0+y, sy0+y
- for x := b.Min.X; x < b.Max.X; x++ {
- mx, sx := mx0+x, sx0+x
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ sy := y + sp.Y - r.Min.Y
+ my := y + mp.Y - r.Min.Y
+ for x := r.Min.X; x < r.Max.X; x++ {
+ if !(image.Point{x, y}.In(b)) {
+ continue
+ }
+ sx := x + sp.X - r.Min.X
+ if !(image.Point{sx, sy}.In(sb)) {
+ continue
+ }
+ mx := x + mp.X - r.Min.X
+ if !(image.Point{mx, my}.In(mb)) {
+ continue
+ }
+
const M = 1<<16 - 1
var dr, dg, db, da uint32
if op == Over {
@@ -189,35 +199,49 @@ func makeGolden(dst, src, mask image.Image, op Op) image.Image {
})
}
}
- golden.Rect = b
- return golden
+ return golden.SubImage(b)
}
func TestDraw(t *testing.T) {
-loop:
- for _, test := range drawTests {
- dst := hgradRed(255)
- // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
- golden := makeGolden(dst, test.src, test.mask, test.op)
- b := dst.Bounds()
- if !b.Eq(golden.Bounds()) {
- t.Errorf("draw %s: bounds %v versus %v", test.desc, dst.Bounds(), golden.Bounds())
- continue
- }
- // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
- DrawMask(dst, b, test.src, image.ZP, test.mask, image.ZP, test.op)
- // Check that the resultant pixel at (8, 8) matches what we expect
- // (the expected value can be verified by hand).
- if !eq(dst.At(8, 8), test.expected) {
- t.Errorf("draw %s: at (8, 8) %v versus %v", test.desc, dst.At(8, 8), test.expected)
- continue
- }
- // Check that the resultant dst image matches the golden output.
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- if !eq(dst.At(x, y), golden.At(x, y)) {
- t.Errorf("draw %s: at (%d, %d), %v versus golden %v", test.desc, x, y, dst.At(x, y), golden.At(x, y))
- continue loop
+ rr := []image.Rectangle{
+ image.Rect(0, 0, 0, 0),
+ image.Rect(0, 0, 16, 16),
+ image.Rect(3, 5, 12, 10),
+ image.Rect(0, 0, 9, 9),
+ image.Rect(8, 8, 16, 16),
+ image.Rect(8, 0, 9, 16),
+ image.Rect(0, 8, 16, 9),
+ image.Rect(8, 8, 9, 9),
+ image.Rect(8, 8, 8, 8),
+ }
+ for _, r := range rr {
+ loop:
+ for _, test := range drawTests {
+ dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
+ // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
+ b := dst.Bounds()
+ if !b.Eq(golden.Bounds()) {
+ t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
+ continue
+ }
+ // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
+ DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
+ if image.Pt(8, 8).In(r) {
+ // Check that the resultant pixel at (8, 8) matches what we expect
+ // (the expected value can be verified by hand).
+ if !eq(dst.At(8, 8), test.expected) {
+ t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
+ continue
+ }
+ }
+ // Check that the resultant dst image matches the golden output.
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(dst.At(x, y), golden.At(x, y)) {
+ t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
+ continue loop
+ }
}
}
}
@@ -230,19 +254,11 @@ func TestDrawOverlap(t *testing.T) {
loop:
for xoff := -2; xoff <= 2; xoff++ {
m := gradYellow(127).(*image.RGBA)
- dst := &image.RGBA{
- Pix: m.Pix,
- Stride: m.Stride,
- Rect: image.Rect(5, 5, 10, 10),
- }
- src := &image.RGBA{
- Pix: m.Pix,
- Stride: m.Stride,
- Rect: image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff),
- }
- // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
- golden := makeGolden(dst, src, nil, op)
+ dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
+ src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
b := dst.Bounds()
+ // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
if !b.Eq(golden.Bounds()) {
t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
continue
@@ -276,3 +292,63 @@ func TestNonZeroSrcPt(t *testing.T) {
t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0))
}
}
+
+func TestFill(t *testing.T) {
+ rr := []image.Rectangle{
+ image.Rect(0, 0, 0, 0),
+ image.Rect(0, 0, 40, 30),
+ image.Rect(10, 0, 40, 30),
+ image.Rect(0, 20, 40, 30),
+ image.Rect(10, 20, 40, 30),
+ image.Rect(10, 20, 15, 25),
+ image.Rect(10, 0, 35, 30),
+ image.Rect(0, 15, 40, 16),
+ image.Rect(24, 24, 25, 25),
+ image.Rect(23, 23, 26, 26),
+ image.Rect(22, 22, 27, 27),
+ image.Rect(21, 21, 28, 28),
+ image.Rect(20, 20, 29, 29),
+ }
+ for _, r := range rr {
+ m := image.NewRGBA(40, 30).SubImage(r).(*image.RGBA)
+ b := m.Bounds()
+ c := image.RGBAColor{11, 0, 0, 255}
+ src := &image.ColorImage{c}
+ check := func(desc string) {
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(c, m.At(x, y)) {
+ t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
+ return
+ }
+ }
+ }
+ }
+ // Draw 1 pixel at a time.
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
+ }
+ }
+ check("pixel")
+ // Draw 1 row at a time.
+ c = image.RGBAColor{0, 22, 0, 255}
+ src = &image.ColorImage{c}
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
+ }
+ check("row")
+ // Draw 1 column at a time.
+ c = image.RGBAColor{0, 0, 33, 255}
+ src = &image.ColorImage{c}
+ for x := b.Min.X; x < b.Max.X; x++ {
+ DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
+ }
+ check("column")
+ // Draw the whole image at once.
+ c = image.RGBAColor{44, 55, 66, 77}
+ src = &image.ColorImage{c}
+ DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
+ check("whole")
+ }
+}
diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go
index f4c38d28a..5ea302d0d 100644
--- a/src/pkg/image/image.go
+++ b/src/pkg/image/image.go
@@ -548,7 +548,7 @@ func NewGray16(w, h int) *Gray16 {
return &Gray16{pix, w, Rectangle{ZP, Point{w, h}}}
}
-// A PalettedColorModel represents a fixed palette of colors.
+// A PalettedColorModel represents a fixed palette of at most 256 colors.
type PalettedColorModel []Color
func diff(a, b uint32) uint32 {
@@ -648,7 +648,20 @@ func (p *Paletted) SubImage(r Rectangle) Image {
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Paletted) Opaque() bool {
- for _, c := range p.Palette {
+ var present [256]bool
+ base := p.Rect.Min.Y * p.Stride
+ i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
+ for _, c := range p.Pix[i0:i1] {
+ present[c] = true
+ }
+ i0 += p.Stride
+ i1 += p.Stride
+ }
+ for i, c := range p.Palette {
+ if !present[i] {
+ continue
+ }
_, _, _, a := c.RGBA()
if a != 0xffff {
return false
diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go
index f167f7f28..5469d6423 100644
--- a/src/pkg/image/image_test.go
+++ b/src/pkg/image/image_test.go
@@ -10,6 +10,7 @@ import (
type image interface {
Image
+ Opaque() bool
Set(int, int, Color)
SubImage(Rectangle) Image
}
@@ -49,6 +50,10 @@ func TestImage(t *testing.T) {
t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
continue
}
+ if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() {
+ t.Errorf("%T: at (6, 3) was not opaque", m)
+ continue
+ }
m = m.SubImage(Rect(3, 2, 9, 8)).(image)
if !Rect(3, 2, 9, 8).Eq(m.Bounds()) {
t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds())
diff --git a/src/pkg/index/suffixarray/suffixarray.go b/src/pkg/index/suffixarray/suffixarray.go
index 079b7d8ed..9d4e93217 100644
--- a/src/pkg/index/suffixarray/suffixarray.go
+++ b/src/pkg/index/suffixarray/suffixarray.go
@@ -115,7 +115,7 @@ func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) {
if len(indices) == 0 {
return
}
- sort.SortInts(indices)
+ sort.Ints(indices)
pairs := make([]int, 2*len(indices))
result = make([][]int, len(indices))
count := 0
@@ -159,7 +159,7 @@ func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) {
if len(indices) == 0 {
return
}
- sort.SortInts(indices)
+ sort.Ints(indices)
result = result[0:0]
prev := 0
for _, i := range indices {
diff --git a/src/pkg/index/suffixarray/suffixarray_test.go b/src/pkg/index/suffixarray/suffixarray_test.go
index b1499027a..385ff0e56 100644
--- a/src/pkg/index/suffixarray/suffixarray_test.go
+++ b/src/pkg/index/suffixarray/suffixarray_test.go
@@ -141,7 +141,7 @@ func testLookup(t *testing.T, tc *testCase, x *Index, s string, n int) {
// we cannot simply check that the res and exp lists are equal
// check that each result is in fact a correct match and there are no duplicates
- sort.SortInts(res)
+ sort.Ints(res)
for i, r := range res {
if r < 0 || len(tc.source) <= r {
t.Errorf("test %q, lookup %q, result %d (n = %d): index %d out of range [0, %d[", tc.name, s, i, n, r, len(tc.source))
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index 790cf94e7..b879fe5b7 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -209,8 +209,16 @@ type RuneScanner interface {
UnreadRune() os.Error
}
+// stringWriter is the interface that wraps the WriteString method.
+type stringWriter interface {
+ WriteString(s string) (n int, err os.Error)
+}
+
// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
+ if sw, ok := w.(stringWriter); ok {
+ return sw.WriteString(s)
+ }
return w.Write([]byte(s))
}
diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go
index e78b60ccb..35a06b0f9 100644
--- a/src/pkg/json/decode.go
+++ b/src/pkg/json/decode.go
@@ -482,7 +482,7 @@ func (d *decodeState) object(v reflect.Value) {
if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ {
f = st.Field(i)
- if f.Tag == key {
+ if f.Tag.Get("json") == key {
ok = true
break
}
diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go
index bf8bf10bf..9b84bc76c 100644
--- a/src/pkg/json/decode_test.go
+++ b/src/pkg/json/decode_test.go
@@ -42,8 +42,9 @@ var (
type badTag struct {
X string
- Y string "y"
- Z string "@#*%(#@"
+ Y string `json:"y"`
+ Z string `x:"@#*%(#@"`
+ W string `json:"@#$@#$"`
}
type unmarshalTest struct {
@@ -68,7 +69,7 @@ var unmarshalTests = []unmarshalTest{
{`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
// skip invalid tags
- {`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil},
+ {`{"X":"a", "y":"b", "Z":"c", "W":"d"}`, new(badTag), badTag{"a", "b", "c", "d"}, nil},
// syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
@@ -250,7 +251,7 @@ type All struct {
Float32 float32
Float64 float64
- Foo string "bar"
+ Foo string `json:"bar"`
PBool *bool
PInt *int
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
index ec0a14a6a..adc0f0f37 100644
--- a/src/pkg/json/encode.go
+++ b/src/pkg/json/encode.go
@@ -36,11 +36,13 @@ import (
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string.
//
-// Struct values encode as JSON objects. Each struct field becomes
-// a member of the object. By default the object's key name is the
-// struct field name. If the struct field has a non-empty tag consisting
-// of only Unicode letters, digits, and underscores, that tag will be used
-// as the name instead. Only exported fields will be encoded.
+// Struct values encode as JSON objects. Each exported struct field
+// becomes a member of the object. By default the object's key string
+// is the struct field name. If the struct field's tag has a "json" key with a
+// value that is a non-empty string consisting of only Unicode letters,
+// digits, and underscores, that value will be used as the object key.
+// For example, the field tag `json:"myName"` says to use "myName"
+// as the object key.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
@@ -236,8 +238,8 @@ func (e *encodeState) reflectValue(v reflect.Value) {
} else {
e.WriteByte(',')
}
- if isValidTag(f.Tag) {
- e.string(f.Tag)
+ if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) {
+ e.string(tag)
} else {
e.string(f.Name)
}
diff --git a/src/pkg/json/scanner_test.go b/src/pkg/json/scanner_test.go
index 0d4de3246..023e7c81e 100644
--- a/src/pkg/json/scanner_test.go
+++ b/src/pkg/json/scanner_test.go
@@ -252,7 +252,10 @@ func genArray(n int) []interface{} {
if f > n {
f = n
}
- x := make([]interface{}, int(f))
+ if n > 0 && f == 0 {
+ f = 1
+ }
+ x := make([]interface{}, f)
for i := range x {
x[i] = genValue(((i+1)*n)/f - (i*n)/f)
}
diff --git a/src/pkg/mail/message.go b/src/pkg/mail/message.go
index fce287bd8..e227d17d6 100644
--- a/src/pkg/mail/message.go
+++ b/src/pkg/mail/message.go
@@ -425,7 +425,7 @@ func (p *addrParser) len() int {
}
func decodeRFC2047Word(s string) (string, os.Error) {
- fields := strings.Split(s, "?", -1)
+ fields := strings.Split(s, "?")
if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
return "", os.NewError("mail: address not RFC 2047 encoded")
}
diff --git a/src/pkg/mime/mediatype.go b/src/pkg/mime/mediatype.go
index a270cb937..40c735c5b 100644
--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -32,10 +32,12 @@ func validMediaTypeOrDisposition(s string) bool {
// ParseMediaType parses a media type value and any optional
// parameters, per RFC 1521. Media types are the values in
-// Content-Type and Content-Disposition headers (RFC 2183). On
-// success, ParseMediaType returns the media type converted to
-// lowercase and trimmed of white space and a non-nil params. On
-// error, it returns an empty string and a nil params.
+// Content-Type and Content-Disposition headers (RFC 2183).
+// On success, ParseMediaType returns the media type converted
+// to lowercase and trimmed of white space. The returned params
+// is always a non-nil map. Params maps from the lowercase
+// attribute to the attribute value with its case preserved.
+// On error, it returns an empty string and a nil params.
func ParseMediaType(v string) (mediatype string, params map[string]string) {
i := strings.Index(v, ";")
if i == -1 {
@@ -132,7 +134,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) {
}
func decode2231Enc(v string) string {
- sv := strings.Split(v, "'", 3)
+ sv := strings.SplitN(v, "'", 3)
if len(sv) != 3 {
return ""
}
@@ -211,6 +213,7 @@ func consumeMediaParam(v string) (param, value, rest string) {
rest = rest[1:] // consume semicolon
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
param, rest = consumeToken(rest)
+ param = strings.ToLower(param)
if param == "" {
return "", "", v
}
diff --git a/src/pkg/mime/mediatype_test.go b/src/pkg/mime/mediatype_test.go
index 454ddd037..93264bd09 100644
--- a/src/pkg/mime/mediatype_test.go
+++ b/src/pkg/mime/mediatype_test.go
@@ -60,6 +60,7 @@ func TestConsumeMediaParam(t *testing.T) {
{" ; foo=bar", "foo", "bar", ""},
{"; foo=bar", "foo", "bar", ""},
{";foo=bar", "foo", "bar", ""},
+ {";FOO=bar", "foo", "bar", ""},
{`;foo="bar"`, "foo", "bar", ""},
{`;foo="bar"; `, "foo", "bar", "; "},
{`;foo="bar"; foo=baz`, "foo", "bar", "; foo=baz"},
@@ -127,7 +128,7 @@ func TestParseMediaType(t *testing.T) {
`URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"`,
"message/external-body",
m("access-type", "URL",
- "URL", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar")},
+ "url", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar")},
{`application/x-stuff; ` +
`title*0*=us-ascii'en'This%20is%20even%20more%20; ` +
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index 5c173f228..4711fd78b 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -24,6 +24,11 @@ import (
"regexp"
)
+// TODO(bradfitz): inline these once the compiler can inline them in
+// read-only situation (such as bytes.HasSuffix)
+var lf = []byte("\n")
+var crlf = []byte("\r\n")
+
var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)")
var emptyParams = make(map[string]string)
@@ -81,6 +86,7 @@ func NewReader(reader io.Reader, boundary string) *Reader {
return &Reader{
bufReader: bufio.NewReader(reader),
+ nl: b[:2],
nlDashBoundary: b[:len(b)-2],
dashBoundaryDash: b[2:],
dashBoundary: b[2 : len(b)-2],
@@ -180,7 +186,7 @@ type Reader struct {
currentPart *Part
partsRead int
- nlDashBoundary, dashBoundaryDash, dashBoundary []byte
+ nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
}
// NextPart returns the next part in the multipart or an error.
@@ -221,11 +227,11 @@ func (mr *Reader) NextPart() (*Part, os.Error) {
continue
}
- if bytes.Equal(line, []byte("\r\n")) {
- // Consume the "\r\n" separator between the
- // body of the previous part and the boundary
- // line we now expect will follow. (either a
- // new part or the end boundary)
+ // Consume the "\n" or "\r\n" separator between the
+ // body of the previous part and the boundary line we
+ // now expect will follow. (either a new part or the
+ // end boundary)
+ if bytes.Equal(line, mr.nl) {
expectNewPart = true
continue
}
@@ -245,13 +251,17 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
if !bytes.HasPrefix(line, mr.dashBoundary) {
return false
}
- if bytes.HasSuffix(line, []byte("\r\n")) {
- return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-2])
+ if bytes.HasSuffix(line, mr.nl) {
+ return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
}
// Violate the spec and also support newlines without the
// carriage return...
- if bytes.HasSuffix(line, []byte("\n")) {
- return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1])
+ if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
+ if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
+ mr.nl = mr.nl[1:]
+ mr.nlDashBoundary = mr.nlDashBoundary[1:]
+ return true
+ }
}
return false
}
@@ -268,5 +278,5 @@ func onlyHorizontalWhitespace(s []byte) bool {
func hasPrefixThenNewline(s, prefix []byte) bool {
return bytes.HasPrefix(s, prefix) &&
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
- len(s) == len(prefix)+2 && bytes.HasSuffix(s, []byte("\r\n")))
+ len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
}
diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go
index 8bc16bbf7..1357466ac 100644
--- a/src/pkg/mime/multipart/multipart_test.go
+++ b/src/pkg/mime/multipart/multipart_test.go
@@ -81,7 +81,7 @@ func TestNameAccessors(t *testing.T) {
var longLine = strings.Repeat("\n\n\r\r\r\n\r\000", (1<<20)/8)
-func testMultipartBody() string {
+func testMultipartBody(sep string) string {
testBody := `
This is a multi-part message. This line is ignored.
--MyBoundary
@@ -112,21 +112,26 @@ never read data
useless trailer
`
- testBody = strings.Replace(testBody, "\n", "\r\n", -1)
+ testBody = strings.Replace(testBody, "\n", sep, -1)
return strings.Replace(testBody, "[longline]", longLine, 1)
}
func TestMultipart(t *testing.T) {
- bodyReader := strings.NewReader(testMultipartBody())
- testMultipart(t, bodyReader)
+ bodyReader := strings.NewReader(testMultipartBody("\r\n"))
+ testMultipart(t, bodyReader, false)
+}
+
+func TestMultipartOnlyNewlines(t *testing.T) {
+ bodyReader := strings.NewReader(testMultipartBody("\n"))
+ testMultipart(t, bodyReader, true)
}
func TestMultipartSlowInput(t *testing.T) {
- bodyReader := strings.NewReader(testMultipartBody())
- testMultipart(t, &slowReader{bodyReader})
+ bodyReader := strings.NewReader(testMultipartBody("\r\n"))
+ testMultipart(t, &slowReader{bodyReader}, false)
}
-func testMultipart(t *testing.T, r io.Reader) {
+func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) {
reader := NewReader(r, "MyBoundary")
buf := new(bytes.Buffer)
@@ -149,8 +154,15 @@ func testMultipart(t *testing.T, r io.Reader) {
if _, err := io.Copy(buf, part); err != nil {
t.Errorf("part 1 copy: %v", err)
}
- expectEq(t, "My value\r\nThe end.",
- buf.String(), "Value of first part")
+
+ adjustNewlines := func(s string) string {
+ if onlyNewlines {
+ return strings.Replace(s, "\r\n", "\n", -1)
+ }
+ return s
+ }
+
+ expectEq(t, adjustNewlines("My value\r\nThe end."), buf.String(), "Value of first part")
// Part2
part, err = reader.NextPart()
@@ -187,7 +199,7 @@ func testMultipart(t *testing.T, r io.Reader) {
if _, err := io.Copy(buf, part); err != nil {
t.Errorf("part 3 copy: %v", err)
}
- expectEq(t, "Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n",
+ expectEq(t, adjustNewlines("Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n"),
buf.String(), "body of part 3")
// Part4
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index ead775fe6..10c67dcc4 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -6,6 +6,28 @@ package net
import "os"
+func resolveNetAddr(op, net, addr string) (a Addr, err os.Error) {
+ if addr == "" {
+ return nil, &OpError{op, net, nil, errMissingAddress}
+ }
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ a, err = ResolveTCPAddr(net, addr)
+ case "udp", "udp4", "udp6":
+ a, err = ResolveUDPAddr(net, addr)
+ case "unix", "unixgram", "unixpacket":
+ a, err = ResolveUnixAddr(net, addr)
+ case "ip", "ip4", "ip6":
+ a, err = ResolveIPAddr(net, addr)
+ default:
+ err = UnknownNetworkError(net)
+ }
+ if err != nil {
+ return nil, &OpError{op, net + " " + addr, nil, err}
+ }
+ return
+}
+
// Dial connects to the address addr on the network net.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
@@ -23,56 +45,26 @@ import "os"
// Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
func Dial(net, addr string) (c Conn, err os.Error) {
- raddr := addr
- if raddr == "" {
- return nil, &OpError{"dial", net, nil, errMissingAddress}
+ addri, err := resolveNetAddr("dial", net, addr)
+ if err != nil {
+ return nil, err
}
- switch net {
- case "tcp", "tcp4", "tcp6":
- var ra *TCPAddr
- if ra, err = ResolveTCPAddr(net, raddr); err != nil {
- goto Error
- }
- c, err := DialTCP(net, nil, ra)
- if err != nil {
- return nil, err
- }
- return c, nil
- case "udp", "udp4", "udp6":
- var ra *UDPAddr
- if ra, err = ResolveUDPAddr(net, raddr); err != nil {
- goto Error
- }
- c, err := DialUDP(net, nil, ra)
- if err != nil {
- return nil, err
- }
- return c, nil
- case "unix", "unixgram", "unixpacket":
- var ra *UnixAddr
- if ra, err = ResolveUnixAddr(net, raddr); err != nil {
- goto Error
- }
+ switch ra := addri.(type) {
+ case *TCPAddr:
+ c, err = DialTCP(net, nil, ra)
+ case *UDPAddr:
+ c, err = DialUDP(net, nil, ra)
+ case *UnixAddr:
c, err = DialUnix(net, nil, ra)
- if err != nil {
- return nil, err
- }
- return c, nil
- case "ip", "ip4", "ip6":
- var ra *IPAddr
- if ra, err = ResolveIPAddr(net, raddr); err != nil {
- goto Error
- }
- c, err := DialIP(net, nil, ra)
- if err != nil {
- return nil, err
- }
- return c, nil
-
+ case *IPAddr:
+ c, err = DialIP(net, nil, ra)
+ default:
+ err = UnknownNetworkError(net)
+ }
+ if err != nil {
+ return nil, &OpError{"dial", net + " " + addr, nil, err}
}
- err = UnknownNetworkError(net)
-Error:
- return nil, &OpError{"dial", net + " " + raddr, nil, err}
+ return
}
// Listen announces on the local network address laddr.
diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go
index ade1bb3a9..640973b13 100644
--- a/src/pkg/net/dnsmsg.go
+++ b/src/pkg/net/dnsmsg.go
@@ -93,7 +93,7 @@ const (
// DNS queries.
type dnsQuestion struct {
- Name string "domain-name" // "domain-name" specifies encoding; see packers below
+ Name string `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below
Qtype uint16
Qclass uint16
}
@@ -102,7 +102,7 @@ type dnsQuestion struct {
// There are many types of messages,
// but they all share the same header.
type dnsRR_Header struct {
- Name string "domain-name"
+ Name string `net:"domain-name"`
Rrtype uint16
Class uint16
Ttl uint32
@@ -121,7 +121,7 @@ type dnsRR interface {
type dnsRR_CNAME struct {
Hdr dnsRR_Header
- Cname string "domain-name"
+ Cname string `net:"domain-name"`
}
func (rr *dnsRR_CNAME) Header() *dnsRR_Header {
@@ -140,7 +140,7 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header {
type dnsRR_MB struct {
Hdr dnsRR_Header
- Mb string "domain-name"
+ Mb string `net:"domain-name"`
}
func (rr *dnsRR_MB) Header() *dnsRR_Header {
@@ -149,7 +149,7 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header {
type dnsRR_MG struct {
Hdr dnsRR_Header
- Mg string "domain-name"
+ Mg string `net:"domain-name"`
}
func (rr *dnsRR_MG) Header() *dnsRR_Header {
@@ -158,8 +158,8 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header {
type dnsRR_MINFO struct {
Hdr dnsRR_Header
- Rmail string "domain-name"
- Email string "domain-name"
+ Rmail string `net:"domain-name"`
+ Email string `net:"domain-name"`
}
func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
@@ -168,7 +168,7 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header {
type dnsRR_MR struct {
Hdr dnsRR_Header
- Mr string "domain-name"
+ Mr string `net:"domain-name"`
}
func (rr *dnsRR_MR) Header() *dnsRR_Header {
@@ -178,7 +178,7 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header {
type dnsRR_MX struct {
Hdr dnsRR_Header
Pref uint16
- Mx string "domain-name"
+ Mx string `net:"domain-name"`
}
func (rr *dnsRR_MX) Header() *dnsRR_Header {
@@ -187,7 +187,7 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header {
type dnsRR_NS struct {
Hdr dnsRR_Header
- Ns string "domain-name"
+ Ns string `net:"domain-name"`
}
func (rr *dnsRR_NS) Header() *dnsRR_Header {
@@ -196,7 +196,7 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header {
type dnsRR_PTR struct {
Hdr dnsRR_Header
- Ptr string "domain-name"
+ Ptr string `net:"domain-name"`
}
func (rr *dnsRR_PTR) Header() *dnsRR_Header {
@@ -205,8 +205,8 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header {
type dnsRR_SOA struct {
Hdr dnsRR_Header
- Ns string "domain-name"
- Mbox string "domain-name"
+ Ns string `net:"domain-name"`
+ Mbox string `net:"domain-name"`
Serial uint32
Refresh uint32
Retry uint32
@@ -232,7 +232,7 @@ type dnsRR_SRV struct {
Priority uint16
Weight uint16
Port uint16
- Target string "domain-name"
+ Target string `net:"domain-name"`
}
func (rr *dnsRR_SRV) Header() *dnsRR_Header {
@@ -241,7 +241,7 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header {
type dnsRR_A struct {
Hdr dnsRR_Header
- A uint32 "ipv4"
+ A uint32 `net:"ipv4"`
}
func (rr *dnsRR_A) Header() *dnsRR_Header {
@@ -250,7 +250,7 @@ func (rr *dnsRR_A) Header() *dnsRR_Header {
type dnsRR_AAAA struct {
Hdr dnsRR_Header
- AAAA [16]byte "ipv6"
+ AAAA [16]byte `net:"ipv6"`
}
func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
@@ -435,7 +435,7 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool)
default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false
- case "domain-name":
+ case `net:"domain-name"`:
off, ok = packDomainName(s, msg, off)
if !ok {
return len(msg), false
@@ -506,7 +506,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
default:
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
return len(msg), false
- case "domain-name":
+ case `net:"domain-name"`:
s, off, ok = unpackDomainName(msg, off)
if !ok {
return len(msg), false
@@ -536,9 +536,9 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
}
// Generic struct printer.
-// Doesn't care about the string tag "domain-name",
-// but does look for an "ipv4" tag on uint32 variables
-// and the "ipv6" tag on array variables,
+// Doesn't care about the string tag `net:"domain-name"`,
+// but does look for an `net:"ipv4"` tag on uint32 variables
+// and the `net:"ipv6"` tag on array variables,
// printing them as IP addresses.
func printStructValue(val reflect.Value) string {
s := "{"
@@ -553,10 +553,10 @@ func printStructValue(val reflect.Value) string {
fval := val.Field(i)
if fv := fval; fv.Kind() == reflect.Struct {
s += printStructValue(fv)
- } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" {
+ } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == `net:"ipv4"` {
i := fv.Uint()
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
- } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" {
+ } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` {
i := fv.Interface().([]byte)
s += IP(i).String()
} else {
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 9ed7801d2..41d06120a 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -29,8 +29,8 @@ func init() {
}
}
-func closesocket(s int) (errno int) {
- return syscall.Closesocket(int32(s))
+func closesocket(s syscall.Handle) (errno int) {
+ return syscall.Closesocket(s)
}
// Interface for all io operations.
@@ -88,7 +88,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte) {
// iocp and send them to the correspondent waiting client
// goroutine via channel supplied in the request.
type resultSrv struct {
- iocp int32
+ iocp syscall.Handle
}
func (s *resultSrv) Run() {
@@ -132,7 +132,7 @@ func (s *ioSrv) ProcessRemoteIO() {
case o := <-s.submchan:
o.Op().errnoc <- o.Submit()
case o := <-s.canchan:
- o.Op().errnoc <- syscall.CancelIo(uint32(o.Op().fd.sysfd))
+ o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd))
}
}
}
@@ -189,7 +189,7 @@ var onceStartServer sync.Once
func startServer() {
resultsrv = new(resultSrv)
var errno int
- resultsrv.iocp, errno = syscall.CreateIoCompletionPort(-1, 0, 0, 1)
+ resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1)
if errno != 0 {
panic("CreateIoCompletionPort failed " + syscall.Errstr(errno))
}
@@ -209,7 +209,7 @@ type netFD struct {
closing bool
// immutable until Close
- sysfd int
+ sysfd syscall.Handle
family int
proto int
net string
@@ -225,7 +225,7 @@ type netFD struct {
wio sync.Mutex
}
-func allocFD(fd, family, proto int, net string) (f *netFD) {
+func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) {
f = &netFD{
sysfd: fd,
family: family,
@@ -236,13 +236,13 @@ func allocFD(fd, family, proto int, net string) (f *netFD) {
return f
}
-func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
+func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err os.Error) {
if initErr != nil {
return nil, initErr
}
onceStartServer.Do(startServer)
// Associate our socket with resultsrv.iocp.
- if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
+ if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 {
return nil, os.Errno(e)
}
return allocFD(fd, family, proto, net), nil
@@ -280,7 +280,7 @@ func (fd *netFD) decref() {
// use the resultsrv for Close too. Sigh.
syscall.SetNonblock(fd.sysfd, false)
closesocket(fd.sysfd)
- fd.sysfd = -1
+ fd.sysfd = syscall.InvalidHandle
// no need for a finalizer anymore
runtime.SetFinalizer(fd, nil)
}
@@ -288,7 +288,7 @@ func (fd *netFD) decref() {
}
func (fd *netFD) Close() os.Error {
- if fd == nil || fd.sysfd == -1 {
+ if fd == nil || fd.sysfd == syscall.InvalidHandle {
return os.EINVAL
}
@@ -307,7 +307,7 @@ type readOp struct {
func (o *readOp) Submit() (errno int) {
var d, f uint32
- return syscall.WSARecv(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
+ return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
}
func (o *readOp) Name() string {
@@ -322,7 +322,7 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) {
defer fd.rio.Unlock()
fd.incref()
defer fd.decref()
- if fd.sysfd == -1 {
+ if fd.sysfd == syscall.InvalidHandle {
return 0, os.EINVAL
}
var o readOp
@@ -344,7 +344,7 @@ type readFromOp struct {
func (o *readFromOp) Submit() (errno int) {
var d, f uint32
l := int32(unsafe.Sizeof(o.rsa))
- return syscall.WSARecvFrom(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil)
+ return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil)
}
func (o *readFromOp) Name() string {
@@ -362,7 +362,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error)
defer fd.rio.Unlock()
fd.incref()
defer fd.decref()
- if fd.sysfd == -1 {
+ if fd.sysfd == syscall.InvalidHandle {
return 0, nil, os.EINVAL
}
var o readFromOp
@@ -380,7 +380,7 @@ type writeOp struct {
func (o *writeOp) Submit() (errno int) {
var d uint32
- return syscall.WSASend(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, &o.o, nil)
+ return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil)
}
func (o *writeOp) Name() string {
@@ -395,7 +395,7 @@ func (fd *netFD) Write(buf []byte) (n int, err os.Error) {
defer fd.wio.Unlock()
fd.incref()
defer fd.decref()
- if fd.sysfd == -1 {
+ if fd.sysfd == syscall.InvalidHandle {
return 0, os.EINVAL
}
var o writeOp
@@ -412,7 +412,7 @@ type writeToOp struct {
func (o *writeToOp) Submit() (errno int) {
var d uint32
- return syscall.WSASendto(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, o.sa, &o.o, nil)
+ return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil)
}
func (o *writeToOp) Name() string {
@@ -430,7 +430,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error)
defer fd.wio.Unlock()
fd.incref()
defer fd.decref()
- if fd.sysfd == -1 {
+ if fd.sysfd == syscall.InvalidHandle {
return 0, os.EINVAL
}
var o writeToOp
@@ -443,14 +443,14 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error)
type acceptOp struct {
anOp
- newsock int
+ newsock syscall.Handle
attrs [2]syscall.RawSockaddrAny // space for local and remote address only
}
func (o *acceptOp) Submit() (errno int) {
var d uint32
l := uint32(unsafe.Sizeof(o.attrs[0]))
- return syscall.AcceptEx(uint32(o.fd.sysfd), uint32(o.newsock),
+ return syscall.AcceptEx(o.fd.sysfd, o.newsock,
(*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o)
}
@@ -459,7 +459,7 @@ func (o *acceptOp) Name() string {
}
func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.Error) {
- if fd == nil || fd.sysfd == -1 {
+ if fd == nil || fd.sysfd == syscall.InvalidHandle {
return nil, os.EINVAL
}
fd.incref()
@@ -478,7 +478,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
// Associate our new socket with IOCP.
onceStartServer.Do(startServer)
- if _, e = syscall.CreateIoCompletionPort(int32(s), resultsrv.iocp, 0, 0); e != 0 {
+ if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 {
return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)}
}
@@ -493,7 +493,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
}
// Inherit properties of the listening socket.
- e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, fd.sysfd)
+ e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, int(fd.sysfd))
if e != 0 {
closesocket(s)
return nil, err
diff --git a/src/pkg/net/hosts_test.go b/src/pkg/net/hosts_test.go
index e5793eef2..1bd00541c 100644
--- a/src/pkg/net/hosts_test.go
+++ b/src/pkg/net/hosts_test.go
@@ -59,7 +59,7 @@ func TestLookupHost(t *testing.T) {
// duplicate addresses (a common bug due to the way
// getaddrinfo works).
addrs, _ := LookupHost("localhost")
- sort.SortStrings(addrs)
+ sort.Strings(addrs)
for i := 0; i+1 < len(addrs); i++ {
if addrs[i] == addrs[i+1] {
t.Fatalf("LookupHost(\"localhost\") = %v, has duplicate addresses", addrs)
diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go
index f54ffed70..198e4096f 100644
--- a/src/pkg/net/interface_windows.go
+++ b/src/pkg/net/interface_windows.go
@@ -42,12 +42,12 @@ func getInterfaceList() ([]syscall.InterfaceInfo, os.Error) {
if e != 0 {
return nil, os.NewSyscallError("Socket", e)
}
- defer syscall.Closesocket(int32(s))
+ defer syscall.Closesocket(s)
ii := [20]syscall.InterfaceInfo{}
ret := uint32(0)
size := uint32(unsafe.Sizeof(ii))
- e = syscall.WSAIoctl(int32(s), syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0)
+ e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0)
if e != 0 {
return nil, os.NewSyscallError("WSAIoctl", e)
}
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index 5d56520a9..e831d9afc 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -26,28 +26,26 @@ import (
// boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
var probes = []struct {
- s int
la TCPAddr
ok bool
}{
// IPv6 communication capability
- {-1, TCPAddr{IP: ParseIP("::1")}, false},
+ {TCPAddr{IP: ParseIP("::1")}, false},
// IPv6 IPv4-mapped address communication capability
- {-1, TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
+ {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
}
- var errno int
for i := range probes {
- probes[i].s, errno = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if errno != 0 {
continue
}
- defer closesocket(probes[i].s)
+ defer closesocket(s)
sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6)
if err != nil {
continue
}
- errno = syscall.Bind(probes[i].s, sa)
+ errno = syscall.Bind(s, sa)
if errno != 0 {
continue
}
diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go
index 34abc5490..3772eee24 100644
--- a/src/pkg/net/sendfile_windows.go
+++ b/src/pkg/net/sendfile_windows.go
@@ -12,12 +12,12 @@ import (
type sendfileOp struct {
anOp
- src int32 // source
+ src syscall.Handle // source
n uint32
}
func (o *sendfileOp) Submit() (errno int) {
- return syscall.TransmitFile(int32(o.fd.sysfd), o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
+ return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
}
func (o *sendfileOp) Name() string {
@@ -56,7 +56,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool)
var o sendfileOp
o.Init(c)
o.n = uint32(n)
- o.src = int32(f.Fd())
+ o.src = f.Fd()
done, err := iosrv.ExecIO(&o, 0)
if err != nil {
return 0, err, false
diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go
index eae7f3711..821716e43 100644
--- a/src/pkg/net/sock.go
+++ b/src/pkg/net/sock.go
@@ -50,8 +50,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
if ra != nil {
if err = fd.connect(ra); err != nil {
- fd.sysfd = -1
- closesocket(s)
+ fd.Close()
return nil, err
}
}
@@ -65,25 +64,25 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
return fd, nil
}
-func setsockoptInt(fd, level, opt int, value int) os.Error {
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value))
+func setsockoptInt(fd *netFD, level, opt int, value int) os.Error {
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, level, opt, value))
}
-func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
+func setsockoptNsec(fd *netFD, level, opt int, nsec int64) os.Error {
var tv = syscall.NsecToTimeval(nsec)
- return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv))
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd.sysfd, level, opt, &tv))
}
func setReadBuffer(fd *netFD, bytes int) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
+ return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
}
func setWriteBuffer(fd *netFD, bytes int) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
+ return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
}
func setReadTimeout(fd *netFD, nsec int64) os.Error {
@@ -106,7 +105,7 @@ func setTimeout(fd *netFD, nsec int64) os.Error {
func setReuseAddr(fd *netFD, reuse bool) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse))
+ return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse))
}
func bindToDevice(fd *netFD, dev string) os.Error {
@@ -117,19 +116,19 @@ func bindToDevice(fd *netFD, dev string) os.Error {
func setDontRoute(fd *netFD, dontroute bool) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute))
+ return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute))
}
func setKeepAlive(fd *netFD, keepalive bool) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))
+ return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))
}
func setNoDelay(fd *netFD, noDelay bool) os.Error {
fd.incref()
defer fd.decref()
- return setsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))
+ return setsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))
}
func setLinger(fd *netFD, sec int) os.Error {
diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go
index e17c60b98..c6dbd0465 100644
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -10,7 +10,7 @@ import (
"syscall"
)
-func setKernelSpecificSockopt(s, f int) {
+func setKernelSpecificSockopt(s syscall.Handle, f int) {
// Allow reuse of recently-used addresses and ports.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index 060cc970d..354e1e8db 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -84,5 +84,5 @@ include ../../Make.pkg
signal_unix.go: ../syscall/zerrors_$(GOOS)_$(GOARCH).go
./mkunixsignals.sh $< > $@ || rm -f $@
-signal_windows.go: ../syscall/ztypes_$(GOOS)_$(GOARCH).go
+signal_windows.go: ../syscall/ztypes_$(GOOS).go
./mkunixsignals.sh $< > $@ || rm -f $@
diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go
index a45d79be3..e6ddc4065 100644
--- a/src/pkg/os/env_windows.go
+++ b/src/pkg/os/env_windows.go
@@ -119,7 +119,7 @@ func init() {
if e != 0 {
return
}
- defer syscall.LocalFree(uint32(uintptr(unsafe.Pointer(argv))))
+ 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/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go
index 7dfcdd486..e2097700e 100644
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -35,16 +35,9 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E
if sysattr.Env == nil {
sysattr.Env = Environ()
}
- // Create array of integer (system) fds.
- intfd := make([]int, len(attr.Files))
- for i, f := range attr.Files {
- if f == nil {
- intfd[i] = -1
- } else {
- intfd[i] = f.Fd()
- }
+ for _, f := range attr.Files {
+ sysattr.Files = append(sysattr.Files, f.Fd())
}
- sysattr.Files = intfd
pid, h, e := syscall.StartProcess(name, argv, sysattr)
if iserror(e) {
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index 991099d4f..5b432d398 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -10,7 +10,7 @@ import (
)
func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
- s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE)
+ s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE)
switch s {
case syscall.WAIT_OBJECT_0:
break
@@ -20,7 +20,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
return nil, NewError("os: unexpected result from WaitForSingleObject")
}
var ec uint32
- e = syscall.GetExitCodeProcess(int32(p.handle), &ec)
+ e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec)
if e != 0 {
return nil, NewSyscallError("GetExitCodeProcess", e)
}
@@ -31,7 +31,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) {
func (p *Process) Signal(sig Signal) Error {
switch sig.(UnixSignal) {
case SIGKILL:
- e := syscall.TerminateProcess(int32(p.handle), 1)
+ e := syscall.TerminateProcess(syscall.Handle(p.handle), 1)
return NewSyscallError("TerminateProcess", e)
}
return Errno(syscall.EWINDOWS)
@@ -41,7 +41,7 @@ func (p *Process) Release() Error {
if p.handle == -1 {
return EINVAL
}
- e := syscall.CloseHandle(int32(p.handle))
+ e := syscall.CloseHandle(syscall.Handle(p.handle))
if e != 0 {
return NewSyscallError("CloseHandle", e)
}
diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go
index 0e97e0bd9..4335d45e5 100644
--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -9,36 +9,12 @@
package os
import (
- "runtime"
- "sync"
"syscall"
)
-// File represents an open file descriptor.
-type File struct {
- fd int
- name string
- dirinfo *dirInfo // nil unless directory being read
- nepipe int // number of consecutive EPIPE in Write
- l sync.Mutex // used to implement windows pread/pwrite
-}
-
-// Fd returns the integer Unix file descriptor referencing the open file.
-func (file *File) Fd() int { return file.fd }
-
// Name returns the name of the file as presented to Open.
func (file *File) Name() string { return file.name }
-// NewFile returns a new File with the given file descriptor and name.
-func NewFile(fd int, name string) *File {
- if fd < 0 {
- return nil
- }
- f := &File{fd: fd, name: name}
- runtime.SetFinalizer(f, (*File).Close)
- return f
-}
-
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
@@ -187,9 +163,7 @@ func (file *File) WriteString(s string) (ret int, err Error) {
if file == nil {
return 0, EINVAL
}
- b := syscall.StringByteSlice(s)
- b = b[0 : len(b)-1]
- return file.Write(b)
+ return file.Write([]byte(s))
}
// Mkdir creates a new directory with the specified name and permission bits.
diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index b0c42d14d..03792191e 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -9,6 +9,32 @@ import (
"syscall"
)
+// File represents an open file descriptor.
+type File struct {
+ fd int
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ nepipe int // number of consecutive EPIPE in Write
+}
+
+// Fd returns the integer Unix file descriptor referencing the open file.
+func (file *File) Fd() int {
+ if file == nil {
+ return -1
+ }
+ return file.fd
+}
+
+// NewFile returns a new File with the given file descriptor and name.
+func NewFile(fd int, name string) *File {
+ if fd < 0 {
+ return nil
+ }
+ f := &File{fd: fd, name: name}
+ runtime.SetFinalizer(f, (*File).Close)
+ return f
+}
+
// Auxiliary information if the File describes a directory
type dirInfo struct {
buf [syscall.STATMAX]byte // buffer for directory I/O
diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index f1191d61f..0791a0dc0 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// The os package provides a platform-independent interface to operating
-// system functionality. The design is Unix-like.
package os
import (
@@ -23,26 +21,6 @@ func epipecheck(file *File, e int) {
}
}
-
-// Pipe returns a connected pair of Files; reads from r return bytes written to w.
-// It returns the files and an Error, if any.
-func Pipe() (r *File, w *File, err Error) {
- var p [2]int
-
- // See ../syscall/exec.go for description of lock.
- syscall.ForkLock.RLock()
- e := syscall.Pipe(p[0:])
- if iserror(e) {
- syscall.ForkLock.RUnlock()
- return nil, nil, NewSyscallError("pipe", e)
- }
- syscall.CloseOnExec(p[0])
- syscall.CloseOnExec(p[1])
- syscall.ForkLock.RUnlock()
-
- return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
-}
-
// Stat returns a FileInfo structure describing the named file and an error, if any.
// If name names a valid symbolic link, the returned FileInfo describes
// the file pointed at by the link and has fi.FollowedSymlink set to true.
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index def9b3bf0..301c2f473 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -9,6 +9,32 @@ import (
"syscall"
)
+// File represents an open file descriptor.
+type File struct {
+ fd int
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ nepipe int // number of consecutive EPIPE in Write
+}
+
+// Fd returns the integer Unix file descriptor referencing the open file.
+func (file *File) Fd() int {
+ if file == nil {
+ return -1
+ }
+ return file.fd
+}
+
+// NewFile returns a new File with the given file descriptor and name.
+func NewFile(fd int, name string) *File {
+ if fd < 0 {
+ return nil
+ }
+ f := &File{fd: fd, name: name}
+ runtime.SetFinalizer(f, (*File).Close)
+ return f
+}
+
// Auxiliary information if the File describes a directory
type dirInfo struct {
buf []byte // buffer for directory I/O
@@ -161,3 +187,22 @@ func basename(name string) string {
return name
}
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an Error, if any.
+func Pipe() (r *File, w *File, err Error) {
+ var p [2]int
+
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e := syscall.Pipe(p[0:])
+ if iserror(e) {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+}
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 80886f6f5..70dd6e241 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -6,9 +6,37 @@ package os
import (
"runtime"
+ "sync"
"syscall"
)
+// File represents an open file descriptor.
+type File struct {
+ fd syscall.Handle
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ nepipe int // number of consecutive EPIPE in Write
+ l sync.Mutex // used to implement windows pread/pwrite
+}
+
+// Fd returns the Windows handle referencing the open file.
+func (file *File) Fd() syscall.Handle {
+ if file == nil {
+ return syscall.InvalidHandle
+ }
+ return file.fd
+}
+
+// NewFile returns a new File with the given file descriptor and name.
+func NewFile(fd syscall.Handle, name string) *File {
+ if fd < 0 {
+ return nil
+ }
+ f := &File{fd: fd, name: name}
+ runtime.SetFinalizer(f, (*File).Close)
+ return f
+}
+
// Auxiliary information if the File describes a directory
type dirInfo struct {
stat syscall.Stat_t
@@ -40,7 +68,7 @@ func openDir(name string) (file *File, err Error) {
if e != 0 {
return nil, &PathError{"open", name, Errno(e)}
}
- f := NewFile(int(r), name)
+ f := NewFile(r, name)
d.usefirststat = true
f.dirinfo = d
return f, nil
@@ -85,15 +113,15 @@ func (file *File) Close() Error {
}
var e int
if file.isdir() {
- e = syscall.FindClose(int32(file.fd))
+ e = syscall.FindClose(syscall.Handle(file.fd))
} else {
- e = syscall.CloseHandle(int32(file.fd))
+ e = syscall.CloseHandle(syscall.Handle(file.fd))
}
var err Error
if e != 0 {
err = &PathError{"close", file.name, Errno(e)}
}
- file.fd = -1 // so it can't be closed again
+ file.fd = syscall.InvalidHandle // so it can't be closed again
// no need for a finalizer anymore
runtime.SetFinalizer(file, nil)
@@ -102,7 +130,7 @@ func (file *File) Close() Error {
func (file *File) statFile(name string) (fi *FileInfo, err Error) {
var stat syscall.ByHandleFileInformation
- e := syscall.GetFileInformationByHandle(int32(file.fd), &stat)
+ e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat)
if e != 0 {
return nil, &PathError{"stat", file.name, Errno(e)}
}
@@ -156,7 +184,7 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
if di.usefirststat {
di.usefirststat = false
} else {
- e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata)
+ e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata)
if e != 0 {
if e == syscall.ERROR_NO_MORE_FILES {
break
@@ -207,7 +235,7 @@ func (f *File) pread(b []byte, off int64) (n int, err int) {
Offset: uint32(off),
}
var done uint32
- e = syscall.ReadFile(int32(f.fd), b, &done, &o)
+ e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
if e != 0 {
return 0, e
}
@@ -237,7 +265,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) {
Offset: uint32(off),
}
var done uint32
- e = syscall.WriteFile(int32(f.fd), b, &done, &o)
+ e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
if e != 0 {
return 0, e
}
@@ -268,3 +296,22 @@ func Truncate(name string, size int64) Error {
}
return nil
}
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an Error, if any.
+func Pipe() (r *File, w *File, err Error) {
+ var p [2]syscall.Handle
+
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e := syscall.Pipe(p[0:])
+ if iserror(e) {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+}
diff --git a/src/pkg/os/mkunixsignals.sh b/src/pkg/os/mkunixsignals.sh
index 6ec764cbd..4bbc43f3d 100755
--- a/src/pkg/os/mkunixsignals.sh
+++ b/src/pkg/os/mkunixsignals.sh
@@ -14,7 +14,7 @@ import (
"syscall"
)
-var _ = syscall.Syscall // in case there are zero signals
+var _ = syscall.Open // in case there are zero signals
const (
EOH
diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go
index 79f6e9d49..df57b59a3 100644
--- a/src/pkg/os/types.go
+++ b/src/pkg/os/types.go
@@ -27,7 +27,7 @@ type FileInfo struct {
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 // name of file as presented to Open.
+ Name string // base name of the file name provided in Open, Stat, etc.
FollowedSymlink bool // followed a symlink to get this information
}
diff --git a/src/pkg/patch/patch.go b/src/pkg/patch/patch.go
index d4977dc99..fcc8307e0 100644
--- a/src/pkg/patch/patch.go
+++ b/src/pkg/patch/patch.go
@@ -319,4 +319,4 @@ func hasPrefix(s []byte, t string) bool {
// splitLines returns the result of splitting s into lines.
// The \n on each line is preserved.
-func splitLines(s []byte) [][]byte { return bytes.SplitAfter(s, newline, -1) }
+func splitLines(s []byte) [][]byte { return bytes.SplitAfter(s, newline) }
diff --git a/src/pkg/path/filepath/match.go b/src/pkg/path/filepath/match.go
index 9c344309d..7fcc214c0 100644
--- a/src/pkg/path/filepath/match.go
+++ b/src/pkg/path/filepath/match.go
@@ -272,7 +272,7 @@ func glob(dir, pattern string, matches []string) (m []string, e os.Error) {
if err != nil {
return
}
- sort.SortStrings(names)
+ sort.Strings(names)
for _, n := range names {
matched, err := Match(pattern, n)
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index dcd8017ad..b181483ed 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -136,7 +136,7 @@ func SplitList(path string) []string {
if path == "" {
return []string{}
}
- return strings.Split(path, string(ListSeparator), -1)
+ return strings.Split(path, string(ListSeparator))
}
// Split splits path immediately following the final Separator,
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index 6a5dd5b00..58c4c0301 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -293,10 +293,6 @@ func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) {
}
func TestWalk(t *testing.T) {
- // TODO(brainman): enable test once Windows version is implemented.
- if runtime.GOOS == "windows" {
- return
- }
makeTree(t)
// 1) ignore error handling, expect none
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index c83a9b75f..34d74b37a 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -127,17 +127,17 @@ var typeTests = []pair{
},
{struct {
x struct {
- a int8 "hi there"
+ a int8 `reflect:"hi there"`
}
}{},
- `struct { a int8 "hi there" }`,
+ `struct { a int8 "reflect:\"hi there\"" }`,
},
{struct {
x struct {
- a int8 "hi \x00there\t\n\"\\"
+ a int8 `reflect:"hi \x00there\t\n\"\\"`
}
}{},
- `struct { a int8 "hi \x00there\t\n\"\\" }`,
+ `struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`,
},
{struct {
x struct {
@@ -423,7 +423,7 @@ func TestAll(t *testing.T) {
// make sure tag strings are not part of element type
typ = TypeOf(struct {
- d []uint32 "TAG"
+ d []uint32 `reflect:"TAG"`
}{}).Field(0).Type
testType(t, 14, typ, "[]uint32")
}
@@ -1050,6 +1050,12 @@ type Point struct {
x, y int
}
+// This will be index 0.
+func (p Point) AnotherMethod(scale int) int {
+ return -1
+}
+
+// This will be index 1.
func (p Point) Dist(scale int) int {
// println("Point.Dist", p.x, p.y, scale)
return p.x*p.x*scale + p.y*p.y*scale
@@ -1058,26 +1064,52 @@ func (p Point) Dist(scale int) int {
func TestMethod(t *testing.T) {
// Non-curried method of type.
p := Point{3, 4}
- i := TypeOf(p).Method(0).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int()
+ i := TypeOf(p).Method(1).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Type Method returned %d; want 250", i)
}
- i = TypeOf(&p).Method(0).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int()
+ m, ok := TypeOf(p).MethodByName("Dist")
+ if !ok {
+ t.Fatalf("method by name failed")
+ }
+ m.Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int()
+ if i != 250 {
+ t.Errorf("Type MethodByName returned %d; want 250", i)
+ }
+
+ i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int()
if i != 250 {
t.Errorf("Pointer Type Method returned %d; want 250", i)
}
+ m, ok = TypeOf(&p).MethodByName("Dist")
+ if !ok {
+ t.Fatalf("ptr method by name failed")
+ }
+ i = m.Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int()
+ if i != 250 {
+ t.Errorf("Pointer Type MethodByName returned %d; want 250", i)
+ }
+
// Curried method of value.
- i = ValueOf(p).Method(0).Call([]Value{ValueOf(10)})[0].Int()
+ i = ValueOf(p).Method(1).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()
+ if i != 250 {
+ t.Errorf("Value MethodByName returned %d; want 250", i)
+ }
// Curried method of pointer.
- i = ValueOf(&p).Method(0).Call([]Value{ValueOf(10)})[0].Int()
+ i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int()
if i != 250 {
- t.Errorf("Value Method returned %d; want 250", i)
+ t.Errorf("Pointer Value Method returned %d; want 250", i)
+ }
+ i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int()
+ if i != 250 {
+ t.Errorf("Pointer Value MethodByName returned %d; want 250", i)
}
// Curried method of interface value.
@@ -1094,6 +1126,10 @@ func TestMethod(t *testing.T) {
if i != 250 {
t.Errorf("Interface Method returned %d; want 250", i)
}
+ i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int()
+ if i != 250 {
+ t.Errorf("Interface MethodByName returned %d; want 250", i)
+ }
}
func TestInterfaceSet(t *testing.T) {
@@ -1508,3 +1544,23 @@ func TestVariadic(t *testing.T) {
t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world")
}
}
+
+var tagGetTests = []struct {
+ Tag StructTag
+ Key string
+ Value string
+}{
+ {`protobuf:"PB(1,2)"`, `protobuf`, `PB(1,2)`},
+ {`protobuf:"PB(1,2)"`, `foo`, ``},
+ {`protobuf:"PB(1,2)"`, `rotobuf`, ``},
+ {`protobuf:"PB(1,2)" json:"name"`, `json`, `name`},
+ {`protobuf:"PB(1,2)" json:"name"`, `protobuf`, `PB(1,2)`},
+}
+
+func TestTagGet(t *testing.T) {
+ for _, tt := range tagGetTests {
+ if v := tt.Tag.Get(tt.Key); v != tt.Value {
+ t.Errorf("StructTag(%#q).Get(%#q) = %#q, want %#q", tt.Tag, tt.Key, v, tt.Value)
+ }
+ }
+}
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 6c1ab6098..a120da732 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -47,6 +47,16 @@ type Type interface {
// method signature, without a receiver, and the Func field is nil.
Method(int) Method
+ // MethodByName returns the method with that name in the type's
+ // method set and a boolean indicating if the method was found.
+ //
+ // For a non-interface type T or *T, the returned Method's Type and Func
+ // fields describe a function whose first argument is the receiver.
+ //
+ // For an interface type, the returned Method's Type field gives the
+ // method signature, without a receiver, and the Func field is nil.
+ MethodByName(string) (Method, bool)
+
// NumMethod returns the number of methods in the type's method set.
NumMethod() int
@@ -264,7 +274,7 @@ const (
// arrayType represents a fixed array type.
type arrayType struct {
- commonType "array"
+ commonType `reflect:"array"`
elem *runtime.Type
slice *runtime.Type
len uintptr
@@ -272,14 +282,14 @@ type arrayType struct {
// chanType represents a channel type.
type chanType struct {
- commonType "chan"
+ commonType `reflect:"chan"`
elem *runtime.Type
dir uintptr
}
// funcType represents a function type.
type funcType struct {
- commonType "func"
+ commonType `reflect:"func"`
dotdotdot bool
in []*runtime.Type
out []*runtime.Type
@@ -294,26 +304,26 @@ type imethod struct {
// interfaceType represents an interface type.
type interfaceType struct {
- commonType "interface"
+ commonType `reflect:"interface"`
methods []imethod
}
// mapType represents a map type.
type mapType struct {
- commonType "map"
+ commonType `reflect:"map"`
key *runtime.Type
elem *runtime.Type
}
// ptrType represents a pointer type.
type ptrType struct {
- commonType "ptr"
+ commonType `reflect:"ptr"`
elem *runtime.Type
}
// sliceType represents a slice type.
type sliceType struct {
- commonType "slice"
+ commonType `reflect:"slice"`
elem *runtime.Type
}
@@ -328,7 +338,7 @@ type structField struct {
// structType represents a struct type.
type structType struct {
- commonType "struct"
+ commonType `reflect:"struct"`
fields []structField
}
@@ -344,6 +354,7 @@ type Method struct {
Name string
Type Type
Func Value
+ Index int
}
// High bit says whether type has
@@ -451,6 +462,7 @@ func (t *uncommonType) Method(i int) (m Method) {
m.Type = toType(p.typ)
fn := p.tfn
m.Func = valueFromIword(flag, m.Type, iword(fn))
+ m.Index = i
return
}
@@ -461,6 +473,20 @@ func (t *uncommonType) NumMethod() int {
return len(t.methods)
}
+func (t *uncommonType) MethodByName(name string) (m Method, ok bool) {
+ if t == nil {
+ return
+ }
+ var p *method
+ for i := range t.methods {
+ p = &t.methods[i]
+ if p.name != nil && *p.name == name {
+ return t.Method(i), true
+ }
+ }
+ return
+}
+
// TODO(rsc): 6g supplies these, but they are not
// as efficient as they could be: they have commonType
// as the receiver instead of *commonType.
@@ -480,6 +506,14 @@ func (t *commonType) Method(i int) (m Method) {
return t.uncommonType.Method(i)
}
+func (t *commonType) MethodByName(name string) (m Method, ok bool) {
+ if t.Kind() == Interface {
+ tt := (*interfaceType)(unsafe.Pointer(t))
+ return tt.MethodByName(name)
+ }
+ return t.uncommonType.MethodByName(name)
+}
+
func (t *commonType) PkgPath() string {
return t.uncommonType.PkgPath()
}
@@ -636,22 +670,98 @@ func (t *interfaceType) Method(i int) (m Method) {
m.PkgPath = *p.pkgPath
}
m.Type = toType(p.typ)
+ m.Index = i
return
}
// NumMethod returns the number of interface methods in the type's method set.
func (t *interfaceType) NumMethod() int { return len(t.methods) }
+// MethodByName method with the given name in the type's method set.
+func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
+ if t == nil {
+ return
+ }
+ var p *imethod
+ for i := range t.methods {
+ p = &t.methods[i]
+ if *p.name == name {
+ return t.Method(i), true
+ }
+ }
+ return
+}
+
type StructField struct {
PkgPath string // empty for uppercase Name
Name string
Type Type
- Tag string
+ Tag StructTag
Offset uintptr
Index []int
Anonymous bool
}
+// A StructTag is the tag string in a struct field.
+//
+// By convention, tag strings are a concatenation of
+// optionally space-separated key:"value" pairs.
+// Each key is a non-empty string consisting of non-control
+// characters other than space (U+0020 ' '), quote (U+0022 '"'),
+// and colon (U+003A ':'). Each value is quoted using U+0022 '"'
+// characters and Go string literal syntax.
+type StructTag string
+
+// Get returns the value associated with key in the tag string.
+// If there is no such key in the tag, Get returns the empty string.
+// If the tag does not have the conventional format, the value
+// returned by Get is unspecified,
+func (tag StructTag) Get(key string) string {
+ for tag != "" {
+ // skip leading space
+ i := 0
+ for i < len(tag) && tag[i] == ' ' {
+ i++
+ }
+ tag = tag[i:]
+ if tag == "" {
+ break
+ }
+
+ // scan to colon.
+ // a space or a quote is a syntax error
+ i = 0
+ for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
+ i++
+ }
+ if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+ break
+ }
+ name := string(tag[:i])
+ tag = tag[i+1:]
+
+ // scan quoted string to find value
+ i = 1
+ for i < len(tag) && tag[i] != '"' {
+ if tag[i] == '\\' {
+ i++
+ }
+ i++
+ }
+ if i >= len(tag) {
+ break
+ }
+ qvalue := string(tag[:i+1])
+ tag = tag[i+1:]
+
+ if key == name {
+ value, _ := strconv.Unquote(qvalue)
+ return value
+ }
+ }
+ return ""
+}
+
// Field returns the i'th struct field.
func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
@@ -673,7 +783,7 @@ func (t *structType) Field(i int) (f StructField) {
f.PkgPath = *p.pkgPath
}
if p.tag != nil {
- f.Tag = *p.tag
+ f.Tag = StructTag(*p.tag)
}
f.Offset = p.offset
f.Index = []int{i}
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index b1999aa63..bfeb3267c 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -933,7 +933,7 @@ func (v Value) Kind() Kind {
}
// Len returns v's length.
-// It panics if v's Kind is not Array, Chan, Map, or Slice.
+// 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 {
@@ -945,6 +945,8 @@ func (v Value) Len() int {
return int(maplen(iv.word))
case Slice:
return (*SliceHeader)(iv.addr).Len
+ case String:
+ return (*StringHeader)(iv.addr).Len
}
panic(&ValueError{"reflect.Value.Len", iv.kind})
}
@@ -1023,6 +1025,23 @@ func (v Value) Method(i int) Value {
return Value{v.Internal, i + 1}
}
+// MethodByName returns a function value corresponding to the method
+// of v with the given name.
+// The arguments to a Call on the returned function should not include
+// 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 {
+ panic(&ValueError{"reflect.Value.MethodByName", Invalid})
+ }
+ m, ok := iv.typ.MethodByName(name)
+ if ok {
+ return Value{v.Internal, m.Index + 1}
+ }
+ return Value{}
+}
+
// NumField returns the number of fields in the struct v.
// It panics if v's Kind is not Struct.
func (v Value) NumField() int {
diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go
index 44da8b671..e8d4c087c 100644
--- a/src/pkg/regexp/regexp.go
+++ b/src/pkg/regexp/regexp.go
@@ -158,6 +158,7 @@ func (i *instr) print() {
// Regexp is the representation of a compiled regular expression.
// The public interface is entirely through methods.
+// A Regexp is safe for concurrent use by multiple goroutines.
type Regexp struct {
expr string // the original expression
prefix string // initial plain text string
diff --git a/src/pkg/rpc/jsonrpc/all_test.go b/src/pkg/rpc/jsonrpc/all_test.go
index 02b9735eb..c1a9e8ecb 100644
--- a/src/pkg/rpc/jsonrpc/all_test.go
+++ b/src/pkg/rpc/jsonrpc/all_test.go
@@ -51,9 +51,9 @@ func init() {
func TestServer(t *testing.T) {
type addResp struct {
- Id interface{} "id"
- Result Reply "result"
- Error interface{} "error"
+ Id interface{} `json:"id"`
+ Result Reply `json:"result"`
+ Error interface{} `json:"error"`
}
cli, srv := net.Pipe()
diff --git a/src/pkg/rpc/jsonrpc/client.go b/src/pkg/rpc/jsonrpc/client.go
index 57e977d32..577d0ce42 100644
--- a/src/pkg/rpc/jsonrpc/client.go
+++ b/src/pkg/rpc/jsonrpc/client.go
@@ -44,9 +44,9 @@ func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
}
type clientRequest struct {
- Method string "method"
- Params [1]interface{} "params"
- Id uint64 "id"
+ Method string `json:"method"`
+ Params [1]interface{} `json:"params"`
+ Id uint64 `json:"id"`
}
func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error {
@@ -60,9 +60,9 @@ func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error {
}
type clientResponse struct {
- Id uint64 "id"
- Result *json.RawMessage "result"
- Error interface{} "error"
+ Id uint64 `json:"id"`
+ Result *json.RawMessage `json:"result"`
+ Error interface{} `json:"error"`
}
func (r *clientResponse) reset() {
diff --git a/src/pkg/rpc/jsonrpc/server.go b/src/pkg/rpc/jsonrpc/server.go
index 9c6b8b40d..9801fdf22 100644
--- a/src/pkg/rpc/jsonrpc/server.go
+++ b/src/pkg/rpc/jsonrpc/server.go
@@ -43,9 +43,9 @@ func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
}
type serverRequest struct {
- Method string "method"
- Params *json.RawMessage "params"
- Id *json.RawMessage "id"
+ Method string `json:"method"`
+ Params *json.RawMessage `json:"params"`
+ Id *json.RawMessage `json:"id"`
}
func (r *serverRequest) reset() {
@@ -59,9 +59,9 @@ func (r *serverRequest) reset() {
}
type serverResponse struct {
- Id *json.RawMessage "id"
- Result interface{} "result"
- Error interface{} "error"
+ Id *json.RawMessage `json:"id"`
+ Result interface{} `json:"result"`
+ Error interface{} `json:"error"`
}
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error {
diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go
index 17ba6a453..07845d128 100644
--- a/src/pkg/rpc/server.go
+++ b/src/pkg/rpc/server.go
@@ -495,7 +495,7 @@ func (server *Server) readRequest(codec ServerCodec) (req *Request, service *ser
return
}
- serviceMethod := strings.Split(req.ServiceMethod, ".", -1)
+ serviceMethod := strings.Split(req.ServiceMethod, ".")
if len(serviceMethod) != 2 {
err = os.NewError("rpc: service/method request ill-formed: " + req.ServiceMethod)
return
diff --git a/src/pkg/runtime/386/atomic.c b/src/pkg/runtime/386/atomic.c
new file mode 100644
index 000000000..c031cc4f6
--- /dev/null
+++ b/src/pkg/runtime/386/atomic.c
@@ -0,0 +1,12 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+
+#pragma textflag 7
+uint32
+runtime·atomicload(uint32 volatile* addr)
+{
+ return *addr;
+}
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index 79f847e64..03f960cb8 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -47,6 +47,7 @@ OFILES_arm=\
OFILES=\
asm.$O\
+ atomic.$O\
cgocall.$O\
chan.$O\
closure.$O\
diff --git a/src/pkg/runtime/amd64/atomic.c b/src/pkg/runtime/amd64/atomic.c
new file mode 100644
index 000000000..c031cc4f6
--- /dev/null
+++ b/src/pkg/runtime/amd64/atomic.c
@@ -0,0 +1,12 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+
+#pragma textflag 7
+uint32
+runtime·atomicload(uint32 volatile* addr)
+{
+ return *addr;
+}
diff --git a/src/pkg/runtime/arm/atomic.c b/src/pkg/runtime/arm/atomic.c
new file mode 100644
index 000000000..9fd47bae7
--- /dev/null
+++ b/src/pkg/runtime/arm/atomic.c
@@ -0,0 +1,12 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+
+#pragma textflag 7
+uint32
+runtime·atomicload(uint32 volatile* addr)
+{
+ return runtime·xadd(addr, 0);
+}
diff --git a/src/pkg/runtime/cgo/darwin_386.c b/src/pkg/runtime/cgo/darwin_386.c
index 13184f321..6d4e259be 100644
--- a/src/pkg/runtime/cgo/darwin_386.c
+++ b/src/pkg/runtime/cgo/darwin_386.c
@@ -113,11 +113,16 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/darwin_amd64.c b/src/pkg/runtime/cgo/darwin_amd64.c
index 38cd80a6f..3471044c0 100644
--- a/src/pkg/runtime/cgo/darwin_amd64.c
+++ b/src/pkg/runtime/cgo/darwin_amd64.c
@@ -83,11 +83,16 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/freebsd_386.c b/src/pkg/runtime/cgo/freebsd_386.c
index d08e1dee8..ae53201b4 100644
--- a/src/pkg/runtime/cgo/freebsd_386.c
+++ b/src/pkg/runtime/cgo/freebsd_386.c
@@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/freebsd_amd64.c b/src/pkg/runtime/cgo/freebsd_amd64.c
index fe6ce391f..5afc1dfea 100644
--- a/src/pkg/runtime/cgo/freebsd_amd64.c
+++ b/src/pkg/runtime/cgo/freebsd_amd64.c
@@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/linux_386.c b/src/pkg/runtime/cgo/linux_386.c
index 00322d4b7..e9df5ffdc 100644
--- a/src/pkg/runtime/cgo/linux_386.c
+++ b/src/pkg/runtime/cgo/linux_386.c
@@ -21,6 +21,7 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
// Not sure why the memset is necessary here,
// but without it, we get a bogus stack size
@@ -30,7 +31,11 @@ libcgo_sys_thread_start(ThreadStart *ts)
size = 0;
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/linux_amd64.c b/src/pkg/runtime/cgo/linux_amd64.c
index e77c5ddfe..d9b8b3706 100644
--- a/src/pkg/runtime/cgo/linux_amd64.c
+++ b/src/pkg/runtime/cgo/linux_amd64.c
@@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_t attr;
pthread_t p;
size_t size;
+ int err;
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
- pthread_create(&p, &attr, threadentry, ts);
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
}
static void*
diff --git a/src/pkg/runtime/cgo/windows_amd64.c b/src/pkg/runtime/cgo/windows_amd64.c
index dafe8cd9d..fd5b397ab 100755
--- a/src/pkg/runtime/cgo/windows_amd64.c
+++ b/src/pkg/runtime/cgo/windows_amd64.c
@@ -37,11 +37,21 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
- * change to actual guard pointer.
- */
+ * libcgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
- crosscall_386(ts.fn);
+ /*
+ * Set specific keys in thread local storage.
+ */
+ asm volatile (
+ "movq %%gs:0x58, %%rax\n" // MOVQ 0x58(GS), tmp
+ "movq %0, 0(%%rax)\n" // MOVQ g, 0(GS)
+ "movq %1, 8(%%rax)\n" // MOVQ m, 8(GS)
+ :: "r"(ts.g), "r"(ts.m) : "%rax"
+ );
+
+ crosscall_amd64(ts.fn);
return nil;
}
diff --git a/src/pkg/runtime/debug/stack.go b/src/pkg/runtime/debug/stack.go
index e5fae632b..a533a5c3b 100644
--- a/src/pkg/runtime/debug/stack.go
+++ b/src/pkg/runtime/debug/stack.go
@@ -52,7 +52,7 @@ func stack() []byte {
if err != nil {
continue
}
- lines = bytes.Split(data, []byte{'\n'}, -1)
+ lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
diff --git a/src/pkg/runtime/debug/stack_test.go b/src/pkg/runtime/debug/stack_test.go
index f4bdc4624..4aeea13ff 100644
--- a/src/pkg/runtime/debug/stack_test.go
+++ b/src/pkg/runtime/debug/stack_test.go
@@ -35,7 +35,7 @@ func (t T) method() []byte {
*/
func TestStack(t *testing.T) {
b := T(0).method()
- lines := strings.Split(string(b), "\n", -1)
+ lines := strings.Split(string(b), "\n")
if len(lines) <= 6 {
t.Fatal("too few lines")
}
diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh
index 00b5b3c89..328e2d5ba 100755
--- a/src/pkg/runtime/mkasmh.sh
+++ b/src/pkg/runtime/mkasmh.sh
@@ -61,14 +61,23 @@ case "$GOARCH" in
esac
;;
amd64)
- # The offsets 0 and 8 are known to:
- # ../../cmd/6l/pass.c:/D_GS
- # ../../libcgo/linux_amd64.c:/^threadentry
- # ../../libcgo/darwin_amd64.c:/^threadentry
- #
- echo '#define get_tls(r)'
- echo '#define g(r) 0(GS)'
- echo '#define m(r) 8(GS)'
+ case "$GOOS" in
+ windows)
+ echo '#define get_tls(r) MOVQ 0x58(GS), r'
+ echo '#define g(r) 0(r)'
+ echo '#define m(r) 8(r)'
+ ;;
+ *)
+ # The offsets 0 and 8 are known to:
+ # ../../cmd/6l/pass.c:/D_GS
+ # ../../libcgo/linux_amd64.c:/^threadentry
+ # ../../libcgo/darwin_amd64.c:/^threadentry
+ #
+ echo '#define get_tls(r)'
+ echo '#define g(r) 0(GS)'
+ echo '#define m(r) 8(GS)'
+ ;;
+ esac
;;
arm)
echo '#define g R10'
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index c5af8b754..a8f3a796a 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -77,7 +77,7 @@ struct Sched {
};
Sched runtime·sched;
-int32 gomaxprocs;
+int32 runtime·gomaxprocs;
// An m that is waiting for notewakeup(&m->havenextg). This may be
// only be accessed while the scheduler lock is held. This is used to
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index f3ccff1bc..ad5da0a96 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -242,6 +242,11 @@ struct M
uint32 fflag; // floating point compare flags
#ifdef __WINDOWS__
void* sehframe;
+
+#ifdef _64BIT
+ void* gostack;
+#endif
+
#endif
};
@@ -416,7 +421,10 @@ int32 runtime·write(int32, void*, int32);
int32 runtime·mincore(void*, uintptr, byte*);
bool runtime·cas(uint32*, uint32, uint32);
bool runtime·casp(void**, void*, void*);
+// Don't confuse with XADD x86 instruction,
+// this one is actually 'addx', that is, add-and-fetch.
uint32 runtime·xadd(uint32 volatile*, int32);
+uint32 runtime·atomicload(uint32 volatile*);
void runtime·jmpdefer(byte*, void*);
void runtime·exit1(int32);
void runtime·ready(G*);
diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc
index 1c77e87a5..ae84351ed 100644
--- a/src/pkg/runtime/sema.goc
+++ b/src/pkg/runtime/sema.goc
@@ -23,104 +23,68 @@ package runtime
typedef struct Sema Sema;
struct Sema
{
- uint32 *addr;
+ uint32 volatile *addr;
G *g;
Sema *prev;
Sema *next;
};
-// TODO: For now, a linked list; maybe a hash table of linked lists later.
-static Sema *semfirst, *semlast;
-static Lock semlock;
+typedef struct SemaRoot SemaRoot;
+struct SemaRoot
+{
+ Lock;
+ Sema *head;
+ Sema *tail;
+ // Number of waiters. Read w/o the lock.
+ uint32 volatile nwait;
+};
+
+// Prime to not correlate with any user patterns.
+#define SEMTABLESZ 251
+
+static union
+{
+ SemaRoot;
+ // Modern processors tend to have 64-byte cache lines,
+ // potentially with 128-byte effective cache line size for reading.
+ // While there are hypothetical architectures
+ // with 16-4096 byte cache lines, 128 looks like a good compromise.
+ uint8 pad[128];
+} semtable[SEMTABLESZ];
+
+static SemaRoot*
+semroot(uint32 *addr)
+{
+ return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];
+}
static void
-semqueue(uint32 *addr, Sema *s)
+semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s)
{
+ s->g = g;
s->addr = addr;
- s->g = nil;
-
- runtime·lock(&semlock);
- s->prev = semlast;
s->next = nil;
- if(semlast)
- semlast->next = s;
+ s->prev = root->tail;
+ if(root->tail)
+ root->tail->next = s;
else
- semfirst = s;
- semlast = s;
- runtime·unlock(&semlock);
+ root->head = s;
+ root->tail = s;
}
static void
-semdequeue(Sema *s)
+semdequeue(SemaRoot *root, Sema *s)
{
- runtime·lock(&semlock);
if(s->next)
s->next->prev = s->prev;
else
- semlast = s->prev;
+ root->tail = s->prev;
if(s->prev)
s->prev->next = s->next;
else
- semfirst = s->next;
+ root->head = s->next;
s->prev = nil;
s->next = nil;
- runtime·unlock(&semlock);
-}
-
-static void
-semwakeup(uint32 *addr)
-{
- Sema *s;
-
- runtime·lock(&semlock);
- for(s=semfirst; s; s=s->next) {
- if(s->addr == addr && s->g) {
- runtime·ready(s->g);
- s->g = nil;
- break;
- }
- }
- runtime·unlock(&semlock);
-}
-
-// Step 1 of sleep: make ourselves available for wakeup.
-// TODO(rsc): Maybe we can write a version without
-// locks by using cas on s->g. Maybe not: I need to
-// think more about whether it would be correct.
-static void
-semsleep1(Sema *s)
-{
- runtime·lock(&semlock);
- s->g = g;
- runtime·unlock(&semlock);
-}
-
-// Decided not to go through with it: undo step 1.
-static void
-semsleepundo1(Sema *s)
-{
- runtime·lock(&semlock);
- if(s->g != nil) {
- s->g = nil; // back ourselves out
- } else {
- // If s->g == nil already, semwakeup
- // already readied us. Since we never stopped
- // running, readying us just set g->readyonstop.
- // Clear it.
- if(g->readyonstop == 0)
- *(int32*)0x555 = 555;
- g->readyonstop = 0;
- }
- runtime·unlock(&semlock);
-}
-
-// Step 2: wait for the wakeup.
-static void
-semsleep2(Sema *s)
-{
- USED(s);
- g->status = Gwaiting;
- runtime·gosched();
}
static int32
@@ -128,52 +92,83 @@ cansemacquire(uint32 *addr)
{
uint32 v;
- while((v = *addr) > 0)
+ while((v = runtime·atomicload(addr)) > 0)
if(runtime·cas(addr, v, v-1))
return 1;
return 0;
}
-// For now has no return value.
-// Might return an ok (not interrupted) bool in the future?
void
-runtime·semacquire(uint32 *addr)
+runtime·semacquire(uint32 volatile *addr)
{
Sema s;
+ SemaRoot *root;
// Easy case.
if(cansemacquire(addr))
return;
// Harder case:
- // queue
- // try semacquire one more time, sleep if failed
- // dequeue
- // wake up one more guy to avoid races (TODO(rsc): maybe unnecessary?)
- semqueue(addr, &s);
+ // increment waiter count
+ // try cansemacquire one more time, return if succeeded
+ // enqueue itself as a waiter
+ // sleep
+ // (waiter descriptor is dequeued by signaler)
+ root = semroot(addr);
for(;;) {
- semsleep1(&s);
+ runtime·lock(root);
+ // Add ourselves to nwait to disable "easy case" in semrelease.
+ runtime·xadd(&root->nwait, 1);
+ // Check cansemacquire to avoid missed wakeup.
if(cansemacquire(addr)) {
- semsleepundo1(&s);
- break;
+ runtime·xadd(&root->nwait, -1);
+ runtime·unlock(root);
+ return;
}
- semsleep2(&s);
+ // Any semrelease after the cansemacquire knows we're waiting
+ // (we set nwait above), so go to sleep.
+ semqueue(root, addr, &s);
+ g->status = Gwaiting;
+ runtime·unlock(root);
+ runtime·gosched();
+ if(cansemacquire(addr))
+ return;
}
- semdequeue(&s);
- semwakeup(addr);
}
void
-runtime·semrelease(uint32 *addr)
+runtime·semrelease(uint32 volatile *addr)
{
- uint32 v;
+ Sema *s;
+ SemaRoot *root;
- for(;;) {
- v = *addr;
- if(runtime·cas(addr, v, v+1))
+ root = semroot(addr);
+ runtime·xadd(addr, 1);
+
+ // Easy case: no waiters?
+ // This check must happen after the xadd, to avoid a missed wakeup
+ // (see loop in semacquire).
+ if(runtime·atomicload(&root->nwait) == 0)
+ return;
+
+ // Harder case: search for a waiter and wake it.
+ runtime·lock(root);
+ if(runtime·atomicload(&root->nwait) == 0) {
+ // The count is already consumed by another goroutine,
+ // so no need to wake up another goroutine.
+ runtime·unlock(root);
+ return;
+ }
+ for(s = root->head; s; s = s->next) {
+ if(s->addr == addr) {
+ runtime·xadd(&root->nwait, -1);
+ semdequeue(root, s);
break;
+ }
}
- semwakeup(addr);
+ runtime·unlock(root);
+ if(s)
+ runtime·ready(s->g);
}
func Semacquire(addr *uint32) {
diff --git a/src/pkg/runtime/sema_test.go b/src/pkg/runtime/sema_test.go
new file mode 100644
index 000000000..d95bb1ec5
--- /dev/null
+++ b/src/pkg/runtime/sema_test.go
@@ -0,0 +1,100 @@
+// 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 runtime_test
+
+import (
+ "runtime"
+ "sync/atomic"
+ "testing"
+)
+
+func BenchmarkSemaUncontended(b *testing.B) {
+ type PaddedSem struct {
+ sem uint32
+ pad [32]uint32
+ }
+ const CallsPerSched = 1000
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ for p := 0; p < procs; p++ {
+ go func() {
+ sem := new(PaddedSem)
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ runtime.Semrelease(&sem.sem)
+ runtime.Semacquire(&sem.sem)
+ }
+ }
+ c <- true
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func benchmarkSema(b *testing.B, block, work bool) {
+ const CallsPerSched = 1000
+ const LocalWork = 100
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ c2 := make(chan bool, procs/2)
+ sem := uint32(0)
+ if block {
+ for p := 0; p < procs/2; p++ {
+ go func() {
+ runtime.Semacquire(&sem)
+ c2 <- true
+ }()
+ }
+ }
+ for p := 0; p < procs; p++ {
+ go func() {
+ foo := 0
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ runtime.Semrelease(&sem)
+ if work {
+ for i := 0; i < LocalWork; i++ {
+ foo *= 2
+ foo /= 2
+ }
+ }
+ runtime.Semacquire(&sem)
+ }
+ }
+ c <- foo == 42
+ runtime.Semrelease(&sem)
+ }()
+ }
+ if block {
+ for p := 0; p < procs/2; p++ {
+ <-c2
+ }
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func BenchmarkSemaSyntNonblock(b *testing.B) {
+ benchmarkSema(b, false, false)
+}
+
+func BenchmarkSemaSyntBlock(b *testing.B) {
+ benchmarkSema(b, true, false)
+}
+
+func BenchmarkSemaWorkNonblock(b *testing.B) {
+ benchmarkSema(b, false, true)
+}
+
+func BenchmarkSemaWorkBlock(b *testing.B) {
+ benchmarkSema(b, true, true)
+}
diff --git a/src/pkg/runtime/windows/amd64/defs.h b/src/pkg/runtime/windows/amd64/defs.h
new file mode 100644
index 000000000..830c6a855
--- /dev/null
+++ b/src/pkg/runtime/windows/amd64/defs.h
@@ -0,0 +1,40 @@
+// g:\opensource\go\bin\godefs.exe -f -m64 defs.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+// Constants
+enum {
+ PROT_NONE = 0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+ MAP_ANON = 0x1,
+ MAP_PRIVATE = 0x2,
+ SIGINT = 0x2,
+ CTRL_C_EVENT = 0,
+ CTRL_BREAK_EVENT = 0x1,
+ EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
+ EXCEPTION_BREAKPOINT = 0x80000003,
+ EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
+ EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
+ EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
+ EXCEPTION_FLT_OVERFLOW = 0xc0000091,
+ EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
+ EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
+ EXCEPTION_INT_OVERFLOW = 0xc0000095,
+};
+
+// Types
+#pragma pack on
+
+typedef struct ExceptionRecord ExceptionRecord;
+struct ExceptionRecord {
+ uint32 ExceptionCode;
+ uint32 ExceptionFlags;
+ ExceptionRecord *ExceptionRecord;
+ void *ExceptionAddress;
+ uint32 NumberParameters;
+ byte pad_godefs_0[4];
+ uint64 ExceptionInformation[15];
+};
+#pragma pack off
diff --git a/src/pkg/runtime/windows/amd64/rt0.s b/src/pkg/runtime/windows/amd64/rt0.s
new file mode 100644
index 000000000..e54e7edeb
--- /dev/null
+++ b/src/pkg/runtime/windows/amd64/rt0.s
@@ -0,0 +1,10 @@
+// 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 "amd64/asm.h"
+
+TEXT _rt0_amd64_windows(SB),7,$-8
+ MOVQ $_rt0_amd64(SB), AX
+ MOVQ SP, DI
+ JMP AX
diff --git a/src/pkg/runtime/windows/amd64/signal.c b/src/pkg/runtime/windows/amd64/signal.c
new file mode 100644
index 000000000..1fc3eb060
--- /dev/null
+++ b/src/pkg/runtime/windows/amd64/signal.c
@@ -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.
+
+#include "runtime.h"
+#include "defs.h"
+#include "os.h"
+
+void
+runtime·initsig(int32 queue)
+{
+}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ // TODO: Enable profiling interrupts.
+
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/windows/amd64/sys.s b/src/pkg/runtime/windows/amd64/sys.s
new file mode 100644
index 000000000..b1eacfc82
--- /dev/null
+++ b/src/pkg/runtime/windows/amd64/sys.s
@@ -0,0 +1,129 @@
+// 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 "amd64/asm.h"
+
+// void *stdcall_raw(void *fn, uintptr nargs, void *args)
+TEXT runtime·stdcall_raw(SB),7,$8
+ MOVQ fn+0(FP), AX
+ MOVQ nargs+8(FP), CX
+ MOVQ args+16(FP), R11
+
+ // Switch to m->g0 if needed.
+ get_tls(DI)
+ MOVQ m(DI), DX
+ MOVQ g(DI), SI
+ MOVQ SI, 0(SP) // save g
+ MOVQ SP, m_gostack(DX) // save SP
+ MOVQ m_g0(DX), SI
+ CMPQ g(DI), SI
+ JEQ 3(PC)
+ MOVQ (g_sched+gobuf_sp)(SI), SP
+ MOVQ SI, g(DI)
+
+ SUBQ $0x60, SP
+
+ // Copy args to new stack.
+ MOVQ SP, DI
+ MOVQ R11, SI
+ CLD
+ REP; MOVSQ
+ MOVQ 0(R11), CX
+ MOVQ 8(R11), DX
+ MOVQ 16(R11), R8
+ MOVQ 24(R11), R9
+
+ // Call stdcall function.
+ CALL AX
+
+ // Restore original SP, g.
+ get_tls(DI)
+ MOVQ m(DI), DX
+ MOVQ m_gostack(DX), SP // restore SP
+ MOVQ 0(SP), SI // restore g
+ MOVQ SI, g(DI)
+
+ RET
+
+// faster get/set last error
+TEXT runtime·getlasterror(SB),7,$0
+ MOVQ 0x30(GS), AX
+ MOVL 0x68(AX), AX
+ RET
+
+TEXT runtime·setlasterror(SB),7,$0
+ MOVL err+0(FP), AX
+ MOVQ 0x30(GS), CX
+ MOVL AX, 0x68(CX)
+ RET
+
+// Windows runs the ctrl handler in a new thread.
+TEXT runtime·ctrlhandler(SB),7,$0
+ // TODO
+ RET
+
+TEXT runtime·callbackasm(SB),7,$0
+ // TODO
+ RET
+
+// void tstart(M *newm);
+TEXT runtime·tstart(SB),7,$0
+ MOVQ newm+8(SP), CX // m
+ MOVQ m_g0(CX), DX // g
+
+ MOVQ SP, DI // remember stack
+
+ // Layout new m scheduler stack on os stack.
+ MOVQ SP, AX
+ MOVQ AX, g_stackbase(DX)
+ SUBQ $(64*1024), AX // stack size
+ MOVQ AX, g_stackguard(DX)
+
+ // Set up tls.
+ LEAQ m_tls(CX), SI
+ MOVQ SI, 0x58(GS)
+ MOVQ CX, m(SI)
+ MOVQ DX, g(SI)
+
+ // Someday the convention will be D is always cleared.
+ CLD
+
+ PUSHQ DI // original stack
+
+ CALL runtime·stackcheck(SB) // clobbers AX,CX
+
+ CALL runtime·mstart(SB)
+
+ POPQ DI // original stack
+ MOVQ DI, SP
+
+ RET
+
+// uint32 tstart_stdcall(M *newm);
+TEXT runtime·tstart_stdcall(SB),7,$0
+ MOVQ CX, BX // stdcall first arg in RCX
+
+ PUSHQ BX
+ CALL runtime·tstart+0(SB)
+ POPQ BX
+
+ // Adjust stack for stdcall to return properly.
+ MOVQ (SP), AX // save return address
+ ADDQ $8, SP // remove single parameter
+ MOVQ AX, (SP) // restore return address
+
+ XORL AX, AX // return 0 == success
+
+ RET
+
+TEXT runtime·notok(SB),7,$0
+ MOVQ $0xf1, BP
+ MOVQ BP, (BP)
+ RET
+
+// set tls base to DI
+TEXT runtime·settls(SB),7,$0
+ MOVQ DI, 0x58(GS)
+ RET
+
diff --git a/src/pkg/runtime/windows/mem.c b/src/pkg/runtime/windows/mem.c
index 54d77da37..5d2291fa3 100644
--- a/src/pkg/runtime/windows/mem.c
+++ b/src/pkg/runtime/windows/mem.c
@@ -24,7 +24,7 @@ void*
runtime·SysAlloc(uintptr n)
{
mstats.sys += n;
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_EXECUTE_READWRITE);
}
void
@@ -40,7 +40,7 @@ runtime·SysFree(void *v, uintptr n)
uintptr r;
mstats.sys -= n;
- r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, 0, MEM_RELEASE);
+ r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, (uintptr)0, (uintptr)MEM_RELEASE);
if(r == 0)
runtime·throw("runtime: failed to release pages");
}
@@ -50,12 +50,12 @@ runtime·SysReserve(void *v, uintptr n)
{
// v is just a hint.
// First try at v.
- v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
if(v != nil)
return v;
// Next let the kernel choose the address.
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
}
void
@@ -64,7 +64,7 @@ runtime·SysMap(void *v, uintptr n)
void *p;
mstats.sys += n;
- p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_EXECUTE_READWRITE);
if(p != v)
runtime·throw("runtime: cannot map pages in arena address space");
}
diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h
index 77881e86e..bc9678733 100644
--- a/src/pkg/runtime/windows/os.h
+++ b/src/pkg/runtime/windows/os.h
@@ -7,6 +7,9 @@ extern void *runtime·GetProcAddress;
// Call a Windows function with stdcall conventions,
// and switch to os stack during the call.
+#pragma varargck countpos runtime·stdcall 2
+#pragma varargck type runtime·stdcall void*
+#pragma varargck type runtime·stdcall uintptr
void *runtime·stdcall_raw(void *fn, uintptr nargs, void *args);
void *runtime·stdcall(void *fn, int32 count, ...);
uintptr runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err);
diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c
index 81ad68033..5644fd5dd 100644
--- a/src/pkg/runtime/windows/thread.c
+++ b/src/pkg/runtime/windows/thread.c
@@ -45,7 +45,7 @@ void
runtime·osinit(void)
{
runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq);
- runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, 1);
+ runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
}
void
@@ -81,7 +81,7 @@ runtime·goenvs(void)
void
runtime·exit(int32 code)
{
- runtime·stdcall(runtime·ExitProcess, 1, code);
+ runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code);
}
int32
@@ -93,15 +93,15 @@ runtime·write(int32 fd, void *buf, int32 n)
written = 0;
switch(fd) {
case 1:
- handle = runtime·stdcall(runtime·GetStdHandle, 1, -11);
+ handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-11);
break;
case 2:
- handle = runtime·stdcall(runtime·GetStdHandle, 1, -12);
+ handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-12);
break;
default:
return -1;
}
- runtime·stdcall(runtime·WriteFile, 5, handle, buf, n, &written, 0);
+ runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0);
return written;
}
@@ -111,7 +111,7 @@ initevent(void **pevent)
{
void *event;
- event = runtime·stdcall(runtime·CreateEvent, 4, 0, 0, 0, 0);
+ event = runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0);
if(!runtime·casp(pevent, 0, event)) {
// Someone else filled it in. Use theirs.
runtime·stdcall(runtime·CloseHandle, 1, event);
@@ -126,7 +126,7 @@ eventlock(Lock *l)
initevent(&l->event);
if(runtime·xadd(&l->key, 1) > 1) // someone else has it; wait
- runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, -1);
+ runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, (uintptr)-1);
}
static void
@@ -190,7 +190,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
USED(g); // assuming g = m->g0
USED(fn); // assuming fn = mstart
- thandle = runtime·stdcall(runtime·CreateThread, 6, 0, 0, runtime·tstart_stdcall, m, 0, 0);
+ thandle = runtime·stdcall(runtime·CreateThread, 6, (uintptr)0, (uintptr)0, runtime·tstart_stdcall, m, (uintptr)0, (uintptr)0);
if(thandle == 0) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror());
runtime·throw("runtime.newosproc");
@@ -219,7 +219,7 @@ runtime·gettime(int64 *sec, int32 *usec)
void *
runtime·stdcall(void *fn, int32 count, ...)
{
- return runtime·stdcall_raw(fn, count, (uintptr*)(&count + 1));
+ return runtime·stdcall_raw(fn, count, (uintptr*)&count + 1);
}
uintptr
diff --git a/src/pkg/smtp/smtp.go b/src/pkg/smtp/smtp.go
index d716df56b..2d5e86247 100644
--- a/src/pkg/smtp/smtp.go
+++ b/src/pkg/smtp/smtp.go
@@ -93,11 +93,11 @@ func (c *Client) ehlo() os.Error {
return err
}
ext := make(map[string]string)
- extList := strings.Split(msg, "\n", -1)
+ extList := strings.Split(msg, "\n")
if len(extList) > 1 {
extList = extList[1:]
for _, line := range extList {
- args := strings.Split(line, " ", 2)
+ args := strings.SplitN(line, " ", 2)
if len(args) > 1 {
ext[args[0]] = args[1]
} else {
@@ -106,7 +106,7 @@ func (c *Client) ehlo() os.Error {
}
}
if mechs, ok := ext["AUTH"]; ok {
- c.auth = strings.Split(mechs, " ", -1)
+ c.auth = strings.Split(mechs, " ")
}
c.ext = ext
return err
diff --git a/src/pkg/smtp/smtp_test.go b/src/pkg/smtp/smtp_test.go
index 49363adf0..c053557d7 100644
--- a/src/pkg/smtp/smtp_test.go
+++ b/src/pkg/smtp/smtp_test.go
@@ -64,8 +64,8 @@ func (f faker) Close() os.Error {
}
func TestBasic(t *testing.T) {
- basicServer = strings.Join(strings.Split(basicServer, "\n", -1), "\r\n")
- basicClient = strings.Join(strings.Split(basicClient, "\n", -1), "\r\n")
+ basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
+ basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go
index b70757959..daed61ea8 100644
--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -190,12 +190,12 @@ func (p StringSlice) Sort() { Sort(p) }
// Convenience wrappers for common cases
-// SortInts sorts a slice of ints in increasing order.
-func SortInts(a []int) { Sort(IntSlice(a)) }
-// SortFloat64s sorts a slice of float64s in increasing order.
-func SortFloat64s(a []float64) { Sort(Float64Slice(a)) }
-// SortStrings sorts a slice of strings in increasing order.
-func SortStrings(a []string) { Sort(StringSlice(a)) }
+// Ints sorts a slice of ints in increasing order.
+func Ints(a []int) { Sort(IntSlice(a)) }
+// Float64s sorts a slice of float64s in increasing order.
+func Float64s(a []float64) { Sort(Float64Slice(a)) }
+// Strings sorts a slice of strings in increasing order.
+func Strings(a []string) { Sort(StringSlice(a)) }
// IntsAreSorted tests whether a slice of ints is sorted in increasing order.
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index 29359c83f..4da262637 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -46,27 +46,27 @@ func TestSortStringSlice(t *testing.T) {
}
}
-func TestSortInts(t *testing.T) {
+func TestInts(t *testing.T) {
data := ints
- SortInts(data[0:])
+ Ints(data[0:])
if !IntsAreSorted(data[0:]) {
t.Errorf("sorted %v", ints)
t.Errorf(" got %v", data)
}
}
-func TestSortFloat64s(t *testing.T) {
+func TestFloat64s(t *testing.T) {
data := float64s
- SortFloat64s(data[0:])
+ Float64s(data[0:])
if !Float64sAreSorted(data[0:]) {
t.Errorf("sorted %v", float64s)
t.Errorf(" got %v", data)
}
}
-func TestSortStrings(t *testing.T) {
+func TestStrings(t *testing.T) {
data := strings
- SortStrings(data[0:])
+ Strings(data[0:])
if !StringsAreSorted(data[0:]) {
t.Errorf("sorted %v", strings)
t.Errorf(" got %v", data)
@@ -85,7 +85,7 @@ func TestSortLarge_Random(t *testing.T) {
if IntsAreSorted(data) {
t.Fatalf("terrible rand.rand")
}
- SortInts(data)
+ Ints(data)
if !IntsAreSorted(data) {
t.Errorf("sort didn't sort - 1M ints")
}
@@ -99,7 +99,7 @@ func BenchmarkSortString1K(b *testing.B) {
data[i] = strconv.Itoa(i ^ 0x2cc)
}
b.StartTimer()
- SortStrings(data)
+ Strings(data)
b.StopTimer()
}
}
@@ -112,7 +112,7 @@ func BenchmarkSortInt1K(b *testing.B) {
data[i] = i ^ 0x2cc
}
b.StartTimer()
- SortInts(data)
+ Ints(data)
b.StopTimer()
}
}
@@ -125,7 +125,7 @@ func BenchmarkSortInt64K(b *testing.B) {
data[i] = i ^ 0xcccc
}
b.StartTimer()
- SortInts(data)
+ Ints(data)
b.StopTimer()
}
}
@@ -241,9 +241,9 @@ func TestBentleyMcIlroy(t *testing.T) {
for i := 0; i < n; i++ {
mdata[i] = data[i]
}
- // SortInts is known to be correct
+ // Ints is known to be correct
// because mode Sort runs after mode _Copy.
- SortInts(mdata)
+ Ints(mdata)
case _Dither:
for i := 0; i < n; i++ {
mdata[i] = data[i] + i%5
diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go
index 34baeee39..3096957f5 100644
--- a/src/pkg/strconv/fp_test.go
+++ b/src/pkg/strconv/fp_test.go
@@ -28,7 +28,7 @@ func pow2(i int) float64 {
// Wrapper around strconv.Atof64. Handles dddddp+ddd (binary exponent)
// itself, passes the rest on to strconv.Atof64.
func myatof64(s string) (f float64, ok bool) {
- a := strings.Split(s, "p", 2)
+ a := strings.SplitN(s, "p", 2)
if len(a) == 2 {
n, err := strconv.Atoi64(a[0])
if err != nil {
@@ -72,7 +72,7 @@ func myatof64(s string) (f float64, ok bool) {
// Wrapper around strconv.Atof32. Handles dddddp+ddd (binary exponent)
// itself, passes the rest on to strconv.Atof32.
func myatof32(s string) (f float32, ok bool) {
- a := strings.Split(s, "p", 2)
+ a := strings.SplitN(s, "p", 2)
if len(a) == 2 {
n, err := strconv.Atoi(a[0])
if err != nil {
@@ -116,7 +116,7 @@ func TestFp(t *testing.T) {
if len(line) == 0 || line[0] == '#' {
continue
}
- a := strings.Split(line, " ", -1)
+ a := strings.Split(line, " ")
if len(a) != 4 {
t.Error("testfp.txt:", lineno, ": wrong field count")
continue
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go
index bfd057180..6afbc7dc2 100644
--- a/src/pkg/strings/strings.go
+++ b/src/pkg/strings/strings.go
@@ -198,26 +198,40 @@ func genSplit(s, sep string, sepSave, n int) []string {
return a[0 : na+1]
}
-// Split slices s into substrings separated by sep and returns a slice of
+// SplitN slices s into substrings separated by sep and returns a slice of
// the substrings between those separators.
-// If sep is empty, Split splits after each UTF-8 sequence.
+// If sep is empty, SplitN splits after each UTF-8 sequence.
// The count determines the number of substrings to return:
// n > 0: at most n substrings; the last substring will be the unsplit remainder.
// n == 0: the result is nil (zero substrings)
// n < 0: all substrings
-func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
+func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
-// SplitAfter slices s into substrings after each instance of sep and
+// SplitAfterN slices s into substrings after each instance of sep and
// returns a slice of those substrings.
-// If sep is empty, Split splits after each UTF-8 sequence.
+// If sep is empty, SplitAfterN splits after each UTF-8 sequence.
// The count determines the number of substrings to return:
// n > 0: at most n substrings; the last substring will be the unsplit remainder.
// n == 0: the result is nil (zero substrings)
// n < 0: all substrings
-func SplitAfter(s, sep string, n int) []string {
+func SplitAfterN(s, sep string, n int) []string {
return genSplit(s, sep, len(sep), n)
}
+// Split slices s into all substrings separated by sep and returns a slice of
+// the substrings between those separators.
+// If sep is empty, Split splits after each UTF-8 sequence.
+// It is equivalent to SplitN with a count of -1.
+func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
+
+// SplitAfter slices s into all substrings after each instance of sep and
+// returns a slice of those substrings.
+// If sep is empty, SplitAfter splits after each UTF-8 sequence.
+// It is equivalent to SplitAfterN with a count of -1.
+func SplitAfter(s, sep string) []string {
+ return genSplit(s, sep, len(sep), -1)
+}
+
// Fields splits the string s around each instance of one or more consecutive white space
// characters, returning an array of substrings of s or an empty list if s contains only white space.
func Fields(s string) []string {
diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go
index a1a635ddd..c54617339 100644
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -186,7 +186,7 @@ var explodetests = []ExplodeTest{
func TestExplode(t *testing.T) {
for _, tt := range explodetests {
- a := Split(tt.s, "", tt.n)
+ a := SplitN(tt.s, "", tt.n)
if !eq(a, tt.a) {
t.Errorf("explode(%q, %d) = %v; want %v", tt.s, tt.n, a, tt.a)
continue
@@ -223,7 +223,7 @@ var splittests = []SplitTest{
func TestSplit(t *testing.T) {
for _, tt := range splittests {
- a := Split(tt.s, tt.sep, tt.n)
+ a := SplitN(tt.s, tt.sep, tt.n)
if !eq(a, tt.a) {
t.Errorf("Split(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, a, tt.a)
continue
@@ -235,6 +235,12 @@ func TestSplit(t *testing.T) {
if s != tt.s {
t.Errorf("Join(Split(%q, %q, %d), %q) = %q", tt.s, tt.sep, tt.n, tt.sep, s)
}
+ if tt.n < 0 {
+ b := Split(tt.s, tt.sep)
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("Split disagrees with SplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
+ }
+ }
}
}
@@ -256,7 +262,7 @@ var splitaftertests = []SplitTest{
func TestSplitAfter(t *testing.T) {
for _, tt := range splitaftertests {
- a := SplitAfter(tt.s, tt.sep, tt.n)
+ a := SplitAfterN(tt.s, tt.sep, tt.n)
if !eq(a, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, a, tt.a)
continue
@@ -265,6 +271,12 @@ func TestSplitAfter(t *testing.T) {
if s != tt.s {
t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
}
+ if tt.n < 0 {
+ b := SplitAfter(tt.s, tt.sep)
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("SplitAfter disagrees with SplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
+ }
+ }
}
}
@@ -623,8 +635,8 @@ func equal(m string, s1, s2 string, t *testing.T) bool {
if s1 == s2 {
return true
}
- e1 := Split(s1, "", -1)
- e2 := Split(s2, "", -1)
+ e1 := Split(s1, "")
+ e2 := Split(s2, "")
for i, c1 := range e1 {
if i > len(e2) {
break
diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go
index 13f03cad3..2d46c8994 100644
--- a/src/pkg/sync/mutex.go
+++ b/src/pkg/sync/mutex.go
@@ -17,8 +17,8 @@ import (
// Mutexes can be created as part of other structures;
// the zero value for a Mutex is an unlocked mutex.
type Mutex struct {
- key int32
- sema uint32
+ state int32
+ sema uint32
}
// A Locker represents an object that can be locked and unlocked.
@@ -27,15 +27,41 @@ type Locker interface {
Unlock()
}
+const (
+ mutexLocked = 1 << iota // mutex is locked
+ mutexWoken
+ mutexWaiterShift = iota
+)
+
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
- if atomic.AddInt32(&m.key, 1) == 1 {
- // changed from 0 to 1; we hold lock
+ // Fast path: grab unlocked mutex.
+ if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
return
}
- runtime.Semacquire(&m.sema)
+
+ awoke := false
+ for {
+ old := m.state
+ new := old | mutexLocked
+ if old&mutexLocked != 0 {
+ new = old + 1<<mutexWaiterShift
+ }
+ if awoke {
+ // The goroutine has been woken from sleep,
+ // so we need to reset the flag in either case.
+ new &^= mutexWoken
+ }
+ if atomic.CompareAndSwapInt32(&m.state, old, new) {
+ if old&mutexLocked == 0 {
+ break
+ }
+ runtime.Semacquire(&m.sema)
+ awoke = true
+ }
+ }
}
// Unlock unlocks m.
@@ -45,14 +71,25 @@ func (m *Mutex) Lock() {
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
- switch v := atomic.AddInt32(&m.key, -1); {
- case v == 0:
- // changed from 1 to 0; no contention
- return
- case v == -1:
- // changed from 0 to -1: wasn't locked
- // (or there are 4 billion goroutines waiting)
+ // Fast path: drop lock bit.
+ new := atomic.AddInt32(&m.state, -mutexLocked)
+ if (new+mutexLocked)&mutexLocked == 0 {
panic("sync: unlock of unlocked mutex")
}
- runtime.Semrelease(&m.sema)
+
+ old := new
+ for {
+ // If there are no waiters or a goroutine has already
+ // been woken or grabbed the lock, no need to wake anyone.
+ if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
+ return
+ }
+ // Grab the right to wake someone.
+ new = (old - 1<<mutexWaiterShift) | mutexWoken
+ if atomic.CompareAndSwapInt32(&m.state, old, new) {
+ runtime.Semrelease(&m.sema)
+ return
+ }
+ old = m.state
+ }
}
diff --git a/src/pkg/sync/mutex_test.go b/src/pkg/sync/mutex_test.go
index 9bfdec365..d5ada8567 100644
--- a/src/pkg/sync/mutex_test.go
+++ b/src/pkg/sync/mutex_test.go
@@ -9,6 +9,7 @@ package sync_test
import (
"runtime"
. "sync"
+ "sync/atomic"
"testing"
)
@@ -72,24 +73,6 @@ func TestMutex(t *testing.T) {
}
}
-func BenchmarkUncontendedMutex(b *testing.B) {
- m := new(Mutex)
- HammerMutex(m, b.N, make(chan bool, 2))
-}
-
-func BenchmarkContendedMutex(b *testing.B) {
- b.StopTimer()
- m := new(Mutex)
- c := make(chan bool)
- defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
- b.StartTimer()
-
- go HammerMutex(m, b.N/2, c)
- go HammerMutex(m, b.N/2, c)
- <-c
- <-c
-}
-
func TestMutexPanic(t *testing.T) {
defer func() {
if recover() == nil {
@@ -102,3 +85,83 @@ func TestMutexPanic(t *testing.T) {
mu.Unlock()
mu.Unlock()
}
+
+func BenchmarkMutexUncontended(b *testing.B) {
+ type PaddedMutex struct {
+ Mutex
+ pad [128]uint8
+ }
+ const CallsPerSched = 1000
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ for p := 0; p < procs; p++ {
+ go func() {
+ var mu PaddedMutex
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ mu.Lock()
+ mu.Unlock()
+ }
+ }
+ c <- true
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func benchmarkMutex(b *testing.B, slack, work bool) {
+ const (
+ CallsPerSched = 1000
+ LocalWork = 100
+ GoroutineSlack = 10
+ )
+ procs := runtime.GOMAXPROCS(-1)
+ if slack {
+ procs *= GoroutineSlack
+ }
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ var mu Mutex
+ for p := 0; p < procs; p++ {
+ go func() {
+ foo := 0
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ mu.Lock()
+ mu.Unlock()
+ if work {
+ for i := 0; i < LocalWork; i++ {
+ foo *= 2
+ foo /= 2
+ }
+ }
+ }
+ }
+ c <- foo == 42
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func BenchmarkMutex(b *testing.B) {
+ benchmarkMutex(b, false, false)
+}
+
+func BenchmarkMutexSlack(b *testing.B) {
+ benchmarkMutex(b, true, false)
+}
+
+func BenchmarkMutexWork(b *testing.B) {
+ benchmarkMutex(b, false, true)
+}
+
+func BenchmarkMutexWorkSlack(b *testing.B) {
+ benchmarkMutex(b, true, true)
+}
diff --git a/src/pkg/sync/once.go b/src/pkg/sync/once.go
index b6f5f5a87..447b71dcb 100644
--- a/src/pkg/sync/once.go
+++ b/src/pkg/sync/once.go
@@ -4,10 +4,14 @@
package sync
+import (
+ "sync/atomic"
+)
+
// Once is an object that will perform exactly one action.
type Once struct {
m Mutex
- done bool
+ done int32
}
// Do calls the function f if and only if the method is being called for the
@@ -26,10 +30,14 @@ type Once struct {
// Do to be called, it will deadlock.
//
func (o *Once) Do(f func()) {
+ if atomic.AddInt32(&o.done, 0) == 1 {
+ return
+ }
+ // Slow-path.
o.m.Lock()
defer o.m.Unlock()
- if !o.done {
- o.done = true
+ if o.done == 0 {
f()
+ atomic.CompareAndSwapInt32(&o.done, 0, 1)
}
}
diff --git a/src/pkg/sync/once_test.go b/src/pkg/sync/once_test.go
index 155954a49..157a3667a 100644
--- a/src/pkg/sync/once_test.go
+++ b/src/pkg/sync/once_test.go
@@ -6,6 +6,8 @@ package sync_test
import (
. "sync"
+ "sync/atomic"
+ "runtime"
"testing"
)
@@ -35,3 +37,26 @@ func TestOnce(t *testing.T) {
t.Errorf("once failed: %d is not 1", *o)
}
}
+
+func BenchmarkOnce(b *testing.B) {
+ const CallsPerSched = 1000
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ var once Once
+ f := func() {}
+ c := make(chan bool, procs)
+ for p := 0; p < procs; p++ {
+ go func() {
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ once.Do(f)
+ }
+ }
+ c <- true
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile
index d7bd58373..212b6f85d 100644
--- a/src/pkg/syscall/Makefile
+++ b/src/pkg/syscall/Makefile
@@ -41,6 +41,8 @@ GOFILES_linux=\
GOFILES_windows=\
exec_windows.go\
+ zerrors_windows.go\
+ ztypes_windows.go\
GOFILES_plan9=\
exec_plan9.go\
diff --git a/src/pkg/syscall/asm_windows_386.s b/src/pkg/syscall/asm_windows_386.s
index 3d9f6fc94..a7b95643d 100644
--- a/src/pkg/syscall/asm_windows_386.s
+++ b/src/pkg/syscall/asm_windows_386.s
@@ -3,5 +3,5 @@
// license that can be found in the LICENSE file.
//
-// System calls for 386, Windows are implemented in ../runtime/windows/syscall.cgo
+// System calls for 386, Windows are implemented in ../runtime/windows/syscall.goc
//
diff --git a/src/pkg/syscall/asm_windows_amd64.s b/src/pkg/syscall/asm_windows_amd64.s
new file mode 100644
index 000000000..8b38710c7
--- /dev/null
+++ b/src/pkg/syscall/asm_windows_amd64.s
@@ -0,0 +1,7 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//
+// System calls for amd64, Windows are implemented in ../runtime/windows/syscall.goc
+//
diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go
index 4b3cfe47f..46f05efef 100644
--- a/src/pkg/syscall/exec_unix.go
+++ b/src/pkg/syscall/exec_unix.go
@@ -146,6 +146,14 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
}
+ // Set process group
+ if sys.Setpgid {
+ _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
// Chroot
if chroot != nil {
_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
@@ -241,6 +249,22 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
}
+ // Detach fd 0 from tty
+ if sys.Noctty {
+ _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ // Make fd 0 the tty
+ if sys.Setctty {
+ _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
// Time to exec.
_, _, err1 = RawSyscall(SYS_EXECVE,
uintptr(unsafe.Pointer(argv0)),
@@ -260,12 +284,16 @@ childerror:
panic("unreached")
}
+// Credential holds user and group identities to be assumed
+// by a child process started by StartProcess.
type Credential struct {
Uid uint32 // User ID.
Gid uint32 // Group ID.
Groups []uint32 // Supplementary group IDs.
}
+// ProcAttr holds attributes that will be applied to a new process started
+// by StartProcess.
type ProcAttr struct {
Dir string // Current working directory.
Env []string // Environment.
@@ -278,6 +306,9 @@ type SysProcAttr struct {
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
+ Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
+ Setctty bool // Set controlling terminal to fd 0
+ Noctty bool // Detach fd 0 from controlling terminal
}
var zeroProcAttr ProcAttr
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go
index 96a01e767..e8b540ad1 100644
--- a/src/pkg/syscall/exec_windows.go
+++ b/src/pkg/syscall/exec_windows.go
@@ -121,11 +121,11 @@ func createEnvBlock(envv []string) *uint16 {
return &utf16.Encode([]int(string(b)))[0]
}
-func CloseOnExec(fd int) {
- SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0)
+func CloseOnExec(fd Handle) {
+ SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
}
-func SetNonblock(fd int, nonblocking bool) (errno int) {
+func SetNonblock(fd Handle, nonblocking bool) (errno int) {
return 0
}
@@ -220,7 +220,7 @@ func joinExeDirAndFName(dir, p string) (name string, err int) {
type ProcAttr struct {
Dir string
Env []string
- Files []int
+ Files []Handle
Sys *SysProcAttr
}
@@ -290,14 +290,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int,
defer ForkLock.Unlock()
p, _ := GetCurrentProcess()
- fd := make([]int32, len(attr.Files))
+ fd := make([]Handle, len(attr.Files))
for i := range attr.Files {
if attr.Files[i] > 0 {
- err := DuplicateHandle(p, int32(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
+ err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
if err != 0 {
return 0, 0, err
}
- defer CloseHandle(int32(fd[i]))
+ defer CloseHandle(Handle(fd[i]))
}
}
si := new(StartupInfo)
@@ -317,7 +317,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int,
if err != 0 {
return 0, 0, err
}
- defer CloseHandle(pi.Thread)
+ defer CloseHandle(Handle(pi.Thread))
return int(pi.ProcessId), int(pi.Process), 0
}
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index f031a38ed..7d0c1ac2a 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -78,6 +78,7 @@ GOOSARCH="${GOOS}_${GOARCH}"
# defaults
mksyscall="./mksyscall.pl"
mkerrors="./mkerrors.sh"
+zerrors="zerrors_$GOOSARCH.go"
run="sh"
case "$1" in
@@ -150,6 +151,14 @@ windows_386)
mksysnum=
mktypes=
mkerrors="./mkerrors_windows.sh -f -m32"
+ zerrors="zerrors_windows.go"
+ ;;
+windows_amd64)
+ mksyscall="./mksyscall_windows.pl"
+ mksysnum=
+ mktypes=
+ mkerrors="./mkerrors_windows.sh -f -m32"
+ zerrors="zerrors_windows.go"
;;
plan9_386)
mkerrors=
@@ -164,7 +173,7 @@ plan9_386)
esac
(
- if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >zerrors_$GOOSARCH.go"; fi
+ if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
syscall_goos="syscall_$GOOS.go"
case "$GOOS" in
darwin | freebsd)
diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh
index 21583603f..c90cd1c00 100755
--- a/src/pkg/syscall/mkerrors.sh
+++ b/src/pkg/syscall/mkerrors.sh
@@ -78,6 +78,7 @@ includes_FreeBSD='
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
+#include <termios.h>
#include <netinet/ip.h>
#include <netinet/ip_mroute.h>
'
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 0e979ff6b..5b8143aac 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -13,6 +13,10 @@ import (
const OS = "windows"
+type Handle uintptr
+
+const InvalidHandle = ^Handle(0)
+
/*
small demo to detect version of windows you are running:
@@ -77,10 +81,10 @@ func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr)
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr)
-func loadlibraryex(filename uintptr) (handle uint32)
-func getprocaddress(handle uint32, procname uintptr) (proc uintptr)
+func loadlibraryex(filename uintptr) (handle uintptr)
+func getprocaddress(handle uintptr, procname uintptr) (proc uintptr)
-func loadDll(fname string) uint32 {
+func loadDll(fname string) uintptr {
m := loadlibraryex(uintptr(unsafe.Pointer(StringBytePtr(fname))))
if m == 0 {
panic("syscall: could not LoadLibraryEx " + fname)
@@ -88,7 +92,7 @@ func loadDll(fname string) uint32 {
return m
}
-func getSysProcAddr(m uint32, pname string) uintptr {
+func getSysProcAddr(m uintptr, pname string) uintptr {
p := getprocaddress(m, uintptr(unsafe.Pointer(StringBytePtr(pname))))
if p == 0 {
panic("syscall: could not GetProcAddress for " + pname)
@@ -96,6 +100,8 @@ func getSysProcAddr(m uint32, pname string) uintptr {
return p
}
+func Getpagesize() int { return 4096 }
+
// Converts a Go function to a function pointer conforming
// to the stdcall calling convention. This is useful when
// interoperating with Windows code requiring callbacks.
@@ -110,22 +116,22 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno
// windows api calls
//sys GetLastError() (lasterrno int)
-//sys LoadLibrary(libname string) (handle uint32, errno int) = LoadLibraryW
-//sys FreeLibrary(handle uint32) (errno int)
-//sys GetProcAddress(module uint32, procname string) (proc uint32, errno int)
+//sys LoadLibrary(libname string) (handle Handle, errno int) = LoadLibraryW
+//sys FreeLibrary(handle Handle) (errno int)
+//sys GetProcAddress(module Handle, procname string) (proc Handle, errno int)
//sys GetVersion() (ver uint32, errno int)
//sys FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) = FormatMessageW
//sys ExitProcess(exitcode uint32)
-//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) [failretval==-1] = CreateFileW
-//sys ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int)
-//sys WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int)
-//sys SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) [failretval==0xffffffff]
-//sys CloseHandle(handle int32) (errno int)
-//sys GetStdHandle(stdhandle int32) (handle int32, errno int) [failretval==-1]
-//sys FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) [failretval==-1] = FindFirstFileW
-//sys FindNextFile(handle int32, data *Win32finddata) (errno int) = FindNextFileW
-//sys FindClose(handle int32) (errno int)
-//sys GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int)
+//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) [failretval==InvalidHandle] = CreateFileW
+//sys ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int)
+//sys WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int)
+//sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) [failretval==0xffffffff]
+//sys CloseHandle(handle Handle) (errno int)
+//sys GetStdHandle(stdhandle int) (handle Handle, errno int) [failretval==InvalidHandle]
+//sys FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) [failretval==InvalidHandle] = FindFirstFileW
+//sys FindNextFile(handle Handle, data *Win32finddata) (errno int) = FindNextFileW
+//sys FindClose(handle Handle) (errno int)
+//sys GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int)
//sys GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) = GetCurrentDirectoryW
//sys SetCurrentDirectory(path *uint16) (errno int) = SetCurrentDirectoryW
//sys CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) = CreateDirectoryW
@@ -133,47 +139,47 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno
//sys DeleteFile(path *uint16) (errno int) = DeleteFileW
//sys MoveFile(from *uint16, to *uint16) (errno int) = MoveFileW
//sys GetComputerName(buf *uint16, n *uint32) (errno int) = GetComputerNameW
-//sys SetEndOfFile(handle int32) (errno int)
+//sys SetEndOfFile(handle Handle) (errno int)
//sys GetSystemTimeAsFileTime(time *Filetime)
//sys sleep(msec uint32) = Sleep
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval==0xffffffff]
-//sys CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int)
-//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int)
-//sys CancelIo(s uint32) (errno int)
+//sys CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int)
+//sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int)
+//sys CancelIo(s Handle) (errno int)
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW
-//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int)
-//sys TerminateProcess(handle int32, exitcode uint32) (errno int)
-//sys GetExitCodeProcess(handle int32, exitcode *uint32) (errno int)
+//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int)
+//sys TerminateProcess(handle Handle, exitcode uint32) (errno int)
+//sys GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int)
//sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW
-//sys GetCurrentProcess() (pseudoHandle int32, errno int)
-//sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int)
-//sys WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) [failretval==0xffffffff]
+//sys GetCurrentProcess() (pseudoHandle Handle, errno int)
+//sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int)
+//sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) [failretval==0xffffffff]
//sys GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) = GetTempPathW
-//sys CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int)
-//sys GetFileType(filehandle uint32) (n uint32, errno int)
-//sys CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) = advapi32.CryptAcquireContextW
-//sys CryptReleaseContext(provhandle uint32, flags uint32) (errno int) = advapi32.CryptReleaseContext
-//sys CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) = advapi32.CryptGenRandom
+//sys CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int)
+//sys GetFileType(filehandle Handle) (n uint32, errno int)
+//sys CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) = advapi32.CryptAcquireContextW
+//sys CryptReleaseContext(provhandle Handle, flags uint32) (errno int) = advapi32.CryptReleaseContext
+//sys CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) = advapi32.CryptGenRandom
//sys GetEnvironmentStrings() (envs *uint16, errno int) [failretval==nil] = kernel32.GetEnvironmentStringsW
//sys FreeEnvironmentStrings(envs *uint16) (errno int) = kernel32.FreeEnvironmentStringsW
//sys GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, errno int) = kernel32.GetEnvironmentVariableW
//sys SetEnvironmentVariable(name *uint16, value *uint16) (errno int) = kernel32.SetEnvironmentVariableW
-//sys SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int)
+//sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int)
//sys GetFileAttributes(name *uint16) (attrs uint32, errno int) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
//sys SetFileAttributes(name *uint16, attrs uint32) (errno int) = kernel32.SetFileAttributesW
//sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW
//sys CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) [failretval==nil] = shell32.CommandLineToArgvW
-//sys LocalFree(hmem uint32) (handle uint32, errno int) [failretval!=0]
-//sys SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int)
-//sys FlushFileBuffers(handle int32) (errno int)
+//sys LocalFree(hmem Handle) (handle Handle, errno int) [failretval!=0]
+//sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int)
+//sys FlushFileBuffers(handle Handle) (errno int)
//sys GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) = kernel32.GetFullPathNameW
-//sys CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle int32, errno int) = kernel32.CreateFileMappingW
-//sys MapViewOfFile(handle int32, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int)
+//sys CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) = kernel32.CreateFileMappingW
+//sys MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int)
//sys UnmapViewOfFile(addr uintptr) (errno int)
//sys FlushViewOfFile(addr uintptr, length uintptr) (errno int)
//sys VirtualLock(addr uintptr, length uintptr) (errno int)
//sys VirtualUnlock(addr uintptr, length uintptr) (errno int)
-//sys TransmitFile(s int32, handle int32, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = wsock32.TransmitFile
+//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = wsock32.TransmitFile
// syscall interface implementation for other packages
@@ -205,9 +211,9 @@ func makeInheritSa() *SecurityAttributes {
return &sa
}
-func Open(path string, mode int, perm uint32) (fd int, errno int) {
+func Open(path string, mode int, perm uint32) (fd Handle, errno int) {
if len(path) == 0 {
- return -1, ERROR_FILE_NOT_FOUND
+ return InvalidHandle, ERROR_FILE_NOT_FOUND
}
var access uint32
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
@@ -244,12 +250,12 @@ func Open(path string, mode int, perm uint32) (fd int, errno int) {
createmode = OPEN_EXISTING
}
h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
- return int(h), int(e)
+ return h, int(e)
}
-func Read(fd int, p []byte) (n int, errno int) {
+func Read(fd Handle, p []byte) (n int, errno int) {
var done uint32
- e := ReadFile(int32(fd), p, &done, nil)
+ e := ReadFile(fd, p, &done, nil)
if e != 0 {
if e == ERROR_BROKEN_PIPE {
// NOTE(brainman): work around ERROR_BROKEN_PIPE is returned on reading EOF from stdin
@@ -260,16 +266,16 @@ func Read(fd int, p []byte) (n int, errno int) {
return int(done), 0
}
-func Write(fd int, p []byte) (n int, errno int) {
+func Write(fd Handle, p []byte) (n int, errno int) {
var done uint32
- e := WriteFile(int32(fd), p, &done, nil)
+ e := WriteFile(fd, p, &done, nil)
if e != 0 {
return 0, e
}
return int(done), 0
}
-func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
+func Seek(fd Handle, offset int64, whence int) (newoffset int64, errno int) {
var w uint32
switch whence {
case 0:
@@ -282,19 +288,19 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) {
hi := int32(offset >> 32)
lo := int32(offset)
// use GetFileType to check pipe, pipe can't do seek
- ft, _ := GetFileType(uint32(fd))
+ ft, _ := GetFileType(fd)
if ft == FILE_TYPE_PIPE {
return 0, EPIPE
}
- rlo, e := SetFilePointer(int32(fd), lo, &hi, w)
+ rlo, e := SetFilePointer(fd, lo, &hi, w)
if e != 0 {
return 0, e
}
return int64(hi)<<32 + int64(rlo), 0
}
-func Close(fd int) (errno int) {
- return CloseHandle(int32(fd))
+func Close(fd Handle) (errno int) {
+ return CloseHandle(fd)
}
var (
@@ -303,9 +309,9 @@ var (
Stderr = getStdHandle(STD_ERROR_HANDLE)
)
-func getStdHandle(h int32) (fd int) {
+func getStdHandle(h int) (fd Handle) {
r, _ := GetStdHandle(h)
- return int(r)
+ return r
}
func Stat(path string, stat *Stat_t) (errno int) {
@@ -384,7 +390,7 @@ func ComputerName() (name string, errno int) {
return string(utf16.Decode(b[0:n])), 0
}
-func Ftruncate(fd int, length int64) (errno int) {
+func Ftruncate(fd Handle, length int64) (errno int) {
curoffset, e := Seek(fd, 0, 1)
if e != 0 {
return e
@@ -394,7 +400,7 @@ func Ftruncate(fd int, length int64) (errno int) {
if e != 0 {
return e
}
- e = SetEndOfFile(int32(fd))
+ e = SetEndOfFile(fd)
if e != 0 {
return e
}
@@ -413,17 +419,17 @@ func Sleep(nsec int64) (errno int) {
return 0
}
-func Pipe(p []int) (errno int) {
+func Pipe(p []Handle) (errno int) {
if len(p) != 2 {
return EINVAL
}
- var r, w uint32
+ var r, w Handle
e := CreatePipe(&r, &w, makeInheritSa(), 0)
if e != 0 {
return e
}
- p[0] = int(r)
- p[1] = int(w)
+ p[0] = r
+ p[1] = w
return 0
}
@@ -437,14 +443,14 @@ func Utimes(path string, tv []Timeval) (errno int) {
if e != 0 {
return e
}
- defer Close(int(h))
+ defer Close(h)
a := NsecToFiletime(tv[0].Nanoseconds())
w := NsecToFiletime(tv[1].Nanoseconds())
return SetFileTime(h, nil, &a, &w)
}
-func Fsync(fd int) (errno int) {
- return FlushFileBuffers(int32(fd))
+func Fsync(fd Handle) (errno int) {
+ return FlushFileBuffers(fd)
}
func Chmod(path string, mode uint32) (errno int) {
@@ -468,22 +474,22 @@ func Chmod(path string, mode uint32) (errno int) {
//sys WSAStartup(verreq uint32, data *WSAData) (sockerrno int) = wsock32.WSAStartup
//sys WSACleanup() (errno int) [failretval==-1] = wsock32.WSACleanup
-//sys WSAIoctl(s int32, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) [failretval==-1] = ws2_32.WSAIoctl
-//sys socket(af int32, typ int32, protocol int32) (handle int32, errno int) [failretval==-1] = wsock32.socket
-//sys setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = wsock32.setsockopt
-//sys bind(s int32, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.bind
-//sys connect(s int32, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.connect
-//sys getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getsockname
-//sys getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getpeername
-//sys listen(s int32, backlog int32) (errno int) [failretval==-1] = wsock32.listen
-//sys shutdown(s int32, how int32) (errno int) [failretval==-1] = wsock32.shutdown
-//sys Closesocket(s int32) (errno int) [failretval==-1] = wsock32.closesocket
-//sys AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx
+//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) [failretval==-1] = ws2_32.WSAIoctl
+//sys socket(af int32, typ int32, protocol int32) (handle Handle, errno int) [failretval==InvalidHandle] = wsock32.socket
+//sys setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = wsock32.setsockopt
+//sys bind(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.bind
+//sys connect(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.connect
+//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getsockname
+//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getpeername
+//sys listen(s Handle, backlog int32) (errno int) [failretval==-1] = wsock32.listen
+//sys shutdown(s Handle, how int32) (errno int) [failretval==-1] = wsock32.shutdown
+//sys Closesocket(s Handle) (errno int) [failretval==-1] = wsock32.closesocket
+//sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx
//sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = wsock32.GetAcceptExSockaddrs
-//sys WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv
-//sys WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend
-//sys WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecvFrom
-//sys WSASendTo(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASendTo
+//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv
+//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend
+//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecvFrom
+//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASendTo
//sys GetHostByName(name string) (h *Hostent, errno int) [failretval==nil] = ws2_32.gethostbyname
//sys GetServByName(name string, proto string) (s *Servent, errno int) [failretval==nil] = ws2_32.getservbyname
//sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs
@@ -578,62 +584,62 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, int) {
return nil, EAFNOSUPPORT
}
-func Socket(domain, typ, proto int) (fd, errno int) {
+func Socket(domain, typ, proto int) (fd Handle, errno int) {
if domain == AF_INET6 && SocketDisableIPv6 {
- return -1, EAFNOSUPPORT
+ return InvalidHandle, EAFNOSUPPORT
}
h, e := socket(int32(domain), int32(typ), int32(proto))
- return int(h), int(e)
+ return h, int(e)
}
-func SetsockoptInt(fd, level, opt int, value int) (errno int) {
+func SetsockoptInt(fd Handle, level, opt int, value int) (errno int) {
v := int32(value)
- return int(setsockopt(int32(fd), int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), int32(unsafe.Sizeof(v))))
+ return int(setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), int32(unsafe.Sizeof(v))))
}
-func Bind(fd int, sa Sockaddr) (errno int) {
+func Bind(fd Handle, sa Sockaddr) (errno int) {
ptr, n, err := sa.sockaddr()
if err != 0 {
return err
}
- return bind(int32(fd), ptr, n)
+ return bind(fd, ptr, n)
}
-func Connect(fd int, sa Sockaddr) (errno int) {
+func Connect(fd Handle, sa Sockaddr) (errno int) {
ptr, n, err := sa.sockaddr()
if err != 0 {
return err
}
- return connect(int32(fd), ptr, n)
+ return connect(fd, ptr, n)
}
-func Getsockname(fd int) (sa Sockaddr, errno int) {
+func Getsockname(fd Handle) (sa Sockaddr, errno int) {
var rsa RawSockaddrAny
l := int32(unsafe.Sizeof(rsa))
- if errno = getsockname(int32(fd), &rsa, &l); errno != 0 {
+ if errno = getsockname(fd, &rsa, &l); errno != 0 {
return
}
return rsa.Sockaddr()
}
-func Getpeername(fd int) (sa Sockaddr, errno int) {
+func Getpeername(fd Handle) (sa Sockaddr, errno int) {
var rsa RawSockaddrAny
l := int32(unsafe.Sizeof(rsa))
- if errno = getpeername(int32(fd), &rsa, &l); errno != 0 {
+ if errno = getpeername(fd, &rsa, &l); errno != 0 {
return
}
return rsa.Sockaddr()
}
-func Listen(s int, n int) (errno int) {
- return int(listen(int32(s), int32(n)))
+func Listen(s Handle, n int) (errno int) {
+ return int(listen(s, int32(n)))
}
-func Shutdown(fd, how int) (errno int) {
- return int(shutdown(int32(fd), int32(how)))
+func Shutdown(fd Handle, how int) (errno int) {
+ return int(shutdown(fd, int32(how)))
}
-func WSASendto(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to Sockaddr, overlapped *Overlapped, croutine *byte) (errno int) {
+func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to Sockaddr, overlapped *Overlapped, croutine *byte) (errno int) {
rsa, l, err := to.sockaddr()
if err != 0 {
return err
@@ -670,10 +676,12 @@ func (w WaitStatus) TrapCause() int { return -1 }
// TODO(brainman): fix all needed for net
-func Accept(fd int) (nfd int, sa Sockaddr, errno int) { return 0, nil, EWINDOWS }
-func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) { return 0, nil, EWINDOWS }
-func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) { return EWINDOWS }
-func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { return EWINDOWS }
+func Accept(fd Handle) (nfd Handle, sa Sockaddr, errno int) { return 0, nil, EWINDOWS }
+func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, errno int) {
+ return 0, nil, EWINDOWS
+}
+func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (errno int) { return EWINDOWS }
+func SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (errno int) { return EWINDOWS }
type Linger struct {
Onoff int32
@@ -695,25 +703,25 @@ type IPv6Mreq struct {
Interface uint32
}
-func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { return EWINDOWS }
-func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) { return EWINDOWS }
-func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { return EWINDOWS }
-func BindToDevice(fd int, device string) (errno int) { return EWINDOWS }
+func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (errno int) { return EWINDOWS }
+func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (errno int) { return EWINDOWS }
+func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (errno int) { return EWINDOWS }
+func BindToDevice(fd Handle, device string) (errno int) { return EWINDOWS }
// TODO(brainman): fix all needed for os
func Getpid() (pid int) { return -1 }
func Getppid() (ppid int) { return -1 }
-func Fchdir(fd int) (errno int) { return EWINDOWS }
+func Fchdir(fd Handle) (errno int) { return EWINDOWS }
func Link(oldpath, newpath string) (errno int) { return EWINDOWS }
func Symlink(path, link string) (errno int) { return EWINDOWS }
func Readlink(path string, buf []byte) (n int, errno int) { return 0, EWINDOWS }
-func Fchmod(fd int, mode uint32) (errno int) { return EWINDOWS }
+func Fchmod(fd Handle, mode uint32) (errno int) { return EWINDOWS }
func Chown(path string, uid int, gid int) (errno int) { return EWINDOWS }
func Lchown(path string, uid int, gid int) (errno int) { return EWINDOWS }
-func Fchown(fd int, uid int, gid int) (errno int) { return EWINDOWS }
+func Fchown(fd Handle, uid int, gid int) (errno int) { return EWINDOWS }
func Getuid() (uid int) { return -1 }
func Geteuid() (euid int) { return -1 }
@@ -723,11 +731,11 @@ func Getgroups() (gids []int, errno int) { return nil, EWINDOWS }
// TODO(brainman): fix all this meaningless code, it is here to compile exec.go
-func read(fd int, buf *byte, nbuf int) (n int, errno int) {
+func read(fd Handle, buf *byte, nbuf int) (n int, errno int) {
return 0, EWINDOWS
}
-func fcntl(fd, cmd, arg int) (val int, errno int) {
+func fcntl(fd Handle, cmd, arg int) (val int, errno int) {
return 0, EWINDOWS
}
diff --git a/src/pkg/syscall/syscall_windows_386.go b/src/pkg/syscall/syscall_windows_386.go
index 1ce025b31..61d2d8cb6 100644
--- a/src/pkg/syscall/syscall_windows_386.go
+++ b/src/pkg/syscall/syscall_windows_386.go
@@ -3,5 +3,3 @@
// license that can be found in the LICENSE file.
package syscall
-
-func Getpagesize() int { return 4096 }
diff --git a/src/pkg/syscall/syscall_windows_amd64.go b/src/pkg/syscall/syscall_windows_amd64.go
new file mode 100644
index 000000000..61d2d8cb6
--- /dev/null
+++ b/src/pkg/syscall/syscall_windows_amd64.go
@@ -0,0 +1,5 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go
index 964e58878..33cc7fd88 100644
--- a/src/pkg/syscall/zerrors_darwin_386.go
+++ b/src/pkg/syscall/zerrors_darwin_386.go
@@ -165,6 +165,13 @@ const (
EBUSY = 0x10
ECANCELED = 0x59
ECHILD = 0xa
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
ECONNABORTED = 0x35
ECONNREFUSED = 0x3d
ECONNRESET = 0x36
@@ -282,6 +289,9 @@ const (
EV_TRIGGER = 0x100
EWOULDBLOCK = 0x23
EXDEV = 0x12
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
F_ADDFILESIGS = 0x3d
@@ -996,6 +1006,75 @@ const (
TCP_NODELAY = 0x1
TCP_NOOPT = 0x8
TCP_NOPUSH = 0x4
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x40087458
+ TIOCDRAIN = 0x2000745e
+ TIOCDSIMICROCODE = 0x20007455
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGWINSZ = 0x40087468
+ TIOCIXOFF = 0x20007480
+ TIOCIXON = 0x20007481
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMODG = 0x40047403
+ TIOCMODS = 0x80047404
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTYGNAME = 0x40807453
+ TIOCPTYGRANT = 0x20007454
+ TIOCPTYUNLK = 0x20007452
+ TIOCREMOTE = 0x80047469
+ TIOCSBRK = 0x2000747b
+ TIOCSCONS = 0x20007463
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2000745f
+ TIOCSPGRP = 0x80047476
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40087459
+ TIOCUCNTL = 0x80047466
WCONTINUED = 0x10
WCOREFLAG = 0x80
WEXITED = 0x4
diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go
index adf039905..571ce907c 100644
--- a/src/pkg/syscall/zerrors_darwin_amd64.go
+++ b/src/pkg/syscall/zerrors_darwin_amd64.go
@@ -165,6 +165,13 @@ const (
EBUSY = 0x10
ECANCELED = 0x59
ECHILD = 0xa
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
ECONNABORTED = 0x35
ECONNREFUSED = 0x3d
ECONNRESET = 0x36
@@ -282,6 +289,9 @@ const (
EV_TRIGGER = 0x100
EWOULDBLOCK = 0x23
EXDEV = 0x12
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
F_ADDFILESIGS = 0x3d
@@ -996,6 +1006,75 @@ const (
TCP_NODELAY = 0x1
TCP_NOOPT = 0x8
TCP_NOPUSH = 0x4
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x40107458
+ TIOCDRAIN = 0x2000745e
+ TIOCDSIMICROCODE = 0x20007455
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x40487413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGWINSZ = 0x40087468
+ TIOCIXOFF = 0x20007480
+ TIOCIXON = 0x20007481
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMODG = 0x40047403
+ TIOCMODS = 0x80047404
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTYGNAME = 0x40807453
+ TIOCPTYGRANT = 0x20007454
+ TIOCPTYUNLK = 0x20007452
+ TIOCREMOTE = 0x80047469
+ TIOCSBRK = 0x2000747b
+ TIOCSCONS = 0x20007463
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x80487414
+ TIOCSETAF = 0x80487416
+ TIOCSETAW = 0x80487415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2000745f
+ TIOCSPGRP = 0x80047476
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40107459
+ TIOCUCNTL = 0x80047466
WCONTINUED = 0x10
WCOREFLAG = 0x80
WEXITED = 0x4
diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go
index 52e42487b..d045cab08 100644
--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -327,6 +327,13 @@ const (
EBUSY = 0x10
ECANCELED = 0x55
ECHILD = 0xa
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
ECONNABORTED = 0x35
ECONNREFUSED = 0x3d
ECONNRESET = 0x36
@@ -432,6 +439,9 @@ const (
EV_SYSFLAGS = 0xf000
EWOULDBLOCK = 0x23
EXDEV = 0x12
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
F_CANCEL = 0x5
@@ -1224,6 +1234,68 @@ const (
TCP_NODELAY = 0x1
TCP_NOOPT = 0x8
TCP_NOPUSH = 0x4
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGPTN = 0x4004740f
+ TIOCGSID = 0x40047463
+ TIOCGWINSZ = 0x40087468
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DCD = 0x40
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTMASTER = 0x2000741c
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2004745f
+ TIOCSPGRP = 0x80047476
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40087459
+ TIOCUCNTL = 0x80047466
WCONTINUED = 0x4
WCOREFLAG = 0x80
WLINUXCLONE = 0x80000000
diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go
index 9b632ba93..871b3818c 100644
--- a/src/pkg/syscall/zerrors_freebsd_amd64.go
+++ b/src/pkg/syscall/zerrors_freebsd_amd64.go
@@ -327,6 +327,13 @@ const (
EBUSY = 0x10
ECANCELED = 0x55
ECHILD = 0xa
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
ECONNABORTED = 0x35
ECONNREFUSED = 0x3d
ECONNRESET = 0x36
@@ -432,6 +439,9 @@ const (
EV_SYSFLAGS = 0xf000
EWOULDBLOCK = 0x23
EXDEV = 0x12
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
F_CANCEL = 0x5
@@ -1224,6 +1234,68 @@ const (
TCP_NODELAY = 0x1
TCP_NOOPT = 0x8
TCP_NOPUSH = 0x4
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGPTN = 0x4004740f
+ TIOCGSID = 0x40047463
+ TIOCGWINSZ = 0x40087468
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DCD = 0x40
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTMASTER = 0x2000741c
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2004745f
+ TIOCSPGRP = 0x80047476
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x40107459
+ TIOCUCNTL = 0x80047466
WCONTINUED = 0x4
WCOREFLAG = 0x80
WLINUXCLONE = 0x80000000
diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go
index 8d315813d..298754052 100644
--- a/src/pkg/syscall/zerrors_linux_386.go
+++ b/src/pkg/syscall/zerrors_linux_386.go
@@ -1104,6 +1104,69 @@ const (
TCP_QUICKACK = 0xc
TCP_SYNCNT = 0x7
TCP_WINDOW_CLAMP = 0xa
+ TIOCCBRK = 0x5428
+ TIOCCONS = 0x541d
+ TIOCEXCL = 0x540c
+ TIOCGETD = 0x5424
+ TIOCGHAYESESP = 0x545e
+ TIOCGICOUNT = 0x545d
+ TIOCGLCKTRMIOS = 0x5456
+ TIOCGPGRP = 0x540f
+ TIOCGPTN = 0x80045430
+ TIOCGRS485 = 0x542e
+ TIOCGSERIAL = 0x541e
+ TIOCGSID = 0x5429
+ TIOCGSOFTCAR = 0x5419
+ TIOCGWINSZ = 0x5413
+ TIOCINQ = 0x541b
+ TIOCLINUX = 0x541c
+ TIOCMBIC = 0x5417
+ TIOCMBIS = 0x5416
+ TIOCMGET = 0x5415
+ TIOCMIWAIT = 0x545c
+ TIOCMSET = 0x5418
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x5422
+ TIOCNXCL = 0x540d
+ TIOCOUTQ = 0x5411
+ TIOCPKT = 0x5420
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCSBRK = 0x5427
+ TIOCSCTTY = 0x540e
+ TIOCSERCONFIG = 0x5453
+ TIOCSERGETLSR = 0x5459
+ TIOCSERGETMULTI = 0x545a
+ TIOCSERGSTRUCT = 0x5458
+ TIOCSERGWILD = 0x5454
+ TIOCSERSETMULTI = 0x545b
+ TIOCSERSWILD = 0x5455
+ TIOCSER_TEMT = 0x1
+ TIOCSETD = 0x5423
+ TIOCSHAYESESP = 0x545f
+ TIOCSLCKTRMIOS = 0x5457
+ TIOCSPGRP = 0x5410
+ TIOCSPTLCK = 0x40045431
+ TIOCSRS485 = 0x542f
+ TIOCSSERIAL = 0x541f
+ TIOCSSOFTCAR = 0x541a
+ TIOCSTI = 0x5412
+ TIOCSWINSZ = 0x5414
TUNGETFEATURES = 0x800454cf
TUNGETIFF = 0x800454d2
TUNGETSNDBUF = 0x800454d3
diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go
index 8f9147818..728eefdde 100644
--- a/src/pkg/syscall/zerrors_linux_amd64.go
+++ b/src/pkg/syscall/zerrors_linux_amd64.go
@@ -1105,6 +1105,69 @@ const (
TCP_QUICKACK = 0xc
TCP_SYNCNT = 0x7
TCP_WINDOW_CLAMP = 0xa
+ TIOCCBRK = 0x5428
+ TIOCCONS = 0x541d
+ TIOCEXCL = 0x540c
+ TIOCGETD = 0x5424
+ TIOCGHAYESESP = 0x545e
+ TIOCGICOUNT = 0x545d
+ TIOCGLCKTRMIOS = 0x5456
+ TIOCGPGRP = 0x540f
+ TIOCGPTN = 0x80045430
+ TIOCGRS485 = 0x542e
+ TIOCGSERIAL = 0x541e
+ TIOCGSID = 0x5429
+ TIOCGSOFTCAR = 0x5419
+ TIOCGWINSZ = 0x5413
+ TIOCINQ = 0x541b
+ TIOCLINUX = 0x541c
+ TIOCMBIC = 0x5417
+ TIOCMBIS = 0x5416
+ TIOCMGET = 0x5415
+ TIOCMIWAIT = 0x545c
+ TIOCMSET = 0x5418
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x5422
+ TIOCNXCL = 0x540d
+ TIOCOUTQ = 0x5411
+ TIOCPKT = 0x5420
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCSBRK = 0x5427
+ TIOCSCTTY = 0x540e
+ TIOCSERCONFIG = 0x5453
+ TIOCSERGETLSR = 0x5459
+ TIOCSERGETMULTI = 0x545a
+ TIOCSERGSTRUCT = 0x5458
+ TIOCSERGWILD = 0x5454
+ TIOCSERSETMULTI = 0x545b
+ TIOCSERSWILD = 0x5455
+ TIOCSER_TEMT = 0x1
+ TIOCSETD = 0x5423
+ TIOCSHAYESESP = 0x545f
+ TIOCSLCKTRMIOS = 0x5457
+ TIOCSPGRP = 0x5410
+ TIOCSPTLCK = 0x40045431
+ TIOCSRS485 = 0x542f
+ TIOCSSERIAL = 0x541f
+ TIOCSSOFTCAR = 0x541a
+ TIOCSTI = 0x5412
+ TIOCSWINSZ = 0x5414
TUNGETFEATURES = 0x800454cf
TUNGETIFF = 0x800454d2
TUNGETSNDBUF = 0x800454d3
diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go
index 64a74c0ff..7d572712f 100644
--- a/src/pkg/syscall/zerrors_linux_arm.go
+++ b/src/pkg/syscall/zerrors_linux_arm.go
@@ -1098,6 +1098,65 @@ const (
TCP_QUICKACK = 0xc
TCP_SYNCNT = 0x7
TCP_WINDOW_CLAMP = 0xa
+ TIOCCBRK = 0x5428
+ TIOCCONS = 0x541d
+ TIOCEXCL = 0x540c
+ TIOCGETD = 0x5424
+ TIOCGICOUNT = 0x545d
+ TIOCGLCKTRMIOS = 0x5456
+ TIOCGPGRP = 0x540f
+ TIOCGPTN = 0x80045430
+ TIOCGSERIAL = 0x541e
+ TIOCGSID = 0x5429
+ TIOCGSOFTCAR = 0x5419
+ TIOCGWINSZ = 0x5413
+ TIOCINQ = 0x541b
+ TIOCLINUX = 0x541c
+ TIOCMBIC = 0x5417
+ TIOCMBIS = 0x5416
+ TIOCMGET = 0x5415
+ TIOCMIWAIT = 0x545c
+ TIOCMSET = 0x5418
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x5422
+ TIOCNXCL = 0x540d
+ TIOCOUTQ = 0x5411
+ TIOCPKT = 0x5420
+ TIOCPKT_DATA = 0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCSBRK = 0x5427
+ TIOCSCTTY = 0x540e
+ TIOCSERCONFIG = 0x5453
+ TIOCSERGETLSR = 0x5459
+ TIOCSERGETMULTI = 0x545a
+ TIOCSERGSTRUCT = 0x5458
+ TIOCSERGWILD = 0x5454
+ TIOCSERSETMULTI = 0x545b
+ TIOCSERSWILD = 0x5455
+ TIOCSER_TEMT = 0x1
+ TIOCSETD = 0x5423
+ TIOCSLCKTRMIOS = 0x5457
+ TIOCSPGRP = 0x5410
+ TIOCSPTLCK = 0x40045431
+ TIOCSSERIAL = 0x541f
+ TIOCSSOFTCAR = 0x541a
+ TIOCSTI = 0x5412
+ TIOCSWINSZ = 0x5414
TUNGETFEATURES = 0x800454cf
TUNGETIFF = 0x800454d2
TUNGETSNDBUF = 0x800454d3
diff --git a/src/pkg/syscall/zerrors_windows.go b/src/pkg/syscall/zerrors_windows.go
new file mode 100644
index 000000000..ae4506fac
--- /dev/null
+++ b/src/pkg/syscall/zerrors_windows.go
@@ -0,0 +1,283 @@
+// mkerrors_windows.sh -f -m32
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+// Go names for Windows errors.
+const (
+ ENOENT = ERROR_FILE_NOT_FOUND
+ ENOTDIR = ERROR_DIRECTORY
+)
+
+// Windows reserves errors >= 1<<29 for application use.
+const APPLICATION_ERROR = 1 << 29
+
+// Invented values to support what package os and others expects.
+const (
+ E2BIG = APPLICATION_ERROR + iota
+ EACCES
+ EADDRINUSE
+ EADDRNOTAVAIL
+ EADV
+ EAFNOSUPPORT
+ EAGAIN
+ EALREADY
+ EBADE
+ EBADF
+ EBADFD
+ EBADMSG
+ EBADR
+ EBADRQC
+ EBADSLT
+ EBFONT
+ EBUSY
+ ECANCELED
+ ECHILD
+ ECHRNG
+ ECOMM
+ ECONNABORTED
+ ECONNREFUSED
+ ECONNRESET
+ EDEADLK
+ EDEADLOCK
+ EDESTADDRREQ
+ EDOM
+ EDOTDOT
+ EDQUOT
+ EEXIST
+ EFAULT
+ EFBIG
+ EHOSTDOWN
+ EHOSTUNREACH
+ EIDRM
+ EILSEQ
+ EINPROGRESS
+ EINTR
+ EINVAL
+ EIO
+ EISCONN
+ EISDIR
+ EISNAM
+ EKEYEXPIRED
+ EKEYREJECTED
+ EKEYREVOKED
+ EL2HLT
+ EL2NSYNC
+ EL3HLT
+ EL3RST
+ ELIBACC
+ ELIBBAD
+ ELIBEXEC
+ ELIBMAX
+ ELIBSCN
+ ELNRNG
+ ELOOP
+ EMEDIUMTYPE
+ EMFILE
+ EMLINK
+ EMSGSIZE
+ EMULTIHOP
+ ENAMETOOLONG
+ ENAVAIL
+ ENETDOWN
+ ENETRESET
+ ENETUNREACH
+ ENFILE
+ ENOANO
+ ENOBUFS
+ ENOCSI
+ ENODATA
+ ENODEV
+ ENOEXEC
+ ENOKEY
+ ENOLCK
+ ENOLINK
+ ENOMEDIUM
+ ENOMEM
+ ENOMSG
+ ENONET
+ ENOPKG
+ ENOPROTOOPT
+ ENOSPC
+ ENOSR
+ ENOSTR
+ ENOSYS
+ ENOTBLK
+ ENOTCONN
+ ENOTEMPTY
+ ENOTNAM
+ ENOTRECOVERABLE
+ ENOTSOCK
+ ENOTSUP
+ ENOTTY
+ ENOTUNIQ
+ ENXIO
+ EOPNOTSUPP
+ EOVERFLOW
+ EOWNERDEAD
+ EPERM
+ EPFNOSUPPORT
+ EPIPE
+ EPROTO
+ EPROTONOSUPPORT
+ EPROTOTYPE
+ ERANGE
+ EREMCHG
+ EREMOTE
+ EREMOTEIO
+ ERESTART
+ EROFS
+ ESHUTDOWN
+ ESOCKTNOSUPPORT
+ ESPIPE
+ ESRCH
+ ESRMNT
+ ESTALE
+ ESTRPIPE
+ ETIME
+ ETIMEDOUT
+ ETOOMANYREFS
+ ETXTBSY
+ EUCLEAN
+ EUNATCH
+ EUSERS
+ EWOULDBLOCK
+ EXDEV
+ EXFULL
+ EWINDOWS
+)
+
+// Error strings for invented errors
+var errors = [...]string{
+ E2BIG - APPLICATION_ERROR: "argument list too long",
+ EACCES - APPLICATION_ERROR: "permission denied",
+ EADDRINUSE - APPLICATION_ERROR: "address already in use",
+ EADDRNOTAVAIL - APPLICATION_ERROR: "cannot assign requested address",
+ EADV - APPLICATION_ERROR: "advertise error",
+ EAFNOSUPPORT - APPLICATION_ERROR: "address family not supported by protocol",
+ EAGAIN - APPLICATION_ERROR: "resource temporarily unavailable",
+ EALREADY - APPLICATION_ERROR: "operation already in progress",
+ EBADE - APPLICATION_ERROR: "invalid exchange",
+ EBADF - APPLICATION_ERROR: "bad file descriptor",
+ EBADFD - APPLICATION_ERROR: "file descriptor in bad state",
+ EBADMSG - APPLICATION_ERROR: "bad message",
+ EBADR - APPLICATION_ERROR: "invalid request descriptor",
+ EBADRQC - APPLICATION_ERROR: "invalid request code",
+ EBADSLT - APPLICATION_ERROR: "invalid slot",
+ EBFONT - APPLICATION_ERROR: "bad font file format",
+ EBUSY - APPLICATION_ERROR: "device or resource busy",
+ ECANCELED - APPLICATION_ERROR: "operation canceled",
+ ECHILD - APPLICATION_ERROR: "no child processes",
+ ECHRNG - APPLICATION_ERROR: "channel number out of range",
+ ECOMM - APPLICATION_ERROR: "communication error on send",
+ ECONNABORTED - APPLICATION_ERROR: "software caused connection abort",
+ ECONNREFUSED - APPLICATION_ERROR: "connection refused",
+ ECONNRESET - APPLICATION_ERROR: "connection reset by peer",
+ EDEADLK - APPLICATION_ERROR: "resource deadlock avoided",
+ EDEADLOCK - APPLICATION_ERROR: "resource deadlock avoided",
+ EDESTADDRREQ - APPLICATION_ERROR: "destination address required",
+ EDOM - APPLICATION_ERROR: "numerical argument out of domain",
+ EDOTDOT - APPLICATION_ERROR: "RFS specific error",
+ EDQUOT - APPLICATION_ERROR: "disk quota exceeded",
+ EEXIST - APPLICATION_ERROR: "file exists",
+ EFAULT - APPLICATION_ERROR: "bad address",
+ EFBIG - APPLICATION_ERROR: "file too large",
+ EHOSTDOWN - APPLICATION_ERROR: "host is down",
+ EHOSTUNREACH - APPLICATION_ERROR: "no route to host",
+ EIDRM - APPLICATION_ERROR: "identifier removed",
+ EILSEQ - APPLICATION_ERROR: "invalid or incomplete multibyte or wide character",
+ EINPROGRESS - APPLICATION_ERROR: "operation now in progress",
+ EINTR - APPLICATION_ERROR: "interrupted system call",
+ EINVAL - APPLICATION_ERROR: "invalid argument",
+ EIO - APPLICATION_ERROR: "input/output error",
+ EISCONN - APPLICATION_ERROR: "transport endpoint is already connected",
+ EISDIR - APPLICATION_ERROR: "is a directory",
+ EISNAM - APPLICATION_ERROR: "is a named type file",
+ EKEYEXPIRED - APPLICATION_ERROR: "key has expired",
+ EKEYREJECTED - APPLICATION_ERROR: "key was rejected by service",
+ EKEYREVOKED - APPLICATION_ERROR: "key has been revoked",
+ EL2HLT - APPLICATION_ERROR: "level 2 halted",
+ EL2NSYNC - APPLICATION_ERROR: "level 2 not synchronized",
+ EL3HLT - APPLICATION_ERROR: "level 3 halted",
+ EL3RST - APPLICATION_ERROR: "level 3 reset",
+ ELIBACC - APPLICATION_ERROR: "can not access a needed shared library",
+ ELIBBAD - APPLICATION_ERROR: "accessing a corrupted shared library",
+ ELIBEXEC - APPLICATION_ERROR: "cannot exec a shared library directly",
+ ELIBMAX - APPLICATION_ERROR: "attempting to link in too many shared libraries",
+ ELIBSCN - APPLICATION_ERROR: ".lib section in a.out corrupted",
+ ELNRNG - APPLICATION_ERROR: "link number out of range",
+ ELOOP - APPLICATION_ERROR: "too many levels of symbolic links",
+ EMEDIUMTYPE - APPLICATION_ERROR: "wrong medium type",
+ EMFILE - APPLICATION_ERROR: "too many open files",
+ EMLINK - APPLICATION_ERROR: "too many links",
+ EMSGSIZE - APPLICATION_ERROR: "message too long",
+ EMULTIHOP - APPLICATION_ERROR: "multihop attempted",
+ ENAMETOOLONG - APPLICATION_ERROR: "file name too long",
+ ENAVAIL - APPLICATION_ERROR: "no XENIX semaphores available",
+ ENETDOWN - APPLICATION_ERROR: "network is down",
+ ENETRESET - APPLICATION_ERROR: "network dropped connection on reset",
+ ENETUNREACH - APPLICATION_ERROR: "network is unreachable",
+ ENFILE - APPLICATION_ERROR: "too many open files in system",
+ ENOANO - APPLICATION_ERROR: "no anode",
+ ENOBUFS - APPLICATION_ERROR: "no buffer space available",
+ ENOCSI - APPLICATION_ERROR: "no CSI structure available",
+ ENODATA - APPLICATION_ERROR: "no data available",
+ ENODEV - APPLICATION_ERROR: "no such device",
+ ENOEXEC - APPLICATION_ERROR: "exec format error",
+ ENOKEY - APPLICATION_ERROR: "required key not available",
+ ENOLCK - APPLICATION_ERROR: "no locks available",
+ ENOLINK - APPLICATION_ERROR: "link has been severed",
+ ENOMEDIUM - APPLICATION_ERROR: "no medium found",
+ ENOMEM - APPLICATION_ERROR: "cannot allocate memory",
+ ENOMSG - APPLICATION_ERROR: "no message of desired type",
+ ENONET - APPLICATION_ERROR: "machine is not on the network",
+ ENOPKG - APPLICATION_ERROR: "package not installed",
+ ENOPROTOOPT - APPLICATION_ERROR: "protocol not available",
+ ENOSPC - APPLICATION_ERROR: "no space left on device",
+ ENOSR - APPLICATION_ERROR: "out of streams resources",
+ ENOSTR - APPLICATION_ERROR: "device not a stream",
+ ENOSYS - APPLICATION_ERROR: "function not implemented",
+ ENOTBLK - APPLICATION_ERROR: "block device required",
+ ENOTCONN - APPLICATION_ERROR: "transport endpoint is not connected",
+ ENOTEMPTY - APPLICATION_ERROR: "directory not empty",
+ ENOTNAM - APPLICATION_ERROR: "not a XENIX named type file",
+ ENOTRECOVERABLE - APPLICATION_ERROR: "state not recoverable",
+ ENOTSOCK - APPLICATION_ERROR: "socket operation on non-socket",
+ ENOTSUP - APPLICATION_ERROR: "operation not supported",
+ ENOTTY - APPLICATION_ERROR: "inappropriate ioctl for device",
+ ENOTUNIQ - APPLICATION_ERROR: "name not unique on network",
+ ENXIO - APPLICATION_ERROR: "no such device or address",
+ EOPNOTSUPP - APPLICATION_ERROR: "operation not supported",
+ EOVERFLOW - APPLICATION_ERROR: "value too large for defined data type",
+ EOWNERDEAD - APPLICATION_ERROR: "owner died",
+ EPERM - APPLICATION_ERROR: "operation not permitted",
+ EPFNOSUPPORT - APPLICATION_ERROR: "protocol family not supported",
+ EPIPE - APPLICATION_ERROR: "broken pipe",
+ EPROTO - APPLICATION_ERROR: "protocol error",
+ EPROTONOSUPPORT - APPLICATION_ERROR: "protocol not supported",
+ EPROTOTYPE - APPLICATION_ERROR: "protocol wrong type for socket",
+ ERANGE - APPLICATION_ERROR: "numerical result out of range",
+ EREMCHG - APPLICATION_ERROR: "remote address changed",
+ EREMOTE - APPLICATION_ERROR: "object is remote",
+ EREMOTEIO - APPLICATION_ERROR: "remote I/O error",
+ ERESTART - APPLICATION_ERROR: "interrupted system call should be restarted",
+ EROFS - APPLICATION_ERROR: "read-only file system",
+ ESHUTDOWN - APPLICATION_ERROR: "cannot send after transport endpoint shutdown",
+ ESOCKTNOSUPPORT - APPLICATION_ERROR: "socket type not supported",
+ ESPIPE - APPLICATION_ERROR: "illegal seek",
+ ESRCH - APPLICATION_ERROR: "no such process",
+ ESRMNT - APPLICATION_ERROR: "srmount error",
+ ESTALE - APPLICATION_ERROR: "stale NFS file handle",
+ ESTRPIPE - APPLICATION_ERROR: "streams pipe error",
+ ETIME - APPLICATION_ERROR: "timer expired",
+ ETIMEDOUT - APPLICATION_ERROR: "connection timed out",
+ ETOOMANYREFS - APPLICATION_ERROR: "too many references: cannot splice",
+ ETXTBSY - APPLICATION_ERROR: "text file busy",
+ EUCLEAN - APPLICATION_ERROR: "structure needs cleaning",
+ EUNATCH - APPLICATION_ERROR: "protocol driver not attached",
+ EUSERS - APPLICATION_ERROR: "too many users",
+ EWOULDBLOCK - APPLICATION_ERROR: "resource temporarily unavailable",
+ EXDEV - APPLICATION_ERROR: "invalid cross-device link",
+ EXFULL - APPLICATION_ERROR: "exchange full",
+ EWINDOWS - APPLICATION_ERROR: "not supported by windows",
+}
diff --git a/src/pkg/syscall/zerrors_windows_386.go b/src/pkg/syscall/zerrors_windows_386.go
index ae4506fac..d1008bd03 100644
--- a/src/pkg/syscall/zerrors_windows_386.go
+++ b/src/pkg/syscall/zerrors_windows_386.go
@@ -1,283 +1,5 @@
-// mkerrors_windows.sh -f -m32
-// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// 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 syscall
-
-// Go names for Windows errors.
-const (
- ENOENT = ERROR_FILE_NOT_FOUND
- ENOTDIR = ERROR_DIRECTORY
-)
-
-// Windows reserves errors >= 1<<29 for application use.
-const APPLICATION_ERROR = 1 << 29
-
-// Invented values to support what package os and others expects.
-const (
- E2BIG = APPLICATION_ERROR + iota
- EACCES
- EADDRINUSE
- EADDRNOTAVAIL
- EADV
- EAFNOSUPPORT
- EAGAIN
- EALREADY
- EBADE
- EBADF
- EBADFD
- EBADMSG
- EBADR
- EBADRQC
- EBADSLT
- EBFONT
- EBUSY
- ECANCELED
- ECHILD
- ECHRNG
- ECOMM
- ECONNABORTED
- ECONNREFUSED
- ECONNRESET
- EDEADLK
- EDEADLOCK
- EDESTADDRREQ
- EDOM
- EDOTDOT
- EDQUOT
- EEXIST
- EFAULT
- EFBIG
- EHOSTDOWN
- EHOSTUNREACH
- EIDRM
- EILSEQ
- EINPROGRESS
- EINTR
- EINVAL
- EIO
- EISCONN
- EISDIR
- EISNAM
- EKEYEXPIRED
- EKEYREJECTED
- EKEYREVOKED
- EL2HLT
- EL2NSYNC
- EL3HLT
- EL3RST
- ELIBACC
- ELIBBAD
- ELIBEXEC
- ELIBMAX
- ELIBSCN
- ELNRNG
- ELOOP
- EMEDIUMTYPE
- EMFILE
- EMLINK
- EMSGSIZE
- EMULTIHOP
- ENAMETOOLONG
- ENAVAIL
- ENETDOWN
- ENETRESET
- ENETUNREACH
- ENFILE
- ENOANO
- ENOBUFS
- ENOCSI
- ENODATA
- ENODEV
- ENOEXEC
- ENOKEY
- ENOLCK
- ENOLINK
- ENOMEDIUM
- ENOMEM
- ENOMSG
- ENONET
- ENOPKG
- ENOPROTOOPT
- ENOSPC
- ENOSR
- ENOSTR
- ENOSYS
- ENOTBLK
- ENOTCONN
- ENOTEMPTY
- ENOTNAM
- ENOTRECOVERABLE
- ENOTSOCK
- ENOTSUP
- ENOTTY
- ENOTUNIQ
- ENXIO
- EOPNOTSUPP
- EOVERFLOW
- EOWNERDEAD
- EPERM
- EPFNOSUPPORT
- EPIPE
- EPROTO
- EPROTONOSUPPORT
- EPROTOTYPE
- ERANGE
- EREMCHG
- EREMOTE
- EREMOTEIO
- ERESTART
- EROFS
- ESHUTDOWN
- ESOCKTNOSUPPORT
- ESPIPE
- ESRCH
- ESRMNT
- ESTALE
- ESTRPIPE
- ETIME
- ETIMEDOUT
- ETOOMANYREFS
- ETXTBSY
- EUCLEAN
- EUNATCH
- EUSERS
- EWOULDBLOCK
- EXDEV
- EXFULL
- EWINDOWS
-)
-
-// Error strings for invented errors
-var errors = [...]string{
- E2BIG - APPLICATION_ERROR: "argument list too long",
- EACCES - APPLICATION_ERROR: "permission denied",
- EADDRINUSE - APPLICATION_ERROR: "address already in use",
- EADDRNOTAVAIL - APPLICATION_ERROR: "cannot assign requested address",
- EADV - APPLICATION_ERROR: "advertise error",
- EAFNOSUPPORT - APPLICATION_ERROR: "address family not supported by protocol",
- EAGAIN - APPLICATION_ERROR: "resource temporarily unavailable",
- EALREADY - APPLICATION_ERROR: "operation already in progress",
- EBADE - APPLICATION_ERROR: "invalid exchange",
- EBADF - APPLICATION_ERROR: "bad file descriptor",
- EBADFD - APPLICATION_ERROR: "file descriptor in bad state",
- EBADMSG - APPLICATION_ERROR: "bad message",
- EBADR - APPLICATION_ERROR: "invalid request descriptor",
- EBADRQC - APPLICATION_ERROR: "invalid request code",
- EBADSLT - APPLICATION_ERROR: "invalid slot",
- EBFONT - APPLICATION_ERROR: "bad font file format",
- EBUSY - APPLICATION_ERROR: "device or resource busy",
- ECANCELED - APPLICATION_ERROR: "operation canceled",
- ECHILD - APPLICATION_ERROR: "no child processes",
- ECHRNG - APPLICATION_ERROR: "channel number out of range",
- ECOMM - APPLICATION_ERROR: "communication error on send",
- ECONNABORTED - APPLICATION_ERROR: "software caused connection abort",
- ECONNREFUSED - APPLICATION_ERROR: "connection refused",
- ECONNRESET - APPLICATION_ERROR: "connection reset by peer",
- EDEADLK - APPLICATION_ERROR: "resource deadlock avoided",
- EDEADLOCK - APPLICATION_ERROR: "resource deadlock avoided",
- EDESTADDRREQ - APPLICATION_ERROR: "destination address required",
- EDOM - APPLICATION_ERROR: "numerical argument out of domain",
- EDOTDOT - APPLICATION_ERROR: "RFS specific error",
- EDQUOT - APPLICATION_ERROR: "disk quota exceeded",
- EEXIST - APPLICATION_ERROR: "file exists",
- EFAULT - APPLICATION_ERROR: "bad address",
- EFBIG - APPLICATION_ERROR: "file too large",
- EHOSTDOWN - APPLICATION_ERROR: "host is down",
- EHOSTUNREACH - APPLICATION_ERROR: "no route to host",
- EIDRM - APPLICATION_ERROR: "identifier removed",
- EILSEQ - APPLICATION_ERROR: "invalid or incomplete multibyte or wide character",
- EINPROGRESS - APPLICATION_ERROR: "operation now in progress",
- EINTR - APPLICATION_ERROR: "interrupted system call",
- EINVAL - APPLICATION_ERROR: "invalid argument",
- EIO - APPLICATION_ERROR: "input/output error",
- EISCONN - APPLICATION_ERROR: "transport endpoint is already connected",
- EISDIR - APPLICATION_ERROR: "is a directory",
- EISNAM - APPLICATION_ERROR: "is a named type file",
- EKEYEXPIRED - APPLICATION_ERROR: "key has expired",
- EKEYREJECTED - APPLICATION_ERROR: "key was rejected by service",
- EKEYREVOKED - APPLICATION_ERROR: "key has been revoked",
- EL2HLT - APPLICATION_ERROR: "level 2 halted",
- EL2NSYNC - APPLICATION_ERROR: "level 2 not synchronized",
- EL3HLT - APPLICATION_ERROR: "level 3 halted",
- EL3RST - APPLICATION_ERROR: "level 3 reset",
- ELIBACC - APPLICATION_ERROR: "can not access a needed shared library",
- ELIBBAD - APPLICATION_ERROR: "accessing a corrupted shared library",
- ELIBEXEC - APPLICATION_ERROR: "cannot exec a shared library directly",
- ELIBMAX - APPLICATION_ERROR: "attempting to link in too many shared libraries",
- ELIBSCN - APPLICATION_ERROR: ".lib section in a.out corrupted",
- ELNRNG - APPLICATION_ERROR: "link number out of range",
- ELOOP - APPLICATION_ERROR: "too many levels of symbolic links",
- EMEDIUMTYPE - APPLICATION_ERROR: "wrong medium type",
- EMFILE - APPLICATION_ERROR: "too many open files",
- EMLINK - APPLICATION_ERROR: "too many links",
- EMSGSIZE - APPLICATION_ERROR: "message too long",
- EMULTIHOP - APPLICATION_ERROR: "multihop attempted",
- ENAMETOOLONG - APPLICATION_ERROR: "file name too long",
- ENAVAIL - APPLICATION_ERROR: "no XENIX semaphores available",
- ENETDOWN - APPLICATION_ERROR: "network is down",
- ENETRESET - APPLICATION_ERROR: "network dropped connection on reset",
- ENETUNREACH - APPLICATION_ERROR: "network is unreachable",
- ENFILE - APPLICATION_ERROR: "too many open files in system",
- ENOANO - APPLICATION_ERROR: "no anode",
- ENOBUFS - APPLICATION_ERROR: "no buffer space available",
- ENOCSI - APPLICATION_ERROR: "no CSI structure available",
- ENODATA - APPLICATION_ERROR: "no data available",
- ENODEV - APPLICATION_ERROR: "no such device",
- ENOEXEC - APPLICATION_ERROR: "exec format error",
- ENOKEY - APPLICATION_ERROR: "required key not available",
- ENOLCK - APPLICATION_ERROR: "no locks available",
- ENOLINK - APPLICATION_ERROR: "link has been severed",
- ENOMEDIUM - APPLICATION_ERROR: "no medium found",
- ENOMEM - APPLICATION_ERROR: "cannot allocate memory",
- ENOMSG - APPLICATION_ERROR: "no message of desired type",
- ENONET - APPLICATION_ERROR: "machine is not on the network",
- ENOPKG - APPLICATION_ERROR: "package not installed",
- ENOPROTOOPT - APPLICATION_ERROR: "protocol not available",
- ENOSPC - APPLICATION_ERROR: "no space left on device",
- ENOSR - APPLICATION_ERROR: "out of streams resources",
- ENOSTR - APPLICATION_ERROR: "device not a stream",
- ENOSYS - APPLICATION_ERROR: "function not implemented",
- ENOTBLK - APPLICATION_ERROR: "block device required",
- ENOTCONN - APPLICATION_ERROR: "transport endpoint is not connected",
- ENOTEMPTY - APPLICATION_ERROR: "directory not empty",
- ENOTNAM - APPLICATION_ERROR: "not a XENIX named type file",
- ENOTRECOVERABLE - APPLICATION_ERROR: "state not recoverable",
- ENOTSOCK - APPLICATION_ERROR: "socket operation on non-socket",
- ENOTSUP - APPLICATION_ERROR: "operation not supported",
- ENOTTY - APPLICATION_ERROR: "inappropriate ioctl for device",
- ENOTUNIQ - APPLICATION_ERROR: "name not unique on network",
- ENXIO - APPLICATION_ERROR: "no such device or address",
- EOPNOTSUPP - APPLICATION_ERROR: "operation not supported",
- EOVERFLOW - APPLICATION_ERROR: "value too large for defined data type",
- EOWNERDEAD - APPLICATION_ERROR: "owner died",
- EPERM - APPLICATION_ERROR: "operation not permitted",
- EPFNOSUPPORT - APPLICATION_ERROR: "protocol family not supported",
- EPIPE - APPLICATION_ERROR: "broken pipe",
- EPROTO - APPLICATION_ERROR: "protocol error",
- EPROTONOSUPPORT - APPLICATION_ERROR: "protocol not supported",
- EPROTOTYPE - APPLICATION_ERROR: "protocol wrong type for socket",
- ERANGE - APPLICATION_ERROR: "numerical result out of range",
- EREMCHG - APPLICATION_ERROR: "remote address changed",
- EREMOTE - APPLICATION_ERROR: "object is remote",
- EREMOTEIO - APPLICATION_ERROR: "remote I/O error",
- ERESTART - APPLICATION_ERROR: "interrupted system call should be restarted",
- EROFS - APPLICATION_ERROR: "read-only file system",
- ESHUTDOWN - APPLICATION_ERROR: "cannot send after transport endpoint shutdown",
- ESOCKTNOSUPPORT - APPLICATION_ERROR: "socket type not supported",
- ESPIPE - APPLICATION_ERROR: "illegal seek",
- ESRCH - APPLICATION_ERROR: "no such process",
- ESRMNT - APPLICATION_ERROR: "srmount error",
- ESTALE - APPLICATION_ERROR: "stale NFS file handle",
- ESTRPIPE - APPLICATION_ERROR: "streams pipe error",
- ETIME - APPLICATION_ERROR: "timer expired",
- ETIMEDOUT - APPLICATION_ERROR: "connection timed out",
- ETOOMANYREFS - APPLICATION_ERROR: "too many references: cannot splice",
- ETXTBSY - APPLICATION_ERROR: "text file busy",
- EUCLEAN - APPLICATION_ERROR: "structure needs cleaning",
- EUNATCH - APPLICATION_ERROR: "protocol driver not attached",
- EUSERS - APPLICATION_ERROR: "too many users",
- EWOULDBLOCK - APPLICATION_ERROR: "resource temporarily unavailable",
- EXDEV - APPLICATION_ERROR: "invalid cross-device link",
- EXFULL - APPLICATION_ERROR: "exchange full",
- EWINDOWS - APPLICATION_ERROR: "not supported by windows",
-}
diff --git a/src/pkg/syscall/zerrors_windows_amd64.go b/src/pkg/syscall/zerrors_windows_amd64.go
new file mode 100644
index 000000000..d1008bd03
--- /dev/null
+++ b/src/pkg/syscall/zerrors_windows_amd64.go
@@ -0,0 +1,5 @@
+// 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 syscall
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index fd28d338c..350ad232a 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -1,4 +1,4 @@
-// mksyscall_windows.pl
+// mksyscall_windows.pl -l32 syscall_windows.go syscall_windows_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
@@ -112,9 +112,9 @@ func GetLastError() (lasterrno int) {
return
}
-func LoadLibrary(libname string) (handle uint32, errno int) {
+func LoadLibrary(libname string) (handle Handle, errno int) {
r0, _, e1 := Syscall(procLoadLibraryW, 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0)
- handle = uint32(r0)
+ handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
errno = int(e1)
@@ -127,7 +127,7 @@ func LoadLibrary(libname string) (handle uint32, errno int) {
return
}
-func FreeLibrary(handle uint32) (errno int) {
+func FreeLibrary(handle Handle) (errno int) {
r1, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -141,9 +141,9 @@ func FreeLibrary(handle uint32) (errno int) {
return
}
-func GetProcAddress(module uint32, procname string) (proc uint32, errno int) {
+func GetProcAddress(module Handle, procname string) (proc Handle, errno int) {
r0, _, e1 := Syscall(procGetProcAddress, 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
- proc = uint32(r0)
+ proc = Handle(r0)
if proc == 0 {
if e1 != 0 {
errno = int(e1)
@@ -195,10 +195,10 @@ func ExitProcess(exitcode uint32) {
return
}
-func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) {
+func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) {
r0, _, e1 := Syscall9(procCreateFileW, 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
- handle = int32(r0)
- if handle == -1 {
+ handle = Handle(r0)
+ if handle == InvalidHandle {
if e1 != 0 {
errno = int(e1)
} else {
@@ -210,7 +210,7 @@ func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes
return
}
-func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
+func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
var _p0 *byte
if len(buf) > 0 {
_p0 = &buf[0]
@@ -228,7 +228,7 @@ func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (e
return
}
-func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
+func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
var _p0 *byte
if len(buf) > 0 {
_p0 = &buf[0]
@@ -246,7 +246,7 @@ func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (
return
}
-func SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) {
+func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) {
r0, _, e1 := Syscall6(procSetFilePointer, 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0)
newlowoffset = uint32(r0)
if newlowoffset == 0xffffffff {
@@ -261,7 +261,7 @@ func SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence
return
}
-func CloseHandle(handle int32) (errno int) {
+func CloseHandle(handle Handle) (errno int) {
r1, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -275,10 +275,10 @@ func CloseHandle(handle int32) (errno int) {
return
}
-func GetStdHandle(stdhandle int32) (handle int32, errno int) {
+func GetStdHandle(stdhandle int) (handle Handle, errno int) {
r0, _, e1 := Syscall(procGetStdHandle, 1, uintptr(stdhandle), 0, 0)
- handle = int32(r0)
- if handle == -1 {
+ handle = Handle(r0)
+ if handle == InvalidHandle {
if e1 != 0 {
errno = int(e1)
} else {
@@ -290,10 +290,10 @@ func GetStdHandle(stdhandle int32) (handle int32, errno int) {
return
}
-func FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) {
+func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) {
r0, _, e1 := Syscall(procFindFirstFileW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0)
- handle = int32(r0)
- if handle == -1 {
+ handle = Handle(r0)
+ if handle == InvalidHandle {
if e1 != 0 {
errno = int(e1)
} else {
@@ -305,7 +305,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int)
return
}
-func FindNextFile(handle int32, data *Win32finddata) (errno int) {
+func FindNextFile(handle Handle, data *Win32finddata) (errno int) {
r1, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -319,7 +319,7 @@ func FindNextFile(handle int32, data *Win32finddata) (errno int) {
return
}
-func FindClose(handle int32) (errno int) {
+func FindClose(handle Handle) (errno int) {
r1, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -333,7 +333,7 @@ func FindClose(handle int32) (errno int) {
return
}
-func GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int) {
+func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int) {
r1, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -446,7 +446,7 @@ func GetComputerName(buf *uint16, n *uint32) (errno int) {
return
}
-func SetEndOfFile(handle int32) (errno int) {
+func SetEndOfFile(handle Handle) (errno int) {
r1, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -485,9 +485,9 @@ func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) {
return
}
-func CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int) {
+func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int) {
r0, _, e1 := Syscall6(procCreateIoCompletionPort, 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0)
- handle = int32(r0)
+ handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
errno = int(e1)
@@ -500,7 +500,7 @@ func CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, thread
return
}
-func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) {
+func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) {
r1, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -514,7 +514,7 @@ func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlap
return
}
-func CancelIo(s uint32) (errno int) {
+func CancelIo(s Handle) (errno int) {
r1, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -548,7 +548,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA
return
}
-func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) {
+func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int) {
var _p0 uint32
if inheritHandle {
_p0 = 1
@@ -556,7 +556,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno
_p0 = 0
}
r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid))
- handle = int32(r0)
+ handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
errno = int(e1)
@@ -569,7 +569,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno
return
}
-func TerminateProcess(handle int32, exitcode uint32) (errno int) {
+func TerminateProcess(handle Handle, exitcode uint32) (errno int) {
r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -583,7 +583,7 @@ func TerminateProcess(handle int32, exitcode uint32) (errno int) {
return
}
-func GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) {
+func GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int) {
r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -611,9 +611,9 @@ func GetStartupInfo(startupInfo *StartupInfo) (errno int) {
return
}
-func GetCurrentProcess() (pseudoHandle int32, errno int) {
+func GetCurrentProcess() (pseudoHandle Handle, errno int) {
r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0)
- pseudoHandle = int32(r0)
+ pseudoHandle = Handle(r0)
if pseudoHandle == 0 {
if e1 != 0 {
errno = int(e1)
@@ -626,7 +626,7 @@ func GetCurrentProcess() (pseudoHandle int32, errno int) {
return
}
-func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) {
+func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) {
var _p0 uint32
if bInheritHandle {
_p0 = 1
@@ -646,7 +646,7 @@ func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetPro
return
}
-func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) {
+func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) {
r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0)
event = uint32(r0)
if event == 0xffffffff {
@@ -676,7 +676,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) {
return
}
-func CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int) {
+func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int) {
r1, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -690,7 +690,7 @@ func CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes,
return
}
-func GetFileType(filehandle uint32) (n uint32, errno int) {
+func GetFileType(filehandle Handle) (n uint32, errno int) {
r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0)
n = uint32(r0)
if n == 0 {
@@ -705,7 +705,7 @@ func GetFileType(filehandle uint32) (n uint32, errno int) {
return
}
-func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) {
+func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) {
r1, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -719,7 +719,7 @@ func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16
return
}
-func CryptReleaseContext(provhandle uint32, flags uint32) (errno int) {
+func CryptReleaseContext(provhandle Handle, flags uint32) (errno int) {
r1, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -733,7 +733,7 @@ func CryptReleaseContext(provhandle uint32, flags uint32) (errno int) {
return
}
-func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) {
+func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) {
r1, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
if int(r1) == 0 {
if e1 != 0 {
@@ -805,7 +805,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (errno int) {
return
}
-func SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) {
+func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) {
r1, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -869,9 +869,9 @@ func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err
return
}
-func LocalFree(hmem uint32) (handle uint32, errno int) {
+func LocalFree(hmem Handle) (handle Handle, errno int) {
r0, _, e1 := Syscall(procLocalFree, 1, uintptr(hmem), 0, 0)
- handle = uint32(r0)
+ handle = Handle(r0)
if handle != 0 {
if e1 != 0 {
errno = int(e1)
@@ -884,7 +884,7 @@ func LocalFree(hmem uint32) (handle uint32, errno int) {
return
}
-func SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) {
+func SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int) {
r1, _, e1 := Syscall(procSetHandleInformation, 3, uintptr(handle), uintptr(mask), uintptr(flags))
if int(r1) == 0 {
if e1 != 0 {
@@ -898,7 +898,7 @@ func SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) {
return
}
-func FlushFileBuffers(handle int32) (errno int) {
+func FlushFileBuffers(handle Handle) (errno int) {
r1, _, e1 := Syscall(procFlushFileBuffers, 1, uintptr(handle), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -927,9 +927,9 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (
return
}
-func CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle int32, errno int) {
+func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) {
r0, _, e1 := Syscall6(procCreateFileMappingW, 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name)))
- handle = int32(r0)
+ handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
errno = int(e1)
@@ -942,7 +942,7 @@ func CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSi
return
}
-func MapViewOfFile(handle int32, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) {
+func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) {
r0, _, e1 := Syscall6(procMapViewOfFile, 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0)
addr = uintptr(r0)
if addr == 0 {
@@ -1013,7 +1013,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (errno int) {
return
}
-func TransmitFile(s int32, handle int32, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) {
+func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) {
r1, _, e1 := Syscall9(procTransmitFile, 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -1047,7 +1047,7 @@ func WSACleanup() (errno int) {
return
}
-func WSAIoctl(s int32, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) {
+func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) {
r1, _, e1 := Syscall9(procWSAIoctl, 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
if int(r1) == -1 {
if e1 != 0 {
@@ -1061,10 +1061,10 @@ func WSAIoctl(s int32, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob
return
}
-func socket(af int32, typ int32, protocol int32) (handle int32, errno int) {
+func socket(af int32, typ int32, protocol int32) (handle Handle, errno int) {
r0, _, e1 := Syscall(procsocket, 3, uintptr(af), uintptr(typ), uintptr(protocol))
- handle = int32(r0)
- if handle == -1 {
+ handle = Handle(r0)
+ if handle == InvalidHandle {
if e1 != 0 {
errno = int(e1)
} else {
@@ -1076,7 +1076,7 @@ func socket(af int32, typ int32, protocol int32) (handle int32, errno int) {
return
}
-func setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32) (errno int) {
+func setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) {
r1, _, e1 := Syscall6(procsetsockopt, 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1090,7 +1090,7 @@ func setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32)
return
}
-func bind(s int32, name uintptr, namelen int32) (errno int) {
+func bind(s Handle, name uintptr, namelen int32) (errno int) {
r1, _, e1 := Syscall(procbind, 3, uintptr(s), uintptr(name), uintptr(namelen))
if int(r1) == -1 {
if e1 != 0 {
@@ -1104,7 +1104,7 @@ func bind(s int32, name uintptr, namelen int32) (errno int) {
return
}
-func connect(s int32, name uintptr, namelen int32) (errno int) {
+func connect(s Handle, name uintptr, namelen int32) (errno int) {
r1, _, e1 := Syscall(procconnect, 3, uintptr(s), uintptr(name), uintptr(namelen))
if int(r1) == -1 {
if e1 != 0 {
@@ -1118,7 +1118,7 @@ func connect(s int32, name uintptr, namelen int32) (errno int) {
return
}
-func getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
+func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
r1, _, e1 := Syscall(procgetsockname, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
if int(r1) == -1 {
if e1 != 0 {
@@ -1132,7 +1132,7 @@ func getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
return
}
-func getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
+func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
r1, _, e1 := Syscall(procgetpeername, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
if int(r1) == -1 {
if e1 != 0 {
@@ -1146,7 +1146,7 @@ func getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
return
}
-func listen(s int32, backlog int32) (errno int) {
+func listen(s Handle, backlog int32) (errno int) {
r1, _, e1 := Syscall(proclisten, 2, uintptr(s), uintptr(backlog), 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1160,7 +1160,7 @@ func listen(s int32, backlog int32) (errno int) {
return
}
-func shutdown(s int32, how int32) (errno int) {
+func shutdown(s Handle, how int32) (errno int) {
r1, _, e1 := Syscall(procshutdown, 2, uintptr(s), uintptr(how), 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1174,7 +1174,7 @@ func shutdown(s int32, how int32) (errno int) {
return
}
-func Closesocket(s int32) (errno int) {
+func Closesocket(s Handle) (errno int) {
r1, _, e1 := Syscall(procclosesocket, 1, uintptr(s), 0, 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1188,7 +1188,7 @@ func Closesocket(s int32) (errno int) {
return
}
-func AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) {
+func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) {
r1, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
if int(r1) == 0 {
if e1 != 0 {
@@ -1207,7 +1207,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen
return
}
-func WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) {
+func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) {
r1, _, e1 := Syscall9(procWSARecv, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1221,7 +1221,7 @@ func WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32
return
}
-func WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) {
+func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) {
r1, _, e1 := Syscall9(procWSASend, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
if int(r1) == -1 {
if e1 != 0 {
@@ -1235,7 +1235,7 @@ func WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32,
return
}
-func WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) {
+func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) {
r1, _, e1 := Syscall9(procWSARecvFrom, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
if int(r1) == -1 {
if e1 != 0 {
@@ -1249,7 +1249,7 @@ func WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui
return
}
-func WSASendTo(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) {
+func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) {
r1, _, e1 := Syscall9(procWSASendTo, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
if int(r1) == -1 {
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
new file mode 100644
index 000000000..e7d09fbc2
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -0,0 +1,1323 @@
+// mksyscall_windows.pl syscall_windows.go syscall_windows_amd64.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+var (
+ modkernel32 = loadDll("kernel32.dll")
+ modadvapi32 = loadDll("advapi32.dll")
+ modshell32 = loadDll("shell32.dll")
+ modwsock32 = loadDll("wsock32.dll")
+ modws2_32 = loadDll("ws2_32.dll")
+ moddnsapi = loadDll("dnsapi.dll")
+ modiphlpapi = loadDll("iphlpapi.dll")
+
+ procGetLastError = getSysProcAddr(modkernel32, "GetLastError")
+ procLoadLibraryW = getSysProcAddr(modkernel32, "LoadLibraryW")
+ procFreeLibrary = getSysProcAddr(modkernel32, "FreeLibrary")
+ procGetProcAddress = getSysProcAddr(modkernel32, "GetProcAddress")
+ procGetVersion = getSysProcAddr(modkernel32, "GetVersion")
+ procFormatMessageW = getSysProcAddr(modkernel32, "FormatMessageW")
+ procExitProcess = getSysProcAddr(modkernel32, "ExitProcess")
+ procCreateFileW = getSysProcAddr(modkernel32, "CreateFileW")
+ procReadFile = getSysProcAddr(modkernel32, "ReadFile")
+ procWriteFile = getSysProcAddr(modkernel32, "WriteFile")
+ procSetFilePointer = getSysProcAddr(modkernel32, "SetFilePointer")
+ procCloseHandle = getSysProcAddr(modkernel32, "CloseHandle")
+ procGetStdHandle = getSysProcAddr(modkernel32, "GetStdHandle")
+ procFindFirstFileW = getSysProcAddr(modkernel32, "FindFirstFileW")
+ procFindNextFileW = getSysProcAddr(modkernel32, "FindNextFileW")
+ procFindClose = getSysProcAddr(modkernel32, "FindClose")
+ procGetFileInformationByHandle = getSysProcAddr(modkernel32, "GetFileInformationByHandle")
+ procGetCurrentDirectoryW = getSysProcAddr(modkernel32, "GetCurrentDirectoryW")
+ procSetCurrentDirectoryW = getSysProcAddr(modkernel32, "SetCurrentDirectoryW")
+ procCreateDirectoryW = getSysProcAddr(modkernel32, "CreateDirectoryW")
+ procRemoveDirectoryW = getSysProcAddr(modkernel32, "RemoveDirectoryW")
+ procDeleteFileW = getSysProcAddr(modkernel32, "DeleteFileW")
+ procMoveFileW = getSysProcAddr(modkernel32, "MoveFileW")
+ procGetComputerNameW = getSysProcAddr(modkernel32, "GetComputerNameW")
+ procSetEndOfFile = getSysProcAddr(modkernel32, "SetEndOfFile")
+ procGetSystemTimeAsFileTime = getSysProcAddr(modkernel32, "GetSystemTimeAsFileTime")
+ procSleep = getSysProcAddr(modkernel32, "Sleep")
+ procGetTimeZoneInformation = getSysProcAddr(modkernel32, "GetTimeZoneInformation")
+ procCreateIoCompletionPort = getSysProcAddr(modkernel32, "CreateIoCompletionPort")
+ procGetQueuedCompletionStatus = getSysProcAddr(modkernel32, "GetQueuedCompletionStatus")
+ procCancelIo = getSysProcAddr(modkernel32, "CancelIo")
+ procCreateProcessW = getSysProcAddr(modkernel32, "CreateProcessW")
+ procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess")
+ procTerminateProcess = getSysProcAddr(modkernel32, "TerminateProcess")
+ procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess")
+ procGetStartupInfoW = getSysProcAddr(modkernel32, "GetStartupInfoW")
+ procGetCurrentProcess = getSysProcAddr(modkernel32, "GetCurrentProcess")
+ procDuplicateHandle = getSysProcAddr(modkernel32, "DuplicateHandle")
+ procWaitForSingleObject = getSysProcAddr(modkernel32, "WaitForSingleObject")
+ procGetTempPathW = getSysProcAddr(modkernel32, "GetTempPathW")
+ procCreatePipe = getSysProcAddr(modkernel32, "CreatePipe")
+ procGetFileType = getSysProcAddr(modkernel32, "GetFileType")
+ procCryptAcquireContextW = getSysProcAddr(modadvapi32, "CryptAcquireContextW")
+ procCryptReleaseContext = getSysProcAddr(modadvapi32, "CryptReleaseContext")
+ procCryptGenRandom = getSysProcAddr(modadvapi32, "CryptGenRandom")
+ procGetEnvironmentStringsW = getSysProcAddr(modkernel32, "GetEnvironmentStringsW")
+ procFreeEnvironmentStringsW = getSysProcAddr(modkernel32, "FreeEnvironmentStringsW")
+ procGetEnvironmentVariableW = getSysProcAddr(modkernel32, "GetEnvironmentVariableW")
+ procSetEnvironmentVariableW = getSysProcAddr(modkernel32, "SetEnvironmentVariableW")
+ procSetFileTime = getSysProcAddr(modkernel32, "SetFileTime")
+ procGetFileAttributesW = getSysProcAddr(modkernel32, "GetFileAttributesW")
+ procSetFileAttributesW = getSysProcAddr(modkernel32, "SetFileAttributesW")
+ procGetCommandLineW = getSysProcAddr(modkernel32, "GetCommandLineW")
+ procCommandLineToArgvW = getSysProcAddr(modshell32, "CommandLineToArgvW")
+ procLocalFree = getSysProcAddr(modkernel32, "LocalFree")
+ procSetHandleInformation = getSysProcAddr(modkernel32, "SetHandleInformation")
+ procFlushFileBuffers = getSysProcAddr(modkernel32, "FlushFileBuffers")
+ procGetFullPathNameW = getSysProcAddr(modkernel32, "GetFullPathNameW")
+ procCreateFileMappingW = getSysProcAddr(modkernel32, "CreateFileMappingW")
+ procMapViewOfFile = getSysProcAddr(modkernel32, "MapViewOfFile")
+ procUnmapViewOfFile = getSysProcAddr(modkernel32, "UnmapViewOfFile")
+ procFlushViewOfFile = getSysProcAddr(modkernel32, "FlushViewOfFile")
+ procVirtualLock = getSysProcAddr(modkernel32, "VirtualLock")
+ procVirtualUnlock = getSysProcAddr(modkernel32, "VirtualUnlock")
+ procTransmitFile = getSysProcAddr(modwsock32, "TransmitFile")
+ procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup")
+ procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup")
+ procWSAIoctl = getSysProcAddr(modws2_32, "WSAIoctl")
+ procsocket = getSysProcAddr(modwsock32, "socket")
+ procsetsockopt = getSysProcAddr(modwsock32, "setsockopt")
+ procbind = getSysProcAddr(modwsock32, "bind")
+ procconnect = getSysProcAddr(modwsock32, "connect")
+ procgetsockname = getSysProcAddr(modwsock32, "getsockname")
+ procgetpeername = getSysProcAddr(modwsock32, "getpeername")
+ proclisten = getSysProcAddr(modwsock32, "listen")
+ procshutdown = getSysProcAddr(modwsock32, "shutdown")
+ procclosesocket = getSysProcAddr(modwsock32, "closesocket")
+ procAcceptEx = getSysProcAddr(modwsock32, "AcceptEx")
+ procGetAcceptExSockaddrs = getSysProcAddr(modwsock32, "GetAcceptExSockaddrs")
+ procWSARecv = getSysProcAddr(modws2_32, "WSARecv")
+ procWSASend = getSysProcAddr(modws2_32, "WSASend")
+ procWSARecvFrom = getSysProcAddr(modws2_32, "WSARecvFrom")
+ procWSASendTo = getSysProcAddr(modws2_32, "WSASendTo")
+ procgethostbyname = getSysProcAddr(modws2_32, "gethostbyname")
+ procgetservbyname = getSysProcAddr(modws2_32, "getservbyname")
+ procntohs = getSysProcAddr(modws2_32, "ntohs")
+ procDnsQuery_W = getSysProcAddr(moddnsapi, "DnsQuery_W")
+ procDnsRecordListFree = getSysProcAddr(moddnsapi, "DnsRecordListFree")
+ procGetIfEntry = getSysProcAddr(modiphlpapi, "GetIfEntry")
+ procGetAdaptersInfo = getSysProcAddr(modiphlpapi, "GetAdaptersInfo")
+)
+
+func GetLastError() (lasterrno int) {
+ r0, _, _ := Syscall(procGetLastError, 0, 0, 0, 0)
+ lasterrno = int(r0)
+ return
+}
+
+func LoadLibrary(libname string) (handle Handle, errno int) {
+ r0, _, e1 := Syscall(procLoadLibraryW, 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0)
+ handle = Handle(r0)
+ if handle == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FreeLibrary(handle Handle) (errno int) {
+ r1, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetProcAddress(module Handle, procname string) (proc Handle, errno int) {
+ r0, _, e1 := Syscall(procGetProcAddress, 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+ proc = Handle(r0)
+ if proc == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetVersion() (ver uint32, errno int) {
+ r0, _, e1 := Syscall(procGetVersion, 0, 0, 0, 0)
+ ver = uint32(r0)
+ if ver == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) {
+ var _p0 *uint16
+ if len(buf) > 0 {
+ _p0 = &buf[0]
+ }
+ r0, _, e1 := Syscall9(procFormatMessageW, 7, uintptr(flags), uintptr(msgsrc), uintptr(msgid), uintptr(langid), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(args)), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func ExitProcess(exitcode uint32) {
+ Syscall(procExitProcess, 1, uintptr(exitcode), 0, 0)
+ return
+}
+
+func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) {
+ r0, _, e1 := Syscall9(procCreateFileW, 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
+ handle = Handle(r0)
+ if handle == InvalidHandle {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
+ var _p0 *byte
+ if len(buf) > 0 {
+ _p0 = &buf[0]
+ }
+ r1, _, e1 := Syscall6(procReadFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) {
+ var _p0 *byte
+ if len(buf) > 0 {
+ _p0 = &buf[0]
+ }
+ r1, _, e1 := Syscall6(procWriteFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) {
+ r0, _, e1 := Syscall6(procSetFilePointer, 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0)
+ newlowoffset = uint32(r0)
+ if newlowoffset == 0xffffffff {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CloseHandle(handle Handle) (errno int) {
+ r1, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetStdHandle(stdhandle int) (handle Handle, errno int) {
+ r0, _, e1 := Syscall(procGetStdHandle, 1, uintptr(stdhandle), 0, 0)
+ handle = Handle(r0)
+ if handle == InvalidHandle {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) {
+ r0, _, e1 := Syscall(procFindFirstFileW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0)
+ handle = Handle(r0)
+ if handle == InvalidHandle {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FindNextFile(handle Handle, data *Win32finddata) (errno int) {
+ r1, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FindClose(handle Handle) (errno int) {
+ r1, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int) {
+ r1, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) {
+ r0, _, e1 := Syscall(procGetCurrentDirectoryW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetCurrentDirectory(path *uint16) (errno int) {
+ r1, _, e1 := Syscall(procSetCurrentDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) {
+ r1, _, e1 := Syscall(procCreateDirectoryW, 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func RemoveDirectory(path *uint16) (errno int) {
+ r1, _, e1 := Syscall(procRemoveDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func DeleteFile(path *uint16) (errno int) {
+ r1, _, e1 := Syscall(procDeleteFileW, 1, uintptr(unsafe.Pointer(path)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func MoveFile(from *uint16, to *uint16) (errno int) {
+ r1, _, e1 := Syscall(procMoveFileW, 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetComputerName(buf *uint16, n *uint32) (errno int) {
+ r1, _, e1 := Syscall(procGetComputerNameW, 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetEndOfFile(handle Handle) (errno int) {
+ r1, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetSystemTimeAsFileTime(time *Filetime) {
+ Syscall(procGetSystemTimeAsFileTime, 1, uintptr(unsafe.Pointer(time)), 0, 0)
+ return
+}
+
+func sleep(msec uint32) {
+ Syscall(procSleep, 1, uintptr(msec), 0, 0)
+ return
+}
+
+func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) {
+ r0, _, e1 := Syscall(procGetTimeZoneInformation, 1, uintptr(unsafe.Pointer(tzi)), 0, 0)
+ rc = uint32(r0)
+ if rc == 0xffffffff {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int) {
+ r0, _, e1 := Syscall6(procCreateIoCompletionPort, 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0)
+ handle = Handle(r0)
+ if handle == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) {
+ r1, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CancelIo(s Handle) (errno int) {
+ r1, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) {
+ var _p0 uint32
+ if inheritHandles {
+ _p0 = 1
+ } else {
+ _p0 = 0
+ }
+ r1, _, e1 := Syscall12(procCreateProcessW, 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int) {
+ var _p0 uint32
+ if inheritHandle {
+ _p0 = 1
+ } else {
+ _p0 = 0
+ }
+ r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid))
+ handle = Handle(r0)
+ if handle == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func TerminateProcess(handle Handle, exitcode uint32) (errno int) {
+ r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int) {
+ r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetStartupInfo(startupInfo *StartupInfo) (errno int) {
+ r1, _, e1 := Syscall(procGetStartupInfoW, 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetCurrentProcess() (pseudoHandle Handle, errno int) {
+ r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0)
+ pseudoHandle = Handle(r0)
+ if pseudoHandle == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) {
+ var _p0 uint32
+ if bInheritHandle {
+ _p0 = 1
+ } else {
+ _p0 = 0
+ }
+ r1, _, e1 := Syscall9(procDuplicateHandle, 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) {
+ r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0)
+ event = uint32(r0)
+ if event == 0xffffffff {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) {
+ r0, _, e1 := Syscall(procGetTempPathW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int) {
+ r1, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetFileType(filehandle Handle) (n uint32, errno int) {
+ r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) {
+ r1, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CryptReleaseContext(provhandle Handle, flags uint32) (errno int) {
+ r1, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) {
+ r1, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetEnvironmentStrings() (envs *uint16, errno int) {
+ r0, _, e1 := Syscall(procGetEnvironmentStringsW, 0, 0, 0, 0)
+ envs = (*uint16)(unsafe.Pointer(r0))
+ if envs == nil {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FreeEnvironmentStrings(envs *uint16) (errno int) {
+ r1, _, e1 := Syscall(procFreeEnvironmentStringsW, 1, uintptr(unsafe.Pointer(envs)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, errno int) {
+ r0, _, e1 := Syscall(procGetEnvironmentVariableW, 3, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(size))
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetEnvironmentVariable(name *uint16, value *uint16) (errno int) {
+ r1, _, e1 := Syscall(procSetEnvironmentVariableW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) {
+ r1, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetFileAttributes(name *uint16) (attrs uint32, errno int) {
+ r0, _, e1 := Syscall(procGetFileAttributesW, 1, uintptr(unsafe.Pointer(name)), 0, 0)
+ attrs = uint32(r0)
+ if attrs == INVALID_FILE_ATTRIBUTES {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetFileAttributes(name *uint16, attrs uint32) (errno int) {
+ r1, _, e1 := Syscall(procSetFileAttributesW, 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetCommandLine() (cmd *uint16) {
+ r0, _, _ := Syscall(procGetCommandLineW, 0, 0, 0, 0)
+ cmd = (*uint16)(unsafe.Pointer(r0))
+ return
+}
+
+func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) {
+ r0, _, e1 := Syscall(procCommandLineToArgvW, 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0)
+ argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0))
+ if argv == nil {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func LocalFree(hmem Handle) (handle Handle, errno int) {
+ r0, _, e1 := Syscall(procLocalFree, 1, uintptr(hmem), 0, 0)
+ handle = Handle(r0)
+ if handle != 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int) {
+ r1, _, e1 := Syscall(procSetHandleInformation, 3, uintptr(handle), uintptr(mask), uintptr(flags))
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FlushFileBuffers(handle Handle) (errno int) {
+ r1, _, e1 := Syscall(procFlushFileBuffers, 1, uintptr(handle), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) {
+ r0, _, e1 := Syscall6(procGetFullPathNameW, 4, uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname)), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) {
+ r0, _, e1 := Syscall6(procCreateFileMappingW, 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name)))
+ handle = Handle(r0)
+ if handle == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) {
+ r0, _, e1 := Syscall6(procMapViewOfFile, 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0)
+ addr = uintptr(r0)
+ if addr == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func UnmapViewOfFile(addr uintptr) (errno int) {
+ r1, _, e1 := Syscall(procUnmapViewOfFile, 1, uintptr(addr), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func FlushViewOfFile(addr uintptr, length uintptr) (errno int) {
+ r1, _, e1 := Syscall(procFlushViewOfFile, 2, uintptr(addr), uintptr(length), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func VirtualLock(addr uintptr, length uintptr) (errno int) {
+ r1, _, e1 := Syscall(procVirtualLock, 2, uintptr(addr), uintptr(length), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func VirtualUnlock(addr uintptr, length uintptr) (errno int) {
+ r1, _, e1 := Syscall(procVirtualUnlock, 2, uintptr(addr), uintptr(length), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) {
+ r1, _, e1 := Syscall9(procTransmitFile, 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) {
+ r0, _, _ := Syscall(procWSAStartup, 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
+ sockerrno = int(r0)
+ return
+}
+
+func WSACleanup() (errno int) {
+ r1, _, e1 := Syscall(procWSACleanup, 0, 0, 0, 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) {
+ r1, _, e1 := Syscall9(procWSAIoctl, 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func socket(af int32, typ int32, protocol int32) (handle Handle, errno int) {
+ r0, _, e1 := Syscall(procsocket, 3, uintptr(af), uintptr(typ), uintptr(protocol))
+ handle = Handle(r0)
+ if handle == InvalidHandle {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) {
+ r1, _, e1 := Syscall6(procsetsockopt, 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func bind(s Handle, name uintptr, namelen int32) (errno int) {
+ r1, _, e1 := Syscall(procbind, 3, uintptr(s), uintptr(name), uintptr(namelen))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func connect(s Handle, name uintptr, namelen int32) (errno int) {
+ r1, _, e1 := Syscall(procconnect, 3, uintptr(s), uintptr(name), uintptr(namelen))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
+ r1, _, e1 := Syscall(procgetsockname, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) {
+ r1, _, e1 := Syscall(procgetpeername, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func listen(s Handle, backlog int32) (errno int) {
+ r1, _, e1 := Syscall(proclisten, 2, uintptr(s), uintptr(backlog), 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func shutdown(s Handle, how int32) (errno int) {
+ r1, _, e1 := Syscall(procshutdown, 2, uintptr(s), uintptr(how), 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func Closesocket(s Handle) (errno int) {
+ r1, _, e1 := Syscall(procclosesocket, 1, uintptr(s), 0, 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) {
+ r1, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
+ if int(r1) == 0 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) {
+ Syscall9(procGetAcceptExSockaddrs, 8, uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(lrsa)), uintptr(unsafe.Pointer(lrsalen)), uintptr(unsafe.Pointer(rrsa)), uintptr(unsafe.Pointer(rrsalen)), 0)
+ return
+}
+
+func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) {
+ r1, _, e1 := Syscall9(procWSARecv, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) {
+ r1, _, e1 := Syscall9(procWSASend, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) {
+ r1, _, e1 := Syscall9(procWSARecvFrom, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) {
+ r1, _, e1 := Syscall9(procWSASendTo, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
+ if int(r1) == -1 {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetHostByName(name string) (h *Hostent, errno int) {
+ r0, _, e1 := Syscall(procgethostbyname, 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ h = (*Hostent)(unsafe.Pointer(r0))
+ if h == nil {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func GetServByName(name string, proto string) (s *Servent, errno int) {
+ r0, _, e1 := Syscall(procgetservbyname, 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0)
+ s = (*Servent)(unsafe.Pointer(r0))
+ if s == nil {
+ if e1 != 0 {
+ errno = int(e1)
+ } else {
+ errno = EINVAL
+ }
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func Ntohs(netshort uint16) (u uint16) {
+ r0, _, _ := Syscall(procntohs, 1, uintptr(netshort), 0, 0)
+ u = uint16(r0)
+ return
+}
+
+func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status uint32) {
+ r0, _, _ := Syscall6(procDnsQuery_W, 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
+ status = uint32(r0)
+ return
+}
+
+func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
+ Syscall(procDnsRecordListFree, 2, uintptr(unsafe.Pointer(rl)), uintptr(freetype), 0)
+ return
+}
+
+func GetIfEntry(pIfRow *MibIfRow) (errcode int) {
+ r0, _, _ := Syscall(procGetIfEntry, 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0)
+ errcode = int(r0)
+ return
+}
+
+func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode int) {
+ r0, _, _ := Syscall(procGetAdaptersInfo, 2, uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol)), 0)
+ errcode = int(r0)
+ return
+}
diff --git a/src/pkg/syscall/zsysnum_windows_amd64.go b/src/pkg/syscall/zsysnum_windows_amd64.go
new file mode 100644
index 000000000..36bf065d1
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_windows_amd64.go
@@ -0,0 +1,3 @@
+// nothing to see here
+
+package syscall
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
new file mode 100644
index 000000000..1a264a405
--- /dev/null
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -0,0 +1,656 @@
+package syscall
+
+// TODO(brainman): autogenerate types in ztypes_windows_386.go
+
+//import "unsafe"
+
+// Constants
+const (
+ sizeofPtr = 0x4
+ sizeofShort = 0x2
+ sizeofInt = 0x4
+ sizeofLong = 0x4
+ sizeofLongLong = 0x8
+ PathMax = 0x1000
+ SizeofLinger = 0x8
+ SizeofMsghdr = 0x1c
+ SizeofCmsghdr = 0xc
+)
+
+const (
+ // Windows errors.
+ ERROR_FILE_NOT_FOUND = 2
+ ERROR_PATH_NOT_FOUND = 3
+ ERROR_NO_MORE_FILES = 18
+ ERROR_BROKEN_PIPE = 109
+ ERROR_BUFFER_OVERFLOW = 111
+ ERROR_INSUFFICIENT_BUFFER = 122
+ ERROR_MOD_NOT_FOUND = 126
+ ERROR_PROC_NOT_FOUND = 127
+ ERROR_ENVVAR_NOT_FOUND = 203
+ ERROR_DIRECTORY = 267
+ ERROR_OPERATION_ABORTED = 995
+ ERROR_IO_PENDING = 997
+)
+
+const (
+ // Invented values to support what package os expects.
+ O_RDONLY = 0x00000
+ O_WRONLY = 0x00001
+ O_RDWR = 0x00002
+ O_CREAT = 0x00040
+ O_EXCL = 0x00080
+ O_NOCTTY = 0x00100
+ O_TRUNC = 0x00200
+ O_NONBLOCK = 0x00800
+ O_APPEND = 0x00400
+ O_SYNC = 0x01000
+ O_ASYNC = 0x02000
+ O_CLOEXEC = 0x80000
+)
+
+const (
+ // More invented values for signals
+ SIGHUP = 0x1
+ SIGINT = 0x2
+ SIGQUIT = 0x3
+ SIGILL = 0x4
+ SIGTRAP = 0x5
+ SIGABRT = 0x6
+ SIGBUS = 0x7
+ SIGFPE = 0x8
+ SIGKILL = 0x9
+ SIGSEGV = 0xb
+ SIGPIPE = 0xd
+ SIGALRM = 0xe
+ SIGTERM = 0xf
+)
+
+const (
+ GENERIC_READ = 0x80000000
+ GENERIC_WRITE = 0x40000000
+ GENERIC_EXECUTE = 0x20000000
+ GENERIC_ALL = 0x10000000
+
+ FILE_APPEND_DATA = 0x00000004
+ FILE_WRITE_ATTRIBUTES = 0x00000100
+
+ FILE_SHARE_READ = 0x00000001
+ FILE_SHARE_WRITE = 0x00000002
+ FILE_SHARE_DELETE = 0x00000004
+ FILE_ATTRIBUTE_READONLY = 0x00000001
+ FILE_ATTRIBUTE_HIDDEN = 0x00000002
+ FILE_ATTRIBUTE_SYSTEM = 0x00000004
+ FILE_ATTRIBUTE_DIRECTORY = 0x00000010
+ FILE_ATTRIBUTE_ARCHIVE = 0x00000020
+ FILE_ATTRIBUTE_NORMAL = 0x00000080
+
+ INVALID_FILE_ATTRIBUTES = 0xffffffff
+
+ CREATE_NEW = 1
+ CREATE_ALWAYS = 2
+ OPEN_EXISTING = 3
+ OPEN_ALWAYS = 4
+ TRUNCATE_EXISTING = 5
+
+ HANDLE_FLAG_INHERIT = 0x00000001
+ STARTF_USESTDHANDLES = 0x00000100
+ STARTF_USESHOWWINDOW = 0x00000001
+ DUPLICATE_CLOSE_SOURCE = 0x00000001
+ DUPLICATE_SAME_ACCESS = 0x00000002
+
+ STD_INPUT_HANDLE = -10
+ STD_OUTPUT_HANDLE = -11
+ STD_ERROR_HANDLE = -12
+
+ FILE_BEGIN = 0
+ FILE_CURRENT = 1
+ FILE_END = 2
+
+ FORMAT_MESSAGE_ALLOCATE_BUFFER = 256
+ FORMAT_MESSAGE_IGNORE_INSERTS = 512
+ FORMAT_MESSAGE_FROM_STRING = 1024
+ FORMAT_MESSAGE_FROM_HMODULE = 2048
+ FORMAT_MESSAGE_FROM_SYSTEM = 4096
+ FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192
+ FORMAT_MESSAGE_MAX_WIDTH_MASK = 255
+
+ MAX_PATH = 260
+ MAX_LONG_PATH = 32768
+
+ MAX_COMPUTERNAME_LENGTH = 15
+
+ TIME_ZONE_ID_UNKNOWN = 0
+ TIME_ZONE_ID_STANDARD = 1
+
+ TIME_ZONE_ID_DAYLIGHT = 2
+ IGNORE = 0
+ INFINITE = 0xffffffff
+
+ WAIT_TIMEOUT = 258
+ WAIT_ABANDONED = 0x00000080
+ WAIT_OBJECT_0 = 0x00000000
+ WAIT_FAILED = 0xFFFFFFFF
+
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400
+
+ STANDARD_RIGHTS_READ = 0x00020000
+ PROCESS_QUERY_INFORMATION = 0x00000400
+ SYNCHRONIZE = 0x00100000
+
+ PAGE_READONLY = 0x02
+ PAGE_READWRITE = 0x04
+ PAGE_WRITECOPY = 0x08
+ PAGE_EXECUTE_READ = 0x20
+ PAGE_EXECUTE_READWRITE = 0x40
+ PAGE_EXECUTE_WRITECOPY = 0x80
+
+ FILE_MAP_COPY = 0x01
+ FILE_MAP_WRITE = 0x02
+ FILE_MAP_READ = 0x04
+ FILE_MAP_EXECUTE = 0x20
+)
+
+const (
+ // wincrypt.h
+ PROV_RSA_FULL = 1
+ PROV_RSA_SIG = 2
+ PROV_DSS = 3
+ PROV_FORTEZZA = 4
+ PROV_MS_EXCHANGE = 5
+ PROV_SSL = 6
+ PROV_RSA_SCHANNEL = 12
+ PROV_DSS_DH = 13
+ PROV_EC_ECDSA_SIG = 14
+ PROV_EC_ECNRA_SIG = 15
+ PROV_EC_ECDSA_FULL = 16
+ PROV_EC_ECNRA_FULL = 17
+ PROV_DH_SCHANNEL = 18
+ PROV_SPYRUS_LYNKS = 20
+ PROV_RNG = 21
+ PROV_INTEL_SEC = 22
+ PROV_REPLACE_OWF = 23
+ PROV_RSA_AES = 24
+ CRYPT_VERIFYCONTEXT = 0xF0000000
+ CRYPT_NEWKEYSET = 0x00000008
+ CRYPT_DELETEKEYSET = 0x00000010
+ CRYPT_MACHINE_KEYSET = 0x00000020
+ CRYPT_SILENT = 0x00000040
+ CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x00000080
+)
+
+// Types
+
+type _C_short int16
+
+type _C_int int32
+
+type _C_long int32
+
+type _C_long_long int64
+
+// Invented values to support what package os expects.
+type Timeval struct {
+ Sec int32
+ Usec int32
+}
+
+func (tv *Timeval) Nanoseconds() int64 {
+ return (int64(tv.Sec)*1e6 + int64(tv.Usec)) * 1e3
+}
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ tv.Sec = int32(nsec / 1e9)
+ tv.Usec = int32(nsec % 1e9 / 1e3)
+ return
+}
+
+type SecurityAttributes struct {
+ Length uint32
+ SecurityDescriptor uintptr
+ InheritHandle uint32
+}
+
+type Overlapped struct {
+ Internal uint32
+ InternalHigh uint32
+ Offset uint32
+ OffsetHigh uint32
+ HEvent Handle
+}
+
+type Filetime struct {
+ LowDateTime uint32
+ HighDateTime uint32
+}
+
+func (ft *Filetime) Nanoseconds() int64 {
+ // 100-nanosecond intervals since January 1, 1601
+ nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
+ // change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
+ nsec -= 116444736000000000
+ // convert into nanoseconds
+ nsec *= 100
+ return nsec
+}
+
+func NsecToFiletime(nsec int64) (ft Filetime) {
+ // convert into 100-nanosecond
+ nsec /= 100
+ // change starting time to January 1, 1601
+ nsec += 116444736000000000
+ // split into high / low
+ ft.LowDateTime = uint32(nsec & 0xffffffff)
+ ft.HighDateTime = uint32(nsec >> 32 & 0xffffffff)
+ return ft
+}
+
+type Win32finddata struct {
+ FileAttributes uint32
+ CreationTime Filetime
+ LastAccessTime Filetime
+ LastWriteTime Filetime
+ FileSizeHigh uint32
+ FileSizeLow uint32
+ Reserved0 uint32
+ Reserved1 uint32
+ FileName [MAX_PATH - 1]uint16
+ AlternateFileName [13]uint16
+}
+
+type ByHandleFileInformation struct {
+ FileAttributes uint32
+ CreationTime Filetime
+ LastAccessTime Filetime
+ LastWriteTime Filetime
+ VolumeSerialNumber uint32
+ FileSizeHigh uint32
+ FileSizeLow uint32
+ NumberOfLinks uint32
+ FileIndexHigh uint32
+ FileIndexLow uint32
+}
+
+// ShowWindow constants
+const (
+ // winuser.h
+ SW_HIDE = 0
+ SW_NORMAL = 1
+ SW_SHOWNORMAL = 1
+ SW_SHOWMINIMIZED = 2
+ SW_SHOWMAXIMIZED = 3
+ SW_MAXIMIZE = 3
+ SW_SHOWNOACTIVATE = 4
+ SW_SHOW = 5
+ SW_MINIMIZE = 6
+ SW_SHOWMINNOACTIVE = 7
+ SW_SHOWNA = 8
+ SW_RESTORE = 9
+ SW_SHOWDEFAULT = 10
+ SW_FORCEMINIMIZE = 11
+)
+
+type StartupInfo struct {
+ Cb uint32
+ _ *uint16
+ Desktop *uint16
+ Title *uint16
+ X uint32
+ Y uint32
+ XSize uint32
+ YSize uint32
+ XCountChars uint32
+ YCountChars uint32
+ FillAttribute uint32
+ Flags uint32
+ ShowWindow uint16
+ _ uint16
+ _ *byte
+ StdInput Handle
+ StdOutput Handle
+ StdErr Handle
+}
+
+type ProcessInformation struct {
+ Process Handle
+ Thread Handle
+ ProcessId uint32
+ ThreadId uint32
+}
+
+// Invented values to support what package os expects.
+type Stat_t struct {
+ Windata Win32finddata
+ Mode uint32
+}
+
+type Systemtime struct {
+ Year uint16
+ Month uint16
+ DayOfWeek uint16
+ Day uint16
+ Hour uint16
+ Minute uint16
+ Second uint16
+ Milliseconds uint16
+}
+
+type Timezoneinformation struct {
+ Bias int32
+ StandardName [32]uint16
+ StandardDate Systemtime
+ StandardBias int32
+ DaylightName [32]uint16
+ DaylightDate Systemtime
+ DaylightBias int32
+}
+
+// Socket related.
+
+const (
+ AF_UNSPEC = 0
+ AF_UNIX = 1
+ AF_INET = 2
+ AF_INET6 = 23
+ AF_NETBIOS = 17
+
+ SOCK_STREAM = 1
+ SOCK_DGRAM = 2
+ SOCK_RAW = 3
+ SOCK_SEQPACKET = 5
+
+ IPPROTO_IP = 0
+ IPPROTO_TCP = 6
+ IPPROTO_UDP = 17
+
+ SOL_SOCKET = 0xffff
+ SO_REUSEADDR = 4
+ SO_KEEPALIVE = 8
+ SO_DONTROUTE = 16
+ SO_BROADCAST = 32
+ SO_LINGER = 128
+ SO_RCVBUF = 0x1002
+ SO_SNDBUF = 0x1001
+ SO_UPDATE_ACCEPT_CONTEXT = 0x700b
+
+ IPPROTO_IPV6 = 0x29
+ IPV6_V6ONLY = 0x1b
+
+ SOMAXCONN = 5
+
+ TCP_NODELAY = 1
+
+ SHUT_RD = 0
+ SHUT_WR = 1
+ SHUT_RDWR = 2
+
+ WSADESCRIPTION_LEN = 256
+ WSASYS_STATUS_LEN = 128
+)
+
+type WSAData struct {
+ Version uint16
+ HighVersion uint16
+ Description [WSADESCRIPTION_LEN + 1]byte
+ SystemStatus [WSASYS_STATUS_LEN + 1]byte
+ MaxSockets uint16
+ MaxUdpDg uint16
+ VendorInfo *byte
+}
+
+type WSABuf struct {
+ Len uint32
+ Buf *byte
+}
+
+// TODO(brainman): fix all needed for os
+
+const (
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
+ MAP_SHARED = 0x1
+ SYS_FORK = 0
+ SYS_PTRACE = 0
+ SYS_CHDIR = 0
+ SYS_DUP2 = 0
+ SYS_FCNTL = 0
+ SYS_EXECVE = 0
+ F_GETFD = 0x1
+ F_SETFD = 0x2
+ F_GETFL = 0x3
+ F_SETFL = 0x4
+ FD_CLOEXEC = 0
+ S_IFMT = 0x1f000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+ S_ISUID = 0x800
+ S_ISGID = 0x400
+ S_ISVTX = 0x200
+ S_IRUSR = 0x100
+ S_IWRITE = 0x80
+ S_IWUSR = 0x80
+ S_IXUSR = 0x40
+)
+
+const (
+ FILE_TYPE_CHAR = 0x0002
+ FILE_TYPE_DISK = 0x0001
+ FILE_TYPE_PIPE = 0x0003
+ FILE_TYPE_REMOTE = 0x8000
+ FILE_TYPE_UNKNOWN = 0x0000
+)
+
+type Hostent struct {
+ Name *byte
+ Aliases **byte
+ AddrType uint16
+ Length uint16
+ AddrList **byte
+}
+
+type Servent struct {
+ Name *byte
+ Aliases **byte
+ Port uint16
+ Proto *byte
+}
+
+const (
+ DNS_TYPE_A = 0x0001
+ DNS_TYPE_NS = 0x0002
+ DNS_TYPE_MD = 0x0003
+ DNS_TYPE_MF = 0x0004
+ DNS_TYPE_CNAME = 0x0005
+ DNS_TYPE_SOA = 0x0006
+ DNS_TYPE_MB = 0x0007
+ DNS_TYPE_MG = 0x0008
+ DNS_TYPE_MR = 0x0009
+ DNS_TYPE_NULL = 0x000a
+ DNS_TYPE_WKS = 0x000b
+ DNS_TYPE_PTR = 0x000c
+ DNS_TYPE_HINFO = 0x000d
+ DNS_TYPE_MINFO = 0x000e
+ DNS_TYPE_MX = 0x000f
+ DNS_TYPE_TEXT = 0x0010
+ DNS_TYPE_RP = 0x0011
+ DNS_TYPE_AFSDB = 0x0012
+ DNS_TYPE_X25 = 0x0013
+ DNS_TYPE_ISDN = 0x0014
+ DNS_TYPE_RT = 0x0015
+ DNS_TYPE_NSAP = 0x0016
+ DNS_TYPE_NSAPPTR = 0x0017
+ DNS_TYPE_SIG = 0x0018
+ DNS_TYPE_KEY = 0x0019
+ DNS_TYPE_PX = 0x001a
+ DNS_TYPE_GPOS = 0x001b
+ DNS_TYPE_AAAA = 0x001c
+ DNS_TYPE_LOC = 0x001d
+ DNS_TYPE_NXT = 0x001e
+ DNS_TYPE_EID = 0x001f
+ DNS_TYPE_NIMLOC = 0x0020
+ DNS_TYPE_SRV = 0x0021
+ DNS_TYPE_ATMA = 0x0022
+ DNS_TYPE_NAPTR = 0x0023
+ DNS_TYPE_KX = 0x0024
+ DNS_TYPE_CERT = 0x0025
+ DNS_TYPE_A6 = 0x0026
+ DNS_TYPE_DNAME = 0x0027
+ DNS_TYPE_SINK = 0x0028
+ DNS_TYPE_OPT = 0x0029
+ DNS_TYPE_DS = 0x002B
+ DNS_TYPE_RRSIG = 0x002E
+ DNS_TYPE_NSEC = 0x002F
+ DNS_TYPE_DNSKEY = 0x0030
+ DNS_TYPE_DHCID = 0x0031
+ DNS_TYPE_UINFO = 0x0064
+ DNS_TYPE_UID = 0x0065
+ DNS_TYPE_GID = 0x0066
+ DNS_TYPE_UNSPEC = 0x0067
+ DNS_TYPE_ADDRS = 0x00f8
+ DNS_TYPE_TKEY = 0x00f9
+ DNS_TYPE_TSIG = 0x00fa
+ DNS_TYPE_IXFR = 0x00fb
+ DNS_TYPE_AXFR = 0x00fc
+ DNS_TYPE_MAILB = 0x00fd
+ DNS_TYPE_MAILA = 0x00fe
+ DNS_TYPE_ALL = 0x00ff
+ DNS_TYPE_ANY = 0x00ff
+ DNS_TYPE_WINS = 0xff01
+ DNS_TYPE_WINSR = 0xff02
+ DNS_TYPE_NBSTAT = 0xff01
+)
+
+type DNSSRVData struct {
+ Target *uint16
+ Priority uint16
+ Weight uint16
+ Port uint16
+ Pad uint16
+}
+
+type DNSPTRData struct {
+ Host *uint16
+}
+
+type DNSRecord struct {
+ Next *DNSRecord
+ Name *uint16
+ Type uint16
+ Length uint16
+ Dw uint32
+ Ttl uint32
+ Reserved uint32
+ Data [40]byte
+}
+
+const (
+ TF_DISCONNECT = 1
+ TF_REUSE_SOCKET = 2
+ TF_WRITE_BEHIND = 4
+ TF_USE_DEFAULT_WORKER = 0
+ TF_USE_SYSTEM_THREAD = 16
+ TF_USE_KERNEL_APC = 32
+)
+
+type TransmitFileBuffers struct {
+ Head uintptr
+ HeadLength uint32
+ Tail uintptr
+ TailLength uint32
+}
+
+const (
+ IFF_UP = 1
+ IFF_BROADCAST = 2
+ IFF_LOOPBACK = 4
+ IFF_POINTTOPOINT = 8
+ IFF_MULTICAST = 16
+)
+
+const SIO_GET_INTERFACE_LIST = 0x4004747F
+
+// TODO(mattn): SockaddrGen is union of sockaddr/sockaddr_in/sockaddr_in6_old.
+// will be fixed to change variable type as suitable.
+
+type SockaddrGen [24]byte
+
+type InterfaceInfo struct {
+ Flags uint32
+ Address SockaddrGen
+ BroadcastAddress SockaddrGen
+ Netmask SockaddrGen
+}
+
+type IpAddressString struct {
+ String [16]byte
+}
+
+type IpMaskString IpAddressString
+
+type IpAddrString struct {
+ Next *IpAddrString
+ IpAddress IpAddressString
+ IpMask IpMaskString
+ Context uint32
+}
+
+const MAX_ADAPTER_NAME_LENGTH = 256
+const MAX_ADAPTER_DESCRIPTION_LENGTH = 128
+const MAX_ADAPTER_ADDRESS_LENGTH = 8
+
+type IpAdapterInfo struct {
+ Next *IpAdapterInfo
+ ComboIndex uint32
+ AdapterName [MAX_ADAPTER_NAME_LENGTH + 4]byte
+ Description [MAX_ADAPTER_DESCRIPTION_LENGTH + 4]byte
+ AddressLength uint32
+ Address [MAX_ADAPTER_ADDRESS_LENGTH]byte
+ Index uint32
+ Type uint32
+ DhcpEnabled uint32
+ CurrentIpAddress *IpAddrString
+ IpAddressList IpAddrString
+ GatewayList IpAddrString
+ DhcpServer IpAddrString
+ HaveWins bool
+ PrimaryWinsServer IpAddrString
+ SecondaryWinsServer IpAddrString
+ LeaseObtained int64
+ LeaseExpires int64
+}
+
+const MAXLEN_PHYSADDR = 8
+const MAX_INTERFACE_NAME_LEN = 256
+const MAXLEN_IFDESCR = 256
+
+type MibIfRow struct {
+ Name [MAX_INTERFACE_NAME_LEN]uint16
+ Index uint32
+ Type uint32
+ Mtu uint32
+ Speed uint32
+ PhysAddrLen uint32
+ PhysAddr [MAXLEN_PHYSADDR]byte
+ AdminStatus uint32
+ OperStatus uint32
+ LastChange uint32
+ InOctets uint32
+ InUcastPkts uint32
+ InNUcastPkts uint32
+ InDiscards uint32
+ InErrors uint32
+ InUnknownProtos uint32
+ OutOctets uint32
+ OutUcastPkts uint32
+ OutNUcastPkts uint32
+ OutDiscards uint32
+ OutErrors uint32
+ OutQLen uint32
+ DescrLen uint32
+ Descr [MAXLEN_IFDESCR]byte
+}
diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go
index 6ea85e2b8..d1008bd03 100644
--- a/src/pkg/syscall/ztypes_windows_386.go
+++ b/src/pkg/syscall/ztypes_windows_386.go
@@ -1,656 +1,5 @@
-package syscall
-
-// TODO(brainman): autogenerate types in ztypes_windows_386.go
-
-//import "unsafe"
-
-// Constants
-const (
- sizeofPtr = 0x4
- sizeofShort = 0x2
- sizeofInt = 0x4
- sizeofLong = 0x4
- sizeofLongLong = 0x8
- PathMax = 0x1000
- SizeofLinger = 0x8
- SizeofMsghdr = 0x1c
- SizeofCmsghdr = 0xc
-)
-
-const (
- // Windows errors.
- ERROR_FILE_NOT_FOUND = 2
- ERROR_PATH_NOT_FOUND = 3
- ERROR_NO_MORE_FILES = 18
- ERROR_BROKEN_PIPE = 109
- ERROR_BUFFER_OVERFLOW = 111
- ERROR_INSUFFICIENT_BUFFER = 122
- ERROR_MOD_NOT_FOUND = 126
- ERROR_PROC_NOT_FOUND = 127
- ERROR_ENVVAR_NOT_FOUND = 203
- ERROR_DIRECTORY = 267
- ERROR_OPERATION_ABORTED = 995
- ERROR_IO_PENDING = 997
-)
-
-const (
- // Invented values to support what package os expects.
- O_RDONLY = 0x00000
- O_WRONLY = 0x00001
- O_RDWR = 0x00002
- O_CREAT = 0x00040
- O_EXCL = 0x00080
- O_NOCTTY = 0x00100
- O_TRUNC = 0x00200
- O_NONBLOCK = 0x00800
- O_APPEND = 0x00400
- O_SYNC = 0x01000
- O_ASYNC = 0x02000
- O_CLOEXEC = 0x80000
-)
-
-const (
- // More invented values for signals
- SIGHUP = 0x1
- SIGINT = 0x2
- SIGQUIT = 0x3
- SIGILL = 0x4
- SIGTRAP = 0x5
- SIGABRT = 0x6
- SIGBUS = 0x7
- SIGFPE = 0x8
- SIGKILL = 0x9
- SIGSEGV = 0xb
- SIGPIPE = 0xd
- SIGALRM = 0xe
- SIGTERM = 0xf
-)
-
-const (
- GENERIC_READ = 0x80000000
- GENERIC_WRITE = 0x40000000
- GENERIC_EXECUTE = 0x20000000
- GENERIC_ALL = 0x10000000
-
- FILE_APPEND_DATA = 0x00000004
- FILE_WRITE_ATTRIBUTES = 0x00000100
-
- FILE_SHARE_READ = 0x00000001
- FILE_SHARE_WRITE = 0x00000002
- FILE_SHARE_DELETE = 0x00000004
- FILE_ATTRIBUTE_READONLY = 0x00000001
- FILE_ATTRIBUTE_HIDDEN = 0x00000002
- FILE_ATTRIBUTE_SYSTEM = 0x00000004
- FILE_ATTRIBUTE_DIRECTORY = 0x00000010
- FILE_ATTRIBUTE_ARCHIVE = 0x00000020
- FILE_ATTRIBUTE_NORMAL = 0x00000080
-
- INVALID_FILE_ATTRIBUTES = 0xffffffff
-
- CREATE_NEW = 1
- CREATE_ALWAYS = 2
- OPEN_EXISTING = 3
- OPEN_ALWAYS = 4
- TRUNCATE_EXISTING = 5
-
- HANDLE_FLAG_INHERIT = 0x00000001
- STARTF_USESTDHANDLES = 0x00000100
- STARTF_USESHOWWINDOW = 0x00000001
- DUPLICATE_CLOSE_SOURCE = 0x00000001
- DUPLICATE_SAME_ACCESS = 0x00000002
-
- STD_INPUT_HANDLE = -10
- STD_OUTPUT_HANDLE = -11
- STD_ERROR_HANDLE = -12
-
- FILE_BEGIN = 0
- FILE_CURRENT = 1
- FILE_END = 2
-
- FORMAT_MESSAGE_ALLOCATE_BUFFER = 256
- FORMAT_MESSAGE_IGNORE_INSERTS = 512
- FORMAT_MESSAGE_FROM_STRING = 1024
- FORMAT_MESSAGE_FROM_HMODULE = 2048
- FORMAT_MESSAGE_FROM_SYSTEM = 4096
- FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192
- FORMAT_MESSAGE_MAX_WIDTH_MASK = 255
-
- MAX_PATH = 260
- MAX_LONG_PATH = 32768
-
- MAX_COMPUTERNAME_LENGTH = 15
-
- TIME_ZONE_ID_UNKNOWN = 0
- TIME_ZONE_ID_STANDARD = 1
-
- TIME_ZONE_ID_DAYLIGHT = 2
- IGNORE = 0
- INFINITE = 0xffffffff
-
- WAIT_TIMEOUT = 258
- WAIT_ABANDONED = 0x00000080
- WAIT_OBJECT_0 = 0x00000000
- WAIT_FAILED = 0xFFFFFFFF
-
- CREATE_UNICODE_ENVIRONMENT = 0x00000400
-
- STANDARD_RIGHTS_READ = 0x00020000
- PROCESS_QUERY_INFORMATION = 0x00000400
- SYNCHRONIZE = 0x00100000
-
- PAGE_READONLY = 0x02
- PAGE_READWRITE = 0x04
- PAGE_WRITECOPY = 0x08
- PAGE_EXECUTE_READ = 0x20
- PAGE_EXECUTE_READWRITE = 0x40
- PAGE_EXECUTE_WRITECOPY = 0x80
-
- FILE_MAP_COPY = 0x01
- FILE_MAP_WRITE = 0x02
- FILE_MAP_READ = 0x04
- FILE_MAP_EXECUTE = 0x20
-)
-
-const (
- // wincrypt.h
- PROV_RSA_FULL = 1
- PROV_RSA_SIG = 2
- PROV_DSS = 3
- PROV_FORTEZZA = 4
- PROV_MS_EXCHANGE = 5
- PROV_SSL = 6
- PROV_RSA_SCHANNEL = 12
- PROV_DSS_DH = 13
- PROV_EC_ECDSA_SIG = 14
- PROV_EC_ECNRA_SIG = 15
- PROV_EC_ECDSA_FULL = 16
- PROV_EC_ECNRA_FULL = 17
- PROV_DH_SCHANNEL = 18
- PROV_SPYRUS_LYNKS = 20
- PROV_RNG = 21
- PROV_INTEL_SEC = 22
- PROV_REPLACE_OWF = 23
- PROV_RSA_AES = 24
- CRYPT_VERIFYCONTEXT = 0xF0000000
- CRYPT_NEWKEYSET = 0x00000008
- CRYPT_DELETEKEYSET = 0x00000010
- CRYPT_MACHINE_KEYSET = 0x00000020
- CRYPT_SILENT = 0x00000040
- CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x00000080
-)
-
-// Types
-
-type _C_short int16
-
-type _C_int int32
-
-type _C_long int32
-
-type _C_long_long int64
-
-// Invented values to support what package os expects.
-type Timeval struct {
- Sec int32
- Usec int32
-}
-
-func (tv *Timeval) Nanoseconds() int64 {
- return (int64(tv.Sec)*1e6 + int64(tv.Usec)) * 1e3
-}
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
- tv.Sec = int32(nsec / 1e9)
- tv.Usec = int32(nsec % 1e9 / 1e3)
- return
-}
-
-type SecurityAttributes struct {
- Length uint32
- SecurityDescriptor uintptr
- InheritHandle uint32
-}
-
-type Overlapped struct {
- Internal uint32
- InternalHigh uint32
- Offset uint32
- OffsetHigh uint32
- HEvent int32
-}
-
-type Filetime struct {
- LowDateTime uint32
- HighDateTime uint32
-}
-
-func (ft *Filetime) Nanoseconds() int64 {
- // 100-nanosecond intervals since January 1, 1601
- nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
- // change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
- nsec -= 116444736000000000
- // convert into nanoseconds
- nsec *= 100
- return nsec
-}
-
-func NsecToFiletime(nsec int64) (ft Filetime) {
- // convert into 100-nanosecond
- nsec /= 100
- // change starting time to January 1, 1601
- nsec += 116444736000000000
- // split into high / low
- ft.LowDateTime = uint32(nsec & 0xffffffff)
- ft.HighDateTime = uint32(nsec >> 32 & 0xffffffff)
- return ft
-}
-
-type Win32finddata struct {
- FileAttributes uint32
- CreationTime Filetime
- LastAccessTime Filetime
- LastWriteTime Filetime
- FileSizeHigh uint32
- FileSizeLow uint32
- Reserved0 uint32
- Reserved1 uint32
- FileName [MAX_PATH - 1]uint16
- AlternateFileName [13]uint16
-}
-
-type ByHandleFileInformation struct {
- FileAttributes uint32
- CreationTime Filetime
- LastAccessTime Filetime
- LastWriteTime Filetime
- VolumeSerialNumber uint32
- FileSizeHigh uint32
- FileSizeLow uint32
- NumberOfLinks uint32
- FileIndexHigh uint32
- FileIndexLow uint32
-}
+// 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.
-// ShowWindow constants
-const (
- // winuser.h
- SW_HIDE = 0
- SW_NORMAL = 1
- SW_SHOWNORMAL = 1
- SW_SHOWMINIMIZED = 2
- SW_SHOWMAXIMIZED = 3
- SW_MAXIMIZE = 3
- SW_SHOWNOACTIVATE = 4
- SW_SHOW = 5
- SW_MINIMIZE = 6
- SW_SHOWMINNOACTIVE = 7
- SW_SHOWNA = 8
- SW_RESTORE = 9
- SW_SHOWDEFAULT = 10
- SW_FORCEMINIMIZE = 11
-)
-
-type StartupInfo struct {
- Cb uint32
- _ *uint16
- Desktop *uint16
- Title *uint16
- X uint32
- Y uint32
- XSize uint32
- YSize uint32
- XCountChars uint32
- YCountChars uint32
- FillAttribute uint32
- Flags uint32
- ShowWindow uint16
- _ uint16
- _ *byte
- StdInput int32
- StdOutput int32
- StdErr int32
-}
-
-type ProcessInformation struct {
- Process int32
- Thread int32
- ProcessId uint32
- ThreadId uint32
-}
-
-// Invented values to support what package os expects.
-type Stat_t struct {
- Windata Win32finddata
- Mode uint32
-}
-
-type Systemtime struct {
- Year uint16
- Month uint16
- DayOfWeek uint16
- Day uint16
- Hour uint16
- Minute uint16
- Second uint16
- Milliseconds uint16
-}
-
-type Timezoneinformation struct {
- Bias int32
- StandardName [32]uint16
- StandardDate Systemtime
- StandardBias int32
- DaylightName [32]uint16
- DaylightDate Systemtime
- DaylightBias int32
-}
-
-// Socket related.
-
-const (
- AF_UNSPEC = 0
- AF_UNIX = 1
- AF_INET = 2
- AF_INET6 = 23
- AF_NETBIOS = 17
-
- SOCK_STREAM = 1
- SOCK_DGRAM = 2
- SOCK_RAW = 3
- SOCK_SEQPACKET = 5
-
- IPPROTO_IP = 0
- IPPROTO_TCP = 6
- IPPROTO_UDP = 17
-
- SOL_SOCKET = 0xffff
- SO_REUSEADDR = 4
- SO_KEEPALIVE = 8
- SO_DONTROUTE = 16
- SO_BROADCAST = 32
- SO_LINGER = 128
- SO_RCVBUF = 0x1002
- SO_SNDBUF = 0x1001
- SO_UPDATE_ACCEPT_CONTEXT = 0x700b
-
- IPPROTO_IPV6 = 0x29
- IPV6_V6ONLY = 0x1b
-
- SOMAXCONN = 5
-
- TCP_NODELAY = 1
-
- SHUT_RD = 0
- SHUT_WR = 1
- SHUT_RDWR = 2
-
- WSADESCRIPTION_LEN = 256
- WSASYS_STATUS_LEN = 128
-)
-
-type WSAData struct {
- Version uint16
- HighVersion uint16
- Description [WSADESCRIPTION_LEN + 1]byte
- SystemStatus [WSASYS_STATUS_LEN + 1]byte
- MaxSockets uint16
- MaxUdpDg uint16
- VendorInfo *byte
-}
-
-type WSABuf struct {
- Len uint32
- Buf *byte
-}
-
-// TODO(brainman): fix all needed for os
-
-const (
- PROT_READ = 0x1
- PROT_WRITE = 0x2
- MAP_SHARED = 0x1
- SYS_FORK = 0
- SYS_PTRACE = 0
- SYS_CHDIR = 0
- SYS_DUP2 = 0
- SYS_FCNTL = 0
- SYS_EXECVE = 0
- F_GETFD = 0x1
- F_SETFD = 0x2
- F_GETFL = 0x3
- F_SETFL = 0x4
- FD_CLOEXEC = 0
- S_IFMT = 0x1f000
- S_IFIFO = 0x1000
- S_IFCHR = 0x2000
- S_IFDIR = 0x4000
- S_IFBLK = 0x6000
- S_IFREG = 0x8000
- S_IFLNK = 0xa000
- S_IFSOCK = 0xc000
- S_ISUID = 0x800
- S_ISGID = 0x400
- S_ISVTX = 0x200
- S_IRUSR = 0x100
- S_IWRITE = 0x80
- S_IWUSR = 0x80
- S_IXUSR = 0x40
-)
-
-const (
- FILE_TYPE_CHAR = 0x0002
- FILE_TYPE_DISK = 0x0001
- FILE_TYPE_PIPE = 0x0003
- FILE_TYPE_REMOTE = 0x8000
- FILE_TYPE_UNKNOWN = 0x0000
-)
-
-type Hostent struct {
- Name *byte
- Aliases **byte
- AddrType uint16
- Length uint16
- AddrList **byte
-}
-
-type Servent struct {
- Name *byte
- Aliases **byte
- Port uint16
- Proto *byte
-}
-
-const (
- DNS_TYPE_A = 0x0001
- DNS_TYPE_NS = 0x0002
- DNS_TYPE_MD = 0x0003
- DNS_TYPE_MF = 0x0004
- DNS_TYPE_CNAME = 0x0005
- DNS_TYPE_SOA = 0x0006
- DNS_TYPE_MB = 0x0007
- DNS_TYPE_MG = 0x0008
- DNS_TYPE_MR = 0x0009
- DNS_TYPE_NULL = 0x000a
- DNS_TYPE_WKS = 0x000b
- DNS_TYPE_PTR = 0x000c
- DNS_TYPE_HINFO = 0x000d
- DNS_TYPE_MINFO = 0x000e
- DNS_TYPE_MX = 0x000f
- DNS_TYPE_TEXT = 0x0010
- DNS_TYPE_RP = 0x0011
- DNS_TYPE_AFSDB = 0x0012
- DNS_TYPE_X25 = 0x0013
- DNS_TYPE_ISDN = 0x0014
- DNS_TYPE_RT = 0x0015
- DNS_TYPE_NSAP = 0x0016
- DNS_TYPE_NSAPPTR = 0x0017
- DNS_TYPE_SIG = 0x0018
- DNS_TYPE_KEY = 0x0019
- DNS_TYPE_PX = 0x001a
- DNS_TYPE_GPOS = 0x001b
- DNS_TYPE_AAAA = 0x001c
- DNS_TYPE_LOC = 0x001d
- DNS_TYPE_NXT = 0x001e
- DNS_TYPE_EID = 0x001f
- DNS_TYPE_NIMLOC = 0x0020
- DNS_TYPE_SRV = 0x0021
- DNS_TYPE_ATMA = 0x0022
- DNS_TYPE_NAPTR = 0x0023
- DNS_TYPE_KX = 0x0024
- DNS_TYPE_CERT = 0x0025
- DNS_TYPE_A6 = 0x0026
- DNS_TYPE_DNAME = 0x0027
- DNS_TYPE_SINK = 0x0028
- DNS_TYPE_OPT = 0x0029
- DNS_TYPE_DS = 0x002B
- DNS_TYPE_RRSIG = 0x002E
- DNS_TYPE_NSEC = 0x002F
- DNS_TYPE_DNSKEY = 0x0030
- DNS_TYPE_DHCID = 0x0031
- DNS_TYPE_UINFO = 0x0064
- DNS_TYPE_UID = 0x0065
- DNS_TYPE_GID = 0x0066
- DNS_TYPE_UNSPEC = 0x0067
- DNS_TYPE_ADDRS = 0x00f8
- DNS_TYPE_TKEY = 0x00f9
- DNS_TYPE_TSIG = 0x00fa
- DNS_TYPE_IXFR = 0x00fb
- DNS_TYPE_AXFR = 0x00fc
- DNS_TYPE_MAILB = 0x00fd
- DNS_TYPE_MAILA = 0x00fe
- DNS_TYPE_ALL = 0x00ff
- DNS_TYPE_ANY = 0x00ff
- DNS_TYPE_WINS = 0xff01
- DNS_TYPE_WINSR = 0xff02
- DNS_TYPE_NBSTAT = 0xff01
-)
-
-type DNSSRVData struct {
- Target *uint16
- Priority uint16
- Weight uint16
- Port uint16
- Pad uint16
-}
-
-type DNSPTRData struct {
- Host *uint16
-}
-
-type DNSRecord struct {
- Next *DNSRecord
- Name *uint16
- Type uint16
- Length uint16
- Dw uint32
- Ttl uint32
- Reserved uint32
- Data [40]byte
-}
-
-const (
- TF_DISCONNECT = 1
- TF_REUSE_SOCKET = 2
- TF_WRITE_BEHIND = 4
- TF_USE_DEFAULT_WORKER = 0
- TF_USE_SYSTEM_THREAD = 16
- TF_USE_KERNEL_APC = 32
-)
-
-type TransmitFileBuffers struct {
- Head uintptr
- HeadLength uint32
- Tail uintptr
- TailLength uint32
-}
-
-const (
- IFF_UP = 1
- IFF_BROADCAST = 2
- IFF_LOOPBACK = 4
- IFF_POINTTOPOINT = 8
- IFF_MULTICAST = 16
-)
-
-const SIO_GET_INTERFACE_LIST = 0x4004747F
-
-// TODO(mattn): SockaddrGen is union of sockaddr/sockaddr_in/sockaddr_in6_old.
-// will be fixed to change variable type as suitable.
-
-type SockaddrGen [24]byte
-
-type InterfaceInfo struct {
- Flags uint32
- Address SockaddrGen
- BroadcastAddress SockaddrGen
- Netmask SockaddrGen
-}
-
-type IpAddressString struct {
- String [16]byte
-}
-
-type IpMaskString IpAddressString
-
-type IpAddrString struct {
- Next *IpAddrString
- IpAddress IpAddressString
- IpMask IpMaskString
- Context uint32
-}
-
-const MAX_ADAPTER_NAME_LENGTH = 256
-const MAX_ADAPTER_DESCRIPTION_LENGTH = 128
-const MAX_ADAPTER_ADDRESS_LENGTH = 8
-
-type IpAdapterInfo struct {
- Next *IpAdapterInfo
- ComboIndex uint32
- AdapterName [MAX_ADAPTER_NAME_LENGTH + 4]byte
- Description [MAX_ADAPTER_DESCRIPTION_LENGTH + 4]byte
- AddressLength uint32
- Address [MAX_ADAPTER_ADDRESS_LENGTH]byte
- Index uint32
- Type uint32
- DhcpEnabled uint32
- CurrentIpAddress *IpAddrString
- IpAddressList IpAddrString
- GatewayList IpAddrString
- DhcpServer IpAddrString
- HaveWins bool
- PrimaryWinsServer IpAddrString
- SecondaryWinsServer IpAddrString
- LeaseObtained int64
- LeaseExpires int64
-}
-
-const MAXLEN_PHYSADDR = 8
-const MAX_INTERFACE_NAME_LEN = 256
-const MAXLEN_IFDESCR = 256
-
-type MibIfRow struct {
- Name [MAX_INTERFACE_NAME_LEN]uint16
- Index uint32
- Type uint32
- Mtu uint32
- Speed uint32
- PhysAddrLen uint32
- PhysAddr [MAXLEN_PHYSADDR]byte
- AdminStatus uint32
- OperStatus uint32
- LastChange uint32
- InOctets uint32
- InUcastPkts uint32
- InNUcastPkts uint32
- InDiscards uint32
- InErrors uint32
- InUnknownProtos uint32
- OutOctets uint32
- OutUcastPkts uint32
- OutNUcastPkts uint32
- OutDiscards uint32
- OutErrors uint32
- OutQLen uint32
- DescrLen uint32
- Descr [MAXLEN_IFDESCR]byte
-}
+package syscall
diff --git a/src/pkg/syscall/ztypes_windows_amd64.go b/src/pkg/syscall/ztypes_windows_amd64.go
new file mode 100644
index 000000000..d1008bd03
--- /dev/null
+++ b/src/pkg/syscall/ztypes_windows_amd64.go
@@ -0,0 +1,5 @@
+// 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 syscall
diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go
index 5bc7ff7e9..464b620c9 100644
--- a/src/pkg/template/execute.go
+++ b/src/pkg/template/execute.go
@@ -114,7 +114,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
if s == "@" {
return indirectPtr(data, numStars)
}
- for _, elem := range strings.Split(s, ".", -1) {
+ for _, elem := range strings.Split(s, ".") {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
if !data.IsValid() {
diff --git a/src/pkg/template/parse.go b/src/pkg/template/parse.go
index b4aa5fcd2..dedf9ad8e 100644
--- a/src/pkg/template/parse.go
+++ b/src/pkg/template/parse.go
@@ -483,7 +483,7 @@ func extractFormatters(words []string) (formatters []string) {
}
}
words[len(words)-1] = lastWord[0:bar]
- formatters = strings.Split(lastWord[bar+1:], "|", -1)
+ formatters = strings.Split(lastWord[bar+1:], "|")
return
}
diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go
index f8b53e63a..3b416acfa 100644
--- a/src/pkg/testing/benchmark.go
+++ b/src/pkg/testing/benchmark.go
@@ -13,6 +13,7 @@ import (
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
+var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
@@ -34,7 +35,11 @@ type B struct {
// 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() { b.start = time.Nanoseconds() }
+func (b *B) StartTimer() {
+ if b.start == 0 {
+ b.start = time.Nanoseconds()
+ }
+}
// StopTimer stops timing a test. This can be used to pause the timer
// while performing complex initialization that you don't
@@ -46,9 +51,12 @@ func (b *B) StopTimer() {
b.start = 0
}
-// ResetTimer stops the timer and sets the elapsed benchmark time to zero.
+// ResetTimer sets the elapsed benchmark time to zero.
+// It does not affect whether the timer is running.
func (b *B) ResetTimer() {
- b.start = 0
+ if b.start > 0 {
+ b.start = time.Nanoseconds()
+ }
b.ns = 0
}
@@ -125,14 +133,15 @@ func (b *B) run() BenchmarkResult {
// Run the benchmark for a single iteration in case it's expensive.
n := 1
b.runN(n)
- // Run the benchmark for at least a second.
- for b.ns < 1e9 && n < 1e9 {
+ // Run the benchmark for at least the specified amount of time.
+ time := int64(*benchTime * 1e9)
+ for b.ns < time && n < 1e9 {
last := n
// Predict iterations/sec.
if b.nsPerOp() == 0 {
n = 1e9
} else {
- n = 1e9 / int(b.nsPerOp())
+ n = int(time / 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.
@@ -172,7 +181,18 @@ func (r BenchmarkResult) String() string {
if mbs != 0 {
mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
}
- return fmt.Sprintf("%8d\t%10d ns/op%s", r.N, r.NsPerOp(), mb)
+ nsop := r.NsPerOp()
+ ns := fmt.Sprintf("%10d ns/op", nsop)
+ if r.N > 0 && nsop < 100 {
+ // 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))
+ } else {
+ ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N))
+ }
+ }
+ return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
}
// An internal function but exported because it is cross-package; part of the implementation
@@ -182,7 +202,6 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark
if len(*matchBenchmarks) == 0 {
return
}
- procs := runtime.GOMAXPROCS(-1)
for _, Benchmark := range benchmarks {
matched, err := matchString(*matchBenchmarks, Benchmark.Name)
if err != nil {
@@ -192,14 +211,19 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark
if !matched {
continue
}
- b := &B{benchmark: Benchmark}
- r := b.run()
- print(fmt.Sprintf("%s\t%v\n", Benchmark.Name, r))
- if p := runtime.GOMAXPROCS(-1); p != procs {
- print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", Benchmark.Name, p))
- procs = p
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ b := &B{benchmark: Benchmark}
+ r := b.run()
+ benchName := Benchmark.Name
+ if procs != 1 {
+ benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
+ }
+ print(fmt.Sprintf("%s\t%v\n", benchName, r))
+ if p := runtime.GOMAXPROCS(-1); p != procs {
+ print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p))
+ }
}
-
}
}
diff --git a/src/pkg/testing/iotest/reader.go b/src/pkg/testing/iotest/reader.go
index e4003d744..daa6ede08 100644
--- a/src/pkg/testing/iotest/reader.go
+++ b/src/pkg/testing/iotest/reader.go
@@ -58,7 +58,7 @@ func (r *dataErrReader) Read(p []byte) (n int, err os.Error) {
r.unread = r.data[0:n1]
err = err1
}
- if n > 0 {
+ if n > 0 || err != nil {
break
}
n = copy(p, r.unread)
@@ -66,3 +66,22 @@ func (r *dataErrReader) Read(p []byte) (n int, err os.Error) {
}
return
}
+
+var ErrTimeout = os.NewError("timeout")
+
+// TimeoutReader returns ErrTimeout on the second read
+// with no data. Subsequent calls to read succeed.
+func TimeoutReader(r io.Reader) io.Reader { return &timeoutReader{r, 0} }
+
+type timeoutReader struct {
+ r io.Reader
+ count int
+}
+
+func (r *timeoutReader) Read(p []byte) (int, os.Error) {
+ r.count++
+ if r.count == 2 {
+ return 0, ErrTimeout
+ }
+ return r.r.Read(p)
+}
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index 3b2dd377a..ba721523e 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -44,6 +44,8 @@ import (
"os"
"runtime"
"runtime/pprof"
+ "strings"
+ "strconv"
"time"
)
@@ -62,6 +64,9 @@ var (
memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
+ cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
+
+ cpuList []int
)
// Short reports whether the -test.short flag is set.
@@ -157,6 +162,7 @@ func tRunner(t *T, test *InternalTest) {
// of gotest.
func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest, benchmarks []InternalBenchmark) {
flag.Parse()
+ parseCpuList()
before()
startAlarm()
@@ -171,7 +177,6 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern
if len(tests) == 0 {
println("testing: warning: no tests to run")
}
- procs := runtime.GOMAXPROCS(-1)
for i := 0; i < len(tests); i++ {
matched, err := matchString(*match, tests[i].Name)
if err != nil {
@@ -181,28 +186,34 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern
if !matched {
continue
}
- if *chatty {
- println("=== RUN ", tests[i].Name)
- }
- ns := -time.Nanoseconds()
- t := new(T)
- t.ch = make(chan *T)
- go tRunner(t, &tests[i])
- <-t.ch
- ns += time.Nanoseconds()
- tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
- if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs {
- t.failed = true
- t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", tests[i].Name, p)
- procs = p
- }
- if t.failed {
- println("--- FAIL:", tests[i].Name, tstr)
- print(t.errors)
- ok = false
- } else if *chatty {
- println("--- PASS:", tests[i].Name, tstr)
- print(t.errors)
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ testName := tests[i].Name
+ if procs != 1 {
+ testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
+ }
+ if *chatty {
+ println("=== RUN ", testName)
+ }
+ ns := -time.Nanoseconds()
+ t := new(T)
+ t.ch = make(chan *T)
+ go tRunner(t, &tests[i])
+ <-t.ch
+ ns += time.Nanoseconds()
+ tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
+ if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs {
+ t.failed = true
+ t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", testName, p)
+ }
+ if t.failed {
+ println("--- FAIL:", testName, tstr)
+ print(t.errors)
+ ok = false
+ } else if *chatty {
+ println("--- PASS:", testName, tstr)
+ print(t.errors)
+ }
}
}
if !ok {
@@ -271,3 +282,18 @@ func stopAlarm() {
func alarm() {
panic("test timed out")
}
+
+func parseCpuList() {
+ if len(*cpuListStr) == 0 {
+ cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
+ } else {
+ for _, val := range strings.Split(*cpuListStr, ",") {
+ cpu, err := strconv.Atoi(val)
+ if err != nil || cpu <= 0 {
+ println("invalid value for -test.cpu")
+ os.Exit(1)
+ }
+ cpuList = append(cpuList, cpu)
+ }
+ }
+}
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index e0f56129e..26f40d141 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -248,7 +248,7 @@ func (t *Time) Format(layout string) string {
var p string
switch std {
case stdYear:
- p = strconv.Itoa64(t.Year % 100)
+ p = zeroPad(int(t.Year % 100))
case stdLongYear:
p = strconv.Itoa64(t.Year)
case stdMonth:
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index 25e79f9fb..a4a1a429f 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -172,7 +172,7 @@ func testAfterQueuing(t *testing.T) os.Error {
for _, slot := range slots {
go await(slot, result, After(int64(slot)*Delta))
}
- sort.SortInts(slots)
+ sort.Ints(slots)
for _, slot := range slots {
r := <-result
if r.slot != slot {
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index eb676bf64..eec8a7a5c 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -142,21 +142,22 @@ type FormatTest struct {
}
var formatTests = []FormatTest{
- {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010"},
- {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010"},
- {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010"},
- {"RFC822", RFC822, "04 Feb 10 2100 PST"},
- {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST"},
- {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST"},
- {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00"},
+ {"ANSIC", ANSIC, "Wed Feb 4 21:00:57 2009"},
+ {"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"},
+ {"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"},
+ {"RFC822", RFC822, "04 Feb 09 2100 PST"},
+ {"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
+ {"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
+ {"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"},
{"Kitchen", Kitchen, "9:00PM"},
{"am/pm", "3pm", "9pm"},
{"AM/PM", "3PM", "9PM"},
+ {"two-digit year", "06 01 02", "09 02 04"},
}
func TestFormat(t *testing.T) {
// The numeric time represents Thu Feb 4 21:00:57 PST 2010
- time := SecondsToLocalTime(1265346057)
+ time := SecondsToLocalTime(1233810057)
for _, test := range formatTests {
result := time.Format(test.format)
if result != test.result {
diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go
index 421d29455..07b931d7e 100644
--- a/src/pkg/unicode/maketables.go
+++ b/src/pkg/unicode/maketables.go
@@ -73,7 +73,7 @@ var category = map[string]bool{
// UnicodeData.txt has form:
// 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
// 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
-// See http://www.unicode.org/Public/5.1.0/ucd/UCD.html for full explanation
+// See http://www.unicode.org/reports/tr44/ for a full explanation
// The fields:
const (
FCodePoint = iota
@@ -81,10 +81,10 @@ const (
FGeneralCategory
FCanonicalCombiningClass
FBidiClass
- FDecompositionType
- FDecompositionMapping
+ FDecompositionTypeAndMapping
FNumericType
- FNumericValue
+ FNumericDigit // If a decimal digit.
+ FNumericValue // Includes non-decimal, e.g. U+2155=1/5
FBidiMirrored
FUnicode1Name
FISOComment
@@ -97,21 +97,21 @@ const (
)
var fieldName = []string{
- "CodePoint",
- "Name",
- "GeneralCategory",
- "CanonicalCombiningClass",
- "BidiClass",
- "DecompositionType",
- "DecompositionMapping",
- "NumericType",
- "NumericValue",
- "BidiMirrored",
- "Unicode1Name",
- "ISOComment",
- "SimpleUppercaseMapping",
- "SimpleLowercaseMapping",
- "SimpleTitlecaseMapping",
+ FCodePoint: "CodePoint",
+ FName: "Name",
+ FGeneralCategory: "GeneralCategory",
+ FCanonicalCombiningClass: "CanonicalCombiningClass",
+ FBidiClass: "BidiClass",
+ FDecompositionTypeAndMapping: "DecompositionTypeAndMapping",
+ FNumericType: "NumericType",
+ FNumericDigit: "NumericDigit",
+ FNumericValue: "NumericValue",
+ FBidiMirrored: "BidiMirrored",
+ FUnicode1Name: "Unicode1Name",
+ FISOComment: "ISOComment",
+ FSimpleUppercaseMapping: "SimpleUppercaseMapping",
+ FSimpleLowercaseMapping: "SimpleLowercaseMapping",
+ FSimpleTitlecaseMapping: "SimpleTitlecaseMapping",
}
// This contains only the properties we're interested in.
@@ -156,7 +156,7 @@ const (
)
func parseCategory(line string) (state State) {
- field := strings.Split(line, ";", -1)
+ field := strings.Split(line, ";")
if len(field) != NumField {
logger.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField)
}
@@ -253,7 +253,7 @@ func all(scripts map[string][]Script) []string {
// Extract the version number from the URL
func version() string {
// Break on slashes and look for the first numeric field
- fields := strings.Split(*url, "/", -1)
+ fields := strings.Split(*url, "/")
for _, f := range fields {
if len(f) > 0 && '0' <= f[0] && f[0] <= '9' {
return f
@@ -336,7 +336,7 @@ func loadCasefold() {
if line[0] == '#' {
continue
}
- field := strings.Split(line, "; ", -1)
+ field := strings.Split(line, "; ")
if len(field) != 4 {
logger.Fatalf("CaseFolding.txt %.5s...: %d fields (expected %d)\n", line, len(field), 4)
}
@@ -372,7 +372,7 @@ func printCategories() {
return
}
// Find out which categories to dump
- list := strings.Split(*tablelist, ",", -1)
+ list := strings.Split(*tablelist, ",")
if *tablelist == "all" {
list = allCategories()
}
@@ -588,7 +588,7 @@ func parseScript(line string, scripts map[string][]Script) {
if len(line) == 0 {
return
}
- field := strings.Split(line, ";", -1)
+ field := strings.Split(line, ";")
if len(field) != 2 {
logger.Fatalf("%s: %d fields (expected 2)\n", line, len(field))
}
@@ -685,7 +685,7 @@ func printScriptOrProperty(doProps bool) {
resp.Body.Close()
// Find out which scripts to dump
- list := strings.Split(flaglist, ",", -1)
+ list := strings.Split(flaglist, ",")
if flaglist == "all" {
list = all(table)
}
@@ -1042,7 +1042,7 @@ func printCasefold() {
if orb == nil {
continue
}
- sort.SortInts(orb)
+ sort.Ints(orb)
c := orb[len(orb)-1]
for _, d := range orb {
chars[c].caseOrbit = d
diff --git a/src/pkg/xml/Makefile b/src/pkg/xml/Makefile
index b780face6..d66c4988a 100644
--- a/src/pkg/xml/Makefile
+++ b/src/pkg/xml/Makefile
@@ -7,6 +7,7 @@ include ../../Make.inc
TARG=xml
GOFILES=\
+ marshal.go\
read.go\
xml.go\
diff --git a/src/pkg/xml/atom_test.go b/src/pkg/xml/atom_test.go
new file mode 100644
index 000000000..d365510bf
--- /dev/null
+++ b/src/pkg/xml/atom_test.go
@@ -0,0 +1,50 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+var atomValue = &Feed{
+ Title: "Example Feed",
+ Link: []Link{{Href: "http://example.org/"}},
+ Updated: ParseTime("2003-12-13T18:30:02Z"),
+ Author: Person{Name: "John Doe"},
+ Id: "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6",
+
+ Entry: []Entry{
+ {
+ Title: "Atom-Powered Robots Run Amok",
+ Link: []Link{{Href: "http://example.org/2003/12/13/atom03"}},
+ Id: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
+ Updated: ParseTime("2003-12-13T18:30:02Z"),
+ Summary: NewText("Some text."),
+ },
+ },
+}
+
+var atomXml = `` +
+ `<feed xmlns="http://www.w3.org/2005/Atom">` +
+ `<Title>Example Feed</Title>` +
+ `<Id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</Id>` +
+ `<Link href="http://example.org/"></Link>` +
+ `<Updated>2003-12-13T18:30:02Z</Updated>` +
+ `<Author><Name>John Doe</Name><URI></URI><Email></Email></Author>` +
+ `<Entry>` +
+ `<Title>Atom-Powered Robots Run Amok</Title>` +
+ `<Id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</Id>` +
+ `<Link href="http://example.org/2003/12/13/atom03"></Link>` +
+ `<Updated>2003-12-13T18:30:02Z</Updated>` +
+ `<Author><Name></Name><URI></URI><Email></Email></Author>` +
+ `<Summary>Some text.</Summary>` +
+ `</Entry>` +
+ `</feed>`
+
+func ParseTime(str string) Time {
+ return Time(str)
+}
+
+func NewText(text string) Text {
+ return Text{
+ Body: text,
+ }
+}
diff --git a/src/pkg/xml/embed_test.go b/src/pkg/xml/embed_test.go
index abfe781ac..ec7f478be 100644
--- a/src/pkg/xml/embed_test.go
+++ b/src/pkg/xml/embed_test.go
@@ -12,14 +12,14 @@ type C struct {
}
type A struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
C
B B
FieldA string
}
type B struct {
- XMLName Name "b"
+ XMLName Name `xml:"b"`
C
FieldB string
}
@@ -65,7 +65,7 @@ func TestEmbedded1(t *testing.T) {
}
type A2 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
XY string
Xy string
}
@@ -92,7 +92,7 @@ func TestEmbedded2(t *testing.T) {
}
type A3 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
xy string
}
@@ -108,7 +108,7 @@ func TestEmbedded3(t *testing.T) {
}
type A4 struct {
- XMLName Name "http://domain a"
+ XMLName Name `xml:"http://domain a"`
Any string
}
diff --git a/src/pkg/xml/marshal.go b/src/pkg/xml/marshal.go
new file mode 100644
index 000000000..2ac03a91e
--- /dev/null
+++ b/src/pkg/xml/marshal.go
@@ -0,0 +1,228 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "bufio"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+const (
+ // A generic XML header suitable for use with the output of Marshal and MarshalIndent.
+ // This is not automatically added to any output of this package, it is provided as a
+ // convenience.
+ Header = `<?xml version="1.0" encoding="UTF-8">\n`
+)
+
+// A Marshaler can produce well-formatted XML representing its internal state.
+// It is used by both Marshal and MarshalIndent.
+type Marshaler interface {
+ MarshalXML() ([]byte, os.Error)
+}
+
+type printer struct {
+ *bufio.Writer
+}
+
+// Marshal writes an XML-formatted representation of v to w.
+//
+// If v implements Marshaler, then Marshal calls its MarshalXML method.
+// Otherwise, Marshal uses the following procedure to create the XML.
+//
+// Marshal handles an array or slice by marshalling each of the elements.
+// Marshal handles a pointer by marshalling the value it points at or, if the
+// pointer is nil, by writing nothing. Marshal handles an interface value by
+// marshalling the value it contains or, if the interface value is nil, by
+// writing nothing. Marshal handles all other data by writing a single XML
+// element containing the data.
+//
+// The name of that XML element is taken from, in order of preference:
+// - the tag on an XMLName field, if the data is a struct
+// - the value of an XMLName field of type xml.Name
+// - the tag of the struct field used to obtain the data
+// - the name of the struct field used to obtain the data
+// - the name '???'.
+//
+// The XML element for a struct contains marshalled elements for each of the
+// exported fields of the struct, with these exceptions:
+// - the XMLName field, described above, is omitted.
+// - a field with tag "attr" becomes an attribute in the XML element.
+// - a field with tag "chardata" is written as character data,
+// not as an XML element.
+// - a field with tag "innerxml" is written verbatim,
+// not subject to the usual marshalling procedure.
+//
+// Marshal will return an error if asked to marshal a channel, function, or map.
+func Marshal(w io.Writer, v interface{}) (err os.Error) {
+ p := &printer{bufio.NewWriter(w)}
+ err = p.marshalValue(reflect.ValueOf(v), "???")
+ p.Flush()
+ return err
+}
+
+func (p *printer) marshalValue(val reflect.Value, name string) os.Error {
+ if !val.IsValid() {
+ return nil
+ }
+
+ kind := val.Kind()
+ typ := val.Type()
+
+ // Try Marshaler
+ if typ.NumMethod() > 0 {
+ if marshaler, ok := val.Interface().(Marshaler); ok {
+ bytes, err := marshaler.MarshalXML()
+ if err != nil {
+ return err
+ }
+ p.Write(bytes)
+ return nil
+ }
+ }
+
+ // Drill into pointers/interfaces
+ if kind == reflect.Ptr || kind == reflect.Interface {
+ if val.IsNil() {
+ return nil
+ }
+ return p.marshalValue(val.Elem(), name)
+ }
+
+ // Slices and arrays iterate over the elements. They do not have an enclosing tag.
+ if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
+ for i, n := 0, val.Len(); i < n; i++ {
+ if err := p.marshalValue(val.Index(i), name); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ // Find XML name
+ xmlns := ""
+ if kind == reflect.Struct {
+ if f, ok := typ.FieldByName("XMLName"); ok {
+ if tag := f.Tag.Get("xml"); tag != "" {
+ if i := strings.Index(tag, " "); i >= 0 {
+ xmlns, name = tag[:i], tag[i+1:]
+ } else {
+ name = tag
+ }
+ } else if v, ok := val.FieldByIndex(f.Index).Interface().(Name); ok && v.Local != "" {
+ xmlns, name = v.Space, v.Local
+ }
+ }
+ }
+
+ p.WriteByte('<')
+ p.WriteString(name)
+
+ // Attributes
+ if kind == reflect.Struct {
+ if len(xmlns) > 0 {
+ p.WriteString(` xmlns="`)
+ Escape(p, []byte(xmlns))
+ p.WriteByte('"')
+ }
+
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" {
+ if f.Type.Kind() == reflect.String {
+ if str := val.Field(i).String(); str != "" {
+ p.WriteByte(' ')
+ p.WriteString(strings.ToLower(f.Name))
+ p.WriteString(`="`)
+ Escape(p, []byte(str))
+ p.WriteByte('"')
+ }
+ }
+ }
+ }
+ }
+ p.WriteByte('>')
+
+ switch k := val.Kind(); k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p.WriteString(strconv.Itoa64(val.Int()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p.WriteString(strconv.Uitoa64(val.Uint()))
+ case reflect.Float32, reflect.Float64:
+ p.WriteString(strconv.Ftoa64(val.Float(), 'g', -1))
+ case reflect.String:
+ Escape(p, []byte(val.String()))
+ case reflect.Bool:
+ p.WriteString(strconv.Btoa(val.Bool()))
+ case reflect.Array:
+ // will be [...]byte
+ bytes := make([]byte, val.Len())
+ for i := range bytes {
+ bytes[i] = val.Index(i).Interface().(byte)
+ }
+ Escape(p, bytes)
+ case reflect.Slice:
+ // will be []byte
+ bytes := val.Interface().([]byte)
+ Escape(p, bytes)
+ case reflect.Struct:
+ for i, n := 0, val.NumField(); i < n; i++ {
+ if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" {
+ name := f.Name
+ switch tag := f.Tag.Get("xml"); tag {
+ case "":
+ case "chardata":
+ if tk := f.Type.Kind(); tk == reflect.String {
+ p.Write([]byte(val.Field(i).String()))
+ } else if tk == reflect.Slice {
+ if elem, ok := val.Field(i).Interface().([]byte); ok {
+ Escape(p, elem)
+ }
+ }
+ continue
+ case "innerxml":
+ iface := val.Field(i).Interface()
+ switch raw := iface.(type) {
+ case []byte:
+ p.Write(raw)
+ continue
+ case string:
+ p.WriteString(raw)
+ continue
+ }
+ case "attr":
+ continue
+ default:
+ name = tag
+ }
+
+ if err := p.marshalValue(val.Field(i), name); err != nil {
+ return err
+ }
+ }
+ }
+ default:
+ return &UnsupportedTypeError{typ}
+ }
+
+ p.WriteByte('<')
+ p.WriteByte('/')
+ p.WriteString(name)
+ p.WriteByte('>')
+
+ return nil
+}
+
+// A MarshalXMLError is returned when Marshal or MarshalIndent encounter a type
+// that cannot be converted into XML.
+type UnsupportedTypeError struct {
+ Type reflect.Type
+}
+
+func (e *UnsupportedTypeError) String() string {
+ return "xml: unsupported type: " + e.Type.String()
+}
diff --git a/src/pkg/xml/marshal_test.go b/src/pkg/xml/marshal_test.go
new file mode 100644
index 000000000..77b2e726d
--- /dev/null
+++ b/src/pkg/xml/marshal_test.go
@@ -0,0 +1,299 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "reflect"
+ "testing"
+
+ "os"
+ "bytes"
+ "strings"
+ "strconv"
+)
+
+type DriveType int
+
+const (
+ HyperDrive DriveType = iota
+ ImprobabilityDrive
+)
+
+type Passenger struct {
+ Name []string `xml:"name"`
+ Weight float32 `xml:"weight"`
+}
+
+type Ship struct {
+ XMLName Name `xml:"spaceship"`
+
+ Name string `xml:"attr"`
+ Pilot string `xml:"attr"`
+ Drive DriveType `xml:"drive"`
+ Age uint `xml:"age"`
+ Passenger []*Passenger `xml:"passenger"`
+ secret string
+}
+
+type RawXML string
+
+func (rx RawXML) MarshalXML() ([]byte, os.Error) {
+ return []byte(rx), nil
+}
+
+type NamedType string
+
+type Port struct {
+ XMLName Name `xml:"port"`
+ Type string `xml:"attr"`
+ Number string `xml:"chardata"`
+}
+
+type Domain struct {
+ XMLName Name `xml:"domain"`
+ Country string `xml:"attr"`
+ Name []byte `xml:"chardata"`
+}
+
+type SecretAgent struct {
+ XMLName Name `xml:"agent"`
+ Handle string `xml:"attr"`
+ Identity string
+ Obfuscate string `xml:"innerxml"`
+}
+
+var nilStruct *Ship
+
+var marshalTests = []struct {
+ Value interface{}
+ ExpectXML string
+}{
+ // Test nil marshals to nothing
+ {Value: nil, ExpectXML: ``},
+ {Value: nilStruct, ExpectXML: ``},
+
+ // Test value types (no tag name, so ???)
+ {Value: true, ExpectXML: `<???>true</???>`},
+ {Value: int(42), ExpectXML: `<???>42</???>`},
+ {Value: int8(42), ExpectXML: `<???>42</???>`},
+ {Value: int16(42), ExpectXML: `<???>42</???>`},
+ {Value: int32(42), ExpectXML: `<???>42</???>`},
+ {Value: uint(42), ExpectXML: `<???>42</???>`},
+ {Value: uint8(42), ExpectXML: `<???>42</???>`},
+ {Value: uint16(42), ExpectXML: `<???>42</???>`},
+ {Value: uint32(42), ExpectXML: `<???>42</???>`},
+ {Value: float32(1.25), ExpectXML: `<???>1.25</???>`},
+ {Value: float64(1.25), ExpectXML: `<???>1.25</???>`},
+ {Value: uintptr(0xFFDD), ExpectXML: `<???>65501</???>`},
+ {Value: "gopher", ExpectXML: `<???>gopher</???>`},
+ {Value: []byte("gopher"), ExpectXML: `<???>gopher</???>`},
+ {Value: "</>", ExpectXML: `<???>&lt;/&gt;</???>`},
+ {Value: []byte("</>"), ExpectXML: `<???>&lt;/&gt;</???>`},
+ {Value: [3]byte{'<', '/', '>'}, ExpectXML: `<???>&lt;/&gt;</???>`},
+ {Value: NamedType("potato"), ExpectXML: `<???>potato</???>`},
+ {Value: []int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`},
+ {Value: [3]int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`},
+
+ // Test innerxml
+ {Value: RawXML("</>"), ExpectXML: `</>`},
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<redacted/>",
+ },
+ //ExpectXML: `<agent handle="007"><redacted/></agent>`,
+ ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
+ },
+
+ // Test structs
+ {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
+ {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
+ {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
+ {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
+ {Value: atomValue, ExpectXML: atomXml},
+ {
+ Value: &Ship{
+ Name: "Heart of Gold",
+ Pilot: "Computer",
+ Age: 1,
+ Drive: ImprobabilityDrive,
+ Passenger: []*Passenger{
+ &Passenger{
+ Name: []string{"Zaphod", "Beeblebrox"},
+ Weight: 7.25,
+ },
+ &Passenger{
+ Name: []string{"Trisha", "McMillen"},
+ Weight: 5.5,
+ },
+ &Passenger{
+ Name: []string{"Ford", "Prefect"},
+ Weight: 7,
+ },
+ &Passenger{
+ Name: []string{"Arthur", "Dent"},
+ Weight: 6.75,
+ },
+ },
+ },
+ ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
+ `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
+ `<age>1</age>` +
+ `<passenger>` +
+ `<name>Zaphod</name>` +
+ `<name>Beeblebrox</name>` +
+ `<weight>7.25</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Trisha</name>` +
+ `<name>McMillen</name>` +
+ `<weight>5.5</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Ford</name>` +
+ `<name>Prefect</name>` +
+ `<weight>7</weight>` +
+ `</passenger>` +
+ `<passenger>` +
+ `<name>Arthur</name>` +
+ `<name>Dent</name>` +
+ `<weight>6.75</weight>` +
+ `</passenger>` +
+ `</spaceship>`,
+ },
+}
+
+func TestMarshal(t *testing.T) {
+ for idx, test := range marshalTests {
+ buf := bytes.NewBuffer(nil)
+ err := Marshal(buf, test.Value)
+ if err != nil {
+ t.Errorf("#%d: Error: %s", idx, err)
+ continue
+ }
+ if got, want := buf.String(), test.ExpectXML; got != want {
+ if strings.Contains(want, "\n") {
+ t.Errorf("#%d: marshal(%#v) - GOT:\n%s\nWANT:\n%s", idx, test.Value, got, want)
+ } else {
+ t.Errorf("#%d: marshal(%#v) = %#q want %#q", idx, test.Value, got, want)
+ }
+ }
+ }
+}
+
+var marshalErrorTests = []struct {
+ Value interface{}
+ ExpectErr string
+ ExpectKind reflect.Kind
+}{
+ {
+ Value: make(chan bool),
+ ExpectErr: "xml: unsupported type: chan bool",
+ ExpectKind: reflect.Chan,
+ },
+ {
+ Value: map[string]string{
+ "question": "What do you get when you multiply six by nine?",
+ "answer": "42",
+ },
+ ExpectErr: "xml: unsupported type: map[string] string",
+ ExpectKind: reflect.Map,
+ },
+ {
+ Value: map[*Ship]bool{nil: false},
+ ExpectErr: "xml: unsupported type: map[*xml.Ship] bool",
+ ExpectKind: reflect.Map,
+ },
+}
+
+func TestMarshalErrors(t *testing.T) {
+ for idx, test := range marshalErrorTests {
+ buf := bytes.NewBuffer(nil)
+ err := Marshal(buf, test.Value)
+ if got, want := err, test.ExpectErr; got == nil {
+ t.Errorf("#%d: want error %s", idx, want)
+ continue
+ } else if got.String() != want {
+ t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, got, want)
+ }
+ if got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKind; got != want {
+ t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, got, want)
+ }
+ }
+}
+
+// Do invertibility testing on the various structures that we test
+func TestUnmarshal(t *testing.T) {
+ for i, test := range marshalTests {
+ // Skip the nil pointers
+ if i <= 1 {
+ continue
+ }
+
+ var dest interface{}
+
+ switch test.Value.(type) {
+ case *Ship, Ship:
+ dest = &Ship{}
+ case *Port, Port:
+ dest = &Port{}
+ case *Domain, Domain:
+ dest = &Domain{}
+ case *Feed, Feed:
+ dest = &Feed{}
+ default:
+ continue
+ }
+
+ buffer := bytes.NewBufferString(test.ExpectXML)
+ err := Unmarshal(buffer, dest)
+
+ // Don't compare XMLNames
+ switch fix := dest.(type) {
+ case *Ship:
+ fix.XMLName = Name{}
+ case *Port:
+ fix.XMLName = Name{}
+ case *Domain:
+ fix.XMLName = Name{}
+ case *Feed:
+ fix.XMLName = Name{}
+ fix.Author.InnerXML = ""
+ for i := range fix.Entry {
+ fix.Entry[i].Author.InnerXML = ""
+ }
+ }
+
+ if err != nil {
+ t.Errorf("#%d: unexpected error: %#v", i, err)
+ } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
+ t.Errorf("#%d: unmarshal(%#s) = %#v, want %#v", i, test.ExpectXML, got, want)
+ }
+ }
+}
+
+func BenchmarkMarshal(b *testing.B) {
+ idx := len(marshalTests) - 1
+ test := marshalTests[idx]
+
+ buf := bytes.NewBuffer(nil)
+ for i := 0; i < b.N; i++ {
+ Marshal(buf, test.Value)
+ buf.Truncate(0)
+ }
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ idx := len(marshalTests) - 1
+ test := marshalTests[idx]
+ sm := &Ship{}
+ xml := []byte(test.ExpectXML)
+
+ for i := 0; i < b.N; i++ {
+ buffer := bytes.NewBuffer(xml)
+ Unmarshal(buffer, sm)
+ }
+}
diff --git a/src/pkg/xml/read.go b/src/pkg/xml/read.go
index 427c31158..786b69f5a 100644
--- a/src/pkg/xml/read.go
+++ b/src/pkg/xml/read.go
@@ -31,16 +31,16 @@ import (
// For example, given these definitions:
//
// type Email struct {
-// Where string "attr"
+// Where string `xml:"attr"`
// Addr string
// }
//
// type Result struct {
-// XMLName xml.Name "result"
+// XMLName xml.Name `xml:"result"`
// Name string
// Phone string
// Email []Email
-// Groups []string "group>value"
+// Groups []string `xml:"group>value"`
// }
//
// result := Result{Name: "name", Phone: "phone", Email: nil}
@@ -79,11 +79,13 @@ import (
// Groups was assigned considering the element path provided in the
// field tag.
//
-// Because Unmarshal uses the reflect package, it can only
-// assign to upper case fields. Unmarshal uses a case-insensitive
+// Because Unmarshal uses the reflect package, it can only assign
+// to exported (upper case) fields. Unmarshal uses a case-insensitive
// comparison to match XML element names to struct field names.
//
-// Unmarshal maps an XML element to a struct using the following rules:
+// Unmarshal maps an XML element to a struct using the following rules.
+// In the rules, the tag of a field refers to the value associated with the
+// key 'xml' in the struct field's tag (see the example above).
//
// * If the struct has a field of type []byte or string with tag "innerxml",
// Unmarshal accumulates the raw XML nested inside the element
@@ -92,9 +94,9 @@ import (
// * If the struct has a field named XMLName of type xml.Name,
// Unmarshal records the element name in that field.
//
-// * If the XMLName field has an associated tag string of the form
-// "tag" or "namespace-URL tag", the XML element must have
-// the given tag (and, optionally, name space) or else Unmarshal
+// * If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
// returns an error.
//
// * If the XML element has an attribute whose name matches a
@@ -112,14 +114,14 @@ import (
// field, the comments are discarded.
//
// * If the XML element contains a sub-element whose name matches
-// the prefix of a struct field tag formatted as "a>b>c", unmarshal
+// the prefix of a tag formatted as "a>b>c", unmarshal
// will descend into the XML structure looking for elements with the
// given names, and will map the innermost elements to that struct field.
-// A struct field tag starting with ">" is equivalent to one starting
+// A tag starting with ">" is equivalent to one starting
// with the field name followed by ">".
//
// * If the XML element contains a sub-element whose name
-// matches a struct field whose tag is neither "attr" nor "chardata",
+// matches a field whose tag is neither "attr" nor "chardata",
// Unmarshal maps the sub-element to that struct field.
// Otherwise, if the struct has a field named Any, unmarshal
// maps the sub-element to that struct field.
@@ -297,8 +299,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
// Assign name.
if f, ok := typ.FieldByName("XMLName"); ok {
// Validate element name.
- if f.Tag != "" {
- tag := f.Tag
+ if tag := f.Tag.Get("xml"); tag != "" {
ns := ""
i := strings.LastIndex(tag, " ")
if i >= 0 {
@@ -330,7 +331,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
// Also, determine whether we need to save character data or comments.
for i, n := 0, typ.NumField(); i < n; i++ {
f := typ.Field(i)
- switch f.Tag {
+ switch f.Tag.Get("xml") {
case "attr":
strv := sv.FieldByIndex(f.Index)
// Look for attribute.
@@ -366,15 +367,15 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error {
}
default:
- if strings.Contains(f.Tag, ">") {
+ if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") {
if fieldPaths == nil {
fieldPaths = make(map[string]pathInfo)
}
- path := strings.ToLower(f.Tag)
- if strings.HasPrefix(f.Tag, ">") {
+ path := strings.ToLower(tag)
+ if strings.HasPrefix(tag, ">") {
path = strings.ToLower(f.Name) + path
}
- if strings.HasSuffix(f.Tag, ">") {
+ if strings.HasSuffix(tag, ">") {
path = path[:len(path)-1]
}
err := addFieldPath(sv, fieldPaths, path, f.Index)
@@ -574,7 +575,7 @@ func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error {
t := sv.Type()
f1 := t.FieldByIndex(idx1)
f2 := t.FieldByIndex(idx2)
- return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag}
+ return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
}
// unmarshalPaths walks down an XML structure looking for
diff --git a/src/pkg/xml/read_test.go b/src/pkg/xml/read_test.go
index e07cb1531..2126da3c7 100644
--- a/src/pkg/xml/read_test.go
+++ b/src/pkg/xml/read_test.go
@@ -78,7 +78,7 @@ not being used from outside intra_region_diff.py.
</summary></entry></feed> `
type Feed struct {
- XMLName Name "http://www.w3.org/2005/Atom feed"
+ XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
Title string
Id string
Link []Link
@@ -97,20 +97,20 @@ type Entry struct {
}
type Link struct {
- Rel string "attr"
- Href string "attr"
+ Rel string `xml:"attr"`
+ Href string `xml:"attr"`
}
type Person struct {
Name string
URI string
Email string
- InnerXML string "innerxml"
+ InnerXML string `xml:"innerxml"`
}
type Text struct {
- Type string "attr"
- Body string "chardata"
+ Type string `xml:"attr"`
+ Body string `xml:"chardata"`
}
type Time string
@@ -255,18 +255,18 @@ type PathTestItem struct {
}
type PathTestA struct {
- Items []PathTestItem ">item1"
+ Items []PathTestItem `xml:">item1"`
Before, After string
}
type PathTestB struct {
- Other []PathTestItem "items>Item1"
+ Other []PathTestItem `xml:"items>Item1"`
Before, After string
}
type PathTestC struct {
- Values1 []string "items>item1>value"
- Values2 []string "items>item2>value"
+ Values1 []string `xml:"items>item1>value"`
+ Values2 []string `xml:"items>item2>value"`
Before, After string
}
@@ -275,7 +275,7 @@ type PathTestSet struct {
}
type PathTestD struct {
- Other PathTestSet "items>"
+ Other PathTestSet `xml:"items>"`
Before, After string
}
@@ -299,15 +299,15 @@ func TestUnmarshalPaths(t *testing.T) {
}
type BadPathTestA struct {
- First string "items>item1"
- Other string "items>item2"
- Second string "items>"
+ First string `xml:"items>item1"`
+ Other string `xml:"items>item2"`
+ Second string `xml:"items>"`
}
type BadPathTestB struct {
- Other string "items>item2>value"
- First string "items>item1"
- Second string "items>item1>value"
+ Other string `xml:"items>item2>value"`
+ First string `xml:"items>item1"`
+ Second string `xml:"items>item1>value"`
}
var badPathTests = []struct {
@@ -342,13 +342,13 @@ type AttrTest struct {
}
type Test1 struct {
- Int int "attr"
- Float float64 "attr"
- Uint8 uint8 "attr"
+ Int int `xml:"attr"`
+ Float float64 `xml:"attr"`
+ Uint8 uint8 `xml:"attr"`
}
type Test2 struct {
- Bool bool "attr"
+ Bool bool `xml:"attr"`
}
const attrString = `
diff --git a/src/run.bash b/src/run.bash
index bb3d06c45..d125fd454 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -34,7 +34,7 @@ if $rebuild; then
(xcd pkg
gomake clean
time gomake install
- ) || exit $i
+ ) || exit $?
fi
(xcd pkg
diff --git a/src/version.bash b/src/version.bash
index b45f15a6c..ce5a9969a 100755
--- a/src/version.bash
+++ b/src/version.bash
@@ -16,8 +16,15 @@ if [ $? != 0 ]; then
VERSION=$(echo $OLD | awk '{print $1}')
fi
-# Find most recent known release tag.
+# Get branch type
+BRANCH=release
+if [ "$(hg identify -b 2>/dev/null)" == "default" ]; then
+ BRANCH=weekly
+fi
+
+# Find most recent known release or weekly tag.
TAG=$(hg tags |
+ grep $BRANCH |
sed 's/:.*//' |
sort -rn -k2 |
awk -v ver=$VERSION '$2 <= ver && $1~/^(release|weekly)\./ {print $1}' |
diff --git a/test/fixedbugs/bug345.dir/io.go b/test/fixedbugs/bug345.dir/io.go
new file mode 100644
index 000000000..1d695c304
--- /dev/null
+++ b/test/fixedbugs/bug345.dir/io.go
@@ -0,0 +1,15 @@
+// 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 io
+
+type Writer interface {
+ WrongWrite()
+}
+
+type SectionReader struct {
+ X int
+}
+
+func SR(*SectionReader) {}
diff --git a/test/fixedbugs/bug345.dir/main.go b/test/fixedbugs/bug345.dir/main.go
new file mode 100644
index 000000000..5bdc713f4
--- /dev/null
+++ b/test/fixedbugs/bug345.dir/main.go
@@ -0,0 +1,28 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "./io"
+ goio "io"
+)
+
+func main() {
+ // The errors here complain that io.X != io.X
+ // for different values of io so they should be
+ // showing the full import path, which for the
+ // "./io" import is really ..../go/test/io.
+ // For example:
+ //
+ // main.go:25: cannot use w (type "/Users/rsc/g/go/test/fixedbugs/bug345.dir/io".Writer) as type "io".Writer in function argument:
+ // io.Writer does not implement io.Writer (missing Write method)
+ // main.go:27: cannot use &x (type *"io".SectionReader) as type *"/Users/rsc/g/go/test/fixedbugs/bug345.dir/io".SectionReader in function argument
+
+ var w io.Writer
+ bufio.NewWriter(w) // ERROR "test/io"
+ var x goio.SectionReader
+ io.SR(&x) // ERROR "test/io"
+}
diff --git a/test/fixedbugs/bug345.go b/test/fixedbugs/bug345.go
new file mode 100644
index 000000000..874710ce8
--- /dev/null
+++ b/test/fixedbugs/bug345.go
@@ -0,0 +1,7 @@
+// $G $D/$F.dir/io.go && errchk $G -e $D/$F.dir/main.go
+
+// 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 ignored