module SimpleCmd (
cmd, cmd_,
cmdBool,
cmdIgnoreErr,
cmdLines,
cmdMaybe,
cmdLog, cmdlog ,
cmdN,
cmdQuiet,
cmdSilent,
cmdStdIn,
cmdStdErr,
cmdTry_,
error',
egrep_, grep, grep_,
ifM,
logMsg,
needProgram,
removePrefix, removeStrictPrefix, removeSuffix,
shell, shell_,
shellBool,
sudo, sudo_,
warning,
PipeCommand,
pipe, pipe_, pipeBool,
pipe3_, pipeFile_,
whenM,
(+-+)) where
#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))
#else
import Control.Applicative ((<$>))
#endif
import Control.Monad
import Data.List (stripPrefix)
import Data.Maybe (isJust, isNothing, fromMaybe)
import System.Directory (findExecutable)
import System.Exit (ExitCode (..))
import System.IO (hGetContents, hPutStrLn, IOMode(ReadMode), stderr, withFile)
import System.Posix.User (getEffectiveUserID)
import System.Process (createProcess, proc, ProcessHandle, rawSystem, readProcess,
readProcessWithExitCode, runProcess, showCommandForUser,
std_in, std_out, StdStream(CreatePipe, UseHandle),
waitForProcess, withCreateProcess)
removeTrailingNewline :: String -> String
removeTrailingNewline "" = ""
removeTrailingNewline str =
if last str == '\n'
then init str
else str
quoteCmd :: String -> [String] -> String
quoteCmd = showCommandForUser
error' :: String -> a
#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,9,0))
error' = errorWithoutStackTrace
#else
error' = error
#endif
cmd :: String
-> [String]
-> IO String
cmd c args = cmdStdIn c args ""
cmd_ :: String -> [String] -> IO ()
cmd_ c args = do
ret <- rawSystem c args
case ret of
ExitSuccess -> return ()
ExitFailure n -> error' $ quoteCmd c args +-+ "failed with exit code" +-+ show n
boolWrapper :: IO ExitCode -> IO Bool
boolWrapper pr = do
ret <- pr
case ret of
ExitSuccess -> return True
ExitFailure _ -> return False
cmdBool :: String -> [String] -> IO Bool
cmdBool c args =
boolWrapper (rawSystem c args)
cmdMaybe :: String -> [String] -> IO (Maybe String)
cmdMaybe c args = do
(ret, out, _err) <- readProcessWithExitCode c args ""
case ret of
ExitSuccess -> return $ Just $ removeTrailingNewline out
ExitFailure _ -> return Nothing
cmdLines :: String -> [String] -> IO [String]
cmdLines c args = lines <$> cmd c args
cmdStdIn :: String -> [String] -> String -> IO String
cmdStdIn c args inp = removeTrailingNewline <$> readProcess c args inp
shell :: String -> IO String
shell cs = cmd "sh" ["-c", cs]
shell_ :: String -> IO ()
shell_ cs = cmd_ "sh" ["-c", cs]
shellBool :: String -> IO Bool
shellBool cs =
boolWrapper (rawSystem "sh" ["-c", cs])
cmdLog :: String -> [String] -> IO ()
cmdLog c args = do
logMsg $ unwords $ c:args
cmd_ c args
cmdlog :: String -> [String] -> IO ()
cmdlog = cmdLog
logMsg :: String -> IO ()
logMsg msg = do
date <- cmd "date" ["+%T"]
putStrLn $ date +-+ msg
cmdN :: String -> [String] -> IO ()
cmdN c args = putStrLn $ unwords $ c:args
cmdStdErr :: String -> [String] -> IO (String, String)
cmdStdErr c args = do
(_ret, out, err) <- readProcessWithExitCode c args ""
return (removeTrailingNewline out, removeTrailingNewline err)
cmdQuiet :: String -> [String] -> IO String
cmdQuiet c args = do
(ret, out, err) <- readProcessWithExitCode c args ""
case ret of
ExitSuccess -> return $removeTrailingNewline out
ExitFailure n -> error' $ quoteCmd c args +-+ "failed with status" +-+ show n ++ "\n" ++ err
cmdSilent :: String -> [String] -> IO ()
cmdSilent c args = do
(ret, _, err) <- readProcessWithExitCode c args ""
case ret of
ExitSuccess -> return ()
ExitFailure n -> error' $ quoteCmd c args +-+ "failed with status" +-+ show n ++ "\n" ++ err
cmdIgnoreErr :: String -> [String] -> String -> IO String
cmdIgnoreErr c args input = do
(_exit, out, _err) <- readProcessWithExitCode c args input
return out
cmdTry_ :: String -> [String] -> IO ()
cmdTry_ c args = do
have <- findExecutable c
when (isJust have) $
cmd_ c args
grep :: String -> FilePath -> IO [String]
grep pat file =
cmdLines "grep" [pat, file]
grep_ :: String
-> FilePath
-> IO Bool
grep_ pat file =
cmdBool "grep" ["-q", pat, file]
egrep_ :: String -> FilePath -> IO Bool
egrep_ pat file =
cmdBool "grep" ["-q", "-e", pat, file]
sudo :: String
-> [String]
-> IO String
sudo = sudoInternal cmd
sudo_ :: String
-> [String]
-> IO ()
sudo_ = sudoInternal cmdLog
sudoInternal :: (String -> [String] -> IO a) -> String -> [String] -> IO a
sudoInternal exc c args = do
uid <- getEffectiveUserID
sd <- if uid == 0
then return Nothing
else findExecutable "sudo"
let noSudo = isNothing sd
when (uid /= 0 && noSudo) $
warning "'sudo' not found"
exc (fromMaybe c sd) (if noSudo then args else c:args)
infixr 4 +-+
(+-+) :: String -> String -> String
"" +-+ s = s
s +-+ "" = s
s +-+ t | last s == ' ' = s ++ t
| head t == ' ' = s ++ t
s +-+ t = s ++ " " ++ t
removePrefix :: String -> String-> String
removePrefix prefix orig =
fromMaybe orig $ stripPrefix prefix orig
removeStrictPrefix :: String -> String -> String
removeStrictPrefix prefix orig =
fromMaybe (error' prefix +-+ "is not prefix of" +-+ orig) $ stripPrefix prefix orig
removeSuffix :: String -> String -> String
removeSuffix suffix orig =
fromMaybe orig $ stripSuffix suffix orig
where
stripSuffix sf str = reverse <$> stripPrefix (reverse sf) (reverse str)
warning :: String -> IO ()
warning = hPutStrLn stderr
type PipeCommand = (String,[String])
pipe :: PipeCommand -> PipeCommand -> IO String
pipe (c1,args1) (c2,args2) =
withCreateProcess ((proc c1 args1) { std_out = CreatePipe }) $
\ _si (Just ho1) _se p1 -> do
(_, Just ho2, _, p2) <- createProcess ((proc c2 args2) {std_in = UseHandle ho1, std_out = CreatePipe})
out <- hGetContents ho2
void $ waitForProcess p1
void $ waitForProcess p2
return out
pipe_ :: PipeCommand -> PipeCommand -> IO ()
pipe_ (c1,args1) (c2,args2) =
void $ pipeInternal (c1,args1) (c2,args2) >>= waitForProcess
pipeBool :: PipeCommand -> PipeCommand -> IO Bool
pipeBool (c1,args1) (c2,args2) =
boolWrapper $ pipeInternal (c1,args1) (c2,args2) >>= waitForProcess
pipeInternal :: PipeCommand -> PipeCommand -> IO ProcessHandle
pipeInternal (c1,args1) (c2,args2) =
withCreateProcess ((proc c1 args1) { std_out = CreatePipe }) $
\ _si so _se p1 -> do
p2 <- runProcess c2 args2 Nothing Nothing so Nothing Nothing
void $ waitForProcess p1
return p2
pipe3_ :: PipeCommand -> PipeCommand -> PipeCommand -> IO ()
pipe3_ (c1,a1) (c2,a2) (c3,a3) =
withCreateProcess ((proc c1 a1) { std_out = CreatePipe }) $
\ _hi1 (Just ho1) _he1 p1 ->
withCreateProcess ((proc c2 a2) {std_in = UseHandle ho1, std_out = CreatePipe}) $
\ _hi2 ho2 _he2 p2 -> do
p3 <- runProcess c3 a3 Nothing Nothing ho2 Nothing Nothing
void $ waitForProcess p1
void $ waitForProcess p2
void $ waitForProcess p3
pipeFile_ :: FilePath -> PipeCommand -> PipeCommand -> IO ()
pipeFile_ infile (c1,a1) (c2,a2) =
withFile infile ReadMode $
\ hin ->
withCreateProcess ((proc c1 a1) { std_in = UseHandle hin, std_out = CreatePipe }) $
\ _si so _se p1 -> do
p2 <- runProcess c2 a2 Nothing Nothing so Nothing Nothing
void $ waitForProcess p1
void $ waitForProcess p2
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM p x y = p >>= \b -> if b then x else y
whenM :: Monad m => m Bool -> m () -> m ()
whenM p x = p >>= \b -> when b x
needProgram :: String -> IO ()
needProgram prog = do
mx <- findExecutable prog
unless (isJust mx) $ error' $ "program needs " ++ prog