module System.Apotiki.Ar (arFromData, arFromFile, ArEntry (..)) where import Data.Attoparsec.Combinator (manyTill) import Data.ByteString.Char8 (pack, unpack) import qualified Data.Map as M import qualified Data.ByteString as B import qualified Data.Attoparsec.ByteString as P armag = "!\n" -- magic header arfmag = "`\n" -- magic pad data ArEntry = ArEntry { -- A file entry in an ar file entryDate :: Int, entryGid :: Int, entryUid :: Int, entryMode :: Int, entrySize :: Int, entryData :: B.ByteString } deriving Show type ArMapEntry = (String, ArEntry) type ArMap = M.Map String ArEntry arEntryParser :: P.Parser ArMapEntry arEntryParser = do let not_space = (\c -> (c /= '/') && (c /= ' ')) name <- (takeWhile not_space . unpack) `fmap` P.take 16 date <- (read . unpack) `fmap` P.take 12 gid <- (read . unpack) `fmap` P.take 6 uid <- (read . unpack) `fmap` P.take 6 mode <- (read . unpack) `fmap` P.take 8 size <- (read . unpack) `fmap` P.take 10 magic <- P.string $ pack arfmag payload <- P.take size padding <- if size `mod` 2 == 1 then P.string $ pack "\n" else P.take 0 return (name, ArEntry date gid uid mode size payload) arParser :: P.Parser ArMap arParser = do magic <- P.string $ pack armag entries <- manyTill arEntryParser P.endOfInput return (M.fromList entries) arFromData :: B.ByteString -> Either String ArMap arFromData input = P.parseOnly arParser input arFromFile :: String -> IO (Either String ArMap) arFromFile path = do content <- B.readFile path return (arFromData content)