[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: M4 syntax $11 vs. ${11}
From: |
Eric Blake |
Subject: |
Re: M4 syntax $11 vs. ${11} |
Date: |
Thu, 1 Mar 2007 04:18:54 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
Eric Blake <ebb9 <at> byu.net> writes:
>
> With 1.9b, I would like to implement the resolution of XCU ERN 111. That
> is, I plan on adding support for ${11}, so that the GNU extension of more
> than 9 parameters is still alive (and in fact, I hope to borrow from shell
> syntax for other extensions such as ${1-default}). I also plan on making
> $11 obey POSIX semantics if POSIXLY_CORRECT, else obey 1.4.x semantics but
> issue a warning that obsolete syntax was encountered. Then, after a
> deprecation period, M4 2.1 could remove the POSIXLY_CORRECT check and
> always treat $11 the way POSIX requires.
Here (finally) is a patch for head, that both implements ${1}, as well as
forward ports --warn-macro-sequence from the branch. The features are
intertwined enough that I didn't see any good reason to separate this into
multiple patches (other than the earlier patches I already committed today).
However, I would like a review; so it is not applied yet. In particular, in
macro.c, this patch does not do a deprecation period for $10, as I originally
thought above, but flat out went with POSIX syntax. I did this on the
principle that relatively few uses of $10 in the wild have been discovered, and
that --warn-macro-sequence can be used to detect even those uses. Bonus points
if you can spot the corner case logic problem that I am already aware of in
macro.c.
The patch looks big, but part of it is moving implementation of regex functions
out of modules/gnu.c into m4/resyntax.c for better code sharing; part of it is
test code; and a lot of it is documentation.
Still to be written:
1) I want to implement a new builtin m4macroseq([regex],[resyntax]), which
behaves like the command-line --warn-macro-sequence (and that also means adding
a command line --m4macroseq for symmetry). With no arguments, it enables the
default warning sequence, with one empty argument, it disables warnings, and
since it uses regular expressions, it takes an optional resyntax argument to
override the current changeresyntax.
2) I have promised to implement changeextarg(start,[stop]), which allows multi-
character extended arguments, so that autoconf can reserve ${1} for shell
output and ${{1}} for the day that autoconf 3.0 depends on m4 2.0. I will
model it on changequote, including how it interacts with single-character quote
syntax in changesyntax, except that an argument always must be supplied (to go
with the policy that macros not beginning with "__" or "m4" must be blind, to
avoid risk of inadvertant expansion).
3) I would like to implement ideas from sh, such as ${1-default} expanding to
the first argument if supplied, or `default' if omitted.
I think that 2) is the only thing that should be completed before I feel
comfortable baselining m4-1.9b for wider test exposure on alpha.gnu.org.
2007-02-28 Eric Blake <address@hidden>
Implement first cut of extended arguments. Use ${10} instead of
$10 to mean the tenth argument.
* doc/m4.texinfo (Extended Arguments): New section.
(Operation modes): Document --warn-macro-sequence.
(Preprocessor features): Document current limitation of
--warn-macro-sequence combined with --import-environment.
(Arguments): Document POSIX behavior.
(Shift): Document argn macro for portably accessing tenth
argument.
(Changesyntax): Document way to override extended arguments.
(Extensions): Update wording to match implementation.
* m4/m4module.h (M4_DEFAULT_MACRO_SEQUENCE): New define.
(m4_set_macro_sequence, m4_free_macro_sequence, m4_macro_define)
(m4_check_macro_sequence): New prototypes.
(m4_pattern_buffer): Move here from gnu.c.
(m4_regexp_compile, m4_regexp_free): New prototypes.
* m4/macro.c (process_extended_argument): New helper.
(process_macro): Disable multi-digit arguments. Implement
extended arguments.
(macro_sequence, macro_sequence_inuse): New variables.
(m4_set_macro_sequence, m4_free_macro_sequence, m4_macro_define)
(m4_check_macro_sequence): New functions.
* m4/module.c (install_macro_table): Use new function.
* m4/resyntax.c (m4_regexp_compile, m4_regexp_free): New
functions, moved from gnu.c.
* modules/gnu.c (regexp_compile): Rename from m4_regexp_compile,
move implementation to libm4, and change all callers.
(M4_FINISH_HANDLER): Use new function.
* modules/m4.c (define, pushdef): Use new function.
* src/freeze.c (reload_frozen_state): Likewise.
* src/main.c (usage): Document --warn-macro-sequence.
(main): Use new functions.
* tests/macros.at (Extended arguments): New test.
* tests/options.at (--warn-macro-sequence): Likewise.
* tests/others.at (sysv-args): Fix typos.
* NEWS: Document this change.
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.40
diff -u -r1.40 NEWS
--- NEWS 28 Feb 2007 21:31:12 -0000 1.40
+++ NEWS 1 Mar 2007 04:13:58 -0000
@@ -79,6 +79,10 @@
dynamic modules from the command line. Also, `-m'/`--load-module' now
acts between input files.
+*** The `--warn-macro-sequence' command-line option can now be interspersed
+ with other options and files, and is affected by the new
+ `--regexp-syntax' option.
+
*** New `--warnings' command-line option re-enables warnings, overriding
`-Q'/`--quiet'/`--silent', allowing warnings even when POSIXLY_CORRECT.
@@ -87,8 +91,29 @@
*** The `defn' builtin now allows any number of arguments, as POSIX requires.
- FIXME: This still doesn't work with concatenating builtins with text.
- - FIXME: POSIX recommends using ${10} instead of $10 for the tenth
- positional argument. We should deprecate $10.
+*** The GNU 1.4.x extension of recognizing the sequence `$10' in macro
+ definitions as the tenth positional parameter is withdrawn, as it is
+ incompatible with POSIX. The sequence `$10' now correctly refers to
+ the first positional parameter concatenated with 0. To directly access
+ the tenth parameter, you must now use extended arguments (you can also
+ portably access the tenth argument indirectly using the `shift'
+ builtin). To detect places in existing scripts that might be affected
+ by this change in behavior, you can use the `--warn-macro-sequence'
+ command-line option.
+
+*** POSIX allows implementations to assign arbitrary behavior to the sequence
+ `${' in macro definitions. All earlier versions of GNU M4 just treated
+ it as literal output, but this version introduces extended arguments.
+ By default, the sequence `${<digits>}' now represents the extended
+ argument referring to a positional parameter, so that it is still
+ possible to directly refer to more than nine arguments. If the older
+ 1.4.x behavior of literal output is desired, the new `changesyntax' or
+ `changeextarg' builtins can be used to cripple extended arguments. To
+ detect places in existing scripts that might be affected by this change
+ in behavior, you can use the `--warn-macro-sequence' command-line
+ option.
+ - FIXME: Extended arguments are not working completely, and changeextarg
+ needs to be added. Also, defaults (ie. ${1-default}) would be nice.
- FIXME: `m4wrap' semantics need an update to FIFO.
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.98
diff -u -r1.98 m4.texinfo
--- doc/m4.texinfo 28 Feb 2007 21:31:12 -0000 1.98
+++ doc/m4.texinfo 1 Mar 2007 04:13:58 -0000
@@ -184,6 +184,7 @@
* Define:: Defining a new macro
* Arguments:: Arguments to macros
* Pseudo Arguments:: Special arguments to macros
+* Extended Arguments:: Extended arguments
* Undefine:: Deleting a macro
* Defn:: Renaming macros
* Pushdef:: Temporarily redefining macros
@@ -671,6 +672,25 @@
option is intended to make it safer to preprocess an input file of
unknown origin.
address@hidden address@hidden@address@hidden
address@hidden TODO: add m4macroseq builtin, and alias --m4macroseq
+Issue a warning if the regular expression @var{REGEXP} has a non-empty
+match in any macro definition (either by @code{define} or
address@hidden). Empty matches are ignored; therefore, supplying the
+empty string as @var{REGEXP} disables any warning. Otherwise,
address@hidden is compiled according to the current regular expression
+syntax. If the optional @var{REGEXP} is not supplied, then a default
+regular expression is used, equivalent to
address@hidden(@address@hidden@}\|[0-9][0-9]+\)} in the @code{GNU_M4} regular
+expression flavor (a literal @samp{$} followed by multiple digits or by
+an open brace). The default expression is chosen to detect the
+sequences that changed semantics in the default operation of
address@hidden M4 2.0 compared to earlier versions of GNU M4
+(@pxref{Extended Arguments}). Providing an alternate regular expression
+can provide a useful reverse lookup feature of finding where a macro is
+defined to have a given definition, or accomodate uses of
address@hidden that intentionally alter extended argument syntax.
+
@item -W
@itemx --warnings
Enable warnings. Warnings are on by default unless
@@ -746,7 +766,9 @@
@item --import-environment
Imports every variable in the environment as a macro. This is done
before @option{-D} and @option{-U}, so they can override the
-environment.
+environment. However, this means that @option{--warn-macro-sequence} is
+unable to detect any environment variable definitions that have a
+questionable sequence.
@item -I @var{DIRECTORY}
@itemx address@hidden
@@ -1725,6 +1747,7 @@
* Define:: Defining a new macro
* Arguments:: Arguments to macros
* Pseudo Arguments:: Special arguments to macros
+* Extended Arguments:: Extended arguments
* Undefine:: Deleting a macro
* Defn:: Renaming macros
* Pushdef:: Temporarily redefining macros
@@ -1854,20 +1877,6 @@
(You should try and improve this example so that clients of @code{exch}
do not have to double quote; or @pxref{Improved exch, , Answers}).
address@hidden @acronym{GNU} extensions
address@hidden @code{m4} allows the number following the @samp{$} to
-consist of one
-or more digits, allowing macros to have any number of arguments. This
-is not so in UNIX implementations of @code{m4}, which only recognize
-one digit.
address@hidden FIXME - See Austin group XCU ERN 111. POSIX says that $11 must
address@hidden be the first argument concatenated with 1, and instead reserves
address@hidden ${11} for implementation use. Once this is implemented, the
address@hidden documentation needs to reflect how these extended arguments
address@hidden are handled, as well as backwards compatibility issues with
address@hidden 1.4.x. Also, consider adding further extensions such as
address@hidden ${1-default}, which expands to `default' if $1 is empty.
-
As a special case, the zeroth argument, @code{$0}, is always the name
of the macro being expanded.
@@ -1892,6 +1901,22 @@
The @samp{foo} in the expansion text is @emph{not} expanded, since it is
a quoted string, and not a name.
address@hidden requires that if multiple digits appear after @samp{$},
+the first digit is used to select the parameter, and the remaining
+digits are concatenated as literal text. Earlier versions of
address@hidden M4 had an incompatible extension that would use all of
+the digits to reference beyond the ninth argument, but this was changed
+in M4 2.0. @xref{Extended Arguments}, for more details on this change.
+
address@hidden
+define(`foo', `$11')
address@hidden
+define(`a1', `hello')
address@hidden
+foo(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k', `l')
address@hidden
address@hidden example
+
@node Pseudo Arguments
@section Special arguments to macros
@@ -2040,6 +2065,96 @@
@result{}nested quote around both: arg
@end example
address@hidden Extended Arguments
address@hidden Extended arguments
+
address@hidden @acronym{GNU} extensions
address@hidden nine arguments, more than
address@hidden more than nine arguments
address@hidden arguments, more than nine
address@hidden arguments, extended
address@hidden positional parameters, more than nine
address@hidden extended arguments
address@hidden FIXME - Need to implement the changeextarg builtin. Also,
address@hidden consider adding further extensions such as ${1-default}, which
address@hidden expands to `default' if $1 is empty.
address@hidden states that @samp{$} followed immediately by @address@hidden
+in a macro definition is implementation-defined. Since @acronym{GNU} M4
+2.0, @code{m4} takes advantage of this to offer @dfn{extended
+arguments}. By default, an extended argument consists of the text
+between @address@hidden and @address@hidden in a macro definition. When the
text
+after @address@hidden cannot be parsed as a recognized extended argument,
+including the case where where no @address@hidden could be found, a warning is
+issued, and the text is output literally.
+
+The currently supported extended arguments are:
+
address@hidden @code
address@hidden @var{digits}
+Refers to the @var{n}th argument, supporting arguments beyond nine.
+
address@hidden #
address@hidden *
address@hidden @@
+Has the same pseudo-argument behavior as in non-extended arguments.
address@hidden table
+
+More extended arguments may be introduced in future versions.
+
address@hidden
+define(`foo', address@hidden@}')
address@hidden
+define(`a1', `hello')
address@hidden
+foo(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k', `l')
address@hidden
+define(`m', address@hidden@} address@hidden')
address@hidden
+m
address@hidden:stdin:5: Warning: m: unrecognized extended argument:
address@hidden@}'
address@hidden:stdin:5: Warning: m: unterminated extended argument:
address@hidden'
address@hidden@address@hidden address@hidden
address@hidden example
+
+If you need to portably access beyond the ninth argument, such as with
+earlier versions of M4 that do not recognize extended arguments, you can
+use the @code{argn} macro documented later (@pxref{Shift}).
+
+Meanwhile, if you want to guarantee that you will get a literal
address@hidden@{} in output when expanding a macro, rather than triggering
+extended argument parsing, you can use nested quoting to your advantage:
+
address@hidden
+define(`foo', `single quoted $`'@address@hidden output')
address@hidden
+define(`bar', ``double quoted $'address@hidden@} output'')
address@hidden
+foo(`a', `b')
address@hidden quoted address@hidden@} output
+bar(`a', `b')
address@hidden quoted address@hidden@} output
address@hidden example
+
+To help you detect places in your M4 input files that might have changed
+in behavior when upgrading from earlier versions of @acronym{GNU} M4,
+you can use the @option{--warn-macro-sequence} command-line option
+(@pxref{Operation modes, , Invoking m4}) with the default regular
+expression. This will add a warning any time a macro definition
+includes @samp{$} followed by multiple digits, or by @address@hidden The
+warning is not enabled by default, because it conflicts with using
+extended arguments.
+
address@hidden options: --warn-macro-sequence
address@hidden
+$ @kbd{m4 --warn-macro-sequence}
+define(`foo', `$001 address@hidden@} $1')
address@hidden:stdin:1: Warning: define: `foo' contains sequence `$001'
address@hidden:stdin:1: Warning: define: `foo' contains sequence
address@hidden@}'
address@hidden
+foo(`bar')
address@hidden bar bar
address@hidden example
+
@node Undefine
@section Deleting a macro
@@ -2920,6 +3035,33 @@
@result{}divert`'dnl
@end example
address@hidden nine arguments, more than
address@hidden more than nine arguments
address@hidden arguments, more than nine
+One more useful macro based on @code{shift} allows portably selecting an
+arbitrary argument (usually greater than the ninth argument), without
+relying on extended arguments (@pxref{Extended Arguments}).
+
address@hidden Composite argn (@var{n}, @dots{})
+Expands to argument @var{n} out of the remaining arguments. @var{n}
+must be a positive number. Usually invoked as
address@hidden(address@hidden',$@@)}.
address@hidden deffn
+
+It is implemented as:
+
address@hidden
+define(`argn', `ifelse(`$1', 1, ``$2'',
+ `argn(decr(`$1'), shift(shift($@@)))')')
address@hidden
+argn(`1', `a')
address@hidden
+define(`foo', `argn(`11', $@@)')
address@hidden
+foo(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k', `l')
address@hidden
address@hidden example
+
@node Forloop
@section Iteration by counting
@@ -4669,6 +4811,23 @@
Note how it is possible to have both long and short quotes, if
@code{changequote} is used before @code{changesyntax}.
address@hidden arguments, extended
address@hidden extended arguments
address@hidden disabling extended arguments
+For backwards compatibility with earlier versions of @acronym{GNU} M4,
+it is sometimes desirable to disable extended arguments
+(@pxref{Extended Arguments}). This can easily be done by ensuring that
+no characters belong to the left brace and right brace categories. The
+following example is portable to @acronym{GNU} M4 1.4.x.
+
address@hidden
+ifdef(`changesyntax', `changesyntax(address@hidden', address@hidden')')dnl
+define(`foo', address@hidden@}')
address@hidden
+foo(`a')
address@hidden@address@hidden
address@hidden example
+
The syntax table is initialized to be backwards compatible, so if you
never call @code{changesyntax}, nothing will have changed.
@@ -7173,12 +7332,11 @@
@itemize @bullet
@item
-In the @address@hidden notation for macro arguments, @var{n} can contain
-several digits, while the System V @code{m4} only accepts one digit.
-This allows macros in GNU @code{m4} to take any number of arguments, and
-not only nine (@pxref{Arguments}).
address@hidden does not allow this extension, so it is disabled if
address@hidden is set.
address@hidden allows implementations to treat @address@hidden in macro
+definitions however they would like. While most implementations merely
+output both characters literally, @acronym{GNU} M4 provides the
+extension of extended arguments (@pxref{Extended Arguments}). This
+allows macros to directly refer to more than nine positional parameters.
@item
Files included with @code{include} and @code{sinclude} are sought in a
Index: m4/m4module.h
===================================================================
RCS file: /sources/m4/m4/m4/m4module.h,v
retrieving revision 1.106
diff -u -r1.106 m4module.h
--- m4/m4module.h 5 Feb 2007 17:31:10 -0000 1.106
+++ m4/m4module.h 1 Mar 2007 04:13:58 -0000
@@ -188,7 +188,7 @@
m4_obstack*);
extern const char * m4_get_module_name (lt_dlhandle);
-extern void m4__module_exit (m4 *context);
+extern void m4__module_exit (m4 *);
@@ -211,7 +211,6 @@
extern void m4_symbol_popdef (m4_symbol_table*, const char *);
extern m4_symbol *m4_symbol_rename (m4_symbol_table*, const char *,
const char *);
-
extern void m4_symbol_delete (m4_symbol_table*, const char *);
#define m4_symbol_delete(symtab, name) M4_STMT_START { \
@@ -280,11 +279,22 @@
/* --- MACRO MANAGEMENT --- */
-extern void m4_macro_expand_input (m4 *);
-extern void m4_macro_call (m4 *, m4_symbol_value *,
- m4_obstack *, int,
- m4_symbol_value **);
-
+/* The default sequence detects multi-digit parameters (obsolete after
+ 1.4.x), and any use of extended arguments with the default ${}
+ syntax (new in 2.0). */
+#define M4_DEFAULT_MACRO_SEQUENCE "\\$\\({[^}]*}\\|[0-9][0-9]+\\)"
+
+extern void m4_macro_expand_input (m4 *);
+extern void m4_macro_call (m4 *, m4_symbol_value *,
+ m4_obstack *, int,
+ m4_symbol_value **);
+extern void m4_set_macro_sequence (m4 *, const char *, int,
+ const char *);
+extern void m4_free_macro_sequence (m4 *);
+extern m4_symbol_value *m4_macro_define (m4 *, const char *, const char
*,
+ bool);
+extern void m4_check_macro_sequence (m4 *, const char *, const char *,
+ const char *);
/* --- RUNTIME DEBUGGING --- */
@@ -336,8 +346,21 @@
/* --- REGEXP SYNTAX --- */
+/* The regs_allocated field in an re_pattern_buffer refers to the
+ state of the re_registers struct used in successive matches with
+ the same compiled pattern. */
+
+typedef struct {
+ struct re_pattern_buffer pat; /* compiled regular expression */
+ struct re_registers regs; /* match registers */
+} m4_pattern_buffer;
+
extern const char * m4_regexp_syntax_decode (int);
extern int m4_regexp_syntax_encode (const char *);
+extern m4_pattern_buffer *m4_regexp_compile (m4 *, const char *,
+ const char *, int,
+ bool, m4_pattern_buffer *);
+extern void m4_regexp_free (m4_pattern_buffer *);
Index: m4/macro.c
===================================================================
RCS file: /sources/m4/m4/m4/macro.c,v
retrieving revision 1.64
diff -u -r1.64 macro.c
--- m4/macro.c 11 Nov 2006 16:21:25 -0000 1.64
+++ m4/macro.c 1 Mar 2007 04:13:58 -0000
@@ -1,5 +1,5 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
@@ -406,6 +406,51 @@
}
}
+/* This function handles recognition and expansion of extended
+ arguments. The range [START,END) contains text that might be an
+ extended argument; if so, expand it based on ARGC and ARGV into
+ OBS. Return true if the argument was recognized. */
+static bool
+process_extended_argument (m4 *context, m4_obstack *obs, int argc,
+ m4_symbol_value **argv, const char *start,
+ const char *end)
+{
+ switch (*start)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ char *endp;
+ int i = (int) strtol (start, &endp, 10);
+ if (endp == end)
+ {
+ if (i < argc)
+ m4_shipout_string (context, obs, M4ARG (i), 0, false);
+ return true;
+ }
+ break;
+ }
+
+ case '#': /* number of arguments */
+ if (end - start == 1)
+ {
+ m4_shipout_int (obs, argc - 1);
+ return true;
+ }
+ break;
+
+ case '*': /* all arguments */
+ case '@': /* ... same, but quoted */
+ if (end - start == 1)
+ {
+ m4_dump_args (context, obs, argc, argv, ",", *start == '@');
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
/* This function handles all expansion of user defined and predefined
macros. It is called with an obstack OBS, where the macros expansion
will be placed, as an unfinished object. SYMBOL points to the macro
@@ -415,38 +460,31 @@
process_macro (m4 *context, m4_symbol_value *value, m4_obstack *obs,
int argc, m4_symbol_value **argv)
{
- const unsigned char *text;
+ const char *text;
int i;
for (text = m4_get_symbol_value_text (value); *text != '\0';)
{
- char ch;
+ const char *dollar;
- if (!m4_has_syntax (M4SYNTAX, *text, M4_SYNTAX_DOLLAR))
+ if (!m4_has_syntax (M4SYNTAX, to_uchar (*text), M4_SYNTAX_DOLLAR))
{
obstack_1grow (obs, *text);
text++;
continue;
}
- ch = *text++;
+ dollar = text++;
switch (*text)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- /* FIXME - multidigit arguments should convert over to ${10}
- syntax instead of $10; see
- http://lists.gnu.org/archive/html/m4-discuss/2006-08/msg00028.html
- for more discussion. */
- if (m4_get_posixly_correct_opt (context) || !isdigit(text[1]))
- {
- i = *text++ - '0';
- }
- else
- {
- char *endp;
- i = (int) strtol (text, &endp, 10);
- text = endp;
- }
+ i = *text++ - '0';
+ /* TODO - warn in this case?
+ if (! m4_get_posixly_correct_opt (context) && isdigit(text[1]))
+ m4_warn (context, 0,
+ _("%s: multi-digit parameters changed behavior from 1.4"),
+ M4ARG (0));
+ */
if (i < argc)
m4_shipout_string (context, obs, M4ARG (i), 0, false);
break;
@@ -463,19 +501,18 @@
break;
default:
- if (m4_get_posixly_correct_opt (context)
- || !VALUE_ARG_SIGNATURE (value))
- {
- obstack_1grow (obs, ch);
- }
- else
+ if (VALUE_ARG_SIGNATURE (value))
{
+ /* TODO - VALUE_ARG_SIGNATURE is not fully implemented.
+ Is it worth killing this as dead code, and figuring
+ out how to use extended arguments to do what was
+ originally envisioned by VALUE_ARG_SIGNATURE? */
size_t len = 0;
- const unsigned char *endp;
+ const char *endp;
const char *key;
for (endp = ++text;
- *endp && m4_has_syntax (M4SYNTAX, *endp,
+ *endp && m4_has_syntax (M4SYNTAX, to_uchar (*endp),
(M4_SYNTAX_OTHER | M4_SYNTAX_ALPHA
| M4_SYNTAX_NUM));
++endp)
@@ -515,9 +552,45 @@
free ((char *) key);
break;
}
+ if (m4_has_syntax (M4SYNTAX, to_uchar (*text), M4_SYNTAX_LBRACE))
+ {
+ const char *start = text + 1;
+ const char *end;
+ /* Check for extended argument candidate. */
+ while (*++text && ! m4_has_syntax (M4SYNTAX, to_uchar (*text),
+ M4_SYNTAX_RBRACE))
+ ;
+ if (! *text)
+ {
+ m4_warn (context, 0,
+ _("%s: unterminated extended argument: `%s'"),
+ M4ARG (0), dollar);
+ text = dollar + 1;
+ obstack_1grow (obs, *dollar);
+ break;
+ }
+ end = text++;
+ if (! process_extended_argument (context, obs, argc, argv,
+ start, end))
+ {
+ m4_warn (context, 0,
+ _("%s: unrecognized extended argument: `%.*s'"),
+ M4ARG (0), text - dollar, dollar);
+ text = dollar + 1;
+ obstack_1grow (obs, *dollar);
+ break;
+ }
+ }
+ else
+ {
+ /* Unrecognized character after dollar; just output the
+ dollar, and let next time around loop pick up the
+ character. */
+ obstack_1grow (obs, *dollar);
+ }
break;
- }
- }
+ } /* default case */
+ } /* for loop */
}
@@ -691,3 +764,104 @@
trace_flush (context);
}
+
+
+/* Storage for the compiled regular expression of
+ --warn-macro-sequence. */
+static m4_pattern_buffer macro_sequence;
+
+/* True if --warn-macro-sequence is in effect. */
+static bool macro_sequence_inuse;
+
+/* Set the regular expression that will be checked during define and
+ pushdef. Warn on failure. If REGEXP is NULL, use the default
+ sequence; if REGEXP is empty, disable checking; otherwise, compile
+ REGEXP written in RESYNTAX. Report errors on behalf of CALLER. */
+void
+m4_set_macro_sequence (m4 *context, const char *regexp, int resyntax,
+ const char *caller)
+{
+ if (! regexp)
+ {
+ /* The default sequence, in emacs flavor, warns for multi-digit
+ parameters, and for any use of extended arguments using
+ default ${} syntax. */
+ regexp = M4_DEFAULT_MACRO_SEQUENCE;
+ resyntax = 0;
+ caller = NULL;
+ }
+ else if (regexp[0] == '\0')
+ {
+ macro_sequence_inuse = false;
+ return;
+ }
+
+ if (m4_regexp_compile (context, caller, regexp, resyntax, false,
+ ¯o_sequence) == NULL)
+ macro_sequence_inuse = false;
+ else
+ macro_sequence_inuse = true;
+}
+
+/* Free storage used by macro_sequence. */
+void
+m4_free_macro_sequence (m4 *context)
+{
+ m4_regexp_free (¯o_sequence);
+ macro_sequence_inuse = false;
+}
+
+/* Define a text macro NAME to have VALUE. If PUSH, use pushdef
+ instead of define semantics. */
+m4_symbol_value *
+m4_macro_define (m4 *context, const char *name, const char *value,
+ bool push)
+{
+ m4_symbol_value *symbol_value = m4_symbol_value_create ();
+ char *defn = xstrdup (value ? value : "");
+
+ m4_check_macro_sequence (context, name, defn, NULL);
+ m4_set_symbol_value_text (symbol_value, defn);
+ VALUE_MAX_ARGS (symbol_value) = -1;
+ if (push)
+ m4_symbol_pushdef (M4SYMTAB, name, symbol_value);
+ else
+ m4_symbol_define (M4SYMTAB, name, symbol_value);
+ return symbol_value;
+}
+
+/* Check the macro NAME just defined to VALUE for any sequences
+ matched in macro_sequence. Report warnings on behalf of CALLER,
+ which may be NULL. */
+void
+m4_check_macro_sequence (m4 *context, const char *name, const char *value,
+ const char *caller)
+{
+ /* Implement --warn-macro-sequence. */
+ if (macro_sequence_inuse && value && value[0])
+ {
+ regoff_t offset = 0;
+ size_t len = strlen (value);
+
+ while ((offset = re_search (¯o_sequence.pat, value, len, offset,
+ len - offset, ¯o_sequence.regs)) >= 0)
+ {
+ /* Skip empty matches. */
+ if (macro_sequence.regs.start[0] == macro_sequence.regs.end[0])
+ offset++;
+ else
+ {
+ offset = macro_sequence.regs.end[0];
+ m4_warn (context, 0,
+ _("%s%s`%s' contains sequence `%.*s'"),
+ caller ? caller : "", caller ? ": " : "",
+ name, offset - macro_sequence.regs.start[0],
+ value + macro_sequence.regs.start[0]);
+ }
+ }
+ if (offset == -2)
+ m4_error (context, 0, 0,
+ _("%s: error checking for macro sequence in `%s'"),
+ caller, name);
+ }
+}
Index: m4/module.c
===================================================================
RCS file: /sources/m4/m4/m4/module.c,v
retrieving revision 1.48
diff -u -r1.48 module.c
--- m4/module.c 27 Oct 2006 17:03:51 -0000 1.48
+++ m4/module.c 1 Mar 2007 04:13:58 -0000
@@ -1,6 +1,6 @@
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1998, 1999, 2002, 2003,
- 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
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
@@ -192,12 +192,9 @@
{
for (; mp->name != NULL; mp++)
{
- m4_symbol_value *value = m4_symbol_value_create ();
-
- m4_set_symbol_value_text (value, xstrdup (mp->value));
+ m4_symbol_value *value = m4_macro_define (context, mp->name,
+ mp->value, true);
VALUE_HANDLE (value) = handle;
-
- m4_symbol_pushdef (M4SYMTAB, mp->name, value);
}
m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
Index: m4/resyntax.c
===================================================================
RCS file: /sources/m4/m4/m4/resyntax.c,v
retrieving revision 1.4
diff -u -r1.4 resyntax.c
--- m4/resyntax.c 1 Feb 2007 05:43:09 -0000 1.4
+++ m4/resyntax.c 1 Mar 2007 04:13:58 -0000
@@ -124,3 +124,51 @@
return resyntax->spec;
}
+
+/* Compile a REGEXP using the RESYNTAX bits, into the buffer BUF.
+ Report errors on behalf of CALLER. If NO_SUB, optimize the
+ compilation to skip filling out the regs member of the buffer.
+ Return BUF on success, NULL on error. BUF should start life
+ zero-initialized, or be previously initialized by this method. */
+m4_pattern_buffer *
+m4_regexp_compile (m4 *context, const char *caller,
+ const char *regexp, int resyntax, bool no_sub,
+ m4_pattern_buffer *buf)
+{
+ /* FIXME - this method is not reentrant, since re_compile_pattern
+ mallocs memory and depends on the global variable
+ re_syntax_options for its syntax (but at least the compiled regex
+ remembers its syntax even if the global variable changes later).
+ To be reentrant, we would need a mutex in this method. */
+
+ const char *msg; /* error message from re_compile_pattern */
+
+ re_set_syntax (resyntax);
+ regfree (&buf->pat);
+ buf->pat.no_sub = no_sub;
+ msg = re_compile_pattern (regexp, strlen (regexp), &buf->pat);
+
+ if (msg != NULL)
+ {
+ assert (caller);
+ m4_error (context, 0, 0, _("%s: bad regular expression `%s': %s"),
+ caller, regexp, msg);
+ return NULL;
+ }
+
+ re_set_registers (&buf->pat, &buf->regs, buf->regs.num_regs,
+ buf->regs.start, buf->regs.end);
+ return buf;
+}
+
+/* Reclaim any dynamic storage used by BUF, as obtained by
+ m4_regexp_compile. */
+void
+m4_regexp_free (m4_pattern_buffer *buf)
+{
+ regfree (&buf->pat);
+ free (buf->regs.start);
+ free (buf->regs.end);
+ /* Explicitly reset the memory, in case buf gets reused. */
+ memset (buf, 0, sizeof *buf);
+}
Index: modules/gnu.c
===================================================================
RCS file: /sources/m4/m4/modules/gnu.c,v
retrieving revision 1.71
diff -u -r1.71 gnu.c
--- modules/gnu.c 15 Jan 2007 14:04:27 -0000 1.71
+++ modules/gnu.c 1 Mar 2007 04:13:58 -0000
@@ -106,15 +106,6 @@
-/* The regs_allocated field in an re_pattern_buffer refers to the
- state of the re_registers struct used in successive matches with
- the same compiled pattern: */
-
-typedef struct {
- struct re_pattern_buffer pat; /* compiled regular expression */
- struct re_registers regs; /* match registers */
-} m4_pattern_buffer;
-
static m4_pattern_buffer gnu_buf; /* compiled regular expression */
/* Compile a REGEXP using the RESYNTAX bits, and return the buffer.
@@ -122,36 +113,13 @@
compilation to skip filling out the regs member of the buffer. */
static m4_pattern_buffer *
-m4_regexp_compile (m4 *context, const char *caller,
- const char *regexp, int resyntax, bool no_sub)
+regexp_compile (m4 *context, const char *caller,
+ const char *regexp, int resyntax, bool no_sub)
{
- /* gnu_buf is guaranteed to start life 0-initialized, which works in the
- below algorithm.
-
- FIXME - this method is not reentrant, since re_compile_pattern
- mallocs memory, depends on the global variable re_syntax_options
- for its syntax (but at least the compiled regex remembers its
- syntax even if the global variable changes later), and since we
- use a static variable. To be reentrant, we would need a mutex in
- this method, and move the storage for gnu_buf into context. */
-
- const char *msg; /* error message from re_compile_pattern */
-
- re_set_syntax (resyntax);
- regfree (&gnu_buf.pat);
- gnu_buf.pat.no_sub = no_sub;
- msg = re_compile_pattern (regexp, strlen (regexp), &gnu_buf.pat);
-
- if (msg != NULL)
- {
- m4_error (context, 0, 0, _("%s: bad regular expression `%s': %s"),
- caller, regexp, msg);
- return NULL;
- }
-
- re_set_registers (&gnu_buf.pat, &gnu_buf.regs, gnu_buf.regs.num_regs,
- gnu_buf.regs.start, gnu_buf.regs.end);
- return &gnu_buf;
+ /* FIXME - this method is not reentrant, since we reference a static
+ variable. */
+ return m4_regexp_compile (context, caller, regexp, resyntax, no_sub,
+ &gnu_buf);
}
@@ -218,7 +186,7 @@
/* For each match against compiled REGEXP (held in BUF -- as returned
- by m4_regexp_compile) in VICTIM, substitute REPLACE. Non-matching
+ by regexp_compile) in VICTIM, substitute REPLACE. Non-matching
characters are copied verbatim, and the result copied to the
obstack. Errors are reported on behalf of CALLER. Return true if
a substitution was made. If IGNORE_DUPLICATES is set, don't worry
@@ -288,12 +256,7 @@
/* Reclaim memory used by this module. */
M4FINISH_HANDLER(gnu)
{
- regfree (&gnu_buf.pat);
- free (gnu_buf.regs.start);
- free (gnu_buf.regs.end);
- /* If this module was preloaded, then we need to explicitly reset
- the memory in case it gets reloaded. */
- memset (&gnu_buf, 0, sizeof gnu_buf);
+ m4_regexp_free (&gnu_buf);
}
@@ -663,7 +626,7 @@
return;
}
- buf = m4_regexp_compile (context, me, M4ARG (2), resyntax, false);
+ buf = regexp_compile (context, me, M4ARG (2), resyntax, false);
if (!buf)
return;
@@ -720,7 +683,7 @@
/* regexp(VICTIM, REGEXP) */
replace = NULL;
- buf = m4_regexp_compile (context, me, M4ARG (2), resyntax, replace == NULL);
+ buf = regexp_compile (context, me, M4ARG (2), resyntax, replace == NULL);
if (!buf)
return;
@@ -778,7 +741,7 @@
return;
}
- buf = m4_regexp_compile (context, me, regexp, resyntax, false);
+ buf = regexp_compile (context, me, regexp, resyntax, false);
if (!buf)
return;
Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.104
diff -u -r1.104 m4.c
--- modules/m4.c 28 Feb 2007 21:31:12 -0000 1.104
+++ modules/m4.c 1 Mar 2007 04:13:58 -0000
@@ -179,7 +179,11 @@
if (argc == 2)
m4_set_symbol_value_text (value, xstrdup (""));
else
- m4_symbol_value_copy (value, argv[2]);
+ {
+ m4_symbol_value_copy (value, argv[2]);
+ if (m4_is_symbol_value_text (value))
+ m4_check_macro_sequence (context, M4ARG (1), M4ARG (2), M4ARG (0));
+ }
m4_symbol_define (M4SYMTAB, M4ARG (1), value);
}
@@ -210,7 +214,11 @@
if (argc == 2)
m4_set_symbol_value_text (value, xstrdup (""));
else
- m4_symbol_value_copy (value, argv[2]);
+ {
+ m4_symbol_value_copy (value, argv[2]);
+ if (m4_is_symbol_value_text (value))
+ m4_check_macro_sequence (context, M4ARG (1), M4ARG (2), M4ARG (0));
+ }
m4_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
}
Index: src/freeze.c
===================================================================
RCS file: /sources/m4/m4/src/freeze.c,v
retrieving revision 1.62
diff -u -r1.62 freeze.c
--- src/freeze.c 28 Feb 2007 21:31:12 -0000 1.62
+++ src/freeze.c 1 Mar 2007 04:13:58 -0000
@@ -244,7 +244,7 @@
produce_mem_dump (file, M4SYNTAX->lquote.string,
M4SYNTAX->lquote.length);
produce_mem_dump (file, M4SYNTAX->rquote.string,
- M4SYNTAX->rquote.length);
+ M4SYNTAX->rquote.length);
fputc ('\n', file);
}
@@ -492,7 +492,7 @@
else
m4__module_open (context, "gnu", NULL);
/* Disable { and } categories, since ${11} was not supported in
- 1.4.x. */
+ 1.4.x. */
m4_set_syntax (M4SYNTAX, 'O', '+', "{}");
break;
default:
@@ -758,17 +758,10 @@
/* Enter a macro having an expansion text as a definition. */
{
- m4_symbol_value *token = xzalloc (sizeof *token);
- lt_dlhandle handle = 0;
-
+ m4_symbol_value *token = m4_macro_define (context, string[0],
+ string[1], true);
if (number[2] > 0)
- handle = m4__module_find (string[2]);
-
- m4_set_symbol_value_text (token, xstrdup (string[1]));
- VALUE_HANDLE (token) = handle;
- VALUE_MAX_ARGS (token) = -1;
-
- m4_symbol_pushdef (M4SYMTAB, string[0], token);
+ VALUE_HANDLE (token) = m4__module_find (string[2]);
}
break;
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.109
diff -u -r1.109 main.c
--- src/main.c 28 Feb 2007 21:31:13 -0000 1.109
+++ src/main.c 1 Mar 2007 04:13:58 -0000
@@ -84,7 +84,7 @@
--help display this help and exit\n\
--version output version information and exit\n\
"), stdout);
- fputs (_("\
+ printf (_("\
-b, --batch buffer output, process interrupts\n\
-c, --discard-comments do not copy comments to the output\n\
-E, --fatal-warnings once: warnings become errors, twice: stop\n\
@@ -94,8 +94,11 @@
-Q, --quiet, --silent suppress some warnings for builtins\n\
-r, --regexp-syntax[=SPEC] set default regexp syntax to SPEC [GNU_M4]\n\
--safer disable potentially unsafe builtins\n\
+ --warn-macro-sequence[=REGEXP]\n\
+ warn if macro definition matches REGEXP,\n\
+ default %s\n\
-W, --warnings enable all warnings\n\
-"), stdout);
+"), M4_DEFAULT_MACRO_SEQUENCE);
puts ("");
fputs (_("\
SPEC is any one of:\n\
@@ -207,6 +210,7 @@
SYNCOUTPUT_OPTION, /* not quite -s, because of opt arg */
TRACEOFF_OPTION, /* no short opt */
UNLOAD_MODULE_OPTION, /* no short opt */
+ WARN_MACRO_SEQUENCE_OPTION, /* no short opt */
WORD_REGEXP_OPTION, /* deprecated, used to be -W */
HELP_OPTION, /* no short opt */
@@ -256,6 +260,7 @@
{"syncoutput", optional_argument, NULL, SYNCOUTPUT_OPTION},
{"traceoff", required_argument, NULL, TRACEOFF_OPTION},
{"unload-module", required_argument, NULL, UNLOAD_MODULE_OPTION},
+ {"warn-macro-sequence", optional_argument, NULL, WARN_MACRO_SEQUENCE_OPTION},
{"word-regexp", required_argument, NULL, WORD_REGEXP_OPTION},
{"help", no_argument, NULL, HELP_OPTION},
@@ -423,6 +428,7 @@
case SYNCOUTPUT_OPTION:
case TRACEOFF_OPTION:
case UNLOAD_MODULE_OPTION:
+ case WARN_MACRO_SEQUENCE_OPTION:
/* Arguments that cannot be handled until later are accumulated. */
defn = xmalloc (sizeof *defn);
@@ -659,21 +665,14 @@
case 'D':
case 'p':
{
- m4_symbol_value *value = m4_symbol_value_create ();
-
/* defn->value is read-only, so we need a copy. */
char *macro_name = xstrdup (arg);
char *macro_value = strchr (macro_name, '=');
if (macro_value != NULL)
*macro_value++ = '\0';
- m4_set_symbol_value_text (value, xstrdup (macro_value
- ? macro_value : ""));
-
- if (defn->code == 'D')
- m4_symbol_define (M4SYMTAB, macro_name, value);
- else
- m4_symbol_pushdef (M4SYMTAB, macro_name, value);
+ m4_macro_define (context, macro_name, macro_value,
+ defn->code == 'p');
free (macro_name);
}
break;
@@ -726,6 +725,12 @@
m4_module_unload (context, arg, NULL);
break;
+ case WARN_MACRO_SEQUENCE_OPTION:
+ m4_set_macro_sequence (context, arg,
+ m4_get_regexp_syntax_opt (context),
+ "--warn-macro-sequence");
+ break;
+
default:
assert (!"INTERNAL ERROR: bad code in deferred arguments");
abort ();
@@ -775,6 +780,7 @@
if (read_stdin && close_stream (stdin) == EOF)
m4_error (context, 0, errno, _("error closing stdin"));
+ m4_free_macro_sequence (context);
exit_status = m4_get_exit_status (context);
m4_delete (context);
Index: tests/macros.at
===================================================================
RCS file: /sources/m4/m4/tests/macros.at,v
retrieving revision 1.12
diff -u -r1.12 macros.at
--- tests/macros.at 14 Nov 2006 05:58:01 -0000 1.12
+++ tests/macros.at 1 Mar 2007 04:13:58 -0000
@@ -1,5 +1,5 @@
# Hand crafted tests for GNU M4. -*- Autotest -*-
-# Copyright (C) 2001, 2006 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2006, 2007 Free Software Foundation, Inc.
# 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
@@ -191,6 +191,42 @@
+## ------------------ ##
+## Extended arguments ##
+## ------------------ ##
+
+AT_SETUP([Extended arguments])
+
+dnl check that changesyntax can enable/disable extended arguments
+AT_DATA([in], [[define(`foo', `${1}')dnl
+foo(`a')
+changesyntax(`{=', `}=')dnl
+foo(`a')
+changesyntax()dnl
+foo(`a')
+]])
+AT_CHECK_M4([in], [0],
+[[a
+${1}
+a
+]])
+
+dnl check that all pseudo args are supported
+AT_DATA([in], [[define(`foo', ``${0}'address@hidden')dnl
+foo
+foo(`a')
+foo(`a', `b')
+]])
+AT_CHECK_M4([in], [0],
+[[foo-0---
+foo-1-a-a-
+foo-2-a,b-a,b-
+]])
+
+AT_CLEANUP
+
+
+
## ---------------- ##
## pushdef/popdef. ##
## ---------------- ##
Index: tests/options.at
===================================================================
RCS file: /sources/m4/m4/tests/options.at,v
retrieving revision 1.25
diff -u -r1.25 options.at
--- tests/options.at 5 Feb 2007 17:31:10 -0000 1.25
+++ tests/options.at 1 Mar 2007 04:13:58 -0000
@@ -782,3 +782,61 @@
AT_CHECK_M4([--traceoff=unknown], [0])
AT_CLEANUP
+
+
+## ------------------- ##
+## warn-macro-sequence ##
+## ------------------- ##
+
+AT_SETUP([--warn-macro-sequence])
+
+AT_DATA([[in]], [[define(`foo', `$01 ${1} ab')dnl
+pushdef(`foo', `$`'{1} $01')dnl
+foo
+]])
+
+dnl no sequence warnings by default
+AT_CHECK_M4([in], [0], [[${1} foo1
+]])
+
+dnl default regex
+AT_CHECK_M4([--warn-macro-sequence in], [0], [[${1} foo1
+]], [[m4:in:1: Warning: define: `foo' contains sequence `$01'
+m4:in:1: Warning: define: `foo' contains sequence `${1}'
+m4:in:2: Warning: pushdef: `foo' contains sequence `$01'
+]])
+
+dnl empty regex disables prior usage
+AT_CHECK_M4([--warn-macro-sequence --warn-macro-sequence= in], [0],
+[[${1} foo1
+]])
+
+dnl check interleaving between files, and alternate regex
+AT_CHECK_M4([--warn-macro-sequence=01 in --warn-macro-sequence='[[ba]*]' in],
+[0], [[${1} foo1
+${1} foo1
+]], [[m4:in:1: Warning: define: `foo' contains sequence `01'
+m4:in:2: Warning: pushdef: `foo' contains sequence `01'
+m4:in:1: Warning: define: `foo' contains sequence `ab'
+]])
+
+dnl check interaction with resyntax
+AT_CHECK_M4([--regexp-syntax=EXTENDED --warn-macro-sequence='(a|b)+' in], [0],
+[[${1} foo1
+]], [[m4:in:1: Warning: define: `foo' contains sequence `ab'
+]])
+
+dnl check interaction with resyntax
+AT_CHECK_M4([--warn-macro-sequence='(a|b)+' --regexp-syntax=EXTENDED in], [0],
+[[${1} foo1
+]])
+
+dnl default regex sequence must work with non-default resyntax
+AT_CHECK_M4([--regexp-syntax=EXTENDED --warn-macro-sequence in], [0],
+[[${1} foo1
+]], [[m4:in:1: Warning: define: `foo' contains sequence `$01'
+m4:in:1: Warning: define: `foo' contains sequence `${1}'
+m4:in:2: Warning: pushdef: `foo' contains sequence `$01'
+]])
+
+AT_CLEANUP
Index: tests/others.at
===================================================================
RCS file: /sources/m4/m4/tests/others.at,v
retrieving revision 1.31
diff -u -r1.31 others.at
--- tests/others.at 5 Feb 2007 17:31:10 -0000 1.31
+++ tests/others.at 1 Mar 2007 04:13:58 -0000
@@ -683,7 +683,8 @@
concat()
concat(`hej', `med', `dig')
concat(`hej', `med', `dig', `en gang igen')
-concat(an, awful, lot, of, argument, at, least, more, that, ten, silly,
arguments)
+concat(an, awful, lot, of, arguments,
+ at, least, more, than, ten, silly, arguments)
]])
AT_DATA([[expout]],
@@ -696,7 +697,7 @@
hej med dig
hej med dig en gang igen
-an awful lot of argument at least more that ten silly arguments
+an awful lot of arguments at least more than ten silly arguments
]])
AT_DATA([[experr]],
@@ -711,15 +712,15 @@
m4trace: -1- concat(`med', `dig', `en gang igen') -> `ifelse(1, 3, `med',
`med` 'concat(shift(`med',`dig',`en gang igen'))')'
m4trace: -1- concat(`dig', `en gang igen') -> `ifelse(1, 2, `dig',
`dig` 'concat(shift(`dig',`en gang igen'))')'
m4trace: -1- concat(`en gang igen') -> `ifelse(1, 1, `en gang igen', `en gang
igen` 'concat(shift(`en gang igen'))')'
-m4trace: -1- concat(`an', `awful', `lot', `of', `argument', `at', `least',
`more', `that', `ten', `silly', `arguments') -> `ifelse(1, 12, `an',
`an` 'concat(shift
(`an',`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`ar
guments'))')'
-m4trace: -1- concat(`awful', `lot', `of', `argument', `at', `least', `more',
`that', `ten', `silly', `arguments') -> `ifelse(1, 11, `awful', `awful` 'concat
(shift
(`awful',`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`argumen
ts'))')'
-m4trace: -1- concat(`lot', `of', `argument', `at', `least', `more', `that',
`ten', `silly', `arguments') -> `ifelse(1, 10, `lot', `lot` 'concat(shift
(`lot',`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`of', `argument', `at', `least', `more', `that', `ten',
`silly', `arguments') -> `ifelse(1, 9, `of', `of` 'concat(shift
(`of',`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`argument', `at', `least', `more', `that', `ten', `silly',
`arguments') -> `ifelse(1, 8, `argument', `argument` 'concat(shift
(`argument',`at',`least',`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`at', `least', `more', `that', `ten', `silly',
`arguments') -> `ifelse(1, 7, `at', `at` 'concat(shift
(`at',`least',`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`least', `more', `that', `ten', `silly', `arguments') ->
`ifelse(1, 6, `least', `least` 'concat(shift
(`least',`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`more', `that', `ten', `silly', `arguments') -> `ifelse(1,
5, `more', `more` 'concat(shift(`more',`that',`ten',`silly',`arguments'))')'
-m4trace: -1- concat(`that', `ten', `silly', `arguments') -> `ifelse(1, 4,
`that', `that` 'concat(shift(`that',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`an', `awful', `lot', `of', `arguments', `at', `least',
`more', `than', `ten', `silly', `arguments') -> `ifelse(1, 12, `an',
`an` 'concat(shift
(`an',`awful',`lot',`of',`arguments',`at',`least',`more',`than',`ten',`silly',`a
rguments'))')'
+m4trace: -1- concat(`awful', `lot', `of', `arguments', `at', `least', `more',
`than', `ten', `silly', `arguments') -> `ifelse(1, 11, `awful', `awful` 'concat
(shift
(`awful',`lot',`of',`arguments',`at',`least',`more',`than',`ten',`silly',`argume
nts'))')'
+m4trace: -1- concat(`lot', `of', `arguments', `at', `least', `more', `than',
`ten', `silly', `arguments') -> `ifelse(1, 10, `lot', `lot` 'concat(shift
(`lot',`of',`arguments',`at',`least',`more',`than',`ten',`silly',`arguments'))')
'
+m4trace: -1- concat(`of', `arguments', `at', `least', `more', `than', `ten',
`silly', `arguments') -> `ifelse(1, 9, `of', `of` 'concat(shift
(`of',`arguments',`at',`least',`more',`than',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`arguments', `at', `least', `more', `than', `ten',
`silly', `arguments') -> `ifelse(1, 8, `arguments', `arguments` 'concat(shift
(`arguments',`at',`least',`more',`than',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`at', `least', `more', `than', `ten', `silly',
`arguments') -> `ifelse(1, 7, `at', `at` 'concat(shift
(`at',`least',`more',`than',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`least', `more', `than', `ten', `silly', `arguments') ->
`ifelse(1, 6, `least', `least` 'concat(shift
(`least',`more',`than',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`more', `than', `ten', `silly', `arguments') -> `ifelse(1,
5, `more', `more` 'concat(shift(`more',`than',`ten',`silly',`arguments'))')'
+m4trace: -1- concat(`than', `ten', `silly', `arguments') -> `ifelse(1, 4,
`than', `than` 'concat(shift(`than',`ten',`silly',`arguments'))')'
m4trace: -1- concat(`ten', `silly', `arguments') -> `ifelse(1, 3, `ten',
`ten` 'concat(shift(`ten',`silly',`arguments'))')'
m4trace: -1- concat(`silly', `arguments') -> `ifelse(1, 2, `silly',
`silly` 'concat(shift(`silly',`arguments'))')'
m4trace: -1- concat(`arguments') -> `ifelse(1, 1, `arguments',
`arguments` 'concat(shift(`arguments'))')'