aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-10-02 00:31:09 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-10-02 01:37:17 +0300
commitebf2f1f10af0cfd6ce665a44eeaf3a68bef1c7ed (patch)
tree3f83e6c47efbe558e350855fb2d3bc6b281024d4
parent5516ec318d5e9d1b69a6064eaf8974dd13e2011d (diff)
downloadmailfromd-ebf2f1f10af0cfd6ce665a44eeaf3a68bef1c7ed.tar.gz
mailfromd-ebf2f1f10af0cfd6ce665a44eeaf3a68bef1c7ed.tar.bz2
Fix scoping in imported modules.
When handling `import' (`require') statements, mailfromd in some cases could have closed and popped the module lexical context before finishing processing its actual contents. In particular, if a module ended with a variable or constant declaration (e.g. status.mf), this declaration would end up in the symbol table of the importing compilation unit. This happened because yywrap, triggered by EOF on the module unit, caused immediate popping of the lexical contents, whereas the parser still had some unreduced symbols on its stack. The actual reduction (e.g. of the rule `stmt: CONST value') happened after the lexer had supplied next symbol, but by that time the original module had already been removed from stack. * mfd/gram.y (BYE): New keyword. (MODBEG, MODEND): New (pseudo)keywords. (input rule): Allow programs to begin with `module'. (modcntl): Fix parsing of the imported files. * mfd/lex.l (keyword): Rewrite as a macro. (INCTX_MODULE,INCTX_HADINPUT): New defines. (struct inctx): New member inctx_flags. (inctx_flags, emit_token): New statics. (lex_new_source): Change semantics of the second argument. (YY_USER_ACTION, YY_INPUT, YY_DECL): New defines. (module): Warn if module is used in included file, or if there were some statements before it. (yylex): New function. (lex_close_source): Error checking: yyin may be NULL on entry. (lex_drain_input, lex_bye): New functions. (yywrap): Set emit_token to MODEND if within a module. (LEX_NONE, LEX_ONCE, LEX_MODULE): New defines. (lex_bye, lex_drain_input): New functions. * mfd/pp.c (begin_module, parse_include): Update calls to lex_new_source. * mfd/status.mfi: Declare a module. * mflib/Makefile.am (install-data-local): Remove. * mflib/dns.mf4, mflib/gettext.mf, mflib/heloarg_test.mf, mflib/is_ip.mf, mflib/localdomain.mf, mflib/match_cidr.mf, mflib/match_dnsbl.mf, mflib/match_rhsbl.mf, mflib/rateok.mf, mflib/revip.mf, mflib/sa.mf, mflib/safedb.mf4, mflib/sieve.mf, mflib/sockmap.mf, mflib/spf.mf, mflib/strip_domain_part.mf, mflib/valid_domain.mf, mflib/verp.mf: Declare as modules.
-rw-r--r--mfd/Makefile.am12
-rw-r--r--mfd/drivers.c5
-rw-r--r--mfd/gram.y56
-rw-r--r--mfd/lex.l100
-rw-r--r--mfd/mailfromd.h9
-rw-r--r--mfd/pp.c9
-rw-r--r--mfd/status.mfh3
-rw-r--r--mfd/status.mfi4
-rw-r--r--mflib/Makefile.am7
-rw-r--r--mflib/dns.mf42
-rw-r--r--mflib/gettext.mf2
-rw-r--r--mflib/heloarg_test.mf1
-rw-r--r--mflib/is_ip.mf2
-rw-r--r--mflib/localdomain.mf2
-rw-r--r--mflib/match_cidr.mf2
-rw-r--r--mflib/match_dnsbl.mf2
-rw-r--r--mflib/match_rhsbl.mf2
-rw-r--r--mflib/rateok.mf2
-rw-r--r--mflib/revip.mf2
-rw-r--r--mflib/sa.mf2
-rw-r--r--mflib/safedb.mf41
-rw-r--r--mflib/sieve.mf1
-rw-r--r--mflib/sockmap.mf1
-rw-r--r--mflib/spf.mf1
-rw-r--r--mflib/strip_domain_part.mf1
-rw-r--r--mflib/valid_domain.mf1
-rw-r--r--mflib/verp.mf1
27 files changed, 181 insertions, 52 deletions
diff --git a/mfd/Makefile.am b/mfd/Makefile.am
index b197fa05..23010434 100644
--- a/mfd/Makefile.am
+++ b/mfd/Makefile.am
@@ -110,6 +110,7 @@ EXTRA_DIST = \
optab.oph\
snarf.m4\
status.mf\
+ status.mfh\
status.mfi
BUILT_SOURCES=\
@@ -198,14 +199,3 @@ debug.h: Makefile.in debugdef.m4 debug.hin
AM_YFLAGS=-dtv
AM_LFLAGS=-dvp
-
-install-data-local:
- @here=`pwd`; \
- cd $(DESTDIR)$(incdir); \
- rm -f status.mfh; \
- $(LN_S) status.mf status.mfh; \
- cd $$here
- -test -d $(DESTDIR)$(DEFAULT_STATE_DIR) || \
- $(mkinstalldirs) -o $(DEFAULT_USER) $(DESTDIR)$(DEFAULT_STATE_DIR)
-
-
diff --git a/mfd/drivers.c b/mfd/drivers.c
index 9b6d3ea6..3c661fd6 100644
--- a/mfd/drivers.c
+++ b/mfd/drivers.c
@@ -1703,6 +1703,11 @@ code_type_call(NODE *node, struct locus **old_locus)
code_node(p);
MARK_LOCUS();
+ if (func->entry == 0) {
+ parse_error("INTERNAL ERROR at %s:%d: unresolved function %s",
+ __FILE__, __LINE__, func->sym.name);
+ abort();
+ }
code_op(opcode_funcall);
s = literal_lookup(func->sym.name);
code_immediate((void*)s->off);
diff --git a/mfd/gram.y b/mfd/gram.y
index bc6f7fe7..37d624be 100644
--- a/mfd/gram.y
+++ b/mfd/gram.y
@@ -357,7 +357,8 @@ _create_alias(void *item, void *data)
FOR LOOP WHILE BREAK NEXT ARGCOUNT ALIAS DOTS ARGX VAPTR
PRECIOUS
OR AND EQ NE LT LE GT GE NOT LOGAND LOGOR LOGXOR LOGNOT
- REQUIRE IMPORT STATIC PUBLIC MODULE
+ REQUIRE IMPORT STATIC PUBLIC MODULE BYE
+%token MODBEG MODEND
%token <literal> STRING
%token <literal> SYMBOL IDENTIFIER
%token <number> ARG NUMBER BACKREF
@@ -388,7 +389,7 @@ _create_alias(void *item, void *data)
common_expr simp_expr atom_expr
asgn catch throw return case_cond autodcl constdecl
loopstmt opt_while jumpstmt
-%type <stmtlist> stmtlist decllist
+%type <stmtlist> program stmtlist decllist modcntl
%type <ret> triplet maybe_triplet
%type <poll> pollstmt pollarglist
%type <pollarg> pollarg loop_parm
@@ -414,7 +415,7 @@ _create_alias(void *item, void *data)
%%
-input : decllist
+input : program
{
if (error_count == 0) {
builtin_post_setup();
@@ -437,14 +438,22 @@ input : decllist
}
;
+program : decllist
+ | moddecl decllist modend
+ {
+ $$ = $2;
+ }
+ | moddecl decllist
+ {
+ $$ = $2;
+ }
+ ;
+
decllist : decl
{
$$.head = $$.tail = $1;
}
| modcntl
- {
- $$.head = $$.tail = NULL;
- }
| decllist decl
{
if ($2) {
@@ -457,12 +466,36 @@ decllist : decl
$$ = $1;
}
| decllist modcntl
+ {
+ if ($2.tail) {
+ if ($1.tail) {
+ $$.head = $1.head;
+ $1.tail->next = $2.head;
+ $$.tail = $2.tail;
+ } else
+ $$ = $2;
+ }
+ }
;
-modcntl : moddecl
- | require
+modcntl : require
+ {
+ $$.head = $$.tail = NULL;
+ }
+ | require MODBEG opt_moddecl decllist modend
+ {
+ $$ = $4;
+ pop_top_module();
+ }
;
+opt_moddecl: /* empty */
+ {
+ parse_warning(_("missing module declaration"));
+ }
+ | moddecl
+ ;
+
moddecl : MODULE literal '.'
{
if (top_module->dclname)
@@ -497,6 +530,13 @@ require : REQUIRE literal
}
;
+modend : BYE
+ {
+ lex_bye();
+ }
+ | MODEND
+ ;
+
imports : literal
{
struct import_rule *rule = import_rule_create($1);
diff --git a/mfd/lex.l b/mfd/lex.l
index 77a8febb..d6c7f6f7 100644
--- a/mfd/lex.l
+++ b/mfd/lex.l
@@ -63,15 +63,13 @@ get_locus()
return &locus;
}
-/* Auxiliary function for returning keyword tokens */
-static int
-keyword(int kw)
-{
- yylval.locus = locus;
- return kw;
-}
+/* Auxiliary macro for returning keyword tokens */
+#define keyword(kw) (yylval.locus = locus,(kw))
+#define INCTX_MODULE 0x1
+#define INCTX_HADINPUT 0x2
+
/* Input stack support */
struct inctx { /* input context structure */
struct inctx *parent;
@@ -79,12 +77,15 @@ struct inctx { /* input context structure */
struct input_file_ident id;
FILE *file;
pid_t pp_pid; /* Preprocessor pid, if used */
+ int inctx_flags;
YY_BUFFER_STATE buf; /* Lex buffer state */
};
struct inctx *inctx_tos;
static pid_t pp_pid; /* Preprocessor pid */
-struct input_file_ident input_file_id;
+static struct input_file_ident input_file_id;
+static int inctx_flags;
+static int emit_token;
struct inctx *
inctx_locate(struct input_file_ident *id)
@@ -108,6 +109,7 @@ lex_buffer_push()
ctx->id = input_file_id;
ctx->file = yyin;
ctx->pp_pid = pp_pid;
+ ctx->inctx_flags = inctx_flags;
ctx->buf = YY_CURRENT_BUFFER;
ctx->parent = inctx_tos;
inctx_tos = ctx;
@@ -120,12 +122,13 @@ lex_buffer_pop()
if (!ctx)
return 1;
+
inctx_tos = ctx->parent;
locus = ctx->locus;
yyin = ctx->file;
input_file_id = ctx->id;
-
+ inctx_flags = ctx->inctx_flags;
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(ctx->buf);
pp_pid = ctx->pp_pid;
@@ -160,7 +163,7 @@ lex_new_source_0(const char *name)
}
int
-lex_new_source(const char *name, int once)
+lex_new_source(const char *name, int flag)
{
int rc;
struct stat st;
@@ -189,7 +192,7 @@ lex_new_source(const char *name, int once)
return 1;
}
- if (once && source_lookup(&id))
+ if (flag == LEX_ONCE && source_lookup(&id))
return 0;
if (yyin)
@@ -197,8 +200,14 @@ lex_new_source(const char *name, int once)
rc = lex_new_source_0(name);
if (rc)
lex_buffer_pop();
- else
+ else {
input_file_id = id;
+ if (flag == LEX_MODULE) {
+ inctx_flags = INCTX_MODULE;
+ emit_token = MODBEG;
+ } else
+ inctx_flags = 0;
+ }
return rc;
}
@@ -244,6 +253,25 @@ advance_line()
#define BEGIN_X(s) do { BEGIN(s); start_locus = locus; } while(0)
+#define YY_USER_ACTION \
+ if (emit_token) { \
+ int tok = emit_token; \
+ emit_token = 0; \
+ if (yy_flex_debug) \
+ fprintf(stderr, "--emitting %d (module %s)\n", \
+ tok,top_module->name); \
+ yyless(0); \
+ return tok; \
+ } \
+
+#define YY_INPUT(buf,result,max_size) \
+ if (yyin == NULL) \
+ result = YY_NULL; \
+ else if (((result = fread(buf, 1, max_size, yyin)) == 0) \
+ && ferror(yyin)) \
+ YY_FATAL_ERROR("input in flex scanner failed");
+
+#define YY_DECL static int lexscan(void)
%}
/* Exclusive states:
@@ -382,7 +410,18 @@ import return keyword(IMPORT);
from return keyword(FROM);
static return keyword(STATIC);
public return keyword(PUBLIC);
-module return keyword(MODULE);
+module { if (inctx_tos && inctx_flags == 0) {
+ parse_warning_locus(&inctx_tos->locus,
+ _("including a module file is unreliable and may cause subtle errors"));
+ /* TRANSLATORS: Do not translate `require %s' */
+ parse_warning_locus(&inctx_tos->locus,
+ _("use `require %s' instead"),
+ locus.file);
+ } else if (inctx_flags & INCTX_HADINPUT)
+ parse_error(_("misplaced `module'"));
+ return keyword(MODULE);
+ }
+bye return keyword(BYE);
{ICONST} { return builtin_const(yytext, yyleng); }
<ONBLOCK>poll return keyword(POLL);
@@ -720,9 +759,17 @@ module return keyword(MODULE);
<ML,QML>. /* If a here-document is not closed and its next line does not
end with a \n, prevent it from being displayed by ECHO */;
. return yytext[0];
-
%%
+int
+yylex()
+{
+ int rc = lexscan();
+ if (rc != MODBEG && rc != MODEND)
+ inctx_flags |= INCTX_HADINPUT;
+ return rc;
+}
+
void
init_string_space()
{
@@ -867,10 +914,13 @@ parse_error_locus(const struct locus *loc, const char *fmt, ...)
static int
lex_close_source()
{
+ if (!yyin)
+ return 1;
if (ext_pp)
pp_extrn_shutdown(yyin, pp_pid);
else
fclose(yyin);
+ yyin = NULL;
switch (YYSTATE) {
case INITIAL:
@@ -890,12 +940,28 @@ lex_close_source()
return lex_buffer_pop();
}
+void
+lex_drain_input()
+{
+ while (input() != EOF)
+ ;
+}
+
+void
+lex_bye()
+{
+ lex_drain_input();
+ lex_close_source();
+}
+
int
yywrap()
{
- int rc = lex_close_source();
- pop_top_module();
- return rc;
+ if (yy_flex_debug && top_module)
+ fprintf(stderr, "--eof in module %s\n", top_module->name);
+ if (inctx_flags & INCTX_MODULE)
+ emit_token = MODEND;
+ return lex_close_source();
}
static int
diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h
index 8ea0fbe7..5cdb34f0 100644
--- a/mfd/mailfromd.h
+++ b/mfd/mailfromd.h
@@ -782,9 +782,16 @@ int parse_line(char *text, struct locus *ploc);
void parse_line_cpp(char *text, struct locus *ploc);
void alloc_ext_pp(void);
int assign_locus(struct locus *ploc, const char *name, const char *line);
-int lex_new_source(const char *name, int once);
int source_lookup(struct input_file_ident *idptr);
+#define LEX_NONE 0
+#define LEX_ONCE 1
+#define LEX_MODULE 2
+
+int lex_new_source(const char *name, int once);
+void lex_bye(void);
+void lex_drain_input(void);
+
/* Parser functions */
int yyparse();
int yylex();
diff --git a/mfd/pp.c b/mfd/pp.c
index 733791f9..21096d51 100644
--- a/mfd/pp.c
+++ b/mfd/pp.c
@@ -168,7 +168,7 @@ begin_module(const char *modname, const char *filename,
}
locus = *get_locus();
if (set_top_module(modname, filename, import_rules, &locus) == 0)
- rc = lex_new_source(filename, 0); /* FIXME: once or not once?*/
+ rc = lex_new_source(filename, LEX_MODULE);
else
rc = 0;
return rc;
@@ -207,11 +207,8 @@ parse_include(const char *text, int once)
advance_line();
- if (p) {
- rc = lex_new_source(p, once);
- if (rc == 0)
- module_push(top_module);
- }
+ if (p)
+ rc = lex_new_source(p, once ? LEX_ONCE : LEX_NONE);
free(tmp);
mu_argcv_free(argc, argv);
return rc;
diff --git a/mfd/status.mfh b/mfd/status.mfh
new file mode 100644
index 00000000..28280ce1
--- /dev/null
+++ b/mfd/status.mfh
@@ -0,0 +1,3 @@
+# This file is provided for backward compatibility with Mailfromd prior to 6.0.
+# Instead of including it, insert this line into your program:
+require status
diff --git a/mfd/status.mfi b/mfd/status.mfi
index c465522c..ba2c764b 100644
--- a/mfd/status.mfi
+++ b/mfd/status.mfi
@@ -1,5 +1,5 @@
TEMPLATE #
-
+module 'status'.
# Socket families
const FAMILY_STDIO 0
@@ -30,4 +30,4 @@ const url e_url
const noresolve e_noresolve
const ioerr e_io
const macroundef e_macroundef
-') \ No newline at end of file
+')
diff --git a/mflib/Makefile.am b/mflib/Makefile.am
index 49a6af45..2f456056 100644
--- a/mflib/Makefile.am
+++ b/mflib/Makefile.am
@@ -54,10 +54,3 @@ check-am:
do \
$$MFD -I$(top_srcdir)/mfd -I$(top_srcdir)/mflib -I$(top_builddir)/mflib --lint $(top_srcdir)/mflib/$$file || exit 1; \
done
-
-install-data-local:
- @here=`pwd`; \
- cd $(DESTDIR)$(incdir); \
- rm -f sieve.mfh; \
- $(LN_S) sieve.mf sieve.mfh; \
- cd $$here
diff --git a/mflib/dns.mf4 b/mflib/dns.mf4
index beb26908..452788c6 100644
--- a/mflib/dns.mf4
+++ b/mflib/dns.mf4
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'dns'.
+
func hostname (string ipstr)
returns string
do
diff --git a/mflib/gettext.mf b/mflib/gettext.mf
index 8688fe2b..1ba7518f 100644
--- a/mflib/gettext.mf
+++ b/mflib/gettext.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'gettext'.
+
string mailfromd_domain "mailfromd"
func textdomain(string domainname)
diff --git a/mflib/heloarg_test.mf b/mflib/heloarg_test.mf
index 7bdd8d54..bce63fe2 100644
--- a/mflib/heloarg_test.mf
+++ b/mflib/heloarg_test.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'heloarg_test'.
require is_ip
require dns
diff --git a/mflib/is_ip.mf b/mflib/is_ip.mf
index e5072d01..ba7dad5b 100644
--- a/mflib/is_ip.mf
+++ b/mflib/is_ip.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'is_ip'.
+
#pragma regex push +extended
func is_ip(string ip)
diff --git a/mflib/localdomain.mf b/mflib/localdomain.mf
index 77f153e5..bbdde22d 100644
--- a/mflib/localdomain.mf
+++ b/mflib/localdomain.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'localdomain'.
+
func localdomain()
returns string
do
diff --git a/mflib/match_cidr.mf b/mflib/match_cidr.mf
index 490866ec..3cf88e01 100644
--- a/mflib/match_cidr.mf
+++ b/mflib/match_cidr.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'match_cidr'.
+
require status
#pragma regex push +extended
diff --git a/mflib/match_dnsbl.mf b/mflib/match_dnsbl.mf
index 462ee154..acd10189 100644
--- a/mflib/match_dnsbl.mf
+++ b/mflib/match_dnsbl.mf
@@ -15,6 +15,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'match_dnsbl'.
+
require status
require dns
require match_cidr
diff --git a/mflib/match_rhsbl.mf b/mflib/match_rhsbl.mf
index 27b371a1..b4e812cc 100644
--- a/mflib/match_rhsbl.mf
+++ b/mflib/match_rhsbl.mf
@@ -15,6 +15,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'match_rhsbl'.
+
require status
require dns
require match_cidr
diff --git a/mflib/rateok.mf b/mflib/rateok.mf
index 7066eeb7..1864b938 100644
--- a/mflib/rateok.mf
+++ b/mflib/rateok.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'rateok'.
+
func rateok(string key, number timespan, number threshold; number mincnt)
returns number
do
diff --git a/mflib/revip.mf b/mflib/revip.mf
index b614becf..5f7a57ca 100644
--- a/mflib/revip.mf
+++ b/mflib/revip.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'revip'.
+
func revip(string ip)
returns string
do
diff --git a/mflib/sa.mf b/mflib/sa.mf
index fcbcdf34..89fb850f 100644
--- a/mflib/sa.mf
+++ b/mflib/sa.mf
@@ -14,6 +14,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'sa'.
+
#pragma regex push +extended
static func __sa_format_score0(string code, number prec)
diff --git a/mflib/safedb.mf4 b/mflib/safedb.mf4
index 808f4860..6d93589b 100644
--- a/mflib/safedb.mf4
+++ b/mflib/safedb.mf4
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program . If not, see <http://www.gnu.org/licenses/>. */
+module 'safedb'.
require status
number safedb_verbose
diff --git a/mflib/sieve.mf b/mflib/sieve.mf
index 03caa180..4357b43e 100644
--- a/mflib/sieve.mf
+++ b/mflib/sieve.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'sieve'.
/* FIXME: These are duplicated in src/bi_sieve.m4. This file should be
generated automatically. */
diff --git a/mflib/sockmap.mf b/mflib/sockmap.mf
index f1d4cf91..3d4c2459 100644
--- a/mflib/sockmap.mf
+++ b/mflib/sockmap.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'sockmap'.
require status
static func __sockmap_send(number fd, string query)
diff --git a/mflib/spf.mf b/mflib/spf.mf
index bf2d5b42..be6343bf 100644
--- a/mflib/spf.mf
+++ b/mflib/spf.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'spf'.
require safedb
#pragma regex push +extended +icase
diff --git a/mflib/strip_domain_part.mf b/mflib/strip_domain_part.mf
index aadbe29b..1de92fe1 100644
--- a/mflib/strip_domain_part.mf
+++ b/mflib/strip_domain_part.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'strip_domain_part'.
#pragma regex push +extended
func strip_domain_part(string domain, number n)
diff --git a/mflib/valid_domain.mf b/mflib/valid_domain.mf
index 6d225e1d..03d31f46 100644
--- a/mflib/valid_domain.mf
+++ b/mflib/valid_domain.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'valid_domain'.
require dns
# boolean valid_domain (string DOMAIN)
diff --git a/mflib/verp.mf b/mflib/verp.mf
index 836ec300..1496d915 100644
--- a/mflib/verp.mf
+++ b/mflib/verp.mf
@@ -14,6 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+module 'verp'.
#pragma regex push +extended
func verp_extract_user(string email, string domain)

Return to:

Send suggestions and report system problems to the System administrator.