zip-stream/cmd/unzip.hs
2017-05-11 21:20:15 -04:00

48 lines
1.9 KiB
Haskell

{-# LANGUAGE RecordWildCards #-}
import Control.Monad (when, unless)
import Control.Monad.IO.Class (liftIO)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Conduit as C
import qualified Data.Conduit.Binary as CB
import Data.Time.LocalTime (localTimeToUTC, utc)
import System.Directory (createDirectoryIfMissing, setModificationTime)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import System.FilePath.Posix (takeDirectory)
import System.IO (stdin, openFile, IOMode(WriteMode), hClose, hSetFileSize, hPutStrLn, stderr)
import Codec.Archive.Zip.Conduit.UnZip
extract :: C.Sink (Either ZipEntry BS.ByteString) IO ()
extract = C.awaitForever start where
start (Left ZipEntry{..}) = do
liftIO $ BSC.putStrLn zipEntryName
liftIO $ createDirectoryIfMissing True (takeDirectory name)
if BSC.last zipEntryName == '/'
then when (zipEntrySize /= 0) $ fail $ name ++ ": non-empty directory"
else do -- C.bracketP
h <- liftIO $ openFile name WriteMode
liftIO $ hSetFileSize h $ toInteger zipEntrySize
write C..| CB.sinkHandle h
liftIO $ hClose h
liftIO $ setModificationTime name $ localTimeToUTC utc zipEntryTime -- FIXME: timezone
where name = BSC.unpack $ BSC.dropWhile ('/' ==) zipEntryName -- should we utf8 decode?
start (Right _) = fail "Unexpected leading or directory data contents"
write = C.await >>= maybe
(return ())
block
block (Right b) = C.yield b >> write
block a = C.leftover a
main :: IO ()
main = do
args <- getArgs
unless (null args) $ do
hPutStrLn stderr "Usage: unzip\nRead a zip file from stdin and extract it in the current directory."
exitFailure
ZipInfo{..} <- C.runConduit
$ CB.sourceHandle stdin
C..| C.fuseUpstream unZip extract
BSC.putStrLn zipComment