tparse.y - synk - synchronize files between hosts
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
tparse.y (7845B)
---
1 /*
2 * Copyright (c) 2006 Bob Beck <beck@openbsd.org>
3 * Copyright (c) 2002-2006 Henning Brauer <henning@openbsd.org>
4 * Copyright (c) 2001 Markus Friedl. All rights reserved.
5 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
6 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 %{
22 #include <errno.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "synk.h"
30
31 static TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
32 static struct file {
33 TAILQ_ENTRY(file) entry;
34 FILE *stream;
35 char *name;
36 int lineno;
37 int errors;
38 } *file, *topfile;
39
40 static struct file *pushfile(const char *);
41 static int popfile(void);
42 static int yyparse(void);
43 static int yylex(void);
44 static int yyerror(const char *, ...);
45 static int kwcmp(const void *, const void *);
46 static int lookup(char *);
47 static int lgetc(int);
48 static int lungetc(int);
49 static int findeol(void);
50
51 static struct peers_t *peers = NULL;
52
53 typedef struct {
54 union {
55 int number;
56 char *string;
57 } v;
58 int lineno;
59 } YYSTYPE;
60 %}
61
62 %token PEER ERROR
63 %token <v.string> STRING
64 %token <v.number> NUMBER
65 %%
66
67 grammar : /* empty */
68 | grammar '\n'
69 | grammar main '\n'
70 | grammar error '\n' {
71 file->errors++;
72 }
73 ;
74
75 main : PEER STRING NUMBER {
76 addpeer(peers, $2, $3);
77 }
78 | PEER STRING {
79 addpeer(peers, $2, DEFPORT);
80 }
81 ;
82 %%
83
84 struct keywords {
85 const char *name;
86 int val;
87 };
88
89 static int
90 yyerror(const char *fmt, ...)
91 {
92 char buf[512];
93 va_list ap;
94
95 file->errors++;
96 va_start(ap, fmt);
97 if (vsnprintf(buf, sizeof(buf), fmt, ap) < 0)
98 perror("vsnprintf");
99 va_end(ap);
100 fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, buf);
101 return 0;
102 }
103
104 static int
105 kwcmp(const void *k, const void *e)
106 {
107 return strcmp(k, ((const struct keywords *)e)->name);
108 }
109
110 static int
111 lookup(char *s)
112 {
113 /* this has to be sorted always */
114 static const struct keywords keywords[] = {
115 { "peer", PEER }
116 };
117 const struct keywords *p;
118
119 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
120 sizeof(keywords[0]), kwcmp);
121
122 if (p)
123 return p->val;
124 else
125 return STRING;
126 }
127
128 #define MAXPUSHBACK 128
129
130 static unsigned char *parsebuf;
131 static int parseindex;
132 static unsigned char pushback_buffer[MAXPUSHBACK];
133 static int pushback_index = 0;
134
135 static int
136 lgetc(int quotec)
137 {
138 int c, next;
139
140 if (parsebuf) {
141 /* Read character from the parsebuffer instead of input. */
142 if (parseindex >= 0) {
143 c = parsebuf[parseindex++];
144 if (c != '\0')
145 return c;
146 parsebuf = NULL;
147 } else
148 parseindex++;
149 }
150
151 if (pushback_index)
152 return pushback_buffer[--pushback_index];
153
154 if (quotec) {
155 if ((c = getc(file->stream)) == EOF) {
156 yyerror("reached end of file while parsing "
157 "quoted string");
158 if (file == topfile || popfile() == EOF)
159 return EOF;
160 return quotec;
161 }
162 return c;
163 }
164
165 while ((c = getc(file->stream)) == '\\') {
166 next = getc(file->stream);
167 if (next != '\n') {
168 c = next;
169 break;
170 }
171 yylval.lineno = file->lineno;
172 file->lineno++;
173 }
174
175 while (c == EOF) {
176 if (file == topfile || popfile() == EOF)
177 return EOF;
178 c = getc(file->stream);
179 }
180 return c;
181 }
182
183 static int
184 lungetc(int c)
185 {
186 if (c == EOF)
187 return EOF;
188 if (parsebuf) {
189 parseindex--;
190 if (parseindex >= 0)
191 return c;
192 }
193 if (pushback_index < MAXPUSHBACK-1)
194 return pushback_buffer[pushback_index++] = c;
195 else
196 return EOF;
197 }
198
199 static int
200 findeol(void)
201 {
202 int c;
203
204 parsebuf = NULL;
205 pushback_index = 0;
206
207 /* skip to either EOF or the first real EOL */
208 while (1) {
209 c = lgetc(0);
210 if (c == '\n') {
211 file->lineno++;
212 break;
213 }
214 if (c == EOF)
215 break;
216 }
217 return ERROR;
218 }
219
220 static int
221 yylex(void)
222 {
223 unsigned char buf[8096];
224 unsigned char *p;
225 int quotec, next, c;
226 int token;
227
228 p = buf;
229 while ((c = lgetc(0)) == ' ' || c == '\t')
230 ; /* nothing */
231
232 yylval.lineno = file->lineno;
233 if (c == '#')
234 while ((c = lgetc(0)) != '\n' && c != EOF)
235 ; /* nothing */
236
237 switch (c) {
238 case '\'':
239 case '"':
240 quotec = c;
241 while (1) {
242 if ((c = lgetc(quotec)) == EOF)
243 return 0;
244 if (c == '\n') {
245 file->lineno++;
246 continue;
247 } else if (c == '\\') {
248 if ((next = lgetc(quotec)) == EOF)
249 return 0;
250 if (next == quotec || c == ' ' || c == '\t')
251 c = next;
252 else if (next == '\n')
253 continue;
254 else
255 lungetc(next);
256 } else if (c == quotec) {
257 *p = '\0';
258 break;
259 } else if (c == '\0') {
260 yyerror("syntax error");
261 return findeol();
262 }
263 if (p + 1 >= buf + sizeof(buf) - 1) {
264 yyerror("string too long");
265 return findeol();
266 }
267 *p++ = c;
268 }
269 yylval.v.string = strdup((char *)buf);
270 if (!yylval.v.string)
271 perror("strdup");
272 return STRING;
273 }
274
275 #define allowed_to_end_number(x) \
276 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
277
278 if (c == '-' || isdigit(c)) {
279 do {
280 *p++ = c;
281 if ((unsigned)(p-buf) >= sizeof(buf)) {
282 yyerror("string too long");
283 return findeol();
284 }
285 } while ((c = lgetc(0)) != EOF && isdigit(c));
286 lungetc(c);
287 if (p == buf + 1 && buf[0] == '-')
288 goto nodigits;
289 if (c == EOF || allowed_to_end_number(c)) {
290
291 *p = '\0';
292 yylval.v.number = strtoll((char *)buf, NULL, 10);
293 if (errno == ERANGE) {
294 yyerror("\"%s\" invalid number: %s",
295 buf, strerror(errno));
296 return findeol();
297 }
298 return NUMBER;
299 } else {
300 nodigits:
301 while (p > buf + 1)
302 lungetc(*--p);
303 c = *--p;
304 if (c == '-')
305 return c;
306 }
307 }
308
309 #define allowed_in_string(x) \
310 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
311 x != '{' && x != '}' && x != '<' && x != '>' && \
312 x != '!' && x != '=' && x != '/' && x != '#' && \
313 x != ','))
314
315 if (isalnum(c) || c == ':' || c == '_' || c == '*') {
316 do {
317 *p++ = c;
318 if ((unsigned)(p-buf) >= sizeof(buf)) {
319 yyerror("string too long");
320 return findeol();
321 }
322 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
323 lungetc(c);
324 *p = '\0';
325 if ((token = lookup((char *)buf)) == STRING)
326 if (!(yylval.v.string = strdup((char *)buf)))
327 perror("strdup");
328 return token;
329 }
330 if (c == '\n') {
331 yylval.lineno = file->lineno;
332 file->lineno++;
333 }
334 if (c == EOF)
335 return 0;
336 return c;
337 }
338
339 static struct file *
340 pushfile(const char *name)
341 {
342 struct file *nfile;
343
344 if (!(nfile = calloc(1, sizeof(struct file))))
345 return NULL;
346 if (!(nfile->name = strdup(name))) {
347 free(nfile);
348 return NULL;
349 }
350 if (!(nfile->stream = fopen(nfile->name, "r"))) {
351 free(nfile->name);
352 free(nfile);
353 return NULL;
354 }
355 nfile->lineno = 1;
356 TAILQ_INSERT_TAIL(&files, nfile, entry);
357 return nfile;
358 }
359
360 static int
361 popfile(void)
362 {
363 struct file *prev;
364
365 if ((prev = TAILQ_PREV(file, files, entry)))
366 prev->errors += file->errors;
367 TAILQ_REMOVE(&files, file, entry);
368 fclose(file->stream);
369 free(file->name);
370 free(file);
371 file = prev;
372 return file ? 0 : EOF;
373 }
374
375 int
376 parseconf(struct peers_t *plist, const char *filename)
377 {
378 int errors = 0;
379
380 if (!(file = pushfile(filename))) {
381 fprintf(stderr, "failed to open %s\n", filename);
382 return -1;
383 }
384 topfile = file;
385
386 peers = plist;
387
388 yyparse();
389 errors = file->errors;
390 popfile();
391
392 if (errors != 0)
393 return -1;
394
395 return errors != 0 ? -1 : 0;
396 }