From dd52fd2ad97717159b086b0743cd3037fcb7d00e Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Sat, 2 Apr 2022 12:11:17 -0400 Subject: [PATCH] feat: make it so hilbish.run can return command output --- api.go | 31 +++++++++++++++++++++++++++---- exec.go | 21 ++++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/api.go b/api.go index 994ece5..5f3a244 100644 --- a/api.go +++ b/api.go @@ -4,6 +4,7 @@ package main import ( + "bytes" "errors" "fmt" "os" @@ -38,7 +39,7 @@ var exports = map[string]util.LuaExport{ "inputMode": {hlinputMode, 1, false}, "interval": {hlinterval, 2, false}, "read": {hlread, 1, false}, - "run": {hlrun, 1, false}, + "run": {hlrun, 1, true}, "timeout": {hltimeout, 2, false}, "which": {hlwhich, 1, false}, } @@ -220,8 +221,10 @@ func unsetVimMode() { util.SetField(l, hshMod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") } -// run(cmd) +// run(cmd, returnOut) -> exitCode, stdout, stderr // Runs `cmd` in Hilbish's sh interpreter. +// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and +// 3rd values instead of being outputted to the terminal. // --- @param cmd string func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err := c.Check1Arg(); err != nil { @@ -231,8 +234,21 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { if err != nil { return nil, err } + + var terminalOut bool + if len(c.Etc()) != 0 { + tout := c.Etc()[0] + termOut, ok := tout.TryBool() + terminalOut = termOut + if !ok { + return nil, errors.New("bad argument to run (expected boolean, got " + tout.TypeName() + ")") + } + } else { + terminalOut = true + } + var exitcode uint8 - err = execCommand(cmd) + stdout, stderr, err := execCommand(cmd, terminalOut) if code, ok := interp.IsExitStatus(err); ok { exitcode = code @@ -240,7 +256,14 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { exitcode = 1 } - return c.PushingNext1(t.Runtime, rt.IntValue(int64(exitcode))), nil + stdoutStr := "" + stderrStr := "" + if !terminalOut { + stdoutStr = stdout.(*bytes.Buffer).String() + stderrStr = stderr.(*bytes.Buffer).String() + } + + return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil } // cwd() diff --git a/exec.go b/exec.go index 2c5ba67..1ead1df 100644 --- a/exec.go +++ b/exec.go @@ -6,6 +6,7 @@ import ( "errors" "os/exec" "fmt" + "io" "os" "path/filepath" "runtime" @@ -120,7 +121,7 @@ func handleLua(cmdString string) (uint8, error) { } func handleSh(cmdString string) (uint8, error) { - err := execCommand(cmdString) + _, _, err := execCommand(cmdString, true) if err != nil { // If input is incomplete, start multiline prompting if syntax.IsIncomplete(err) { @@ -129,7 +130,7 @@ func handleSh(cmdString string) (uint8, error) { if err != nil { break } - err = execCommand(cmdString) + _, _, err = execCommand(cmdString, true) if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") { continue } else if code, ok := interp.IsExitStatus(err); ok { @@ -153,10 +154,16 @@ func handleSh(cmdString string) (uint8, error) { } // Run command in sh interpreter -func execCommand(cmd string) error { +func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "") if err != nil { - return err + return nil, nil, err + } + var stdout io.Writer = os.Stdout + var stderr io.Writer = os.Stderr + if !terminalOut { + stdout = new(bytes.Buffer) + stderr = new(bytes.Buffer) } var bg bool @@ -320,7 +327,7 @@ func execCommand(cmd string) error { } runner, _ := interp.New( - interp.StdIO(os.Stdin, os.Stdout, os.Stderr), + interp.StdIO(os.Stdin, stdout, stderr), interp.ExecHandler(exechandle), ) @@ -340,11 +347,11 @@ func execCommand(cmd string) error { err = runner.Run(context.TODO(), stmt) if err != nil { - return err + return stdout, stderr, err } } - return nil + return stdout, stderr, nil } func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable