refactor band parser a tad
[platform/upstream/crda.git] / dbparse.py
1 #!/usr/bin/env python
2
3 import sys
4
5 power_flags = {
6     'OFDM': 2,
7     'CCK': 1,
8 }
9
10 class SyntaxError(Exception):
11     pass
12
13 class DBParser(object):
14     def __init__(self):
15         pass
16
17     def _syntax_error(self, txt=None):
18         txt = txt and ' (%s)' % txt or ''
19         raise SyntaxError("Syntax error in line %d%s" % (self._lineno, txt))
20
21     def _warn(self, txt):
22         sys.stderr.write("Warning (line %d): %s\n" % (self._lineno, txt))
23
24     def _parse_band_def(self, bname, banddef):
25         line = banddef
26         try:
27             freqs, line = line.split(',', 1)
28         except ValueError:
29             freqs = line
30             line = ''
31
32         flags = [f.upper() for f in line.split(',') if f]
33
34         try:
35             freqs, bw = freqs.split('@')
36             bw = float(bw)
37         except ValueError:
38             bw = 20.0
39
40         try:
41             start, end = freqs.split('-')
42             start = float(start)
43             end = float(end)
44         except ValueError:
45             self._syntax_error("band must have frequency range")
46
47         fl = 0
48         for f in flags:
49             if not f in power_flags:
50                 self._syntax_error("Invalid band flag")
51             fl |= power_flags[f]
52
53         b = (start, end, bw, fl)
54         self._banddup[bname] = bname
55         if b in self._bandrev:
56             self._warn('Duplicate band definition ("%s" and "%s")' % (
57                           bname, self._bandrev[b]))
58             self._banddup[bname] = self._bandrev[b]
59         self._bands[bname] = b
60         self._bandrev[b] = bname
61
62     def _parse_band(self, line):
63         try:
64             bname, line = line.split(':', 1)
65             if not bname:
66                 self._syntax_error("'band' keyword must be followed by name")
67         except ValueError:
68             self._syntax_error("band name must be followed by colon")
69
70         self._parse_band_def(bname, line)
71
72     def _parse_power(self, line):
73         try:
74             pname, line = line.split(':', 1)
75             if not pname:
76                 self._syntax_error("'power' keyword must be followed by name")
77         except ValueError:
78             self._syntax_error("power name must be followed by colon")
79
80         try:
81             (environ,
82              max_ant_gain,
83              max_ir_ptmp,
84              max_ir_ptp,
85              max_eirp_ptmp,
86              max_eirp_ptp) = line.split(',')
87             max_ant_gain = float(max_ant_gain)
88             max_ir_ptmp = float(max_ir_ptmp)
89             max_ir_ptp = float(max_ir_ptp)
90             max_eirp_ptmp = float(max_eirp_ptmp)
91             max_eirp_ptp = float(max_eirp_ptp)
92         except ValueError:
93             self._syntax_error("invalid power data")
94
95         if environ == 'OI':
96             environ = ' '
97         if not environ in ('I', 'O', ' '):
98             self._syntax_error("Invalid environment specifier")
99
100         p = (environ, max_ant_gain, max_ir_ptmp,
101              max_ir_ptp, max_eirp_ptmp, max_eirp_ptp)
102         self._powerdup[pname] = pname
103         if p in self._powerrev:
104             self._warn('Duplicate power definition ("%s" and "%s")' % (
105                           pname, self._powerrev[p]))
106             self._powerdup[pname] = self._powerrev[p]
107         self._power[pname] = p
108         self._powerrev[p] = pname
109
110     def _parse_country(self, line):
111         try:
112             cname, line = line.split(':', 1)
113             if not cname:
114                 self._syntax_error("'country' keyword must be followed by name")
115             if line:
116                 self._syntax_error("extra data at end of country line")
117         except ValueError:
118             self._syntax_error("country name must be followed by colon")
119
120         if not cname in self._countries:
121             self._countries[cname] = []
122         self._current_country = self._countries[cname]
123         self._current_country_name = cname
124
125     def _parse_country_item(self, line):
126         try:
127             bname, pname = line.split(',', 1)
128             if not bname:
129                 self._syntax_error("country definition must have band name")
130             if not pname:
131                 self._syntax_error("country definition must have power name")
132         except ValueError:
133             self._syntax_error("country definition must have band and power names")
134         if not bname in self._bands:
135             self._syntax_error("band does not exist")
136         if not pname in self._power:
137             self._syntax_error("power does not exist")
138         bname = self._banddup[bname]
139         pname = self._powerdup[pname]
140         # de-duplicate so binary database is more compact
141         self._bands_used[bname] = True
142         self._power_used[pname] = True
143         tup = (bname, pname)
144         if tup in self._current_country:
145             self._warn('Rule "%s, %s" added to "%s" twice' % (
146                           bname, pname, self._current_country_name))
147         else:
148             self._current_country.append((bname, pname))
149
150     def parse(self, f):
151         self._current_country = None
152         self._bands = {}
153         self._power = {}
154         self._countries = {}
155         self._bands_used = {}
156         self._power_used = {}
157         self._bandrev = {}
158         self._powerrev = {}
159         self._banddup = {}
160         self._powerdup = {}
161
162         self._lineno = 0
163         for line in f:
164             self._lineno += 1
165             line = line.strip()
166             line = line.replace(' ', '').replace('\t', '')
167             line = line.split('#')[0]
168             if not line:
169                 continue
170             if line[0:4] == 'band':
171                 self._parse_band(line[4:])
172                 self._current_country = None
173             elif line[0:5] == 'power':
174                 self._parse_power(line[5:])
175                 self._current_country = None
176             elif line[0:7] == 'country':
177                 self._parse_country(line[7:])
178             elif self._current_country is not None:
179                 self._parse_country_item(line)
180             else:
181                 self._syntax_error("Expected band, power or country definition")
182
183         countries = {}
184         for k, v in self._countries.iteritems():
185             v.sort()
186             for k in k.split(','):
187                 countries[k] = tuple(v)
188         bands = {}
189         for k, v in self._bands.iteritems():
190             if k in self._bands_used:
191                 bands[k] = v
192                 continue
193             # we de-duplicated, but don't warn again about the dupes
194             if self._banddup[k] == k:
195                 self._warn('Unused band definition "%s"' % k)
196         power = {}
197         for k, v in self._power.iteritems():
198             if k in self._power_used:
199                 power[k] = v
200                 continue
201             # we de-duplicated, but don't warn again about the dupes
202             if self._powerdup[k] == k:
203                 self._warn('Unused power definition "%s"' % k)
204         return bands, power, countries
205
206 def create_rules(countries):
207     result = {}
208     for c in countries.itervalues():
209         for rule in c:
210             result[rule] = 1
211     return result.keys()
212
213 def create_collections(countries):
214     result = {}
215     for c in countries.itervalues():
216         c = tuple(c)
217         result[c] = 1
218     return result.keys()
219
220 if __name__ == '__main__':
221     import sys
222     p = DBParser()
223     b, p, c = p.parse(sys.stdin)
224     print b
225     print p
226     print c
227     print create_rules(c)
228     print create_collections(c)