{-# LANGUAGE ForeignFunctionInterface #-} module Codec.Archive.Read where import Control.Monad (when) import Data.ByteString (ByteString) import qualified Data.ByteString as B import Foreign import Foreign.C.String import Foreign.C.Types import Codec.Archive.Util foreign import ccall "archive.h archive_read_new" archiveReadNew :: IO (Ptr Archive) foreign import ccall "archive.h archive_read_free" archiveReadFree :: Ptr Archive -> IO CInt foreign import ccall "archive.h archive_read_support_filter_all" archiveReadSupportFilterAll :: Ptr Archive -> IO () foreign import ccall "archive.h archive_read_support_format_all" archiveReadSupportFormatAll :: Ptr Archive -> IO () foreign import ccall "archive.h archive_read_open_filename" archiveReadOpenFilename :: Ptr Archive -> CString -> CSize -> IO CInt foreign import ccall "archive.h archive_read_next_header" archiveReadNextHeader :: Ptr Archive -> Ptr (Ptr Entry) -> IO CInt foreign import ccall "archive.h archive_read_data" archiveReadData :: Ptr Archive -> CString -> CSize -> IO CSize foreign import ccall "archive.h archive_entry_pathname" archiveEntryPathname :: Ptr Entry -> IO CString foreign import ccall "archive.h archive_entry_size" archiveEntrySize :: Ptr Entry -> IO CSize readArchive :: FilePath -> IO (Ptr Archive) readArchive path = do p <- archiveReadNew archiveReadSupportFilterAll p archiveReadSupportFormatAll p _ <- withCString path $ \cpath -> archiveReadOpenFilename p cpath (64 * 1024) >>= checkArchiveError p return p getNextEntry :: Ptr Archive -> IO (Maybe (FilePath, ByteString)) getNextEntry archive = alloca $ \pentry -> do eof <- archiveReadNextHeader archive pentry >>= checkArchiveError archive if eof then return Nothing else do entry <- peek pentry path <- archiveEntryPathname entry >>= peekCString size <- archiveEntrySize entry dat <- allocaArray (fromIntegral size) $ \dat -> do size' <- archiveReadData archive dat size when (size' < 0) $ throwArchiveException archive B.packCStringLen (dat, fromIntegral size') return $ Just (path, dat)