term: add IsTerminal function

Extracted from golang.org/x/crypto/ssh/terminal

The implementation for plan9 is based on IsTerminal from
github.com/mattn/go-isatty

Change-Id: Id7bf2d6595639ff08e5fd5f786a145d340bbed08
Reviewed-on: https://go-review.googlesource.com/c/term/+/200681
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/builders_test.go b/builders_test.go
deleted file mode 100644
index 39500a3..0000000
--- a/builders_test.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 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.
-
-// Temporary file to make the builders happy so there's
-// something buildable in this repo. This can be deleted
-// when other code is added here.
-
-package builders_test
diff --git a/go.mod b/go.mod
index c7216bd..13ae60a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,5 @@
 module golang.org/x/term
 
 go 1.11
+
+require golang.org/x/sys v0.0.0-20191010194322-b09406accb47
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..9878210
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/term.go b/term.go
new file mode 100644
index 0000000..a4f6cd9
--- /dev/null
+++ b/term.go
@@ -0,0 +1,12 @@
+// Copyright 2019 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 term provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+package term
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	return isTerminal(fd)
+}
diff --git a/term_aix.go b/term_aix.go
new file mode 100644
index 0000000..ec21c5c
--- /dev/null
+++ b/term_aix.go
@@ -0,0 +1,11 @@
+// Copyright 2019 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 term
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+const ioctlReadTermios = unix.TCGETS
diff --git a/term_bsd.go b/term_bsd.go
new file mode 100644
index 0000000..04835ed
--- /dev/null
+++ b/term_bsd.go
@@ -0,0 +1,13 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package term
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+const ioctlReadTermios = unix.TIOCGETA
diff --git a/term_linux.go b/term_linux.go
new file mode 100644
index 0000000..ec21c5c
--- /dev/null
+++ b/term_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2019 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 term
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+const ioctlReadTermios = unix.TCGETS
diff --git a/term_linux_test.go b/term_linux_test.go
new file mode 100644
index 0000000..5f2443a
--- /dev/null
+++ b/term_linux_test.go
@@ -0,0 +1,42 @@
+// Copyright 2019 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 term_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"golang.org/x/sys/unix"
+	"golang.org/x/term"
+)
+
+func TestIsTerminalTerm(t *testing.T) {
+
+	dir, err := ioutil.TempDir("", "TestIsTerminalTerm")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	tty := filepath.Join(dir, "tty")
+	err = unix.Mknod(tty, unix.S_IFCHR, int(unix.Mkdev(5, 0)))
+	if err == unix.EPERM {
+		t.Skip("no permission to create terminal file, skipping test")
+	} else if err != nil {
+		t.Fatal(err)
+	}
+
+	file, err := os.Open(tty)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer file.Close()
+
+	if !term.IsTerminal(int(file.Fd())) {
+		t.Fatalf("IsTerminal unexpectedly returned false for terminal file %s", file.Name())
+	}
+}
diff --git a/term_plan9.go b/term_plan9.go
new file mode 100644
index 0000000..0e5335f
--- /dev/null
+++ b/term_plan9.go
@@ -0,0 +1,17 @@
+// Copyright 2019 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 term
+
+import (
+	"golang.org/x/sys/plan9"
+)
+
+func isTerminal(fd int) bool {
+	path, err := plan9.Fd2path(fd)
+	if err != nil {
+		return false
+	}
+	return path == "/dev/cons" || path == "/mnt/term/dev/cons"
+}
diff --git a/term_solaris.go b/term_solaris.go
new file mode 100644
index 0000000..cd6feae
--- /dev/null
+++ b/term_solaris.go
@@ -0,0 +1,16 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+package term
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+func isTerminal(fd int) bool {
+	_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+	return err == nil
+}
diff --git a/term_test.go b/term_test.go
new file mode 100644
index 0000000..aef6b51
--- /dev/null
+++ b/term_test.go
@@ -0,0 +1,26 @@
+// Copyright 2019 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 term_test
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"golang.org/x/term"
+)
+
+func TestIsTerminalTempFile(t *testing.T) {
+	file, err := ioutil.TempFile("", "TestIsTerminalTempFile")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(file.Name())
+	defer file.Close()
+
+	if term.IsTerminal(int(file.Fd())) {
+		t.Fatalf("IsTerminal unexpectedly returned true for temporary file %s", file.Name())
+	}
+}
diff --git a/term_unix.go b/term_unix.go
new file mode 100644
index 0000000..bb770cf
--- /dev/null
+++ b/term_unix.go
@@ -0,0 +1,16 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+package term
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+func isTerminal(fd int) bool {
+	_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+	return err == nil
+}
diff --git a/term_windows.go b/term_windows.go
new file mode 100644
index 0000000..24f9d6d
--- /dev/null
+++ b/term_windows.go
@@ -0,0 +1,15 @@
+// Copyright 2019 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 term
+
+import (
+	"golang.org/x/sys/windows"
+)
+
+func isTerminal(fd int) bool {
+	var st uint32
+	err := windows.GetConsoleMode(windows.Handle(fd), &st)
+	return err == nil
+}