1313use std:: borrow:: Cow ;
1414use std:: error;
1515use std:: fmt;
16- use std:: mem ;
16+ use std:: iter ;
1717use std:: result;
18+ use std:: str:: Chars ;
1819
1920/// An error returned when shell parsing fails.
2021#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -28,6 +29,7 @@ impl fmt::Display for ParseError {
2829
2930impl error:: Error for ParseError { }
3031
32+ #[ derive( Debug ) ]
3133enum State {
3234 /// Within a delimiter.
3335 Delimiter ,
@@ -47,6 +49,136 @@ enum State {
4749 Comment ,
4850}
4951
52+ pub struct Split < ' a > {
53+ chars : Chars < ' a > ,
54+ state : State ,
55+ }
56+
57+ pub fn split_iter ( s : & str ) -> Split < ' _ > {
58+ Split {
59+ chars : s. chars ( ) ,
60+ state : State :: Delimiter ,
61+ }
62+ }
63+
64+ impl Iterator for Split < ' _ > {
65+ type Item = Result < String , ParseError > ;
66+
67+ fn next ( & mut self ) -> Option < Result < String , ParseError > > {
68+ use State :: * ;
69+
70+ let mut word = String :: new ( ) ;
71+
72+ loop {
73+ let c = self . chars . next ( ) ;
74+ self . state = match self . state {
75+ Delimiter => match c {
76+ None => return None ,
77+ Some ( '\'' ) => SingleQuoted ,
78+ Some ( '\"' ) => DoubleQuoted ,
79+ Some ( '\\' ) => Backslash ,
80+ Some ( '\t' ) | Some ( ' ' ) | Some ( '\n' ) => Delimiter ,
81+ Some ( '#' ) => Comment ,
82+ Some ( c) => {
83+ word. push ( c) ;
84+ Unquoted
85+ }
86+ } ,
87+ Backslash => match c {
88+ None => {
89+ word. push ( '\\' ) ;
90+ self . state = Delimiter ;
91+ return Some ( Ok ( word) ) ;
92+ }
93+ Some ( '\n' ) => Delimiter ,
94+ Some ( c) => {
95+ word. push ( c) ;
96+ Unquoted
97+ }
98+ } ,
99+ Unquoted => match c {
100+ None => {
101+ self . state = Delimiter ;
102+ return Some ( Ok ( word) ) ;
103+ }
104+ Some ( '\'' ) => SingleQuoted ,
105+ Some ( '\"' ) => DoubleQuoted ,
106+ Some ( '\\' ) => UnquotedBackslash ,
107+ Some ( '\t' ) | Some ( ' ' ) | Some ( '\n' ) => {
108+ self . state = Delimiter ;
109+ return Some ( Ok ( word) ) ;
110+ }
111+ Some ( c) => {
112+ word. push ( c) ;
113+ Unquoted
114+ }
115+ } ,
116+ UnquotedBackslash => match c {
117+ None => {
118+ word. push ( '\\' ) ;
119+ self . state = Delimiter ;
120+ return Some ( Ok ( word) ) ;
121+ }
122+ Some ( '\n' ) => Unquoted ,
123+ Some ( c) => {
124+ word. push ( c) ;
125+ Unquoted
126+ }
127+ } ,
128+ SingleQuoted => match c {
129+ None => {
130+ self . state = Delimiter ;
131+ return Some ( Err ( ParseError ) ) ;
132+ }
133+ Some ( '\'' ) => Unquoted ,
134+ Some ( c) => {
135+ word. push ( c) ;
136+ SingleQuoted
137+ }
138+ } ,
139+ DoubleQuoted => match c {
140+ None => {
141+ self . state = Delimiter ;
142+ return Some ( Err ( ParseError ) ) ;
143+ }
144+ Some ( '\"' ) => Unquoted ,
145+ Some ( '\\' ) => DoubleQuotedBackslash ,
146+ Some ( c) => {
147+ word. push ( c) ;
148+ DoubleQuoted
149+ }
150+ } ,
151+ DoubleQuotedBackslash => match c {
152+ None => {
153+ self . state = Delimiter ;
154+ return Some ( Err ( ParseError ) ) ;
155+ }
156+ Some ( '\n' ) => DoubleQuoted ,
157+ Some ( c @ '$' ) | Some ( c @ '`' ) | Some ( c @ '"' ) | Some ( c @ '\\' ) => {
158+ word. push ( c) ;
159+ DoubleQuoted
160+ }
161+ Some ( c) => {
162+ word. push ( '\\' ) ;
163+ word. push ( c) ;
164+ DoubleQuoted
165+ }
166+ } ,
167+ Comment => match c {
168+ None => {
169+ self . state = Delimiter ;
170+ return None ;
171+ }
172+ Some ( '\n' ) => Delimiter ,
173+ Some ( _) => Comment ,
174+ } ,
175+ }
176+ }
177+ }
178+ }
179+
180+ impl iter:: FusedIterator for Split < ' _ > { }
181+
50182/// Splits command line into separate arguments, in much the same way Unix shell would, but without
51183/// many of expansion the shell would perform.
52184///
@@ -106,107 +238,10 @@ enum State {
106238/// .expect("failed to wait for subprocess");
107239/// ```
108240pub fn split ( s : & str ) -> result:: Result < Vec < String > , ParseError > {
109- use State :: * ;
110-
111241 let mut words = Vec :: new ( ) ;
112- let mut word = String :: new ( ) ;
113- let mut chars = s. chars ( ) ;
114- let mut state = Delimiter ;
115-
116- loop {
117- let c = chars. next ( ) ;
118- state = match state {
119- Delimiter => match c {
120- None => break ,
121- Some ( '\'' ) => SingleQuoted ,
122- Some ( '\"' ) => DoubleQuoted ,
123- Some ( '\\' ) => Backslash ,
124- Some ( '\t' ) | Some ( ' ' ) | Some ( '\n' ) => Delimiter ,
125- Some ( '#' ) => Comment ,
126- Some ( c) => {
127- word. push ( c) ;
128- Unquoted
129- }
130- } ,
131- Backslash => match c {
132- None => {
133- word. push ( '\\' ) ;
134- words. push ( mem:: replace ( & mut word, String :: new ( ) ) ) ;
135- break ;
136- }
137- Some ( '\n' ) => Delimiter ,
138- Some ( c) => {
139- word. push ( c) ;
140- Unquoted
141- }
142- } ,
143- Unquoted => match c {
144- None => {
145- words. push ( mem:: replace ( & mut word, String :: new ( ) ) ) ;
146- break ;
147- }
148- Some ( '\'' ) => SingleQuoted ,
149- Some ( '\"' ) => DoubleQuoted ,
150- Some ( '\\' ) => UnquotedBackslash ,
151- Some ( '\t' ) | Some ( ' ' ) | Some ( '\n' ) => {
152- words. push ( mem:: replace ( & mut word, String :: new ( ) ) ) ;
153- Delimiter
154- }
155- Some ( c) => {
156- word. push ( c) ;
157- Unquoted
158- }
159- } ,
160- UnquotedBackslash => match c {
161- None => {
162- word. push ( '\\' ) ;
163- words. push ( mem:: replace ( & mut word, String :: new ( ) ) ) ;
164- break ;
165- }
166- Some ( '\n' ) => Unquoted ,
167- Some ( c) => {
168- word. push ( c) ;
169- Unquoted
170- }
171- } ,
172- SingleQuoted => match c {
173- None => return Err ( ParseError ) ,
174- Some ( '\'' ) => Unquoted ,
175- Some ( c) => {
176- word. push ( c) ;
177- SingleQuoted
178- }
179- } ,
180- DoubleQuoted => match c {
181- None => return Err ( ParseError ) ,
182- Some ( '\"' ) => Unquoted ,
183- Some ( '\\' ) => DoubleQuotedBackslash ,
184- Some ( c) => {
185- word. push ( c) ;
186- DoubleQuoted
187- }
188- } ,
189- DoubleQuotedBackslash => match c {
190- None => return Err ( ParseError ) ,
191- Some ( '\n' ) => DoubleQuoted ,
192- Some ( c @ '$' ) | Some ( c @ '`' ) | Some ( c @ '"' ) | Some ( c @ '\\' ) => {
193- word. push ( c) ;
194- DoubleQuoted
195- }
196- Some ( c) => {
197- word. push ( '\\' ) ;
198- word. push ( c) ;
199- DoubleQuoted
200- }
201- } ,
202- Comment => match c {
203- None => break ,
204- Some ( '\n' ) => Delimiter ,
205- Some ( _) => Comment ,
206- } ,
207- }
242+ for word in split_iter ( s) {
243+ words. push ( word?) ;
208244 }
209-
210245 Ok ( words)
211246}
212247
0 commit comments