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 }