4
4
5
5
const shell_special = " #{}()[]<>|&*?~;"
6
6
7
+ # strips the end but respects the space when the string ends with "\\ "
8
+ function rstrip_shell (s:: AbstractString )
9
+ c_old = nothing
10
+ for (i, c) in Iterators. reverse (pairs (s))
11
+ ((c == ' \\ ' ) && c_old == ' ' ) && return SubString (s, 1 , i+ 1 )
12
+ c in _default_delims || return SubString (s, 1 , i)
13
+ c_old = c
14
+ end
15
+ SubString (s, 1 , 0 )
16
+ end
17
+
18
+
7
19
# needs to be factored out so depwarn only warns once
8
20
# when removed, also need to update shell_escape for a Cmd to pass shell_special
9
21
# and may want to use it in the test for #10120 (currently the implementation is essentially copied there)
@@ -12,24 +24,10 @@ const shell_special = "#{}()[]<>|&*?~;"
12
24
13
25
function shell_parse (str:: AbstractString , interpolate:: Bool = true ;
14
26
special:: AbstractString = " " )
15
- s = lstrip (str)
16
- # strips the end but respects the space when the string ends with "\\ "
17
- r = reverse (s)
18
- i = start (r)
19
- c_old = nothing
20
- while ! done (r,i)
21
- c, j = next (r,i)
22
- if c == ' \\ ' && c_old == ' '
23
- i -= 1
24
- break
25
- elseif ! (c in _default_delims)
26
- break
27
- end
28
- i = j
29
- c_old = c
30
- end
31
- s = s[1 : end - i+ 1 ]
27
+ s:: SubString = SubString (str, firstindex (str))
28
+ s = rstrip_shell (lstrip (s))
32
29
30
+ # N.B.: This is used by REPLCompletions
33
31
last_parse = 0 : - 1
34
32
isempty (s) && return interpolate ? (Expr (:tuple ,:()),last_parse) : ([],last_parse)
35
33
@@ -38,73 +36,69 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
38
36
39
37
args:: Vector{Any} = []
40
38
arg:: Vector{Any} = []
41
- i = start (s)
42
- j = i
39
+ i = firstindex (s)
40
+ st = Iterators . Stateful ( pairs (s))
43
41
44
42
function update_arg (x)
45
43
if ! isa (x,AbstractString) || ! isempty (x)
46
44
push! (arg, x)
47
45
end
48
46
end
47
+ function consume_upto (j)
48
+ update_arg (s[i: prevind (s, j)])
49
+ i = coalesce (peek (st), (lastindex (s)+ 1 ,' \0 ' ))[1 ]
50
+ end
49
51
function append_arg ()
50
52
if isempty (arg); arg = Any[" " ,]; end
51
53
push! (args, arg)
52
54
arg = []
53
55
end
54
56
55
- while ! done (s,j)
56
- c, k = next (s,j)
57
+ for (j, c) in st
57
58
if ! in_single_quotes && ! in_double_quotes && isspace (c)
58
- update_arg (s[i : prevind (s, j)] )
59
+ consume_upto (j )
59
60
append_arg ()
60
- j = k
61
- while ! done (s,j)
62
- c, k = next (s,j)
63
- if ! isspace (c)
64
- i = j
65
- break
66
- end
67
- j = k
61
+ while ! isempty (st)
62
+ # We've made sure above that we don't end in whitespace,
63
+ # so updateing `i` here is ok
64
+ (i, c) = peek (st)
65
+ isspace (c) || break
66
+ popfirst! (st)
68
67
end
69
68
elseif interpolate && ! in_single_quotes && c == ' $'
70
- update_arg (s[i: prevind (s, j)]); i = k; j = k
71
- if done (s,k)
72
- error (" \$ right before end of command" )
73
- end
74
- if isspace (s[k])
75
- error (" space not allowed right after \$ " )
76
- end
77
- stpos = j
78
- ex, j = Meta. parse (s,j,greedy= false )
79
- last_parse = stpos: j
80
- update_arg (ex); i = j
69
+ consume_upto (j)
70
+ isempty (st) && error (" \$ right before end of command" )
71
+ stpos, c = popfirst! (st)
72
+ isspace (c) && error (" space not allowed right after \$ " )
73
+ ex, j = Meta. parse (s,stpos,greedy= false )
74
+ last_parse = (stpos: prevind (s, j)) .+ s. offset
75
+ update_arg (ex);
76
+ s = SubString (s, j)
77
+ Iterators. reset! (st, pairs (s))
78
+ i = firstindex (s)
81
79
else
82
80
if ! in_double_quotes && c == ' \' '
83
81
in_single_quotes = ! in_single_quotes
84
- update_arg (s[i : prevind (s, j)]); i = k
82
+ consume_upto (j)
85
83
elseif ! in_single_quotes && c == ' "'
86
84
in_double_quotes = ! in_double_quotes
87
- update_arg (s[i : prevind (s, j)]); i = k
85
+ consume_upto (j)
88
86
elseif c == ' \\ '
89
87
if in_double_quotes
90
- if done (s,k)
91
- error (" unterminated double quote" )
92
- end
93
- if s[k] == ' "' || s[k] == ' $' || s[k] == ' \\ '
94
- update_arg (s[i: prevind (s, j)]); i = k
95
- c, k = next (s,k)
88
+ isempty (st) && error (" unterminated double quote" )
89
+ k, c′ = peek (st)
90
+ if c′ == ' "' || c′ == ' $' || c′ == ' \\ '
91
+ consume_upto (j)
92
+ _ = popfirst! (st)
96
93
end
97
94
elseif ! in_single_quotes
98
- if done (s,k)
99
- error (" dangling backslash" )
100
- end
101
- update_arg (s[i: prevind (s, j)]); i = k
102
- c, k = next (s,k)
95
+ isempty (st) && error (" dangling backslash" )
96
+ consume_upto (j)
97
+ _ = popfirst! (st)
103
98
end
104
99
elseif ! in_single_quotes && ! in_double_quotes && c in special
105
100
warn_shell_special (special) # noinline depwarn
106
101
end
107
- j = k
108
102
end
109
103
end
110
104
0 commit comments