182 lines
3.4 KiB
Go
182 lines
3.4 KiB
Go
|
package shelly
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"os/exec"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type Mode int
|
||
|
|
||
|
type Opts struct {
|
||
|
SetupProc func(cmd *exec.Cmd)
|
||
|
}
|
||
|
|
||
|
func (m Mode) String() string {
|
||
|
switch m {
|
||
|
case MODE_NORMAL:
|
||
|
return "NORMAL"
|
||
|
case MODE_STR:
|
||
|
return "STR"
|
||
|
case MODE_ESCAPE:
|
||
|
return "ESCAPE"
|
||
|
case MODE_ESCAPE_STR:
|
||
|
return "ESCAPE_STR"
|
||
|
}
|
||
|
|
||
|
return "UNKNOWN"
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
MODE_NORMAL Mode = iota
|
||
|
MODE_STR
|
||
|
MODE_ESCAPE
|
||
|
MODE_ESCAPE_STR
|
||
|
)
|
||
|
|
||
|
// Tokens will break the string into tokens - It can be later organized in command lines and alikes
|
||
|
func Tokens(str string) ([]string, error) {
|
||
|
params := make([]string, 0)
|
||
|
sb := strings.Builder{}
|
||
|
mode := MODE_NORMAL
|
||
|
for i, a := range str {
|
||
|
switch a {
|
||
|
case ' ', '\t', '\r':
|
||
|
switch mode {
|
||
|
case MODE_NORMAL:
|
||
|
if sb.Len() > 0 {
|
||
|
params = append(params, sb.String())
|
||
|
}
|
||
|
sb = strings.Builder{}
|
||
|
case MODE_STR:
|
||
|
sb.WriteRune(a)
|
||
|
default:
|
||
|
return nil, errors.New(fmt.Sprintf("Error at char %d - cant add space here. Mode: %s", i, mode.String()))
|
||
|
}
|
||
|
case '\n':
|
||
|
switch mode {
|
||
|
case MODE_NORMAL:
|
||
|
if sb.Len() > 0 {
|
||
|
params = append(params, sb.String())
|
||
|
}
|
||
|
params = append(params, "\n")
|
||
|
sb = strings.Builder{}
|
||
|
case MODE_STR:
|
||
|
sb.WriteRune(a)
|
||
|
default:
|
||
|
return nil, errors.New(fmt.Sprintf("Error at char %d - cant add new line here. Mode: %s", i, mode.String()))
|
||
|
}
|
||
|
case '"':
|
||
|
switch mode {
|
||
|
case MODE_NORMAL:
|
||
|
if sb.Len() > 0 {
|
||
|
params = append(params, sb.String())
|
||
|
}
|
||
|
sb = strings.Builder{}
|
||
|
mode = MODE_STR
|
||
|
case MODE_STR:
|
||
|
params = append(params, sb.String())
|
||
|
sb = strings.Builder{}
|
||
|
mode = MODE_NORMAL
|
||
|
case MODE_ESCAPE:
|
||
|
sb.WriteRune(a)
|
||
|
mode = MODE_NORMAL
|
||
|
case MODE_ESCAPE_STR:
|
||
|
sb.WriteRune(a)
|
||
|
mode = MODE_STR
|
||
|
}
|
||
|
case '\\':
|
||
|
switch mode {
|
||
|
case MODE_NORMAL:
|
||
|
mode = MODE_ESCAPE
|
||
|
case MODE_STR:
|
||
|
mode = MODE_ESCAPE_STR
|
||
|
case MODE_ESCAPE:
|
||
|
sb.WriteString("\\")
|
||
|
mode = MODE_NORMAL
|
||
|
case MODE_ESCAPE_STR:
|
||
|
sb.WriteString("\\")
|
||
|
mode = MODE_STR
|
||
|
}
|
||
|
default:
|
||
|
switch mode {
|
||
|
case MODE_NORMAL:
|
||
|
sb.WriteRune(a)
|
||
|
case MODE_STR:
|
||
|
sb.WriteRune(a)
|
||
|
case MODE_ESCAPE:
|
||
|
sb.WriteString("\\" + string(a))
|
||
|
mode = MODE_NORMAL
|
||
|
case MODE_ESCAPE_STR:
|
||
|
sb.WriteString("\\" + string(a))
|
||
|
mode = MODE_STR
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
if mode != MODE_NORMAL {
|
||
|
return nil, errors.New("Unterminated String")
|
||
|
}
|
||
|
if sb.Len() > 0 {
|
||
|
params = append(params, sb.String())
|
||
|
}
|
||
|
|
||
|
return params, nil
|
||
|
}
|
||
|
|
||
|
// Lines will call Tokens first, and reorg them in executable lines
|
||
|
func Lines(str string) ([][]string, error) {
|
||
|
ret := make([][]string, 0)
|
||
|
tokens, err := Tokens(str)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
line := make([]string, 0)
|
||
|
for _, tk := range tokens {
|
||
|
switch tk {
|
||
|
case "\n", "&&", ";":
|
||
|
if len(line) > 0 {
|
||
|
ret = append(ret, line)
|
||
|
}
|
||
|
line = make([]string, 0)
|
||
|
default:
|
||
|
line = append(line, tk)
|
||
|
}
|
||
|
}
|
||
|
if len(line) > 0 {
|
||
|
ret = append(ret, line)
|
||
|
}
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
// Exec will call Lines and execute one by one
|
||
|
func Exec(str string, opts ...*Opts) error {
|
||
|
lines, err := Lines(str)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for _, l := range lines {
|
||
|
if len(l) < 1 {
|
||
|
continue
|
||
|
}
|
||
|
if strings.HasPrefix(l[0], "#") {
|
||
|
continue
|
||
|
}
|
||
|
cmd := exec.Command(l[0], l[1:]...)
|
||
|
if opts != nil && len(opts) > 0 {
|
||
|
opts[0].SetupProc(cmd)
|
||
|
}
|
||
|
cmd.Stdout = log.Writer()
|
||
|
cmd.Stderr = log.Writer()
|
||
|
err = cmd.Run()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
|
||
|
}
|