module Linspire.Debian.Control.ByteString ( Control'(..) , Paragraph'(..) , Field'(..) , Control , Paragraph , Field , ControlFunctions(..) -- * Helper Functions , mergeControls , fieldValue , removeField , prependFields , appendFields , renameField , modifyField , raiseFields ) where -- Standard GHC modules import Control.Monad.State import Data.Char(chr,ord) import Data.List import Data.Maybe import Data.Word import Foreign.C.String (CString, CStringLen) import Foreign.C.Types (CSize) import Foreign.ForeignPtr import Foreign.Marshal.Array import Foreign.Ptr import Foreign.Storable (Storable(..)) import System.IO.Unsafe import System.Environment import Text.ParserCombinators.Parsec.Error import Text.ParserCombinators.Parsec.Pos -- Third Party Modules import qualified Data.ByteString as B import qualified Data.ByteString.Base as BB import qualified Data.ByteString.Char8 as C import Linspire.Debian.Control.Common -- Local Modules -- import ByteStreamParser -- * Types {- newtype Control = Control [Paragraph] newtype Paragraph = Paragraph [Field] newtype Field = Field (C.ByteString, C.ByteString) -} type Control = Control' C.ByteString type Paragraph = Paragraph' C.ByteString type Field = Field' C.ByteString -- * Control Parser type ControlParser a = Parser C.ByteString a pKey :: ControlParser C.ByteString pKey = notEmpty $ pTakeWhile (\c -> (c /= ':') && (c /= '\n')) pValue :: ControlParser C.ByteString pValue = pTakeWhile2 (\a b -> not (endOfValue a b)) where endOfValue :: Char -> Maybe Char -> Bool endOfValue '\n' Nothing = True endOfValue '\n' (Just ' ') = False endOfValue '\n' (Just '\t') = False endOfValue '\n' (Just '#') = False endOfValue '\n' _ = True endOfValue _ _ = False pField :: ControlParser Field pField = do k <- pKey pChar ':' v <- pValue -- pChar '\n' (pChar '\n' >> return ()) <|> pEOF return (Field (k,v)) pComment :: ControlParser Field pComment = do c1 <- pChar '#' text <- pTakeWhile2 (\ a b -> not (endOfComment a b)) return . Comment $ (B.append (B.singleton . c2w $ c1) text) where endOfComment '\n' Nothing = True endOfComment '\n' (Just '#') = False endOfComment '\n' _ = True endOfComment _ _ = False pParagraph :: ControlParser Paragraph pParagraph = do f <- pMany1 (pComment <|> pField) pSkipMany (pChar '\n') return (Paragraph f) pControl :: ControlParser Control pControl = do pSkipMany (pChar '\n') c <- pMany pParagraph return (Control c) -- parseControlFromFile :: FilePath -> IO (Either String Control) instance ControlFunctions C.ByteString where parseControlFromFile fp = do c <- C.readFile fp case parse pControl c of Nothing -> return (Left (newErrorMessage (Message ("Failed to parse " ++ fp)) (newPos fp 0 0))) (Just (cntl,_)) -> return (Right cntl) parseControlFromHandle sourceName handle = do c <- C.hGetContents handle case parse pControl c of Nothing -> return (Left (newErrorMessage (Message ("Failed to parse " ++ sourceName)) (newPos sourceName 0 0))) (Just (cntl,_)) -> return (Right cntl) lookupP fieldName (Paragraph fields) = let pFieldName = C.pack fieldName in find (\ (Field (fieldName',_)) -> fieldName' == pFieldName) fields -- NOTE: probably inefficient stripWS = C.reverse . strip . C.reverse . strip where strip = C.dropWhile (flip elem " \t") {- main = do [fp] <- getArgs C.readFile fp >>= \c -> maybe (putStrLn "failed.") (print . length . fst) (parse pControl c) -} -- * Helper Functions -- | 'takeWhile', applied to a predicate @p@ and a ByteString @xs@, -- returns the longest prefix (possibly empty) of @xs@ of elements that -- satisfy @p@. takeWhile2 :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> B.ByteString takeWhile2 f ps = BB.unsafeTake (findIndex2OrEnd (\w1 w2 -> not (f w1 w2)) ps) ps {-# INLINE takeWhile2 #-} break2 :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> Maybe (B.ByteString, B.ByteString) break2 p ps = case findIndex2OrEnd p ps of n -> Just (BB.unsafeTake n ps, BB.unsafeDrop n ps) span2 :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> Maybe (B.ByteString, B.ByteString) span2 p ps = break2 (\a b -> not (p a b)) ps -- | 'findIndexOrEnd' is a variant of findIndex, that returns the length -- of the string if no element is found, rather than Nothing. findIndex2OrEnd :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> Int findIndex2OrEnd k (BB.PS x s l) = unsafePerformIO $ withForeignPtr x $ \f -> go (f `plusPtr` s) 0 where go a b | a `seq` b `seq` False = undefined go ptr n | n >= l = return l | otherwise = do w1 <- peek ptr w2 <- if (n + 1 < l) then (peek (ptr `plusPtr` 1) >>= return . Just) else return Nothing if k w1 w2 then return n else go (ptr `plusPtr` 1) (n+1) {- findIndex2OrEnd :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> Int findIndex2OrEnd k (B.PS x s l) = unsafePerformIO $ withForeignPtr x $ \f -> go (f `plusPtr` s) 0 where go a b | a `seq` b `seq` False = undefined go ptr n | n >= l = return l | otherwise = do w1 <- peek ptr case (w2c w1) of '\n' -> if (n + 1 < l) then do w2 <- peek (ptr `plusPtr` 1) case (w2c w2) of ' ' -> go (ptr `plusPtr` 2) (n + 2) _ -> return n else return l -- go (ptr `plusPtr` 1) (n + 1) _ -> go (ptr `plusPtr` 1) (n + 1) -} {- w2 <- if (n + 1 < l) then (peek (ptr `plusPtr` 1) >>= return . Just) else return Nothing if k w1 w2 then return n else go (ptr `plusPtr` 1) (n+1) -} {-# INLINE findIndex2OrEnd #-} -- | The 'findIndex' function takes a predicate and a 'ByteString' and -- returns the index of the first element in the ByteString -- satisfying the predicate. findIndex2 :: (Word8 -> Maybe Word8 -> Bool) -> B.ByteString -> Maybe Int findIndex2 k (BB.PS x s l) = unsafePerformIO $ withForeignPtr x $ \f -> go (f `plusPtr` s) 0 where go a b | a `seq` b `seq` False = undefined go ptr n | n >= l = return Nothing | otherwise = do w1 <- peek ptr w2 <- if (n + 1 < l) then (peek (ptr `plusPtr` 1) >>= return . Just) else return Nothing if k w1 w2 then return (Just n) else go (ptr `plusPtr` 1) (n+1) {-# INLINE findIndex2 #-} -- Copied from ByteStream because they are not exported w2c :: Word8 -> Char w2c = chr . fromIntegral c2w :: Char -> Word8 c2w = fromIntegral . ord -- * Parser data Result a = Ok a | Fail | Empty deriving Show m2r :: Maybe a -> Result a m2r (Just a) = Ok a m2r Nothing = Empty r2m :: Result a -> Maybe a r2m (Ok a) = Just a r2m _ = Nothing newtype Parser state a = Parser { unParser :: (state -> Result (a, state)) } instance Monad (Parser state) where return a = Parser (\s -> Ok (a,s)) m >>= f = Parser $ \state -> let r = (unParser m) state in case r of Ok (a,state') -> case unParser (f a) $ state' of Empty -> Fail o -> o Empty -> Empty Fail -> Fail instance MonadPlus (Parser state) where mzero = Parser (const Empty) mplus (Parser p1) (Parser p2) = Parser (\s -> case p1 s of Empty -> p2 s o -> o ) -- Parser (\s -> maybe (p2 s) (Just) (p1 s)) pSucceed :: a -> Parser state a pSucceed = return pFail :: Parser state a pFail = Parser (const Empty) (<|>) :: Parser state a -> Parser state a -> Parser state a (<|>) = mplus satisfy :: (Char -> Bool) -> Parser C.ByteString Char satisfy f = Parser $ \bs -> if C.null bs then Empty else let (s,ss) = (C.head bs, C.tail bs) in if (f s) then Ok (s,ss) else Empty pChar :: Char -> Parser C.ByteString Char pChar c = satisfy ((==) c) try :: Parser state a -> Parser state a try (Parser p) = Parser $ \bs -> case (p bs) of Fail -> Empty o -> o pEOF :: Parser C.ByteString () pEOF = Parser $ \bs -> if C.null bs then Ok ((),bs) else Empty pTakeWhile2 :: (Char -> Maybe Char -> Bool) -> Parser C.ByteString C.ByteString pTakeWhile2 f = Parser $ \bs -> m2r (span2 (\w1 w2 -> f (w2c w1) (fmap w2c w2)) bs) pTakeWhile :: (Char -> Bool) -> Parser C.ByteString C.ByteString pTakeWhile f = Parser $ \bs -> Ok (B.span (\w -> f (w2c w)) bs) pSkipWhile :: (Char -> Bool) -> Parser C.ByteString () pSkipWhile p = Parser $ \bs -> Ok ((), C.dropWhile p bs) pMany :: Parser st a -> Parser st [a] pMany p = scan id where scan f = do x <- p scan (\tail -> f (x:tail)) <|> return (f []) notEmpty :: Parser st C.ByteString -> Parser st C.ByteString notEmpty (Parser p) = Parser $ \s -> case p s of o@(Ok (a, s)) -> if C.null a then Empty else o x -> x pMany1 :: Parser st a -> Parser st [a] pMany1 p = do x <- p xs <- pMany p return (x:xs) pSkipMany :: Parser st a -> Parser st () pSkipMany p = scan where scan = (p >> scan) <|> return () pSkipMany1 :: Parser st a -> Parser st () pSkipMany1 p = p >> pSkipMany p parse :: Parser state a -> state -> Maybe (a, state) parse p s = r2m ((unParser p) s)