-- Compiler Toolkit: operations on file -- -- Author : Manuel M T Chakravarty -- Created: 6 November 1999 -- -- Version $Revision: 1.1.1.1 $ from $Date: 2004/11/13 16:42:49 $ -- -- Copyright (c) [1999..2003] Manuel M T Chakravarty -- -- This file is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- This file is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- --- DESCRIPTION --------------------------------------------------------------- -- -- Typical operations needed when manipulating file names. -- --- DOCU ---------------------------------------------------------------------- -- -- language: Haskell 98 -- --- TODO ---------------------------------------------------------------------- -- module FileOps (fileFindIn, mktemp) where import Prelude hiding (catch) -- standard libs import Data.Char (chr, ord) import System.Directory (doesFileExist) import System.IO (Handle, IOMode(..), openFile) import Control.Monad (liftM) import Control.Exception (catch, SomeException) import System.Random (newStdGen, randomRs) import FNameOps (dirname, stripDirname, addPath) -- search for the given file in the given list of directories (EXPORTED) -- -- * if the file does not exist, an exception is raised -- -- * if the given file name is absolute, it is first tried whether this file -- exists, afterwards the path component is stripped and the given -- directories are searched; otherwise, if the file name is not absolute, -- the path component is retained while searching the directories -- fileFindIn :: FilePath -> [FilePath] -> IO FilePath "" `fileFindIn` paths = fail "Empty file name" file `fileFindIn` paths = do let (paths', file') = if head file == '/' then (dirname file : paths, stripDirname file) else (paths, file) files = map (`addPath` file') paths' existsFlags <- mapM doesFileExist files let existingFiles = [file | (file, flag) <- zip files existsFlags, flag] if null existingFiles then fail (file ++ ": File does not exist") else return $ head existingFiles -- |Create a temporary file with a unique name. -- -- * A unique sequence of at least six characters and digits is added -- inbetween the two given components (the latter of which must include the -- file suffix if any is needed) -- -- * Default permissions are used, which might not be optimal, but -- unfortunately the Haskell standard libs don't support proper permission -- management. -- -- * We make 100 attempts on getting a unique filename before giving up. -- mktemp :: FilePath -> FilePath -> IO (Handle, FilePath) mktemp pre post = do rs <- liftM (randomRs (0, 61)) newStdGen -- range for lower and upper case letters plus digits createLoop 100 rs where createLoop 0 _ = fail "mktemp: failed 100 times" createLoop attempts rs = let (rs', fname) = nextName rs in do h <- openFile fname ReadWriteMode return (h, fname) `catch` handler attempts rs' -- handler :: Int -> [Int] -> SomeException -> IO (Handle,FilePath) handler attempts rs' _ = createLoop (attempts - 1) rs' sixChars :: [Int] -> ([Int], String) sixChars is = let (sixInts, is') = splitAt 6 is -- toChar i | i < 10 = chr . (ord '0' +) $ i | i < 36 = chr . (ord 'A' +) . (subtract 10) $ i | otherwise = chr . (ord 'a' +) . (subtract 36) $ i in (is', map toChar sixInts) -- nextName :: [Int] -> ([Int], String) nextName is = let (is', rndChars) = sixChars is in (is', pre ++ rndChars ++ post)