ファイルコピー

突然だけど、ちゃんと活動していることを示す(誰に?)ためにコードを貼付けておく。
ディレクトリを指定するとコピーするというだけの投げやりプログラム。ソースもいろいろ投げやり。動けばなんでもいーや的な。1週間以内にはこれが GUI っぽくなる予定です。

{-# OPTIONS -Wall #-}
import Control.Monad
import Control.Monad.Reader
import Control.Concurrent
import System
import System.Directory
import System.IO

-- | True when given string starts with '.'
startsWithDot ('.':_) = True
startsWithDot _       = False
-- | True given
doesntExist :: FilePath -> IO Bool
doesntExist p = do b1 <- doesDirectoryExist p
                   b2 <- doesFileExist p
                   return $ not $ b1 || b2
-- | count number of files in given path
countFile :: FilePath -> IO Int
countFile p =
    do b1 <- doesFileExist p
       b2 <- doesDirectoryExist p
       case (b1, b2) of
         (True, False) -> return 1
         (False, True) ->
             do c <- getDirectoryContents p
                let c' = map ((p++"/")++) $ filter (not . startsWithDot) c
                liftM sum $ mapM countFile c'
         _             -> error $ "file not found: "++p
-- | copy directory recursively
--   both of the arguments are directoryname
copyDir :: FilePath -> FilePath -> MVar () -> IO ()
copyDir s t m =
    do b1 <- doesDirectoryExist s
       b2 <- doesntExist t
       if b1 && b2 then runReaderT copy (dropSep s, dropSep t)
                   else error $ "File or Directory already exists: "++t
    where dropSep s | last s == '/' = init s
                    | otherwise     = s
          copy :: ReaderT (FilePath, FilePath) IO ()
          copy = do (s,t) <- ask
                    b <- liftIO $ doesFileExist s
                    if b
                      then liftIO $ copyFile s t >> putMVar m ()
                      else do c <- liftIO $ getDirectoryContents s
                              liftIO $ createDirectory t
                              mapM_ (\c -> local (addDir c) copy)
                                        $ filter (not . startsWithDot) c
          addDir c (s,t) = (s++"/"++c,t++"/"++c)
-- | print progressbar
printer :: MVar () -> Int -> [Int] -> IO ()
printer _ _ [] = putChar '\n'
printer m c (x:xs) = do takeMVar m
                        if c == x then do putChar '*'
                                          hFlush stdout
                                          printer m (c+1) xs
                                  else printer m (c+1) (x:xs)
                        
-- | main function
start :: FilePath -> FilePath -> IO ()
start s t = do m <- newEmptyMVar
               putMVar m ()
               forkIO $ copyDir s t m
               fnum <- countFile s
               let one = fnum `div` 20
               putStrLn $ "0      :********************:"++show fnum
               putStr     "copying:" >> hFlush stdout
               let meter = let x = takeWhile (<fnum) [one,one+one..]
                           in  if length x < 20 then x++[fnum]
                                                else init x++[fnum]
               printer m 0 meter
main :: IO ()
main = getArgs >>= \[s,t] -> start s t
Main> start "ghc-6.6.1" "test"
0      :********************:2265
copying:********************
Main>