nptools-0.2.1: cp-rescue.hs
{-# LANGUAGE ScopedTypeVariables #-}
-- This script is originally from Neil Mitchell's blog
import Data.ByteString (hGet, hPut)
import Data.Char (isDigit)
import System.IO
import System.Environment
import Control.Monad
import Control.Exception
type Offset = Integer
type Length = Integer
type ChunkLength = Length
defaultChunkLength :: ChunkLength
defaultChunkLength = 10000000
main :: IO ()
main = do
args <- getArgs
(clen,src,dest) <-
case args of
[src,dest]
-> return (defaultChunkLength,src,dest)
["--chunk-length",clen,src,dest]
| all isDigit clen
-> return (read clen,src,dest)
_ -> fail "Usage: cp-rescue [--chunk-length <num>] <src> <dest>"
withBinaryFile src ReadMode $ \hSrc ->
withBinaryFile dest WriteMode $ \hDest -> do
nSrc <- hFileSize hSrc
nDest <- hFileSize hDest
when (nSrc /= nDest) $ hSetFileSize hDest nSrc
copy hSrc hDest $ split clen 0 nSrc
copyChunk :: Handle -> Handle -> Offset -> Length -> IO ()
copyChunk hSrc hDest from len = do
hSeek hSrc AbsoluteSeek from
hSeek hDest AbsoluteSeek from
bs <- hGet hSrc $ fromIntegral len
hPut hDest bs
copy :: Handle -> Handle -> [(Offset,Length)] -> IO ()
copy _ _ [] = return ()
copy hSrc hDest chunks = do
putStrLn $ "Copying " ++ show (length chunks) ++ " of at most " ++ show (snd $ head chunks)
chunks' <- forM chunks $ \(from,len) -> do
res <- Control.Exception.try $ copyChunk hSrc hDest from len
case res of
Left (_ :: IOException) -> putChar '#' >> return (split (len `div` 5) from len)
Right _ -> putChar '.' >> return []
putChar '\n'
copy hSrc hDest $ concat chunks'
split :: ChunkLength -> Offset -> Length -> [(Offset,Length)]
split clen off len
| clen <= 0 = []
| clen >= len = [(off,len)]
| otherwise = (off,clen) : split clen (off+clen) (len-clen)