initial
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1f0a99f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+[568].out
+_go*
+_test*
+_obj
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..139ac85
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+GOROOT ?= $(shell printf 't:;@echo $$(GOROOT)\n' | gomake -f -)
+include $(GOROOT)/src/Make.inc
+
+TARG=github.com/kr/pty
+GOFILES=\
+ pty_$(GOOS).go\
+ run.go\
+
+include $(GOROOT)/src/Make.pkg
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2ad52eb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+# pty
+
+Pty is a Go package for using unix pseudo-terminals.
+
+## Install
+
+ goinstall github.com/kr/pty
+
+## Example
+
+ package main
+
+ import (
+ "fmt"
+ "github.com/kr/pty"
+ "io"
+ "os"
+ )
+
+
+ func main() {
+ c, err := pty.Run(
+ "/bin/grep",
+ []string{"grep", "--color=auto", "bar"},
+ nil,
+ "",
+ )
+ if err != nil {
+ panic(err)
+ }
+
+ go func() {
+ fmt.Fprintln(c.Stdin, "foo")
+ fmt.Fprintln(c.Stdin, "bar")
+ fmt.Fprintln(c.Stdin, "baz")
+ c.Stdin.Close()
+ }()
+ io.Copy(os.Stdout, c.Stdout)
+ c.Wait(0)
+ }
diff --git a/pty_darwin.go b/pty_darwin.go
new file mode 100644
index 0000000..f12e51f
--- /dev/null
+++ b/pty_darwin.go
@@ -0,0 +1,89 @@
+package pty
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sys_TIOCGPTN = 0x80045430
+ sys_TIOCSPTLCK = 0x40045431
+)
+
+
+// Opens a pty and its corresponding tty.
+func Open() (pty, tty *os.File, err os.Error) {
+ p, err := os.Open("/dev/ptmx", os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ err = grantpt(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.Open(sname, os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+const (
+ ptdev1 = "pqrsPQRS"
+ ptdev2 = "0123456789abcdefghijklmnopqrstuv"
+)
+
+func ptsname(f *os.File) (string, os.Error) {
+ fi, err := f.Stat()
+ if err != nil {
+ return "", err
+ }
+ return "/dev/tty" + string([]byte{
+ ptdev1[minor(fi.Rdev)/32],
+ ptdev2[minor(fi.Rdev)%32],
+ }), nil
+}
+
+
+func grantpt(f *os.File) os.Error {
+ p, err := os.StartProcess("/bin/ptchown", []string{"/bin/ptchown"},
+nil, "", []*os.File{f})
+ if err != nil {
+ return err
+ }
+ w, err := p.Wait(0)
+ if err != nil {
+ return err
+ }
+ if w.Exited() && w.ExitStatus() == 0 {
+ return nil
+ }
+ return os.EACCES
+}
+
+
+func ioctl(fd int, cmd uint, data *int) os.Error {
+ _, _, e := syscall.Syscall(
+ syscall.SYS_IOCTL,
+ uintptr(fd),
+ uintptr(cmd),
+ uintptr(unsafe.Pointer(data)),
+ )
+ if e != 0 {
+ return os.ENOTTY
+ }
+ return nil
+}
+
+
+func minor(d uint64) int {
+ return int(d & 0xffffffff)
+}
diff --git a/pty_linux.go b/pty_linux.go
new file mode 100644
index 0000000..51fe28a
--- /dev/null
+++ b/pty_linux.go
@@ -0,0 +1,68 @@
+package pty
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ sys_TIOCGPTN = 0x80045430
+ sys_TIOCSPTLCK = 0x40045431
+)
+
+
+// Opens a pty and its corresponding tty.
+func Open() (pty, tty *os.File, err os.Error) {
+ p, err := os.Open("/dev/ptmx", os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ err = unlockpt(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.Open(sname, os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+
+func ptsname(f *os.File) (string, os.Error) {
+ var n int
+ err := ioctl(f.Fd(), sys_TIOCGPTN, &n)
+ if err != nil {
+ return "", err
+ }
+ return "/dev/pts/" + strconv.Itoa(n), nil
+}
+
+
+func unlockpt(f *os.File) os.Error {
+ var u int
+ return ioctl(f.Fd(), sys_TIOCSPTLCK, &u)
+}
+
+
+func ioctl(fd int, cmd uint, data *int) os.Error {
+ _, _, e := syscall.Syscall(
+ syscall.SYS_IOCTL,
+ uintptr(fd),
+ uintptr(cmd),
+ uintptr(unsafe.Pointer(data)),
+ )
+ if e != 0 {
+ return os.ENOTTY
+ }
+ return nil
+}
diff --git a/run.go b/run.go
new file mode 100644
index 0000000..30063a6
--- /dev/null
+++ b/run.go
@@ -0,0 +1,33 @@
+package pty
+
+import (
+ "exec"
+ "os"
+)
+
+
+// Run starts a process with its stdin, stdout, and stderr
+// connected to a pseudo-terminal tty;
+// Stdin and Stdout of the returned exec.Cmd
+// are the corresponding pty (Stderr is always nil).
+// Arguments name, argv, envv, and dir are passed
+// to os.StartProcess unchanged.
+func Run(name string, argv, envv []string, dir string) (c *exec.Cmd, err os.Error) {
+ c = new(exec.Cmd)
+ var fd [3]*os.File
+
+ c.Stdin, fd[0], err = Open()
+ if err != nil {
+ return nil, err
+ }
+ fd[1] = fd[0]
+ fd[2] = fd[0]
+ c.Stdout = c.Stdin
+ c.Process, err = os.StartProcess(name, argv, envv, dir, fd[:])
+ fd[0].Close()
+ if err != nil {
+ c.Stdin.Close()
+ return nil, err
+ }
+ return c, nil
+}