% 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; if not, write to the Free Software Foundation, % Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \begin{code} module PatchInfo ( PatchInfo, patchinfo, invert_name, is_inverted, make_filename, make_alt_filename, readPatchInfoPS, just_name, repopatchinfo, RepoPatchInfo, human_friendly, to_xml, pi_date, set_pi_date, ) where import Time import Text.Html hiding (name) import FastPackedString import IsoDate ( cleanDate, readDate ) import System.Time ( CalendarTime, calendarTimeToString, toClockTime, toCalendarTime ) import List (isPrefixOf) import System.IO.Unsafe ( unsafePerformIO ) import SHA1 ( sha1PS ) import Prelude hiding (pi, log) data RepoPatchInfo = RPI String PatchInfo repopatchinfo :: String -> PatchInfo -> RepoPatchInfo repopatchinfo r pi = RPI r pi data PatchInfo = PatchInfo !PackedString !PackedString !PackedString ![PackedString] !Bool deriving (Eq,Ord) patchinfo :: String -> String -> String -> [String] -> PatchInfo patchinfo date name author log = PatchInfo (packString date) (packString name) (packString author) (map packString log) False \end{code} \section{Patch info formatting} \begin{code} invert_name :: PatchInfo -> PatchInfo invert_name (PatchInfo d n a l inv) = PatchInfo d n a l (not inv) is_inverted :: PatchInfo -> Bool is_inverted (PatchInfo _ _ _ _ inv) = inv \end{code} \begin{code} just_name :: PatchInfo -> String just_name (PatchInfo _ n _ _ _) = unpackPS n human_friendly :: PatchInfo -> String human_friendly (PatchInfo d n a l inv) = friendly_d d ++ " " ++ unpackPS a ++ "\n" ++ hfn (unpackPS n) ++ "\n" ++ unlines (map ((" "++).unpackPS) l) where hfn x = if "TAG " `isPrefixOf` x then " tagged "++drop 4 x else inverted++x inverted = if inv then " UNDO: " else " * " pi_date :: PatchInfo -> CalendarTime pi_date (PatchInfo d _ _ _ _) = readDate $ unpackPS d set_pi_date :: String -> PatchInfo -> PatchInfo set_pi_date date (PatchInfo _ a b c d) = PatchInfo (packString date) a b c d friendly_d :: PackedString -> String --friendly_d d = calendarTimeToString . readDate . unpackPS . d friendly_d d = unsafePerformIO $ do ct <- toCalendarTime $ toClockTime $ readDate $ unpackPS d return $ calendarTimeToString ct \end{code} \begin{code} to_xml :: PatchInfo -> String to_xml pi@(PatchInfo date patch_name author comments inverted) = "\n\t" ++ escapeXML (unpackPS patch_name) ++ "" ++ comments_as_xml comments ++ "\n" comments_as_xml :: [PackedString] -> String comments_as_xml comments | lengthPS comments' > 0 = "\n\t" ++ escapeXML (unpackPS comments') ++ "" | otherwise = "" where comments' = unlinesPS comments escapeXML :: String -> String escapeXML = strReplace '\'' "'" . strReplace '"' """ . strReplace '<' "<" . strReplace '&' "&" strReplace :: Char -> String -> String -> String strReplace _ _ [] = [] strReplace x y (z:zs) | x == z = y ++ (strReplace x y zs) | otherwise = z : (strReplace x y zs) \end{code} \begin{code} make_alt_filename :: PatchInfo -> String make_alt_filename (PatchInfo d n a _ False) = fix_up_fname (midtrunc (unpackPS n)++"-"++unpackPS a++"-"++unpackPS d) make_alt_filename (PatchInfo d n a l True) = make_alt_filename (PatchInfo d n a l False) ++ "-inverted" make_filename :: PatchInfo -> String make_filename (PatchInfo dps nps aps lps inv) = cleanDate d++"-"++sha1_a++"-"++sha1PS sha1_me++".gz" where b2ps True = packString "t" b2ps False = packString "f" sha1_me = concatPS [nps, aps, dps, concatPS lps, b2ps inv] d = unpackPS dps sha1_a = take 5 $ sha1PS aps midtrunc :: String -> String midtrunc s | length s < 73 = s | otherwise = (take 40 s)++"..."++(reverse $ take 30 $ reverse s) fix_up_fname :: String -> String fix_up_fname = map munge_char munge_char :: Char -> Char munge_char '*' = '+' munge_char '?' = '2' munge_char '>' = '7' munge_char '<' = '2' munge_char ' ' = '_' munge_char '"' = '~' munge_char '`' = '.' munge_char '\'' = '.' munge_char '/' = '1' munge_char '\\' = '1' munge_char '!' = '1' munge_char ':' = '.' munge_char ';' = ',' munge_char '{' = '~' munge_char '}' = '~' munge_char '(' = '~' munge_char ')' = '~' munge_char '[' = '~' munge_char ']' = '~' munge_char '=' = '+' munge_char '#' = '+' munge_char '%' = '8' munge_char '&' = '6' munge_char '@' = '9' munge_char '|' = '1' munge_char c = c \end{code} \begin{code} instance HTML RepoPatchInfo where toHtml = htmlPatchInfo instance Show PatchInfo where show = showPatchInfo \end{code} \paragraph{Patch info} Patch is stored between square brackets. \begin{verbatim} [ * (indented one) ] \end{verbatim} \begin{code} -- note that below I assume the name has no newline in it. showPatchInfo :: PatchInfo -> String showPatchInfo (PatchInfo ct name author log inv) = "[" ++ unpackPS name ++"\n"++ unpackPS author ++ inverted ++ unpackPS ct ++ myunlines log ++ "] " where inverted = if inv then "*-" else "**" myunlines [] = "" myunlines xs = mul xs where mul [] = "\n" mul (s:ss) = "\n "++unpackPS s++mul ss readPatchInfoPS :: PackedString -> Maybe (PatchInfo,PackedString) readPatchInfoPS s | nullPS (dropWhitePS s) = Nothing readPatchInfoPS s = if headPS (dropWhitePS s) /= '[' -- ] then Nothing else case breakOnPS '\n' $ tailPS $ dropWhitePS s of (name,s') -> case breakOnPS '*' $ tailPS s' of (author,s2) -> case breakPS (\c->c==']'||c=='\n') $ dropPS 2 s2 of (ct,s''') -> case lines_starting_with_ending_withPS ' ' ']' $ dnPS s''' of Just (log, s4) -> if indexPS s2 1 == '*' then Just (PatchInfo ct name author log False, s4) else Just (PatchInfo ct name author log True, s4) Nothing -> error $ "Error parsing patchinfo:\n"++ unlines (map show $ lines $ unpackPS $ takePS 480 s) where dnPS x = if nullPS x || headPS x /= '\n' then x else tailPS x \end{code} \begin{code} lines_starting_with_ending_withPS :: Char -> Char -> PackedString -> Maybe ([PackedString],PackedString) lines_starting_with_ending_withPS st en s = lswew s where lswew x | nullPS x = Nothing lswew x = if headPS x == en then Just ([], tailPS x) else if headPS x /= st then Nothing else case breakOnPS '\n' $ tailPS x of (l,r) -> case lswew $ tailPS r of Just (ls,r') -> Just (l:ls,r') Nothing -> case breakLastPS en l of Just (l2,_) -> Just ([l2], dropPS (lengthPS l2+2) x) Nothing -> Nothing \end{code} \begin{code} htmlPatchInfo :: RepoPatchInfo -> Html htmlPatchInfo (RPI r pi@(PatchInfo ct _ author _ _)) = toHtml $ (td << patch_link r pi) `above` ((td ! [align "right"] << mail_link (unpackPS author)) `beside` (td << (friendly_d ct))) patch_link :: String -> PatchInfo -> Html patch_link r pi@(PatchInfo _ name _ _ _) = toHtml $ hotlink ("darcs?"++r++"**"++make_filename pi) [toHtml $ unpackPS name] mail_link :: String -> Html mail_link email = toHtml $ hotlink ("mailto:"++email) [toHtml email] \end{code}