chiark / gitweb /
04b0ac561119dd6a9a6a52a744caed8606593f9c
[fwd] / conf.c
1 /* -*-c-*-
2  *
3  * Configuration parsing
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------* 
9  *
10  * This file is part of the `fw' port forwarder.
11  *
12  * `fw' is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * `fw' is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with `fw'; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <mLib/dstr.h>
37 #include <mLib/quis.h>
38 #include <mLib/report.h>
39
40 #include "conf.h"
41 #include "scan.h"
42
43 /*----- Main code ---------------------------------------------------------*/
44
45 /* --- @conf_undelim@ --- *
46  *
47  * Arguments:   @scanner *sc@ = pointer to scanner definition
48  *              @const char *d, *dd@ = pointer to characters to escape
49  *
50  * Returns:     ---
51  *
52  * Use:         Modifies the tokenizer.  Characters in the first list will
53  *              always be considered to begin a word.  Characters in the
54  *              second list will always be allowed to continue a word.
55  */
56
57 void conf_undelim(scanner *sc, const char *d, const char *dd)
58 {
59   sc->wbegin = d;
60   sc->wcont = dd;
61 }
62
63 /* --- @token@ --- *
64  *
65  * Arguments:   @scanner *sc@ = pointer to scanner definition
66  *
67  * Returns:     Type of token scanned.
68  *
69  * Use:         Reads the next token from the character scanner.
70  */
71
72 int token(scanner *sc)
73 {
74 #define SELFDELIM \
75   '{': case '}': case '/': case ',': \
76   case '=': case ':': case ';': \
77   case '.': case '[': case ']'
78
79   int ch;
80
81   DRESET(&sc->d);
82
83   /* --- Main tokenization --- */
84
85   for (;;) {
86     ch = scan(sc);
87
88     /* --- Deal with pushed-back tokens --- */
89
90     if (sc->head->tok) {
91       dstr_puts(&sc->d, sc->head->tok);
92       xfree(sc->head->tok);
93       sc->head->tok = 0;
94       sc->t = sc->head->t;
95       goto done;
96     }
97
98     else if (isspace(ch))
99       ;
100     else switch (ch) {
101
102       /* --- End of file --- */
103
104       case EOF:
105         sc->t = CTOK_EOF;
106         goto done;
107
108       /* --- Comment character --- */
109
110       case '#':
111         do ch = scan(sc); while (ch != EOF && ch != '\n');
112         break;
113
114       /* --- Various self-delimiting characters --- */
115
116       case SELFDELIM:
117         if (!sc->wbegin || strchr(sc->wbegin, ch) == 0) {
118           dstr_putc(&sc->d, ch);
119           dstr_putz(&sc->d);
120           sc->t = ch;
121           goto done;
122         }
123
124       /* --- Bare words --- *
125        *
126        * These aren't as bare any more.  You can now backslash-escape
127        * individual characters, and enclose sections in double-quotes.
128        */
129
130       default: {
131         int q = 0;
132
133         for (;;) {
134           switch (ch) {
135             case EOF:
136               goto word;
137             case '\\':
138               ch = scan(sc);
139               if (ch == EOF)
140                 goto word;
141               DPUTC(&sc->d, ch);
142               break;
143             case '\"':
144               q = !q;
145               break;
146             case SELFDELIM:
147               if (q || (sc->wcont && strchr(sc->wcont, ch)))
148                 goto insert;
149               goto word;
150             default:
151               if (!q && isspace(ch))
152                 goto word;
153             insert:
154               DPUTC(&sc->d, ch);
155               break;
156           }
157           ch = scan(sc);
158         }
159       word:
160         unscan(sc, ch);
161         DPUTZ(&sc->d);
162         sc->t = CTOK_WORD;
163         goto done;
164       }
165     }
166   }
167
168 done:
169   return (sc->t);
170 }
171
172 /* --- @pushback@ --- *
173  *
174  * Arguments:   @scanner *sc@ = pointer to scanner definition
175  *
176  * Returns:     ---
177  *
178  * Use:         Pushes the current token back.  This is normally a precursor
179  *              to pushing a new scanner source.
180  */
181
182 void pushback(scanner *sc)
183 {
184   sc->head->tok = xstrdup(sc->d.buf);
185   sc->head->t = sc->t;
186 }
187
188 /* --- @error@ --- *
189  *
190  * Arguments:   @scanner *sc@ = pointer to scanner definition
191  *              @const char *msg@ = message skeleton string
192  *              @...@ = extra arguments for the skeleton
193  *
194  * Returns:     Doesn't
195  *
196  * Use:         Reports an error at the current scanner location.
197  */
198
199 void error(scanner *sc, const char *msg, ...)
200 {
201   va_list ap;
202   va_start(ap, msg);
203   fprintf(stderr, "%s: %s:%i: ", QUIS, sc->head->src, sc->head->line);
204   vfprintf(stderr, msg, ap);
205   fputc('\n', stderr);
206   exit(1);
207 }
208
209 /* --- @conf_enum@ --- *
210  *
211  * Arguments:   @scanner *sc@ = pointer to a scanner object
212  *              @const char *list@ = comma-separated things to allow
213  *              @unsigned f@ = flags for the search
214  *              @const char *err@ = error message if not found
215  *
216  * Returns:     Index into list, zero-based, or @-1@.
217  *
218  * Use:         Checks whether the current token is a string which matches
219  *              one of the comma-separated items given.  The return value is
220  *              the index (zero-based) of the matched string in the list.
221  *
222  *              The flags control the behaviour if no exact match is found.
223  *              If @ENUM_ABBREV@ is set, and the current token is a left
224  *              substring of exactly one of the possibilities, then that one
225  *              is chosen.  If @ENUM_NONE@ is set, the value @-1@ is
226  *              returned; otherwise an error is reported and the program is
227  *              terminated.
228  */
229
230 int conf_enum(scanner *sc, const char *list, unsigned f, const char *err)
231 {
232   const char *p, *q;
233   int chosen = -1;
234   int ok;
235   int index;
236
237   /* --- Make sure it's a string --- */
238
239   if (sc->t != CTOK_WORD)
240     error(sc, "parse error, expected %s", err);
241
242   /* --- Grind through the list --- */
243
244   q = sc->d.buf;
245   ok = 1;
246   index = 0;
247   p = list;
248   for (;;) {
249     switch (*p) {
250       case 0:
251         if (ok && !*q) {
252           token(sc);
253           return (index);
254         } else if (chosen != -1) {
255           token(sc);
256           return (chosen);
257         }
258         else if (f & ENUM_NONE)
259           return (-1);
260         else
261           error(sc, "unknown %s `%s'", err, sc->d.buf);
262         break;
263       case ',':
264         if (ok && !*q) {
265           token(sc);
266           return (index);
267         }
268         ok = 1;
269         q = sc->d.buf;
270         index++;
271         break;
272       default:
273         if (!ok)
274           break;
275         if ((f & ENUM_ABBREV) && !*q) {
276           if (chosen != -1)
277             error(sc, "ambiguous %s `%s'", err, sc->d.buf);
278           chosen = index;
279           ok = 0;
280         }
281         if (*p == *q)
282           q++;
283         else
284           ok = 0;
285         break;
286     }
287     p++;
288   }
289 }
290
291 /* --- @conf_prefix@ --- *
292  *
293  * Arguments:   @scanner *sc@ = pointer to a scanner object
294  *              @const char *p@ = pointer to prefix string to check
295  *
296  * Returns:     Nonzero if the prefix matches.
297  *
298  * Use:         If the current token is a word matching the given prefix
299  *              string, then it and an optional `.' character are removed and
300  *              a nonzero result is returned.  Otherwise the current token is
301  *              left as it is, and zero is returned.
302  *
303  *              Typical options parsing code would remove an expected prefix,
304  *              scan an option anyway (since qualifying prefixes are
305  *              optional) and if a match is found, claim the option.  If no
306  *              match is found, and a prefix was stripped, then an error
307  *              should be reported.
308  */
309
310 int conf_prefix(scanner *sc, const char *p)
311 {
312   if (sc->t == CTOK_WORD && strcmp(p, sc->d.buf) == 0) {
313     token(sc);
314     if (sc->t == '.')
315       token(sc);
316     return (1);
317   }
318   return (0);
319 }
320
321 /* --- @conf_name@ --- *
322  *
323  * Arguments:   @scanner *sc@ = pointer to scanner
324  *              @char delim@ = delimiter character to look for
325  *              @dstr *d@ = pointer to dynamic string for output
326  *
327  * Returns:     ---
328  *
329  * Use:         Reads in a compound name consisting of words separated by
330  *              delimiters.  Leading and trailing delimiters are permitted,
331  *              although they'll probably cause confusion if used.  The name
332  *              may be enclosed in square brackets if that helps at all.
333  *
334  *              Examples of compound names are filenames (delimited by `/')
335  *              and IP addresses (delimited by `.').
336  */
337
338 void conf_name(scanner *sc, char delim, dstr *d)
339 {
340   unsigned f = 0;
341
342 #define f_ok 1u
343 #define f_bra 2u
344
345   /* --- Read an optional opening bracket --- */
346
347   if (sc->t == '[') {
348     token(sc);
349     f |= f_bra | f_ok;
350   }
351
352   /* --- Do the main reading sequence --- */
353
354   do {
355     if (sc->t == delim) {
356       DPUTC(d, delim);
357       f |= f_ok;
358       token(sc);
359     }
360     if (sc->t == CTOK_WORD) {
361       DPUTD(d, &sc->d);
362       f |= f_ok;
363       token(sc);
364     }
365   } while (sc->t == delim);
366
367   /* --- Check that the string was OK --- */
368
369   if (!(f & f_ok))
370     error(sc, "parse error, name expected");
371
372   /* --- Read a closing bracket --- */
373
374   if (f & f_bra) {
375     if (sc->t == ']')
376       token(sc);
377     else
378       error(sc, "parse error, missing `]'");
379   }
380   DPUTZ(d);
381
382 #undef f_ok
383 #undef f_bra
384 }
385
386 /*----- That's all, folks -------------------------------------------------*/