]> gitweb.michael.orlitzky.com - hath.git/blob - src/Main.hs
Remove the "reversed" mode -- do one thing and do it well and all that.
[hath.git] / src / Main.hs
1 module Main
2 where
3
4 import Control.Monad (when)
5 import Data.List ((\\), intercalate)
6 import Data.Maybe (catMaybes, isNothing)
7 import Data.String.Utils (splitWs)
8 import System.Exit (ExitCode(..), exitWith)
9 import System.IO (stderr, hPutStrLn)
10 import Text.Read (readMaybe)
11
12 import Cidr (
13 Cidr(..),
14 combine_all,
15 enumerate,
16 max_octet1,
17 max_octet2,
18 max_octet3,
19 max_octet4,
20 min_octet1,
21 min_octet2,
22 min_octet3,
23 min_octet4 )
24 import CommandLine (Args(..), get_args)
25 import ExitCodes ( exit_invalid_cidr )
26 import Octet ()
27
28
29 -- | A regular expression that matches a non-address character.
30 --
31 non_addr_char :: String
32 non_addr_char = "[^\\.0-9]"
33
34
35 -- | Add non_addr_chars on either side of the given String. This
36 -- prevents (for example) the regex '127.0.0.1' from matching
37 -- '127.0.0.100'.
38 --
39 add_barriers :: String -> String
40 add_barriers x = non_addr_char ++ x ++ non_addr_char
41
42
43 -- | The magic happens here. We take a CIDR String as an argument, and
44 -- return the equivalent regular expression. We do this as follows:
45 --
46 -- 1. Compute the minimum possible value of each octet.
47 -- 2. Compute the maximum possible value of each octet.
48 -- 3. Generate a regex matching every value between those min and
49 -- max values.
50 -- 4. Join the regexes from step 3 with regexes matching periods.
51 -- 5. Stick an address boundary on either side of the result if
52 -- use_barriers is True.
53 --
54 cidr_to_regex :: Bool -> Cidr.Cidr -> String
55 cidr_to_regex use_barriers cidr =
56 let f = if use_barriers then add_barriers else id in
57 f (intercalate "\\." [range1, range2, range3, range4])
58 where
59 range1 = numeric_range min1 max1
60 range2 = numeric_range min2 max2
61 range3 = numeric_range min3 max3
62 range4 = numeric_range min4 max4
63 min1 = fromEnum (min_octet1 cidr)
64 min2 = fromEnum (min_octet2 cidr)
65 min3 = fromEnum (min_octet3 cidr)
66 min4 = fromEnum (min_octet4 cidr)
67 max1 = fromEnum (max_octet1 cidr)
68 max2 = fromEnum (max_octet2 cidr)
69 max3 = fromEnum (max_octet3 cidr)
70 max4 = fromEnum (max_octet4 cidr)
71
72
73
74 -- | Take a list of Strings, and return a regular expression matching
75 -- any of them.
76 --
77 alternate :: [String] -> String
78 alternate terms = "(" ++ (intercalate "|" terms) ++ ")"
79
80
81 -- | Take two Ints as parameters, and return a regex matching any
82 -- integer between them (inclusive).
83 --
84 -- IMPORTANT: we match from max to min so that if e.g. the last
85 -- octet is '255', we want '255' to match before '2' in the regex
86 -- (255|254|...|3|2|1) which does not happen if we use
87 -- (1|2|3|...|254|255).
88 --
89 numeric_range :: Int -> Int -> String
90 numeric_range x y =
91 alternate (map show $ reverse [lower..upper])
92 where
93 lower = minimum [x,y]
94 upper = maximum [x,y]
95
96
97 main :: IO ()
98 main = do
99 args <- get_args
100
101 -- This reads stdin.
102 input <- getContents
103
104 let cidr_strings = splitWs input
105 let cidrs = map readMaybe cidr_strings
106
107 when (any isNothing cidrs) $ do
108 hPutStrLn stderr "ERROR: not valid CIDR notation:"
109
110 -- Output the bad lines, safely.
111 let pairs = zip cidr_strings cidrs
112 let print_pair (x, Nothing) = hPutStrLn stderr (" * " ++ x)
113 print_pair (_, _) = return ()
114
115 mapM_ print_pair pairs
116 exitWith (ExitFailure exit_invalid_cidr)
117
118 -- Filter out only the valid ones.
119 let valid_cidrs = catMaybes cidrs
120
121 case args of
122 Regexed{} -> do
123 let cidrs' = combine_all valid_cidrs
124 let regexes = map (cidr_to_regex (barriers args)) cidrs'
125 putStrLn $ alternate regexes
126 Reduced{} ->
127 mapM_ print (combine_all valid_cidrs)
128 Duped{} ->
129 mapM_ print dupes
130 where
131 dupes = valid_cidrs \\ (combine_all valid_cidrs)
132 Diffed{} -> do
133 mapM_ putStrLn deletions
134 mapM_ putStrLn additions
135 where
136 dupes = valid_cidrs \\ (combine_all valid_cidrs)
137 deletions = map (\s -> '-' : (show s)) dupes
138 newcidrs = (combine_all valid_cidrs) \\ valid_cidrs
139 additions = map (\s -> '+' : (show s)) newcidrs
140 Listed{} -> do
141 let combined_cidrs = combine_all valid_cidrs
142 let addrs = concatMap enumerate combined_cidrs
143 mapM_ print addrs