chan.c (chan_open): Actually initialize the error indicator.
[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 -------------------------------------------------*/