ndmitchell / cmdargs

Haskell library for command line argument processing
Other
91 stars 12 forks source link

splitArgs: document quoting rules #65

Open hseg opened 4 years ago

hseg commented 4 years ago

Behaviour is at odds with usual shell quoting practices, violating principle of least surprise. Examples of unexpected behaviour:

In addition, some of the state complexity seems unwarranted. Why not something along the lines of:

import           Data.Bifunctor                 ( first )
import           Data.Char                      ( isSpace )
import           Data.List.NonEmpty             ( NonEmpty((:|))
                                                , (<|)
                                                )

data IState = Lit | SingQ | DoubleQ
data OState = Break | Word

-- | split a string into quoted words
-- single/double quotes literal until next occurrence,
-- are escaped by prefixing \
-- \ is itself escaped by \
-- outside quotes, spaces break words and are coalesced
splitArgs :: String -> Maybe (NonEmpty String)
splitArgs s = snd <$> f Lit s
 where
  f :: IState -> String -> Maybe (OState, NonEmpty String)
  f s ('\\' : x : xs) | x `elem` ['\\', '\'', '\"'] = push x <$> f s xs
  f Lit ('\'' : xs)          = f SingQ xs
  f Lit ('\"' : xs)          = f DoubleQ xs
  f Lit (x : xs) | isSpace x = first (const Break) <$> f Lit xs
  f SingQ   ('\'' : xs)      = f Lit xs
  f DoubleQ ('\"' : xs)      = f Lit xs
  f s       (x    : xs)      = push x <$> f s xs
  f Lit     []               = Just (Word, "" :| [])
  f s       []               = Nothing

  push :: a -> (OState, NonEmpty [a]) -> (OState, NonEmpty [a])
  push x (Break, xss      ) = (Word, [x] <| xss)
  push x (Word , xs :| xss) = (Word, (x : xs) :| xss)