mirror of https://github.com/Hilbis/Hilbish
feat: add sink for commanders to write output/read input
to write output, you would usually just use the print builtin since commanders are just lua custom commands but this does not consider the fact of pipes or other shell operators being used to redirect or whatever. this adds readable/writable "sinks" which is a type for input or output and is currently only used for commanders but can be used for other hilbish things in the futurecommander-stdout
parent
97188e73a5
commit
c690691ede
13
exec.go
13
exec.go
|
@ -323,8 +323,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hc := interp.HandlerCtx(ctx)
|
||||||
if commands[args[0]] != nil {
|
if commands[args[0]] != nil {
|
||||||
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
|
stdin := newSinkInput(hc.Stdin)
|
||||||
|
stdout := newSinkOutput(hc.Stdout)
|
||||||
|
stderr := newSinkOutput(hc.Stderr)
|
||||||
|
|
||||||
|
sinks := rt.NewTable()
|
||||||
|
sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
|
||||||
|
sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
|
||||||
|
sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
|
||||||
|
|
||||||
|
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||||
return interp.NewExitStatus(1)
|
return interp.NewExitStatus(1)
|
||||||
|
@ -364,7 +374,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
|
||||||
killTimeout := 2 * time.Second
|
killTimeout := 2 * time.Second
|
||||||
// from here is basically copy-paste of the default exec handler from
|
// from here is basically copy-paste of the default exec handler from
|
||||||
// sh/interp but with our job handling
|
// sh/interp but with our job handling
|
||||||
hc := interp.HandlerCtx(ctx)
|
|
||||||
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(hc.Stderr, err)
|
fmt.Fprintln(hc.Stderr, err)
|
||||||
|
|
1
lua.go
1
lua.go
|
@ -23,6 +23,7 @@ func luaInit() {
|
||||||
MessageHandler: debuglib.Traceback,
|
MessageHandler: debuglib.Traceback,
|
||||||
})
|
})
|
||||||
lib.LoadAll(l)
|
lib.LoadAll(l)
|
||||||
|
setupSinkType(l)
|
||||||
|
|
||||||
lib.LoadLibs(l, hilbishLoader)
|
lib.LoadLibs(l, hilbishLoader)
|
||||||
// yes this is stupid, i know
|
// yes this is stupid, i know
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local commander = require 'commander'
|
local commander = require 'commander'
|
||||||
local fs = require 'fs'
|
local fs = require 'fs'
|
||||||
|
|
||||||
commander.register('cat', function(args)
|
commander.register('cat', function(args, sinks)
|
||||||
local exit = 0
|
local exit = 0
|
||||||
|
|
||||||
if #args == 0 then
|
if #args == 0 then
|
||||||
|
@ -17,7 +17,7 @@ usage: cat [file]...]]
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
|
|
||||||
io.write(f:read '*a')
|
sinks.out:write(f:read '*a')
|
||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
io.flush()
|
io.flush()
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"hilbish/util"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sinkMetaKey = rt.StringValue("hshsink")
|
||||||
|
|
||||||
|
// a sink is a structure that has input and/or output
|
||||||
|
// it is like a lua file when used in popen, but specific to hilbish
|
||||||
|
type sink struct{
|
||||||
|
writer io.Writer
|
||||||
|
reader io.Reader
|
||||||
|
ud *rt.UserData
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupSinkType(rtm *rt.Runtime) {
|
||||||
|
sinkMeta := rt.NewTable()
|
||||||
|
|
||||||
|
sinkMethods := rt.NewTable()
|
||||||
|
sinkFuncs := map[string]util.LuaExport{
|
||||||
|
"write": {luaSinkWrite, 2, false},
|
||||||
|
}
|
||||||
|
util.SetExports(l, sinkMethods, sinkFuncs)
|
||||||
|
|
||||||
|
sinkIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
arg := c.Arg(1)
|
||||||
|
val := sinkMethods.Get(arg)
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, val), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
|
||||||
|
l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
|
||||||
|
}
|
||||||
|
|
||||||
|
func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := sinkArg(c, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := c.StringArg(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writer.Write([]byte(data))
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSinkInput(r io.Reader) *sink {
|
||||||
|
s := &sink{
|
||||||
|
reader: r,
|
||||||
|
}
|
||||||
|
s.ud = sinkUserData(s)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSinkOutput(w io.Writer) *sink {
|
||||||
|
s := &sink{
|
||||||
|
writer: w,
|
||||||
|
}
|
||||||
|
s.ud = sinkUserData(s)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func sinkArg(c *rt.GoCont, arg int) (*sink, error) {
|
||||||
|
s, ok := valueToSink(c.Arg(arg))
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("#%d must be a sink", arg + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueToSink(val rt.Value) (*sink, bool) {
|
||||||
|
u, ok := val.TryUserData()
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := u.Value().(*sink)
|
||||||
|
return s, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func sinkUserData(s *sink) *rt.UserData {
|
||||||
|
sinkMeta := l.Registry(sinkMetaKey)
|
||||||
|
return rt.NewUserData(s, sinkMeta.AsTable())
|
||||||
|
}
|
Loading…
Reference in New Issue