/* grecs - Gray's Extensible Configuration System -*- c -*- */ %option noinput %top { #ifdef HAVE_CONFIG_H # include #endif } %{ /* grecs - Gray's Extensible Configuration System Copyright (C) 2007-2016 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 . */ #include #include #include #include #include #include #include #include #include #define YY_USER_ACTION do { \ if (YYSTATE == 0) { \ yylloc.beg = grecs_current_locus_point; \ yylloc.beg.col++; \ } \ grecs_current_locus_point.col += yyleng; \ yylloc.end = grecs_current_locus_point; \ } while (0); #define ISWS(c) ((c)==' '||(c)=='\t') %} %x COMMENT STR BOOL EXPR OWS [ \t\f]* WS [ \t\f][ \t\f]* ID [a-zA-Z_][a-zA-Z_0-9-]* P [1-9][0-9]* %% /* Line directive */ ^[ \t]*#[ \t]*{P}[ \t]+\".*\".*\n { grecs_parse_line_directive_cpp(yytext, &yylloc, &grecs_current_locus_point, NULL); } ^[ \t]*#[ \t]*line[ \t].*\n { grecs_parse_line_directive(yytext, &yylloc, &grecs_current_locus_point, NULL); } /* End-of-line comments */ #.*\n { grecs_locus_point_advance_line(grecs_current_locus_point); } #.* /* end-of-file comment */; "//".*\n { grecs_locus_point_advance_line(grecs_current_locus_point); } "//".* /* end-of-file comment */; "if" return DHCPD_IF; "elsif" return DHCPD_ELSIF; "else" return DHCPD_ELSE; [^\{\n]*\n { char *p; size_t len; for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) ; if (len) { grecs_line_add(p, len); grecs_line_add(" ", 1); } grecs_locus_point_advance_line(grecs_current_locus_point); } [^\{\n]*\{ { char *p; size_t len; unput('{'); for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) ; for (; len > 0 && ISWS(p[len-1]); len--) ; grecs_line_add(p, len); BEGIN(INITIAL); yylval.string = grecs_line_finish(); return DHCPD_EXPR; } [^;\n]*\n { char *p; size_t len; for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) ; if (len) { grecs_line_add(p, len); grecs_line_add(" ", 1); } grecs_locus_point_advance_line(grecs_current_locus_point); } [^;\n]*; { char *p; size_t len; unput(';'); for (p = yytext, len = yyleng - 1; ISWS(*p) && len > 0; p++, len--) ; for (; len > 0 && ISWS(p[len-1]); len--) ; grecs_line_add(p, len); BEGIN(INITIAL); yylval.string = grecs_line_finish(); return DHCPD_EXPR; } /* Identifiers */ {ID} { grecs_line_begin(); grecs_line_add(yytext, yyleng); yylval.string = grecs_line_finish(); return DHCPD_IDENT; } /* Strings */ [a-zA-Z0-9_\.\*/:@-]([a-zA-Z0-9_\./:@-][a-zA-Z0-9_\.\*/:@-]*)? { grecs_line_begin(); grecs_line_add(yytext, yyleng); yylval.string = grecs_line_finish(); return DHCPD_STRING; } /* Quoted strings */ \"[^\\"\n]*\" { grecs_line_begin(); grecs_line_add(yytext + 1, yyleng - 2); yylval.string = grecs_line_finish(); return DHCPD_STRING; } \"[^\\"\n]*\\. | \"[^\\"\n]*\\\n { BEGIN(STR); grecs_line_begin(); grecs_line_acc_grow_unescape_last(yytext + 1, yyleng - 1, &yylloc); } \"[^\\"\n]*\n { BEGIN(STR); grecs_line_begin(); grecs_line_acc_grow(yytext + 1, yyleng - 1); } [^\\"\n]*\\. | \"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng, &yylloc); } [^\\"\n]*\n | \"[^\\"\n]*\n { grecs_line_acc_grow(yytext, yyleng); } [^\\"\n]*\" { BEGIN(INITIAL); if (yyleng > 1) grecs_line_add(yytext, yyleng - 1); yylval.string = grecs_line_finish(); return DHCPD_STRING; } {WS} ; /* Other tokens */ \n { grecs_locus_point_advance_line(grecs_current_locus_point); } [,;{}()!=] return yytext[0]; . { if (isascii(yytext[0]) && isprint(yytext[0])) grecs_error(&yylloc, 0, _("stray character %c"), yytext[0]); else grecs_error(&yylloc, 0, _("stray character \\%03o"), (unsigned char) yytext[0]); } %% void grecs_dhcpd_begin_bool(void) { BEGIN(BOOL); } void grecs_dhcpd_begin_expr(void) { BEGIN(EXPR); } struct dhcpd_input_context { ino_t i_node; dev_t i_dev; struct grecs_locus_point point; grecs_locus_t locus; /* Current input location */ YY_BUFFER_STATE state; FILE *input; }; static struct grecs_list *input_stack; static ino_t i_node; static dev_t i_dev; static void free_context(void *ptr) { free(ptr); } static int cmp_context(const void *a, const void *b) { struct dhcpd_input_context const *ac = a; struct dhcpd_input_context const *bc = b; return !(ac->i_node == bc->i_node && ac->i_dev == bc->i_dev); } static int _push_context(const char *name, ino_t i_node, dev_t i_dev, grecs_locus_t *loc) { struct dhcpd_input_context ctx, *pctx; if (!input_stack) { input_stack = grecs_list_create(); input_stack->free_entry = free_context; input_stack->cmp = cmp_context; } else { ctx.i_dev = i_dev; ctx.i_node = i_node; pctx = grecs_list_locate(input_stack, &ctx); if (pctx) { grecs_error(&yylloc, 0, _("%s has already been included"), name); grecs_error(&pctx->locus, 0, _("this is where the previous inclusion occurred")); return 1; } pctx = grecs_malloc(sizeof(*pctx)); pctx->i_node = i_node; pctx->i_dev = i_dev; if (loc) pctx->locus = *loc; else memset(&pctx->locus, 0, sizeof(pctx->locus)); /* FIXME */ pctx->point = grecs_current_locus_point; pctx->state = YY_CURRENT_BUFFER; pctx->input = yyin; grecs_list_push(input_stack, pctx); } return 0; } static int _pop_context() { struct dhcpd_input_context *pctx; if (!yyin) return 1; if (grecs_preprocessor) pclose(yyin); else fclose(yyin); pctx = grecs_list_pop(input_stack); if (!pctx) { yyin = NULL; return 1; } i_node = pctx->i_node; i_dev = pctx->i_dev; grecs_current_locus_point = pctx->point; yyin = pctx->input; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(pctx->state); grecs_free(pctx); return 0; } int yywrap() { return _pop_context(); } int grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc) { struct stat st; FILE *fp; if (access(name, F_OK)) { int ec = errno; char *tmp = grecs_find_include_file(name, 0); if (!tmp) { grecs_error(loc, ec, _("cannot open `%s'"), name); return 1; } name = grecs_install_text(tmp); free(tmp); } fp = fopen(name, "r"); if (!fp) { grecs_error(loc, errno, _("cannot open `%s'"), name); return 1; } if (fstat(fileno(fp), &st)) { grecs_error(loc, errno, _("can't state %s"), name); fclose(fp); return 1; } if (grecs_preprocessor) { char *cmd = NULL; size_t size = 0; fclose(fp); if (grecs_asprintf(&cmd, &size, "%s \"%s\"", grecs_preprocessor, name)) grecs_alloc_die(); fp = popen(cmd, "r"); if (!fp) { grecs_error(loc, errno, _("cannot open `%s'"), cmd); grecs_free(cmd); return 1; } grecs_free(cmd); } if (_push_context(name, st.st_ino, st.st_dev, loc)) { return 1; } i_node = st.st_ino; i_dev = st.st_dev; yyin = fp; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); grecs_current_locus_point.file = grecs_install_text(name); grecs_current_locus_point.line = 1; grecs_current_locus_point.col = 0; return 0; } void grecs_dhcpd_close_sources() { while (!_pop_context()) ; grecs_list_free(input_stack); input_stack = NULL; }