62 lines
2.1 KiB
Haskell
62 lines
2.1 KiB
Haskell
import Crypto.Hash.MD5 (hash)
|
|
import Data.ByteString as BS (readFile, ByteString())
|
|
import System.Environment (getArgs, getProgName)
|
|
import System.Directory (doesDirectoryExist, getDirectoryContents)
|
|
import System.FilePath.Posix ((</>))
|
|
import Control.Monad (forM)
|
|
import Text.Printf (printf)
|
|
import System.IO (withFile, IOMode(ReadMode), hFileSize)
|
|
|
|
|
|
type File = (BS.ByteString, -- md5hash
|
|
FilePath) -- filepath
|
|
|
|
type FileSize = Integer
|
|
|
|
getRecursiveContents :: FilePath -> FileSize -> IO [File]
|
|
getRecursiveContents curDir maxsize = do
|
|
names <- getDirectoryContents curDir
|
|
let dirs = filter (`notElem` [".", ".."]) names
|
|
files <- forM dirs $ \path -> do
|
|
let path' = curDir </> path
|
|
exists <- doesDirectoryExist path'
|
|
if exists
|
|
then getRecursiveContents path' maxsize
|
|
else genFileHash path' maxsize
|
|
return $ concat files
|
|
|
|
|
|
genFileHash :: FilePath -> FileSize -> IO [File]
|
|
genFileHash path maxsize = do
|
|
size <- withFile path ReadMode hFileSize
|
|
if size <= maxsize
|
|
then BS.readFile path >>= \bs -> return [(hash bs, path)]
|
|
else return []
|
|
|
|
findDuplicates :: FilePath -> FileSize -> IO ()
|
|
findDuplicates dir bytes = do
|
|
exists <- doesDirectoryExist dir
|
|
if exists
|
|
then getRecursiveContents dir bytes >>= findSameHashes
|
|
else printf "Sorry, the directory \"%s\" does not exist...\n" dir
|
|
|
|
findSameHashes :: [File] -> IO ()
|
|
findSameHashes [] = return ()
|
|
findSameHashes ((hash, fp):xs) = do
|
|
case lookup hash xs of
|
|
(Just dupFile) -> printf "===========================\n\
|
|
\Found duplicate:\n\
|
|
\=> %s \n\
|
|
\=> %s \n\n" fp dupFile
|
|
>> findSameHashes xs
|
|
(_) -> findSameHashes xs
|
|
|
|
main :: IO ()
|
|
main = do
|
|
args <- getArgs
|
|
case args of
|
|
[dir, mbytes] | [(bytes ,"")] <- reads mbytes
|
|
, bytes >= 1 -> findDuplicates dir bytes
|
|
(_) -> do
|
|
name <- getProgName
|
|
printf "Something went wrong - please use ./%s <dir> <bytes>\n" name
|