- 提升 go.mod 基线到 Go 1.20,并补齐对应测试 - 修正 Passwd / PasswdResponseSignal 语义,Ctrl+C 默认退出当前流程 - 优化 raw terminal redraw、Restore 与 StopUntil 的边界行为 - 新增 StarPipe、FrameReader/FrameWriter、ReadFullContext/WriteFullContext/CopyContext、IsTerminal/ReadPasswordContext - 收口 StarQueue / StarBuffer 语义,删除 EndWrite,统一 Close / Abort 行为 - 补齐 signal、timeout、queue、terminal、pipe、buffer 的回归测试与 race 覆盖
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()
|
|
}
|
|
}
|