2010-10-10 04:18:10 +00:00
|
|
|
{- git-annex utility functions
|
2010-10-27 20:53:54 +00:00
|
|
|
-
|
|
|
|
- Copyright 2010 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
2010-10-10 04:18:10 +00:00
|
|
|
-}
|
|
|
|
|
2010-10-11 21:52:46 +00:00
|
|
|
module Utility (
|
|
|
|
hGetContentsStrict,
|
2010-10-15 20:09:30 +00:00
|
|
|
parentDir,
|
|
|
|
relPathCwdToDir,
|
2010-10-19 05:45:45 +00:00
|
|
|
relPathDirToDir,
|
2010-10-22 18:57:02 +00:00
|
|
|
boolSystem,
|
2010-11-08 21:44:08 +00:00
|
|
|
shellEscape,
|
2010-11-28 19:28:20 +00:00
|
|
|
unsetFileMode,
|
|
|
|
readMaybe
|
2010-10-11 21:52:46 +00:00
|
|
|
) where
|
2010-10-10 04:18:10 +00:00
|
|
|
|
|
|
|
import System.IO
|
2010-10-19 05:45:45 +00:00
|
|
|
import System.Exit
|
2010-10-29 17:57:22 +00:00
|
|
|
import System.Posix.Process
|
2010-10-19 05:45:45 +00:00
|
|
|
import System.Posix.Signals
|
2010-11-08 21:44:08 +00:00
|
|
|
import System.Posix.Files
|
|
|
|
import System.Posix.Types
|
2010-10-10 04:18:10 +00:00
|
|
|
import Data.String.Utils
|
2010-10-15 20:09:30 +00:00
|
|
|
import System.Path
|
2010-10-15 23:01:20 +00:00
|
|
|
import System.FilePath
|
2010-10-15 20:09:30 +00:00
|
|
|
import System.Directory
|
2010-11-08 21:44:08 +00:00
|
|
|
import Foreign (complement)
|
2010-10-10 04:18:10 +00:00
|
|
|
|
2010-10-10 06:22:35 +00:00
|
|
|
{- A version of hgetContents that is not lazy. Ensures file is
|
|
|
|
- all read before it gets closed. -}
|
2010-10-31 20:00:32 +00:00
|
|
|
hGetContentsStrict :: Handle -> IO String
|
2010-10-10 06:22:35 +00:00
|
|
|
hGetContentsStrict h = hGetContents h >>= \s -> length s `seq` return s
|
|
|
|
|
2010-10-10 04:18:10 +00:00
|
|
|
{- Returns the parent directory of a path. Parent of / is "" -}
|
|
|
|
parentDir :: String -> String
|
|
|
|
parentDir dir =
|
2010-11-22 21:51:55 +00:00
|
|
|
if not $ null dirs
|
|
|
|
then slash ++ join s (take (length dirs - 1) dirs)
|
2010-10-10 04:18:10 +00:00
|
|
|
else ""
|
|
|
|
where
|
2010-11-22 21:51:55 +00:00
|
|
|
dirs = filter (not . null) $ split s dir
|
|
|
|
slash = if isAbsolute dir then s else ""
|
2010-10-15 23:01:20 +00:00
|
|
|
s = [pathSeparator]
|
2010-10-15 20:09:30 +00:00
|
|
|
|
|
|
|
{- Constructs a relative path from the CWD to a directory.
|
|
|
|
-
|
|
|
|
- For example, assuming CWD is /tmp/foo/bar:
|
|
|
|
- relPathCwdToDir "/tmp/foo" == "../"
|
|
|
|
- relPathCwdToDir "/tmp/foo/bar" == ""
|
|
|
|
- relPathCwdToDir "/tmp/foo/bar" == ""
|
|
|
|
-}
|
|
|
|
relPathCwdToDir :: FilePath -> IO FilePath
|
|
|
|
relPathCwdToDir dir = do
|
|
|
|
cwd <- getCurrentDirectory
|
2010-10-31 20:00:32 +00:00
|
|
|
let absdir = absnorm cwd
|
2010-10-15 20:09:30 +00:00
|
|
|
return $ relPathDirToDir cwd absdir
|
|
|
|
where
|
|
|
|
-- absolute, normalized form of the directory
|
2010-10-31 20:00:32 +00:00
|
|
|
absnorm cwd =
|
2010-11-22 21:51:55 +00:00
|
|
|
case absNormPath cwd dir of
|
2010-10-15 20:09:30 +00:00
|
|
|
Just d -> d
|
|
|
|
Nothing -> error $ "unable to normalize " ++ dir
|
|
|
|
|
|
|
|
{- Constructs a relative path from one directory to another.
|
|
|
|
-
|
|
|
|
- Both directories must be absolute, and normalized (eg with absNormpath).
|
|
|
|
-
|
|
|
|
- The path will end with "/", unless it is empty.
|
2010-10-15 23:01:20 +00:00
|
|
|
-}
|
2010-10-15 20:09:30 +00:00
|
|
|
relPathDirToDir :: FilePath -> FilePath -> FilePath
|
|
|
|
relPathDirToDir from to =
|
2010-11-22 21:51:55 +00:00
|
|
|
if not $ null path
|
2010-10-15 23:01:20 +00:00
|
|
|
then addTrailingPathSeparator path
|
2010-10-15 20:09:30 +00:00
|
|
|
else ""
|
|
|
|
where
|
2010-10-15 23:01:20 +00:00
|
|
|
s = [pathSeparator]
|
|
|
|
pfrom = split s from
|
|
|
|
pto = split s to
|
2010-10-15 20:09:30 +00:00
|
|
|
common = map fst $ filter same $ zip pfrom pto
|
|
|
|
same (c,d) = c == d
|
|
|
|
uncommon = drop numcommon pto
|
2010-11-22 21:51:55 +00:00
|
|
|
dotdots = replicate (length pfrom - numcommon) ".."
|
|
|
|
numcommon = length common
|
2010-10-15 23:01:20 +00:00
|
|
|
path = join s $ dotdots ++ uncommon
|
2010-10-19 05:45:45 +00:00
|
|
|
|
|
|
|
{- Run a system command, and returns True or False
|
|
|
|
- if it succeeded or failed.
|
|
|
|
-
|
2010-10-29 17:57:22 +00:00
|
|
|
- SIGINT(ctrl-c) is allowed to propigate and will terminate the program.
|
2010-10-19 05:45:45 +00:00
|
|
|
-}
|
|
|
|
boolSystem :: FilePath -> [String] -> IO Bool
|
|
|
|
boolSystem command params = do
|
2010-10-29 17:57:22 +00:00
|
|
|
-- Going low-level because all the high-level system functions
|
|
|
|
-- block SIGINT etc. We need to block SIGCHLD, but allow
|
|
|
|
-- SIGINT to do its default program termination.
|
|
|
|
let sigset = addSignal sigCHLD emptySignalSet
|
|
|
|
oldint <- installHandler sigINT Default Nothing
|
|
|
|
oldset <- getSignalMask
|
|
|
|
blockSignals sigset
|
|
|
|
childpid <- forkProcess $ childaction oldint oldset
|
|
|
|
mps <- getProcessStatus True False childpid
|
|
|
|
restoresignals oldint oldset
|
|
|
|
case mps of
|
|
|
|
Just (Exited ExitSuccess) -> return True
|
|
|
|
_ -> return False
|
|
|
|
where
|
|
|
|
restoresignals oldint oldset = do
|
2010-10-31 20:00:32 +00:00
|
|
|
_ <- installHandler sigINT oldint Nothing
|
2010-10-29 17:57:22 +00:00
|
|
|
setSignalMask oldset
|
|
|
|
childaction oldint oldset = do
|
|
|
|
restoresignals oldint oldset
|
|
|
|
executeFile command True params Nothing
|
2010-10-22 18:57:02 +00:00
|
|
|
|
|
|
|
{- Escapes a filename to be safely able to be exposed to the shell. -}
|
2010-11-01 02:20:07 +00:00
|
|
|
shellEscape :: FilePath -> String
|
2010-11-01 02:22:01 +00:00
|
|
|
shellEscape f = "'" ++ escaped ++ "'"
|
2010-10-22 18:57:02 +00:00
|
|
|
where
|
|
|
|
-- replace ' with '"'"'
|
2010-11-01 02:22:01 +00:00
|
|
|
escaped = join "'\"'\"'" $ split "'" f
|
2010-11-08 21:44:08 +00:00
|
|
|
|
|
|
|
{- Removes a FileMode from a file.
|
|
|
|
- For example, call with otherWriteMode to chmod o-w -}
|
|
|
|
unsetFileMode :: FilePath -> FileMode -> IO ()
|
|
|
|
unsetFileMode f m = do
|
|
|
|
s <- getFileStatus f
|
2010-11-22 21:51:55 +00:00
|
|
|
setFileMode f $ fileMode s `intersectFileModes` complement m
|
2010-11-28 19:28:20 +00:00
|
|
|
|
|
|
|
{- Attempts to read a value from a String. -}
|
|
|
|
readMaybe :: (Read a) => String -> Maybe a
|
|
|
|
readMaybe s = case reads s of
|
|
|
|
((x,_):_) -> Just x
|
|
|
|
_ -> Nothing
|