2021-01-12 03:13:21 +01:00
|
|
|
// Package terminal encapsulates some platform-specific logic for detecting
|
|
|
|
// if we're running in a terminal and, if so, properly configuring that
|
|
|
|
// terminal to meet the assumptions that the rest of Terraform makes.
|
|
|
|
//
|
|
|
|
// Specifically, Terraform requires a Terminal which supports virtual terminal
|
|
|
|
// sequences and which accepts UTF-8-encoded text.
|
|
|
|
//
|
|
|
|
// This is an abstraction only over the platform-specific detection of and
|
|
|
|
// possibly initialization of terminals. It's not intended to provide
|
|
|
|
// higher-level abstractions of the sort provided by packages like termcap or
|
|
|
|
// curses; ultimately we just assume that terminals are "standard" VT100-like
|
|
|
|
// terminals and use a subset of control codes that works across the various
|
|
|
|
// platforms we support. Our approximate target is "xterm-compatible"
|
|
|
|
// virtual terminals.
|
|
|
|
package terminal
|
|
|
|
|
|
|
|
import (
|
2021-02-12 18:58:25 +01:00
|
|
|
"fmt"
|
2021-01-12 03:13:21 +01:00
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Streams represents a collection of three streams that each may or may not
|
|
|
|
// be connected to a terminal.
|
|
|
|
//
|
|
|
|
// If a stream is connected to a terminal then there are more possibilities
|
|
|
|
// available, such as detecting the current terminal width. If we're connected
|
|
|
|
// to something else, such as a pipe or a file on disk, the stream will
|
|
|
|
// typically provide placeholder values or do-nothing stubs for
|
|
|
|
// terminal-requiring operatons.
|
|
|
|
//
|
|
|
|
// Note that it's possible for only a subset of the streams to be connected
|
|
|
|
// to a terminal. For example, this happens if the user runs Terraform with
|
|
|
|
// I/O redirection where Stdout might refer to a regular disk file while Stderr
|
|
|
|
// refers to a terminal, or various other similar combinations.
|
|
|
|
type Streams struct {
|
|
|
|
Stdout *OutputStream
|
|
|
|
Stderr *OutputStream
|
|
|
|
Stdin *InputStream
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init tries to initialize a terminal, if Terraform is running in one, and
|
|
|
|
// returns an object describing what it was able to set up.
|
|
|
|
//
|
|
|
|
// An error for this function indicates that the current execution context
|
|
|
|
// can't meet Terraform's assumptions. For example, on Windows Init will return
|
|
|
|
// an error if Terraform is running in a Windows Console that refuses to
|
|
|
|
// activate UTF-8 mode, which can happen if we're running on an unsupported old
|
|
|
|
// version of Windows.
|
|
|
|
//
|
|
|
|
// Note that the success of this function doesn't mean that we're actually
|
|
|
|
// running in a terminal. It could also represent successfully detecting that
|
|
|
|
// one or more of the input/output streams is not a terminal.
|
|
|
|
func Init() (*Streams, error) {
|
|
|
|
// These configure* functions are platform-specific functions in other
|
|
|
|
// files that use //+build constraints to vary based on target OS.
|
|
|
|
|
|
|
|
stderr, err := configureOutputHandle(os.Stderr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stdout, err := configureOutputHandle(os.Stdout)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stdin, err := configureInputHandle(os.Stdin)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Streams{
|
|
|
|
Stdout: stdout,
|
|
|
|
Stderr: stderr,
|
|
|
|
Stdin: stdin,
|
|
|
|
}, nil
|
|
|
|
}
|
2021-02-12 18:58:25 +01:00
|
|
|
|
|
|
|
// Print is a helper for conveniently calling fmt.Fprint on the Stdout stream.
|
|
|
|
func (s *Streams) Print(a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprint(s.Stdout.File, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Printf is a helper for conveniently calling fmt.Fprintf on the Stdout stream.
|
|
|
|
func (s *Streams) Printf(format string, a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprintf(s.Stdout.File, format, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Println is a helper for conveniently calling fmt.Fprintln on the Stdout stream.
|
|
|
|
func (s *Streams) Println(a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprintln(s.Stdout.File, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eprint is a helper for conveniently calling fmt.Fprint on the Stderr stream.
|
|
|
|
func (s *Streams) Eprint(a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprint(s.Stderr.File, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eprintf is a helper for conveniently calling fmt.Fprintf on the Stderr stream.
|
|
|
|
func (s *Streams) Eprintf(format string, a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprintf(s.Stderr.File, format, a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eprintln is a helper for conveniently calling fmt.Fprintln on the Stderr stream.
|
|
|
|
func (s *Streams) Eprintln(a ...interface{}) (n int, err error) {
|
|
|
|
return fmt.Fprintln(s.Stderr.File, a...)
|
|
|
|
}
|