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