-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Simple.Program.Script
-- Copyright   :  Duncan Coutts 2009
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- This module provides an library interface to the @hc-pkg@ program.
-- Currently only GHC and LHC have hc-pkg programs.

module Distribution.Simple.Program.Script (

    invocationAsSystemScript,
    invocationAsShellScript,
    invocationAsBatchFile,
  ) where

import Prelude ()
import Distribution.Compat.Prelude

import Distribution.Simple.Program.Run
import Distribution.System

-- | Generate a system script, either POSIX shell script or Windows batch file
-- as appropriate for the given system.
--
invocationAsSystemScript :: OS -> ProgramInvocation -> String
invocationAsSystemScript :: OS -> ProgramInvocation -> String
invocationAsSystemScript Windows = ProgramInvocation -> String
invocationAsBatchFile
invocationAsSystemScript _       = ProgramInvocation -> String
invocationAsShellScript


-- | Generate a POSIX shell script that invokes a program.
--
invocationAsShellScript :: ProgramInvocation -> String
invocationAsShellScript :: ProgramInvocation -> String
invocationAsShellScript
  ProgramInvocation {
    progInvokePath :: ProgramInvocation -> String
progInvokePath  = String
path,
    progInvokeArgs :: ProgramInvocation -> [String]
progInvokeArgs  = [String]
args,
    progInvokeEnv :: ProgramInvocation -> [(String, Maybe String)]
progInvokeEnv   = [(String, Maybe String)]
envExtra,
    progInvokeCwd :: ProgramInvocation -> Maybe String
progInvokeCwd   = Maybe String
mcwd,
    progInvokeInput :: ProgramInvocation -> Maybe String
progInvokeInput = Maybe String
minput
  } = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
          [ "#!/bin/sh" ]
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ ((String, Maybe String) -> [String])
-> [(String, Maybe String)] -> [String]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, Maybe String) -> [String]
setEnv [(String, Maybe String)]
envExtra
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ "cd " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
quote String
cwd | String
cwd <- Maybe String -> [String]
forall a. Maybe a -> [a]
maybeToList Maybe String
mcwd ]
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ (case Maybe String
minput of
              Nothing    -> ""
              Just input :: String
input -> "echo " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
quote String
input String -> String -> String
forall a. [a] -> [a] -> [a]
++ " | ")
         String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords ((String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
quote ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ String
path String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
args) String -> String -> String
forall a. [a] -> [a] -> [a]
++ " \"$@\""]

  where
    setEnv :: (String, Maybe String) -> [String]
setEnv (var :: String
var, Nothing)  = ["unset " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var, "export " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var]
    setEnv (var :: String
var, Just val :: String
val) = ["export " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ "=" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
quote String
val]

    quote :: String -> String
    quote :: String -> String
quote s :: String
s = "'" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ "'"

    escape :: String -> String
escape []        = []
    escape ('\'':cs :: String
cs) = "'\\''" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape (c :: Char
c   :cs :: String
cs) = Char
c        Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escape String
cs


-- | Generate a Windows batch file that invokes a program.
--
invocationAsBatchFile :: ProgramInvocation -> String
invocationAsBatchFile :: ProgramInvocation -> String
invocationAsBatchFile
  ProgramInvocation {
    progInvokePath :: ProgramInvocation -> String
progInvokePath  = String
path,
    progInvokeArgs :: ProgramInvocation -> [String]
progInvokeArgs  = [String]
args,
    progInvokeEnv :: ProgramInvocation -> [(String, Maybe String)]
progInvokeEnv   = [(String, Maybe String)]
envExtra,
    progInvokeCwd :: ProgramInvocation -> Maybe String
progInvokeCwd   = Maybe String
mcwd,
    progInvokeInput :: ProgramInvocation -> Maybe String
progInvokeInput = Maybe String
minput
  } = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
          [ "@echo off" ]
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ ((String, Maybe String) -> String)
-> [(String, Maybe String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, Maybe String) -> String
setEnv [(String, Maybe String)]
envExtra
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ "cd \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
cwd String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\"" | String
cwd <- Maybe String -> [String]
forall a. Maybe a -> [a]
maybeToList Maybe String
mcwd ]
       [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ case Maybe String
minput of
            Nothing    ->
                [ String
path String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (' 'Char -> String -> String
forall a. a -> [a] -> [a]
:) [String]
args ]

            Just input :: String
input ->
                [ "(" ]
             [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ "echo " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
line | String
line <- String -> [String]
lines String
input ]
             [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ ") | "
               String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
path String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""
               String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\arg :: String
arg -> ' 'Char -> String -> String
forall a. a -> [a] -> [a]
:String -> String
quote String
arg) [String]
args ]

  where
    setEnv :: (String, Maybe String) -> String
setEnv (var :: String
var, Nothing)  = "set " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ "="
    setEnv (var :: String
var, Just val :: String
val) = "set " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ "=" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
val

    quote :: String -> String
    quote :: String -> String
quote s :: String
s = "\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQ String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ "\""

    escapeQ :: String -> String
escapeQ []       = []
    escapeQ ('"':cs :: String
cs) = "\"\"\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeQ String
cs
    escapeQ (c :: Char
c  :cs :: String
cs) = Char
c         Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escapeQ String
cs

    escape :: String -> String
escape []        = []
    escape ('|':cs :: String
cs) = "^|" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape ('<':cs :: String
cs) = "^<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape ('>':cs :: String
cs) = "^>" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape ('&':cs :: String
cs) = "^&" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape ('(':cs :: String
cs) = "^(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape (')':cs :: String
cs) = "^)" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape ('^':cs :: String
cs) = "^^" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escape String
cs
    escape (c :: Char
c  :cs :: String
cs) = Char
c     Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
escape String
cs