2019-09-26 21:14:47 +01:00
|
|
|
// 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"
|
2020-10-06 16:34:26 +01:00
|
|
|
|
|
|
|
ChangeTitle = "\033]0;"
|
|
|
|
BEL = "\007"
|
2019-09-26 21:14:47 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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
|
2020-07-31 19:57:48 +01:00
|
|
|
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
|
2019-09-26 21:14:47 +01:00
|
|
|
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
|
2019-09-26 21:14:47 +01:00
|
|
|
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
|
2020-10-13 17:49:58 -04:00
|
|
|
// e.g. for use with fmt.Fprintf(terminal.Out, "terminal fun: %d\n", n)
|
2019-09-26 21:14:47 +01:00
|
|
|
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)
|
|
|
|
}
|