module Shell.Utility.Quote (always, minimal, lazy) where

mustEscape, mustQuote :: [Char]
mustEscape = "\"$`\\!"
mustQuote = "' \t\n|&;()<>{}[]*?^#"


{- |
Escape a single character if it is necessary to escape it even within quotes.
The exclamation mark is also escaped
for compatibility with the history expansion misfeature of Bash.
-}
escapeChar :: Char -> String
escapeChar c = (if elem c mustEscape then ('\\':) else id) [c]


enclose :: String -> String
enclose txt = '"' : txt ++ '"' : []

{- |
Put a string in quotes and escape characters as necessary.
This allows you to construct shell commands
such that a shell interprets the arguments in the right way.
-}
always :: String -> String
always = enclose . concatMap escapeChar

{- |
Like 'always' but encloses in quotes only if necessary.
-}
minimal :: String -> String
minimal txt =
   if null txt || any (flip elem mustQuote) txt
      then always txt
      else concatMap escapeChar txt

{- |
Similar to 'minimal' but starts quoting only as soon as it becomes necessary.
This is lazy both with respect to quoting and with respect to processing.
-}
lazy :: String -> String
lazy "" = "\"\""
lazy txt =
   let go "" = ""
       go str@(c:cs) =
         if elem c mustQuote then always str else escapeChar c ++ go cs
   in go txt