mirror of
https://github.com/gopasspw/gopass.git
synced 2025-12-08 19:24:54 +00:00
* [chore] Migrate to golangci-lint v2 Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [chore] Fix more lint issues Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [chore] Fix more lint issue Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [chore] Fix more lint issues Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [chore] Add more package comments. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [chore] Fix golangci-lint config and the remaining checks Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [fix] Use Go 1.24 Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [fix] Fix container builds Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Fix more failing tests Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Fix test failure Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Fix another len assertion Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Move location tests Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [fix] Fix most remaining lint issues Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [fix] Only run XDG specific tests on linux Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * [fix] Attempt to address on source of flaky failures Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> --------- Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
81 lines
1.7 KiB
Go
81 lines
1.7 KiB
Go
// Package termio provides helpers and functions to work with
|
|
// terminal input and output.
|
|
package termio
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// LineReader is an unbuffered line reader.
|
|
type LineReader struct {
|
|
r io.Reader
|
|
ctx context.Context //nolint:containedctx
|
|
}
|
|
|
|
// NewReader creates a new line reader.
|
|
func NewReader(ctx context.Context, r io.Reader) *LineReader {
|
|
return &LineReader{r: r, ctx: ctx}
|
|
}
|
|
|
|
// Read implements io.Reader.
|
|
func (lr LineReader) Read(p []byte) (int, error) {
|
|
return lr.r.Read(p) //nolint:wrapcheck
|
|
}
|
|
|
|
// rr is a composite value to transport the result of Read through a channel.
|
|
type rr struct {
|
|
n int
|
|
err error
|
|
}
|
|
|
|
// ReadLine reads one line w/o buffering.
|
|
func (lr LineReader) ReadLine() (string, error) {
|
|
out := &bytes.Buffer{}
|
|
buf := make([]byte, 1) // important: we must only read one byte at a time!
|
|
|
|
for {
|
|
// we wait for the user input in the background so we can use the
|
|
// select statement below to be able to immediately quit when the
|
|
// user presses Ctrl+C
|
|
msg := make(chan rr, 1)
|
|
|
|
go func() {
|
|
n, err := lr.r.Read(buf)
|
|
msg <- rr{n, err}
|
|
}()
|
|
|
|
var n int
|
|
|
|
var err error
|
|
|
|
// wait for a user input (or a signal to abort)
|
|
select {
|
|
case <-lr.ctx.Done():
|
|
return "", ErrAborted
|
|
case s := <-msg:
|
|
n = s.n
|
|
err = s.err
|
|
}
|
|
|
|
// process the user input
|
|
for i := range n {
|
|
if buf[i] == '\n' {
|
|
return out.String(), nil
|
|
}
|
|
// err is always nil
|
|
_ = out.WriteByte(buf[i])
|
|
}
|
|
// Callers should always process the n > 0 bytes returned before considering the error err.
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return out.String(), nil
|
|
}
|
|
|
|
return out.String(), err
|
|
}
|
|
}
|
|
}
|