module Debian.Changes
( ChangesFile(..)
, ChangedFileSpec(..)
, changesFileName
, ChangeLogEntry(..)
, parseLog
, parseEntry
, parseChanges
) where
import Data.List (intercalate)
import Data.Maybe
import qualified Debian.Control.String as S
import Debian.Release
import Debian.URI()
import Debian.Version
import System.Posix.Types
import Text.Regex
data ChangesFile =
Changes { changeDir :: FilePath
, changePackage :: String
, changeVersion :: DebianVersion
, changeRelease :: ReleaseName
, changeArch :: Arch
, changeInfo :: S.Paragraph
, changeEntry :: ChangeLogEntry
, changeFiles :: [ChangedFileSpec]
}
data ChangedFileSpec =
ChangedFileSpec { changedFileMD5sum :: String
, changedFileSHA1sum :: String
, changedFileSHA256sum :: String
, changedFileSize :: FileOffset
, changedFileSection :: SubSection
, changedFilePriority :: String
, changedFileName :: FilePath
}
data ChangeLogEntry = Entry { logPackage :: String
, logVersion :: DebianVersion
, logDists :: [ReleaseName]
, logUrgency :: String
, logComments :: String
, logWho :: String
, logDate :: String
}
instance Show ChangesFile where
show = changesFileName
changesFileName :: ChangesFile -> String
changesFileName changes =
changePackage changes ++ "_" ++ show (changeVersion changes) ++ "_" ++ archName (changeArch changes) ++ ".changes"
instance Show ChangedFileSpec where
show file = changedFileMD5sum file ++ " " ++
show (changedFileSize file) ++ " " ++
sectionName (changedFileSection file) ++ " " ++
changedFilePriority file ++ " " ++
changedFileName file
instance Show ChangeLogEntry where
show (Entry package version dists urgency details who date) =
package ++ " (" ++ show version ++ ") " ++ intercalate " " (map releaseName' dists) ++ "; urgency=" ++ urgency ++ "\n\n" ++
details ++ " -- " ++ who ++ " " ++ date ++ "\n\n"
showHeader :: ChangeLogEntry -> String
showHeader (Entry package version dists urgency _ _ _) =
package ++ " (" ++ show version ++ ") " ++ intercalate " " (map releaseName' dists) ++ "; urgency=" ++ urgency ++ "..."
parseLog :: String -> [Either String ChangeLogEntry]
parseLog text =
case parseEntry text of
Nothing -> []
Just (Left message) -> [Left message]
Just (Right (entry, text')) -> Right entry : parseLog text'
parseEntry :: String -> Maybe (Either String (ChangeLogEntry, String))
parseEntry text | dropWhile (\ x -> elem x " \t\n") text == "" = Nothing
parseEntry text =
case matchRegexAll entryRE text of
Nothing -> Just (Left ("Parse error in changelog:\n" ++ text))
Just ("", _, remaining, [_, name, version, dists, urgency, _, details, _, _, _, _, _, who, date, _]) ->
let entry =
Entry name
(parseDebianVersion version)
(map parseReleaseName . words $ dists)
urgency
details
who
date in
Just (Right (entry, remaining))
Just ("", _, _remaining, submatches) -> Just (Left ("Internal error 15, submatches=" ++ show submatches))
Just (before, _, _, _) -> Just (Left ("Parse error in changelog at:\n" ++ show before ++ "\nin:\n" ++ text))
where
entryRE = mkRegex $ bol ++ blankLines ++ headerRE ++ nonSigLines ++ blankLines ++ signature ++ blankLines
nonSigLines = "((( .*)|([ \t]*)\n)+)"
signature = "( -- ([^\n]*) (..., ? ?.. ... .... ........ .....))[ \t]*\n"
parseChanges :: String -> Maybe ChangeLogEntry
parseChanges text =
case matchRegex changesRE text of
Nothing -> Nothing
Just [_, name, version, dists, urgency, _, details] ->
Just $ Entry name
(parseDebianVersion version)
(map parseReleaseName . words $ dists)
urgency
details
"" ""
Just x -> error $ "Unexpected match: " ++ show x
where
changesRE = mkRegexWithOpts (bol ++ blankLines ++ optWhite ++ headerRE ++ "(.*)$") False False
headerRE =
package ++ version ++ dists ++ urgency
where
package = "([^ \t(]*)" ++ optWhite
version = "\\(([^)]*)\\)" ++ optWhite
dists = "([^;]*);" ++ optWhite
urgency = "urgency=([^\n]*)\n" ++ blankLines
blankLines = blankLine ++ "*"
blankLine = "(" ++ optWhite ++ "\n)"
optWhite = "[ \t]*"
bol = "^"