From aa31497d9f0a3e96801d3752dd2d8f4ea20a2f4c Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Mon, 16 May 2011 12:18:04 +0300 Subject: Implement parser for git-style config files. * am/grecs.m4: New option: parser-git * doc/GRECS_SETUP.3: Document new options. * src/git-parser.c: New file. * src/Make.am [GRECS_COND_GIT_PARSER]: Define GRECS_PARSER_GIT. (GRECS_SRC): Add GRECS_PARSER_GIT. * src/grecs.h (grecs_git_parser): New proto. * src/txtacc.c (grecs_txtacc_free): Ignore NULL argument. --- am/grecs.m4 | 4 + doc/GRECS_SETUP.3 | 8 +- src/Make.am | 5 + src/git-parser.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/grecs.h | 1 + src/txtacc.c | 8 +- 6 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 src/git-parser.c diff --git a/am/grecs.m4 b/am/grecs.m4 index 9685bca..5611e04 100644 --- a/am/grecs.m4 +++ b/am/grecs.m4 @@ -156,6 +156,10 @@ AC_DEFUN([GRECS_SETUP],[ _GRECS_OPTION_SWITCH([parser-bind],[true], [all-parsers],[true], [false])) + AM_CONDITIONAL([GRECS_COND_GIT_PARSER], + _GRECS_OPTION_SWITCH([parser-git],[true], + [all-parsers],[true], + [false])) AC_SUBST([GRECS_SRCDIR],$1) AC_SUBST([GRECS_BUILD_AUX]) AC_SUBST([GRECS_INCLUDES]) diff --git a/doc/GRECS_SETUP.3 b/doc/GRECS_SETUP.3 index 13b568e..58d1ebe 100644 --- a/doc/GRECS_SETUP.3 +++ b/doc/GRECS_SETUP.3 @@ -14,7 +14,7 @@ .\" You should have received a copy of the GNU General Public License .\" along with Grecs. If not, see . .\" -.TH GRECS_SETUP 3 "May 15, 2011" "GRECS" "Grecs User Reference" +.TH GRECS_SETUP 3 "May 16, 2011" "GRECS" "Grecs User Reference" .SH NAME GRECS_SETUP \- Initialize \fBgrecs\fR submodule. .SH SYNOPSIS @@ -47,6 +47,12 @@ are understood: .B all-parsers Compile all available parsers. .TP +.B parser-bind +Build the parser for BIND configuration files. +.TP +.B parser-git +Build the parser for GIT-style configuration files. +.TP .B parser-meta1 Build the parser for MeTA1 configuration files. .TP diff --git a/src/Make.am b/src/Make.am index e05afc5..312c734 100644 --- a/src/Make.am +++ b/src/Make.am @@ -24,6 +24,10 @@ if GRECS_COND_BIND_PARSER GRECS_EXTRA_BIND = bind-gram.h endif +if GRECS_COND_GIT_PARSER + GRECS_PARSER_GIT = git-parser.c +endif + GRECS_SRC = \ asprintf.c\ diag.c\ @@ -45,6 +49,7 @@ GRECS_SRC = \ version.c\ wordsplit.c\ $(GRECS_PARSER_BIND)\ + $(GRECS_PARSER_GIT)\ $(GRECS_PARSER_META1) noinst_HEADERS = diff --git a/src/git-parser.c b/src/git-parser.c new file mode 100644 index 0000000..e0675ef --- /dev/null +++ b/src/git-parser.c @@ -0,0 +1,297 @@ +/* Git-style configuration file parser for Grecs. + Copyright (C) 2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include + +static FILE *infile; +static int input_char; +static struct grecs_txtacc *acc; + +#define TOK_EOF 0 +#define TOK_EQ '=' +#define TOK_SECTION 256 +#define TOK_KEYWORD 257 +#define TOK_VALUE 258 +#define TOK_ERR -1 + +struct token { + int type; + char *buf; + char *tag; + char chbuf[2]; + int putback; +} tok; + +#define ISSPACE(c) (strchr(" \t\r\f\n", c) != NULL) +#define ISIDENT(c) ((isascii(c) && isalnum(c)) || (c) == '_') +#define ISINITIAL(c) ((isascii(c) && isalpha(c)) || (c) == '_') + +static int +input() +{ + if (!infile || feof(infile)) + return 0; + input_char = fgetc(infile); + if (input_char == '\n') + grecs_current_locus.line++; + return input_char; +} + +static void +unput() +{ + if (!input_char) + return; + if (input_char == '\n') + grecs_current_locus.line--; + ungetc(input_char, infile); +} + +static void +error_recovery() +{ + while (input() && input_char != '\n') + ; +} + +static void +collect_tag() +{ + while (input() && + !(ISSPACE(input_char) || input_char == ']')) + grecs_txtacc_grow_char(acc, input_char); + grecs_txtacc_grow_char(acc, 0); + tok.tag = grecs_txtacc_finish(acc, 0); +} + +static void +collect_string() +{ + while (input()) { + if (input_char == '\\') { + if (!input()) { + grecs_error(&grecs_current_locus, 0, + "unexpected EOF in string"); + break; + } + } else if (input_char == '"') + break; + grecs_txtacc_grow_char(acc, input_char); + } + grecs_txtacc_grow_char(acc, 0); + tok.tag = grecs_txtacc_finish(acc, 0); +} + +static void +gettoken(void) +{ + int putback = tok.putback; + tok.putback = 0; + if (putback) + return; + + tok.buf = tok.tag = NULL; + /* Skip whitespace */ + while (input() && ISSPACE(input_char)) + ; + + if (input_char <= 0) { + tok.type = TOK_EOF; + return; + } + + if (input_char == '[') { + tok.type = TOK_SECTION; + while (input() && + !(ISSPACE(input_char) || input_char == ']')) + grecs_txtacc_grow_char(acc, input_char); + grecs_txtacc_grow_char(acc, 0); + tok.buf = grecs_txtacc_finish(acc, 0); + if (input_char != ']') { + while (input() && ISSPACE(input_char)) + ; + if (input_char == '"') + collect_string(); + else if (input_char != ']') + collect_tag(); + } + if (input_char != ']') { + while (input() && ISSPACE(input_char)) + ; + if (input_char != ']') { + if (isascii(input_char) && isprint(input_char)) + grecs_error(&grecs_current_locus, 0, + "expected `]' but found `%c'", + input_char); + else if (!input_char) + grecs_error(&grecs_current_locus, 0, + "expected `]' but found EOF"); + else + grecs_error(&grecs_current_locus, 0, + "expected `]' but found \\%03o", + (unsigned char)input_char); + tok.type = TOK_ERR; + } + } + return; + } + + if (ISINITIAL(input_char)) { + tok.type = TOK_KEYWORD; + do + grecs_txtacc_grow_char(acc, input_char); + while (input() && ISIDENT(input_char)); + unput(); + grecs_txtacc_grow_char(acc, 0); + tok.buf = grecs_txtacc_finish(acc, 0); + return; + } + + tok.chbuf[0] = input_char; + tok.chbuf[1] = 0; + tok.buf = tok.chbuf; + tok.type = input_char; +} + +static struct grecs_value * +getvalue() +{ + int len; + struct grecs_value *val = grecs_malloc(sizeof(*val)); + while (input() && ISSPACE(input_char) && input_char != '\n') + ; + if (input_char != '\n') + do + grecs_txtacc_grow_char(acc, input_char); + while (input() && input_char != '\n'); + grecs_txtacc_grow_char(acc, 0); + tok.type = TOK_VALUE; + tok.buf = grecs_txtacc_finish(acc, 1); + len = strlen(tok.buf); + while (len > 0 && ISSPACE(tok.buf[len-1])) + tok.buf[--len] = 0; + val->type = GRECS_TYPE_STRING; + val->v.string = tok.buf; + return val; +} + +static int +read_statement(struct grecs_node *parent) +{ + struct grecs_node *node; + + gettoken(); + if (tok.type == TOK_EOF || tok.type == TOK_SECTION) { + tok.putback = 1; + return 0; + } + if (tok.type != TOK_KEYWORD) { + grecs_error(&grecs_current_locus, 0, "syntax error"); + error_recovery(); + return 1; + } + + node = grecs_node_create(grecs_node_stmt, &grecs_current_locus); + node->ident = grecs_strdup(tok.buf); + + gettoken(); + if (tok.type == TOK_EOF) { + grecs_error(&grecs_current_locus, 0, "unexpected EOF"); + grecs_node_free(node); + return 0; + } + if (tok.type != TOK_EQ) { + grecs_error(&grecs_current_locus, 0, + "expected `=', but found `%s'", tok.buf); + error_recovery(); + grecs_node_free(node); + return 1; + } + node->v.value = getvalue(); + grecs_node_bind(parent, node, 1); + return 1; +} + +static void +read_statement_list(struct grecs_node *parent) +{ + while (read_statement(parent)) + ; +} + +static int +read_section(struct grecs_node *parent) +{ + gettoken(); + if (tok.type == TOK_EOF) + return 0; + else if (tok.type == TOK_SECTION) { + struct grecs_node *node = + grecs_node_create(grecs_node_block, + &grecs_current_locus); + node->ident = grecs_strdup(tok.buf); + if (tok.tag) { + struct grecs_value *val= grecs_malloc(sizeof(val[0])); + val->type = GRECS_TYPE_STRING; + val->v.string = grecs_strdup(tok.tag); + node->v.value = val; + } + grecs_node_bind(parent, node, 1); + read_statement_list(node); + } else if (tok.type == TOK_KEYWORD) { + read_statement(parent); + } else { + grecs_error(&grecs_current_locus, 0, "syntax error"); + error_recovery(); + } + return 1; +} + +/* FIXME: traceflags not used */ +struct grecs_node * +grecs_git_parser(const char *name, int traceflags) +{ + struct grecs_node *root; + + infile = fopen(name, "r"); + if (!infile) { + grecs_error(NULL, errno, _("cannot open `%s'"), name); + return NULL; + } + grecs_current_locus.file = grecs_install_text(name); + grecs_current_locus.line = 1; + + acc = grecs_txtacc_create(); + + root = grecs_node_create(grecs_node_root, &grecs_current_locus); + + while (read_section(root)) + ; + fclose(infile); + grecs_txtacc_free(acc); + if (grecs_error_count) { + grecs_tree_free(root); + root = NULL; + } + return root; +} diff --git a/src/grecs.h b/src/grecs.h index 48b53ae..b839d71 100644 --- a/src/grecs.h +++ b/src/grecs.h @@ -218,6 +218,7 @@ extern struct grecs_node *(*grecs_parser_fun)(const char *name, int trace); struct grecs_node *grecs_grecs_parser(const char *name, int traceflags); struct grecs_node *grecs_meta1_parser(const char *name, int traceflags); struct grecs_node *grecs_bind_parser(const char *name, int traceflags); +struct grecs_node *grecs_git_parser(const char *name, int traceflags); struct grecs_list *_grecs_simple_list_create(int dispose); diff --git a/src/txtacc.c b/src/txtacc.c index ff13e42..5ff9ec6 100644 --- a/src/txtacc.c +++ b/src/txtacc.c @@ -105,9 +105,11 @@ grecs_txtacc_create() void grecs_txtacc_free(struct grecs_txtacc *acc) { - grecs_list_free (acc->cur); - grecs_list_free (acc->mem); - free (acc); + if (acc) { + grecs_list_free (acc->cur); + grecs_list_free (acc->mem); + free (acc); + } } void -- cgit v1.2.1