74 lines
1.9 KiB
Go
74 lines
1.9 KiB
Go
|
|
package stario
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"os"
|
||
|
|
|
||
|
|
"golang.org/x/term"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ErrTerminalNotTTY reports that terminal-only input was requested from a
|
||
|
|
// non-terminal stdin.
|
||
|
|
var ErrTerminalNotTTY = errors.New("terminal input requires a tty")
|
||
|
|
|
||
|
|
var terminalCheckFunc = term.IsTerminal
|
||
|
|
|
||
|
|
// IsTerminal reports whether os.Stdin is attached to a terminal.
|
||
|
|
func IsTerminal() bool {
|
||
|
|
return IsTerminalFile(os.Stdin)
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsTerminalFD reports whether fd is attached to a terminal.
|
||
|
|
func IsTerminalFD(fd int) bool {
|
||
|
|
return terminalCheckFunc(fd)
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsTerminalFile reports whether file is attached to a terminal.
|
||
|
|
func IsTerminalFile(file *os.File) bool {
|
||
|
|
if file == nil {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return IsTerminalFD(int(file.Fd()))
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadPasswordContext reads one password-style line from the terminal.
|
||
|
|
//
|
||
|
|
// It returns ErrTerminalNotTTY when stdin is not a terminal. If ctx is canceled
|
||
|
|
// while waiting for input, this function returns ctx.Err() without waiting for
|
||
|
|
// the underlying terminal read to finish.
|
||
|
|
func ReadPasswordContext(ctx context.Context, hint string, defaultVal string) (string, error) {
|
||
|
|
return ReadPasswordContextWithMask(ctx, hint, defaultVal, "")
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadPasswordContextWithMask is like ReadPasswordContext but echoes the given
|
||
|
|
// mask string while the user types.
|
||
|
|
func ReadPasswordContextWithMask(ctx context.Context, hint string, defaultVal string, mask string) (string, error) {
|
||
|
|
if ctx == nil {
|
||
|
|
ctx = context.Background()
|
||
|
|
}
|
||
|
|
if err := ctx.Err(); err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
if !IsTerminal() {
|
||
|
|
return "", ErrTerminalNotTTY
|
||
|
|
}
|
||
|
|
session, err := rawTerminalSessionFactory(hint, true)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
resultCh := make(chan InputMsg, 1)
|
||
|
|
go func() {
|
||
|
|
defer session.Close()
|
||
|
|
resultCh <- rawLineInputSession(session, defaultVal, mask, rawInputSignalReturnError)
|
||
|
|
}()
|
||
|
|
select {
|
||
|
|
case result := <-resultCh:
|
||
|
|
return result.String()
|
||
|
|
case <-ctx.Done():
|
||
|
|
session.Abort()
|
||
|
|
<-resultCh
|
||
|
|
return "", ctx.Err()
|
||
|
|
}
|
||
|
|
}
|