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