2 // +build !appengine |
2 // +build !appengine |
3 |
3 |
4 package isatty |
4 package isatty |
5 |
5 |
6 import ( |
6 import ( |
|
7 "errors" |
7 "strings" |
8 "strings" |
8 "syscall" |
9 "syscall" |
9 "unicode/utf16" |
10 "unicode/utf16" |
10 "unsafe" |
11 "unsafe" |
11 ) |
12 ) |
12 |
13 |
13 const ( |
14 const ( |
14 fileNameInfo uintptr = 2 |
15 objectNameInfo uintptr = 1 |
15 fileTypePipe = 3 |
16 fileNameInfo = 2 |
|
17 fileTypePipe = 3 |
16 ) |
18 ) |
17 |
19 |
18 var ( |
20 var ( |
19 kernel32 = syscall.NewLazyDLL("kernel32.dll") |
21 kernel32 = syscall.NewLazyDLL("kernel32.dll") |
|
22 ntdll = syscall.NewLazyDLL("ntdll.dll") |
20 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") |
23 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") |
21 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") |
24 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") |
22 procGetFileType = kernel32.NewProc("GetFileType") |
25 procGetFileType = kernel32.NewProc("GetFileType") |
|
26 procNtQueryObject = ntdll.NewProc("NtQueryObject") |
23 ) |
27 ) |
24 |
28 |
25 func init() { |
29 func init() { |
26 // Check if GetFileInformationByHandleEx is available. |
30 // Check if GetFileInformationByHandleEx is available. |
27 if procGetFileInformationByHandleEx.Find() != nil { |
31 if procGetFileInformationByHandleEx.Find() != nil { |
66 } |
73 } |
67 |
74 |
68 return true |
75 return true |
69 } |
76 } |
70 |
77 |
|
78 // getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler |
|
79 // since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion |
|
80 // guys are using Windows XP, this is a workaround for those guys, it will also work on system from |
|
81 // Windows vista to 10 |
|
82 // see https://stackoverflow.com/a/18792477 for details |
|
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 |
71 // IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 |
98 // IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 |
72 // terminal. |
99 // terminal. |
73 func IsCygwinTerminal(fd uintptr) bool { |
100 func IsCygwinTerminal(fd uintptr) bool { |
74 if procGetFileInformationByHandleEx == nil { |
101 if procGetFileInformationByHandleEx == nil { |
75 return false |
102 name, err := getFileNameByHandle(fd) |
|
103 if err != nil { |
|
104 return false |
|
105 } |
|
106 return isCygwinPipeName(name) |
76 } |
107 } |
77 |
108 |
78 // Cygwin/msys's pty is a pipe. |
109 // Cygwin/msys's pty is a pipe. |
79 ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) |
110 ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) |
80 if ft != fileTypePipe || e != 0 { |
111 if ft != fileTypePipe || e != 0 { |