3
This program converts the Build-Essential (bess) list into a form
4
suitable for use in a Debian Depends control field.
6
> module Main (main) where
8
Due to problems with the Emacs Haskell major mode (see Bug#46115), the
9
string constants that include a certain magic word are imported from
17
The list we are parsing is quite free-form. Everything up to a
18
certain line (beginConstant) and everything after another certain line
19
(endConstant) are ignored, as is everything that is indented. The
20
function bessDeComment filters out these comments; deComment weeds out
21
indented lines and stripSurroundings removes the head and the tail.
23
> bessDeComment :: String -> String
24
> bessDeComment = unlines . deComment . stripSurroundings . lines
25
> where deComment :: [String] -> [String]
26
> deComment = filter (not . isComment)
27
> where isComment :: String -> Bool
29
> isComment (c:_) = c == ' ' || c == '\t'
30
> stripSurroundings :: [String] -> [String]
31
> stripSurroundings = stripTail . stripHead
32
> stripHead :: [String] -> [String]
33
> stripHead = tail . dropWhile (/= beginConstant)
34
> stripTail = takeWhile (/= endConstant)
36
The list contains entries that are meant for only some Debian
37
architectures. filterByArchitecture assumes de-commented input and
38
outputs the input without those parts that are not meant for the
39
current architecture (which is given as the first argument to this
42
> filterByArchitecture :: String -> String -> String
43
> filterByArchitecture arch = listToAlts . catMaybes .
44
> filterAltsByArch arch . altsToList
45
> where listToAlts [] = ""
46
> listToAlts s = join " | " s
47
> altsToList = split '|'
49
Here we continue filtering out unwanted architectures.
50
filterAltsByArch assumes that the input is in the format used for the
51
Build-Depends control file field, with no commas or vertical lines.
52
This function is given a list of package-version-architecture clauses,
53
and it returns a list. An element of the return value is either
54
Nothing, if the clause was not for this arch, or the clause without
55
the arch spec, lifted to Just, if it was for this arch.
57
> filterAltsByArch :: String -> [String] -> [Maybe String]
58
> filterAltsByArch arch ss = map (checkArch arch . parseAlt) ss
60
parseAlt takes a string of the form "package (>> version) [i386 m68k]"
61
and parses it into a triplet ("package", ">> version", ["i386",
64
> parseAlt :: String -> (String, String, [String])
65
> parseAlt s = let (pkg, rest) = getPkg . dropWSP $ s
66
> (vers, rest') = getVer . dropWSP $ rest
67
> (arch, rest'') = getArch . dropWSP $ rest'
68
> in (pkg, vers, arch)
70
dropWSP is what many people call "left strip" or "lstrip": it removes
71
all whitespace from the beginning of the string, so that the first
72
character of the result is not whitespace.
74
> dropWSP :: String -> String
75
> dropWSP = dropWhile (`elem` " \t\n")
77
getPkg parses "package ..." into ("package", " ..."), returning a pair
78
whose left element is the package name and the right element is what
79
remains of the string after the parse.
81
> getPkg :: String -> (String, String)
82
> getPkg = span (\c -> c /= ' ' && c /= '\t')
84
getVer parses "(>> version) ..." into (">> version", " ..."). If
85
there is no parenthesis at the beginning of the argument, the first
86
element of the return pair is empty and the second is the argument in
89
> getVer :: String -> (String, String)
90
> getVer ('(':s) = let (f, r) = span (/= ')') s
92
> getVer s@(_:_) = ("", s)
93
> getVer "" = ("", "")
95
getArch parses an arch spec "[i386 !m68k] ..." into a pair (["i386",
96
"!m68k"], " ...") whose first argument is a list of the archs
97
mentioned in the spec. The rest of the string is again returned in
98
the second element of the pair.
100
> getArch :: String -> ([String], String)
101
> getArch ('[':ss) = let (f, r) = span (/= ']') ss
102
> rv = (filter (not . onlySpace) . split ' ' $ f, tail r)
104
> where onlySpace [] = True
105
> onlySpace (' ':xs) = onlySpace xs
106
> onlySpace ('\t':xs) = onlySpace xs
107
> onlySpace (_:_) = False
108
> getArch s@(_:_) = ([], s)
109
> getArch "" = ([], "")
111
checkArch takes two arguments: the first is a string, containing the
112
name of the current architecture; the second is a triplet produced by
113
parseAlt. The result is Nothing, if the pair is not meant for the
114
current architecture. Otherwise the result is Just string, where
115
string is a string representation of the triplet without the
116
architecture spec. The logic that determines this is contained in
117
four intermediate results:
119
- isInList is true iff current architecture is in the list without
122
- isInListWithBang is true iff current architecture is in the list
125
- hasbang is true iff there is a bang in the version spec
127
- test is what determines whether the return value is Just or Nothing.
129
> checkArch :: String -> (String, String, [String]) -> Maybe String
130
> checkArch _ t@(_,_,[]) = Just (stringify t)
131
> checkArch arch t@(_,_,a)
132
> = let isInList = arch `elem` a
133
> isInListWithBang = ('!' : arch) `elem` a
134
> hasbang = hasBang a
135
> test = (not isInList && not hasbang) || isInListWithBang
138
> else Just (stringify t)
140
stringify converts the triplet ("A", "B", ["C", "D"]) into the string
141
"A (B)", thereby converting the relationship spec into a string form.
143
> stringify :: (String, String, [String]) -> String
144
> stringify (p, "", _) = p
145
> stringify (p, v, _) = p ++ " (" ++ v ++ ")"
147
hasBang returns true if the argument list contains a string
148
that starts with a bang.
150
> hasBang :: [String] -> Bool
152
> hasBang (('!':_):_) = True
153
> hasBang ((_:_):ss) = hasBang ss
155
checkArgs tests whether the command line argument list given is in the
156
correct format. If not, we bail out.
158
> checkArgs :: [String] -> IO ()
159
> checkArgs [] = fail "Too few arguments"
160
> checkArgs [x] = return () -- ok
161
> checkArgs (_:_:_) = fail "Too many arguments"
163
Here's the main program. We parse arguments, read in the input from
164
stdin and then print out the result, which is in the normal Depends
167
> main = do args <- getArgs
169
> cont <- getContents
170
> putStrLn . (join ", ") . filter (/= "") .
171
> map (filterByArchitecture (head args)) .
172
> lines . bessDeComment $ cont
176
split and join are utility functions that really should be in some
177
library. split is a generalization of the standard filter "lines",
178
allowing one to specify what is used as the line separator. join is
179
its conceptual inverse.
181
> join :: String -> [String] -> String
184
> join is (s:ss@(_:_)) = s ++ join' is ss
185
> where join' is = foldl (\a b -> a ++ is ++ b) []
187
> split :: Char -> String -> [String]
189
> split c s = let (l, s') = break (== c) s
192
> (_:s'') -> split c s''