1// wrappedreadline is a package that has helpers for interacting with
2// readline from a panicwrap executable.
3//
4// panicwrap overrides the standard file descriptors so that the child process
5// no longer looks like a TTY. The helpers here access the extra file descriptors
6// passed by panicwrap to fix that.
7//
8// panicwrap should be checked for with panicwrap.Wrapped before using this
9// librar, since this library won't adapt if the binary is not wrapped.
10package wrappedreadline
11
12import (
13	"runtime"
14
15	"github.com/chzyer/readline"
16
17	"github.com/hashicorp/terraform/internal/helper/wrappedstreams"
18)
19
20// Override overrides the values in readline.Config that need to be
21// set with wrapped values.
22func Override(cfg *readline.Config) *readline.Config {
23	cfg.Stdin = wrappedstreams.Stdin()
24	cfg.Stdout = wrappedstreams.Stdout()
25	cfg.Stderr = wrappedstreams.Stderr()
26
27	cfg.FuncGetWidth = TerminalWidth
28	cfg.FuncIsTerminal = IsTerminal
29
30	rm := RawMode{StdinFd: int(wrappedstreams.Stdin().Fd())}
31	cfg.FuncMakeRaw = rm.Enter
32	cfg.FuncExitRaw = rm.Exit
33
34	return cfg
35}
36
37// IsTerminal determines if this process is attached to a TTY.
38func IsTerminal() bool {
39	// Windows is always a terminal
40	if runtime.GOOS == "windows" {
41		return true
42	}
43
44	// Same implementation as readline but with our custom fds
45	return readline.IsTerminal(int(wrappedstreams.Stdin().Fd())) &&
46		(readline.IsTerminal(int(wrappedstreams.Stdout().Fd())) ||
47			readline.IsTerminal(int(wrappedstreams.Stderr().Fd())))
48}
49
50// TerminalWidth gets the terminal width in characters.
51func TerminalWidth() int {
52	if runtime.GOOS == "windows" {
53		return readline.GetScreenWidth()
54	}
55
56	return getWidth()
57}
58
59// RawMode is a helper for entering and exiting raw mode.
60type RawMode struct {
61	StdinFd int
62
63	state *readline.State
64}
65
66func (r *RawMode) Enter() (err error) {
67	r.state, err = readline.MakeRaw(r.StdinFd)
68	return err
69}
70
71func (r *RawMode) Exit() error {
72	if r.state == nil {
73		return nil
74	}
75
76	return readline.Restore(r.StdinFd, r.state)
77}
78