Files
tablizer/vendor/github.com/glycerine/zygomys/zygo/system.go
2024-05-14 12:10:58 +02:00

126 lines
2.9 KiB
Go

package zygo
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
)
var ShellCmd string = "/bin/bash"
func init() {
SetShellCmd()
}
// set ShellCmd as used by SystemFunction
func SetShellCmd() {
if runtime.GOOS == "windows" {
ShellCmd = os.Getenv("COMSPEC")
return
}
try := []string{"/usr/bin/bash"}
if !FileExists(ShellCmd) {
for i := range try {
b := try[i]
if FileExists(b) {
ShellCmd = b
return
}
}
}
}
// sys is a builder. shell out, return the combined output.
func SystemBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
//P("SystemBuilder called with args='%#v'", args)
return SystemFunction(env, name, args)
}
func SystemFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
if len(args) == 0 {
return SexpNull, WrongNargs
}
flat, err := flattenToWordsHelper(args)
if err != nil {
return SexpNull, fmt.Errorf("flatten on '%#v' failed with error '%s'", args, err)
}
if len(flat) == 0 {
return SexpNull, WrongNargs
}
joined := strings.Join(flat, " ")
cmd := ShellCmd
var out []byte
if runtime.GOOS == "windows" {
out, err = exec.Command(cmd, "/c", joined).CombinedOutput()
} else {
out, err = exec.Command(cmd, "-c", joined).CombinedOutput()
}
if err != nil {
return SexpNull, fmt.Errorf("error from command: '%s'. Output:'%s'", err, string(Chomp(out)))
}
return &SexpStr{S: string(Chomp(out))}, nil
}
// given strings/lists of strings with possible whitespace
// flatten out to a array of SexpStr with no internal whitespace,
// suitable for passing along to (system) / exec.Command()
func FlattenToWordsFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
if len(args) == 0 {
return SexpNull, WrongNargs
}
stringArgs, err := flattenToWordsHelper(args)
if err != nil {
return SexpNull, err
}
// Now convert to []Sexp{SexpStr}
res := make([]Sexp, len(stringArgs))
for i := range stringArgs {
res[i] = &SexpStr{S: stringArgs[i]}
}
return env.NewSexpArray(res), nil
}
func flattenToWordsHelper(args []Sexp) ([]string, error) {
stringArgs := []string{}
for i := range args {
switch c := args[i].(type) {
case *SexpStr:
many := strings.Split(c.S, " ")
stringArgs = append(stringArgs, many...)
case *SexpSymbol:
stringArgs = append(stringArgs, c.name)
case *SexpPair:
carry, err := ListToArray(c)
if err != nil {
return []string{}, fmt.Errorf("tried to convert list of strings to array but failed with error '%s'. Input was type %T / val = '%#v'", err, c, c)
}
moreWords, err := flattenToWordsHelper(carry)
if err != nil {
return []string{}, err
}
stringArgs = append(stringArgs, moreWords...)
default:
return []string{}, fmt.Errorf("arguments to system must be strings; instead we have %T / val = '%#v'", c, c)
}
} // end i over args
// INVAR: stringArgs has our flattened list.
return stringArgs, nil
}
func Chomp(by []byte) []byte {
if len(by) > 0 {
n := len(by)
if by[n-1] == '\n' {
return by[:n-1]
}
}
return by
}