David K. Bainbridge | 215e024 | 2017-09-05 23:18:24 -0700 | [diff] [blame] | 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 | // +build windows,!appengine |
| 7 | |
| 8 | package logrus |
| 9 | |
| 10 | import ( |
| 11 | "bytes" |
| 12 | "errors" |
| 13 | "io" |
| 14 | "os" |
| 15 | "os/exec" |
| 16 | "strconv" |
| 17 | "strings" |
| 18 | "syscall" |
| 19 | "unsafe" |
| 20 | ) |
| 21 | |
| 22 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") |
| 23 | |
| 24 | var ( |
| 25 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") |
| 26 | procSetConsoleMode = kernel32.NewProc("SetConsoleMode") |
| 27 | ) |
| 28 | |
| 29 | const ( |
| 30 | enableProcessedOutput = 0x0001 |
| 31 | enableWrapAtEolOutput = 0x0002 |
| 32 | enableVirtualTerminalProcessing = 0x0004 |
| 33 | ) |
| 34 | |
| 35 | func getVersion() (float64, error) { |
| 36 | stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} |
| 37 | cmd := exec.Command("cmd", "ver") |
| 38 | cmd.Stdout = stdout |
| 39 | cmd.Stderr = stderr |
| 40 | err := cmd.Run() |
| 41 | if err != nil { |
| 42 | return -1, err |
| 43 | } |
| 44 | |
| 45 | // The output should be like "Microsoft Windows [Version XX.X.XXXXXX]" |
| 46 | version := strings.Replace(stdout.String(), "\n", "", -1) |
| 47 | version = strings.Replace(version, "\r\n", "", -1) |
| 48 | |
| 49 | x1 := strings.Index(version, "[Version") |
| 50 | |
| 51 | if x1 == -1 || strings.Index(version, "]") == -1 { |
| 52 | return -1, errors.New("Can't determine Windows version") |
| 53 | } |
| 54 | |
| 55 | return strconv.ParseFloat(version[x1+9:x1+13], 64) |
| 56 | } |
| 57 | |
| 58 | func init() { |
| 59 | ver, err := getVersion() |
| 60 | if err != nil { |
| 61 | return |
| 62 | } |
| 63 | |
| 64 | // Activate Virtual Processing for Windows CMD |
| 65 | // Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx |
| 66 | if ver >= 10 { |
| 67 | handle := syscall.Handle(os.Stderr.Fd()) |
| 68 | procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // IsTerminal returns true if stderr's file descriptor is a terminal. |
| 73 | func IsTerminal(f io.Writer) bool { |
| 74 | switch v := f.(type) { |
| 75 | case *os.File: |
| 76 | var st uint32 |
| 77 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) |
| 78 | return r != 0 && e == 0 |
| 79 | default: |
| 80 | return false |
| 81 | } |
| 82 | } |