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.
18 var kernel32 = syscall.NewLazyDLL("kernel32.dll")
21 procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
22 msysPipeNameRegex = regexp.MustCompile(`\\(cygwin|msys)-\w+-pty\d?-(to|from)-master`)
29 // IsTerminal returns true if w writes to a terminal.
30 func IsTerminal(w io.Writer) bool {
31 return IsConsole(w) || IsMSYSTerminal(w)
34 // IsConsole returns true if w writes to a Windows console.
35 func IsConsole(w io.Writer) bool {
36 var handle syscall.Handle
38 if fw, ok := w.(fder); ok {
39 handle = syscall.Handle(fw.Fd())
41 // The writer has no file-descriptor and so can't be a terminal.
46 err := syscall.GetConsoleMode(handle, &st)
48 // If the handle is attached to a terminal, GetConsoleMode returns a
49 // non-zero value containing the console mode flags. We don't care about
50 // the specifics of flags, just that it is not zero.
51 return (err == nil && st != 0)
54 // IsMSYSTerminal returns true if w writes to a MSYS/MSYS2 terminal.
55 func IsMSYSTerminal(w io.Writer) bool {
56 var handle syscall.Handle
58 if fw, ok := w.(fder); ok {
59 handle = syscall.Handle(fw.Fd())
61 // The writer has no file-descriptor and so can't be a terminal.
65 // MSYS(2) terminal reports as a pipe for STDIN/STDOUT/STDERR. If it isn't
66 // a pipe, it can't be a MSYS(2) terminal.
67 filetype, err := syscall.GetFileType(handle)
69 if filetype != syscall.FILE_TYPE_PIPE || err != nil {
73 // MSYS2/Cygwin terminal's name looks like: \msys-dd50a72ab4668b33-pty2-to-master
74 data := make([]byte, 256, 256)
76 r, _, e := syscall.Syscall6(
77 procGetFileInformationByHandleEx.Addr(),
80 uintptr(fileNameInfo),
81 uintptr(unsafe.Pointer(&data[0])),
88 // The first 4 bytes of the buffer are the size of the UTF16 name, in bytes.
89 unameLen := binary.LittleEndian.Uint32(data[:4]) / 2
90 uname := make([]uint16, unameLen, unameLen)
92 for i := uint32(0); i < unameLen; i++ {
93 uname[i] = binary.LittleEndian.Uint16(data[i*2+4 : i*2+2+4])
96 name := syscall.UTF16ToString(uname)
98 return msysPipeNameRegex.MatchString(name)