{-# LANGUAGE ScopedTypeVariables #-}
module GHC.ResponseFile (
    getArgsWithResponseFiles,
    unescapeArgs,
    escapeArgs,
    expandResponse
  ) where
import Control.Exception
import Data.Char          (isSpace)
import Data.Foldable      (foldl')
import System.Environment (getArgs)
import System.Exit        (exitFailure)
import System.IO
getArgsWithResponseFiles :: IO [String]
getArgsWithResponseFiles :: IO [String]
getArgsWithResponseFiles = IO [String]
getArgs IO [String] -> ([String] -> IO [String]) -> IO [String]
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [String] -> IO [String]
expandResponse
unescapeArgs :: String -> [String]
unescapeArgs :: String -> [String]
unescapeArgs = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
unescape
escapeArgs :: [String] -> String
escapeArgs :: [String] -> String
escapeArgs = [String] -> String
unlines ([String] -> String)
-> ([String] -> [String]) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
escapeArg
expandResponse :: [String] -> IO [String]
expandResponse :: [String] -> IO [String]
expandResponse = ([[String]] -> [String]) -> IO [[String]] -> IO [String]
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (IO [[String]] -> IO [String])
-> ([String] -> IO [[String]]) -> [String] -> IO [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> IO [String]) -> [String] -> IO [[String]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM String -> IO [String]
expand
  where
    expand :: String -> IO [String]
    expand :: String -> IO [String]
expand (Char
'@':String
f) = String -> IO String
readFileExc String
f IO String -> (String -> IO [String]) -> IO [String]
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [String] -> IO [String]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String])
-> (String -> [String]) -> String -> IO [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
unescapeArgs
    expand String
x = [String] -> IO [String]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [String
x]
    readFileExc :: String -> IO String
readFileExc String
f =
      String -> IO String
readFile String
f IO String -> (IOException -> IO String) -> IO String
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \(IOException
e :: IOException) -> do
        Handle -> String -> IO ()
hPutStrLn Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Error while expanding response file: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ IOException -> String
forall a. Show a => a -> String
show IOException
e
        IO String
forall a. IO a
exitFailure
data Quoting = NoneQ | SngQ | DblQ
unescape :: String -> [String]
unescape :: String -> [String]
unescape String
args = [String] -> [String]
forall a. [a] -> [a]
reverse ([String] -> [String])
-> ([String] -> [String]) -> [String] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
forall a. [a] -> [a]
reverse ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ String -> Quoting -> Bool -> String -> [String] -> [String]
go String
args Quoting
NoneQ Bool
False [] []
    where
      
      
      go :: String -> Quoting -> Bool -> String -> [String] -> [String]
go []     Quoting
_q    Bool
_bs   String
a [String]
as = String
aString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
as
      
      go (Char
c:String
cs) Quoting
q     Bool
True  String
a [String]
as = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
q     Bool
False (Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String
a) [String]
as
      
      go (Char
c:String
cs) Quoting
q     Bool
False String
a [String]
as
        | Char
'\\' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
q     Bool
True  String
a     [String]
as
      
      go (Char
c:String
cs) Quoting
SngQ  Bool
False String
a [String]
as
        | Char
'\'' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
NoneQ Bool
False String
a     [String]
as
        | Bool
otherwise              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
SngQ  Bool
False (Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String
a) [String]
as
      
      go (Char
c:String
cs) Quoting
DblQ  Bool
False String
a [String]
as
        | Char
'"' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c               = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
NoneQ Bool
False String
a     [String]
as
        | Bool
otherwise              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
DblQ  Bool
False (Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String
a) [String]
as
      
      go (Char
c:String
cs) Quoting
NoneQ Bool
False String
a [String]
as
        | Char -> Bool
isSpace Char
c              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
NoneQ Bool
False []    (String
aString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
as)
        | Char
'\'' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
SngQ  Bool
False String
a     [String]
as
        | Char
'"'  Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
DblQ  Bool
False String
a     [String]
as
        | Bool
otherwise              = String -> Quoting -> Bool -> String -> [String] -> [String]
go String
cs Quoting
NoneQ Bool
False (Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String
a) [String]
as
escapeArg :: String -> String
escapeArg :: String -> String
escapeArg = String -> String
forall a. [a] -> [a]
reverse (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Char -> String) -> String -> String -> String
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' String -> Char -> String
escape []
escape :: String -> Char -> String
escape :: String -> Char -> String
escape String
cs Char
c
  |    Char -> Bool
isSpace Char
c
    Bool -> Bool -> Bool
|| Char
'\\' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c
    Bool -> Bool -> Bool
|| Char
'\'' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c
    Bool -> Bool -> Bool
|| Char
'"'  Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c = Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:Char
'\\'Char -> String -> String
forall a. a -> [a] -> [a]
:String
cs 
  | Bool
otherwise    = Char
cChar -> String -> String
forall a. a -> [a] -> [a]
:String
cs