@@ -185,8 +185,8 @@ defmodule OptionParser do
185
185
186
186
@ spec next ( argv , options ) ::
187
187
{ :ok , key :: atom , value :: term , argv } |
188
- { :invalid , key :: atom , value :: term , argv } |
189
- { :undefined , key :: atom , value :: term , argv } |
188
+ { :invalid , String . t , String . t | nil , argv } |
189
+ { :undefined , String . t , String . t | nil , argv } |
190
190
{ :error , argv }
191
191
192
192
def next ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
@@ -231,6 +231,62 @@ defmodule OptionParser do
231
231
{ :error , argv }
232
232
end
233
233
234
+ @ doc ~S"""
235
+ Splits a string into argv chunks.
236
+
237
+ ## Examples
238
+
239
+ iex> OptionParser.split("foo bar")
240
+ ["foo", "bar"]
241
+
242
+ iex> OptionParser.split("foo \" bar baz\" ")
243
+ ["foo", "bar baz"]
244
+ """
245
+ @ spec split ( String . t ) :: argv
246
+ def split ( string ) do
247
+ do_split ( strip_leading_spaces ( string ) , "" , [ ] , nil )
248
+ end
249
+
250
+ # If we have a escaped quote, simply remove the escape
251
+ defp do_split ( << ?\\ , quote , t :: binary >> , buffer , acc , quote ) ,
252
+ do: do_split ( t , << buffer :: binary , quote >> , acc , quote )
253
+
254
+ # If we have a quote and we were not in a quote, start one
255
+ defp do_split ( << quote , t :: binary >> , buffer , acc , nil ) when quote in [ ?" , ?' ] ,
256
+ do: do_split ( t , buffer , acc , quote )
257
+
258
+ # If we have a quote and we were inside it, close it
259
+ defp do_split ( << quote , t :: binary >> , buffer , acc , quote ) ,
260
+ do: do_split ( t , buffer , acc , nil )
261
+
262
+ # If we have a escaped quote/space, simply remove the escape as long as we are not inside a quote
263
+ defp do_split ( << ?\\ , h , t :: binary >> , buffer , acc , nil ) when h in [ ?\s , ?' , ?" ] ,
264
+ do: do_split ( t , << buffer :: binary , h >> , acc , nil )
265
+
266
+ # If we have space and we are outside of a quote, start new segment
267
+ defp do_split ( << ?\s , t :: binary >> , buffer , acc , nil ) ,
268
+ do: do_split ( strip_leading_spaces ( t ) , "" , [ buffer | acc ] , nil )
269
+
270
+ # All other characters are moved to buffer
271
+ defp do_split ( << h , t :: binary >> , buffer , acc , quote ) do
272
+ do_split ( t , << buffer :: binary , h >> , acc , quote )
273
+ end
274
+
275
+ # Finish the string expecting a nil marker
276
+ defp do_split ( << >> , "" , acc , nil ) ,
277
+ do: Enum . reverse ( acc )
278
+
279
+ defp do_split ( << >> , buffer , acc , nil ) ,
280
+ do: Enum . reverse ( [ buffer | acc ] )
281
+
282
+ # Otherwise raise
283
+ defp do_split ( << >> , _ , _acc , marker ) do
284
+ raise "argv string did not terminate properly, a #{ << marker >> } was opened but never closed"
285
+ end
286
+
287
+ defp strip_leading_spaces ( " " <> t ) , do: strip_leading_spaces ( t )
288
+ defp strip_leading_spaces ( t ) , do: t
289
+
234
290
## Helpers
235
291
236
292
defp compile_config ( opts ) do
0 commit comments