]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | // +build windows |
2 | // +build !appengine | |
3 | ||
4 | package isatty | |
5 | ||
6 | import ( | |
107c1cdb | 7 | "strings" |
15c0b25d | 8 | "syscall" |
107c1cdb | 9 | "unicode/utf16" |
15c0b25d AP |
10 | "unsafe" |
11 | ) | |
12 | ||
107c1cdb ND |
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 | } | |
15c0b25d AP |
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 | } | |
107c1cdb ND |
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 | } |