packages feed

curry-base-0.2.9: Curry/Files/PathUtils.hs

{-
  $Id: PathUtils.lhs,v 1.5 2003/05/04 16:12:35 wlux Exp $

  Copyright (c) 1999-2003, Wolfgang Lux
  See LICENSE for the full license.
-}

module Curry.Files.PathUtils
  ( -- * Re-exports from 'System.FilePath'
    takeBaseName, dropExtension, takeExtension, takeFileName

    -- * Retrieving curry fiiles
  , lookupModule, lookupFile, lookupInterface, getCurryPath

    -- * Reading and writing modules from files
  , writeModule, readModule, maybeReadModule
  , doesModuleExist, getModuleModTime
  ) where

import Control.Monad (liftM)
import System.FilePath
import System.Directory
import System.Time (ClockTime)

import Curry.Base.Ident
import Curry.Files.Filenames


{- |Search for a given curry module in the given source file paths and
    library paths. Note that the current directory is always searched first.
-}
lookupModule :: [FilePath]          -- ^ list of paths to source files
             -> [FilePath]          -- ^ list of paths to library files
             -> ModuleIdent         -- ^ module identifier
             -> IO (Maybe FilePath) -- ^ the file path if found
lookupModule paths libPaths m =
  lookupFile ("" : paths ++ libPaths) moduleExts fn
  where fn = foldr1 combine (moduleQualifiers m)


{- |Search for an interface file in the import search path using the
    interface extension 'flatIntExt'. Note that the current directory is
    always searched first.
-}
lookupInterface :: [FilePath]          -- ^ list of paths to search in
                -> ModuleIdent         -- ^ module identifier
                -> IO (Maybe FilePath) -- ^ the file path if found
lookupInterface paths m = lookupFile ("" : paths) [flatIntExt] ifn
  where ifn = foldr1 combine (moduleQualifiers m)


-- |Search for a source file name and eventually return its content
lookupFile :: [FilePath]          -- ^ list of file paths to search in
           -> [String]            -- ^ list of possible extensions of the file
           -> String              -- ^ initial file name
           -> IO (Maybe FilePath) -- ^ the file path if found
lookupFile paths exts file = lookupFile' paths' where
  paths' = do
           p <- paths
           e <- exts
           let fn = p `combine` replaceExtension file e
           [fn, ensureCurrySubdir fn]
  lookupFile' []      = return Nothing
  lookupFile' (fn:ps) = do
                        so <- doesFileExist fn
                        if so then return (Just fn) else lookupFile' ps


{- | Search in the given list of paths for the given file name. If the file
     name has no extension then source file extension is assumed. If the file
     name already contains a directory than the paths to search in are
     ignored.
-}
getCurryPath :: [FilePath] -> FilePath -> IO (Maybe FilePath)
getCurryPath paths fn = lookupFile filepaths exts fn where
  filepaths = "" : paths'
  fnext     = takeExtension fn
  exts   | null fnext = sourceExts
         | otherwise  = [fnext]
  paths' | pathSeparator `elem` fn = []
         | otherwise               = paths


-- Writing and reading files

{- | Write the content to a file in the given directory or in the
     'currySubdir' sub-directory if the first parameter is set to 'True'.
-}
writeModule :: Bool     -- ^ should the 'currySubdir' be included in the path?
            -> FilePath -- ^ original path
            -> String   -- ^ file content
            -> IO ()
writeModule inSubdir filename contents = do
  let fn = if inSubdir then ensureCurrySubdir filename else filename
  createDirectoryIfMissing True $ takeDirectory fn
  writeFile fn contents


{- | Read the content from a file in the given directory or in the
     'currySubdir' sub-directory of the given sub-directory.
-}
readModule :: FilePath -> IO String
readModule = onExistingFileDo readFile


{- | Tries to read the specified module and returns either 'Just String' if
     reading was successful or 'Nothing' otherwise.
-}
maybeReadModule :: FilePath -> IO (Maybe String)
maybeReadModule filename =
  catch (liftM Just (readModule filename)) (\ _ -> return Nothing)


{- | Check whether a module exists either in the given directory or in the
     'currySubdir'.
-}
doesModuleExist :: FilePath -> IO Bool
doesModuleExist = onExistingFileDo doesFileExist


-- | Get the modification time of a file
getModuleModTime :: FilePath -> IO ClockTime
getModuleModTime = onExistingFileDo getModificationTime


-- Helper functions


{- | Ensure that the 'currySubdir' is the last component of the
     directory structure of the given 'FilePath'. If the 'FilePath' already
     contains the 'currySubdir' it remains unchanged.
-}
ensureCurrySubdir :: FilePath -> FilePath
ensureCurrySubdir = ensureSubdir currySubdir


{- | Ensure that the given sub-directory is the last component of the
     directory structure of the given 'FilePath'. If the 'FilePath' already
     contains the sub-directory it remains unchanged.
-}
ensureSubdir :: String   -- ^ sub-directory to add
             -> FilePath -- ^ original 'FilePath'
             -> FilePath -- ^ original 'FilePath'
ensureSubdir subdir file
  = replaceDirectory file
  $ addSub (splitDirectories $ takeDirectory file) subdir where
  addSub :: [String] -> String -> String
  addSub [] sub      = sub
  addSub ds sub
    | last ds == sub = joinPath ds
    | otherwise      = joinPath ds </> sub


{- | Perform an action on a file either in the given directory or else in the
     'currySubdir' sub-directory.
-}
onExistingFileDo :: (FilePath -> IO a) -> FilePath -> IO a
onExistingFileDo act filename = do
  ex <- doesFileExist filename
  if ex then act filename
        else act $ ensureCurrySubdir filename