/* This file is part of Mailfromd. Copyright (C) 2009-2018 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 . */ #ifdef HAVE_CONFIG_H # include #endif #include "mailfromd.h" #include "prog.h" /* Opool used for expanding built-in constants in pragmas */ mu_opool_t expand_pool; /* Select next "word" from STRING. Point PSTART to the beginning of the word, and PEND to its end. A "word" is either a sequence of non-whitespace characters, or a sequence of characters enclosed in "" or ''. */ static const char * nextword(const char *string, const char **pstart, const char **pend) { const char *end; while (*string && mu_isspace(*string)) string++; if (*string == '"' || *string == '\'') { int quote = *string; for (end = string + 1; *end && *end != quote; end++) if (quote == '"' && *end == '\\' && *++end == 0) break; end++; } else for (end = string + 1; *end && !mu_isspace(*end); end++) ; *pstart = string; *pend = end; return string; } /* Expand (possible) built-in constant from the first LEN bytes of TEXT. */ void expand_builtin(const char *text, size_t len) { char buf[NUMERIC_BUFSIZE_BOUND]; const char *sval; long nval; switch (builtin_const_value(text, len, &sval, &nval)) { case dtype_string: mu_opool_appendz(expand_pool, sval); break; case dtype_number: snprintf(buf, sizeof(buf), "%ld", nval); mu_opool_appendz(expand_pool, buf); break; default: mu_opool_append(expand_pool, text, len); } } /* Find double underscore in first LEN bytes of TEXT. If found, return 1 and store to POS the offset of the byte past the `__'. */ static int find_double_underscore(const char *text, size_t len, size_t *pos) { const char *q; size_t ret = 0; while (len && (q = memchr(text, '_', len))) { size_t s = q - text + 1; if (q[1] == '_') { *pos = ret + s + 1; return 1; } text = q + 1; len -= s; ret += s; } return 0; } static int is_delim(int c) { return !mu_isalnum(c) || mu_isblank(c); } /* Expand LEN bytes of quoted string TEXT. Constants are expanded only if prefixed by %. */ void expand_string(const char *text, size_t len) { const char *p; while (*text) { size_t s; p = memchr(text, '%', len); if (!p) { mu_opool_append(expand_pool, text, len); break; } s = p - text; mu_opool_append(expand_pool, text, s); text = p + 1; len -= s + 1; if (len > 4 && memcmp(text, "__", 2) == 0 && find_double_underscore(text + 2, len - 2, &s) && is_delim(text[s+2])) { s += 2; /* account for initial __ */ expand_builtin(text, s); text += s; len -= s; } else if (len > 6 && memcmp(text, "{__", 3) == 0 && find_double_underscore(text + 3, len - 3, &s) && text[s + 3] == '}') { expand_builtin(text + 1, s + 2); s += 4; /* "{__" + "}" */ text += s; len -= s; } else { mu_opool_append_char(expand_pool, '%'); } } } void expand_text(const char *text) { const char *end; while (nextword(text, &text, &end)) { size_t len = end - text; if (*text == '"' && end > text && end[-1] == '"') expand_string(text, len); else if (len > 2 && memcmp(text, "__", 2) == 0) expand_builtin(text, len); else mu_opool_append(expand_pool, text, len); if (*end == 0) break; text = end + 1; mu_opool_append_char(expand_pool, ' '); } mu_opool_append_char(expand_pool, 0); } int check_pragma_args(const struct pragma *pdef, int argc) { if (pdef->minargs > 0 && argc < pdef->minargs) { parse_error(_("too few arguments in pragma")); return 1; } if (pdef->maxargs > 0 && argc > pdef->maxargs) { parse_error(_("too many arguments in pragma")); return 1; } return 0; } void parse_pragma(const char *text) { int rc; struct mu_wordsplit ws; const struct pragma *pdef; char *s; while (*text != '#') text++; ++text; while (*text && mu_isspace(*text)) text++; text += 6; /* "pragma" */ mu_opool_create(&expand_pool, MU_OPOOL_ENOMEMABRT); expand_text(text); s = mu_opool_finish(expand_pool, NULL); rc = mu_wordsplit(s, &ws, MU_WRDSF_DEFFLAGS); mu_opool_destroy(&expand_pool); if (rc) { parse_error(_("error parsing pragma: %s"), mu_wordsplit_strerror (&ws)); return; } if (ws.ws_wordc == 0) { parse_error(_("empty pragma")); mu_wordsplit_free(&ws); return; } pdef = lookup_pragma(ws.ws_wordv[0]); if (!pdef) parse_error(_("unknown pragma")); else if (check_pragma_args(pdef, ws.ws_wordc) == 0) pdef->handler(ws.ws_wordc, ws.ws_wordv, text); mu_wordsplit_free(&ws); }