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 = "!<arch>\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)