tUse yacc(1) to parse config file - synk - synchronize files between hosts
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit b3f644e436534e648650db11022bd575defe56f9
(DIR) parent 11db759707a8de9f5dc0686083d679d16a756555
(HTM) Author: Willy <willyatmailoodotorg>
Date: Wed, 7 Sep 2016 12:55:10 +0200
Use yacc(1) to parse config file
Diffstat:
M config.mk | 1 +
M mkfile | 7 +++++--
A parse.y | 404 +++++++++++++++++++++++++++++++
M synk.c | 29 +----------------------------
4 files changed, 411 insertions(+), 30 deletions(-)
---
(DIR) diff --git a/config.mk b/config.mk
t@@ -2,6 +2,7 @@ VERSION = 0.0
CC = cc
LD = ${CC}
+YACC = yacc
PREFIX = /usr/local
MANDIR = ${PREFIX}/man
(DIR) diff --git a/mkfile b/mkfile
t@@ -1,13 +1,16 @@
<config.mk
-synk: synk.o sha512.o
+synk: y.tab.o synk.o sha512.o
$LD -o $target $prereq $LDFLAGS $LIBS
%.o: %.c synk.h
$CC $CFLAGS -c $stem.c -o $stem.o
+y.tab.c: parse.y
+ $YACC $prereq
+
clean:V:
- rm -f *.o synk
+ rm -f *.o synk y.tab.c
install:V: all
mkdir -p ${DESTDIR}${PREFIX}/bin
(DIR) diff --git a/parse.y b/parse.y
t@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2006 Bob Beck <beck@openbsd.org>
+ * Copyright (c) 2002-2006 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "synk.h"
+
+static TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+
+static struct file *pushfile(const char *);
+static int popfile(void);
+static int yyparse(void);
+static int yylex(void);
+static int yyerror(const char *, ...);
+static int kwcmp(const void *, const void *);
+static int lookup(char *);
+static int lgetc(int);
+static int lungetc(int);
+static int findeol(void);
+
+static struct peers_t *peers = NULL;
+
+typedef struct {
+ union {
+ int number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+%}
+
+%token PEER ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar main '\n'
+ | grammar error '\n' {
+ file->errors++;
+ }
+ ;
+
+main : PEER STRING NUMBER {
+ addpeer(peers, $2, $3);
+ }
+ | PEER STRING {
+ addpeer(peers, $2, DEFPORT);
+ }
+ ;
+%%
+
+struct keywords {
+ const char *name;
+ int val;
+};
+
+static int
+yyerror(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vsnprintf(buf, sizeof(buf), fmt, ap) < 0)
+ perror("vsnprintf");
+ va_end(ap);
+ printf("%s:%d: %s", file->name, yylval.lineno, buf);
+ return 0;
+}
+
+static int
+kwcmp(const void *k, const void *e)
+{
+ return strcmp(k, ((const struct keywords *)e)->name);
+}
+
+static int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "peer", PEER }
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kwcmp);
+
+ if (p)
+ return p->val;
+ else
+ return STRING;
+}
+
+#define MAXPUSHBACK 128
+
+static unsigned char *parsebuf;
+static int parseindex;
+static unsigned char pushback_buffer[MAXPUSHBACK];
+static int pushback_index = 0;
+
+static int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return c;
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return pushback_buffer[--pushback_index];
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ return quotec;
+ }
+ return c;
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ c = getc(file->stream);
+ }
+ return c;
+}
+
+static int
+lungetc(int c)
+{
+ if (c == EOF)
+ return EOF;
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return c;
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return pushback_buffer[pushback_index++] = c;
+ else
+ return EOF;
+}
+
+static int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return ERROR;
+}
+
+static int
+yylex(void)
+{
+ unsigned char buf[8096];
+ unsigned char *p;
+ int quotec, next, c;
+ int token;
+
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return 0;
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return 0;
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return findeol();
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return findeol();
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup((char *)buf);
+ if (!yylval.v.string)
+ perror("strdup");
+ return STRING;
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+
+ *p = '\0';
+ yylval.v.number = strtoll((char *)buf, NULL, 10);
+ if (errno == ERANGE) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, strerror(errno));
+ return findeol();
+ }
+ return NUMBER;
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return c;
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup((char *)buf)) == STRING)
+ if (!(yylval.v.string = strdup((char *)buf)))
+ perror("strdup");
+ return token;
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return 0;
+ return c;
+}
+
+static struct file *
+pushfile(const char *name)
+{
+ struct file *nfile;
+
+ if (!(nfile = calloc(1, sizeof(struct file))))
+ return NULL;
+ if (!(nfile->name = strdup(name))) {
+ free(nfile);
+ return NULL;
+ }
+ if (!(nfile->stream = fopen(nfile->name, "r"))) {
+ free(nfile->name);
+ free(nfile);
+ return NULL;
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return nfile;
+}
+
+static int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)))
+ prev->errors += file->errors;
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return file ? 0 : EOF;
+}
+
+int
+parseconf(struct peers_t *plist, const char *filename)
+{
+ struct stat sb;
+ int errors = 0;
+
+ if (stat(filename, &sb) == 0 && sb.st_mode & 007) {
+ printf("%s: shouldn't be readable by others", filename);
+ return -1;
+ }
+
+ if (!(file = pushfile(filename))) {
+ printf("failed to open %s", filename);
+ return -1;
+ }
+ topfile = file;
+
+ peers = plist;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ if (errors != 0)
+ return -1;
+
+ return errors != 0 ? -1 : 0;
+}
(DIR) diff --git a/synk.c b/synk.c
t@@ -274,33 +274,6 @@ getpeermeta(struct peer_t *clt, struct metadata_t *local)
}
/*
- * Load peers from a file
- */
-int
-loadpeers(struct peers_t *plist, const char *fn)
-{
- char host[HOST_NAME_MAX], *lf;
- FILE *f = NULL;
-
- f = fopen(fn, "r");
- if (!f) {
- perror(fn);
- return -1;
- }
-
- while (fgets(host, HOST_NAME_MAX, f)) {
- if ((lf = strchr(host, '\n')) != NULL)
- *lf = '\0';
-
- log(LOG_VERBOSE, "config: %s\n", host);
- addpeer(plist, host, SERVER_PORT);
- }
-
- fclose(f);
- return 0;
-}
-
-/*
* Empty the linked-list containing all peers
*/
int
t@@ -577,7 +550,7 @@ main(int argc, char *argv[])
}ARGEND;
if (hostname == NULL)
- loadpeers(&plist, config);
+ parseconf(&plist, config);
switch(mode) {
case SYNK_CLIENT: