/
substitutions.go
138 lines (118 loc) · 2.83 KB
/
substitutions.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 main
import (
"bytes"
"os"
"strconv"
"strings"
"gopkg.in/logex.v1"
"github.com/danwakefield/gosh/T"
"github.com/danwakefield/gosh/arith"
"github.com/danwakefield/gosh/variables"
)
type Substitution interface {
Sub(*variables.Scope) string
}
type SubSubshell struct {
N Node
}
func (s SubSubshell) Sub(scp *variables.Scope) (returnString string) {
logex.Debug("Substituting shell")
defer func() {
logex.Debugf("Returned '%s'", returnString)
}()
if _, isNoop := s.N.(NodeNoop); isNoop {
return ""
}
out := &bytes.Buffer{}
// Not sure if we need to capture this exit code for the $? var.
// Ignore it for now
_ = s.N.Eval(scp.Copy(), &T.IOContainer{&bytes.Buffer{}, out, os.Stderr})
return strings.TrimRight(out.String(), "\n")
}
type VarSubType int
const (
VarSubNormal VarSubType = iota
VarSubMinus
VarSubPlus
VarSubQuestion
VarSubAssign
VarSubTrimRight
VarSubTrimRightMax
VarSubTrimLeft
VarSubTrimLeftMax
VarSubLength
// VarSubSubString, VarSubReplace and VarSubReplaceAll
// are not used. The parser can currently only handle
// Var subs that use a single arg following the operator symbol
VarSubSubString
VarSubReplace
VarSubReplaceAll
)
type SubVariable struct {
VarName string
SubVal string `json:",omitempty"` // The text following any sub operator
CheckNull bool
SubType VarSubType
}
func (s SubVariable) Sub(scp *variables.Scope) (returnString string) {
logex.Debug("Substituting variable")
defer func() {
logex.Debugf("Returned '%s'", returnString)
}()
v := scp.Get(s.VarName)
switch s.SubType {
case VarSubNormal:
return v.Val
case VarSubLength:
// For the values ${#*} and ${#@}
// the number of positional parameters is returned
// We need to perform IFS splitting to figure this out
return strconv.Itoa(len(v.Val))
}
varExists := v.Set == true
// CheckNull means that an empty string is treated as unset
if s.CheckNull {
varExists = varExists && v.Val != ""
}
switch s.SubType {
case VarSubAssign:
if varExists {
return v.Val
}
scp.Set(s.VarName, s.SubVal)
return s.SubVal
case VarSubMinus:
if varExists {
return v.Val
}
return s.SubVal
case VarSubPlus:
if varExists {
return ""
}
return s.SubVal
case VarSubQuestion:
if varExists {
return v.Val
}
if s.SubVal != "" {
ExitShellWithMessage(T.ExitFailure, s.SubVal)
}
ExitShellWithMessage(T.ExitFailure, s.VarName+": Parameter not set")
case VarSubTrimRight, VarSubTrimRightMax, VarSubTrimLeft, VarSubTrimLeftMax:
ExitShellWithMessage(T.ExitFailure, "Trim operations not implemented")
}
logex.Fatal("SubVariable.Sub unreached")
return ""
}
type SubArith struct {
Raw string
}
func (s SubArith) Sub(scp *variables.Scope) string {
logex.Debug("Subtituting arithmetic")
i, err := arith.Parse(s.Raw, scp)
if err != nil {
panic(err)
}
return strconv.FormatInt(i, 10)
}