module Data.Rakhana.XRef
( getXRef
, getXRefPos
) where
import qualified Data.ByteString.Char8 as B8
import Data.Char (isDigit, isSpace)
import Data.Typeable
import Control.Monad.Catch (Exception, MonadThrow(..))
import Pipes.Safe ()
import Data.Rakhana.Internal.Parsers
import Data.Rakhana.Internal.Types
import Data.Rakhana.Tape
import Data.Rakhana.Util.Drive
data XRefParsingException
= XRefParsingException String
deriving (Show, Typeable)
instance Exception XRefParsingException
bufferSize :: Int
bufferSize = 4096
getXRefPos :: MonadThrow m => Drive m Integer
getXRefPos
= do driveBottom
driveBackward
skipEOL
parseEOF
skipEOL
p <- parseXRefPosInteger
skipEOL
parseStartXRef
return p
getXRef :: MonadThrow m => Integer -> Drive m XRef
getXRef pos
= do driveTop
driveForward
driveSeek pos
eR <- parseRepeatedly bufferSize parseXRef
either (throwM . XRefParsingException) return eR
skipEOL :: Monad m => Drive m ()
skipEOL
= do bs <- drivePeek 1
case B8.uncons bs of
Just (c, _)
| isSpace c -> driveDiscard 1 >> skipEOL
| otherwise -> return ()
_ -> return ()
parseEOF :: MonadThrow m => Drive m ()
parseEOF
= do bs <- driveGet 5
case bs of
"%%EOF" -> return ()
_ -> throwM $ XRefParsingException "Expected %%EOF"
parseXRefPosInteger :: MonadThrow m => Drive m Integer
parseXRefPosInteger = go []
where
go cs = do bs <- drivePeek 1
case B8.uncons bs of
Just (c,_)
| isDigit c -> driveDiscard 1 >> go (c:cs)
| otherwise -> return $ read cs
_ -> return $ read cs
parseStartXRef :: MonadThrow m => Drive m ()
parseStartXRef
= do bs <- driveGet 9
case bs of
"startxref" -> return ()
_ -> throwM $ XRefParsingException "Expected startxref"