vendor/github.com/mattn/go-isatty/isatty_windows.go
changeset 242 2a9ec03fe5a1
child 251 1c52a0eeb952
equal deleted inserted replaced
241:e77dad242f4c 242:2a9ec03fe5a1
       
     1 // +build windows
       
     2 // +build !appengine
       
     3 
       
     4 package isatty
       
     5 
       
     6 import (
       
     7 	"strings"
       
     8 	"syscall"
       
     9 	"unicode/utf16"
       
    10 	"unsafe"
       
    11 )
       
    12 
       
    13 const (
       
    14 	fileNameInfo uintptr = 2
       
    15 	fileTypePipe         = 3
       
    16 )
       
    17 
       
    18 var (
       
    19 	kernel32                         = syscall.NewLazyDLL("kernel32.dll")
       
    20 	procGetConsoleMode               = kernel32.NewProc("GetConsoleMode")
       
    21 	procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
       
    22 	procGetFileType                  = kernel32.NewProc("GetFileType")
       
    23 )
       
    24 
       
    25 func init() {
       
    26 	// Check if GetFileInformationByHandleEx is available.
       
    27 	if procGetFileInformationByHandleEx.Find() != nil {
       
    28 		procGetFileInformationByHandleEx = nil
       
    29 	}
       
    30 }
       
    31 
       
    32 // IsTerminal return true if the file descriptor is terminal.
       
    33 func IsTerminal(fd uintptr) bool {
       
    34 	var st uint32
       
    35 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
       
    36 	return r != 0 && e == 0
       
    37 }
       
    38 
       
    39 // Check pipe name is used for cygwin/msys2 pty.
       
    40 // Cygwin/MSYS2 PTY has a name like:
       
    41 //   \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
       
    42 func isCygwinPipeName(name string) bool {
       
    43 	token := strings.Split(name, "-")
       
    44 	if len(token) < 5 {
       
    45 		return false
       
    46 	}
       
    47 
       
    48 	if token[0] != `\msys` && token[0] != `\cygwin` {
       
    49 		return false
       
    50 	}
       
    51 
       
    52 	if token[1] == "" {
       
    53 		return false
       
    54 	}
       
    55 
       
    56 	if !strings.HasPrefix(token[2], "pty") {
       
    57 		return false
       
    58 	}
       
    59 
       
    60 	if token[3] != `from` && token[3] != `to` {
       
    61 		return false
       
    62 	}
       
    63 
       
    64 	if token[4] != "master" {
       
    65 		return false
       
    66 	}
       
    67 
       
    68 	return true
       
    69 }
       
    70 
       
    71 // IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
       
    72 // terminal.
       
    73 func IsCygwinTerminal(fd uintptr) bool {
       
    74 	if procGetFileInformationByHandleEx == nil {
       
    75 		return false
       
    76 	}
       
    77 
       
    78 	// Cygwin/msys's pty is a pipe.
       
    79 	ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
       
    80 	if ft != fileTypePipe || e != 0 {
       
    81 		return false
       
    82 	}
       
    83 
       
    84 	var buf [2 + syscall.MAX_PATH]uint16
       
    85 	r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
       
    86 		4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
       
    87 		uintptr(len(buf)*2), 0, 0)
       
    88 	if r == 0 || e != 0 {
       
    89 		return false
       
    90 	}
       
    91 
       
    92 	l := *(*uint32)(unsafe.Pointer(&buf))
       
    93 	return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
       
    94 }