/* SHS: Syntactically Homogeneous Shell * Copyright (C) 2019 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package stdlib import ( "os" "fmt" "bytes" "os/exec" "syscall" "os/signal" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) var bgProcs = make([]*exec.Cmd, 0) var LastExitCode int var sigs = []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGTSTP, syscall.SIGTTIN, syscall.SIGTTOU, syscall.SIGCONT} func call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if in == nil { return nil } path, err := exec.LookPath(in.Inner.(string)) if err != nil { log.Log(log.ERR, "Couldnt exec " + in.Inner.(string) + ", file not found", "call") return nil } args := []string{} for i := in.Next; i != nil; i = i.Next { if i.Tag == ast.LIST { log.Log(log.ERR, "Couldnt exec " + path + ", element in arguments is a list", "call") return nil } args = append(args, i.Inner.(string)) } var cmd *exec.Cmd if len(args) > 0 { cmd = exec.Command(path, args...) } else { cmd = exec.Command(path) } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin signalChan := make(chan os.Signal, 2) signal.Notify(signalChan, sigs...) go func() { sig := <-signalChan cmd.Process.Signal(sig) }() err = cmd.Run() close(signalChan) signal.Reset(sigs...) if err != nil { if exitError, ok := err.(*exec.ExitError); ok { LastExitCode = exitError.ExitCode() } else { log.Log(log.ERR, "Execution step returned unparsable error: " + err.Error(), "call") } } return nil } func bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if in == nil { return nil } path, err := exec.LookPath(in.Inner.(string)) if err != nil { log.Log(log.ERR, "Couldnt exec " + in.Inner.(string) + ", file not found", "call") return nil } args := []string{} for i := in.Next; i != nil; i = i.Next { if i.Tag == ast.LIST { log.Log(log.ERR, "Couldnt exec " + path + ", element in arguments is a list", "call") return nil } args = append(args, i.Inner.(string)) } var cmd *exec.Cmd if len(args) > 0 { cmd = exec.Command(path, args...) } else { cmd = exec.Command(path) } cmd.Stderr = os.Stderr bgProcs = append(bgProcs, cmd) cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr cmd.Start() cmd.Process.Signal(syscall.SIGTSTP) return nil } func fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if len(bgProcs) < 1 { return nil } cmd := bgProcs[0] bgProcs = bgProcs[1:] signalChan := make(chan os.Signal, 2) signal.Notify(signalChan, sigs...) go func() { sig := <-signalChan cmd.Process.Signal(sig) }() cmd.Process.Signal(syscall.SIGCONT) err := cmd.Wait() close(signalChan) signal.Reset(sigs...) if err != nil { if exitError, ok := err.(*exec.ExitError); ok { LastExitCode = exitError.ExitCode() } else { log.Log(log.ERR, "Execution step returned error: " + err.Error(), "call") } } return nil } func read_cmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if in == nil { return nil } var out bytes.Buffer path, err := exec.LookPath(in.Inner.(string)) if err != nil { log.Log(log.ERR, "Couldnt exec " + in.Inner.(string) + ", file not found", "call") return nil } args := []string{} for i := in.Next; i != nil; i = i.Next { if i.Tag == ast.LIST { log.Log(log.ERR, "Couldnt exec " + path + ", element in arguments is a list", "call") return nil } args = append(args, i.Inner.(string)) } var cmd *exec.Cmd if len(args) > 0 { cmd = exec.Command(path, args...) } else { cmd = exec.Command(path) } cmd.Stdout = &out cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin signalChan := make(chan os.Signal, 2) signal.Notify(signalChan, sigs...) go func() { sig := <-signalChan cmd.Process.Signal(sig) }() err = cmd.Run() close(signalChan) signal.Reset(sigs...) if err != nil { if exitError, ok := err.(*exec.ExitError); ok { LastExitCode = exitError.ExitCode() } else { log.Log(log.ERR, "Execution step returned error: " + err.Error(), "$") } } return &ast.Token{Tag: ast.STRING, Inner: out.String()} } func get_exit(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return &ast.Token{Tag: ast.NUMBER, Inner: fmt.Sprintf("%d", LastExitCode)} }