isatty_windows.go

 1// +build windows
 2// +build !appengine
 3
 4package isatty
 5
 6import (
 7	"strings"
 8	"syscall"
 9	"unicode/utf16"
10	"unsafe"
11)
12
13const (
14	fileNameInfo uintptr = 2
15	fileTypePipe         = 3
16)
17
18var (
19	kernel32                         = syscall.NewLazyDLL("kernel32.dll")
20	procGetConsoleMode               = kernel32.NewProc("GetConsoleMode")
21	procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
22	procGetFileType                  = kernel32.NewProc("GetFileType")
23)
24
25func init() {
26	// Check if GetFileInformationByHandleEx is available.
27	if procGetFileInformationByHandleEx.Find() != nil {
28		procGetFileInformationByHandleEx = nil
29	}
30}
31
32// IsTerminal return true if the file descriptor is terminal.
33func IsTerminal(fd uintptr) bool {
34	var st uint32
35	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
36	return r != 0 && e == 0
37}
38
39// Check pipe name is used for cygwin/msys2 pty.
40// Cygwin/MSYS2 PTY has a name like:
41//   \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
42func isCygwinPipeName(name string) bool {
43	token := strings.Split(name, "-")
44	if len(token) < 5 {
45		return false
46	}
47
48	if token[0] != `\msys` && token[0] != `\cygwin` {
49		return false
50	}
51
52	if token[1] == "" {
53		return false
54	}
55
56	if !strings.HasPrefix(token[2], "pty") {
57		return false
58	}
59
60	if token[3] != `from` && token[3] != `to` {
61		return false
62	}
63
64	if token[4] != "master" {
65		return false
66	}
67
68	return true
69}
70
71// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
72// terminal.
73func IsCygwinTerminal(fd uintptr) bool {
74	if procGetFileInformationByHandleEx == nil {
75		return false
76	}
77
78	// Cygwin/msys's pty is a pipe.
79	ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
80	if ft != fileTypePipe || e != 0 {
81		return false
82	}
83
84	var buf [2 + syscall.MAX_PATH]uint16
85	r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
86		4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
87		uintptr(len(buf)*2), 0, 0)
88	if r == 0 || e != 0 {
89		return false
90	}
91
92	l := *(*uint32)(unsafe.Pointer(&buf))
93	return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
94}