1127be1a7SAdrian Chadd /*-
2127be1a7SAdrian Chadd * Copyright (c) 2008 Sam Leffler, Errno Consulting
3127be1a7SAdrian Chadd * All rights reserved.
4127be1a7SAdrian Chadd *
5127be1a7SAdrian Chadd * Redistribution and use in source and binary forms, with or without
6127be1a7SAdrian Chadd * modification, are permitted provided that the following conditions
7127be1a7SAdrian Chadd * are met:
8127be1a7SAdrian Chadd * 1. Redistributions of source code must retain the above copyright
9127be1a7SAdrian Chadd * notice, this list of conditions and the following disclaimer.
10127be1a7SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright
11127be1a7SAdrian Chadd * notice, this list of conditions and the following disclaimer in the
12127be1a7SAdrian Chadd * documentation and/or other materials provided with the distribution.
13127be1a7SAdrian Chadd *
14127be1a7SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15127be1a7SAdrian Chadd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16127be1a7SAdrian Chadd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17127be1a7SAdrian Chadd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18127be1a7SAdrian Chadd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19127be1a7SAdrian Chadd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20127be1a7SAdrian Chadd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21127be1a7SAdrian Chadd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22127be1a7SAdrian Chadd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23127be1a7SAdrian Chadd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24127be1a7SAdrian Chadd */
25127be1a7SAdrian Chadd
26127be1a7SAdrian Chadd #include <sys/types.h>
27127be1a7SAdrian Chadd #include <sys/errno.h>
28127be1a7SAdrian Chadd #include <sys/param.h>
29127be1a7SAdrian Chadd #include <sys/mman.h>
30127be1a7SAdrian Chadd #include <sys/sbuf.h>
31127be1a7SAdrian Chadd #include <sys/stat.h>
32127be1a7SAdrian Chadd
33127be1a7SAdrian Chadd #include <stdio.h>
34127be1a7SAdrian Chadd #include <string.h>
35127be1a7SAdrian Chadd #include <ctype.h>
36127be1a7SAdrian Chadd #include <fcntl.h>
37127be1a7SAdrian Chadd #include <err.h>
38127be1a7SAdrian Chadd #include <unistd.h>
39127be1a7SAdrian Chadd
40127be1a7SAdrian Chadd #include <bsdxml.h>
41127be1a7SAdrian Chadd
42127be1a7SAdrian Chadd #include "lib80211_regdomain.h"
43127be1a7SAdrian Chadd
44127be1a7SAdrian Chadd #include <net80211/_ieee80211.h>
45127be1a7SAdrian Chadd
46127be1a7SAdrian Chadd #define MAXLEVEL 20
47127be1a7SAdrian Chadd
48127be1a7SAdrian Chadd struct mystate {
49127be1a7SAdrian Chadd XML_Parser parser;
50127be1a7SAdrian Chadd struct regdata *rdp;
51127be1a7SAdrian Chadd struct regdomain *rd; /* current domain */
52127be1a7SAdrian Chadd struct netband *netband; /* current netband */
53127be1a7SAdrian Chadd struct freqband *freqband; /* current freqband */
54127be1a7SAdrian Chadd struct country *country; /* current country */
55127be1a7SAdrian Chadd netband_head *curband; /* current netband list */
56127be1a7SAdrian Chadd int level;
57127be1a7SAdrian Chadd struct sbuf *sbuf[MAXLEVEL];
58127be1a7SAdrian Chadd int nident;
59127be1a7SAdrian Chadd };
60127be1a7SAdrian Chadd
61127be1a7SAdrian Chadd struct ident {
62127be1a7SAdrian Chadd const void *id;
63127be1a7SAdrian Chadd void *p;
64127be1a7SAdrian Chadd enum { DOMAIN, COUNTRY, FREQBAND } type;
65127be1a7SAdrian Chadd };
66127be1a7SAdrian Chadd
67127be1a7SAdrian Chadd static void
start_element(void * data,const char * name,const char ** attr)68127be1a7SAdrian Chadd start_element(void *data, const char *name, const char **attr)
69127be1a7SAdrian Chadd {
70127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0)
71127be1a7SAdrian Chadd struct mystate *mt;
72127be1a7SAdrian Chadd const void *id, *ref, *mode;
73127be1a7SAdrian Chadd int i;
74127be1a7SAdrian Chadd
75127be1a7SAdrian Chadd mt = data;
76127be1a7SAdrian Chadd if (++mt->level == MAXLEVEL) {
77127be1a7SAdrian Chadd /* XXX force parser to abort */
78127be1a7SAdrian Chadd return;
79127be1a7SAdrian Chadd }
80127be1a7SAdrian Chadd mt->sbuf[mt->level] = sbuf_new_auto();
81127be1a7SAdrian Chadd id = ref = mode = NULL;
82127be1a7SAdrian Chadd for (i = 0; attr[i] != NULL; i += 2) {
83127be1a7SAdrian Chadd if (iseq(attr[i], "id")) {
84127be1a7SAdrian Chadd id = attr[i+1];
85127be1a7SAdrian Chadd } else if (iseq(attr[i], "ref")) {
86127be1a7SAdrian Chadd ref = attr[i+1];
87127be1a7SAdrian Chadd } else if (iseq(attr[i], "mode")) {
88127be1a7SAdrian Chadd mode = attr[i+1];
89127be1a7SAdrian Chadd } else
90127be1a7SAdrian Chadd printf("%*.*s[%s = %s]\n", mt->level + 1,
91127be1a7SAdrian Chadd mt->level + 1, "", attr[i], attr[i+1]);
92127be1a7SAdrian Chadd }
93127be1a7SAdrian Chadd if (iseq(name, "rd") && mt->rd == NULL) {
94127be1a7SAdrian Chadd if (mt->country == NULL) {
95127be1a7SAdrian Chadd mt->rd = calloc(1, sizeof(struct regdomain));
96127be1a7SAdrian Chadd mt->rd->name = strdup(id);
97127be1a7SAdrian Chadd mt->nident++;
98127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
99127be1a7SAdrian Chadd } else
100127be1a7SAdrian Chadd mt->country->rd = (void *)strdup(ref);
101127be1a7SAdrian Chadd return;
102127be1a7SAdrian Chadd }
103127be1a7SAdrian Chadd if (iseq(name, "defcc") && mt->rd != NULL) {
104127be1a7SAdrian Chadd mt->rd->cc = (void *)strdup(ref);
105127be1a7SAdrian Chadd return;
106127be1a7SAdrian Chadd }
107127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
108127be1a7SAdrian Chadd if (mode == NULL) {
109127be1a7SAdrian Chadd warnx("no mode for netband at line %ld",
110127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
111127be1a7SAdrian Chadd return;
112127be1a7SAdrian Chadd }
113127be1a7SAdrian Chadd if (iseq(mode, "11b"))
114127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11b;
115127be1a7SAdrian Chadd else if (iseq(mode, "11g"))
116127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11g;
117127be1a7SAdrian Chadd else if (iseq(mode, "11a"))
118127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11a;
119127be1a7SAdrian Chadd else if (iseq(mode, "11ng"))
120127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11ng;
121127be1a7SAdrian Chadd else if (iseq(mode, "11na"))
122127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11na;
12336ea5759SAdrian Chadd else if (iseq(mode, "11ac"))
12436ea5759SAdrian Chadd mt->curband = &mt->rd->bands_11ac;
12536ea5759SAdrian Chadd else if (iseq(mode, "11acg"))
12636ea5759SAdrian Chadd mt->curband = &mt->rd->bands_11acg;
127127be1a7SAdrian Chadd else
128127be1a7SAdrian Chadd warnx("unknown mode \"%s\" at line %ld",
129127be1a7SAdrian Chadd __DECONST(char *, mode),
130127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
131127be1a7SAdrian Chadd return;
132127be1a7SAdrian Chadd }
133127be1a7SAdrian Chadd if (iseq(name, "band") && mt->netband == NULL) {
134127be1a7SAdrian Chadd if (mt->curband == NULL) {
135127be1a7SAdrian Chadd warnx("band without enclosing netband at line %ld",
136127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
137127be1a7SAdrian Chadd return;
138127be1a7SAdrian Chadd }
139127be1a7SAdrian Chadd mt->netband = calloc(1, sizeof(struct netband));
140127be1a7SAdrian Chadd LIST_INSERT_HEAD(mt->curband, mt->netband, next);
141127be1a7SAdrian Chadd return;
142127be1a7SAdrian Chadd }
143127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
144127be1a7SAdrian Chadd /* XXX handle inlines and merge into table? */
145127be1a7SAdrian Chadd if (mt->netband->band != NULL) {
146127be1a7SAdrian Chadd warnx("duplicate freqband at line %ld ignored",
147127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
148127be1a7SAdrian Chadd /* XXX complain */
149127be1a7SAdrian Chadd } else
150127be1a7SAdrian Chadd mt->netband->band = (void *)strdup(ref);
151127be1a7SAdrian Chadd return;
152127be1a7SAdrian Chadd }
153127be1a7SAdrian Chadd
154127be1a7SAdrian Chadd if (iseq(name, "country") && mt->country == NULL) {
155127be1a7SAdrian Chadd mt->country = calloc(1, sizeof(struct country));
156127be1a7SAdrian Chadd mt->country->isoname = strdup(id);
157127be1a7SAdrian Chadd mt->country->code = NO_COUNTRY;
158127be1a7SAdrian Chadd mt->nident++;
159127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
160127be1a7SAdrian Chadd return;
161127be1a7SAdrian Chadd }
162127be1a7SAdrian Chadd
163127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband == NULL) {
164127be1a7SAdrian Chadd mt->freqband = calloc(1, sizeof(struct freqband));
165127be1a7SAdrian Chadd mt->freqband->id = strdup(id);
166127be1a7SAdrian Chadd mt->nident++;
167127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
168127be1a7SAdrian Chadd return;
169127be1a7SAdrian Chadd }
170127be1a7SAdrian Chadd #undef iseq
171127be1a7SAdrian Chadd }
172127be1a7SAdrian Chadd
173127be1a7SAdrian Chadd static int
decode_flag(struct mystate * mt,const char * p,int len)174127be1a7SAdrian Chadd decode_flag(struct mystate *mt, const char *p, int len)
175127be1a7SAdrian Chadd {
176127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0)
177127be1a7SAdrian Chadd static const struct {
178127be1a7SAdrian Chadd const char *name;
179127be1a7SAdrian Chadd int len;
180127be1a7SAdrian Chadd uint32_t value;
181127be1a7SAdrian Chadd } flags[] = {
182127be1a7SAdrian Chadd #define FLAG(x) { #x, sizeof(#x)-1, x }
183127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_A),
184127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_B),
185127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_G),
186127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT20),
187127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40),
18836ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT20),
18936ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT40),
19036ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT80),
1911e375f3aSBjoern A. Zeeb FLAG(IEEE80211_CHAN_VHT160),
19236ea5759SAdrian Chadd /*
193fd6eb8feSBjoern A. Zeeb * XXX VHT80P80? This likely should be done by
19436ea5759SAdrian Chadd * 80MHz chan logic in net80211 / ifconfig.
19536ea5759SAdrian Chadd */
196127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_ST),
197127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_TURBO),
198127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_PASSIVE),
199127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_DFS),
200127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_CCK),
201127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_OFDM),
202127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_2GHZ),
203127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_5GHZ),
204127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_DYN),
205127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_GFSK),
206127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_GSM),
207127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_STURBO),
208127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HALF),
209127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_QUARTER),
210127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40U),
211127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40D),
212127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_4MSXMIT),
213127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_NOADHOC),
214127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_NOHOSTAP),
215127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_11D),
216127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_FHSS),
217127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_PUREG),
218127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_108A),
219127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_108G),
220127be1a7SAdrian Chadd #undef FLAG
221127be1a7SAdrian Chadd { "ECM", 3, REQ_ECM },
222127be1a7SAdrian Chadd { "INDOOR", 6, REQ_INDOOR },
223127be1a7SAdrian Chadd { "OUTDOOR", 7, REQ_OUTDOOR },
224127be1a7SAdrian Chadd };
225127be1a7SAdrian Chadd unsigned int i;
226127be1a7SAdrian Chadd
227127be1a7SAdrian Chadd for (i = 0; i < nitems(flags); i++)
228127be1a7SAdrian Chadd if (len == flags[i].len && iseq(p, flags[i].name))
229127be1a7SAdrian Chadd return flags[i].value;
230127be1a7SAdrian Chadd warnx("unknown flag \"%.*s\" at line %ld ignored",
231127be1a7SAdrian Chadd len, p, XML_GetCurrentLineNumber(mt->parser));
232127be1a7SAdrian Chadd return 0;
233127be1a7SAdrian Chadd #undef iseq
234127be1a7SAdrian Chadd }
235127be1a7SAdrian Chadd
236127be1a7SAdrian Chadd static void
end_element(void * data,const char * name)237127be1a7SAdrian Chadd end_element(void *data, const char *name)
238127be1a7SAdrian Chadd {
239127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0)
240127be1a7SAdrian Chadd struct mystate *mt;
241127be1a7SAdrian Chadd int len;
242127be1a7SAdrian Chadd char *p;
243127be1a7SAdrian Chadd
244127be1a7SAdrian Chadd mt = data;
245127be1a7SAdrian Chadd sbuf_finish(mt->sbuf[mt->level]);
246127be1a7SAdrian Chadd p = sbuf_data(mt->sbuf[mt->level]);
247127be1a7SAdrian Chadd len = sbuf_len(mt->sbuf[mt->level]);
248127be1a7SAdrian Chadd
249127be1a7SAdrian Chadd /* <freqband>...</freqband> */
250127be1a7SAdrian Chadd if (iseq(name, "freqstart") && mt->freqband != NULL) {
251127be1a7SAdrian Chadd mt->freqband->freqStart = strtoul(p, NULL, 0);
252127be1a7SAdrian Chadd goto done;
253127be1a7SAdrian Chadd }
254127be1a7SAdrian Chadd if (iseq(name, "freqend") && mt->freqband != NULL) {
255127be1a7SAdrian Chadd mt->freqband->freqEnd = strtoul(p, NULL, 0);
256127be1a7SAdrian Chadd goto done;
257127be1a7SAdrian Chadd }
258127be1a7SAdrian Chadd if (iseq(name, "chanwidth") && mt->freqband != NULL) {
259127be1a7SAdrian Chadd mt->freqband->chanWidth = strtoul(p, NULL, 0);
260127be1a7SAdrian Chadd goto done;
261127be1a7SAdrian Chadd }
262127be1a7SAdrian Chadd if (iseq(name, "chansep") && mt->freqband != NULL) {
263127be1a7SAdrian Chadd mt->freqband->chanSep = strtoul(p, NULL, 0);
264127be1a7SAdrian Chadd goto done;
265127be1a7SAdrian Chadd }
266127be1a7SAdrian Chadd if (iseq(name, "flags")) {
267127be1a7SAdrian Chadd if (mt->freqband != NULL)
268127be1a7SAdrian Chadd mt->freqband->flags |= decode_flag(mt, p, len);
269127be1a7SAdrian Chadd else if (mt->netband != NULL)
270127be1a7SAdrian Chadd mt->netband->flags |= decode_flag(mt, p, len);
271127be1a7SAdrian Chadd else {
272127be1a7SAdrian Chadd warnx("flags without freqband or netband at line %ld ignored",
273127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
274127be1a7SAdrian Chadd }
275127be1a7SAdrian Chadd goto done;
276127be1a7SAdrian Chadd }
277127be1a7SAdrian Chadd
278127be1a7SAdrian Chadd /* <rd> ... </rd> */
279127be1a7SAdrian Chadd if (iseq(name, "name") && mt->rd != NULL) {
280127be1a7SAdrian Chadd mt->rd->name = strdup(p);
281127be1a7SAdrian Chadd goto done;
282127be1a7SAdrian Chadd }
283127be1a7SAdrian Chadd if (iseq(name, "sku") && mt->rd != NULL) {
284127be1a7SAdrian Chadd mt->rd->sku = strtoul(p, NULL, 0);
285127be1a7SAdrian Chadd goto done;
286127be1a7SAdrian Chadd }
287127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->rd != NULL) {
288127be1a7SAdrian Chadd mt->curband = NULL;
289127be1a7SAdrian Chadd goto done;
290127be1a7SAdrian Chadd }
291127be1a7SAdrian Chadd
292127be1a7SAdrian Chadd /* <band> ... </band> */
293127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->netband != NULL) {
294127be1a7SAdrian Chadd /* XXX handle inline freqbands */
295127be1a7SAdrian Chadd goto done;
296127be1a7SAdrian Chadd }
297127be1a7SAdrian Chadd if (iseq(name, "maxpower") && mt->netband != NULL) {
298127be1a7SAdrian Chadd mt->netband->maxPower = strtoul(p, NULL, 0);
299127be1a7SAdrian Chadd goto done;
300127be1a7SAdrian Chadd }
301127be1a7SAdrian Chadd if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
302127be1a7SAdrian Chadd mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
303127be1a7SAdrian Chadd goto done;
304127be1a7SAdrian Chadd }
305127be1a7SAdrian Chadd if (iseq(name, "maxantgain") && mt->netband != NULL) {
306127be1a7SAdrian Chadd mt->netband->maxAntGain = strtoul(p, NULL, 0);
307127be1a7SAdrian Chadd goto done;
308127be1a7SAdrian Chadd }
309127be1a7SAdrian Chadd
310127be1a7SAdrian Chadd /* <country>...</country> */
311127be1a7SAdrian Chadd if (iseq(name, "isocc") && mt->country != NULL) {
312127be1a7SAdrian Chadd mt->country->code = strtoul(p, NULL, 0);
313127be1a7SAdrian Chadd goto done;
314127be1a7SAdrian Chadd }
315127be1a7SAdrian Chadd if (iseq(name, "name") && mt->country != NULL) {
316127be1a7SAdrian Chadd mt->country->name = strdup(p);
317127be1a7SAdrian Chadd goto done;
318127be1a7SAdrian Chadd }
319127be1a7SAdrian Chadd
320127be1a7SAdrian Chadd if (len != 0) {
321127be1a7SAdrian Chadd warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
322127be1a7SAdrian Chadd name, p, XML_GetCurrentLineNumber(mt->parser));
323127be1a7SAdrian Chadd /* XXX goto done? */
324127be1a7SAdrian Chadd }
325127be1a7SAdrian Chadd /* </freqband> */
326127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband != NULL) {
327127be1a7SAdrian Chadd /* XXX must have start/end frequencies */
328127be1a7SAdrian Chadd /* XXX must have channel width/sep */
329127be1a7SAdrian Chadd mt->freqband = NULL;
330127be1a7SAdrian Chadd goto done;
331127be1a7SAdrian Chadd }
332127be1a7SAdrian Chadd /* </rd> */
333127be1a7SAdrian Chadd if (iseq(name, "rd") && mt->rd != NULL) {
334127be1a7SAdrian Chadd mt->rd = NULL;
335127be1a7SAdrian Chadd goto done;
336127be1a7SAdrian Chadd }
337127be1a7SAdrian Chadd /* </band> */
338127be1a7SAdrian Chadd if (iseq(name, "band") && mt->netband != NULL) {
339127be1a7SAdrian Chadd if (mt->netband->band == NULL) {
340127be1a7SAdrian Chadd warnx("no freqbands for band at line %ld",
341127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
342127be1a7SAdrian Chadd }
343127be1a7SAdrian Chadd if (mt->netband->maxPower == 0) {
344127be1a7SAdrian Chadd warnx("no maxpower for band at line %ld",
345127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
346127be1a7SAdrian Chadd }
347127be1a7SAdrian Chadd /* default max power w/ DFS to max power */
348127be1a7SAdrian Chadd if (mt->netband->maxPowerDFS == 0)
349127be1a7SAdrian Chadd mt->netband->maxPowerDFS = mt->netband->maxPower;
350127be1a7SAdrian Chadd mt->netband = NULL;
351127be1a7SAdrian Chadd goto done;
352127be1a7SAdrian Chadd }
353127be1a7SAdrian Chadd /* </netband> */
354127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->netband != NULL) {
355127be1a7SAdrian Chadd mt->curband = NULL;
356127be1a7SAdrian Chadd goto done;
357127be1a7SAdrian Chadd }
358127be1a7SAdrian Chadd /* </country> */
359127be1a7SAdrian Chadd if (iseq(name, "country") && mt->country != NULL) {
360127be1a7SAdrian Chadd /* XXX NO_COUNTRY should be in the net80211 country enum */
361127be1a7SAdrian Chadd if ((int) mt->country->code == NO_COUNTRY) {
362127be1a7SAdrian Chadd warnx("no ISO cc for country at line %ld",
363127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
364127be1a7SAdrian Chadd }
365127be1a7SAdrian Chadd if (mt->country->name == NULL) {
366127be1a7SAdrian Chadd warnx("no name for country at line %ld",
367127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
368127be1a7SAdrian Chadd }
369127be1a7SAdrian Chadd if (mt->country->rd == NULL) {
370127be1a7SAdrian Chadd warnx("no regdomain reference for country at line %ld",
371127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
372127be1a7SAdrian Chadd }
373127be1a7SAdrian Chadd mt->country = NULL;
374127be1a7SAdrian Chadd goto done;
375127be1a7SAdrian Chadd }
376127be1a7SAdrian Chadd done:
377127be1a7SAdrian Chadd sbuf_delete(mt->sbuf[mt->level]);
378127be1a7SAdrian Chadd mt->sbuf[mt->level--] = NULL;
379127be1a7SAdrian Chadd #undef iseq
380127be1a7SAdrian Chadd }
381127be1a7SAdrian Chadd
382127be1a7SAdrian Chadd static void
char_data(void * data,const XML_Char * s,int len)383127be1a7SAdrian Chadd char_data(void *data, const XML_Char *s, int len)
384127be1a7SAdrian Chadd {
385127be1a7SAdrian Chadd struct mystate *mt;
386127be1a7SAdrian Chadd const char *b, *e;
387127be1a7SAdrian Chadd
388127be1a7SAdrian Chadd mt = data;
389127be1a7SAdrian Chadd
390127be1a7SAdrian Chadd b = s;
391127be1a7SAdrian Chadd e = s + len-1;
392127be1a7SAdrian Chadd for (; isspace(*b) && b < e; b++)
393127be1a7SAdrian Chadd ;
394127be1a7SAdrian Chadd for (; isspace(*e) && e > b; e++)
395127be1a7SAdrian Chadd ;
396127be1a7SAdrian Chadd if (e != b || (*b != '\0' && !isspace(*b)))
397127be1a7SAdrian Chadd sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
398127be1a7SAdrian Chadd }
399127be1a7SAdrian Chadd
400127be1a7SAdrian Chadd static void *
findid(struct regdata * rdp,const void * id,int type)401127be1a7SAdrian Chadd findid(struct regdata *rdp, const void *id, int type)
402127be1a7SAdrian Chadd {
403127be1a7SAdrian Chadd struct ident *ip;
404127be1a7SAdrian Chadd
405127be1a7SAdrian Chadd for (ip = rdp->ident; ip->id != NULL; ip++)
406127be1a7SAdrian Chadd if ((int) ip->type == type && strcasecmp(ip->id, id) == 0)
407127be1a7SAdrian Chadd return ip->p;
408127be1a7SAdrian Chadd return NULL;
409127be1a7SAdrian Chadd }
410127be1a7SAdrian Chadd
411127be1a7SAdrian Chadd /*
412127be1a7SAdrian Chadd * Parse an regdomain XML configuration and build the internal representation.
413127be1a7SAdrian Chadd */
414127be1a7SAdrian Chadd int
lib80211_regdomain_readconfig(struct regdata * rdp,const void * p,size_t len)415127be1a7SAdrian Chadd lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
416127be1a7SAdrian Chadd {
417127be1a7SAdrian Chadd struct mystate *mt;
418127be1a7SAdrian Chadd struct regdomain *dp;
419127be1a7SAdrian Chadd struct country *cp;
420127be1a7SAdrian Chadd struct freqband *fp;
421127be1a7SAdrian Chadd struct netband *nb;
422127be1a7SAdrian Chadd const void *id;
423127be1a7SAdrian Chadd int i, errors;
424127be1a7SAdrian Chadd
425127be1a7SAdrian Chadd memset(rdp, 0, sizeof(struct regdata));
426127be1a7SAdrian Chadd mt = calloc(1, sizeof(struct mystate));
427127be1a7SAdrian Chadd if (mt == NULL)
428127be1a7SAdrian Chadd return ENOMEM;
429127be1a7SAdrian Chadd /* parse the XML input */
430127be1a7SAdrian Chadd mt->rdp = rdp;
431127be1a7SAdrian Chadd mt->parser = XML_ParserCreate(NULL);
432127be1a7SAdrian Chadd XML_SetUserData(mt->parser, mt);
433127be1a7SAdrian Chadd XML_SetElementHandler(mt->parser, start_element, end_element);
434127be1a7SAdrian Chadd XML_SetCharacterDataHandler(mt->parser, char_data);
435127be1a7SAdrian Chadd if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
436127be1a7SAdrian Chadd warnx("%s: %s at line %ld", __func__,
437127be1a7SAdrian Chadd XML_ErrorString(XML_GetErrorCode(mt->parser)),
438127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser));
439127be1a7SAdrian Chadd return -1;
440127be1a7SAdrian Chadd }
441127be1a7SAdrian Chadd XML_ParserFree(mt->parser);
442127be1a7SAdrian Chadd
443127be1a7SAdrian Chadd /* setup the identifer table */
444*8aadd10aSJohn Baldwin rdp->ident = calloc(mt->nident + 1, sizeof(struct ident));
445127be1a7SAdrian Chadd if (rdp->ident == NULL)
446127be1a7SAdrian Chadd return ENOMEM;
447127be1a7SAdrian Chadd free(mt);
448127be1a7SAdrian Chadd
449127be1a7SAdrian Chadd errors = 0;
450127be1a7SAdrian Chadd i = 0;
451127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) {
452127be1a7SAdrian Chadd rdp->ident[i].id = dp->name;
453127be1a7SAdrian Chadd rdp->ident[i].p = dp;
454127be1a7SAdrian Chadd rdp->ident[i].type = DOMAIN;
455127be1a7SAdrian Chadd i++;
456127be1a7SAdrian Chadd }
457127be1a7SAdrian Chadd LIST_FOREACH(fp, &rdp->freqbands, next) {
458127be1a7SAdrian Chadd rdp->ident[i].id = fp->id;
459127be1a7SAdrian Chadd rdp->ident[i].p = fp;
460127be1a7SAdrian Chadd rdp->ident[i].type = FREQBAND;
461127be1a7SAdrian Chadd i++;
462127be1a7SAdrian Chadd }
463127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) {
464127be1a7SAdrian Chadd rdp->ident[i].id = cp->isoname;
465127be1a7SAdrian Chadd rdp->ident[i].p = cp;
466127be1a7SAdrian Chadd rdp->ident[i].type = COUNTRY;
467127be1a7SAdrian Chadd i++;
468127be1a7SAdrian Chadd }
469127be1a7SAdrian Chadd
470127be1a7SAdrian Chadd /* patch references */
471127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) {
472127be1a7SAdrian Chadd if (dp->cc != NULL) {
473127be1a7SAdrian Chadd id = dp->cc;
474127be1a7SAdrian Chadd dp->cc = findid(rdp, id, COUNTRY);
475127be1a7SAdrian Chadd if (dp->cc == NULL) {
476127be1a7SAdrian Chadd warnx("undefined country \"%s\"",
477127be1a7SAdrian Chadd __DECONST(char *, id));
478127be1a7SAdrian Chadd errors++;
479127be1a7SAdrian Chadd }
480127be1a7SAdrian Chadd free(__DECONST(char *, id));
481127be1a7SAdrian Chadd }
482127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11b, next) {
483127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
484127be1a7SAdrian Chadd if (id == NULL) {
485127be1a7SAdrian Chadd warnx("undefined 11b band \"%s\"",
486127be1a7SAdrian Chadd __DECONST(char *, nb->band));
487127be1a7SAdrian Chadd errors++;
488127be1a7SAdrian Chadd }
489127be1a7SAdrian Chadd nb->band = id;
490127be1a7SAdrian Chadd }
491127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11g, next) {
492127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
493127be1a7SAdrian Chadd if (id == NULL) {
494127be1a7SAdrian Chadd warnx("undefined 11g band \"%s\"",
495127be1a7SAdrian Chadd __DECONST(char *, nb->band));
496127be1a7SAdrian Chadd errors++;
497127be1a7SAdrian Chadd }
498127be1a7SAdrian Chadd nb->band = id;
499127be1a7SAdrian Chadd }
500127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11a, next) {
501127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
502127be1a7SAdrian Chadd if (id == NULL) {
503127be1a7SAdrian Chadd warnx("undefined 11a band \"%s\"",
504127be1a7SAdrian Chadd __DECONST(char *, nb->band));
505127be1a7SAdrian Chadd errors++;
506127be1a7SAdrian Chadd }
507127be1a7SAdrian Chadd nb->band = id;
508127be1a7SAdrian Chadd }
509127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11ng, next) {
510127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
511127be1a7SAdrian Chadd if (id == NULL) {
512127be1a7SAdrian Chadd warnx("undefined 11ng band \"%s\"",
513127be1a7SAdrian Chadd __DECONST(char *, nb->band));
514127be1a7SAdrian Chadd errors++;
515127be1a7SAdrian Chadd }
516127be1a7SAdrian Chadd nb->band = id;
517127be1a7SAdrian Chadd }
518127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11na, next) {
519127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
520127be1a7SAdrian Chadd if (id == NULL) {
521127be1a7SAdrian Chadd warnx("undefined 11na band \"%s\"",
522127be1a7SAdrian Chadd __DECONST(char *, nb->band));
523127be1a7SAdrian Chadd errors++;
524127be1a7SAdrian Chadd }
525127be1a7SAdrian Chadd nb->band = id;
526127be1a7SAdrian Chadd }
52736ea5759SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11ac, next) {
52836ea5759SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
52936ea5759SAdrian Chadd if (id == NULL) {
53036ea5759SAdrian Chadd warnx("undefined 11ac band \"%s\"",
53136ea5759SAdrian Chadd __DECONST(char *, nb->band));
53236ea5759SAdrian Chadd errors++;
53336ea5759SAdrian Chadd }
53436ea5759SAdrian Chadd nb->band = id;
53536ea5759SAdrian Chadd }
53636ea5759SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11acg, next) {
53736ea5759SAdrian Chadd id = findid(rdp, nb->band, FREQBAND);
53836ea5759SAdrian Chadd if (id == NULL) {
53936ea5759SAdrian Chadd warnx("undefined 11acg band \"%s\"",
54036ea5759SAdrian Chadd __DECONST(char *, nb->band));
54136ea5759SAdrian Chadd errors++;
54236ea5759SAdrian Chadd }
54336ea5759SAdrian Chadd nb->band = id;
54436ea5759SAdrian Chadd }
545127be1a7SAdrian Chadd }
546127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) {
547127be1a7SAdrian Chadd id = cp->rd;
548127be1a7SAdrian Chadd cp->rd = findid(rdp, id, DOMAIN);
549127be1a7SAdrian Chadd if (cp->rd == NULL) {
550127be1a7SAdrian Chadd warnx("undefined country \"%s\"",
551127be1a7SAdrian Chadd __DECONST(char *, id));
552127be1a7SAdrian Chadd errors++;
553127be1a7SAdrian Chadd }
554127be1a7SAdrian Chadd free(__DECONST(char *, id));
555127be1a7SAdrian Chadd }
556127be1a7SAdrian Chadd
557127be1a7SAdrian Chadd return errors ? EINVAL : 0;
558127be1a7SAdrian Chadd }
559127be1a7SAdrian Chadd
560127be1a7SAdrian Chadd static void
cleanup_bands(netband_head * head)561127be1a7SAdrian Chadd cleanup_bands(netband_head *head)
562127be1a7SAdrian Chadd {
563127be1a7SAdrian Chadd struct netband *nb;
564127be1a7SAdrian Chadd
565127be1a7SAdrian Chadd for (;;) {
566127be1a7SAdrian Chadd nb = LIST_FIRST(head);
567127be1a7SAdrian Chadd if (nb == NULL)
568127be1a7SAdrian Chadd break;
5698ac09f39SAdrian Chadd LIST_REMOVE(nb, next);
570127be1a7SAdrian Chadd free(nb);
571127be1a7SAdrian Chadd }
572127be1a7SAdrian Chadd }
573127be1a7SAdrian Chadd
574127be1a7SAdrian Chadd /*
575127be1a7SAdrian Chadd * Cleanup state/resources for a previously parsed regdomain database.
576127be1a7SAdrian Chadd */
577127be1a7SAdrian Chadd void
lib80211_regdomain_cleanup(struct regdata * rdp)578127be1a7SAdrian Chadd lib80211_regdomain_cleanup(struct regdata *rdp)
579127be1a7SAdrian Chadd {
580127be1a7SAdrian Chadd
581127be1a7SAdrian Chadd free(rdp->ident);
582127be1a7SAdrian Chadd rdp->ident = NULL;
583127be1a7SAdrian Chadd for (;;) {
584127be1a7SAdrian Chadd struct regdomain *dp = LIST_FIRST(&rdp->domains);
585127be1a7SAdrian Chadd if (dp == NULL)
586127be1a7SAdrian Chadd break;
587127be1a7SAdrian Chadd LIST_REMOVE(dp, next);
588127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11b);
589127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11g);
590127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11a);
591127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11ng);
592127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11na);
59336ea5759SAdrian Chadd cleanup_bands(&dp->bands_11ac);
59436ea5759SAdrian Chadd cleanup_bands(&dp->bands_11acg);
595127be1a7SAdrian Chadd if (dp->name != NULL)
596127be1a7SAdrian Chadd free(__DECONST(char *, dp->name));
597127be1a7SAdrian Chadd }
598127be1a7SAdrian Chadd for (;;) {
599127be1a7SAdrian Chadd struct country *cp = LIST_FIRST(&rdp->countries);
600127be1a7SAdrian Chadd if (cp == NULL)
601127be1a7SAdrian Chadd break;
602127be1a7SAdrian Chadd LIST_REMOVE(cp, next);
603127be1a7SAdrian Chadd if (cp->name != NULL)
604127be1a7SAdrian Chadd free(__DECONST(char *, cp->name));
605127be1a7SAdrian Chadd free(cp);
606127be1a7SAdrian Chadd }
607127be1a7SAdrian Chadd for (;;) {
608127be1a7SAdrian Chadd struct freqband *fp = LIST_FIRST(&rdp->freqbands);
609127be1a7SAdrian Chadd if (fp == NULL)
610127be1a7SAdrian Chadd break;
611127be1a7SAdrian Chadd LIST_REMOVE(fp, next);
612127be1a7SAdrian Chadd free(fp);
613127be1a7SAdrian Chadd }
614127be1a7SAdrian Chadd }
615127be1a7SAdrian Chadd
616127be1a7SAdrian Chadd struct regdata *
lib80211_alloc_regdata(void)617127be1a7SAdrian Chadd lib80211_alloc_regdata(void)
618127be1a7SAdrian Chadd {
619127be1a7SAdrian Chadd struct regdata *rdp;
620127be1a7SAdrian Chadd struct stat sb;
621127be1a7SAdrian Chadd void *xml;
622127be1a7SAdrian Chadd int fd;
623127be1a7SAdrian Chadd
624127be1a7SAdrian Chadd rdp = calloc(1, sizeof(struct regdata));
625127be1a7SAdrian Chadd
626127be1a7SAdrian Chadd fd = open(_PATH_REGDOMAIN, O_RDONLY);
627127be1a7SAdrian Chadd if (fd < 0) {
628127be1a7SAdrian Chadd #ifdef DEBUG
629127be1a7SAdrian Chadd warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
630127be1a7SAdrian Chadd #endif
631127be1a7SAdrian Chadd free(rdp);
632127be1a7SAdrian Chadd return NULL;
633127be1a7SAdrian Chadd }
634127be1a7SAdrian Chadd if (fstat(fd, &sb) < 0) {
635127be1a7SAdrian Chadd #ifdef DEBUG
636127be1a7SAdrian Chadd warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
637127be1a7SAdrian Chadd #endif
638127be1a7SAdrian Chadd close(fd);
639127be1a7SAdrian Chadd free(rdp);
640127be1a7SAdrian Chadd return NULL;
641127be1a7SAdrian Chadd }
642127be1a7SAdrian Chadd xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
643127be1a7SAdrian Chadd if (xml == MAP_FAILED) {
644127be1a7SAdrian Chadd #ifdef DEBUG
645127be1a7SAdrian Chadd warn("%s: mmap", __func__);
646127be1a7SAdrian Chadd #endif
647127be1a7SAdrian Chadd close(fd);
648127be1a7SAdrian Chadd free(rdp);
649127be1a7SAdrian Chadd return NULL;
650127be1a7SAdrian Chadd }
651127be1a7SAdrian Chadd if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
652127be1a7SAdrian Chadd #ifdef DEBUG
653127be1a7SAdrian Chadd warn("%s: error reading regulatory database", __func__);
654127be1a7SAdrian Chadd #endif
655127be1a7SAdrian Chadd munmap(xml, sb.st_size);
656127be1a7SAdrian Chadd close(fd);
657127be1a7SAdrian Chadd free(rdp);
658127be1a7SAdrian Chadd return NULL;
659127be1a7SAdrian Chadd }
660127be1a7SAdrian Chadd munmap(xml, sb.st_size);
661127be1a7SAdrian Chadd close(fd);
662127be1a7SAdrian Chadd
663127be1a7SAdrian Chadd return rdp;
664127be1a7SAdrian Chadd }
665127be1a7SAdrian Chadd
666127be1a7SAdrian Chadd void
lib80211_free_regdata(struct regdata * rdp)667127be1a7SAdrian Chadd lib80211_free_regdata(struct regdata *rdp)
668127be1a7SAdrian Chadd {
669127be1a7SAdrian Chadd lib80211_regdomain_cleanup(rdp);
670127be1a7SAdrian Chadd free(rdp);
671127be1a7SAdrian Chadd }
672127be1a7SAdrian Chadd
673127be1a7SAdrian Chadd /*
674127be1a7SAdrian Chadd * Lookup a regdomain by SKU.
675127be1a7SAdrian Chadd */
676127be1a7SAdrian Chadd const struct regdomain *
lib80211_regdomain_findbysku(const struct regdata * rdp,enum RegdomainCode sku)677127be1a7SAdrian Chadd lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
678127be1a7SAdrian Chadd {
679127be1a7SAdrian Chadd const struct regdomain *dp;
680127be1a7SAdrian Chadd
681127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) {
682127be1a7SAdrian Chadd if (dp->sku == sku)
683127be1a7SAdrian Chadd return dp;
684127be1a7SAdrian Chadd }
685127be1a7SAdrian Chadd return NULL;
686127be1a7SAdrian Chadd }
687127be1a7SAdrian Chadd
688127be1a7SAdrian Chadd /*
689127be1a7SAdrian Chadd * Lookup a regdomain by name.
690127be1a7SAdrian Chadd */
691127be1a7SAdrian Chadd const struct regdomain *
lib80211_regdomain_findbyname(const struct regdata * rdp,const char * name)692127be1a7SAdrian Chadd lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
693127be1a7SAdrian Chadd {
694127be1a7SAdrian Chadd const struct regdomain *dp;
695127be1a7SAdrian Chadd
696127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) {
697127be1a7SAdrian Chadd if (strcasecmp(dp->name, name) == 0)
698127be1a7SAdrian Chadd return dp;
699127be1a7SAdrian Chadd }
700127be1a7SAdrian Chadd return NULL;
701127be1a7SAdrian Chadd }
702127be1a7SAdrian Chadd
703127be1a7SAdrian Chadd /*
704127be1a7SAdrian Chadd * Lookup a country by ISO country code.
705127be1a7SAdrian Chadd */
706127be1a7SAdrian Chadd const struct country *
lib80211_country_findbycc(const struct regdata * rdp,enum ISOCountryCode cc)707127be1a7SAdrian Chadd lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
708127be1a7SAdrian Chadd {
709127be1a7SAdrian Chadd const struct country *cp;
710127be1a7SAdrian Chadd
711127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) {
712127be1a7SAdrian Chadd if (cp->code == cc)
713127be1a7SAdrian Chadd return cp;
714127be1a7SAdrian Chadd }
715127be1a7SAdrian Chadd return NULL;
716127be1a7SAdrian Chadd }
717127be1a7SAdrian Chadd
718127be1a7SAdrian Chadd /*
719127be1a7SAdrian Chadd * Lookup a country by ISO/long name.
720127be1a7SAdrian Chadd */
721127be1a7SAdrian Chadd const struct country *
lib80211_country_findbyname(const struct regdata * rdp,const char * name)722127be1a7SAdrian Chadd lib80211_country_findbyname(const struct regdata *rdp, const char *name)
723127be1a7SAdrian Chadd {
724127be1a7SAdrian Chadd const struct country *cp;
725127be1a7SAdrian Chadd int len;
726127be1a7SAdrian Chadd
727127be1a7SAdrian Chadd len = strlen(name);
728127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) {
729127be1a7SAdrian Chadd if (strcasecmp(cp->isoname, name) == 0)
730127be1a7SAdrian Chadd return cp;
731127be1a7SAdrian Chadd }
732127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) {
733127be1a7SAdrian Chadd if (strncasecmp(cp->name, name, len) == 0)
734127be1a7SAdrian Chadd return cp;
735127be1a7SAdrian Chadd }
736127be1a7SAdrian Chadd return NULL;
737127be1a7SAdrian Chadd }
738