rclone/lib/terminal/terminal.go

104 lines
2.2 KiB
Go
Raw Normal View History

// Package terminal provides VT100 terminal codes and a windows
// implementation of that.
package terminal
import (
"io"
"os"
"runtime"
"sync"
colorable "github.com/mattn/go-colorable"
)
// VT100 codes
const (
EraseLine = "\x1b[2K"
MoveToStartOfLine = "\x1b[1G"
MoveUp = "\x1b[1A"
Reset = "\x1b[0m"
Bright = "\x1b[1m"
Dim = "\x1b[2m"
Underscore = "\x1b[4m"
Blink = "\x1b[5m"
Reverse = "\x1b[7m"
Hidden = "\x1b[8m"
BlackFg = "\x1b[30m"
RedFg = "\x1b[31m"
GreenFg = "\x1b[32m"
YellowFg = "\x1b[33m"
BlueFg = "\x1b[34m"
MagentaFg = "\x1b[35m"
CyanFg = "\x1b[36m"
WhiteFg = "\x1b[37m"
BlackBg = "\x1b[40m"
RedBg = "\x1b[41m"
GreenBg = "\x1b[42m"
YellowBg = "\x1b[43m"
BlueBg = "\x1b[44m"
MagentaBg = "\x1b[45m"
CyanBg = "\x1b[46m"
WhiteBg = "\x1b[47m"
HiBlackFg = "\x1b[90m"
HiRedFg = "\x1b[91m"
HiGreenFg = "\x1b[92m"
HiYellowFg = "\x1b[93m"
HiBlueFg = "\x1b[94m"
HiMagentaFg = "\x1b[95m"
HiCyanFg = "\x1b[96m"
HiWhiteFg = "\x1b[97m"
HiBlackBg = "\x1b[100m"
HiRedBg = "\x1b[101m"
HiGreenBg = "\x1b[102m"
HiYellowBg = "\x1b[103m"
HiBlueBg = "\x1b[104m"
HiMagentaBg = "\x1b[105m"
HiCyanBg = "\x1b[106m"
HiWhiteBg = "\x1b[107m"
ChangeTitle = "\033]0;"
BEL = "\007"
)
var (
// make sure that start is only called once
once sync.Once
)
// Start the terminal - must be called before use
func Start() {
once.Do(func() {
terminal: use stderr for terminal progress output, etc Avoid mixing rclone command output with progress output by sending terminal output to stderr. Discussion: The --progress option produces output designed for the terminal, and the terminal library sent all its output to stdout. This is a problem for any command that produces output for further processing, when combined with --progress, because the main command output and the progress output are then sent together to stdout. This is most obviously a problem for the rclone 'cat' command. Say you want to retrieve a large file but output to a pipe for further processing, rather than write it to a file. But you also want rclone to display its progress output as you wait for the transfer. Because both the 'cat' output and the progress output go to stdout, this leaves the progress output as garbage data within the streamed cat output, and also prevents the progress output from being displayed. Notably, other rclone commands like 'ls', 'lsjson', and even 'm5dsum', produce meaningful progress output with the --progress option, but when mixed with the regular command output, it makes both the progress output and the command output unusable. The simple solution here is to send output intended for the terminal (including progress output) to stderr instead of stdout. This way the rclone command output can be redirected as desired from stdout, and the progress output will still go to the terminal attached to stderr. If for some reason the user wants to capture/redirect the terminal/progress output for some other purpose, stderr can be redirected instead.
2022-09-30 08:06:49 -05:00
f := os.Stderr
if !IsTerminal(int(f.Fd())) {
terminal: use stderr for terminal progress output, etc Avoid mixing rclone command output with progress output by sending terminal output to stderr. Discussion: The --progress option produces output designed for the terminal, and the terminal library sent all its output to stdout. This is a problem for any command that produces output for further processing, when combined with --progress, because the main command output and the progress output are then sent together to stdout. This is most obviously a problem for the rclone 'cat' command. Say you want to retrieve a large file but output to a pipe for further processing, rather than write it to a file. But you also want rclone to display its progress output as you wait for the transfer. Because both the 'cat' output and the progress output go to stdout, this leaves the progress output as garbage data within the streamed cat output, and also prevents the progress output from being displayed. Notably, other rclone commands like 'ls', 'lsjson', and even 'm5dsum', produce meaningful progress output with the --progress option, but when mixed with the regular command output, it makes both the progress output and the command output unusable. The simple solution here is to send output intended for the terminal (including progress output) to stderr instead of stdout. This way the rclone command output can be redirected as desired from stdout, and the progress output will still go to the terminal attached to stderr. If for some reason the user wants to capture/redirect the terminal/progress output for some other purpose, stderr can be redirected instead.
2022-09-30 08:06:49 -05:00
// If stderr not a tty then remove escape codes
Out = colorable.NewNonColorable(f)
} else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" {
terminal: use stderr for terminal progress output, etc Avoid mixing rclone command output with progress output by sending terminal output to stderr. Discussion: The --progress option produces output designed for the terminal, and the terminal library sent all its output to stdout. This is a problem for any command that produces output for further processing, when combined with --progress, because the main command output and the progress output are then sent together to stdout. This is most obviously a problem for the rclone 'cat' command. Say you want to retrieve a large file but output to a pipe for further processing, rather than write it to a file. But you also want rclone to display its progress output as you wait for the transfer. Because both the 'cat' output and the progress output go to stdout, this leaves the progress output as garbage data within the streamed cat output, and also prevents the progress output from being displayed. Notably, other rclone commands like 'ls', 'lsjson', and even 'm5dsum', produce meaningful progress output with the --progress option, but when mixed with the regular command output, it makes both the progress output and the command output unusable. The simple solution here is to send output intended for the terminal (including progress output) to stderr instead of stdout. This way the rclone command output can be redirected as desired from stdout, and the progress output will still go to the terminal attached to stderr. If for some reason the user wants to capture/redirect the terminal/progress output for some other purpose, stderr can be redirected instead.
2022-09-30 08:06:49 -05:00
// If TERM is set just use stderr
Out = f
} else {
Out = colorable.NewColorable(f)
}
})
}
// WriteString writes the string passed in to the terminal
func WriteString(s string) {
Write([]byte(s))
}
// Out is an io.Writer which can be used to write to the terminal
// e.g. for use with fmt.Fprintf(terminal.Out, "terminal fun: %d\n", n)
var Out io.Writer
// Write sends out to the VT100 terminal.
// It will initialise the terminal if this is the first call.
func Write(out []byte) {
Start()
_, _ = Out.Write(out)
}