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