/* grecs - Gray's Extensible Configuration System -*- c -*- */ %option nounput %top { #ifdef HAVE_CONFIG_H # include #endif } %{ /* grecs - Gray's Extensible Configuration System Copyright (C) 2007-2022 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 #include #include #include /* * Locus tracking */ struct grecs_locus_point grecs_current_locus_point; /* Input file location */ #define YY_USER_ACTION do { \ if (YYSTATE == INITIAL) { \ yylloc.beg = grecs_current_locus_point; \ yylloc.beg.col++; \ } \ grecs_current_locus_point.col += yyleng; \ yylloc.end = grecs_current_locus_point; \ } while (0); /* * Redefine default error reporting function. */ #define YY_FATAL_ERROR(t) grecs_error(NULL, errno, "%s", t) /* * Preprocessing and synclines. */ static int preproc_mode; static int emit_syncline; /* If not null, emit syncline before next output * line. */ static int beginning_of_line; /* True if output is at the beginning of line */ /* * Echo LEN bytes from TEXT to yyout. */ static void echo(char const *text, size_t len) { if (len == 0) return; /* Check if we should print a syncline */ if (beginning_of_line && emit_syncline) { fprintf(yyout, "#line %lu \"%s\"\n", (unsigned long)yylloc.end.line, yylloc.end.file); emit_syncline = 0; } /* Actually echo the text. */ fwrite(text, len, 1, yyout); /* Determine the beginning of line status. */ beginning_of_line = text[len-1] == '\n'; } /* Inform flex that it should use echo. */ #define ECHO echo(yytext, yyleng) #define PPECHO do { if (preproc_mode) ECHO; } while(0) /* * String concatenation support. * * String concatenation is a legacy feature of Grecs parser that instructs * it to concatenate adjacent quoted strings. It is enabled if the * GRECS_OPTION_QUOTED_STRING_CONCAT bit is set in parser options. This * bit causes the lexer to qualify each double-quoted string as QSTRING. */ #define qstring() \ ((grecs_parser_options & GRECS_OPTION_QUOTED_STRING_CONCAT) \ ? QSTRING : STRING) /* * Lexical tie-in variables. */ extern int grecs_yyerror_suppress; /* * Here-document support. */ static char *multiline_delimiter; /* Here-document delimiter text. */ static size_t multiline_delimiter_len; /* Its length. */ static int multiline_unescape; /* Unescape here-document contents */ static int (*char_to_strip)(char); /* Strips matching characters of each here-document line */ static void multiline_begin(char *); static void multiline_add(char *); static char *multiline_strip_tabs(char *text); static int isemptystr(int off); /* * Forward declarations. */ static int string_or_ident(void); static void qstring_locus_fixup(void); static int parse_include(char *text); static int push_source(const char *name, int once); static int pop_source(void); static void ctx_abort(void); /* * Versions of grecs up to 03044f768f didn't recognize comment starters that * followed a non-quoted string constituent, as in inet://127.0.0.1. That * silently encouraged a bad practice of omitting quotes around such strings. * * To discontinue such practice and point to the source of the problem, a * warning is emitted each time a comment starter is encountered immediately * following a non-quoted string constituent. */ static int emit_comment_warning; static void comment_warning(void) { if (emit_comment_warning) { grecs_warning(&yylloc, 0, _("\"%2.2s\" starts comment here; use quotes if you want it to be a part of string, or insert a whitespace to suppress this warning"), yytext); emit_comment_warning = 0; } } /* * The report_diag should be called only when yytext begins with the following * prefix: * ^[ \t]*#[ \t]*.{LEN} * It returns a pointer to yytext past the prefix. */ static char * report_diag(size_t len) { char *p = strchr(yytext, '#'); p += strspn(p, " \t") + len; p += strspn(p, " \t"); yytext[yyleng-1] = 0; if (p[0] == '"' && yytext[yyleng-2] == '"') { char *q, *r; yytext[yyleng-2] = 0; p++; q = r = p; do { if (*r == '\\') { int c = wordsplit_c_unquote_char(*++r); *q = c ? c : *r; } else *q = *r++; } while (*q++ == 0); } return p; } %} %x COMMENT ML STR NQSTR WS [ \t\f][ \t\f]* ID [a-zA-Z_][a-zA-Z_0-9-]* P [1-9][0-9]* NQSC [^ \t\f\n'"(){},;/*] %% /* C-style comments */ "/*" { BEGIN(COMMENT); beginning_of_line = 0; comment_warning(); } { [^*\n]* /* eat anything that's not a '*' */ "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ \n { grecs_locus_point_advance_line(grecs_current_locus_point); emit_syncline = 1; } "*"+"/" { BEGIN(INITIAL); if (preproc_mode) { if (yylloc.end.line != yylloc.beg.line) { if (!(yylloc.beg.line == 1 && yylloc.beg.col == 1)) echo("\n", 1); beginning_of_line = 1; } else beginning_of_line = 0; } } <> { grecs_error(&yylloc, 0, _("end of file in comment")); return 0; } } /* Line directive */ ^[ \t]*#[ \t]*{P}[ \t]+\".*\".*\n { PPECHO; grecs_parse_line_directive_cpp(yytext, &yylloc, &grecs_current_locus_point); } ^[ \t]*#[ \t]*line[ \t].*\n { PPECHO; grecs_parse_line_directive(yytext, &yylloc, &grecs_current_locus_point); } /* Includes */ ^[ \t]*#[ \t]*include(_once)?.*/\n { parse_include(yytext); } /* Error directives */ ^[ \t]*#[ \t]*error[ \t].*\n { grecs_error(&yylloc, 0, "%s", report_diag(sizeof("error"))); } ^[ \t]*#[ \t]*warning[ \t].*\n { grecs_warning(&yylloc, 0, "%s", report_diag(sizeof("warning"))); } ^[ \t]*#[ \t]*abend[ \t].*\n { grecs_error(&yylloc, 0, "%s", report_diag(sizeof("abend"))); ctx_abort(); grecs_yyerror_suppress = 1; return 0; } /* End-of-line comments */ #.*\n { grecs_locus_point_advance_line(grecs_current_locus_point); emit_syncline = 1; } #.* /* end-of-file comment */; "//".*\n { comment_warning(); grecs_locus_point_advance_line(grecs_current_locus_point); emit_syncline = 1; } "//".* { comment_warning(); } /* end of file comment */ /* Non-quoted strings and identifiers */ {NQSC}+ { PPECHO; grecs_line_begin(); grecs_line_add(yytext, yyleng); BEGIN(NQSTR); } [/*]{NQSC}* { PPECHO; grecs_line_begin(); grecs_line_add(yytext, yyleng); BEGIN(NQSTR); } { {NQSC}*/"/"[/*] { PPECHO; emit_comment_warning = 1; grecs_line_add(yytext, yyleng); yylval.string = grecs_line_finish(); BEGIN(INITIAL); return STRING; } {NQSC}*[/*] { PPECHO; grecs_line_add(yytext, yyleng); } {NQSC}+ { PPECHO; grecs_line_add(yytext, yyleng); yylval.string = grecs_line_finish(); BEGIN(INITIAL); return string_or_ident(); } \n { yyless(0); yylloc.end.col--; grecs_current_locus_point.col--; yylval.string = grecs_line_finish(); BEGIN(INITIAL); return string_or_ident(); } . { yyless(0); yylloc.end.col--; grecs_current_locus_point.col--; yylval.string = grecs_line_finish(); BEGIN(INITIAL); return string_or_ident(); } } /* Quoted strings */ \"[^\\"\n]*\" { PPECHO; grecs_line_begin(); grecs_line_add(yytext + 1, yyleng - 2); yylval.string = grecs_line_finish(); qstring_locus_fixup(); return qstring(); } \"[^\\"\n]*\\\n { PPECHO; BEGIN(STR); grecs_line_begin(); grecs_line_acc_grow_unescape_last(yytext + 1, yyleng - 1, &yylloc); grecs_locus_point_advance_line(grecs_current_locus_point); } \"[^\\"\n]*\\. { PPECHO; BEGIN(STR); grecs_line_begin(); grecs_line_acc_grow_unescape_last(yytext + 1, yyleng - 1, &yylloc); } { [^\\"\n]*\\\n { PPECHO; grecs_line_acc_grow_unescape_last(yytext, yyleng, &yylloc); grecs_locus_point_advance_line(grecs_current_locus_point); } [^\\"\n]*\\. { PPECHO; grecs_line_acc_grow_unescape_last(yytext, yyleng, &yylloc); } [^\\"\n]*\" { PPECHO; BEGIN(INITIAL); if (yyleng > 1) grecs_line_add(yytext, yyleng - 1); yylval.string = grecs_line_finish(); qstring_locus_fixup(); return qstring(); } <> { grecs_error(&yylloc, 0, _("end of file in quoted string")); return 0; } } /* Multiline strings */ "<<"(-" "?)?\\?{ID}[ \t]*#.*\n | "<<"(-" "?)?\\?{ID}[ \t]*"//".*\n | "<<"(-" "?)?\\?{ID}[ \t]*\n | "<<"(-" "?)?\"{ID}\"[ \t]*#.*\n | "<<"(-" "?)?\"{ID}\"[ \t]*"//".*\n | "<<"(-" "?)?\"{ID}\"[ \t]*\n { PPECHO; BEGIN(ML); multiline_begin(yytext+2); } { /* Ignore m4 line statements */ ^"#line ".*\n { grecs_locus_point_advance_line(grecs_current_locus_point); } .*\n { char *p = multiline_strip_tabs(yytext); int len = p + multiline_delimiter_len - yytext; if (!strncmp(p, multiline_delimiter, multiline_delimiter_len) && isemptystr(len)) { /* Update end pos */ if (grecs_parser_options & GRECS_OPTION_ADJUST_STRING_LOCATIONS) yylloc.end.col = 1; else yylloc.end.col -= yyleng - len; /* * Adjust current position and put the characters that * follow the delimiter back to input. */ grecs_current_locus_point.col -= yyleng - len; yyless(len); /* Echo the consumed input if preprocessing. */ PPECHO; grecs_free(multiline_delimiter); multiline_delimiter = NULL; BEGIN(INITIAL); yylval.string = grecs_line_finish(); return MSTRING; } else PPECHO; grecs_locus_point_advance_line(grecs_current_locus_point); multiline_add(p); } } {WS} PPECHO; /* Other tokens */ \n { PPECHO; grecs_locus_point_advance_line(grecs_current_locus_point); } [,;{}()] { PPECHO; 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]); } %% /* * Source inclusion support. */ static glob_t include_glob; static size_t include_pos; static int include_once; static int isglob(const char *s) { return s[strcspn(s, "*?[")] != 0; } static int parse_include(char *text) { char *p; int rc = 1; int once; p = strchr(text, '#'); p += strspn(p, " \t") + sizeof("include"); if (strncmp(p, "_once", 5) == 0) { once = 1; p += 5; } else once = 0; p += strspn(p, " \t"); if (!*p) { grecs_error(&yylloc, 0, _("invalid include statement")); return 1; } else { size_t len; int allow_cwd; len = strlen (p); if (p[0] == '<' && p[len - 1] == '>') { allow_cwd = 0; p[len - 1] = 0; p++; } else if (p[0] == '"' && p[len - 1] == '"') { allow_cwd = 1; p[len - 1] = 0; p++; } else { grecs_error(&yylloc, 0, _("invalid include statement")); return 1; } if (isglob(p)) { switch (glob(p, 0, NULL, &include_glob)) { case 0: include_pos = 0; include_once = once; break; case GLOB_NOSPACE: grecs_alloc_die(); case GLOB_NOMATCH: break; default: grecs_error(&yylloc, 0, _("read error")); } p = NULL; } else if (p[0] != '/') { char *q = p; p = grecs_find_include_file(q, allow_cwd); if (!p) grecs_error(&yylloc, 0, _("%s: No such file or directory"), q); } } if (p) rc = push_source(p, once); else if (include_pos < include_glob.gl_pathc) rc = push_source(include_glob.gl_pathv[include_pos++], once); return rc; } /* * Include search path. */ static struct grecs_list *grecs_usr_include_path; static struct grecs_list *grecs_std_include_path; size_t grecs_include_path_count(int flag) { size_t count = 0; if (flag & GRECS_STD_INCLUDE) count += grecs_list_size(grecs_std_include_path); if (flag & GRECS_USR_INCLUDE) count += grecs_list_size(grecs_usr_include_path); return count; } static int foreach_dir(struct grecs_list *list, int flag, int (*fun)(int, const char *, void *), void *data) { int rc = 0; struct grecs_list_entry *ep; for (ep = list->head; rc == 0 && ep; ep = ep->next) rc = fun(flag, ep->data, data); return rc; } int grecs_foreach_include_dir(int flag, int (*fun)(int, const char *, void *), void *data) { int rc = 0; if (flag & GRECS_STD_INCLUDE) rc = foreach_dir(grecs_std_include_path, GRECS_STD_INCLUDE, fun, data); if (rc == 0 && (flag & GRECS_USR_INCLUDE)) rc = foreach_dir(grecs_usr_include_path, GRECS_USR_INCLUDE, fun, data); return rc; } void grecs_include_path_clear() { if (grecs_usr_include_path) grecs_list_clear(grecs_usr_include_path); if (grecs_std_include_path) grecs_list_clear(grecs_std_include_path); } static void incl_free(void *data) { grecs_free(data); } void grecs_include_path_setup_v(char **dirs) { if (!grecs_usr_include_path) { grecs_usr_include_path = grecs_list_create(); grecs_usr_include_path->free_entry = incl_free; } grecs_std_include_path = grecs_list_create(); grecs_std_include_path->free_entry = incl_free; if (dirs) { int i; for (i = 0; dirs[i]; i++) /* FIXME: Element never freed */ grecs_list_append(grecs_std_include_path, grecs_strdup(dirs[i])); } } void grecs_include_path_setup(const char *dir, ...) { const char *p; char **argv = NULL; size_t argc = 0; size_t argi = 0; va_list ap; va_start(ap, dir); p = dir; while (1) { if (argi == argc) { if (argc == 0) argc = 16; else argc += 16; argv = grecs_realloc(argv, argc * sizeof(argv[0])); } argv[argi++] = (char*) p; if (!p) break; p = va_arg(ap, const char*); } grecs_include_path_setup_v(argv); grecs_free(argv); va_end(ap); } void grecs_preproc_add_include_dir(char *dir) { if (!grecs_usr_include_path) { grecs_usr_include_path = grecs_list_create(); grecs_usr_include_path->free_entry = incl_free; } grecs_list_append(grecs_usr_include_path, grecs_strdup(dir)); } struct file_data { const char *name; size_t namelen; char *buf; size_t buflen; int found; }; static int pp_list_find(struct grecs_list *list, struct file_data *dptr) { struct grecs_list_entry *ep; if (!list) return 0; for (ep = list->head; !dptr->found && ep; ep = ep->next) { const char *dir = ep->data; size_t size = strlen (dir) + 1 + dptr->namelen + 1; if (size > dptr->buflen) { dptr->buflen = size; dptr->buf = grecs_realloc(dptr->buf, dptr->buflen); } strcpy(dptr->buf, dir); strcat(dptr->buf, "/"); strcat(dptr->buf, dptr->name); dptr->found = access(dptr->buf, F_OK) == 0; } return dptr->found; } char * grecs_find_include_file(const char *name, int allow_cwd) { static char *cwd = "."; struct file_data fd; fd.name = name; fd.namelen = strlen(name); fd.buf = NULL; fd.buflen = 0; fd.found = 0; if (!grecs_usr_include_path) grecs_include_path_setup(NULL); if (allow_cwd) { grecs_list_append(grecs_usr_include_path, cwd); pp_list_find(grecs_usr_include_path, &fd); grecs_list_remove_tail(grecs_usr_include_path); } else pp_list_find(grecs_usr_include_path, &fd); if (!fd.found) { pp_list_find(grecs_std_include_path, &fd); if (!fd.found) return NULL; } return fd.buf; } /* * Input context support. */ struct input_file_ident { ino_t i_node; dev_t device; pid_t pid; }; struct buffer_ctx { struct buffer_ctx *prev; /* Pointer to previous context */ struct grecs_locus locus; struct grecs_locus_point point; /* Current input location */ size_t namelen; /* Length of the file name */ struct input_file_ident id; FILE *infile; YY_BUFFER_STATE state; }; static struct buffer_ctx *context_stack; static struct input_file_ident input_file_ident; static int sigchld_reset; static struct sigaction oldsigchld; static void sigchld_default(void) { if (!sigchld_reset) { struct sigaction act; act.sa_flags = 0; act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, &oldsigchld); sigchld_reset = 1; } } static void sigchld_restore(void) { if (sigchld_reset) { sigaction(SIGCHLD, &oldsigchld, NULL); sigchld_reset = 0; } } #define STAT_ID_EQ(st,id) ((id).i_node == (st).st_ino \ && (id).device == (st).st_dev) static struct buffer_ctx * ctx_lookup(struct stat *st) { struct buffer_ctx *ctx; if (!context_stack) return NULL; for (ctx = context_stack; ctx; ctx = ctx->prev) if (STAT_ID_EQ(*st, ctx->id)) break; return ctx; } static void ctx_abort(void) { /* Delete current input buffer */ yy_delete_buffer(YY_CURRENT_BUFFER); /* Clean-up context stack */ while (context_stack) { struct buffer_ctx *prev = context_stack->prev; if (context_stack->id.pid > 0) kill(context_stack->id.pid, SIGKILL); fclose(context_stack->infile); grecs_free(context_stack); context_stack = prev; } } static struct grecs_symtab *incl_sources; /* Calculate the hash of a struct input_file_ident. */ static unsigned incl_hasher(void *data, unsigned long n_buckets) { const struct input_file_ident *id = data; return (id->i_node + id->device) % n_buckets; } /* Compare two input_file_idents for equality. */ static int incl_compare(void const *data1, void const *data2) { const struct input_file_ident *id1 = data1; const struct input_file_ident *id2 = data2; return !(id1->device == id2->device && id1->i_node == id2->i_node); } static int incl_copy(void *dst, void *src) { memcpy(dst, src, sizeof(struct input_file_ident)); return 0; } static int source_lookup(struct stat *st) { struct input_file_ident key; int install = 1; if (!incl_sources) { incl_sources = grecs_symtab_create( sizeof(struct input_file_ident), incl_hasher, incl_compare, incl_copy, NULL,/*FIXME: alloc*/ NULL); if (!incl_sources) grecs_alloc_die(); } key.i_node = st->st_ino; key.device = st->st_dev; if (!grecs_symtab_lookup_or_install(incl_sources, &key, &install)) grecs_alloc_die(); return !install; } static int push_source(const char *name, int once) { FILE *fp; pid_t pid; struct buffer_ctx *ctx; struct stat st; int rc = stat(name, &st); if (context_stack) { if (rc) { grecs_error(&yylloc, errno, _("Cannot stat `%s'"), name); return 1; } if (grecs_current_locus_point.col && STAT_ID_EQ(st, input_file_ident)) { grecs_error(&yylloc, 0, _("Recursive inclusion")); return 1; } if ((ctx = ctx_lookup(&st))) { grecs_error(&yylloc, 0, _("Recursive inclusion")); if (ctx->prev) grecs_error(&ctx->prev->locus, 0, _("`%s' already included here"), name); else grecs_error(&yylloc, 0, _("`%s' already included at top level"), name); return 1; } } else if (rc) { grecs_error(NULL, errno, _("Cannot stat `%s'"), name); return 1; } if (once && source_lookup(&st)) return -1; if (grecs_preprocessor) { if (access(name,R_OK)) { grecs_error(context_stack ? &yylloc : NULL, errno, _("Cannot open `%s'"), name); return -1; } if (!context_stack) { /* * Ensure default handling of SIGCHLD. * It will be reset back to its original state * by pop_source, when it has popped off the initial * context. */ sigchld_default(); } fp = grecs_preproc_extrn_start(name, &pid); } else { fp = fopen(name, "r"); pid = -1; } if (!fp) { grecs_error(context_stack ? &yylloc : NULL, errno, _("Cannot open `%s'"), name); if (!context_stack) sigchld_restore(); return 1; } /* Push current context */ if (yyin) { ctx = grecs_malloc(sizeof(*ctx)); ctx->locus = yylloc; ctx->point = grecs_current_locus_point; ctx->id = input_file_ident; ctx->infile = yyin; ctx->state = YY_CURRENT_BUFFER; ctx->prev = context_stack; context_stack = ctx; } 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; input_file_ident.i_node = st.st_ino; input_file_ident.device = st.st_dev; input_file_ident.pid = pid; beginning_of_line = 1; if (yy_flex_debug) fprintf (stderr, "Processing file `%s'\n", name); if (preproc_mode) emit_syncline = 1; return 0; } static int pop_source(void) { struct buffer_ctx *ctx; fclose(yyin); if (input_file_ident.pid > 0) { if (grecs_preproc_extrn_shutdown(input_file_ident.pid)) { /* Abort further input */ ctx_abort(); /* Suppress further errors */ grecs_yyerror_suppress = 1; /* Indicate there is no more input */ return 1; } } if (!context_stack) { if (yy_flex_debug) fprintf(stderr, "End of input\n"); /* Reset SIGCHLD to its original configuration */ sigchld_restore(); yyin = NULL; return 1; } /* Restore previous context */ yyin = context_stack->infile; grecs_current_locus_point = context_stack->point; input_file_ident = context_stack->id; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(context_stack->state); ctx = context_stack->prev; grecs_free(context_stack); context_stack = ctx; while (include_pos < include_glob.gl_pathc) { if (push_source(include_glob.gl_pathv[include_pos++], include_once) == 0) return 0; } if (include_glob.gl_pathc) { globfree(&include_glob); include_pos = include_glob.gl_pathc = 0; } if (yy_flex_debug) fprintf(stderr, "Resuming file `%s' at line %lu\n", grecs_current_locus_point.file, (unsigned long) grecs_current_locus_point.line); if (preproc_mode) { input(); grecs_locus_point_advance_line(grecs_current_locus_point); emit_syncline = 1; } return 0; } /* * Internal lexer functions. */ int yywrap() { return pop_source(); } int grecs_lex_begin(const char *name, int trace) { yy_flex_debug = trace; grecs_line_acc_create(); return push_source(name, 1); } void grecs_lex_end(int err) { grecs_symtab_clear(incl_sources); grecs_line_acc_free(); } /* * Here-document functions. */ static int isemptystr(int off) { for (; yytext[off] && isspace(yytext[off]); off++) ; return yytext[off] == ';' || yytext[off] == 0; } char * multiline_strip_tabs(char *text) { if (char_to_strip) for (; *text && char_to_strip(*text); text++) ; return text; } static void multiline_add(char *s) { if (multiline_unescape) { for (; *s; s++) { if (*s == '\\') { grecs_line_acc_grow_char_unescape(s[1]); ++s; } else grecs_line_acc_grow_char(*s); } } else grecs_line_add(s, strlen(s)); } static int is_tab(char c) { return c == '\t'; } static int is_ws(char c) { return c == '\t' || c == ' '; } void multiline_begin(char *p) { if (*p == '-') { if (*++p == ' ') { char_to_strip = is_ws; p++; } else char_to_strip = is_tab; } else char_to_strip = NULL; if (*p == '\\') { p++; multiline_unescape = 0; multiline_delimiter_len = strcspn(p, " \t"); } else if (*p == '"') { char *q; p++; multiline_unescape = 0; q = strchr(p, '"'); multiline_delimiter_len = q - p; } else { multiline_delimiter_len = strcspn(p, " \t"); multiline_unescape = 1; } /* Remove trailing newline */ multiline_delimiter_len--; multiline_delimiter = grecs_malloc(multiline_delimiter_len + 1); memcpy(multiline_delimiter, p, multiline_delimiter_len); multiline_delimiter[multiline_delimiter_len] = 0; grecs_line_begin(); /* Update locus */ grecs_locus_point_advance_line(grecs_current_locus_point); if (grecs_parser_options & GRECS_OPTION_ADJUST_STRING_LOCATIONS) { yylloc.beg = grecs_current_locus_point; yylloc.beg.col++; } } /* * Auxiliary lexer functions. */ static int string_or_ident(void) { char *p = yylval.string; if (isalpha(*p) || *p == '_') { while (*++p) { if (!(isalnum(*p) || *p == '_' || *p == '-')) return STRING; } return IDENT; } return STRING; } static void qstring_locus_fixup(void) { if (grecs_parser_options & GRECS_OPTION_ADJUST_STRING_LOCATIONS) { yylloc.beg.col++; yylloc.end.col--; } } grecs_value_t * grecs_value_ptr_from_static(grecs_value_t *input) { grecs_value_t *ptr = grecs_malloc(sizeof(*ptr)); *ptr = *input; return ptr; } /* * Syncline directive parsers. */ static int assign_locus(struct grecs_locus_point *ploc, char *name, char *line) { char *p; if (name) { ploc->file = grecs_install_text(name); } ploc->line = strtoul(line, &p, 10); ploc->col = 0; return *p != 0; } void grecs_parse_line_directive(char *text, grecs_locus_t *ploc, struct grecs_locus_point *ppoint) { int rc = 1; struct wordsplit ws; if (wordsplit(text, &ws, WRDSF_DEFFLAGS)) grecs_error(ploc, 0, _("cannot parse #line line: %s"), wordsplit_strerror(&ws)); else if (ws.ws_wordc == 2) rc = assign_locus(ppoint, NULL, ws.ws_wordv[1]); else if (ws.ws_wordc >= 3) rc = assign_locus(ppoint, ws.ws_wordv[2], ws.ws_wordv[1]); else grecs_error(ploc, 0, _("invalid #line statement")); if (rc) grecs_error(ploc, 0, _("malformed #line statement")); wordsplit_free(&ws); } void grecs_parse_line_directive_cpp(char *text, grecs_locus_t *ploc, struct grecs_locus_point *ppoint) { struct wordsplit ws; if (wordsplit(text, &ws, WRDSF_DEFFLAGS)) grecs_error(ploc, 0, _("cannot parse #line line: %s"), wordsplit_strerror(&ws)); else if (ws.ws_wordc < 3) grecs_error(ploc, 0, _("invalid #line statement")); else if (assign_locus(ppoint, ws.ws_wordv[2], ws.ws_wordv[1])) grecs_error(ploc, 0, _("malformed #line statement")); wordsplit_free(&ws); } /* * External preprocessor support. */ const char *grecs_preprocessor = NULL; /* Preprocessor command */ int grecs_log_to_stderr = 1; /* Log to stderr or to syslog? */ void (*grecs_log_setup_hook) () = NULL;/* Log subsystem setup routine. */ static int preproc_file(const char *file) { size_t size = 0; char *cmd = NULL; char *setup_file; char *argv[3]; int rc; setup_file = grecs_find_include_file("pp-setup", 0); if (setup_file) rc = grecs_asprintf(&cmd, &size, "%s \"%s\" \"%s\"", grecs_preprocessor, setup_file, file); else rc = grecs_asprintf(&cmd, &size, "%s \"%s\"", grecs_preprocessor, file); if (rc) grecs_alloc_die(); if ((argv[0] = getenv("SHELL")) == NULL) argv[0] = "/bin/sh"; argv[1] = "-c"; argv[2] = cmd; argv[3] = NULL; execvp(argv[0], argv); return -1; } FILE * grecs_preproc_extrn_start(const char *file_name, pid_t *ppid) { int pout[2]; pid_t pid; int i; FILE *fp = NULL; int rc; fflush(yyout); if (pipe(pout)) { grecs_error(NULL, errno, "pipe"); return NULL; } switch (pid = fork()) { /* The child branch. */ case 0: if (pout[1] != 1) { if (dup2(pout[1], 1) == -1) { grecs_error(NULL, errno, "dup2"); exit(127); } } if (!grecs_log_to_stderr) { int p[2]; char *buf = NULL; size_t size = 0; FILE *fp; signal(SIGCHLD, SIG_DFL); if (pipe(p)) { grecs_error(NULL, errno, "pipe"); exit(127); } switch (pid = fork()) { /* Grandchild */ case 0: if (p[1] != 2 && dup2(p[1], 2) == -1) { grecs_error(NULL, errno, "dup2"); exit(127); } close(p[0]); rc = preproc_file(file_name); break; case -1: /* Fork failed */ if (grecs_log_setup_hook) grecs_log_setup_hook(); grecs_error(NULL, errno, _("Cannot run `%s'"), grecs_preprocessor); exit(127); default: /* Sub-master */ close (p[1]); fp = fdopen(p[0], "r"); if (grecs_log_setup_hook) grecs_log_setup_hook(); while (grecs_getline(&buf, &size, fp) > 0) { size_t n = strlen(buf); if (buf[n-1] == '\n') buf[n-1] = 0; grecs_error(NULL, 0, "%s", buf); } wait(&i); if (WIFEXITED(i)) rc = WEXITSTATUS(i); else rc = 127; } } else { rc = preproc_file(file_name); } if (rc == -1) exit(127); exit(rc); case -1: /* Fork failed */ grecs_error(NULL, errno, _("Cannot run `%s'"), grecs_preprocessor); break; default: close(pout[1]); fp = fdopen(pout[0], "r"); break; } *ppid = pid; return fp; } int grecs_preproc_extrn_shutdown(pid_t pid) { int status; if (waitpid(pid, &status, 0) != pid) { grecs_error(NULL, errno, "waitpid(%d)", pid); } else if (WIFEXITED(status)) { if (WEXITSTATUS(status)) { grecs_error(NULL, 0, _("%s exited with status %d"), grecs_preprocessor, WEXITSTATUS(status)); return -1; } } else if (WIFSIGNALED(status)) { grecs_error(NULL, 0, _("%s terminated on signal %d"), grecs_preprocessor, WTERMSIG(status)); return -1; } else { grecs_error(NULL, 0, _("%s terminated abnormally (%d)"), grecs_preprocessor, status); return -1; } return 0; } int grecs_preprocess(char const *name, int trace) { int rc; yy_flex_debug = trace; yyout = stdout; grecs_line_acc_create(); preproc_mode = 1; rc = push_source(name, 1); if (rc == 0) { int t; while ((t = yylex()) != 0) { if (trace) { char *p; YY_LOCATION_PRINT(stderr, yylloc); fprintf(stderr, ": %d: ", t); switch (t) { case STRING: case QSTRING: case MSTRING: fputc('"', stderr); for (p = yylval.string; *p; p++) { if (*p == '\\' || *p == '"') fputc('\\', stderr); fputc(*p, stderr); } fputc('"', stderr); break; case IDENT: fprintf(stderr, "%s", yylval.string); break; default: fputc(t, stderr); } fputc('\n', stderr); } } grecs_lex_end(0); } return rc; } /* * Additional function. * * Not used by the lexer, but retained for backward compatibility. */ ssize_t grecs_getline(char **pbuf, size_t *psize, FILE *fp) { char *buf = *pbuf; size_t size = *psize; ssize_t off = 0; if (!buf) { size = 1; buf = grecs_malloc(size); } do { if (off == size - 1) { size_t nsize = 2 * size; if (nsize < size) grecs_alloc_die(); buf = grecs_realloc(buf, nsize); size = nsize; } if (!fgets(buf + off, size - off, fp)) { if (off == 0) off = -1; break; } off += strlen(buf + off); } while (buf[off - 1] != '\n'); *pbuf = buf; *psize = size; return off; }