%{ /* This file is part of mailfromd. Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff This program 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, or (at your option) any later version. This program 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 this program. If not, see . */ #define MF_SOURCE_NAME MF_SOURCE_LEX #ifdef HAVE_CONFIG_H # include #endif #include #include #define obstack_chunk_alloc malloc #define obstack_chunk_free free #include #include "mailfromd.h" #include "gram.h" #include "prog.h" static struct locus locus; /* Current input location */ static struct obstack string_stk; /* Obstack for constructing string values */ static char *multiline_delimiter; /* End of here-document delimiter */ static size_t multiline_delimiter_len; /* Length of multiline_delimiter_len */ static int multiline_unescape; /* Unescape here-document contents */ static int (*char_to_strip)(char); /* Strip matching characters of each here-document line */ static int is_tab(char c) { return c == '\t'; } static int is_space(char c) { return c == '\t' || c == ' '; } #define line_begin string_begin #define line_add string_add #define line_add_char string_add_char static void line_finish(void); static void string(const char *str, size_t len); static int isemptystr(char *text); static int builtin_const(const char *s); /* External interface to locus */ const struct locus * get_locus() { return &locus; } /* Auxiliary function for returning keyword tokens */ static int keyword(int kw) { yylval.locus = locus; return kw; } /* Input stack support */ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) do { \ if (ext_pp) \ result = fread(buf, 1, max_size, yyin); \ else \ result = pp_fill_buffer(buf, max_size); \ } while (0) static int get_const(const char *name) { struct value *value_ptr; if (value_ptr = constant_lookup(name)) { switch (value_ptr->type) { case dtype_number: yylval.number = value_ptr->v.number; return NUMBER; case dtype_string: yylval.literal = value_ptr->v.literal; return STRING; default: abort(); } } return 0; } static size_t input_line; static void advance_line() { ++locus.line; ++input_line; } static int is_stdin(const char *name) { return name == NULL || strcmp(name, "stdin") == 0 || strcmp(name, "") == 0 || strcmp(name, "-") == 0; } %} /* Exclusive states: COMMENT Within a C-style comment; STR Processing a complex string; ML Within a multi-line aggregator. The line being built requires stripping leading whitespace (if requested). CML Continuation within a multi-line aggregator. The line being built does not require stripping leading whitespace. QML Quoted multi-line aggregator. No variable substitution and unquoting is needed. Inclusive states: ONBLOCK Lexical tie-in after an `on' keyword. In ONBLOCK state the strigns `as', `host', `from', and `poll' are recognized as keywords. The string `for' also acquires special meaning. */ %x COMMENT STR ML CML QML %s ONBLOCK N [0-9][0-9]* P [1-9][0-9]* X [0-9a-fA-F] O [0-7] WS [ \t][ \t]* IDENT [a-zA-Z_][a-zA-Z_0-9]* LOCUS __file__|__line__|__function__ VCONST __package__|__version__|__major__|__minor__|__patch__ ICONST {LOCUS}|{VCONST}|__statedir__|__preproc__ %% /* C-style comments */ "/*" BEGIN(COMMENT); [^*\n]* /* eat anything that's not a '*' */ "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ \n advance_line(); "*"+"/" BEGIN(INITIAL); /* Configuration directives */ ^[ \t]*#[ \t]*{P}[ \t]+\".*\".*\n parse_line_cpp(yytext, &locus); /* Normally, everything within a comment should be ignored, so the exclusive condition for the rule below is an error. Unfortunately, GNU m4 (versions up to 1.4.9) outputs line synchronisation directives in comments, which makes any decent compiler lost trace of which input line it is on. To avoid this, mailfromd handles #line directives even within a C-comment block. The bug report with a patch was sent to bug-m4@gnu.org on 2007-05-24: http://lists.gnu.org/archive/html/bug-m4/2007-05/msg00018.html Hopefully it will be fixed in the next release. */ ^[ \t]*#[ \t]*line[ \t].*\n { struct locus newloc; memset(&newloc, 0, sizeof newloc); parse_line(yytext, &newloc); if (is_stdin(newloc.file)) { if (newloc.line > input_line) locus.line += newloc.line - input_line; else { size_t diff = input_line - newloc.line; if (diff < locus.line) locus.line -= diff; } input_line = newloc.line; } else { locus = newloc; input_line++; } } ^[ \t]*#[ \t]*pragma[ \t].*/\n parse_pragma(yytext); ^[ \t]*#[ \t]*error[ \t].*\n { parse_error("%s", yytext); advance_line(); } /* End-of-line comments */ #.*\n { advance_line(); } #.* /* end-of-file comment */; /* Reserved words */ accept return keyword(ACT_ACCEPT); reject return keyword(ACT_REJECT); tempfail return keyword(ACT_TEMPFAIL); continue return keyword(ACT_CONTINUE); discard return keyword(ACT_DISCARD); add return keyword(ADD); replace return keyword(REPLACE); delete return keyword(DELETE); if return keyword(IF); fi return keyword(FI); else return keyword(ELSE); elif return keyword(ELIF); on return keyword(ON); do return keyword(DO); done return keyword(DONE); matches return keyword(MATCHES); fnmatches return keyword(FNMATCHES); mx{WS}matches return keyword(MXMATCHES); mx{WS}fnmatches return keyword(MXFNMATCHES); when return keyword(WHEN); or return keyword(OR); and return keyword(AND); not return keyword(NOT); next return keyword(NEXT); prog return keyword(PROG); set return keyword(SET); catch return keyword(CATCH); echo return keyword(KW_ECHO); return return keyword(RETURN); returns return keyword(RETURNS); func return keyword(FUNC); switch return keyword(SWITCH); case return keyword(CASE); default return keyword(DEFAULT); string { yylval.type = dtype_string; return TYPE; } number { yylval.type = dtype_number; return TYPE; } const return keyword(CONST); throw return keyword(THROW); loop return keyword(LOOP); while return keyword(WHILE); for return keyword(FOR); break return keyword(BREAK); pass return keyword(PASS); begin return keyword(KW_BEGIN); end return keyword(KW_END); {ICONST} { return builtin_const(yytext); } poll return keyword(POLL); host return keyword(HOST); as return keyword(AS); from return keyword(FROM); /* Variables */ \%({ICONST}) { return builtin_const(yytext+1); } \%{IDENT} { int rc; string(yytext + 1, yyleng - 1); if ((rc = get_const(yylval.literal->text)) != 0) return rc; else return VARIABLE; } \%\{{IDENT}\} { int rc; string(yytext + 2, yyleng - 3); if ((rc = get_const(yylval.literal->text)) != 0) return rc; else return VARIABLE; } /* Positional arguments */ \$# { return ARGCOUNT; } \${P} { yylval.number = strtol(yytext + 1, NULL, 0); return ARG; } /* Sendmail variables */ \${IDENT} { if (yyleng == 2) string(yytext + 1, 1); else { line_begin(); line_add("{", 1); line_add(yytext + 1, yyleng - 1); line_add("}", 1); line_finish(); } return SYMBOL; } \$\{{IDENT}\} { string(yytext+1, yyleng - 1); return SYMBOL; } /* Back-references */ \\{P} { yylval.number = strtoul(yytext+1, NULL, 0); return BACKREF; } /* Numeric strings */ {N}\.{N}\.{N} { string(yytext, yyleng); return XCODE; } [0-9]{3} { string(yytext, yyleng); return CODE; } 0[xX]{X}{X}* { yylval.number = strtoul(yytext, NULL, 16); return NUMBER; }; 0{O}{O}* { yylval.number = strtoul(yytext, NULL, 8); return NUMBER; }; 0|{P} { yylval.number = strtoul(yytext, NULL, 10); return NUMBER; }; /* Strings */ {IDENT} { int paren_follows = 0; enum { is_ident, is_builtin, is_func } state; if (yylval.builtin = builtin_lookup(yytext)) state = is_builtin; else if (yylval.function = function_lookup(yytext)) state = is_func; else state = is_ident; if (state != is_ident) { int c; while ((c = input()) && isascii(c) && isspace(c)) if (c == '\n') advance_line(); paren_follows = (c == '('); unput(c); } if (state == is_builtin) return yylval.builtin->rettype == dtype_unspecified ? BUILTIN_PROC : (paren_follows ? BUILTIN : BUILTIN_P); else if (state == is_func) return yylval.function->rettype == dtype_unspecified ? FUNCTION_PROC : (paren_follows ? FUNCTION : FUNCTION_P); else { string(yytext, yyleng); return IDENTIFIER; } } '[^\n']*' { string(yytext+1, yyleng-2); return STRING; } \"[^\\\"$%\n]*\" { string(yytext+1, yyleng-2); return STRING; } \"[^\\\"$%\n]*\\\n { advance_line(); BEGIN(STR); line_begin(); line_add(yytext + 1, yyleng - 3); } \"[^\\\"$%\n]*/[\\$%] { BEGIN(STR); string(yytext+1, yyleng-1); line_begin(); return STRING; } \"\\x{X}{X}/[\\$%] { BEGIN(STR); line_add_char(strtoul(yytext + 3, NULL, 16)); line_finish(); return STRING; } \"\\x{X}{X} { BEGIN(STR); line_add_char(strtoul(yytext + 3, NULL, 16)); } \"\\0{O}{O}{O}/[\\$%] { BEGIN(STR); line_add_char(strtoul(yytext + 3, NULL, 8)); line_finish(); return STRING; } \"\\0{O}{O}{O} { BEGIN(STR); line_add_char(strtoul(yytext + 3, NULL, 8)); } \"\\[^1-9]/[\\$%] { BEGIN(STR); line_add_char(mu_argcv_unquote_char(yytext[2])); line_finish(); return STRING; } \"\\[^1-9] { BEGIN(STR); line_add_char(mu_argcv_unquote_char(yytext[2])); } [^\\\"$%\n]*\\\n { advance_line(); line_add(yytext, yyleng - 2); } [^\\\"$%\n]*\" { BEGIN(INITIAL); if (yyleng > 1) line_add(yytext, yyleng - 1); line_finish(); return STRING; } [^\\\"$%\n]+/[\\$%] { line_add(yytext, yyleng); line_finish(); line_begin(); return STRING; } \\x{X}{X}/[\\$%] { line_add_char(strtoul(yytext + 2, NULL, 16)); line_finish(); line_begin(); return STRING; } \\x{X}{X} { line_add_char(strtoul(yytext + 2, NULL, 16)); } \\0{O}{O}{O}/[\\$%] { line_add_char(strtoul(yytext + 2, NULL, 8)); line_finish(); line_begin(); return STRING; } \\0{O}{O}{O} { line_add_char(strtoul(yytext + 2, NULL, 8)); } \\[^1-9]/[\\$%] { line_add_char(mu_argcv_unquote_char(yytext[1])); line_finish(); line_begin(); return STRING; } \\[^1-9] { line_add_char(mu_argcv_unquote_char(yytext[1])); } /* Multi-line strings */ "<<"(-" "?)?\\?{IDENT}[ \t]*.*\n | "<<"(-" "?)?'{IDENT}'[ \t]*.*\n { char *p; advance_line(); char_to_strip = NULL; multiline_unescape = 1; line_begin(); p = yytext + 2; if (*p == '-') { ++p; if (*p == ' ') { ++p; char_to_strip = is_space; } else char_to_strip = is_tab; } if (*p == '\\') { p++; multiline_unescape = 0; } if (*p == '\'') { char *q; p++; multiline_unescape = 0; q = strchr(p, '\''); multiline_delimiter_len = q - p; } else multiline_delimiter_len = strcspn(p, " \t"); multiline_delimiter = xmalloc(multiline_delimiter_len + 1); memcpy(multiline_delimiter, p, multiline_delimiter_len); multiline_delimiter[multiline_delimiter_len] = 0; if (multiline_unescape) BEGIN(ML); else BEGIN(QML); } /* Quoted multilines */ [^\\\n]*\n { char *p; advance_line(); p = yytext; if (char_to_strip) for (; char_to_strip (*p); p++) ; if (strlen(p) >= multiline_delimiter_len && memcmp(p, multiline_delimiter, multiline_delimiter_len) == 0 && isemptystr(p + multiline_delimiter_len)) { free (multiline_delimiter); multiline_delimiter = NULL; multiline_delimiter_len = 0; BEGIN(INITIAL); line_finish(); return STRING; } line_add(p, strlen(p)); } /* Unquoted multilines */ [^\\$%\n]+/[\\$%] { char *p = yytext; if (char_to_strip) for (; char_to_strip (*p); p++) ; line_add(p, strlen(p)); line_finish(); BEGIN(CML); return STRING; } [^\\$%\n]+/[\\$%] { line_add(yytext, yyleng); line_finish(); line_begin(); return STRING; } "%%"|"$$" { line_add(yytext, yyleng); line_finish(); return STRING; } [$%] { line_add(yytext, yyleng); } [^\\$%\n]*\n { char *p; advance_line(); p = yytext; if (char_to_strip) for (; char_to_strip (*p); p++) ; if (strlen(p) >= multiline_delimiter_len && memcmp(p, multiline_delimiter, multiline_delimiter_len) == 0 && isemptystr(p + multiline_delimiter_len)) { free (multiline_delimiter); multiline_delimiter = NULL; multiline_delimiter_len = 0; BEGIN(INITIAL); line_finish(); return STRING; } line_add(p, strlen(p)); } [^\\$%\n]*\n { locus.line++; if (yyleng >= multiline_delimiter_len && memcmp(yytext, multiline_delimiter, multiline_delimiter_len) == 0 && isemptystr(yytext + multiline_delimiter_len)) { free (multiline_delimiter); multiline_delimiter = NULL; multiline_delimiter_len = 0; BEGIN(INITIAL); line_finish(); return STRING; } line_add(yytext, yyleng); BEGIN(ML); } /* Other tokens */ {WS} ; \n { advance_line(); } "="|"==" return keyword(EQ); "!=" return keyword(NE); "<" return keyword(LT); "<=" return keyword(LE); ">" return keyword(GT); ">=" return keyword(GE); "&" return keyword(LOGAND); "|" return keyword(LOGOR); "^" return keyword(LOGXOR); "~" return keyword(LOGNOT); . return yytext[0]; %% void init_string_space() { obstack_init(&string_stk); } void free_string_space() { obstack_free(&string_stk, NULL); } char * mf_strdup(const char *str) { string_add(str, strlen(str) + 1); return obstack_finish(&string_stk); } struct literal * string_alloc(const char *str, size_t len) { string_begin(); string_add(str, len); return string_finish(); } static void string(const char *str, size_t len) { yylval.literal = string_alloc(str, len); } void string_begin() { /* nothing */ } struct literal * string_finish() { char *ptr; struct literal *lit; obstack_1grow(&string_stk, 0); ptr = obstack_finish(&string_stk); lit = literal_lookup(ptr); if (lit->text != ptr) obstack_free(&string_stk, ptr); return lit; } static void line_finish() { yylval.literal = string_finish(); if (yy_flex_debug) fprintf(stderr, "constructed line: %s\n", yylval.literal->text); } void string_add(const char *str, size_t len) { obstack_grow(&string_stk, str, len); } void string_add_char(unsigned char c) { obstack_1grow(&string_stk, c); } static void print_parse_message(const struct locus *locus, const char *prefix, const char *fmt, va_list ap) { int n; char buf[512]; /* FIXME */ if (locus && locus->file) n = snprintf(buf, sizeof buf, "%s:%lu: ", locus->file, (unsigned long) locus->line); else n = 0; if (prefix) n += snprintf(buf + n, sizeof buf - n, "%s: ", prefix); vsnprintf(buf + n, sizeof buf - n, gettext(fmt), ap); mu_error(buf); } void parse_warning(char *fmt, ...) { va_list ap; va_start(ap, fmt); print_parse_message(&locus, _("Warning"), fmt, ap); va_end(ap); } void parse_warning_locus(const struct locus *loc, char *fmt, ...) { va_list ap; va_start(ap, fmt); print_parse_message(loc, _("Warning"), fmt, ap); va_end(ap); } void parse_error(char *fmt, ...) { va_list ap; va_start(ap, fmt); print_parse_message(&locus, NULL, fmt, ap); va_end(ap); error_count++; } void parse_error_locus(const struct locus *loc, char *fmt, ...) { va_list ap; va_start(ap, fmt); print_parse_message(loc, NULL, fmt, ap); va_end(ap); error_count++; } static pid_t pp_pid; int source(char *name) { if (ext_pp) { int fd; char *ppcmd; int argc; char **argv; fd = open(name, O_RDONLY); if (fd == -1) { mu_error(_("Cannot open `%s': %s"), name, mu_strerror(errno)); return EX_NOINPUT; } close(fd); pp_make_argcv(&argc, &argv); yyin = pp_extrn_start(argc, argv, &pp_pid); if (!yyin) { mu_error(_("Unable to start external preprocessor `%s': %s"), ppcmd, mu_strerror(errno)); return EX_OSFILE; } } else return pp_init(name) ? EX_NOINPUT : EX_OK; return EX_OK; } int yywrap() { if (yyin) pp_extrn_shutdown(pp_pid); else pp_done(); locus.file = NULL; return 1; } static int isemptystr(char *text) { for (; *text && isspace (*text); text++) ; return *text == 0; } void onblock(int enable) { if (enable) BEGIN(ONBLOCK); else BEGIN(INITIAL); } static int builtin_const(const char *s) { if (strcmp(s, "__file__") == 0) { yylval.literal = literal_lookup(locus.file); return STRING; } else if (strcmp(s, "__line__") == 0) { yylval.number = locus.line; return NUMBER; } else if (strcmp(s, "__function__") == 0) { s = function_name(); string(s, strlen(s)); return STRING; } else if (strcmp(s, "__package__") == 0) { string(PACKAGE_TARNAME, sizeof PACKAGE_TARNAME - 1); return STRING; } else if (strcmp(s, "__version__") == 0) { string(PACKAGE_VERSION, sizeof PACKAGE_VERSION - 1); return STRING; } else if (strcmp(s, "__major__") == 0) { yylval.number = MAILFROMD_VERSION_MAJOR; return NUMBER; } else if (strcmp(s, "__minor__") == 0) { yylval.number = MAILFROMD_VERSION_MINOR; return NUMBER; } else if (strcmp(s, "__patch__") == 0) { yylval.number = MAILFROMD_VERSION_PATCH; return NUMBER; } else if (strcmp(s, "__statedir__") == 0) { string(DEFAULT_STATE_DIR, sizeof DEFAULT_STATE_DIR - 1); return STRING; } else if (strcmp(s, "__preproc__") == 0) { if (!ext_pp) string("", 0); else string(ext_pp, strlen(ext_pp)); return STRING; } abort(); } /* End of lex.l */