gtk2hs-buildtools-0.12.4: c2hs/base/general/FileOps.hs
-- 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)