packages feed

debian-1.0: Linspire/Debian/ChangeLog.hs

-- |Module for handling the Debian changelog file format
-- <http://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog>
--
-- Author: David Fox
{-
   |package (version) distribution(s); urgency=urgency
   | 	    [optional blank line(s), stripped]
   |  * change details
   |     more change details
   | 	    [blank line(s), included in output of dpkg-parsechangelog]
   |  * even more change details
   | 	    [optional blank line(s), stripped]
   | -- maintainer name <email address>[two spaces]  date
-}
module Linspire.Debian.ChangeLog
    (ChangeLogEntry(Entry),
     -- * Accessors
     package,
     Linspire.Debian.ChangeLog.version,
     dists,
     urgency,
     comments,
     who,
     date,
     -- * read, show
     parse,			-- String -> [ChangeLogEntry]
     showHeader,		-- ChangeLogEntry -> String
     -- * Helper
     getRFC822Date		-- IO String
    ) where

import Data.List
import Data.Maybe
import Linspire.Debian.Version
import System.IO
import System.Process
import Text.Regex

-- |A changelog is a series of ChangeLogEntries
data ChangeLogEntry = Entry {package :: String,		-- Package name
                             version :: DebianVersion,	-- Version number
                             dists :: [String],		-- distributions
                             urgency :: String,		-- urgency
                             comments :: String,	-- comments
                             who :: String,		-- who
                             date :: String}		-- date

instance Show ChangeLogEntry where
    show (Entry package version dists urgency details who date) =
        package ++ " (" ++ show version ++ ") " ++ consperse " " dists ++ "; urgency=" ++ urgency ++ "\n\n" ++
             details ++ "\n\n -- " ++ who ++ "  " ++ date ++ "\n\n"

-- |Show just the top line of a changelog entry (for debugging output.)
showHeader :: ChangeLogEntry -> String
showHeader (Entry package version dists urgency _ _ _) =
    package ++ " (" ++ show version ++ ") " ++ consperse " " dists ++ "; urgency=" ++ urgency ++ "..."

-- |Parse a Debian Changelog and return a list of entries
parse :: String -> [ChangeLogEntry]
parse text =
    case matchRegexAll entryRE text of
      Nothing -> []
      Just (_, _, remaining, submatches) ->
          case submatches of
            [name, version, dists, urgency, _, _, details, _, _, _, _, who, date, _] ->
                let entry =
                        Entry name 
                              (parseDebianVersion version)
                              (words dists)
                              urgency
			      details
                              who
                              date in
                entry : parse remaining
            _ -> error "Parse error in changelog"
    where
      entryRE = mkRegex (header ++ blankLines ++ details ++ blankLines ++ signature)

      header = package ++ version ++ dists ++ urgency

      package = "^([^ \t(]*)" ++ optWhite
      version = "\\(([^)]*)\\)" ++ optWhite
      dists = "([^;]*);" ++ optWhite
      urgency = "urgency=([^\n]*)\n" ++ blankLines
      details = "(" ++ prefixedLine ++ "(" ++ blankLines ++ prefixedLine ++ ")*)"
      signature = "( -- ([^\n]*)  (...............................))[ \t]*\n" ++ blankLines

      prefixedLine = "  [^\n]*\n"
      blankLines = "(" ++ optWhite ++ "\n)*"
      optWhite = "[ \t]*"

-- FIXME: The %z formatting directive (rfc-822 conformant timezone)
-- isn't working:
-- date = getClockTime >>= toCalendarTime >>=
--   return . formatCalendarTime defaultTimeLocale "%a, %d %b %Y %T %z"
getRFC822Date :: IO String
getRFC822Date =
    do (_, stdout, _, handle) <- runInteractiveCommand "date -R"
       date <- hGetContents stdout >>= return . lines >>= return . listToMaybe
       waitForProcess handle
       return $ maybe (error "Error running 'date -R'") id date

-- |The mighty consperse function
consperse :: [a] -> [[a]] -> [a]
consperse sep items = concat (intersperse sep items)