...
1
2
3
4 package isatty
5
6 import (
7 "errors"
8 "strings"
9 "syscall"
10 "unicode/utf16"
11 "unsafe"
12 )
13
14 const (
15 objectNameInfo uintptr = 1
16 fileNameInfo = 2
17 fileTypePipe = 3
18 )
19
20 var (
21 kernel32 = syscall.NewLazyDLL("kernel32.dll")
22 ntdll = syscall.NewLazyDLL("ntdll.dll")
23 procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
24 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
25 procGetFileType = kernel32.NewProc("GetFileType")
26 procNtQueryObject = ntdll.NewProc("NtQueryObject")
27 )
28
29 func init() {
30
31 if procGetFileInformationByHandleEx.Find() != nil {
32 procGetFileInformationByHandleEx = nil
33 }
34 }
35
36
37 func IsTerminal(fd uintptr) bool {
38 var st uint32
39 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
40 return r != 0 && e == 0
41 }
42
43
44
45
46 func isCygwinPipeName(name string) bool {
47 token := strings.Split(name, "-")
48 if len(token) < 5 {
49 return false
50 }
51
52 if token[0] != `\msys` &&
53 token[0] != `\cygwin` &&
54 token[0] != `\Device\NamedPipe\msys` &&
55 token[0] != `\Device\NamedPipe\cygwin` {
56 return false
57 }
58
59 if token[1] == "" {
60 return false
61 }
62
63 if !strings.HasPrefix(token[2], "pty") {
64 return false
65 }
66
67 if token[3] != `from` && token[3] != `to` {
68 return false
69 }
70
71 if token[4] != "master" {
72 return false
73 }
74
75 return true
76 }
77
78
79
80
81
82
83 func getFileNameByHandle(fd uintptr) (string, error) {
84 if procNtQueryObject == nil {
85 return "", errors.New("ntdll.dll: NtQueryObject not supported")
86 }
87
88 var buf [4 + syscall.MAX_PATH]uint16
89 var result int
90 r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
91 fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
92 if r != 0 {
93 return "", e
94 }
95 return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
96 }
97
98
99
100 func IsCygwinTerminal(fd uintptr) bool {
101 if procGetFileInformationByHandleEx == nil {
102 name, err := getFileNameByHandle(fd)
103 if err != nil {
104 return false
105 }
106 return isCygwinPipeName(name)
107 }
108
109
110 ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
111 if ft != fileTypePipe || e != 0 {
112 return false
113 }
114
115 var buf [2 + syscall.MAX_PATH]uint16
116 r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
117 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
118 uintptr(len(buf)*2), 0, 0)
119 if r == 0 || e != 0 {
120 return false
121 }
122
123 l := *(*uint32)(unsafe.Pointer(&buf))
124 return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
125 }
126
View as plain text