vendor/github.com/mattn/go-isatty/isatty_windows.go
changeset 251 1c52a0eeb952
parent 242 2a9ec03fe5a1
child 260 445e01aede7e
equal deleted inserted replaced
250:c040f992052f 251:1c52a0eeb952
     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 {
    43 	token := strings.Split(name, "-")
    47 	token := strings.Split(name, "-")
    44 	if len(token) < 5 {
    48 	if len(token) < 5 {
    45 		return false
    49 		return false
    46 	}
    50 	}
    47 
    51 
    48 	if token[0] != `\msys` && token[0] != `\cygwin` {
    52 	if token[0] != `\msys` &&
       
    53 		token[0] != `\cygwin` &&
       
    54 		token[0] != `\Device\NamedPipe\msys` &&
       
    55 		token[0] != `\Device\NamedPipe\cygwin` {
    49 		return false
    56 		return false
    50 	}
    57 	}
    51 
    58 
    52 	if token[1] == "" {
    59 	if token[1] == "" {
    53 		return false
    60 		return false
    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 {