more sorting
[platform/upstream/crda.git] / dbparse.py
1 #!/usr/bin/env python
2
3 import sys
4
5 band_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, dupwarn=True):
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 band_flags:
50                 self._syntax_error("Invalid band flag")
51             fl |= band_flags[f]
52
53         b = (start, end, bw, fl)
54         self._banddup[bname] = bname
55         if b in self._bandrev:
56             if dupwarn:
57                 self._warn('Duplicate band definition ("%s" and "%s")' % (
58                               bname, self._bandrev[b]))
59             self._banddup[bname] = self._bandrev[b]
60         self._bands[bname] = b
61         self._bandrev[b] = bname
62         self._bandline[bname] = self._lineno
63
64     def _parse_band(self, line):
65         try:
66             bname, line = line.split(':', 1)
67             if not bname:
68                 self._syntax_error("'band' keyword must be followed by name")
69         except ValueError:
70             self._syntax_error("band name must be followed by colon")
71
72         self._parse_band_def(bname, line)
73
74     def _parse_power(self, line):
75         try:
76             pname, line = line.split(':', 1)
77             if not pname:
78                 self._syntax_error("'power' keyword must be followed by name")
79         except ValueError:
80             self._syntax_error("power name must be followed by colon")
81
82         self._parse_power_def(pname, line)
83
84     def _parse_power_def(self, pname, line, dupwarn=True):
85         try:
86             (environ,
87              max_ant_gain,
88              max_ir_ptmp,
89              max_ir_ptp,
90              max_eirp_ptmp,
91              max_eirp_ptp) = line.split(',')
92             max_ant_gain = float(max_ant_gain)
93             max_ir_ptmp = float(max_ir_ptmp)
94             max_ir_ptp = float(max_ir_ptp)
95             max_eirp_ptmp = float(max_eirp_ptmp)
96             max_eirp_ptp = float(max_eirp_ptp)
97         except ValueError:
98             self._syntax_error("invalid power data")
99
100         if environ == 'OI':
101             environ = ' '
102         if not environ in ('I', 'O', ' '):
103             self._syntax_error("Invalid environment specifier")
104
105         p = (environ, max_ant_gain, max_ir_ptmp,
106              max_ir_ptp, max_eirp_ptmp, max_eirp_ptp)
107         self._powerdup[pname] = pname
108         if p in self._powerrev:
109             if dupwarn:
110                 self._warn('Duplicate power definition ("%s" and "%s")' % (
111                               pname, self._powerrev[p]))
112             self._powerdup[pname] = self._powerrev[p]
113         self._power[pname] = p
114         self._powerrev[p] = pname
115         self._powerline[pname] = self._lineno
116
117     def _parse_country(self, line):
118         try:
119             cname, line = line.split(':', 1)
120             if not cname:
121                 self._syntax_error("'country' keyword must be followed by name")
122             if line:
123                 self._syntax_error("extra data at end of country line")
124         except ValueError:
125             self._syntax_error("country name must be followed by colon")
126
127         if not cname in self._countries:
128             self._countries[cname] = []
129         self._current_country = self._countries[cname]
130         self._current_country_name = cname
131
132     def _parse_country_item(self, line):
133         if line[0] == '(':
134             try:
135                 band, pname = line[1:].split('),')
136                 bname = 'UNNAMED %d' % self._lineno
137                 self._parse_band_def(bname, band, dupwarn=False)
138             except:
139                 self._syntax_error("Badly parenthesised band definition")
140         else:
141             try:
142                 bname, pname = line.split(',', 1)
143                 if not bname:
144                     self._syntax_error("country definition must have band")
145                 if not pname:
146                     self._syntax_error("country definition must have power")
147             except ValueError:
148                 self._syntax_error("country definition must have band and power")
149
150         if pname[0] == '(':
151             if not pname[-1] == ')':
152                 self._syntax_error("Badly parenthesised power definition")
153             power = pname[1:-1]
154             pname = 'UNNAMED %d' % self._lineno
155             self._parse_power_def(pname, power, dupwarn=False)
156
157         if not bname in self._bands:
158             self._syntax_error("band does not exist")
159         if not pname in self._power:
160             self._syntax_error("power does not exist")
161         self._bands_used[bname] = True
162         self._power_used[pname] = True
163         # de-duplicate so binary database is more compact
164         bname = self._banddup[bname]
165         pname = self._powerdup[pname]
166         tup = (bname, pname)
167         if tup in self._current_country:
168             self._warn('Rule "%s, %s" added to "%s" twice' % (
169                           bname, pname, self._current_country_name))
170         else:
171             self._current_country.append((bname, pname))
172
173     def parse(self, f):
174         self._current_country = None
175         self._bands = {}
176         self._power = {}
177         self._countries = {}
178         self._bands_used = {}
179         self._power_used = {}
180         self._bandrev = {}
181         self._powerrev = {}
182         self._banddup = {}
183         self._powerdup = {}
184         self._bandline = {}
185         self._powerline = {}
186
187         self._lineno = 0
188         for line in f:
189             self._lineno += 1
190             line = line.strip()
191             line = line.replace(' ', '').replace('\t', '')
192             line = line.split('#')[0]
193             if not line:
194                 continue
195             if line[0:4] == 'band':
196                 self._parse_band(line[4:])
197                 self._current_country = None
198             elif line[0:5] == 'power':
199                 self._parse_power(line[5:])
200                 self._current_country = None
201             elif line[0:7] == 'country':
202                 self._parse_country(line[7:])
203             elif self._current_country is not None:
204                 self._parse_country_item(line)
205             else:
206                 self._syntax_error("Expected band, power or country definition")
207
208         countries = {}
209         for k, v in self._countries.iteritems():
210             v.sort()
211             for k in k.split(','):
212                 countries[k] = tuple(v)
213         bands = {}
214         for k, v in self._bands.iteritems():
215             if k in self._bands_used:
216                 bands[self._banddup[k]] = v
217                 continue
218             # we de-duplicated, but don't warn again about the dupes
219             if self._banddup[k] == k:
220                 self._lineno = self._bandline[k]
221                 self._warn('Unused band definition "%s"' % k)
222         power = {}
223         for k, v in self._power.iteritems():
224             if k in self._power_used:
225                 power[self._powerdup[k]] = v
226                 continue
227             # we de-duplicated, but don't warn again about the dupes
228             if self._powerdup[k] == k:
229                 self._lineno = self._powerline[k]
230                 self._warn('Unused power definition "%s"' % k)
231         return bands, power, countries
232
233 def create_rules(countries):
234     result = {}
235     for c in countries.itervalues():
236         for rule in c:
237             result[rule] = 1
238     return result.keys()
239
240 def create_collections(countries):
241     result = {}
242     for c in countries.itervalues():
243         c = tuple(c)
244         result[c] = 1
245     return result.keys()
246
247 if __name__ == '__main__':
248     import sys
249     p = DBParser()
250     b, p, c = p.parse(sys.stdin)
251     print b
252     print p
253     print c
254     print create_rules(c)
255     print create_collections(c)