% Copyright (C) 2002-2003 David Roundy % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation; either version 2, or (at your option) % any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program; see the file COPYING. If not, write to % the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, % Boston, MA 02110-1301, USA. \begin{code} module PatchRead ( readPatch, readPatchLazily, gzReadPatchFileLazily ) where import Prelude hiding ( pi ) import Control.Monad ( liftM ) import PatchInfo ( PatchInfo, readPatchInfo ) import FastPackedString ( PackedString, LazyFile(..), gzReadFileLazily ) import FileName ( fn2fp ) import PatchCore ( Patch(..), DirPatchType(..), FilePatchType(..), invert, hunk, binary ) import PatchCommute ( merger ) import PatchReadMonads (Stringalike(..), ParserM, work, maybe_work, alter_input, parse_lazily, parse_strictly, peek_input, lex_char, lex_string, lex_eof, my_lex) #include "impossible.h" \end{code} \begin{code} readPatch :: Stringalike s => s -> Maybe (Patch, s) readPatch ps = case parse_strictly (readPatch' False) ps of Just (Just p, ps') -> Just (p, ps') _ -> Nothing gzReadPatchFileLazily :: FilePath -> IO Patch gzReadPatchFileLazily fp = do lf <- gzReadFileLazily fp return $ case lf of LazyString s -> fst $ readPatchLazily' True s MMappedPackedString ps -> fst $ readPatchLazily' False ps LazyPackedStrings pss -> fst $ readPatchLazily' True pss readPatchLazily :: Stringalike s => s -> (Patch, s) readPatchLazily = readPatchLazily' False readPatchLazily' :: Stringalike s => Bool -> s -> (Patch, s) readPatchLazily' want_eof ps = case parse_lazily (readPatch' want_eof) ps of (Just p, ps') -> (p, ps') _ -> impossible readPatch' :: (Stringalike s, ParserM m, Monad (m s)) => Bool -> m s (Maybe Patch) readPatch' want_eof = do s <- peek_input case liftM (sal_to_string . fst) $ my_lex s of Just "{" -> liftM Just $ readComP want_eof -- } Just "(" -> liftM Just $ readSplit -- ) Just "hunk" -> liftM Just $ readHunk Just "replace" -> liftM Just $ readTok Just "binary" -> liftM Just $ readBinary Just "addfile" -> liftM Just $ readAddFile Just "adddir" -> liftM Just $ readAddDir Just "rmfile" -> liftM Just $ readRmFile Just "rmdir" -> liftM Just $ readRmDir Just "move" -> liftM Just $ readMove Just "changepref" -> liftM Just $ readChangePref Just "merger" -> liftM Just $ readMerger True Just "regrem" -> liftM Just $ readMerger False Just "conflict" -> liftM Just $ readConflictor Just "tcilfnoc" -> liftM Just $ readInvConflictor Just ('[':_) -> liftM Just $ readNamed want_eof -- ] _ -> return Nothing \end{code} \begin{code} readComP :: (Stringalike s, ParserM m, Monad (m s)) => Bool -> m s Patch readComP want_eof = do work my_lex ps <- read_patches "}" want_eof return $ ComP ps read_patches :: (Stringalike s, ParserM m, Monad (m s)) => String -> Bool -> m s [Patch] read_patches str want_eof = do mp <- readPatch' False case mp of Nothing -> do unit <- lex_string str case unit of () -> if want_eof then do unit' <- lex_eof case unit' of () -> return [] else return [] Just p -> do ps <- read_patches str want_eof return (p:ps) \end{code} \begin{code} readSplit :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readSplit = do work my_lex ps <- read_patches ")" False return $ Split ps \end{code} \begin{code} readHunk :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readHunk = do work my_lex fi <- work my_lex l <- work sal_readInt alter_input sal_tail -- skipping the newline... work $ lines_starting_with ' ' -- skipping context old <- work $ lines_starting_with '-' new <- work $ lines_starting_with '+' work $ lines_starting_with ' ' -- skipping context return $ hunk (fn2fp $ sal_to_fn fi) l old new \end{code} \begin{code} readTok :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readTok = do work my_lex f <- work my_lex regstr <- work my_lex o <- work my_lex n <- work my_lex return $ FP (sal_to_fn f) $ TokReplace (drop_brackets $ sal_to_string regstr) (sal_to_string o) (sal_to_string n) where drop_brackets = init . tail \end{code} \paragraph{Binary file modification} Modify a binary file \begin{verbatim} binary FILENAME oldhex *HEXHEXHEX ... newhex *HEXHEXHEX ... \end{verbatim} \begin{code} readBinary :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readBinary = do work my_lex fi <- work my_lex work my_lex alter_input sal_dropWhite old <- work $ lines_starting_with '*' work my_lex alter_input sal_dropWhite new <- work $ lines_starting_with '*' return $ binary (fn2fp $ sal_to_fn fi) (sal_to_PS $ sal_fromHex $ sal_concat old) (sal_to_PS $ sal_fromHex $ sal_concat new) \end{code} \begin{code} readAddFile :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readAddFile = do work my_lex f <- work my_lex return $ FP (sal_to_fn f) AddFile \end{code} \begin{code} readRmFile :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readRmFile = do work my_lex f <- work my_lex return $ FP (sal_to_fn f) RmFile \end{code} \begin{code} readMove :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readMove = do work my_lex d <- work my_lex d' <- work my_lex return $ Move (sal_to_fn d) (sal_to_fn d') \end{code} \begin{code} readChangePref :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readChangePref = do work my_lex p <- work my_lex f <- work (Just . sal_breakOn '\n' . sal_tail . sal_dropWhile (== ' ')) t <- work (Just . sal_breakOn '\n' . sal_tail) alter_input sal_tail return $ ChangePref (sal_to_string p) (sal_to_string f) (sal_to_string t) \end{code} \begin{code} readAddDir :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readAddDir = do work my_lex f <- work my_lex return $ DP (sal_to_fn f) AddDir \end{code} \begin{code} readRmDir :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readRmDir = do work my_lex f <- work my_lex return $ DP (sal_to_fn f) RmDir \end{code} \begin{code} readMerger :: (Stringalike s, ParserM m, Monad (m s)) => Bool -> m s Patch readMerger b = do work my_lex g <- work my_lex lex_char '(' Just p1 <- readPatch' False Just p2 <- readPatch' False lex_char ')' let m = merger (sal_to_string g) p1 p2 return $ if b then m else invert m \end{code} \begin{code} readConflictor :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readConflictor = do work my_lex b <- read_patches "with" False a <- read_patches "done_conflict" False return $ Conflictor False a b readInvConflictor :: (Stringalike s, ParserM m, Monad (m s)) => m s Patch readInvConflictor = do work my_lex b <- read_patches "with" False a <- read_patches "done_tcilfnoc" False return $ Conflictor True a b \end{code} \begin{code} readNamed :: (Stringalike s, ParserM m, Monad (m s)) => Bool -> m s Patch readNamed want_eof = do mn <- maybe_work readPatchInfo case mn of Nothing -> bug "readNamed 1" Just n -> do d <- read_depends Just p <- readPatch' want_eof return $ NamedP n d p read_depends :: (Stringalike s, ParserM m, Monad (m s)) => m s [PatchInfo] read_depends = do s <- peek_input case my_lex s of Just (xs, _) | sal_to_string xs == "<" -> do work my_lex read_pis _ -> return [] read_pis :: (Stringalike s, ParserM m, Monad (m s)) => m s [PatchInfo] read_pis = do mpi <- maybe_work readPatchInfo case mpi of Just pi -> do pis <- read_pis return (pi:pis) Nothing -> do alter_input (sal_tail . sal_dropWhile (/= '>')) return [] \end{code} \begin{code} lines_starting_with :: Stringalike s => Char -> s -> Maybe ([PackedString], s) lines_starting_with c thes = Just (lsw [] thes) where lsw acc s | sal_null s || sal_head s /= c = (reverse acc, s) lsw acc s = let s' = sal_tail s in case sal_breakFirst '\n' s' of Just (l, r) -> lsw (sal_to_PS l:acc) r Nothing -> (reverse (sal_to_PS s':acc), sal_empty) \end{code}