diff --git a/System/Directory.hs b/System/Directory.hs index fb403913a2298f30aa17c1fdbcd0e13895c9218d..be986328bb1860a9050455021c7862f16f84d169 100644 --- a/System/Directory.hs +++ b/System/Directory.hs @@ -416,7 +416,7 @@ data DirectoryType = NotDirectory -- | Obtain the type of a directory. getDirectoryType :: FilePath -> IO DirectoryType getDirectoryType path = - (`ioeSetLocation` "getDirectoryType") `modifyIOError` do + (`ioeAddLocation` "getDirectoryType") `modifyIOError` do #ifdef mingw32_HOST_OS isDir <- withFileStatus "getDirectoryType" path isDirectory if isDir @@ -490,7 +490,7 @@ removeDirectory path = -- On Windows, the operation fails if /dir/ is a directory symbolic link. removeDirectoryRecursive :: FilePath -> IO () removeDirectoryRecursive path = - (`ioeSetLocation` "removeDirectoryRecursive") `modifyIOError` do + (`ioeAddLocation` "removeDirectoryRecursive") `modifyIOError` do dirType <- getDirectoryType path case dirType of Directory -> @@ -506,7 +506,7 @@ removeDirectoryRecursive path = -- removed without affecting their the targets. removePathRecursive :: FilePath -> IO () removePathRecursive path = - (`ioeSetLocation` "removePathRecursive") `modifyIOError` do + (`ioeAddLocation` "removePathRecursive") `modifyIOError` do dirType <- getDirectoryType path case dirType of NotDirectory -> removeFile path @@ -518,7 +518,7 @@ removePathRecursive path = -- targets. removeContentsRecursive :: FilePath -> IO () removeContentsRecursive path = - (`ioeSetLocation` "removeContentsRecursive") `modifyIOError` do + (`ioeAddLocation` "removeContentsRecursive") `modifyIOError` do cont <- listDirectory path mapM_ removePathRecursive [path </> x | x <- cont] removeDirectory path @@ -542,7 +542,7 @@ removeContentsRecursive path = -- @since 1.2.7.0 removePathForcibly :: FilePath -> IO () removePathForcibly path = - (`ioeSetLocation` "removePathForcibly") `modifyIOError` do + (`ioeAddLocation` "removePathForcibly") `modifyIOError` do makeRemovable path `catchIOError` \ _ -> return () ignoreDoesNotExistError $ do dirType <- getDirectoryType path @@ -738,7 +738,7 @@ Either path refers to an existing directory. -} renameFile :: FilePath -> FilePath -> IO () -renameFile opath npath = (`ioeSetLocation` "renameFile") `modifyIOError` do +renameFile opath npath = (`ioeAddLocation` "renameFile") `modifyIOError` do -- XXX the tests are not performed atomically with the rename checkNotDir opath renamePath opath npath @@ -806,7 +806,7 @@ renameFile opath npath = (`ioeSetLocation` "renameFile") `modifyIOError` do renamePath :: FilePath -- ^ Old path -> FilePath -- ^ New path -> IO () -renamePath opath npath = (`ioeSetLocation` "renamePath") `modifyIOError` do +renamePath opath npath = (`ioeAddLocation` "renamePath") `modifyIOError` do #ifdef mingw32_HOST_OS Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING #else @@ -821,7 +821,7 @@ copyFile :: FilePath -- ^ Source filename -> FilePath -- ^ Destination filename -> IO () copyFile fromFPath toFPath = - (`ioeSetLocation` "copyFile") `modifyIOError` do + (`ioeAddLocation` "copyFile") `modifyIOError` do atomicCopyFileContents fromFPath toFPath (ignoreIOExceptions . copyPermissions fromFPath) @@ -834,7 +834,7 @@ copyFileContents :: FilePath -- ^ Source filename -> FilePath -- ^ Destination filename -> IO () copyFileContents fromFPath toFPath = - (`ioeSetLocation` "copyFileContents") `modifyIOError` do + (`ioeAddLocation` "copyFileContents") `modifyIOError` do withBinaryFile toFPath WriteMode $ \ hTo -> copyFileToHandle fromFPath hTo #endif @@ -847,7 +847,7 @@ atomicCopyFileContents :: FilePath -- ^ Source filename -> (FilePath -> IO ()) -- ^ Post-action -> IO () atomicCopyFileContents fromFPath toFPath postAction = - (`ioeSetLocation` "atomicCopyFileContents") `modifyIOError` do + (`ioeAddLocation` "atomicCopyFileContents") `modifyIOError` do withReplacementFile toFPath postAction $ \ hTo -> do copyFileToHandle fromFPath hTo @@ -862,7 +862,7 @@ withReplacementFile :: FilePath -- ^ Destination file -> (Handle -> IO a) -- ^ Main action -> IO a withReplacementFile path postAction action = - (`ioeSetLocation` "withReplacementFile") `modifyIOError` do + (`ioeAddLocation` "withReplacementFile") `modifyIOError` do mask $ \ restore -> do (tmpFPath, hTmp) <- openBinaryTempFile (takeDirectory path) ".copyFile.tmp" @@ -884,7 +884,7 @@ copyFileToHandle :: FilePath -- ^ Source file -> Handle -- ^ Destination handle -> IO () copyFileToHandle fromFPath hTo = - (`ioeSetLocation` "copyFileToHandle") `modifyIOError` do + (`ioeAddLocation` "copyFileToHandle") `modifyIOError` do withBinaryFile fromFPath ReadMode $ \ hFrom -> copyHandleData hFrom hTo @@ -893,7 +893,7 @@ copyHandleData :: Handle -- ^ Source handle -> Handle -- ^ Destination handle -> IO () copyHandleData hFrom hTo = - (`ioeSetLocation` "copyData") `modifyIOError` do + (`ioeAddLocation` "copyData") `modifyIOError` do allocaBytes bufferSize go where bufferSize = 1024 @@ -925,7 +925,7 @@ copyFileWithMetadata :: FilePath -- ^ Source file -> FilePath -- ^ Destination file -> IO () copyFileWithMetadata src dst = - (`ioeSetLocation` "copyFileWithMetadata") `modifyIOError` doCopy + (`ioeAddLocation` "copyFileWithMetadata") `modifyIOError` doCopy where #ifdef mingw32_HOST_OS doCopy = Win32.copyFile src dst False @@ -1031,7 +1031,7 @@ copyFileTimesFromStatus st dst = do -- canonicalizePath :: FilePath -> IO FilePath canonicalizePath = \ path -> - modifyIOError ((`ioeSetLocation` "canonicalizePath") . + modifyIOError ((`ioeAddLocation` "canonicalizePath") . (`ioeSetFileName` path)) $ -- normalise does more stuff, like upper-casing the drive letter dropTrailingPathSeparator . normalise <$> @@ -1079,7 +1079,7 @@ canonicalizePath = \ path -> -- makeAbsolute :: FilePath -> IO FilePath makeAbsolute path = - modifyIOError ((`ioeSetLocation` "makeAbsolute") . + modifyIOError ((`ioeAddLocation` "makeAbsolute") . (`ioeSetFileName` path)) $ matchTrailingSeparator path . normalise <$> prependCurrentDirectory path @@ -1094,7 +1094,7 @@ makeAbsolute path = -- (internal API) prependCurrentDirectory :: FilePath -> IO FilePath prependCurrentDirectory path = - modifyIOError ((`ioeSetLocation` "prependCurrentDirectory") . + modifyIOError ((`ioeAddLocation` "prependCurrentDirectory") . (`ioeSetFileName` path)) $ if isRelative path -- avoid the call to `getCurrentDirectory` if we can then (</> path) <$> getCurrentDirectory @@ -1246,7 +1246,7 @@ findFileWithIn f name d = do getDirectoryContents :: FilePath -> IO [FilePath] getDirectoryContents path = modifyIOError ((`ioeSetFileName` path) . - (`ioeSetLocation` "getDirectoryContents")) $ do + (`ioeAddLocation` "getDirectoryContents")) $ do #ifndef mingw32_HOST_OS bracket (Posix.openDirStream path) @@ -1344,7 +1344,7 @@ listDirectory path = -- getCurrentDirectory :: IO FilePath getCurrentDirectory = - modifyIOError (`ioeSetLocation` "getCurrentDirectory") $ + modifyIOError (`ioeAddLocation` "getCurrentDirectory") $ specializeErrorString "Current working directory no longer exists" isDoesNotExistError @@ -1419,7 +1419,7 @@ withCurrentDirectory dir action = -- @since 1.2.7.0 getFileSize :: FilePath -> IO Integer getFileSize path = - (`ioeSetLocation` "getFileSize") `modifyIOError` do + (`ioeAddLocation` "getFileSize") `modifyIOError` do #ifdef mingw32_HOST_OS fromIntegral <$> withFileStatus "" path st_size #else @@ -1475,7 +1475,7 @@ doesFileExist name = -- @since 1.3.0.0 pathIsSymbolicLink :: FilePath -> IO Bool pathIsSymbolicLink path = - (`ioeSetLocation` "getDirectoryType") `modifyIOError` do + (`ioeAddLocation` "getDirectoryType") `modifyIOError` do #ifdef mingw32_HOST_OS isReparsePoint <$> Win32.getFileAttributes path where @@ -1516,7 +1516,7 @@ openFileHandle path mode = Win32.createFile path mode share Nothing -- @since 1.2.3.0 -- getAccessTime :: FilePath -> IO UTCTime -getAccessTime = modifyIOError (`ioeSetLocation` "getAccessTime") . +getAccessTime = modifyIOError (`ioeAddLocation` "getAccessTime") . (fst <$>) . getFileTimes -- | Obtain the time at which the file or directory was last modified. @@ -1533,12 +1533,12 @@ getAccessTime = modifyIOError (`ioeSetLocation` "getAccessTime") . -- and the underlying filesystem supports them. -- getModificationTime :: FilePath -> IO UTCTime -getModificationTime = modifyIOError (`ioeSetLocation` "getModificationTime") . +getModificationTime = modifyIOError (`ioeAddLocation` "getModificationTime") . (snd <$>) . getFileTimes getFileTimes :: FilePath -> IO (UTCTime, UTCTime) getFileTimes path = - modifyIOError (`ioeSetLocation` "getFileTimes") . + modifyIOError (`ioeAddLocation` "getFileTimes") . modifyIOError (`ioeSetFileName` path) $ getTimes where @@ -1595,7 +1595,7 @@ fileTimesFromStatus st = -- setAccessTime :: FilePath -> UTCTime -> IO () setAccessTime path atime = - modifyIOError (`ioeSetLocation` "setAccessTime") $ + modifyIOError (`ioeAddLocation` "setAccessTime") $ setFileTimes path (Just atime, Nothing) -- | Change the time at which the file or directory was last modified. @@ -1623,13 +1623,13 @@ setAccessTime path atime = -- setModificationTime :: FilePath -> UTCTime -> IO () setModificationTime path mtime = - modifyIOError (`ioeSetLocation` "setModificationTime") $ + modifyIOError (`ioeAddLocation` "setModificationTime") $ setFileTimes path (Nothing, Just mtime) setFileTimes :: FilePath -> (Maybe UTCTime, Maybe UTCTime) -> IO () setFileTimes _ (Nothing, Nothing) = return () setFileTimes path (atime, mtime) = - modifyIOError (`ioeSetLocation` "setFileTimes") . + modifyIOError (`ioeAddLocation` "setFileTimes") . modifyIOError (`ioeSetFileName` path) $ setTimes (utcTimeToPOSIXSeconds <$> atime, utcTimeToPOSIXSeconds <$> mtime) where @@ -1727,7 +1727,7 @@ The home directory for the current user does not exist, or cannot be found. -} getHomeDirectory :: IO FilePath -getHomeDirectory = modifyIOError (`ioeSetLocation` "getHomeDirectory") get +getHomeDirectory = modifyIOError (`ioeAddLocation` "getHomeDirectory") get where #if defined(mingw32_HOST_OS) get = getFolderPath Win32.cSIDL_PROFILE `catchIOError` \ _ -> @@ -1791,7 +1791,7 @@ getXdgDirectory :: XdgDirectory -- ^ which special directory -- path is returned -> IO FilePath getXdgDirectory xdgDir suffix = - modifyIOError (`ioeSetLocation` "getXdgDirectory") $ + modifyIOError (`ioeAddLocation` "getXdgDirectory") $ normalise . (</> suffix) <$> case xdgDir of XdgData -> get False "XDG_DATA_HOME" ".local/share" @@ -1867,7 +1867,7 @@ getAppUserDataDirectory :: FilePath -- ^ a relative path that is appended -- to the path -> IO FilePath getAppUserDataDirectory appName = do - modifyIOError (`ioeSetLocation` "getAppUserDataDirectory") $ do + modifyIOError (`ioeAddLocation` "getAppUserDataDirectory") $ do #if defined(mingw32_HOST_OS) s <- Win32.sHGetFolderPath nullPtr Win32.cSIDL_APPDATA nullPtr 0 return (s++'\\':appName) @@ -1898,7 +1898,7 @@ cannot be found. -} getUserDocumentsDirectory :: IO FilePath getUserDocumentsDirectory = do - modifyIOError (`ioeSetLocation` "getUserDocumentsDirectory") $ do + modifyIOError (`ioeAddLocation` "getUserDocumentsDirectory") $ do #if defined(mingw32_HOST_OS) Win32.sHGetFolderPath nullPtr Win32.cSIDL_PERSONAL nullPtr 0 #else @@ -1939,3 +1939,10 @@ getTemporaryDirectory = getEnv "TMPDIR" `catchIOError` \ err -> if isDoesNotExistError err then return "/tmp" else ioError err #endif + +ioeAddLocation :: IOError -> String -> IOError +ioeAddLocation e loc = do + ioeSetLocation e newLoc + where + newLoc = loc <> if null oldLoc then "" else ":" <> oldLoc + oldLoc = ioeGetLocation e diff --git a/changelog.md b/changelog.md index 554570ed28ab11033fc2e90cb6f0a0db57e3db39..296551b1ec3eea9ae00e8f478030c0b0e9622d42 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,9 @@ Changelog for the [`directory`][1] package * On Windows, `canonicalizePath` now also dereferences symbolic links + * When exceptions are thrown, the error location will now contain additional + information about the internal function(s) used. + ## 1.2.7.1 (November 2016) * Don't abort `removePathForcibly` if files or directories go missing.