@@ -185,8 +185,8 @@ defmodule OptionParser do
185185
186186 @ spec next ( argv , options ) ::
187187 { :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 } |
190190 { :error , argv }
191191
192192 def next ( argv , opts \\ [ ] ) when is_list ( argv ) and is_list ( opts ) do
@@ -231,6 +231,62 @@ defmodule OptionParser do
231231 { :error , argv }
232232 end
233233
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+
234290 ## Helpers
235291
236292 defp compile_config ( opts ) do
0 commit comments