Files

Files.hs

Copyright © 2008 Dave Bayer. Subject to a BSD-style license.

This module is part of the Annote project.

module Files
    (createDirectory,doesFileExist,doesDirectoryExist,
    reportError,baseName,newSuffix,isNewer)
where

Files provides miscellaneous file utilities.


Directory provides an interface for directory manipulation. We import the following functions:

createDirectory :: FilePath -> IO ()
getModificationTime :: FilePath -> IO ClockTime
doesFileExist :: FilePath -> IO Bool
doesDirectoryExist :: FilePath -> IO Bool
import Directory
    (createDirectory,getModificationTime,doesFileExist,doesDirectoryExist)

Control.Exception provides support for raising and catching exceptions. We import the following functions:

tryJust :: (Exception -> Maybe b) -> IO a -> IO (Either b a)
ioErrors :: Exception -> Maybe IOError
import Control.Exception (tryJust,ioErrors)

System.IO is the standard IO library. We import the following function:

hPutStrLn :: Handle -> String -> IO ()
import System.IO (stderr,hPutStrLn)

Regex provides regular expression matching. It is a wrapper around Text.Regex.

import Regex (mkRegex,mkSub,doSub,isMatch)

reportError

reportError writes an error message to stderr. The reversed argument order is chosen to allow calls of the form

reportError msg $ who

where msg is a simple string, but who needs to be constructed.

reportError :: String → String → IO ()
reportError msg who = hPutStrLn stderr $ "annote: " ++ who ++ ": " ++ msg

baseName

baseName returns the base filename of name.

baseName :: FilePath → FilePath
baseName name =
    doSub name $ mkSub ("^.*/", "")

newSuffix

newSuffix replaces the filename extension of name with ext. If name does not have an extension, then ext is added.

newSuffix :: FilePath → String → FilePath
newSuffix name ext =
    if isMatch name $ mkRegex "[.]"
        then doSub name $ mkSub ("[.][^.]*$", suffix)
        else name ++ suffix
    where suffix = '.' : ext

isNewer

isNewer is a predicate, reporting whether or not name1 has a more recent modification date than name2. It returns False if name1 does not exist, and True if name2 does not exist.

isNewer can be used to decide whether or not name2 should be updated from name1.

getModificationTime may fail with isPermissionError even though we have already checked existence; we catch and report this error.

isNewer :: FilePath → FilePath → IO Bool
isNewer name1 name2 =
    do  exists1 ← doesFileExist name1
        if exists1
            then do
                exists2 ← doesFileExist name2
                if exists2
                    then do
                        try ← tryJust ioErrors newer
                        case try of
                            Right t → return t
                            Left _  → do
                                reportError perr $ name1 ++ " or " ++ name2
                                return False
                    else do
                        return True
            else do
                reportError derr name1 
                return False
    where newer = do
              t1 ← getModificationTime name1
              t2 ← getModificationTime name2
              return $ t1 > t2
          derr = "file does not exist"
          perr = "file permission error"