/* This file is part of Mailfromd.
Copyright (C) 2009-2011, 2015-2016 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);
}