summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--NEWS9
-rw-r--r--doc/m4.texinfo10
-rw-r--r--ltdl/m4/gnulib-cache.m44
-rw-r--r--m4/macro.c3
-rw-r--r--m4/system_.h4
-rw-r--r--src/main.c33
-rw-r--r--tests/options.at131
8 files changed, 194 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index 6478c507..561f1966 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-09-29 Eric Blake <ebb9@byu.net>
+
+ * ltdl/m4/gnulib-cache.m4: Augment with gnulib-tool --import
+ xstrtol.
+ * m4/system_.h (N_): Define.
+ * src/main.c (main): Validate numeric arguments.
+ (size_opt): New function, idea borrowed from coreutils.
+ * m4/macro.c (expand_macro): -L0 implies no limit.
+ * doc/m4.texinfo (Invoking m4): Document this change.
+ * NEWS: Likewise.
+ * tests/options.at: (--arglength, --nesting-limit)
+ (--regexp-syntax): New tests of argument validation.
+
2006-09-28 Eric Blake <ebb9@byu.net>
* tests/options.at: Alphabetize the tests.
diff --git a/NEWS b/NEWS
index a75b9c23..e3c91e0a 100644
--- a/NEWS
+++ b/NEWS
@@ -96,7 +96,14 @@ promoted to 2.0.
the prefix `m4' or `__'.
* The `-l'/`--arglength' command line argument now affects dumpdef
- output as well as trace output.
+ output as well as trace output. Also, it now performs argument
+ validation and accepts an optional multiplier suffix.
+
+* The `-L'/`--nesting-limit' command line option can now be set to 0
+ to remove the default limit. However, it is still possible that heavily
+ nested input can cause abrupt program termination due to stack
+ overflow. Also, the option now performs argument validation and accepts
+ an optional multiplier suffix.
* FIXME: `m4wrap' semantics need an update to FIFO.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 1a99a0ae..563c6e86 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -609,7 +609,13 @@ loads the @samp{traditional} module in place of the @samp{gnu} module.
@itemx --nesting-limit=@var{NUM}
Artificially limit the nesting of macro calls to @var{NUM} levels,
stopping program execution if this limit is ever exceeded. When not
-specified, nesting is limited to 1024 levels.
+specified, nesting is limited to 1024 levels. A value of zero means
+unlimited; but then heavily nested code could potentially cause a stack
+overflow. @var{NUM} can have an optional scaling suffix.
+@comment FIXME - need a node on what scaling suffixes are supported (see
+@comment [info coreutils 'block size'] for ideas), and need to consider
+@comment whether builtins should also understand scaling suffixes:
+@comment eval, mpeval, perhaps format
The precise effect of this option might be more correctly associated
with textual nesting than dynamic recursion. It has been useful
@@ -695,10 +701,12 @@ future release.
Restrict the size of the output generated by macro tracing or by
@code{dumpdef} to @var{NUM} characters per string. If unspecified or
zero, output is unlimited. @xref{Debug Levels}, for more details.
+@var{NUM} can have an optional scaling suffix.
@comment FIXME - should we add a debuglen macro that can alter this
@comment setting on the fly? Also, should we add an option that
@comment controls whether output strings are sanitized with escape
@comment sequences, so that dumpdef is truly one line per macro?
+@comment FIXME - see comment on --nesting-limit about NUM.
@item -t @var{NAME}
@itemx --trace=@var{NAME}
diff --git a/ltdl/m4/gnulib-cache.m4 b/ltdl/m4/gnulib-cache.m4
index f7922b43..208e937b 100644
--- a/ltdl/m4/gnulib-cache.m4
+++ b/ltdl/m4/gnulib-cache.m4
@@ -15,11 +15,11 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool --macro-prefix=M4 assert binary-io cloexec close-stream dirname error exit fdl filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror xalloc xalloc-die xstrndup xvasprintf
+# gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu --m4-base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool --macro-prefix=M4 assert binary-io cloexec close-stream dirname error exit fdl filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror xalloc xalloc-die xstrndup xstrtol xvasprintf
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
-gl_MODULES([assert binary-io cloexec close-stream dirname error exit fdl filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror xalloc xalloc-die xstrndup xvasprintf])
+gl_MODULES([assert binary-io cloexec close-stream dirname error exit fdl filenamecat fopen-safer free gendocs gettext gnupload mkstemp obstack progname regex regexprops-generic stdbool stdlib-safer strnlen strtol unlocked-io verror xalloc xalloc-die xstrndup xstrtol xvasprintf])
gl_AVOID([])
gl_SOURCE_BASE([gnu])
gl_M4_BASE([ltdl/m4])
diff --git a/m4/macro.c b/m4/macro.c
index 19a584fd..5836b141 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -238,7 +238,8 @@ expand_macro (m4 *context, const char *name, m4_symbol *symbol)
value = m4_get_symbol_value (symbol);
VALUE_PENDING (value)++;
expansion_level++;
- if (expansion_level > m4_get_nesting_limit_opt (context))
+ if (m4_get_nesting_limit_opt (context) > 0
+ && expansion_level > m4_get_nesting_limit_opt (context))
m4_error (context, EXIT_FAILURE, 0, _("\
recursion limit of %d exceeded, use -L<N> to change it"),
m4_get_nesting_limit_opt (context));
diff --git a/m4/system_.h b/m4/system_.h
index 13c204ad..4a3c0587 100644
--- a/m4/system_.h
+++ b/m4/system_.h
@@ -51,10 +51,12 @@
#ifndef _
# ifdef ENABLE_NLS
# include <libintl.h>
-# define _(Text) gettext ((Text))
+# define _(Text) gettext (Text)
# else
# define _(Text) (Text)
# endif
+# define gettext_noop(Text) Text
+# define N_(Text) gettext_noop (Text)
#endif
diff --git a/src/main.c b/src/main.c
index 2c9de7b0..8d25ba1f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -25,6 +25,7 @@
#include "version-etc.h"
#include "gnu/progname.h"
#include "pathconf.h"
+#include "xstrtol.h"
#include <limits.h>
@@ -236,6 +237,22 @@ enum interactive_choice
INTERACTIVE_NO /* -b specified last */
};
+/* Convert OPT to size_t, reporting an error using MSGID if it does
+ not fit. */
+static size_t
+size_opt (char const *opt, char const *msgid)
+{
+ unsigned long int size;
+ strtol_error status = xstrtoul (opt, NULL, 10, &size, "kKmMgGtTPEZY0");
+ if (SIZE_MAX < size && status == LONGINT_OK)
+ status = LONGINT_OVERFLOW;
+ if (status != LONGINT_OK)
+ STRTOL_FATAL_ERROR (opt, _(msgid), status);
+ return size;
+}
+
+
+/* Main entry point. Parse arguments, load modules, then parse input. */
int
main (int argc, char *const *argv, char *const *envp)
{
@@ -243,6 +260,7 @@ main (int argc, char *const *argv, char *const *envp)
macro_definition *tail;
macro_definition *defn;
int optchar; /* option character */
+ size_t size; /* for parsing numeric option arguments */
macro_definition *defines;
FILE *fp;
@@ -291,8 +309,8 @@ main (int argc, char *const *argv, char *const *envp)
case 'H':
case HASHSIZE_OPTION:
- /* -H was supported in 1.4.x, but is a no-op now. FIXME -
- remove support for -H after 2.0. */
+ /* -H was supported in 1.4.x, but is a no-op now. FIXME -
+ remove support for -H after 2.0. */
error (0, 0, _("Warning: `%s' is deprecated"),
optchar == 'H' ? "-H" : "--hashsize");
break;
@@ -370,7 +388,8 @@ main (int argc, char *const *argv, char *const *envp)
break;
case 'L':
- m4_set_nesting_limit_opt (context, atoi (optarg));
+ size = size_opt (optarg, N_("nesting limit"));
+ m4_set_nesting_limit_opt (context, size);
break;
case 'M':
@@ -424,15 +443,15 @@ main (int argc, char *const *argv, char *const *envp)
case 'e':
error (0, 0, _("Warning: `%s' is deprecated, use `%s' instead"),
"-e", "-i");
- /* fall through */
+ /* fall through */
case 'i':
interactive = INTERACTIVE_YES;
break;
case 'l':
- m4_set_max_debug_arg_length_opt (context, atoi (optarg));
- if (m4_get_max_debug_arg_length_opt (context) <= 0)
- m4_set_max_debug_arg_length_opt (context, 0);
+ size = size_opt (optarg,
+ N_("debug argument length"));
+ m4_set_max_debug_arg_length_opt (context, size);
break;
case 'o':
diff --git a/tests/options.at b/tests/options.at
index 3820e44b..adbf3deb 100644
--- a/tests/options.at
+++ b/tests/options.at
@@ -133,6 +133,53 @@ m@&t@4_dnl()
AT_CLEANUP
+## --------- ##
+## arglength ##
+## --------- ##
+
+AT_SETUP([--arglength])
+
+dnl Check for argument validation.
+
+AT_DATA([in],
+[[define(`echo', `$@')dnl
+traceon(`echo')dnl
+echo(`long string')
+]])
+
+AT_CHECK_M4([--arglength=-1 in], [1], [],
+[[m4: invalid debug argument length `-1'
+]])
+
+AT_CHECK_M4([--arglength oops in], [1], [],
+[[m4: invalid debug argument length `oops'
+]])
+
+AT_CHECK_M4([-l 10oops in], [1], [],
+[[m4: invalid character following debug argument length in `10oops'
+]])
+
+dnl MiB is the suffix to implict 1, resulting in 1048576
+AT_CHECK_M4([-lMiB in], [0], [[long string
+]], [[m4trace: -1- echo(`long string') -> ``long string''
+]])
+
+dnl this assumes size_t is no bigger than 64 bits
+AT_CHECK_M4([-l 123456789012345678901234567890 in], [1], [],
+[[m4: debug argument length `123456789012345678901234567890' too large
+]])
+
+AT_CHECK_M4([-l 3 in], [0], [[long string
+]], [[m4trace: -1- echo(`lon...') -> ``lo...'
+]])
+
+AT_CHECK_M4([--arglength=3 -l0 in], [0], [[long string
+]], [[m4trace: -1- echo(`long string') -> ``long string''
+]])
+
+AT_CLEANUP
+
+
## ----------- ##
## debug flags ##
## ----------- ##
@@ -294,6 +341,59 @@ OVERRIDE=It is changed.
AT_CLEANUP
+## ------------- ##
+## nesting-limit ##
+## ------------- ##
+
+AT_SETUP([--nesting-limit])
+
+dnl Check for argument validation.
+
+AT_DATA([in],
+[[define(`echo', `$@')dnl
+echo(echo(echo(echo(`nested string'))))
+echo(echo(echo(echo(echo(echo(echo(echo(echo(`nested string')))))))))
+]])
+
+AT_CHECK_M4([--nesting-limit=-1 in], [1], [],
+[[m4: invalid nesting limit `-1'
+]])
+
+AT_CHECK_M4([--nesting-limit oops in], [1], [],
+[[m4: invalid nesting limit `oops'
+]])
+
+AT_CHECK_M4([-L 10oops in], [1], [],
+[[m4: invalid character following nesting limit in `10oops'
+]])
+
+dnl MiB is the suffix to implict 1, resulting in 1048576
+AT_CHECK_M4([-LMiB in], [0], [[nested string
+nested string
+]])
+
+dnl this assumes size_t is no bigger than 64 bits
+AT_CHECK_M4([-L 123456789012345678901234567890 in], [1], [],
+[[m4: nesting limit `123456789012345678901234567890' too large
+]])
+
+AT_CHECK_M4([-L 5 in], [1], [[nested string
+]],
+[[m4:in:3: recursion limit of 5 exceeded, use -L<N> to change it
+]])
+
+dnl per POSIX guidelines, this is a decimal number 10, not octal 8
+AT_CHECK_M4([-L 010 in], [0], [[nested string
+nested string
+]])
+
+AT_CHECK_M4([--nesting-limit=3 -L0 in], [0], [[nested string
+nested string
+]])
+
+AT_CLEANUP
+
+
## --------------- ##
## prepend-include ##
## --------------- ##
@@ -338,6 +438,37 @@ in post/blah
AT_CLEANUP
+## ------------- ##
+## regexp-syntax ##
+## ------------- ##
+
+AT_SETUP([--regexp-syntax])
+
+dnl test argument validation
+
+AT_DATA([[in]], [[regexp(`(', `(')
+]])
+
+AT_CHECK_M4([--regexp-syntax=unknown in], [1], [],
+[[m4: bad regexp syntax option: `unknown'
+]])
+
+AT_CHECK_M4([--regexp-syntax '' in], [0], [[0
+]])
+
+AT_CHECK_M4([-r EXTENDED in], [1], [[
+]], [[m4:in:1: regexp: bad regular expression `(': Unmatched ( or \(
+]])
+
+AT_CHECK_M4([-rgnu-m4 in], [0], [[0
+]])
+
+AT_CHECK_M4([-r "gnu M4" in], [0], [[0
+]])
+
+AT_CLEANUP
+
+
## ----- ##
## safer ##
## ----- ##