github.com/go-kit/log@v0.2.1/term/terminal_windows.go (about) 1 // Based on ssh/terminal: 2 // Copyright 2011 The Go Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 //go:build windows 7 // +build windows 8 9 package term 10 11 import ( 12 "encoding/binary" 13 "io" 14 "regexp" 15 "syscall" 16 "unsafe" 17 ) 18 19 var kernel32 = syscall.NewLazyDLL("kernel32.dll") 20 21 var ( 22 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") 23 msysPipeNameRegex = regexp.MustCompile(`\\(cygwin|msys)-\w+-pty\d?-(to|from)-master`) 24 ) 25 26 const ( 27 fileNameInfo = 0x02 28 ) 29 30 // IsTerminal returns true if w writes to a terminal. 31 func IsTerminal(w io.Writer) bool { 32 return IsConsole(w) || IsMSYSTerminal(w) 33 } 34 35 // IsConsole returns true if w writes to a Windows console. 36 func IsConsole(w io.Writer) bool { 37 var handle syscall.Handle 38 39 if fw, ok := w.(fder); ok { 40 handle = syscall.Handle(fw.Fd()) 41 } else { 42 // The writer has no file-descriptor and so can't be a terminal. 43 return false 44 } 45 46 var st uint32 47 err := syscall.GetConsoleMode(handle, &st) 48 49 // If the handle is attached to a terminal, GetConsoleMode returns a 50 // non-zero value containing the console mode flags. We don't care about 51 // the specifics of flags, just that it is not zero. 52 return (err == nil && st != 0) 53 } 54 55 // IsMSYSTerminal returns true if w writes to a MSYS/MSYS2 terminal. 56 func IsMSYSTerminal(w io.Writer) bool { 57 var handle syscall.Handle 58 59 if fw, ok := w.(fder); ok { 60 handle = syscall.Handle(fw.Fd()) 61 } else { 62 // The writer has no file-descriptor and so can't be a terminal. 63 return false 64 } 65 66 // MSYS(2) terminal reports as a pipe for STDIN/STDOUT/STDERR. If it isn't 67 // a pipe, it can't be a MSYS(2) terminal. 68 filetype, err := syscall.GetFileType(handle) 69 70 if filetype != syscall.FILE_TYPE_PIPE || err != nil { 71 return false 72 } 73 74 // MSYS2/Cygwin terminal's name looks like: \msys-dd50a72ab4668b33-pty2-to-master 75 data := make([]byte, 256) 76 77 r, _, e := syscall.Syscall6( 78 procGetFileInformationByHandleEx.Addr(), 79 4, 80 uintptr(handle), 81 uintptr(fileNameInfo), 82 uintptr(unsafe.Pointer(&data[0])), 83 uintptr(len(data)), 84 0, 85 0, 86 ) 87 88 if r != 0 && e == 0 { 89 // The first 4 bytes of the buffer are the size of the UTF16 name, in bytes. 90 unameLen := binary.LittleEndian.Uint32(data[:4]) / 2 91 uname := make([]uint16, unameLen) 92 93 for i := uint32(0); i < unameLen; i++ { 94 uname[i] = binary.LittleEndian.Uint16(data[i*2+4 : i*2+2+4]) 95 } 96 97 name := syscall.UTF16ToString(uname) 98 99 return msysPipeNameRegex.MatchString(name) 100 } 101 102 return false 103 }