|
1 // +build windows |
|
2 // +build !appengine |
|
3 |
|
4 package isatty |
|
5 |
|
6 import ( |
|
7 "strings" |
|
8 "syscall" |
|
9 "unicode/utf16" |
|
10 "unsafe" |
|
11 ) |
|
12 |
|
13 const ( |
|
14 fileNameInfo uintptr = 2 |
|
15 fileTypePipe = 3 |
|
16 ) |
|
17 |
|
18 var ( |
|
19 kernel32 = syscall.NewLazyDLL("kernel32.dll") |
|
20 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") |
|
21 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") |
|
22 procGetFileType = kernel32.NewProc("GetFileType") |
|
23 ) |
|
24 |
|
25 func 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. |
|
33 func 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 |
|
42 func 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. |
|
73 func 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 } |