aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os/exec
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/os/exec
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.zip
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.bz2
libgo: update to Go1.18beta2
gotools/ * Makefile.am (go_cmd_cgo_files): Add ast_go118.go (check-go-tool): Copy golang.org/x/tools directories. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/os/exec')
-rw-r--r--libgo/go/os/exec/exec.go12
-rw-r--r--libgo/go/os/exec/exec_linux_test.go1
-rw-r--r--libgo/go/os/exec/exec_posix_test.go1
-rw-r--r--libgo/go/os/exec/exec_test.go165
-rw-r--r--libgo/go/os/exec/exec_unix.go1
-rw-r--r--libgo/go/os/exec/exec_windows_test.go15
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_js.go18
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_plan9.go20
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_test.go21
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_unix.go19
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_windows.go12
-rw-r--r--libgo/go/os/exec/lp_js.go1
-rw-r--r--libgo/go/os/exec/lp_unix.go1
-rw-r--r--libgo/go/os/exec/lp_unix_test.go1
-rw-r--r--libgo/go/os/exec/read3.go93
15 files changed, 195 insertions, 186 deletions
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index 0c49575..845b737 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -216,7 +216,7 @@ func (c *Cmd) String() string {
// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
-func interfaceEqual(a, b interface{}) bool {
+func interfaceEqual(a, b any) bool {
defer func() {
recover()
}()
@@ -748,12 +748,11 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string {
out := make([]string, 0, len(env))
saw := make(map[string]int, len(env)) // key => index into out
for _, kv := range env {
- eq := strings.Index(kv, "=")
- if eq < 0 {
+ k, _, ok := strings.Cut(kv, "=")
+ if !ok {
out = append(out, kv)
continue
}
- k := kv[:eq]
if caseInsensitive {
k = strings.ToLower(k)
}
@@ -775,11 +774,10 @@ func addCriticalEnv(env []string) []string {
return env
}
for _, kv := range env {
- eq := strings.Index(kv, "=")
- if eq < 0 {
+ k, _, ok := strings.Cut(kv, "=")
+ if !ok {
continue
}
- k := kv[:eq]
if strings.EqualFold(k, "SYSTEMROOT") {
// We already have it.
return env
diff --git a/libgo/go/os/exec/exec_linux_test.go b/libgo/go/os/exec/exec_linux_test.go
index 3cfa30e..4a37c96 100644
--- a/libgo/go/os/exec/exec_linux_test.go
+++ b/libgo/go/os/exec/exec_linux_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && cgo
-// +build linux,cgo
// On systems that use glibc, calling malloc can create a new arena,
// and creating a new arena can read /sys/devices/system/cpu/online.
diff --git a/libgo/go/os/exec/exec_posix_test.go b/libgo/go/os/exec/exec_posix_test.go
index 7b2c0c0..fd7fb42 100644
--- a/libgo/go/os/exec/exec_posix_test.go
+++ b/libgo/go/os/exec/exec_posix_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package exec_test
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index d854e0d..73aa35f 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -21,7 +21,9 @@ import (
"net/http/httptest"
"os"
"os/exec"
+ "os/exec/internal/fdtest"
"path/filepath"
+ "reflect"
"runtime"
"strconv"
"strings"
@@ -29,15 +31,10 @@ import (
"time"
)
-// haveUnexpectedFDs is set at init time to report whether any
-// file descriptors were open at program start.
+// haveUnexpectedFDs is set at init time to report whether any file descriptors
+// were open at program start.
var haveUnexpectedFDs bool
-// unfinalizedFiles holds files that should not be finalized,
-// because that would close the associated file descriptor,
-// which we don't want to do.
-var unfinalizedFiles []*os.File
-
func init() {
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
return
@@ -49,21 +46,10 @@ func init() {
if poll.IsPollDescriptor(fd) {
continue
}
- // We have no good portable way to check whether an FD is open.
- // We use NewFile to create a *os.File, which lets us
- // know whether it is open, but then we have to cope with
- // the finalizer on the *os.File.
- f := os.NewFile(fd, "")
- if _, err := f.Stat(); err != nil {
- // Close the file to clear the finalizer.
- // We expect the Close to fail.
- f.Close()
- } else {
- fmt.Printf("fd %d open at test start\n", fd)
+
+ if fdtest.Exists(fd) {
haveUnexpectedFDs = true
- // Use a global variable to avoid running
- // the finalizer, which would close the FD.
- unfinalizedFiles = append(unfinalizedFiles, f)
+ return
}
}
}
@@ -166,12 +152,10 @@ func TestCatGoodAndBadFile(t *testing.T) {
if _, ok := err.(*exec.ExitError); !ok {
t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err)
}
- s := string(bs)
- sp := strings.SplitN(s, "\n", 2)
- if len(sp) != 2 {
- t.Fatalf("expected two lines from cat; got %q", s)
+ errLine, body, ok := strings.Cut(string(bs), "\n")
+ if !ok {
+ t.Fatalf("expected two lines from cat; got %q", bs)
}
- errLine, body := sp[0], sp[1]
if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
t.Errorf("expected stderr to complain about file; got %q", errLine)
}
@@ -379,50 +363,21 @@ func TestStdinCloseRace(t *testing.T) {
// Issue 5071
func TestPipeLookPathLeak(t *testing.T) {
- // If we are reading from /proc/self/fd we (should) get an exact result.
- tolerance := 0
-
- // Reading /proc/self/fd is more reliable than calling lsof, so try that
- // first.
- numOpenFDs := func() (int, []byte, error) {
- fds, err := os.ReadDir("/proc/self/fd")
- if err != nil {
- return 0, nil, err
- }
- return len(fds), nil, nil
+ if runtime.GOOS == "windows" {
+ t.Skip("we don't currently suppore counting open handles on windows")
}
- want, before, err := numOpenFDs()
- if err != nil {
- // We encountered a problem reading /proc/self/fd (we might be on
- // a platform that doesn't have it). Fall back onto lsof.
- t.Logf("using lsof because: %v", err)
- numOpenFDs = func() (int, []byte, error) {
- // Android's stock lsof does not obey the -p option,
- // so extra filtering is needed.
- // https://golang.org/issue/10206
- if runtime.GOOS == "android" {
- // numOpenFDsAndroid handles errors itself and
- // might skip or fail the test.
- n, lsof := numOpenFDsAndroid(t)
- return n, lsof, nil
- }
- lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
- return bytes.Count(lsof, []byte("\n")), lsof, err
- }
-
- // lsof may see file descriptors associated with the fork itself,
- // so we allow some extra margin if we have to use it.
- // https://golang.org/issue/19243
- tolerance = 5
- // Retry reading the number of open file descriptors.
- want, before, err = numOpenFDs()
- if err != nil {
- t.Log(err)
- t.Skipf("skipping test; error finding or running lsof")
+ openFDs := func() []uintptr {
+ var fds []uintptr
+ for i := uintptr(0); i < 100; i++ {
+ if fdtest.Exists(i) {
+ fds = append(fds, i)
+ }
}
+ return fds
}
+ want := openFDs()
for i := 0; i < 6; i++ {
cmd := exec.Command("something-that-does-not-exist-executable")
cmd.StdoutPipe()
@@ -432,59 +387,10 @@ func TestPipeLookPathLeak(t *testing.T) {
t.Fatal("unexpected success")
}
}
- got, after, err := numOpenFDs()
- if err != nil {
- // numOpenFDs has already succeeded once, it should work here.
- t.Errorf("unexpected failure: %v", err)
- }
- if got-want > tolerance {
- t.Errorf("number of open file descriptors changed: got %v, want %v", got, want)
- if before != nil {
- t.Errorf("before:\n%v\n", before)
- }
- if after != nil {
- t.Errorf("after:\n%v\n", after)
- }
- }
-}
-
-func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) {
- raw, err := exec.Command("lsof").Output()
- if err != nil {
- t.Skip("skipping test; error finding or running lsof")
- }
-
- // First find the PID column index by parsing the first line, and
- // select lines containing pid in the column.
- pid := []byte(strconv.Itoa(os.Getpid()))
- pidCol := -1
-
- s := bufio.NewScanner(bytes.NewReader(raw))
- for s.Scan() {
- line := s.Bytes()
- fields := bytes.Fields(line)
- if pidCol < 0 {
- for i, v := range fields {
- if bytes.Equal(v, []byte("PID")) {
- pidCol = i
- break
- }
- }
- lsof = append(lsof, line...)
- continue
- }
- if bytes.Equal(fields[pidCol], pid) {
- lsof = append(lsof, '\n')
- lsof = append(lsof, line...)
- }
- }
- if pidCol < 0 {
- t.Fatal("error processing lsof output: unexpected header format")
- }
- if err := s.Err(); err != nil {
- t.Fatalf("error processing lsof output: %v", err)
+ got := openFDs()
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("set of open file descriptors changed: got %v, want %v", got, want)
}
- return bytes.Count(lsof, []byte("\n")), lsof
}
func TestExtraFilesFDShuffle(t *testing.T) {
@@ -794,7 +700,7 @@ func TestHelperProcess(*testing.T) {
cmd, args := args[0], args[1:]
switch cmd {
case "echo":
- iargs := []interface{}{}
+ iargs := []any{}
for _, s := range args {
iargs = append(iargs, s)
}
@@ -1048,6 +954,14 @@ func TestContext(t *testing.T) {
}
func TestContextCancel(t *testing.T) {
+ if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" {
+ testenv.SkipFlaky(t, 42061)
+ }
+
+ // To reduce noise in the final goroutine dump,
+ // let other parallel tests complete if possible.
+ t.Parallel()
+
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := helperCommandContext(t, ctx, "cat")
@@ -1072,14 +986,25 @@ func TestContextCancel(t *testing.T) {
// Calling cancel should have killed the process, so writes
// should now fail. Give the process a little while to die.
start := time.Now()
+ delay := 1 * time.Millisecond
for {
if _, err := io.WriteString(stdin, "echo"); err != nil {
break
}
+
if time.Since(start) > time.Minute {
- t.Fatal("canceling context did not stop program")
+ // Panic instead of calling t.Fatal so that we get a goroutine dump.
+ // We want to know exactly what the os/exec goroutines got stuck on.
+ panic("canceling context did not stop program")
+ }
+
+ // Back off exponentially (up to 1-second sleeps) to give the OS time to
+ // terminate the process.
+ delay *= 2
+ if delay > 1*time.Second {
+ delay = 1 * time.Second
}
- time.Sleep(time.Millisecond)
+ time.Sleep(delay)
}
if err := c.Wait(); err == nil {
diff --git a/libgo/go/os/exec/exec_unix.go b/libgo/go/os/exec/exec_unix.go
index 467c069..c20f352 100644
--- a/libgo/go/os/exec/exec_unix.go
+++ b/libgo/go/os/exec/exec_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !plan9 && !windows
-// +build !plan9,!windows
package exec
diff --git a/libgo/go/os/exec/exec_windows_test.go b/libgo/go/os/exec/exec_windows_test.go
index fbccffe..8e31e47 100644
--- a/libgo/go/os/exec/exec_windows_test.go
+++ b/libgo/go/os/exec/exec_windows_test.go
@@ -3,13 +3,13 @@
// license that can be found in the LICENSE file.
//go:build windows
-// +build windows
package exec_test
import (
"io"
"os"
+ "os/exec"
"strconv"
"syscall"
"testing"
@@ -41,3 +41,16 @@ func TestPipePassing(t *testing.T) {
t.Error(err)
}
}
+
+func TestNoInheritHandles(t *testing.T) {
+ cmd := exec.Command("cmd", "/c exit 88")
+ cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true}
+ err := cmd.Run()
+ exitError, ok := err.(*exec.ExitError)
+ if !ok {
+ t.Fatalf("got error %v; want ExitError", err)
+ }
+ if exitError.ExitCode() != 88 {
+ t.Fatalf("got exit code %d; want 88", exitError.ExitCode())
+ }
+}
diff --git a/libgo/go/os/exec/internal/fdtest/exists_js.go b/libgo/go/os/exec/internal/fdtest/exists_js.go
new file mode 100644
index 0000000..a7ce33c
--- /dev/null
+++ b/libgo/go/os/exec/internal/fdtest/exists_js.go
@@ -0,0 +1,18 @@
+// Copyright 2021 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.
+
+//go:build js
+
+package fdtest
+
+import (
+ "syscall"
+)
+
+// Exists returns true if fd is a valid file descriptor.
+func Exists(fd uintptr) bool {
+ var s syscall.Stat_t
+ err := syscall.Fstat(int(fd), &s)
+ return err != syscall.EBADF
+}
diff --git a/libgo/go/os/exec/internal/fdtest/exists_plan9.go b/libgo/go/os/exec/internal/fdtest/exists_plan9.go
new file mode 100644
index 0000000..8886e06
--- /dev/null
+++ b/libgo/go/os/exec/internal/fdtest/exists_plan9.go
@@ -0,0 +1,20 @@
+// Copyright 2021 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.
+
+//go:build plan9
+
+package fdtest
+
+import (
+ "syscall"
+)
+
+const errBadFd = syscall.ErrorString("fd out of range or not open")
+
+// Exists returns true if fd is a valid file descriptor.
+func Exists(fd uintptr) bool {
+ var buf [1]byte
+ _, err := syscall.Fstat(int(fd), buf[:])
+ return err != errBadFd
+}
diff --git a/libgo/go/os/exec/internal/fdtest/exists_test.go b/libgo/go/os/exec/internal/fdtest/exists_test.go
new file mode 100644
index 0000000..a02dddf
--- /dev/null
+++ b/libgo/go/os/exec/internal/fdtest/exists_test.go
@@ -0,0 +1,21 @@
+// Copyright 2021 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 fdtest
+
+import (
+ "os"
+ "runtime"
+ "testing"
+)
+
+func TestExists(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("Exists not implemented for windows")
+ }
+
+ if !Exists(os.Stdout.Fd()) {
+ t.Errorf("Exists(%d) got false want true", os.Stdout.Fd())
+ }
+}
diff --git a/libgo/go/os/exec/internal/fdtest/exists_unix.go b/libgo/go/os/exec/internal/fdtest/exists_unix.go
new file mode 100644
index 0000000..49f264c
--- /dev/null
+++ b/libgo/go/os/exec/internal/fdtest/exists_unix.go
@@ -0,0 +1,19 @@
+// Copyright 2021 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.
+
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+
+// Package fdtest provides test helpers for working with file descriptors across exec.
+package fdtest
+
+import (
+ "syscall"
+)
+
+// Exists returns true if fd is a valid file descriptor.
+func Exists(fd uintptr) bool {
+ var s syscall.Stat_t
+ err := syscall.Fstat(int(fd), &s)
+ return err != syscall.EBADF
+}
diff --git a/libgo/go/os/exec/internal/fdtest/exists_windows.go b/libgo/go/os/exec/internal/fdtest/exists_windows.go
new file mode 100644
index 0000000..72b8ccf
--- /dev/null
+++ b/libgo/go/os/exec/internal/fdtest/exists_windows.go
@@ -0,0 +1,12 @@
+// Copyright 2021 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.
+
+//go:build windows
+
+package fdtest
+
+// Exists is not implemented on windows and panics.
+func Exists(fd uintptr) bool {
+ panic("unimplemented")
+}
diff --git a/libgo/go/os/exec/lp_js.go b/libgo/go/os/exec/lp_js.go
index 4eac25f..54ddc4d 100644
--- a/libgo/go/os/exec/lp_js.go
+++ b/libgo/go/os/exec/lp_js.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build js && wasm
-// +build js,wasm
package exec
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index ebecc74..0935ab9 100644
--- a/libgo/go/os/exec/lp_unix.go
+++ b/libgo/go/os/exec/lp_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
package exec
diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go
index fe4df1a..bf602fc 100644
--- a/libgo/go/os/exec/lp_unix_test.go
+++ b/libgo/go/os/exec/lp_unix_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
package exec
diff --git a/libgo/go/os/exec/read3.go b/libgo/go/os/exec/read3.go
index a8c7183..10cbfbd 100644
--- a/libgo/go/os/exec/read3.go
+++ b/libgo/go/os/exec/read3.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
// This is a test program that verifies that it can read from
// descriptor 3 and that no other descriptors are open.
@@ -19,12 +18,15 @@ import (
"io"
"os"
"os/exec"
+ "os/exec/internal/fdtest"
"runtime"
"strings"
)
func main() {
fd3 := os.NewFile(3, "fd3")
+ defer fd3.Close()
+
bs, err := io.ReadAll(fd3)
if err != nil {
fmt.Printf("ReadAll from fd 3: %v\n", err)
@@ -38,65 +40,52 @@ func main() {
// descriptor from parent == 3
// All descriptors 4 and up should be available,
// except for any used by the network poller.
- var files []*os.File
- for wantfd := uintptr(4); wantfd <= 100; wantfd++ {
- if poll.IsPollDescriptor(wantfd) {
+ for fd := uintptr(4); fd <= 100; fd++ {
+ if poll.IsPollDescriptor(fd) {
continue
}
- f, err := os.Open(os.Args[0])
- if err != nil {
- fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
- os.Exit(1)
+
+ if !fdtest.Exists(fd) {
+ continue
}
- if got := f.Fd(); got != wantfd {
- fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
- fdfile := fmt.Sprintf("/proc/self/fd/%d", wantfd)
- link, err := os.Readlink(fdfile)
- fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err)
- var args []string
- switch runtime.GOOS {
- case "plan9":
- args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
- case "aix", "solaris", "illumos":
- args = []string{fmt.Sprint(os.Getpid())}
- default:
- args = []string{"-p", fmt.Sprint(os.Getpid())}
- }
- // Determine which command to use to display open files.
- ofcmd := "lsof"
- switch runtime.GOOS {
- case "dragonfly", "freebsd", "netbsd", "openbsd":
- ofcmd = "fstat"
- case "plan9":
- ofcmd = "/bin/cat"
- case "aix":
- ofcmd = "procfiles"
- case "solaris", "illumos":
- ofcmd = "pfiles"
- }
+ fmt.Printf("leaked parent file. fdtest.Exists(%d) got true want false\n", fd)
+
+ fdfile := fmt.Sprintf("/proc/self/fd/%d", fd)
+ link, err := os.Readlink(fdfile)
+ fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err)
- cmd := exec.Command(ofcmd, args...)
- out, err := cmd.CombinedOutput()
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
- }
- fmt.Printf("%s", out)
- os.Exit(1)
+ var args []string
+ switch runtime.GOOS {
+ case "plan9":
+ args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
+ case "aix", "solaris", "illumos":
+ args = []string{fmt.Sprint(os.Getpid())}
+ default:
+ args = []string{"-p", fmt.Sprint(os.Getpid())}
}
- files = append(files, f)
- }
- for _, f := range files {
- f.Close()
- }
+ // Determine which command to use to display open files.
+ ofcmd := "lsof"
+ switch runtime.GOOS {
+ case "dragonfly", "freebsd", "netbsd", "openbsd":
+ ofcmd = "fstat"
+ case "plan9":
+ ofcmd = "/bin/cat"
+ case "aix":
+ ofcmd = "procfiles"
+ case "solaris", "illumos":
+ ofcmd = "pfiles"
+ }
- // Referring to fd3 here ensures that it is not
- // garbage collected, and therefore closed, while
- // executing the wantfd loop above. It doesn't matter
- // what we do with fd3 as long as we refer to it;
- // closing it is the easy choice.
- fd3.Close()
+ cmd := exec.Command(ofcmd, args...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
+ }
+ fmt.Printf("%s", out)
+ os.Exit(1)
+ }
os.Stdout.Write(bs)
}