Filemanip on Windows

Dominic Steinitz dominic at steinitz.org
Wed Apr 6 17:56:36 CEST 2011


I tried to install filemanip on a windows system but it failed at the build 
stage:

C:\temp\filemanip-0.3.5.2>runghc Setup.lhs build
Preprocessing library filemanip-0.3.5.2...
Building filemanip-0.3.5.2...

System\FilePath\Manip.hs:25:7:
    Could not find module `System.Posix.Temp':
      Use -v to see a list of the files searched for.

It seems that Manip.hs depends on System.Posix.Temp but the cabal file says it 
doesn't on windows:

if !os(windows) build-depends: unix
 
I amended Manip.hs to work on windows and I am pretty sure it will still work 
on unix. I've attached the amended source (I would have attached a patch but 
I'm not familiar with the source control system you are using).

I also noticed that someone else had submitted a patch for windows support: 
https://bitbucket.org/bos/filemanip/issue/1/patch-for-windows-support. Did this 
every get incorporated? For some reason I wasn't able to look at it but it may 
be better than my patch.

Dominic.

{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TypeSynonymInstances #-}

-- |
-- Module:      System.FilePath.Manip
-- Copyright:   Bryan O'Sullivan
-- License:     BSD3
-- Maintainer:  Bryan O'Sullivan <bos at serpentine.com>
-- Stability:   unstable
-- Portability: Unix-like systems (requires flexible instances)

module System.FilePath.Manip (
      Streamable(..)
    , renameWith
    , modifyWith
    , modifyWithBackup
    , modifyInPlace
    ) where

import Control.Exception
import Control.Monad (liftM)
import Data.Bits ((.&.))
import System.Directory (removeFile, getTemporaryDirectory)
import System.IO (Handle, IOMode(..), hClose, openFile, openTempFile)
import System.PosixCompat.Files (fileMode, getFileStatus, rename, setFileMode)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as L
import qualified System.IO as I

-- | Use a renaming function to generate a new name for a file, then
-- rename it.
renameWith :: (FilePath -> FilePath) -- ^ function to rename with
           -> FilePath -- ^ file to rename
           -> IO ()

renameWith f path = rename path (f path)

-- | Type class for string manipulation over files.
class Streamable a where
    -- | Read the entire contents of a 'Handle'.
    readAll :: Handle -> IO a
    -- | Write an entire string to a 'Handle'.
    writeAll :: Handle -> a -> IO ()

instance Streamable B.ByteString where
    readAll = B.hGetContents
    writeAll = B.hPut

instance Streamable L.ByteString where
    readAll = L.hGetContents
    writeAll = L.hPut

instance Streamable String where
    readAll = I.hGetContents
    writeAll = I.hPutStr

-- | Modify a file in place using the given function.  This is
-- performed by writing to a temporary file, then renaming it on top of
-- the existing file when done.
modifyInPlace :: Streamable a => (a -> a) -- ^ transformation function
              -> FilePath -- ^ name of file to modify
              -> IO ()

modifyInPlace = modifyWith (flip rename)

-- | Modify a file in place using the given function.  The original
-- copy of the file is saved under a new name.  This is performed by
-- writing to a temporary file; renaming the original file to its new
-- name; then renaming the temporary file to the original name.
--
-- Example:
--
-- @
--     -- save original file with a \".bak\" extension
--     'modifyWithBackup' (\<.\> \"bak\")
-- @ 
modifyWithBackup :: Streamable a =>
                    (FilePath -> FilePath) -- ^ chooses new name for original 
file
                 -> (a -> a) -- ^ transformation function
                 -> FilePath -- ^ name of file to modify
                 -> IO ()

modifyWithBackup f = modifyWith backup
    where backup path tmpPath = renameWith f path >> rename tmpPath path

-- | Modify a file in place using the given function.  The new content
-- is written to a temporary file.  Once this is complete, the file
-- manipulation action is called.  Its arguments are the names of the
-- original and temporary files.
--
-- Example:
--
-- @
--     'modifyInPlace' = 'modifyWith' (flip rename)
-- @ 
modifyWith :: Streamable a =>
                (FilePath -> FilePath -> IO ()) -- ^ file manipulation action
             -> (a -> a) -- ^ transformation function
             -> FilePath
             -> IO ()

modifyWith after transform path =
    bracket (openFile path ReadMode) hClose $ \ih -> do
        tmpDir <- getTemporaryDirectory
        (tmpPath, oh) <- openTempFile tmpDir "XXX.XXX"
        let ignore = return ()
            nukeTmp = handle (\(_::IOException) -> ignore) (removeFile tmpPath)
        handle (\(e::IOException) -> nukeTmp >> throw e) $ do
            bracket_ ignore (hClose oh) $
                readAll ih >>= return . transform >>= writeAll oh
            handle (\(_::IOException) -> nukeTmp) $ do
                mode <- fileMode `liftM` getFileStatus path
                setFileMode tmpPath (mode .&. 0777)
                after path tmpPath





More information about the Libraries mailing list