File: Common.hs

package info (click to toggle)
haskell-debian 3.64-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 364 kB
  • sloc: haskell: 3,226; ansic: 8; makefile: 3
file content (174 lines) | stat: -rw-r--r-- 6,238 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
-- |A module for parsing, comparing, and (eventually) modifying debian version
-- numbers. <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version>
{-# OPTIONS -fno-warn-orphans -fno-warn-unused-do-bind #-}
module Debian.Version.Common
    (DebianVersion -- |Exported abstract because the internal representation is likely to change 
    , prettyDebianVersion
    , ParseDebianVersion(..)
    , evr		-- DebianVersion -> (Maybe Int, String, Maybe String)
    , epoch
    , version
    , revision
    , buildDebianVersion
    , parseDV
    ) where 

import Data.Char
import Text.ParserCombinators.Parsec
import Text.Regex
import Debian.Version.Internal
import Text.PrettyPrint (Doc, text)

prettyDebianVersion :: DebianVersion -> Doc
prettyDebianVersion (DebianVersion s _) = text s

instance Eq DebianVersion where
    (DebianVersion _ v1) == (DebianVersion _ v2) = v1 == v2

instance Ord DebianVersion where
    compare (DebianVersion _ v1) (DebianVersion _ v2) = compare v1 v2

-- * Ord instance

-- make ~ less than everything, and everything else higher that letters
order :: Char -> Int
order c
    | isDigit c = 0
    | isAlpha c = ord c
    | c == '~' = -1
    | otherwise = (ord c) + 256

-- |We have to do this wackiness because ~ is less than the empty string
compareNonNumeric :: [Char] -> [Char] -> Ordering
compareNonNumeric "" "" = EQ
compareNonNumeric "" ('~':_cs) = GT
compareNonNumeric ('~':_cs) "" = LT
compareNonNumeric "" _ = LT
compareNonNumeric _ "" = GT
compareNonNumeric (c1:cs1) (c2:cs2) =
    if (order c1) == (order c2)
       then compareNonNumeric cs1 cs2
       else compare (order c1) (order c2)

instance Eq NonNumeric where
    (NonNumeric s1 n1) == (NonNumeric s2 n2) =
        case compareNonNumeric s1 s2 of
          EQ -> n1 == n2
          _o -> False

instance Ord NonNumeric where
    compare (NonNumeric s1 n1) (NonNumeric s2 n2) =
        case compareNonNumeric s1 s2 of
          EQ -> compare n1 n2
          o -> o

instance Eq Numeric where
    (Numeric n1 mnn1) == (Numeric n2 mnn2) =
        case compare n1 n2 of
          EQ -> case compareMaybeNonNumeric mnn1 mnn2 of
                  EQ -> True
                  _ -> False
          _ -> False

compareMaybeNonNumeric :: Maybe NonNumeric -> Maybe NonNumeric -> Ordering
compareMaybeNonNumeric mnn1 mnn2 =
    case (mnn1, mnn2) of
      (Nothing, Nothing) -> EQ
      (Just (NonNumeric nn _), Nothing) -> compareNonNumeric nn ""
      (Nothing, Just (NonNumeric nn _)) -> compareNonNumeric "" nn
      (Just nn1, Just nn2) -> compare nn1 nn2

instance Ord Numeric where
    compare (Numeric n1 mnn1) (Numeric n2 mnn2) =
        case compare n1 n2 of
          EQ -> compareMaybeNonNumeric mnn1 mnn2
          o -> o

-- * Parser

class ParseDebianVersion a where
    parseDebianVersion :: a-> DebianVersion
-- |Convert a string to a debian version number. May throw an
-- exception if the string is unparsable -- but I am not sure if that
-- can currently happen. Are there any invalid version strings?
-- Perhaps ones with underscore, or something?

{-
showNN :: NonNumeric -> String
showNN (NonNumeric s n) = s ++ showN n

showN :: Found Numeric -> String
showN (Found (Numeric n nn)) = show n ++ maybe "" showNN nn
showN (Simulated _) = "" 
-}

parseDV :: CharParser () (Found Int, NonNumeric, Found NonNumeric)
parseDV =
    do skipMany $ oneOf " \t"
       e <- parseEpoch 
       upstreamVersion <- parseNonNumeric True True
       debianRevision <- option (Simulated (NonNumeric "" (Simulated (Numeric 0 Nothing)))) (char '-' >> parseNonNumeric True False >>= return . Found)
       return (e, upstreamVersion, debianRevision)

parseEpoch :: CharParser () (Found Int)
parseEpoch =
    option (Simulated 0) (try (many1 digit >>= \d -> char ':' >> return (Found (read d))))
       

parseNonNumeric :: Bool -> Bool -> CharParser () NonNumeric
parseNonNumeric zeroOk upstream =
    do nn <- (if zeroOk then many else many1) ((noneOf "-0123456789") <|> (if upstream then upstreamDash else pzero))
       n <- parseNumeric upstream
       return $ NonNumeric nn n
    where
      upstreamDash :: CharParser () Char
      upstreamDash = try $ do char '-'
                              lookAhead $ (many (noneOf "- \n\t") >> char '-')
                              return '-'

parseNumeric :: Bool -> CharParser () (Found Numeric)
parseNumeric upstream =
    do n <- many1 (satisfy isDigit)
       nn <- option Nothing  (parseNonNumeric False upstream >>= return . Just)
       return $ Found (Numeric (read n) nn)
    <|>
    return (Simulated (Numeric 0 Nothing))

{-
compareTest :: String -> String -> Ordering
compareTest str1 str2 =
    let v1 = either (error . show) id $ parse parseDV str1 str1
        v2 = either (error . show) id $ parse parseDV str2 str2
        in 
          compare v1 v2
-}

-- |Split a DebianVersion into its three components: epoch, version,
-- revision.  It is not safe to use the parsed version number for
-- this because you will lose information, such as leading zeros.
evr :: DebianVersion -> (Maybe Int, String, Maybe String)
evr (DebianVersion s _) =
    let re = mkRegex "^(([0-9]+):)?(([^-]*)|((.*)-([^-]*)))$" in
    --                 (         ) (        (            ))
    --		        (   e  )    (  v  )  (v2) (  r  )
    case matchRegex re s of
      Just ["", _, _, v, "", _, _] -> (Nothing, v, Nothing)
      Just ["", _, _, _, _,  v, r] -> (Nothing, v, Just r)
      Just [_,  e, _, v, "", _, _] -> (Just (read e), v, Nothing)
      Just [_,  e, _, _, _,  v, r] -> (Just (read e), v, Just r)
      -- I really don't think this can happen.
      _ -> error ("Invalid Debian Version String: " ++ s)

epoch :: DebianVersion -> Maybe Int
epoch v = case evr v of (x, _, _) -> x
version :: DebianVersion -> String
version v = case evr v of (_, x, _) -> x
revision :: DebianVersion -> Maybe String
revision v = case evr v of (_, _, x) -> x

-- Build a Debian version number from epoch, version, revision
buildDebianVersion :: Maybe Int -> String -> Maybe String -> DebianVersion
buildDebianVersion e v r =
    either (error . show) (DebianVersion str) $ parse parseDV str str
    where
      str = (maybe "" (\ n -> show n ++ ":") e ++ v ++ maybe "" (\ s -> "-" ++ s) r)