-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsub-command.go
138 lines (123 loc) · 3.29 KB
/
sub-command.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package arp
import (
"fmt"
"os/exec"
"strings"
)
const (
CMD_PREFIX = "$("
CMD_SUFFIX = ")"
CMD_DELIMITER = " "
)
func executeCommandStr(input string) (string, error) {
sanitized := []rune(input)
sanitized = sanitized[len(CMD_PREFIX) : len(sanitized)-len(CMD_SUFFIX)]
args := PromoteTokenQuotes(SplitStringTokens(string(sanitized), CMD_DELIMITER))
if len(args) == 0 {
return "", nil
}
cmd := exec.Command(args[0], args[1:]...)
val, err := cmd.CombinedOutput()
return strings.TrimSuffix(string(val), "\n"), err
}
func isCmd(input string) bool {
return strings.HasPrefix(input, CMD_PREFIX) && strings.HasSuffix(input, CMD_SUFFIX)
}
func ExecuteCommand(input string) (interface{}, error) {
var outputString = input
commands := TokenStack{}
commands.Parse(input, CMD_PREFIX, CMD_SUFFIX)
if len(commands.Frames) == 0 {
return input, nil
}
type ExtendedStackFrame struct {
TokenStackFrame
ExecuteCommandResult string
}
toExecute := []ExtendedStackFrame{}
for _, v := range commands.Frames {
toExecute = append(toExecute, ExtendedStackFrame{
TokenStackFrame: v,
ExecuteCommandResult: v.Token,
})
}
for i, v := range toExecute {
var commandOutput string
// make sure we are executing commands and not the results of commands that were already executed
if isCmd(v.ExecuteCommandResult) {
var err error
commandOutput, err = executeCommandStr(v.ExecuteCommandResult)
if err != nil {
errMsg := fmt.Sprintf("Execution error: %v: %q", err, commandOutput)
return errMsg, fmt.Errorf(errMsg)
}
}
if v.Nested == 0 {
outputString = strings.ReplaceAll(outputString, v.Token, commandOutput)
}
// once a command is executed, we want to populate the parent command stack frames with the text result in place
// of this nested command.
for offset := i + 1; offset < len(toExecute); offset++ {
frame := toExecute[offset]
if !strings.Contains(frame.ExecuteCommandResult, v.Token) {
continue
}
frame.ExecuteCommandResult = strings.ReplaceAll(frame.ExecuteCommandResult, v.Token, commandOutput)
toExecute[offset] = frame
}
}
return outputString, nil
}
// Iterate through an object and execute any command strings that are located.
// Returns the input object with the command strings expanded to their results
func RecursiveExecuteCommand(input interface{}) (interface{}, error) {
if input == nil {
return nil, nil
}
switch n := input.(type) {
case map[interface{}]interface{}:
for k := range n {
if node, err := RecursiveExecuteCommand(n[k]); err != nil {
return nil, err
} else {
n[k] = node
}
}
return n, nil
case map[string]interface{}:
for k := range n {
if node, err := RecursiveExecuteCommand(n[k]); err != nil {
return nil, err
} else {
n[k] = node
}
}
return n, nil
case []interface{}:
for i, e := range n {
if node, err := RecursiveExecuteCommand(e); err != nil {
return nil, err
} else {
n[i] = node
}
}
return n, nil
case []string:
var newElements []interface{}
for _, e := range n {
res, err := ExecuteCommand(e)
if err != nil {
return nil, err
}
newElements = append(newElements, res)
}
return newElements, nil
case string:
res, err := ExecuteCommand(n)
if res == nil {
return input, nil
}
return res, err
}
return input, nil
}