...

Source file src/github.com/go-kit/log/term/terminal_windows.go

Documentation: github.com/go-kit/log/term

     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  }
   104  

View as plain text