File: update-haskell-control.lhs.in

package info (click to toggle)
haskell-utils 1.6
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 104 kB
  • ctags: 13
  • sloc: haskell: 411; makefile: 87
file content (223 lines) | stat: -rw-r--r-- 9,361 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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

Written by Ian Lynagh <igloo@debian.org>.
Copyright (C) 2003, 2004 Ian Lynagh.
Released under the GNU GPL version 2.

\begin{code}
module Main (main) where

import Control.Monad (when, unless)
import Data.Char (isAlphaNum, isAlpha)
import System.Directory (doesFileExist)
import System.Environment (getArgs)
import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith)
import System.IO (hPutStrLn, stderr)
#ifdef COMPILERnhc98
import RE (match)
#else
import Text.Regex (mkRegex, matchRegex)
#endif

type Mapping = [(String, String)]
data Pos = Pos !Int !Int

data Verbosity = Quiet | Normal | Verbose
    deriving Eq
data Action = Update | Check
data Flags = Flags { action :: Action,
                     verbosity :: Verbosity,
                     input_file :: Maybe FilePath,
                     output_file :: Maybe FilePath,
                     include_paths :: [FilePath] }
           | HelpFlag
           | VersionFlag
           | ErrorFlag String

show_pos :: Pos -> String
show_pos (Pos c l) = "line " ++ show l ++ ", character " ++ show c

parse_args :: [String] -> Flags
parse_args = pa (Flags Update Normal Nothing Nothing ["@libdir@/haskell-utils"])
    where pa fs []
           = let fs1 = case input_file fs of
                           Nothing ->
                               fs { input_file = Just "debian/control.in" }
                           _ -> fs
                 fs2 = case (input_file fs1, output_file fs1) of
                       (Just i, Nothing) ->
                         case break ('.' ==) (reverse i) of
                         ("ni", '.':rfn) ->
                             fs1 { output_file = Just (reverse rfn) }
                         _ -> ErrorFlag "Output filename not given or deducable"
                       _ -> fs1
             in fs2
          pa _ ("--help":_) = HelpFlag
          pa _ ("-h":_) = HelpFlag
          pa _ ("--version":_) = VersionFlag
          pa _ ("-V":_) = VersionFlag
          pa fs ("--update":as) = pa (fs { action = Update }) as
          pa fs ("--check":as) = pa (fs { action = Check }) as
          pa fs ("-v":as) = pa (fs { verbosity = Verbose }) as
          pa fs ("-q":as) = pa (fs { verbosity = Quiet }) as
          pa fs ("-I":d:as) = pa (fs { include_paths = d:include_paths fs }) as
          pa _ ["-I"] = ErrorFlag "Missing argument to -I"
          pa fs ("-i":f:as) = case input_file fs of
                                  Nothing -> pa (fs { input_file = Just f }) as
                                  Just _ -> ErrorFlag "Two input files given"
          pa _ ["-i"] = ErrorFlag "Missing argument to -i"
          pa fs ("-o":f:as) = case output_file fs of
                                  Nothing -> pa (fs { output_file = Just f }) as
                                  Just _ -> ErrorFlag "Two output files given"
          pa _ ["-o"] = ErrorFlag "Missing argument to -o"
          pa _ (a:_) = ErrorFlag ("Unknown argument: " ++ a)

main :: IO ()
main = do args <- getArgs
          case parse_args args of
              ErrorFlag s -> do hPutStrLn stderr s
                                exitWith (ExitFailure 1)
              HelpFlag -> usage
              VersionFlag -> show_version
              Flags Update v (Just i) (Just o) ps -> update v i o ps
              Flags Check  v (Just i) (Just o) _  -> check  v i o
              _ -> error "update-haskell-control: Can't happen"

check :: Verbosity -> FilePath -> FilePath -> IO ()
check v i o
 = do when (v == Verbose) $ do putStrLn $ "Input filename: " ++ i
                               putStrLn $ "Check filename: " ++ o
      inp <- readFile i
      out <- readFile o
      let inp' = number inp
          (re, warnings) = mk_regexp inp'
#ifndef COMPILERnhc98
          re' = mkRegex re
#endif
      unless (v == Quiet) $ mapM_ putStrLn warnings
      when (v == Verbose) $ do putStrLn "Regexp:"
                               putStrLn re
#ifdef COMPILERnhc98
      case match re out of
#else
      case matchRegex re' out of
#endif
          Nothing -> do putStrLn "File mismatch!"
                        exitWith (ExitFailure 1)
          _ -> exitWith ExitSuccess

update :: Verbosity -> FilePath -> FilePath -> [FilePath] -> IO ()
update v i o ps
 = do when (v == Verbose) $ do putStrLn $ "Input filename: " ++ i
                               putStrLn $ "Output filename: " ++ o
                               putStrLn $ "Search path:"
                               mapM_ (putStrLn . ("  " ++)) ps
      inp <- readFile i
      mss <- mapM (get_varmappings v ps) ["ghc6", "ghc5", "nhc98", "hugs"]
      let ms = concat mss
      let inp' = number inp
          (inp'', warnings) = apply ms inp'
      unless (v == Quiet) $ mapM_ putStrLn warnings
      writeFile o inp''

get_varmappings :: Verbosity -> [FilePath] -> FilePath -> IO Mapping
get_varmappings v [] f
 = do unless (v == Quiet) $ putStrLn $ f ++ " varfile not found"
      return []
get_varmappings v (p:ps) f
 = do let f' = p ++ "/" ++ f ++ "_vars"
      exists <- doesFileExist f'
      if exists
        then do when (v == Verbose) $ putStrLn $ "Loading " ++ f'
                xs <- readFile f'
                let mes = map mk_maplet $ zip [1..] $ filter ("" /=) $ lines xs
                    es = [ e | Right e <- mes ]
                    ms = [ m | Left m <- mes ]
                if null es
                  then do return ms
                  else do mapM_ (hPutStrLn stderr) es
                          exitWith (ExitFailure 1)
        else do when (v == Verbose) $ putStrLn $ f' ++ " not found"
                get_varmappings v ps f

mk_maplet :: (Int, String) -> Either (String, String) String
mk_maplet (n, xs) = case break ('=' ==) xs of
                        ("", _) -> Right $ "No variable name on line " ++ s
                        (ys, '=':'"':zs) -> case read_val "" zs of
                                                Left zs' -> Left (ys, zs')
                                                Right err -> Right err
                        _ -> Right bvb
    where s = show n
          read_val acc "\"" = Left (reverse acc)
          read_val _   [] = Right bvb
          read_val _   [_] = Right bvb
          read_val acc ('\\':'n':ys) = read_val ('\n':acc) ys
          read_val acc ('\\':y:ys) = read_val (y:acc) ys
          read_val acc (y:ys) = read_val (y:acc) ys
          bvb = "Bad variable binding on line " ++ s

number :: String -> [(Char, Pos)]
number = f (Pos 1 1)
    where f _ "" = []
          f p@(Pos _ l) ('\n':xs) = seq p $ ('\n', p):f (Pos 1 (l+1)) xs
          f p@(Pos c l) (x:xs) = seq p $ (x, p):f (Pos (c+1) l) xs

apply :: Mapping -> [(Char, Pos)] -> (String, [String])
apply m = f "" []
    where f res ws [] = (reverse res, reverse ws)
          f res ws (('\\', _):(x, _):xs) = f (x:res) ws xs
          f res ws [('$', p)] = f ('$':res) (bvs p:ws) []
          f res ws (('$', p):xs)
           = case get_var_name xs of
                 Nothing -> f ('$':res) (bvs p:ws) xs
                 Just (n, xs') -> case lookup n m of
                                      Nothing -> f ('$':res) (uv n p:ws) xs
                                      Just v ->
                                          f res ws (map (\c -> (c, p)) v ++ xs')
          f res ws ((x, _):xs) = f (x:res) ws xs
          bvs p = "Bad variable spec at " ++ show_pos p
          uv v p = "Unbound variable " ++ v ++ " at " ++ show_pos p

mk_regexp :: [(Char, Pos)] -> (String, [String])
mk_regexp = f "" []
    where f res ws [] = ("^" ++ reverse res ++ "$", reverse ws)
          f res ws (('\\', _):('n', _):xs) = f ('\n':res) ws xs
          f res ws (('\\', _):(x, _):xs) = f (esc x ++ res) ws xs
          f res ws [('$', p)] = f ("$\\" ++ res) (bvs p:ws) []
          f res ws (('$', p):xs)
           = case get_var_name xs of
                 Nothing -> f ("$\\" ++ res) (bvs p:ws) xs
                 Just (_, xs') -> f ("*." ++ res) ws xs'
          f res ws ((x, _):xs) = f (esc x ++ res) ws xs
          bvs p = "Bad variable spec at " ++ show_pos p
          esc c | c `elem` ".[\\(*+?{|^$" = [c, '\\']
                | otherwise               = [c]

get_var_name :: [(Char, Pos)] -> Maybe (String, [(Char, Pos)])
get_var_name (('{', _):xs) = case break (('}' ==) . fst) xs of
                                 (ys, _:zs) -> Just (map fst ys, zs)
                                 _ -> Nothing
get_var_name xs@((c, _):_)
 | isAlpha c = case span (\(x, _) -> isAlphaNum x || x == '_') xs of
                   (ys, zs) -> Just (map fst ys, zs)
get_var_name _ = Nothing

usage :: IO ()
usage =
    do putStrLn "Usage: update-haskell-control [ --help | -h | --version | -V ]"
       putStrLn "       update-haskell-control [ OPTION ]..."
       putStrLn ""
       putStrLn "   --update       Update output filename (default)"
       putStrLn "   --check        Check output filename"
       putStrLn "   -i filename    Input filename"
       putStrLn "   -o filename    Output filename"
       putStrLn "   -I path        Add search path"
       putStrLn "   -v             Verbose"
       putStrLn "   -q             Input filename"
       putStrLn ""

show_version :: IO ()
show_version = do putStrLn "update-haskell-control @version@"
                  putStrLn "Written by Ian Lynagh."
                  putStrLn "Copyright (C) 2004 Ian Lynagh."
\end{code}