mirror of
https://github.com/Hilbis/Hilbish
synced 2025-04-22 05:23:23 +00:00
Compare commits
No commits in common. "15e3c1a74b4e0c164b3ee88cf1bd51982ca39120" and "56ba00e213fca2bf91f057a5c77b896f76f2537d" have entirely different histories.
15e3c1a74b
...
56ba00e213
113
api.go
113
api.go
@ -23,7 +23,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hilbish/sink"
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
@ -49,6 +48,7 @@ var exports = map[string]util.LuaExport{
|
|||||||
"inputMode": {hlinputMode, 1, false},
|
"inputMode": {hlinputMode, 1, false},
|
||||||
"interval": {hlinterval, 2, false},
|
"interval": {hlinterval, 2, false},
|
||||||
"read": {hlread, 1, false},
|
"read": {hlread, 1, false},
|
||||||
|
//"run": {hlrun, 1, true},
|
||||||
"timeout": {hltimeout, 2, false},
|
"timeout": {hltimeout, 2, false},
|
||||||
"which": {hlwhich, 1, false},
|
"which": {hlwhich, 1, false},
|
||||||
}
|
}
|
||||||
@ -133,9 +133,6 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
|||||||
pluginModule := moduleLoader(rtm)
|
pluginModule := moduleLoader(rtm)
|
||||||
mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
|
mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
|
||||||
|
|
||||||
sinkModule := sink.Loader(l)
|
|
||||||
mod.Set(rt.StringValue("sink"), rt.TableValue(sinkModule))
|
|
||||||
|
|
||||||
return rt.TableValue(mod), nil
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +184,114 @@ func handleStream(v rt.Value, strms *streams, errStream bool) error {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// run(cmd, streams) -> exitCode (number), stdout (string), stderr (string)
|
||||||
|
// Runs `cmd` in Hilbish's shell script interpreter.
|
||||||
|
// The `streams` parameter specifies the output and input streams the command should use.
|
||||||
|
// For example, to write command output to a sink.
|
||||||
|
// As a table, the caller can directly specify the standard output, error, and input
|
||||||
|
// streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||||
|
// As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||||
|
// #param cmd string
|
||||||
|
// #param streams table|boolean
|
||||||
|
// #returns number, string, string
|
||||||
|
// #example
|
||||||
|
/*
|
||||||
|
// This code is the same as `ls -l | wc -l`
|
||||||
|
local fs = require 'fs'
|
||||||
|
local pr, pw = fs.pipe()
|
||||||
|
hilbish.run('ls -l', {
|
||||||
|
stdout = pw,
|
||||||
|
stderr = pw,
|
||||||
|
})
|
||||||
|
|
||||||
|
pw:close()
|
||||||
|
|
||||||
|
hilbish.run('wc -l', {
|
||||||
|
stdin = pr
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
// #example
|
||||||
|
/*
|
||||||
|
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
// TODO: ON BREAKING RELEASE, DO NOT ACCEPT `streams` AS A BOOLEAN.
|
||||||
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
strms := &streams{}
|
||||||
|
var terminalOut bool
|
||||||
|
if len(c.Etc()) != 0 {
|
||||||
|
tout := c.Etc()[0]
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
terminalOut, ok = tout.TryBool()
|
||||||
|
if !ok {
|
||||||
|
luastreams, ok := tout.TryTable()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad argument to run (expected boolean or table, got " + tout.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("out")), strms, false)
|
||||||
|
handleStream(luastreams.Get(rt.StringValue("err")), strms, true)
|
||||||
|
|
||||||
|
stdinstrm := luastreams.Get(rt.StringValue("input"))
|
||||||
|
if !stdinstrm.IsNil() {
|
||||||
|
ud, ok := stdinstrm.TryUserData()
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file, got " + stdinstrm.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ud.Value()
|
||||||
|
var varstrm io.Reader
|
||||||
|
if f, ok := val.(*iolib.File); ok {
|
||||||
|
varstrm = f.Handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if f, ok := val.(*sink); ok {
|
||||||
|
varstrm = f.reader
|
||||||
|
}
|
||||||
|
|
||||||
|
if varstrm == nil {
|
||||||
|
return nil, errors.New("bad type as run stdin stream (expected userdata as either sink or file)")
|
||||||
|
}
|
||||||
|
|
||||||
|
strms.stdin = varstrm
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !terminalOut {
|
||||||
|
strms = &streams{
|
||||||
|
stdout: new(bytes.Buffer),
|
||||||
|
stderr: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var exitcode uint8
|
||||||
|
stdout, stderr, err := execCommand(cmd, strms)
|
||||||
|
|
||||||
|
if code, ok := interp.IsExitStatus(err); ok {
|
||||||
|
exitcode = code
|
||||||
|
} else if err != nil {
|
||||||
|
exitcode = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdoutStr, stderrStr string
|
||||||
|
if stdoutBuf, ok := stdout.(*bytes.Buffer); ok {
|
||||||
|
stdoutStr = stdoutBuf.String()
|
||||||
|
}
|
||||||
|
if stderrBuf, ok := stderr.(*bytes.Buffer); ok {
|
||||||
|
stderrStr = stderrBuf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// cwd() -> string
|
// cwd() -> string
|
||||||
// Returns the current directory of the shell.
|
// Returns the current directory of the shell.
|
||||||
// #returns string
|
// #returns string
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
package snail
|
package snail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"hilbish/sink"
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/arnodel/golua/lib/packagelib"
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
"github.com/arnodel/golua/lib/iolib"
|
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
"mvdan.cc/sh/v3/syntax"
|
"mvdan.cc/sh/v3/syntax"
|
||||||
)
|
)
|
||||||
@ -26,7 +22,7 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
|||||||
snailMeta := rt.NewTable()
|
snailMeta := rt.NewTable()
|
||||||
snailMethods := rt.NewTable()
|
snailMethods := rt.NewTable()
|
||||||
snailFuncs := map[string]util.LuaExport{
|
snailFuncs := map[string]util.LuaExport{
|
||||||
"run": {srun, 3, false},
|
"run": {srun, 2, false},
|
||||||
}
|
}
|
||||||
util.SetExports(rtm, snailMethods, snailFuncs)
|
util.SetExports(rtm, snailMethods, snailFuncs)
|
||||||
|
|
||||||
@ -69,27 +65,11 @@ func srun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
streams := &util.Streams{}
|
|
||||||
thirdArg := c.Arg(2)
|
|
||||||
switch thirdArg.Type() {
|
|
||||||
case rt.TableType:
|
|
||||||
args := thirdArg.AsTable()
|
|
||||||
|
|
||||||
if luastreams, ok := args.Get(rt.StringValue("sinks")).TryTable(); ok {
|
|
||||||
handleStream(luastreams.Get(rt.StringValue("out")), streams, false, false)
|
|
||||||
handleStream(luastreams.Get(rt.StringValue("err")), streams, true, false)
|
|
||||||
handleStream(luastreams.Get(rt.StringValue("input")), streams, false, true)
|
|
||||||
}
|
|
||||||
case rt.NilType: // noop
|
|
||||||
default:
|
|
||||||
return nil, errors.New("expected 3rd arg to either be a table or a boolean")
|
|
||||||
}
|
|
||||||
|
|
||||||
var newline bool
|
var newline bool
|
||||||
var cont bool
|
var cont bool
|
||||||
var luaErr rt.Value = rt.NilValue
|
var luaErr rt.Value = rt.NilValue
|
||||||
exitCode := 0
|
exitCode := 0
|
||||||
bg, _, _, err := s.Run(cmd, streams)
|
bg, _, _, err := s.Run(cmd, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if syntax.IsIncomplete(err) {
|
if syntax.IsIncomplete(err) {
|
||||||
/*
|
/*
|
||||||
@ -123,41 +103,6 @@ func srun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return c.PushingNext1(t.Runtime, rt.TableValue(runnerRet)), nil
|
return c.PushingNext1(t.Runtime, rt.TableValue(runnerRet)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStream(v rt.Value, strms *util.Streams, errStream, inStream bool) error {
|
|
||||||
if v == rt.NilValue {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ud, ok := v.TryUserData()
|
|
||||||
if !ok {
|
|
||||||
return errors.New("expected metatable argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
val := ud.Value()
|
|
||||||
var varstrm io.ReadWriter
|
|
||||||
if f, ok := val.(*iolib.File); ok {
|
|
||||||
varstrm = f.Handle()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f, ok := val.(*sink.Sink); ok {
|
|
||||||
varstrm = f.Rw
|
|
||||||
}
|
|
||||||
|
|
||||||
if varstrm == nil {
|
|
||||||
return errors.New("expected either a sink or file")
|
|
||||||
}
|
|
||||||
|
|
||||||
if errStream {
|
|
||||||
strms.Stderr = varstrm
|
|
||||||
} else if inStream {
|
|
||||||
strms.Stdin = varstrm
|
|
||||||
} else {
|
|
||||||
strms.Stdout = varstrm
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func snailArg(c *rt.GoCont, arg int) (*snail, error) {
|
func snailArg(c *rt.GoCont, arg int) (*snail, error) {
|
||||||
s, ok := valueToSnail(c.Arg(arg))
|
s, ok := valueToSnail(c.Arg(arg))
|
||||||
if !ok {
|
if !ok {
|
||||||
|
2
lua.go
2
lua.go
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"hilbish/sink"
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
"hilbish/golibs/bait"
|
"hilbish/golibs/bait"
|
||||||
"hilbish/golibs/commander"
|
"hilbish/golibs/commander"
|
||||||
@ -24,6 +25,7 @@ func luaInit() {
|
|||||||
MessageHandler: debuglib.Traceback,
|
MessageHandler: debuglib.Traceback,
|
||||||
})
|
})
|
||||||
lib.LoadAll(l)
|
lib.LoadAll(l)
|
||||||
|
sink.SetupSinkType(l)
|
||||||
|
|
||||||
lib.LoadLibs(l, hilbishLoader)
|
lib.LoadLibs(l, hilbishLoader)
|
||||||
// yes this is stupid, i know
|
// yes this is stupid, i know
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
local hilbish = require 'hilbish'
|
|
||||||
local snail = require 'snail'
|
|
||||||
|
|
||||||
hilbish.snail = snail.new()
|
|
||||||
|
|
||||||
function hilbish.run(cmd, streams)
|
|
||||||
local sinks = {}
|
|
||||||
|
|
||||||
if type(streams) == 'boolean' then
|
|
||||||
if not streams then
|
|
||||||
sinks = {
|
|
||||||
out = hilbish.sink.new(),
|
|
||||||
err = hilbish.sink.new(),
|
|
||||||
input = io.stdin
|
|
||||||
}
|
|
||||||
end
|
|
||||||
elseif type(streams) == 'table' then
|
|
||||||
sinks = streams
|
|
||||||
end
|
|
||||||
|
|
||||||
local out = hilbish.snail:run(cmd, {sinks = sinks})
|
|
||||||
local returns = {out}
|
|
||||||
|
|
||||||
if type(streams) == 'boolean' and not streams then
|
|
||||||
table.insert(returns, sinks.out:readAll())
|
|
||||||
table.insert(returns, sinks.err:readAll())
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.unpack(returns)
|
|
||||||
end
|
|
@ -18,8 +18,6 @@ table.insert(package.searchers, function(module)
|
|||||||
return function() return hilbish.module.load(path) end, path
|
return function() return hilbish.module.load(path) end, path
|
||||||
end)
|
end)
|
||||||
|
|
||||||
require 'nature.hilbish'
|
|
||||||
|
|
||||||
require 'nature.commands'
|
require 'nature.commands'
|
||||||
require 'nature.completions'
|
require 'nature.completions'
|
||||||
require 'nature.opts'
|
require 'nature.opts'
|
||||||
|
@ -83,8 +83,9 @@ function hilbish.runner.getCurrent()
|
|||||||
return currentRunner
|
return currentRunner
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local snaili = snail.new()
|
||||||
function hilbish.runner.sh(input)
|
function hilbish.runner.sh(input)
|
||||||
return hilbish.snail:run(input)
|
return snaili:run(input)
|
||||||
end
|
end
|
||||||
|
|
||||||
hilbish.runner.add('hybrid', function(input)
|
hilbish.runner.add('hybrid', function(input)
|
||||||
|
52
sink/sink.go
52
sink/sink.go
@ -2,7 +2,6 @@ package sink
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -19,13 +18,14 @@ var sinkMetaKey = rt.StringValue("hshsink")
|
|||||||
// A sink is a structure that has input and/or output to/from
|
// A sink is a structure that has input and/or output to/from
|
||||||
// a desination.
|
// a desination.
|
||||||
type Sink struct{
|
type Sink struct{
|
||||||
Rw *bufio.ReadWriter
|
writer *bufio.Writer
|
||||||
|
reader *bufio.Reader
|
||||||
file *os.File
|
file *os.File
|
||||||
UserData *rt.UserData
|
UserData *rt.UserData
|
||||||
autoFlush bool
|
autoFlush bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Loader(rtm *rt.Runtime) *rt.Table {
|
func SetupSinkType(rtm *rt.Runtime) {
|
||||||
sinkMeta := rt.NewTable()
|
sinkMeta := rt.NewTable()
|
||||||
|
|
||||||
sinkMethods := rt.NewTable()
|
sinkMethods := rt.NewTable()
|
||||||
@ -65,24 +65,9 @@ func Loader(rtm *rt.Runtime) *rt.Table {
|
|||||||
|
|
||||||
sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
|
sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
|
||||||
rtm.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
|
rtm.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
|
||||||
|
|
||||||
exports := map[string]util.LuaExport{
|
|
||||||
"new": {luaSinkNew, 0, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
mod := rt.NewTable()
|
|
||||||
util.SetExports(rtm, mod, exports)
|
|
||||||
|
|
||||||
return mod
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func luaSinkNew(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
||||||
snk := NewSink(t.Runtime, new(bytes.Buffer))
|
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(snk.UserData)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// #member
|
// #member
|
||||||
// readAll() -> string
|
// readAll() -> string
|
||||||
// --- @returns string
|
// --- @returns string
|
||||||
@ -99,7 +84,7 @@ func luaSinkReadAll(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
|
|
||||||
lines := []string{}
|
lines := []string{}
|
||||||
for {
|
for {
|
||||||
line, err := s.Rw.ReadString('\n')
|
line, err := s.reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
@ -128,7 +113,7 @@ func luaSinkRead(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
str, _ := s.Rw.ReadString('\n')
|
str, _ := s.reader.ReadString('\n')
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.StringValue(str)), nil
|
return c.PushingNext1(t.Runtime, rt.StringValue(str)), nil
|
||||||
}
|
}
|
||||||
@ -150,9 +135,9 @@ func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Rw.Write([]byte(data))
|
s.writer.Write([]byte(data))
|
||||||
if s.autoFlush {
|
if s.autoFlush {
|
||||||
s.Rw.Flush()
|
s.writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
@ -175,9 +160,9 @@ func luaSinkWriteln(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Rw.Write([]byte(data + "\n"))
|
s.writer.Write([]byte(data + "\n"))
|
||||||
if s.autoFlush {
|
if s.autoFlush {
|
||||||
s.Rw.Flush()
|
s.writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
@ -196,7 +181,7 @@ func luaSinkFlush(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Rw.Flush()
|
s.writer.Flush()
|
||||||
|
|
||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
@ -227,22 +212,9 @@ func luaSinkAutoFlush(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return c.Next(), nil
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSink(rtm *rt.Runtime, Rw io.ReadWriter) *Sink {
|
|
||||||
s := &Sink{
|
|
||||||
Rw: bufio.NewReadWriter(bufio.NewReader(Rw), bufio.NewWriter(Rw)),
|
|
||||||
}
|
|
||||||
s.UserData = sinkUserData(rtm, s)
|
|
||||||
|
|
||||||
if f, ok := Rw.(*os.File); ok {
|
|
||||||
s.file = f
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSinkInput(rtm *rt.Runtime, r io.Reader) *Sink {
|
func NewSinkInput(rtm *rt.Runtime, r io.Reader) *Sink {
|
||||||
s := &Sink{
|
s := &Sink{
|
||||||
Rw: bufio.NewReadWriter(bufio.NewReader(r), nil),
|
reader: bufio.NewReader(r),
|
||||||
}
|
}
|
||||||
s.UserData = sinkUserData(rtm, s)
|
s.UserData = sinkUserData(rtm, s)
|
||||||
|
|
||||||
@ -255,7 +227,7 @@ func NewSinkInput(rtm *rt.Runtime, r io.Reader) *Sink {
|
|||||||
|
|
||||||
func NewSinkOutput(rtm *rt.Runtime, w io.Writer) *Sink {
|
func NewSinkOutput(rtm *rt.Runtime, w io.Writer) *Sink {
|
||||||
s := &Sink{
|
s := &Sink{
|
||||||
Rw: bufio.NewReadWriter(nil, bufio.NewWriter(w)),
|
writer: bufio.NewWriter(w),
|
||||||
autoFlush: true,
|
autoFlush: true,
|
||||||
}
|
}
|
||||||
s.UserData = sinkUserData(rtm, s)
|
s.UserData = sinkUserData(rtm, s)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user