Skip to content

Commit

Permalink
Cancellable reads; EOF on "read" sets var to ""
Browse files Browse the repository at this point in the history
- Make reading cancellable. Not all input from stdin, just that done
  directly by the shell (i.e. the "read" builtin). Exec'ed programs
  still read directly from stdin's os.File and are not cancellable.
- If you press ^D (EOF) when reading into a shell variable, set the
  variable to "". This is consistent with bash & zsh.
  • Loading branch information
theclapp committed Mar 8, 2024
1 parent 0763f7d commit c3175c5
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 7 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/go-quicktest/qt v1.101.0
github.com/google/go-cmp v0.6.0
github.com/google/renameio/v2 v2.0.0
github.com/muesli/cancelreader v0.2.2
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
github.com/rogpeppe/go-internal v1.12.0
golang.org/x/sync v0.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
Expand Down
33 changes: 27 additions & 6 deletions interp/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strconv"
"strings"

"github.com/muesli/cancelreader"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/syntax"
)
Expand Down Expand Up @@ -589,10 +590,7 @@ func (r *Runner) builtinCode(ctx context.Context, pos syntax.Pos, name string, a
r.out(prompt)
}

line, err := r.readLine(raw)
if err != nil {
return 1
}
line, err := r.readLine(ctx, raw)
if len(args) == 0 {
args = append(args, shellReplyVar)
}
Expand All @@ -606,6 +604,10 @@ func (r *Runner) builtinCode(ctx context.Context, pos syntax.Pos, name string, a
r.setVarString(name, val)
}

if err != nil {
return 1
}

return 0

case "getopts":
Expand Down Expand Up @@ -917,17 +919,36 @@ func (r *Runner) printOptLine(name string, enabled, supported bool) {
r.outf("%s\t%s\t(%q not supported)\n", name, state, r.optStatusText(!enabled))
}

func (r *Runner) readLine(raw bool) ([]byte, error) {
func (r *Runner) readLine(ctx context.Context, raw bool) ([]byte, error) {
if r.stdin == nil {
return nil, errors.New("interp: can't read, there's no stdin")
}

var line []byte
esc := false

stdin := r.stdin
if osFile, ok := stdin.(*os.File); ok {
cr, err := cancelreader.NewReader(osFile)
if err != nil {
return nil, err
}
stdin = cr
done := make(chan bool)
go func() {
select {
case <-ctx.Done():
cr.Cancel()
case <-done:
}
cr.Close()
}()
defer close(done)
}

for {
var buf [1]byte
n, err := r.stdin.Read(buf[:])
n, err := stdin.Read(buf[:])
if n > 0 {
b := buf[0]
switch {
Expand Down
2 changes: 1 addition & 1 deletion interp/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ func (r *Runner) cmd(ctx context.Context, cm syntax.Command) {
}
r.errf("%s", ps3)

line, err := r.readLine(true)
line, err := r.readLine(ctx, true)
if err != nil {
r.exit = 1
return nil
Expand Down

0 comments on commit c3175c5

Please sign in to comment.