forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/mattn/go-isatty/isatty_windows.go

203 lines
3.3 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
//go:build windows && !appengine
// +build windows,!appengine
package isatty
import (
"errors"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
)
const (
objectNameInfo uintptr = 1
fileNameInfo = 2
fileTypePipe = 3
2024-02-18 10:42:21 +00:00
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
ntdll = syscall.NewLazyDLL("ntdll.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
2024-02-18 10:42:21 +00:00
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
procGetFileType = kernel32.NewProc("GetFileType")
procNtQueryObject = ntdll.NewProc("NtQueryObject")
2024-02-18 10:42:21 +00:00
)
func init() {
2024-02-18 10:42:21 +00:00
// Check if GetFileInformationByHandleEx is available.
2024-02-18 10:42:21 +00:00
if procGetFileInformationByHandleEx.Find() != nil {
2024-02-18 10:42:21 +00:00
procGetFileInformationByHandleEx = nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// IsTerminal return true if the file descriptor is terminal.
2024-02-18 10:42:21 +00:00
func IsTerminal(fd uintptr) bool {
2024-02-18 10:42:21 +00:00
var st uint32
2024-02-18 10:42:21 +00:00
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
2024-02-18 10:42:21 +00:00
return r != 0 && e == 0
2024-02-18 10:42:21 +00:00
}
// Check pipe name is used for cygwin/msys2 pty.
2024-02-18 10:42:21 +00:00
// Cygwin/MSYS2 PTY has a name like:
2024-06-14 08:41:36 +00:00
//
2024-06-14 08:41:36 +00:00
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
2024-02-18 10:42:21 +00:00
func isCygwinPipeName(name string) bool {
2024-02-18 10:42:21 +00:00
token := strings.Split(name, "-")
2024-02-18 10:42:21 +00:00
if len(token) < 5 {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if token[0] != `\msys` &&
2024-02-18 10:42:21 +00:00
token[0] != `\cygwin` &&
2024-02-18 10:42:21 +00:00
token[0] != `\Device\NamedPipe\msys` &&
2024-02-18 10:42:21 +00:00
token[0] != `\Device\NamedPipe\cygwin` {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if token[1] == "" {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if !strings.HasPrefix(token[2], "pty") {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if token[3] != `from` && token[3] != `to` {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if token[4] != "master" {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
return true
2024-02-18 10:42:21 +00:00
}
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
2024-02-18 10:42:21 +00:00
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
2024-02-18 10:42:21 +00:00
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
2024-02-18 10:42:21 +00:00
// Windows vista to 10
2024-02-18 10:42:21 +00:00
// see https://stackoverflow.com/a/18792477 for details
2024-02-18 10:42:21 +00:00
func getFileNameByHandle(fd uintptr) (string, error) {
2024-02-18 10:42:21 +00:00
if procNtQueryObject == nil {
2024-02-18 10:42:21 +00:00
return "", errors.New("ntdll.dll: NtQueryObject not supported")
2024-02-18 10:42:21 +00:00
}
var buf [4 + syscall.MAX_PATH]uint16
2024-02-18 10:42:21 +00:00
var result int
2024-02-18 10:42:21 +00:00
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
2024-02-18 10:42:21 +00:00
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
2024-02-18 10:42:21 +00:00
if r != 0 {
2024-02-18 10:42:21 +00:00
return "", e
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
2024-02-18 10:42:21 +00:00
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
2024-02-18 10:42:21 +00:00
// terminal.
2024-02-18 10:42:21 +00:00
func IsCygwinTerminal(fd uintptr) bool {
2024-02-18 10:42:21 +00:00
if procGetFileInformationByHandleEx == nil {
2024-02-18 10:42:21 +00:00
name, err := getFileNameByHandle(fd)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return isCygwinPipeName(name)
2024-02-18 10:42:21 +00:00
}
// Cygwin/msys's pty is a pipe.
2024-02-18 10:42:21 +00:00
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
2024-02-18 10:42:21 +00:00
if ft != fileTypePipe || e != 0 {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
var buf [2 + syscall.MAX_PATH]uint16
2024-02-18 10:42:21 +00:00
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
2024-02-18 10:42:21 +00:00
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
2024-02-18 10:42:21 +00:00
uintptr(len(buf)*2), 0, 0)
2024-02-18 10:42:21 +00:00
if r == 0 || e != 0 {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
l := *(*uint32)(unsafe.Pointer(&buf))
2024-02-18 10:42:21 +00:00
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
2024-02-18 10:42:21 +00:00
}