Skip to content

Commit dda5e47

Browse files
committed
add util
1 parent a64511b commit dda5e47

File tree

2 files changed

+312
-0
lines changed

2 files changed

+312
-0
lines changed

util.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package commandproxy
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
var (
10+
errQuoteNotClose = fmt.Errorf("quote is not closed")
11+
errEmptyCmmand = fmt.Errorf("cmd is empty")
12+
)
13+
14+
func SplitCommand(cmd string) ([]string, error) {
15+
cmd = strings.TrimSpace(cmd)
16+
if cmd == "" {
17+
return nil, errEmptyCmmand
18+
}
19+
var ss []string
20+
21+
var buf bytes.Buffer
22+
for len(cmd) != 0 {
23+
i := strings.IndexAny(cmd, "'\"\\ \n\t")
24+
if i == -1 {
25+
buf.WriteString(cmd)
26+
break
27+
}
28+
buf.WriteString(cmd[:i])
29+
c := cmd[i]
30+
cmd = cmd[i+1:]
31+
switch c {
32+
case '"':
33+
i := strings.IndexByte(cmd, '"')
34+
if i == -1 {
35+
return nil, errQuoteNotClose
36+
}
37+
buf.WriteString(cmd[:i])
38+
cmd = cmd[i+1:]
39+
case '\'':
40+
i := strings.IndexByte(cmd, '\'')
41+
if i == -1 {
42+
return nil, errQuoteNotClose
43+
}
44+
buf.WriteString(cmd[:i])
45+
cmd = cmd[i+1:]
46+
case '\\':
47+
if len(cmd) == 0 {
48+
break
49+
}
50+
if cmd[0] != '\n' {
51+
buf.WriteByte(cmd[0])
52+
cmd = cmd[:1]
53+
continue
54+
}
55+
fallthrough
56+
case '\t', ' ':
57+
if buf.Len() != 0 {
58+
ss = append(ss, buf.String())
59+
buf.Reset()
60+
}
61+
}
62+
}
63+
64+
if buf.Len() != 0 {
65+
if len(ss) == 0 {
66+
return []string{cmd}, nil
67+
}
68+
ss = append(ss, buf.String())
69+
}
70+
71+
return ss, nil
72+
}
73+
74+
func ReplaceEscape(s string, re map[byte]string) string {
75+
re['%'] = "%"
76+
var buf bytes.Buffer
77+
for len(s) != 0 {
78+
i := strings.IndexByte(s, '%')
79+
if i == -1 || i >= len(s)-1 {
80+
if buf.Len() == 0 {
81+
return s
82+
}
83+
buf.WriteString(s)
84+
break
85+
}
86+
buf.WriteString(s[:i])
87+
s = s[i:]
88+
rep, ok := re[s[1]]
89+
if ok {
90+
buf.WriteString(rep)
91+
} else {
92+
buf.WriteString(s[:2])
93+
}
94+
s = s[2:]
95+
}
96+
return buf.String()
97+
}

util_test.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package commandproxy
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestSplitCommand(t *testing.T) {
9+
type args struct {
10+
cmd string
11+
}
12+
tests := []struct {
13+
name string
14+
args args
15+
want []string
16+
wantErr bool
17+
}{
18+
{
19+
args: args{
20+
cmd: "",
21+
},
22+
wantErr: true,
23+
},
24+
{
25+
args: args{
26+
cmd: "a",
27+
},
28+
want: []string{"a"},
29+
},
30+
{
31+
args: args{
32+
cmd: "a b",
33+
},
34+
want: []string{"a", "b"},
35+
},
36+
{
37+
args: args{
38+
cmd: " a b",
39+
},
40+
want: []string{"a", "b"},
41+
},
42+
{
43+
args: args{
44+
cmd: "a b ",
45+
},
46+
want: []string{"a", "b"},
47+
},
48+
{
49+
args: args{
50+
cmd: "a b",
51+
},
52+
want: []string{"a", "b"},
53+
},
54+
{
55+
args: args{
56+
cmd: "a \"",
57+
},
58+
wantErr: true,
59+
},
60+
{
61+
args: args{
62+
cmd: "a '",
63+
},
64+
wantErr: true,
65+
},
66+
{
67+
args: args{
68+
cmd: "a\t\tb",
69+
},
70+
want: []string{"a", "b"},
71+
},
72+
{
73+
args: args{
74+
cmd: `a \\`,
75+
},
76+
want: []string{"a", "\\"},
77+
},
78+
{
79+
args: args{
80+
cmd: `a \
81+
b`,
82+
},
83+
want: []string{"a", "b"},
84+
},
85+
{
86+
args: args{
87+
cmd: `a "b"`,
88+
},
89+
want: []string{"a", "b"},
90+
},
91+
{
92+
args: args{
93+
cmd: `a 'b'`,
94+
},
95+
want: []string{"a", "b"},
96+
},
97+
{
98+
args: args{
99+
cmd: `a "b "`,
100+
},
101+
want: []string{"a", "b "},
102+
},
103+
{
104+
args: args{
105+
cmd: `a """"`,
106+
},
107+
want: []string{"a"},
108+
},
109+
110+
{
111+
args: args{
112+
cmd: `a "'b'"`,
113+
},
114+
want: []string{"a", "'b'"},
115+
},
116+
}
117+
for _, tt := range tests {
118+
t.Run(tt.name, func(t *testing.T) {
119+
got, err := SplitCommand(tt.args.cmd)
120+
if (err != nil) != tt.wantErr {
121+
t.Errorf("SplitCommand() error = %v, wantErr %v", err, tt.wantErr)
122+
return
123+
}
124+
if !reflect.DeepEqual(got, tt.want) {
125+
t.Errorf("SplitCommand() got = %v, want %v", got, tt.want)
126+
}
127+
})
128+
}
129+
}
130+
131+
func TestReplaceEscape(t *testing.T) {
132+
type args struct {
133+
s string
134+
re map[byte]string
135+
}
136+
tests := []struct {
137+
name string
138+
args args
139+
want string
140+
}{
141+
{
142+
args: args{
143+
s: "",
144+
re: map[byte]string{},
145+
},
146+
want: "",
147+
},
148+
{
149+
args: args{
150+
s: "a",
151+
re: map[byte]string{},
152+
},
153+
want: "a",
154+
},
155+
{
156+
args: args{
157+
s: "%",
158+
re: map[byte]string{},
159+
},
160+
want: "%",
161+
},
162+
{
163+
args: args{
164+
s: "%%",
165+
re: map[byte]string{},
166+
},
167+
want: "%",
168+
},
169+
{
170+
args: args{
171+
s: "%%a",
172+
re: map[byte]string{},
173+
},
174+
want: "%a",
175+
},
176+
{
177+
args: args{
178+
s: "a%%",
179+
re: map[byte]string{},
180+
},
181+
want: "a%",
182+
},
183+
{
184+
args: args{
185+
s: "aa%%aa",
186+
re: map[byte]string{},
187+
},
188+
want: "aa%aa",
189+
},
190+
{
191+
args: args{
192+
s: "%a",
193+
re: map[byte]string{},
194+
},
195+
want: "%a",
196+
},
197+
{
198+
args: args{
199+
s: "%h:%p",
200+
re: map[byte]string{
201+
'h': "127.0.0.1",
202+
'p': "80",
203+
},
204+
},
205+
want: "127.0.0.1:80",
206+
},
207+
}
208+
for _, tt := range tests {
209+
t.Run(tt.name, func(t *testing.T) {
210+
if got := ReplaceEscape(tt.args.s, tt.args.re); got != tt.want {
211+
t.Errorf("ReplaceEscape() = %v, want %v", got, tt.want)
212+
}
213+
})
214+
}
215+
}

0 commit comments

Comments
 (0)