got configure working after Utility.Path ByteString conversion
Had to split out some modules because getWorkingDirectory needs unix, which is not a build-dep of configure. This commit was sponsored by Brock Spratlen on Patreon.
This commit is contained in:
parent
e219aadbab
commit
d6e94a6b2e
10 changed files with 301 additions and 231 deletions
|
@ -39,7 +39,7 @@ fdoDesktopMenu command = genDesktopEntry
|
||||||
|
|
||||||
installIcon :: FilePath -> FilePath -> IO ()
|
installIcon :: FilePath -> FilePath -> IO ()
|
||||||
installIcon src dest = do
|
installIcon src dest = do
|
||||||
createDirectoryIfMissing True (parentDir dest)
|
createDirectoryIfMissing True (fromRawFilePath (parentDir (toRawFilePath dest)))
|
||||||
withBinaryFile src ReadMode $ \hin ->
|
withBinaryFile src ReadMode $ \hin ->
|
||||||
withBinaryFile dest WriteMode $ \hout ->
|
withBinaryFile dest WriteMode $ \hout ->
|
||||||
hGetContents hin >>= hPutStr hout
|
hGetContents hin >>= hPutStr hout
|
||||||
|
|
|
@ -16,6 +16,7 @@ import Utility.FreeDesktop
|
||||||
import Utility.Path
|
import Utility.Path
|
||||||
import Utility.Monad
|
import Utility.Monad
|
||||||
import Utility.Directory
|
import Utility.Directory
|
||||||
|
import Utility.FileSystemEncoding
|
||||||
import Config.Files
|
import Config.Files
|
||||||
import Utility.OSX
|
import Utility.OSX
|
||||||
import Assistant.Install.AutoStart
|
import Assistant.Install.AutoStart
|
||||||
|
@ -77,7 +78,7 @@ install command = do
|
||||||
( return ()
|
( return ()
|
||||||
, do
|
, do
|
||||||
programfile <- inDestDir =<< programFile
|
programfile <- inDestDir =<< programFile
|
||||||
createDirectoryIfMissing True (parentDir programfile)
|
createDirectoryIfMissing True (fromRawFilePath (parentDir (toRawFilePath programfile)))
|
||||||
writeFile programfile command
|
writeFile programfile command
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,40 +22,6 @@ userConfigFile file = do
|
||||||
autoStartFile :: IO FilePath
|
autoStartFile :: IO FilePath
|
||||||
autoStartFile = userConfigFile "autostart"
|
autoStartFile = userConfigFile "autostart"
|
||||||
|
|
||||||
{- Returns anything listed in the autostart file (which may not exist). -}
|
|
||||||
readAutoStartFile :: IO [FilePath]
|
|
||||||
readAutoStartFile = do
|
|
||||||
f <- autoStartFile
|
|
||||||
filter valid . nub . map dropTrailingPathSeparator . lines
|
|
||||||
<$> catchDefaultIO "" (readFile f)
|
|
||||||
where
|
|
||||||
-- Ignore any relative paths; some old buggy versions added eg "."
|
|
||||||
valid = isAbsolute
|
|
||||||
|
|
||||||
modifyAutoStartFile :: ([FilePath] -> [FilePath]) -> IO ()
|
|
||||||
modifyAutoStartFile func = do
|
|
||||||
dirs <- readAutoStartFile
|
|
||||||
let dirs' = nubBy equalFilePath $ func dirs
|
|
||||||
when (dirs' /= dirs) $ do
|
|
||||||
f <- autoStartFile
|
|
||||||
createDirectoryIfMissing True (parentDir f)
|
|
||||||
viaTmp writeFile f $ unlines dirs'
|
|
||||||
|
|
||||||
{- Adds a directory to the autostart file. If the directory is already
|
|
||||||
- present, it's moved to the top, so it will be used as the default
|
|
||||||
- when opening the webapp. -}
|
|
||||||
addAutoStartFile :: FilePath -> IO ()
|
|
||||||
addAutoStartFile path = do
|
|
||||||
path' <- absPath path
|
|
||||||
modifyAutoStartFile $ (:) path'
|
|
||||||
|
|
||||||
{- Removes a directory from the autostart file. -}
|
|
||||||
removeAutoStartFile :: FilePath -> IO ()
|
|
||||||
removeAutoStartFile path = do
|
|
||||||
path' <- absPath path
|
|
||||||
modifyAutoStartFile $
|
|
||||||
filter (not . equalFilePath path')
|
|
||||||
|
|
||||||
{- The path to git-annex is written here; which is useful when something
|
{- The path to git-annex is written here; which is useful when something
|
||||||
- has installed it to some awful non-PATH location. -}
|
- has installed it to some awful non-PATH location. -}
|
||||||
programFile :: IO FilePath
|
programFile :: IO FilePath
|
||||||
|
|
50
Config/Files/AutoStart.hs
Normal file
50
Config/Files/AutoStart.hs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{- git-annex autostart file
|
||||||
|
-
|
||||||
|
- Copyright 2012-2019 Joey Hess <id@joeyh.name>
|
||||||
|
-
|
||||||
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
|
module Config.Files.AutoStart where
|
||||||
|
|
||||||
|
import Common
|
||||||
|
import Config.Files
|
||||||
|
import Utility.Tmp
|
||||||
|
import Utility.FreeDesktop
|
||||||
|
import Utility.Directory.AbsRel
|
||||||
|
|
||||||
|
{- Returns anything listed in the autostart file (which may not exist). -}
|
||||||
|
readAutoStartFile :: IO [FilePath]
|
||||||
|
readAutoStartFile = do
|
||||||
|
f <- autoStartFile
|
||||||
|
filter valid . nub . map dropTrailingPathSeparator . lines
|
||||||
|
<$> catchDefaultIO "" (readFile f)
|
||||||
|
where
|
||||||
|
-- Ignore any relative paths; some old buggy versions added eg "."
|
||||||
|
valid = isAbsolute
|
||||||
|
|
||||||
|
modifyAutoStartFile :: ([FilePath] -> [FilePath]) -> IO ()
|
||||||
|
modifyAutoStartFile func = do
|
||||||
|
dirs <- readAutoStartFile
|
||||||
|
let dirs' = nubBy equalFilePath $ func dirs
|
||||||
|
when (dirs' /= dirs) $ do
|
||||||
|
f <- autoStartFile
|
||||||
|
createDirectoryIfMissing True (parentDir f)
|
||||||
|
viaTmp writeFile f $ unlines dirs'
|
||||||
|
|
||||||
|
{- Adds a directory to the autostart file. If the directory is already
|
||||||
|
- present, it's moved to the top, so it will be used as the default
|
||||||
|
- when opening the webapp. -}
|
||||||
|
addAutoStartFile :: FilePath -> IO ()
|
||||||
|
addAutoStartFile path = do
|
||||||
|
path' <- absPath path
|
||||||
|
modifyAutoStartFile $ (:) path'
|
||||||
|
|
||||||
|
{- Removes a directory from the autostart file. -}
|
||||||
|
removeAutoStartFile :: FilePath -> IO ()
|
||||||
|
removeAutoStartFile path = do
|
||||||
|
path' <- absPath path
|
||||||
|
modifyAutoStartFile $
|
||||||
|
filter (not . equalFilePath path')
|
4
Test.hs
4
Test.hs
|
@ -188,8 +188,8 @@ properties = localOption (QuickCheckTests 1000) $ testGroup "QuickCheck" $
|
||||||
, testProperty "prop_isomorphic_configEscape" Logs.Remote.prop_isomorphic_configEscape
|
, testProperty "prop_isomorphic_configEscape" Logs.Remote.prop_isomorphic_configEscape
|
||||||
, testProperty "prop_parse_show_Config" Logs.Remote.prop_parse_show_Config
|
, testProperty "prop_parse_show_Config" Logs.Remote.prop_parse_show_Config
|
||||||
, testProperty "prop_upFrom_basics" Utility.Path.prop_upFrom_basics
|
, testProperty "prop_upFrom_basics" Utility.Path.prop_upFrom_basics
|
||||||
, testProperty "prop_relPathDirToFile_basics" Utility.Path.prop_relPathDirToFile_basics
|
, testProperty "prop_relPathDirToFileAbs_basics" Utility.Path.prop_relPathDirToFileAbs_basics
|
||||||
, testProperty "prop_relPathDirToFile_regressionTest" Utility.Path.prop_relPathDirToFile_regressionTest
|
, testProperty "prop_relPathDirToFileAbs_regressionTest" Utility.Path.prop_relPathDirToFileAbs_regressionTest
|
||||||
, testProperty "prop_cost_sane" Config.Cost.prop_cost_sane
|
, testProperty "prop_cost_sane" Config.Cost.prop_cost_sane
|
||||||
, testProperty "prop_matcher_sane" Utility.Matcher.prop_matcher_sane
|
, testProperty "prop_matcher_sane" Utility.Matcher.prop_matcher_sane
|
||||||
, testProperty "prop_HmacSha1WithCipher_sane" Crypto.prop_HmacSha1WithCipher_sane
|
, testProperty "prop_HmacSha1WithCipher_sane" Crypto.prop_HmacSha1WithCipher_sane
|
||||||
|
|
|
@ -18,7 +18,6 @@ import Control.Monad
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import System.PosixCompat.Files
|
import System.PosixCompat.Files
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Control.Monad.IfElse
|
import Control.Monad.IfElse
|
||||||
import System.IO.Unsafe (unsafeInterleaveIO)
|
import System.IO.Unsafe (unsafeInterleaveIO)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
|
@ -30,12 +29,10 @@ import Utility.SafeCommand
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
import Utility.SystemDirectory
|
import Utility.SystemDirectory
|
||||||
import Utility.Path
|
|
||||||
import Utility.Tmp
|
import Utility.Tmp
|
||||||
import Utility.Exception
|
import Utility.Exception
|
||||||
import Utility.Monad
|
import Utility.Monad
|
||||||
import Utility.Applicative
|
import Utility.Applicative
|
||||||
import Utility.PartialPrelude
|
|
||||||
|
|
||||||
dirCruft :: FilePath -> Bool
|
dirCruft :: FilePath -> Bool
|
||||||
dirCruft "." = True
|
dirCruft "." = True
|
||||||
|
@ -157,74 +154,3 @@ nukeFile file = void $ tryWhenExists go
|
||||||
#else
|
#else
|
||||||
go = removeFile file
|
go = removeFile file
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{- Like createDirectoryIfMissing True, but it will only create
|
|
||||||
- missing parent directories up to but not including the directory
|
|
||||||
- in the first parameter.
|
|
||||||
-
|
|
||||||
- For example, createDirectoryUnder "/tmp/foo" "/tmp/foo/bar/baz"
|
|
||||||
- will create /tmp/foo/bar if necessary, but if /tmp/foo does not exist,
|
|
||||||
- it will throw an exception.
|
|
||||||
-
|
|
||||||
- The exception thrown is the same that createDirectory throws if the
|
|
||||||
- parent directory does not exist.
|
|
||||||
-
|
|
||||||
- If the second FilePath is not under the first
|
|
||||||
- FilePath (or the same as it), it will fail with an exception
|
|
||||||
- even if the second FilePath's parent directory already exists.
|
|
||||||
-
|
|
||||||
- Either or both of the FilePaths can be relative, or absolute.
|
|
||||||
- They will be normalized as necessary.
|
|
||||||
-
|
|
||||||
- Note that, the second FilePath, if relative, is relative to the current
|
|
||||||
- working directory, not to the first FilePath.
|
|
||||||
-}
|
|
||||||
createDirectoryUnder :: FilePath -> FilePath -> IO ()
|
|
||||||
createDirectoryUnder topdir dir =
|
|
||||||
createDirectoryUnder' topdir dir createDirectory
|
|
||||||
|
|
||||||
createDirectoryUnder'
|
|
||||||
:: (MonadIO m, MonadCatch m)
|
|
||||||
=> FilePath
|
|
||||||
-> FilePath
|
|
||||||
-> (FilePath -> m ())
|
|
||||||
-> m ()
|
|
||||||
createDirectoryUnder' topdir dir0 mkdir = do
|
|
||||||
p <- liftIO $ relPathDirToFile topdir dir0
|
|
||||||
let dirs = splitDirectories p
|
|
||||||
-- Catch cases where the dir is not beneath the topdir.
|
|
||||||
-- If the relative path between them starts with "..",
|
|
||||||
-- it's not. And on Windows, if they are on different drives,
|
|
||||||
-- the path will not be relative.
|
|
||||||
if headMaybe dirs == Just ".." || isAbsolute p
|
|
||||||
then liftIO $ ioError $ customerror userErrorType
|
|
||||||
("createDirectoryFrom: not located in " ++ topdir)
|
|
||||||
-- If dir0 is the same as the topdir, don't try to create
|
|
||||||
-- it, but make sure it does exist.
|
|
||||||
else if null dirs
|
|
||||||
then liftIO $ unlessM (doesDirectoryExist topdir) $
|
|
||||||
ioError $ customerror doesNotExistErrorType
|
|
||||||
"createDirectoryFrom: does not exist"
|
|
||||||
else createdirs $
|
|
||||||
map (topdir </>) (reverse (scanl1 (</>) dirs))
|
|
||||||
where
|
|
||||||
customerror t s = mkIOError t s Nothing (Just dir0)
|
|
||||||
|
|
||||||
createdirs [] = pure ()
|
|
||||||
createdirs (dir:[]) = createdir dir (liftIO . ioError)
|
|
||||||
createdirs (dir:dirs) = createdir dir $ \_ -> do
|
|
||||||
createdirs dirs
|
|
||||||
createdir dir (liftIO . ioError)
|
|
||||||
|
|
||||||
-- This is the same method used by createDirectoryIfMissing,
|
|
||||||
-- in particular the handling of errors that occur when the
|
|
||||||
-- directory already exists. See its source for explanation
|
|
||||||
-- of several subtleties.
|
|
||||||
createdir dir notexisthandler = tryIO (mkdir dir) >>= \case
|
|
||||||
Right () -> pure ()
|
|
||||||
Left e
|
|
||||||
| isDoesNotExistError e -> notexisthandler e
|
|
||||||
| isAlreadyExistsError e || isPermissionError e ->
|
|
||||||
liftIO $ unlessM (doesDirectoryExist dir) $
|
|
||||||
ioError e
|
|
||||||
| otherwise -> liftIO $ ioError e
|
|
||||||
|
|
102
Utility/Directory/Create.hs
Normal file
102
Utility/Directory/Create.hs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
{- directory creating
|
||||||
|
-
|
||||||
|
- Copyright 2011-2020 Joey Hess <id@joeyh.name>
|
||||||
|
-
|
||||||
|
- License: BSD-2-clause
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
|
module Utility.Directory.Create (
|
||||||
|
createDirectoryUnder,
|
||||||
|
createDirectoryUnder',
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import System.FilePath
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.IfElse
|
||||||
|
import System.IO.Error
|
||||||
|
import Data.Maybe
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Utility.SystemDirectory
|
||||||
|
import Utility.Path.AbsRel
|
||||||
|
import Utility.Exception
|
||||||
|
import Utility.FileSystemEncoding
|
||||||
|
import Utility.PartialPrelude
|
||||||
|
|
||||||
|
{- Like createDirectoryIfMissing True, but it will only create
|
||||||
|
- missing parent directories up to but not including the directory
|
||||||
|
- in the first parameter.
|
||||||
|
-
|
||||||
|
- For example, createDirectoryUnder "/tmp/foo" "/tmp/foo/bar/baz"
|
||||||
|
- will create /tmp/foo/bar if necessary, but if /tmp/foo does not exist,
|
||||||
|
- it will throw an exception.
|
||||||
|
-
|
||||||
|
- The exception thrown is the same that createDirectory throws if the
|
||||||
|
- parent directory does not exist.
|
||||||
|
-
|
||||||
|
- If the second FilePath is not under the first
|
||||||
|
- FilePath (or the same as it), it will fail with an exception
|
||||||
|
- even if the second FilePath's parent directory already exists.
|
||||||
|
-
|
||||||
|
- Either or both of the FilePaths can be relative, or absolute.
|
||||||
|
- They will be normalized as necessary.
|
||||||
|
-
|
||||||
|
- Note that, the second FilePath, if relative, is relative to the current
|
||||||
|
- working directory, not to the first FilePath.
|
||||||
|
-}
|
||||||
|
createDirectoryUnder :: FilePath -> FilePath -> IO ()
|
||||||
|
createDirectoryUnder topdir dir =
|
||||||
|
createDirectoryUnder' topdir dir createDirectory
|
||||||
|
|
||||||
|
createDirectoryUnder'
|
||||||
|
:: (MonadIO m, MonadCatch m)
|
||||||
|
=> FilePath
|
||||||
|
-> FilePath
|
||||||
|
-> (FilePath -> m ())
|
||||||
|
-> m ()
|
||||||
|
createDirectoryUnder' topdir dir0 mkdir = do
|
||||||
|
p <- liftIO $ fromRawFilePath <$> relPathDirToFile
|
||||||
|
(toRawFilePath topdir)
|
||||||
|
(toRawFilePath dir0)
|
||||||
|
let dirs = splitDirectories p
|
||||||
|
-- Catch cases where the dir is not beneath the topdir.
|
||||||
|
-- If the relative path between them starts with "..",
|
||||||
|
-- it's not. And on Windows, if they are on different drives,
|
||||||
|
-- the path will not be relative.
|
||||||
|
if headMaybe dirs == Just ".." || isAbsolute p
|
||||||
|
then liftIO $ ioError $ customerror userErrorType
|
||||||
|
("createDirectoryFrom: not located in " ++ topdir)
|
||||||
|
-- If dir0 is the same as the topdir, don't try to create
|
||||||
|
-- it, but make sure it does exist.
|
||||||
|
else if null dirs
|
||||||
|
then liftIO $ unlessM (doesDirectoryExist topdir) $
|
||||||
|
ioError $ customerror doesNotExistErrorType
|
||||||
|
"createDirectoryFrom: does not exist"
|
||||||
|
else createdirs $
|
||||||
|
map (topdir </>) (reverse (scanl1 (</>) dirs))
|
||||||
|
where
|
||||||
|
customerror t s = mkIOError t s Nothing (Just dir0)
|
||||||
|
|
||||||
|
createdirs [] = pure ()
|
||||||
|
createdirs (dir:[]) = createdir dir (liftIO . ioError)
|
||||||
|
createdirs (dir:dirs) = createdir dir $ \_ -> do
|
||||||
|
createdirs dirs
|
||||||
|
createdir dir (liftIO . ioError)
|
||||||
|
|
||||||
|
-- This is the same method used by createDirectoryIfMissing,
|
||||||
|
-- in particular the handling of errors that occur when the
|
||||||
|
-- directory already exists. See its source for explanation
|
||||||
|
-- of several subtleties.
|
||||||
|
createdir dir notexisthandler = tryIO (mkdir dir) >>= \case
|
||||||
|
Right () -> pure ()
|
||||||
|
Left e
|
||||||
|
| isDoesNotExistError e -> notexisthandler e
|
||||||
|
| isAlreadyExistsError e || isPermissionError e ->
|
||||||
|
liftIO $ unlessM (doesDirectoryExist dir) $
|
||||||
|
ioError e
|
||||||
|
| otherwise -> liftIO $ ioError e
|
167
Utility/Path.hs
167
Utility/Path.hs
|
@ -11,27 +11,22 @@
|
||||||
|
|
||||||
module Utility.Path (
|
module Utility.Path (
|
||||||
simplifyPath,
|
simplifyPath,
|
||||||
absPathFrom,
|
|
||||||
parentDir,
|
parentDir,
|
||||||
upFrom,
|
upFrom,
|
||||||
dirContains,
|
dirContains,
|
||||||
absPath,
|
|
||||||
relPathCwdToFile,
|
|
||||||
relPathDirToFile,
|
|
||||||
relPathDirToFileAbs,
|
|
||||||
segmentPaths,
|
segmentPaths,
|
||||||
segmentPaths',
|
segmentPaths',
|
||||||
runSegmentPaths,
|
runSegmentPaths,
|
||||||
runSegmentPaths',
|
runSegmentPaths',
|
||||||
relHome,
|
|
||||||
inPath,
|
inPath,
|
||||||
searchPath,
|
searchPath,
|
||||||
dotfile,
|
dotfile,
|
||||||
splitShortExtensions,
|
splitShortExtensions,
|
||||||
|
relPathDirToFileAbs,
|
||||||
|
|
||||||
prop_upFrom_basics,
|
prop_upFrom_basics,
|
||||||
prop_relPathDirToFile_basics,
|
prop_relPathDirToFileAbs_basics,
|
||||||
prop_relPathDirToFile_regressionTest,
|
prop_relPathDirToFileAbs_regressionTest,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import System.FilePath.ByteString
|
import System.FilePath.ByteString
|
||||||
|
@ -39,11 +34,6 @@ import qualified System.FilePath as P
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
import Data.Char
|
|
||||||
#else
|
|
||||||
import System.Posix.Directory.ByteString (getWorkingDirectory)
|
|
||||||
#endif
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
|
@ -80,19 +70,6 @@ simplifyPath path = dropTrailingPathSeparator $
|
||||||
where
|
where
|
||||||
p' = dropTrailingPathSeparator p
|
p' = dropTrailingPathSeparator p
|
||||||
|
|
||||||
{- Makes a path absolute.
|
|
||||||
-
|
|
||||||
- Also simplifies it using simplifyPath.
|
|
||||||
-
|
|
||||||
- The first parameter is a base directory (ie, the cwd) to use if the path
|
|
||||||
- is not already absolute, and should itsef be absolute.
|
|
||||||
-
|
|
||||||
- Does not attempt to deal with edge cases or ensure security with
|
|
||||||
- untrusted inputs.
|
|
||||||
-}
|
|
||||||
absPathFrom :: RawFilePath -> RawFilePath -> RawFilePath
|
|
||||||
absPathFrom dir path = simplifyPath (combine dir path)
|
|
||||||
|
|
||||||
{- takeDirectory "foo/bar/" is "foo/bar". This instead yields "foo" -}
|
{- takeDirectory "foo/bar/" is "foo/bar". This instead yields "foo" -}
|
||||||
parentDir :: RawFilePath -> RawFilePath
|
parentDir :: RawFilePath -> RawFilePath
|
||||||
parentDir = takeDirectory . dropTrailingPathSeparator
|
parentDir = takeDirectory . dropTrailingPathSeparator
|
||||||
|
@ -132,90 +109,6 @@ dirContains a b = a == b
|
||||||
b' = norm b
|
b' = norm b
|
||||||
norm = normalise . simplifyPath
|
norm = normalise . simplifyPath
|
||||||
|
|
||||||
{- Converts a filename into an absolute path.
|
|
||||||
-
|
|
||||||
- Also simplifies it using simplifyPath.
|
|
||||||
-
|
|
||||||
- Unlike Directory.canonicalizePath, this does not require the path
|
|
||||||
- already exists. -}
|
|
||||||
absPath :: RawFilePath -> IO RawFilePath
|
|
||||||
absPath file
|
|
||||||
-- Avoid unncessarily getting the current directory when the path
|
|
||||||
-- is already absolute. absPathFrom uses simplifyPath
|
|
||||||
-- so also used here for consistency.
|
|
||||||
| isAbsolute file = return $ simplifyPath file
|
|
||||||
| otherwise = do
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
cwd <- toRawFilePath <$> getCurrentDirectory
|
|
||||||
#else
|
|
||||||
cwd <- getWorkingDirectory
|
|
||||||
#endif
|
|
||||||
return $ absPathFrom cwd file
|
|
||||||
|
|
||||||
{- Constructs a relative path from the CWD to a file.
|
|
||||||
-
|
|
||||||
- For example, assuming CWD is /tmp/foo/bar:
|
|
||||||
- relPathCwdToFile "/tmp/foo" == ".."
|
|
||||||
- relPathCwdToFile "/tmp/foo/bar" == ""
|
|
||||||
-}
|
|
||||||
relPathCwdToFile :: RawFilePath -> IO RawFilePath
|
|
||||||
relPathCwdToFile f = do
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
c <- toRawFilePath <$> getCurrentDirectory
|
|
||||||
#else
|
|
||||||
c <- getWorkingDirectory
|
|
||||||
#endif
|
|
||||||
relPathDirToFile c f
|
|
||||||
|
|
||||||
{- Constructs a relative path from a directory to a file. -}
|
|
||||||
relPathDirToFile :: RawFilePath -> RawFilePath -> IO RawFilePath
|
|
||||||
relPathDirToFile from to = relPathDirToFileAbs <$> absPath from <*> absPath to
|
|
||||||
|
|
||||||
{- This requires the first path to be absolute, and the
|
|
||||||
- second path cannot contain ../ or ./
|
|
||||||
-
|
|
||||||
- On Windows, if the paths are on different drives,
|
|
||||||
- a relative path is not possible and the path is simply
|
|
||||||
- returned as-is.
|
|
||||||
-}
|
|
||||||
relPathDirToFileAbs :: RawFilePath -> RawFilePath -> RawFilePath
|
|
||||||
relPathDirToFileAbs from to
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
| normdrive from /= normdrive to = to
|
|
||||||
#endif
|
|
||||||
| otherwise = joinPath $ dotdots ++ uncommon
|
|
||||||
where
|
|
||||||
pfrom = sp from
|
|
||||||
pto = sp to
|
|
||||||
sp = map dropTrailingPathSeparator . splitPath . dropDrive
|
|
||||||
common = map fst $ takeWhile same $ zip pfrom pto
|
|
||||||
same (c,d) = c == d
|
|
||||||
uncommon = drop numcommon pto
|
|
||||||
dotdots = replicate (length pfrom - numcommon) ".."
|
|
||||||
numcommon = length common
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
normdrive = map toLower . takeWhile (/= ':') . fromRawFilePath . takeDrive
|
|
||||||
#endif
|
|
||||||
|
|
||||||
prop_relPathDirToFile_basics :: RawFilePath -> RawFilePath -> Bool
|
|
||||||
prop_relPathDirToFile_basics from to
|
|
||||||
| B.null from || B.null to = True
|
|
||||||
| from == to = B.null r
|
|
||||||
| otherwise = not (B.null r)
|
|
||||||
where
|
|
||||||
r = relPathDirToFileAbs from to
|
|
||||||
|
|
||||||
prop_relPathDirToFile_regressionTest :: Bool
|
|
||||||
prop_relPathDirToFile_regressionTest = same_dir_shortcurcuits_at_difference
|
|
||||||
where
|
|
||||||
{- Two paths have the same directory component at the same
|
|
||||||
- location, but it's not really the same directory.
|
|
||||||
- Code used to get this wrong. -}
|
|
||||||
same_dir_shortcurcuits_at_difference =
|
|
||||||
relPathDirToFileAbs (joinPath [pathSeparator `B.cons` "tmp", "r", "lll", "xxx", "yyy", "18"])
|
|
||||||
(joinPath [pathSeparator `B.cons` "tmp", "r", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"])
|
|
||||||
== joinPath ["..", "..", "..", "..", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"]
|
|
||||||
|
|
||||||
{- Given an original list of paths, and an expanded list derived from it,
|
{- Given an original list of paths, and an expanded list derived from it,
|
||||||
- which may be arbitrarily reordered, generates a list of lists, where
|
- which may be arbitrarily reordered, generates a list of lists, where
|
||||||
- each sublist corresponds to one of the original paths.
|
- each sublist corresponds to one of the original paths.
|
||||||
|
@ -253,15 +146,6 @@ runSegmentPaths c a paths = segmentPaths c paths <$> a paths
|
||||||
runSegmentPaths' :: (Maybe RawFilePath -> a -> r) -> (a -> RawFilePath) -> ([RawFilePath] -> IO [a]) -> [RawFilePath] -> IO [[r]]
|
runSegmentPaths' :: (Maybe RawFilePath -> a -> r) -> (a -> RawFilePath) -> ([RawFilePath] -> IO [a]) -> [RawFilePath] -> IO [[r]]
|
||||||
runSegmentPaths' si c a paths = segmentPaths' si c paths <$> a paths
|
runSegmentPaths' si c a paths = segmentPaths' si c paths <$> a paths
|
||||||
|
|
||||||
{- Converts paths in the home directory to use ~/ -}
|
|
||||||
relHome :: FilePath -> IO String
|
|
||||||
relHome path = do
|
|
||||||
let path' = toRawFilePath path
|
|
||||||
home <- toRawFilePath <$> myHomeDir
|
|
||||||
return $ if dirContains home path'
|
|
||||||
then fromRawFilePath ("~/" <> relPathDirToFileAbs home path')
|
|
||||||
else path
|
|
||||||
|
|
||||||
{- Checks if a command is available in PATH.
|
{- Checks if a command is available in PATH.
|
||||||
-
|
-
|
||||||
- The command may be fully-qualified, in which case, this succeeds as
|
- The command may be fully-qualified, in which case, this succeeds as
|
||||||
|
@ -314,3 +198,48 @@ splitShortExtensions' maxextension = go []
|
||||||
where
|
where
|
||||||
(base, ext) = splitExtension f
|
(base, ext) = splitExtension f
|
||||||
len = B.length ext
|
len = B.length ext
|
||||||
|
|
||||||
|
{- This requires the first path to be absolute, and the
|
||||||
|
- second path cannot contain ../ or ./
|
||||||
|
-
|
||||||
|
- On Windows, if the paths are on different drives,
|
||||||
|
- a relative path is not possible and the path is simply
|
||||||
|
- returned as-is.
|
||||||
|
-}
|
||||||
|
relPathDirToFileAbs :: RawFilePath -> RawFilePath -> RawFilePath
|
||||||
|
relPathDirToFileAbs from to
|
||||||
|
#ifdef mingw32_HOST_OS
|
||||||
|
| normdrive from /= normdrive to = to
|
||||||
|
#endif
|
||||||
|
| otherwise = joinPath $ dotdots ++ uncommon
|
||||||
|
where
|
||||||
|
pfrom = sp from
|
||||||
|
pto = sp to
|
||||||
|
sp = map dropTrailingPathSeparator . splitPath . dropDrive
|
||||||
|
common = map fst $ takeWhile same $ zip pfrom pto
|
||||||
|
same (c,d) = c == d
|
||||||
|
uncommon = drop numcommon pto
|
||||||
|
dotdots = replicate (length pfrom - numcommon) ".."
|
||||||
|
numcommon = length common
|
||||||
|
#ifdef mingw32_HOST_OS
|
||||||
|
normdrive = map toLower . takeWhile (/= ':') . fromRawFilePath . takeDrive
|
||||||
|
#endif
|
||||||
|
|
||||||
|
prop_relPathDirToFileAbs_basics :: RawFilePath -> RawFilePath -> Bool
|
||||||
|
prop_relPathDirToFileAbs_basics from to
|
||||||
|
| B.null from || B.null to = True
|
||||||
|
| from == to = B.null r
|
||||||
|
| otherwise = not (B.null r)
|
||||||
|
where
|
||||||
|
r = relPathDirToFileAbs from to
|
||||||
|
|
||||||
|
prop_relPathDirToFileAbs_regressionTest :: Bool
|
||||||
|
prop_relPathDirToFileAbs_regressionTest = same_dir_shortcurcuits_at_difference
|
||||||
|
where
|
||||||
|
{- Two paths have the same directory component at the same
|
||||||
|
- location, but it's not really the same directory.
|
||||||
|
- Code used to get this wrong. -}
|
||||||
|
same_dir_shortcurcuits_at_difference =
|
||||||
|
relPathDirToFileAbs (joinPath [pathSeparator `B.cons` "tmp", "r", "lll", "xxx", "yyy", "18"])
|
||||||
|
(joinPath [pathSeparator `B.cons` "tmp", "r", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"])
|
||||||
|
== joinPath ["..", "..", "..", "..", ".git", "annex", "objects", "18", "gk", "SHA256-foo", "SHA256-foo"]
|
||||||
|
|
93
Utility/Path/AbsRel.hs
Normal file
93
Utility/Path/AbsRel.hs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{- absolute and relative path manipulation
|
||||||
|
-
|
||||||
|
- Copyright 2010-2020 Joey Hess <id@joeyh.name>
|
||||||
|
-
|
||||||
|
- License: BSD-2-clause
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
||||||
|
|
||||||
|
module Utility.Path.AbsRel (
|
||||||
|
absPathFrom,
|
||||||
|
absPath,
|
||||||
|
relPathCwdToFile,
|
||||||
|
relPathDirToFile,
|
||||||
|
relPathDirToFileAbs,
|
||||||
|
relHome,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import System.FilePath.ByteString
|
||||||
|
#ifdef mingw32_HOST_OS
|
||||||
|
import System.Directory (getCurrentDirectory)
|
||||||
|
#else
|
||||||
|
import System.Posix.Directory.ByteString (getWorkingDirectory)
|
||||||
|
#endif
|
||||||
|
import Control.Applicative
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Utility.Path
|
||||||
|
import Utility.UserInfo
|
||||||
|
import Utility.FileSystemEncoding
|
||||||
|
|
||||||
|
{- Makes a path absolute.
|
||||||
|
-
|
||||||
|
- Also simplifies it using simplifyPath.
|
||||||
|
-
|
||||||
|
- The first parameter is a base directory (ie, the cwd) to use if the path
|
||||||
|
- is not already absolute, and should itsef be absolute.
|
||||||
|
-
|
||||||
|
- Does not attempt to deal with edge cases or ensure security with
|
||||||
|
- untrusted inputs.
|
||||||
|
-}
|
||||||
|
absPathFrom :: RawFilePath -> RawFilePath -> RawFilePath
|
||||||
|
absPathFrom dir path = simplifyPath (combine dir path)
|
||||||
|
|
||||||
|
{- Converts a filename into an absolute path.
|
||||||
|
-
|
||||||
|
- Also simplifies it using simplifyPath.
|
||||||
|
-
|
||||||
|
- Unlike Directory.canonicalizePath, this does not require the path
|
||||||
|
- already exists. -}
|
||||||
|
absPath :: RawFilePath -> IO RawFilePath
|
||||||
|
absPath file
|
||||||
|
-- Avoid unncessarily getting the current directory when the path
|
||||||
|
-- is already absolute. absPathFrom uses simplifyPath
|
||||||
|
-- so also used here for consistency.
|
||||||
|
| isAbsolute file = return $ simplifyPath file
|
||||||
|
| otherwise = do
|
||||||
|
#ifdef mingw32_HOST_OS
|
||||||
|
cwd <- toRawFilePath <$> getCurrentDirectory
|
||||||
|
#else
|
||||||
|
cwd <- getWorkingDirectory
|
||||||
|
#endif
|
||||||
|
return $ absPathFrom cwd file
|
||||||
|
|
||||||
|
{- Constructs a relative path from the CWD to a file.
|
||||||
|
-
|
||||||
|
- For example, assuming CWD is /tmp/foo/bar:
|
||||||
|
- relPathCwdToFile "/tmp/foo" == ".."
|
||||||
|
- relPathCwdToFile "/tmp/foo/bar" == ""
|
||||||
|
-}
|
||||||
|
relPathCwdToFile :: RawFilePath -> IO RawFilePath
|
||||||
|
relPathCwdToFile f = do
|
||||||
|
#ifdef mingw32_HOST_OS
|
||||||
|
c <- toRawFilePath <$> getCurrentDirectory
|
||||||
|
#else
|
||||||
|
c <- getWorkingDirectory
|
||||||
|
#endif
|
||||||
|
relPathDirToFile c f
|
||||||
|
|
||||||
|
{- Constructs a relative path from a directory to a file. -}
|
||||||
|
relPathDirToFile :: RawFilePath -> RawFilePath -> IO RawFilePath
|
||||||
|
relPathDirToFile from to = relPathDirToFileAbs <$> absPath from <*> absPath to
|
||||||
|
|
||||||
|
{- Converts paths in the home directory to use ~/ -}
|
||||||
|
relHome :: FilePath -> IO String
|
||||||
|
relHome path = do
|
||||||
|
let path' = toRawFilePath path
|
||||||
|
home <- toRawFilePath <$> myHomeDir
|
||||||
|
return $ if dirContains home path'
|
||||||
|
then fromRawFilePath ("~/" <> relPathDirToFileAbs home path')
|
||||||
|
else path
|
|
@ -817,6 +817,7 @@ Executable git-annex
|
||||||
Config
|
Config
|
||||||
Config.Cost
|
Config.Cost
|
||||||
Config.Files
|
Config.Files
|
||||||
|
Config.Files.AutoStart
|
||||||
Config.DynamicConfig
|
Config.DynamicConfig
|
||||||
Config.GitConfig
|
Config.GitConfig
|
||||||
Config.Smudge
|
Config.Smudge
|
||||||
|
@ -1055,6 +1056,7 @@ Executable git-annex
|
||||||
Utility.DirWatcher
|
Utility.DirWatcher
|
||||||
Utility.DirWatcher.Types
|
Utility.DirWatcher.Types
|
||||||
Utility.Directory
|
Utility.Directory
|
||||||
|
Utility.Directory.Create
|
||||||
Utility.Directory.Stream
|
Utility.Directory.Stream
|
||||||
Utility.DiskFree
|
Utility.DiskFree
|
||||||
Utility.Dot
|
Utility.Dot
|
||||||
|
@ -1094,6 +1096,7 @@ Executable git-annex
|
||||||
Utility.PID
|
Utility.PID
|
||||||
Utility.PartialPrelude
|
Utility.PartialPrelude
|
||||||
Utility.Path
|
Utility.Path
|
||||||
|
Utility.Path.AbsRel
|
||||||
Utility.Path.Max
|
Utility.Path.Max
|
||||||
Utility.Percentage
|
Utility.Percentage
|
||||||
Utility.Process
|
Utility.Process
|
||||||
|
|
Loading…
Reference in a new issue