CVSROOT: /sources/m4
Module name: m4
Branch: branch-1_4
Changes by: Eric Blake <ericb> 06/10/21 02:55:56
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.90
retrieving revision 1.1.1.1.2.91
diff -u -b -r1.1.1.1.2.90 -r1.1.1.1.2.91
--- doc/m4.texinfo 19 Oct 2006 23:13:05 -0000 1.1.1.1.2.90
+++ doc/m4.texinfo 21 Oct 2006 02:55:56 -0000 1.1.1.1.2.91
@@ -1629,7 +1629,7 @@
')
@result{}
string
[EMAIL PROTECTED] macro @comment
[EMAIL PROTECTED] [EMAIL PROTECTED] }
defn(`string')
@result{}The macro dnl is very useful
@result{}
@@ -2187,7 +2187,7 @@
include(`forloop.m4')
@result{}
forloop(`i', `1', `8', `i ')
[EMAIL PROTECTED] 2 3 4 5 6 7 8 @comment
[EMAIL PROTECTED] 2 3 4 5 6 7 [EMAIL PROTECTED] }
@end example
For-loops can be nested, like:
@@ -2207,12 +2207,12 @@
The implementation of the @code{forloop} macro is fairly
straightforward. The @code{forloop} macro itself is simply a wrapper,
which saves the previous definition of the first argument, calls the
-internal macro @code{_forloop}, and re-establishes the saved definition of
-the first argument.
+internal macro @[EMAIL PROTECTED], and re-establishes the saved
+definition of the first argument.
-The macro @code{_forloop} expands the fourth argument once, and tests
-to see if the iterator has reached the final value. If it has not
-finished, it increments the iterator (using the predefined macro
+The macro @[EMAIL PROTECTED] expands the fourth argument once, and
+tests to see if the iterator has reached the final value. If it has
+not finished, it increments the iterator (using the predefined macro
@code{incr}, @pxref{Incr}), and recurses.
Here is an actual implementation of @code{forloop}, distributed as
@@ -2240,7 +2240,151 @@
@node Foreach
@section Iteration by list contents
-FIXME - Fill out this section.
[EMAIL PROTECTED] for each loops
[EMAIL PROTECTED] loops, list iteration
[EMAIL PROTECTED] iterating over lists
+Here is an example of a loop macro that implements list iteration.
+
[EMAIL PROTECTED] Composite foreach (@var{iterator}, @var{paren-list},
@var{text})
[EMAIL PROTECTED] Composite foreachq (@var{iterator}, @var{quote-list},
@var{text})
+Takes the name in @var{iterator}, which must be a valid macro name, and
+successively assign it each value from @var{paren-list} or
[EMAIL PROTECTED] In @code{foreach}, @var{paren-list} is a
+comma-separated list of elements contained in parentheses. In
[EMAIL PROTECTED], @var{quote-list} is a comma-separated list of elements
+contained in a quoted string. For each assignment to @var{iterator},
+append @var{text} to the overall expansion. @var{text} may refer to
[EMAIL PROTECTED] Any definition of @var{iterator} prior to this
+invocation is restored.
[EMAIL PROTECTED] deffn
+
+As an example, this displays each word in a list inside of a sentence,
+using an implementation of @code{foreach} distributed as
[EMAIL PROTECTED]@value{VERSION}/@/examples/@/foreach.m4}, and @code{foreachq}
+in @[EMAIL PROTECTED]/@/examples/@/foreachq.m4}.
+
[EMAIL PROTECTED]
+include(`foreach.m4')
[EMAIL PROTECTED]
+foreach(`x', (foo, bar, foobar), `Word was: x
+')dnl
[EMAIL PROTECTED] was: foo
[EMAIL PROTECTED] was: bar
[EMAIL PROTECTED] was: foobar
+include(`foreachq.m4')
[EMAIL PROTECTED]
+foreachq(`x', `foo, bar, foobar', `Word was: x
+')dnl
[EMAIL PROTECTED] was: foo
[EMAIL PROTECTED] was: bar
[EMAIL PROTECTED] was: foobar
[EMAIL PROTECTED] example
+
+It is possible to be more complex; each element of the @var{paren-list}
+or @var{quote-list} can itself be a list, to pass as further arguments
+to a helper macro. This example generates a shell case statement:
+
[EMAIL PROTECTED]
+include(`foreach.m4')
[EMAIL PROTECTED]
+define(`_case', ` $1)
+ $2=" $1";;
+')dnl
+define(`_cat', `$1$2')dnl
+case $`'1 in
[EMAIL PROTECTED] $1 in
+foreach(`x', `(`(`a', `vara')', `(`b', `varb')', `(`c', `varc')')',
+ `_cat(`_case', x)')dnl
[EMAIL PROTECTED] a)
[EMAIL PROTECTED] vara=" a";;
[EMAIL PROTECTED] b)
[EMAIL PROTECTED] varb=" b";;
[EMAIL PROTECTED] c)
[EMAIL PROTECTED] varc=" c";;
+esac
[EMAIL PROTECTED]
[EMAIL PROTECTED] example
+
+The implementation of the @code{foreach} macro is a bit more involved;
+it is a wrapper around two helper macros. First, @[EMAIL PROTECTED] is
+needed to grab the first element of a list. Second,
[EMAIL PROTECTED]@w{_foreach}} implements the recursion, successively walking
+through the original list. Here is a simple implementation of
[EMAIL PROTECTED]:
+
[EMAIL PROTECTED]
+undivert(`foreach.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] foreach(x, (item_1, item_2, ..., item_n), stmt)
[EMAIL PROTECTED] parenthesized list, simple version
[EMAIL PROTECTED](`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')
[EMAIL PROTECTED](`_arg1', `$1')
[EMAIL PROTECTED](`_foreach', `ifelse(`$2', `()', `',
[EMAIL PROTECTED] `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
[EMAIL PROTECTED]'dnl
[EMAIL PROTECTED] example
+
+Unfortunately, that implementation is not robust to macro names as list
+elements. Each iteration of @[EMAIL PROTECTED] is stripping another
+layer of quotes, leading to erratic results if list elements are not
+already fully expanded. The first cut at implementing @code{foreachq}
+takes this into account. Also, when using quoted elements in a
[EMAIL PROTECTED], the overall list must be quoted. A @var{quote-list}
+has the nice property of requiring fewer characters to create a list
+containing the same quoted elements. To see the difference between the
+two macros, we attempt to pass double-quoted macro names in a list,
+expecting the macro name on output after one layer of quotes is removed
+during list iteration and the final layer removed during the final
+rescan:
+
[EMAIL PROTECTED]
+define(`a', `1')define(`b', `2')define(`c', `3')
[EMAIL PROTECTED]
+include(`foreach.m4')
[EMAIL PROTECTED]
+include(`foreachq.m4')
[EMAIL PROTECTED]
+foreach(`x', `(``a'', ``(b'', ``c)'')', `x
+')
[EMAIL PROTECTED]
[EMAIL PROTECTED](2)1
[EMAIL PROTECTED]
[EMAIL PROTECTED], x
[EMAIL PROTECTED])
+foreachq(`x', ```a'', ``(b'', ``c)''', `x
+')dnl
[EMAIL PROTECTED]
[EMAIL PROTECTED](b
[EMAIL PROTECTED])
[EMAIL PROTECTED] example
+
+Obviously, @code{foreachq} did a better job; here is its implementation:
+
[EMAIL PROTECTED]
+undivert(`foreachq.m4')dnl
[EMAIL PROTECTED](`quote.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] foreachq(x, `item_1, item_2, ..., item_n', stmt)
[EMAIL PROTECTED] quoted list, simple version
[EMAIL PROTECTED](`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')')
[EMAIL PROTECTED](`_arg1', `$1')
[EMAIL PROTECTED](`_foreachq', `ifelse(quote($2), `', `',
[EMAIL PROTECTED] `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')')
[EMAIL PROTECTED]'dnl
[EMAIL PROTECTED] example
+
+Notice that @[EMAIL PROTECTED] had to use the helper macro
[EMAIL PROTECTED] defined earlier (@pxref{Shift}), to ensure that the
+embedded @code{ifelse} call does not go haywire if a list element
+contains a comma. Unfortunately, this implementation of @code{foreachq}
+has its own severe flaw. Whereas the @code{foreach} implementation was
+linear, this macro is quadratic in the number of list elements, and is
+much more likely to trip up the limit set by the command line option
[EMAIL PROTECTED] (or @option{-L}, @pxref{Limits control, ,
+Invoking m4}). (It is possible to have robust iteration with linear
+behavior for either list style. See if you can learn from the best
+elements of both of these implementations to create robust macros; or
[EMAIL PROTECTED] foreach, , Answers}).
@node Debugging
@chapter How to debug macros and input
@@ -3937,7 +4081,7 @@
patsubst(`GNUs not Unix', `\w+', `(\&)')
@result{}(GNUs) (not) (Unix)
patsubst(`GNUs not Unix', `[A-Z][a-z]+')
[EMAIL PROTECTED] not @comment
[EMAIL PROTECTED] [EMAIL PROTECTED] }
patsubst(`GNUs not Unix', `not', `NOT\')
@error{}m4:stdin:6: Warning: trailing \ ignored in replacement
@result{}GNUs NOT Unix
@@ -4307,10 +4451,10 @@
string.
@end deffn
-When @acronym{GNU} extensions are in effect (that is, when you did not use the
[EMAIL PROTECTED] option, @pxref{Limits control, , Invoking m4}),
[EMAIL PROTECTED] @code{m4} will
-define the macro @code{__gnu__} to expand to the empty string.
+When @acronym{GNU} extensions are in effect (that is, when you did not
+use the @option{-G} option, @pxref{Limits control, , Invoking m4}),
[EMAIL PROTECTED] @code{m4} will define the macro @[EMAIL PROTECTED] to
+expand to the empty string.
@example
__gnu__
@@ -4320,15 +4464,15 @@
@end example
@cindex platform macro
-On UNIX systems, @acronym{GNU} @code{m4} will define @code{__unix__} by
-default, or @code{unix} when the @option{-G} option is specified.
+On UNIX systems, @acronym{GNU} @code{m4} will define @[EMAIL PROTECTED]
+by default, or @code{unix} when the @option{-G} option is specified.
On native Windows systems, @acronym{GNU} @code{m4} will define
[EMAIL PROTECTED] by default, or @code{windows} when the @option{-G}
-option is specified.
[EMAIL PROTECTED]@w{__windows__}} by default, or @code{windows} when the
[EMAIL PROTECTED] option is specified.
-On OS/2 systems, @acronym{GNU} @code{m4} will define @code{__os2__} by
-default, or @code{os2} when the @option{-G} option is specified.
+On OS/2 systems, @acronym{GNU} @code{m4} will define @[EMAIL PROTECTED]
+by default, or @code{os2} when the @option{-G} option is specified.
If @acronym{GNU} @code{m4} does not provide a platform macro for your system,
please report that as a bug.
@@ -4719,16 +4863,16 @@
@result{}6
@end example
-The @code{__program__} macro behaves like @samp{$0} in shell
+The @[EMAIL PROTECTED] macro behaves like @samp{$0} in shell
terminology. If you invoke @code{m4} through an absolute path or a link
with a different spelling, rather than by relying on a @env{PATH} search
-for plain @samp{m4}, it will affect how @code{__program__} expands. The
-intent is that you can use it to produce error messages with the same
-formatting that @code{m4} produces internally. It can also be used
+for plain @samp{m4}, it will affect how @[EMAIL PROTECTED] expands.
+The intent is that you can use it to produce error messages with the
+same formatting that @code{m4} produces internally. It can also be used
within @code{syscmd} (@pxref{Syscmd}) to pick the same version of
@code{m4} that is currently running, rather than whatever version of
[EMAIL PROTECTED] happens to be first in @env{PATH}. It was first introduced
-in @acronym{GNU} M4 1.4.6.
[EMAIL PROTECTED] happens to be first in @env{PATH}. It was first introduced in
[EMAIL PROTECTED] M4 1.4.6.
@node M4exit
@section Exiting from @code{m4}
@@ -5069,8 +5213,8 @@
@item
The name of the program, the current input file, and the current input
-line number are accessible through the builtins @code{__program__},
[EMAIL PROTECTED], and @code{__line__} (@pxref{Location}).
+line number are accessible through the builtins @[EMAIL PROTECTED],
[EMAIL PROTECTED]@w{__file__}}, and @[EMAIL PROTECTED] (@pxref{Location}).
@item
The format of the output from @code{dumpdef} and macro tracing can be
@@ -5342,7 +5486,7 @@
only permits decimal numbers for bounds. Here is an improved version,
shipped as @[EMAIL PROTECTED]/@/examples/@/forloop2.m4}; this
version also optimizes based on the fact that the starting bound does
-not need to be passed to the helper @code{_forloop}.
+not need to be passed to the helper @[EMAIL PROTECTED]
@example
undivert(`forloop2.m4')dnl
@@ -5379,7 +5523,199 @@
@node Improved foreach
@section Solution for @code{foreach}
-FIXME - add content.
+The @code{foreach} and @code{foreachq} macros (@pxref{Foreach}) as
+presented earlier each have flaws. First, we will examine and fix the
+quadratic behavior of @code{foreachq}:
+
[EMAIL PROTECTED]
+include(`foreachq.m4')
[EMAIL PROTECTED]
+traceon(`shift')debugmode(`aq')
[EMAIL PROTECTED]
+foreachq(`x', ``1', `2', `3', `4'', `x
+')dnl
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -2- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -4- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -3- shift(`2', `3', `4')
[EMAIL PROTECTED]: -3- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -2- shift(`2', `3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -5- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -4- shift(`2', `3', `4')
[EMAIL PROTECTED]: -3- shift(`3', `4')
[EMAIL PROTECTED]: -4- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -3- shift(`2', `3', `4')
[EMAIL PROTECTED]: -2- shift(`3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -6- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -5- shift(`2', `3', `4')
[EMAIL PROTECTED]: -4- shift(`3', `4')
[EMAIL PROTECTED]: -3- shift(`4')
[EMAIL PROTECTED] example
+
+Each successive iteration was adding more quoted @code{shift}
+invocations, and the entire list contents were passing through every
+iteration. In general, when recursing, it is a good idea to make the
+recursion use fewer arguments, rather than adding additional quoted
+uses of @code{shift}. By doing so, @code{m4} uses less memory, invokes
+fewer macros, is less likely to run into machine limits, and most
+importantly, performs faster. The fixed version of @code{foreachq} can
+be found in @[EMAIL PROTECTED]/@/examples/@/foreachq2.m4}:
+
[EMAIL PROTECTED]
+include(`foreachq2.m4')
[EMAIL PROTECTED]
+undivert(`foreachq2.m4')dnl
[EMAIL PROTECTED](`quote.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] foreachq(x, `item_1, item_2, ..., item_n', stmt)
[EMAIL PROTECTED] quoted list, improved version
[EMAIL PROTECTED](`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')')
[EMAIL PROTECTED](`_arg1q', ``$1'')
[EMAIL PROTECTED](`_rest', `ifelse(`$#', `1', `', `dquote(shift($@@))')')
[EMAIL PROTECTED](`_foreachq', `ifelse(`$2', `', `',
[EMAIL PROTECTED] `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')')
[EMAIL PROTECTED]'dnl
+traceon(`shift')debugmode(`aq')
[EMAIL PROTECTED]
+foreachq(`x', ``1', `2', `3', `4'', `x
+')dnl
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(`2', `3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(`3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED] example
+
+Note that the fixed version calls unquoted helper macros in
[EMAIL PROTECTED]@w{_foreachq}} to trim elements immediately; those helper
macros
+in turn must re-supply the layer of quotes lost in the macro invocation.
+Contrast the use of @[EMAIL PROTECTED], which quotes the first list
+element, with @[EMAIL PROTECTED] of the earlier implementation that
+returned the first list element directly.
+
+For a different approach, the improved version of @code{foreach},
+available in @[EMAIL PROTECTED]/@/examples/@/foreach2.m4}, simply
+overquotes the arguments to @[EMAIL PROTECTED] to begin with, using
[EMAIL PROTECTED] Then @[EMAIL PROTECTED] can just use
[EMAIL PROTECTED]@w{_arg1}} to remove the extra layer of quoting that was added
up
+front:
+
[EMAIL PROTECTED]
+include(`foreach2.m4')
[EMAIL PROTECTED]
+undivert(`foreach2.m4')dnl
[EMAIL PROTECTED](`quote.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] foreach(x, (item_1, item_2, ..., item_n), stmt)
[EMAIL PROTECTED] parenthesized list, improved version
[EMAIL PROTECTED](`foreach', `pushdef(`$1')_foreach(`$1',
[EMAIL PROTECTED] (dquote(dquote_elt$2)), `$3')popdef(`$1')')
[EMAIL PROTECTED](`_arg1', `$1')
[EMAIL PROTECTED](`_foreach', `ifelse(`$2', `(`')', `',
[EMAIL PROTECTED] `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)),
`$3')')')
[EMAIL PROTECTED]'dnl
+traceon(`shift')debugmode(`aq')
[EMAIL PROTECTED]
+foreach(`x', `(`1', `2', `3', `4')', `x
+')dnl
[EMAIL PROTECTED]: -4- shift(`1', `2', `3', `4')
[EMAIL PROTECTED]: -4- shift(`2', `3', `4')
[EMAIL PROTECTED]: -4- shift(`3', `4')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(``1'', ``2'', ``3'', ``4'')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(``2'', ``3'', ``4'')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(``3'', ``4'')
[EMAIL PROTECTED]
[EMAIL PROTECTED]: -3- shift(``4'')
[EMAIL PROTECTED] example
+
+In summary, recursion over list elements is trickier than it appeared at
+first glance, but provides a powerful idiom within @code{m4} processing.
+As a final demonstration, both list styles are now able to handle
+several scenarios that would wreak havoc on the original
+implementations. This points out one other difference between the two
+list styles. @code{foreach} evaluates unquoted list elements only once,
+in preparation for calling @[EMAIL PROTECTED] But @code{foreachq}
+evaluates unquoted list elements twice while visiting the first list
+element, once in @[EMAIL PROTECTED] and once in @[EMAIL PROTECTED] When
+deciding which list style to use, one must take into account whether
+repeating the side effects of unquoted list elements will have any
+detrimental effects.
+
[EMAIL PROTECTED]
+include(`foreach2.m4')
[EMAIL PROTECTED]
+include(`foreachq2.m4')
[EMAIL PROTECTED]
+dnl 0-element list:
+foreach(`x', `', `<x>') / foreachq(`x', `', `<x>')
[EMAIL PROTECTED] /@w{ }
+dnl 1-element list of empty element
+foreach(`x', `()', `<x>') / foreachq(`x', ``'', `<x>')
[EMAIL PROTECTED]<> / <>
+dnl 2-element list of empty elements
+foreach(`x', `(`',`')', `<x>') / foreachq(`x', ``',`'', `<x>')
[EMAIL PROTECTED]<><> / <><>
+dnl 1-element list of a comma
+foreach(`x', `(`,')', `<x>') / foreachq(`x', ``,'', `<x>')
[EMAIL PROTECTED]<,> / <,>
+dnl 2-element list of unbalanced parentheses
+foreach(`x', `(`(', `)')', `<x>') / foreachq(`x', ``(', `)'', `<x>')
[EMAIL PROTECTED]<(><)> / <(><)>
+define(`active', `ACT, IVE')
[EMAIL PROTECTED]
+traceon(`active')
[EMAIL PROTECTED]
+dnl list of unquoted macros; expansion occurs before recursion
+foreach(`x', `(active, active)', `<x>
+')dnl
[EMAIL PROTECTED]: -4- active -> `ACT, IVE'
[EMAIL PROTECTED]: -4- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT>
[EMAIL PROTECTED]<IVE>
[EMAIL PROTECTED]<ACT>
[EMAIL PROTECTED]<IVE>
+foreachq(`x', `active, active', `<x>
+')dnl
[EMAIL PROTECTED]: -3- active -> `ACT, IVE'
[EMAIL PROTECTED]: -3- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT>
[EMAIL PROTECTED]: -3- active -> `ACT, IVE'
[EMAIL PROTECTED]: -3- active -> `ACT, IVE'
[EMAIL PROTECTED]<IVE>
[EMAIL PROTECTED]<ACT>
[EMAIL PROTECTED]<IVE>
+dnl list of quoted macros; expansion occurs during recursion
+foreach(`x', `(`active', `active')', `<x>
+')dnl
[EMAIL PROTECTED]: -1- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT, IVE>
[EMAIL PROTECTED]: -1- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT, IVE>
+foreachq(`x', ``active', `active'', `<x>
+')dnl
[EMAIL PROTECTED]: -1- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT, IVE>
[EMAIL PROTECTED]: -1- active -> `ACT, IVE'
[EMAIL PROTECTED]<ACT, IVE>
+dnl list of double-quoted macro names; no expansion
+foreach(`x', `(``active'', ``active'')', `<x>
+')dnl
[EMAIL PROTECTED]<active>
[EMAIL PROTECTED]<active>
+foreachq(`x', ```active'', ``active''', `<x>
+')dnl
[EMAIL PROTECTED]<active>
[EMAIL PROTECTED]<active>
[EMAIL PROTECTED] example
@node Improved cleardivert
@section Solution for @code{cleardivert}
@@ -5419,12 +5755,13 @@
@section Solution for @code{fatal_error}
The @code{fatal_error} macro (@pxref{M4exit}) is not robust to versions
-of @acronym{GNU} M4 earlier than 1.4.8, where invoking @code{__file__}
-(@pxref{Location}) inside @code{m4wrap} would result in an empty string,
-and @code{__line__} resulted in @samp{0} even though all files start at
-line 1. Furthermore, versions earlier than 1.4.6 did not support the
[EMAIL PROTECTED] macro. If you want @code{fatal_error} to work across
-the entire 1.4.x release series, a better implementation would be:
+of @acronym{GNU} M4 earlier than 1.4.8, where invoking
[EMAIL PROTECTED]@w{__file__}} (@pxref{Location}) inside @code{m4wrap} would
result
+in an empty string, and @[EMAIL PROTECTED] resulted in @samp{0} even
+though all files start at line 1. Furthermore, versions earlier than
+1.4.6 did not support the @[EMAIL PROTECTED] macro. If you want
[EMAIL PROTECTED] to work across the entire 1.4.x release series, a
+better implementation would be:
@example
define(`fatal_error',