diff options
author | Eric Blake <ebb9@byu.net> | 2007-12-06 14:47:26 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-05-03 13:31:39 -0600 |
commit | 8a47a2029b7eb60ac61abb1b6423d4a67b371281 (patch) | |
tree | 9d8e69fd47147572834e81b099e376ff4aeade83 | |
parent | 32d4bf4d447e0e252c809897b795b57b3bfd74de (diff) | |
download | m4-8a47a2029b7eb60ac61abb1b6423d4a67b371281.tar.gz |
Stage22: allow builtin concatenation outside $@
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | NEWS | 10 | ||||
-rw-r--r-- | checks/Makefile.in | 10 | ||||
-rwxr-xr-x | checks/check-them | 41 | ||||
-rw-r--r-- | doc/Makefile.am | 14 | ||||
-rw-r--r-- | doc/m4.texinfo | 172 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/builtin.c | 34 | ||||
-rw-r--r-- | src/debug.c | 36 | ||||
-rw-r--r-- | src/format.c | 22 | ||||
-rw-r--r-- | src/input.c | 106 | ||||
-rw-r--r-- | src/m4.c | 9 | ||||
-rw-r--r-- | src/m4.h | 18 | ||||
-rw-r--r-- | src/macro.c | 242 | ||||
-rw-r--r-- | src/output.c | 42 |
15 files changed, 423 insertions, 341 deletions
diff --git a/Makefile.am b/Makefile.am index a67b4159..7d123e55 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,7 +32,9 @@ MAINTAINERCLEANFILES = COPYING INSTALL Makefile.in aclocal.m4 \ m4/gnulib-tool.m4 missing stamp-h.in ACLOCAL_AMFLAGS = -I m4 -DISTCHECK_CONFIGURE_FLAGS = --enable-changeword +## Enough users install GNU M4 as gm4 that we make sure 'make installcheck' +## will handle that, prior to making a release. +DISTCHECK_CONFIGURE_FLAGS = --enable-changeword --program-prefix=g BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: @@ -12,9 +12,19 @@ Foundation, Inc. required by POSIX. The manual mentions a way to restore the LIFO order present in earlier GNU M4 versions. +** The `defn' builtin now warns when operating on an undefined macro name. + To simulate 1.4.x behavior, use: + pushdef(`defn', `ifdef(`$1', `builtin(`defn', `$1')')') + ** Enhance the `ifdef', `ifelse', and `shift' builtins, as well as all user macros, to transparently handle builtin tokens generated by `defn'. +** Allow the concatenation of builtin macros with arbitrary text in + several contexts, via the `defn' builtin or argument expansion, rather + than warning and converting the builtin token to an empty string. + However, it is still not possible to use a concatenated builtin when + defining a macro. + ** Enhance the `defn', `dumpdef', `ifdef', `popdef', `traceon', `traceoff', and `undefine' macros to warn when encountering a builtin token in the context of a macro name, rather than acting on the empty string. This diff --git a/checks/Makefile.in b/checks/Makefile.in index 6d93144e..0e1091e5 100644 --- a/checks/Makefile.in +++ b/checks/Makefile.in @@ -1,6 +1,7 @@ ## Makefile.in - template for building Makefile for M4 testsuite. ## -## Copyright (C) 1992, 1993, 1994, 2006, 2007 Free Software Foundation, Inc. +## Copyright (C) 1992, 1993, 1994, 2006, 2007, 2008 Free Software +## Foundation, Inc. ## ## This file is part of GNU M4. ## @@ -17,10 +18,11 @@ ## You should have received a copy of the GNU General Public License ## along with this program. If not, see <http://www.gnu.org/licenses/>. +@SET_MAKE@ PACKAGE = @PACKAGE@ VERSION = @VERSION@ -SHELL = /bin/sh +SHELL = @SHELL@ bindir = @bindir@ exec_prefix = @exec_prefix@ @@ -28,6 +30,7 @@ prefix = @prefix@ srcdir = @srcdir@ VPATH = @srcdir@ PATH_SEPARATOR = @PATH_SEPARATOR@ +program_transform_name = @program_transform_name@ # Should be GNU awk, for the get-them script. AWK = @AWK@ @@ -57,7 +60,8 @@ check: $(srcdir)/stamp-checks installcheck: $(srcdir)/stamp-checks PATH='$(bindir)'"$(PATH_SEPARATOR)"$$PATH; export PATH; \ - $(srcdir)/check-them -I $(srcdir)/../examples $(CHECKS) + $(srcdir)/check-them -I $(srcdir)/../examples \ + -m "`echo m4 | sed '$(program_transform_name)'`" $(CHECKS) tags: diff --git a/checks/check-them b/checks/check-them index 9fca39b5..7fba1d6f 100755 --- a/checks/check-them +++ b/checks/check-them @@ -2,9 +2,6 @@ # Check GNU m4 against examples from the manual source. # Copyright (C) 1992, 2006, 2007, 2008 Free Software Foundation, Inc. -# Sanity check what we are testing -m4 --version - # Clean up temp files on exit pwd=`pwd` tmp=m4-tmp.$$ @@ -29,13 +26,27 @@ skipped= strip_needed=false diffopts=-c +# Find out where the examples live. +examples=. +if test "x$1" = x-I ; then + examples="$2" + shift; shift +fi + +# Find out how to run m4. +m4=m4 +if test "x$1" = x-m ; then + m4="$2" + shift; shift +fi + # Find out how the executable prints argv[0] -m4=`m4 --help | sed -e 's/Usage: \(.*\) \[OPTION.*/\1/' \ - -e 's/\\\\/\\\\\\\\/g' -e 1q` +m4name=`"$m4" --help | sed -e 's/Usage: \(.*\) \[OPTION.*/\1/' \ + -e 's/\\\\/\\\\\\\\/g' -e 1q` # Find out if we should strip \r in the output -m4 --version > $out -m4 --version | tr -d '\015' > $xout +"$m4" --version | tee $out +"$m4" --version | tr -d '\015' > $xout if cmp -s $out $xout; then : else @@ -43,13 +54,6 @@ else strip_needed=: fi -# Find out where the examples live. -examples=. -if test "x$1" = x-I ; then - examples="$2" - shift; shift -fi - # Find out if diff supports useful options. if diff -u /dev/null /dev/null 2>/dev/null ; then diffopts="-u" @@ -68,7 +72,7 @@ do echo "Checking $file" options=`sed -ne '3s/^dnl @ extra options: //p;3q' "$file"` sed -e '/^dnl @/d' -e '/^\^D$/q' "$file" \ - | LC_MESSAGES=C M4PATH=$examples m4 -d $options - >$out 2>$err + | LC_MESSAGES=C M4PATH=$examples "$m4" -d $options - >$out 2>$err stat=$? xstat=`sed -ne '2s/^dnl @ expected status: //p;2q' "$file"` @@ -88,7 +92,7 @@ do xoutfile=`sed -n 's/^dnl @ expected output: //p' "$file"` if test -z "$xoutfile" ; then - sed -e '/^dnl @result{}/!d' -e 's///' -e "s|\.\./examples|$examples|" \ + sed -e '/^dnl @result{}/!d' -e 's///' -e "s|examples/|$examples/|" \ "$file" > $xout else cp "$examples/$xoutfile" $xout @@ -96,10 +100,11 @@ do xerrfile=`sed -n 's/^dnl @ expected error: //p' "$file"` if test -z "$xerrfile" ; then - sed '/^dnl @error{}/!d; s///; '"s|^m4:|$m4:|; s|\.\./examples|$examples|" \ + sed '/^dnl @error{}/!d + s///; '"s|^m4:|$m4name:|; s|examples/|$examples/|" \ "$file" > $xerr else - sed "s|^m4:|$m4:|; s|\.\./examples|$examples|" \ + sed "s|^m4:|$m4name:|; s|examples/|$examples/|" \ "$examples/$xerrfile" > $xerr fi diff --git a/doc/Makefile.am b/doc/Makefile.am index ded347f6..b1b7eac8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am - template for generating Makefile via Automake ## -## Copyright (C) 2006, 2007 Free Software Foundation, Inc. +## Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. ## ## This file is part of GNU M4. ## @@ -21,22 +21,22 @@ info_TEXINFOS = m4.texinfo m4_TEXINFOS = fdl.texi gpl-3.0.texi -man_MANS = m4.1 +man_MANS = $(srcdir)/m4.1 EXTRA_DIST = $(man_MANS) gendocs_template MAINTAINERCLEANFILES = $(man_MANS) gendocs_template SUFFIXES = .1 +HELP2MAN = $(SHELL) $(top_srcdir)/build-aux/missing --run help2man -# Depend on configure.ac for version, m4.c for usage text. Do not depend on +# Depend on ../.version for version, m4.c for usage text. Do not depend on # built m4 executable, since not everyone has help2man or perl. # Build the man page once in the srcdir, rather than in every VPATH build # dir, to match how automake builds info pages. This is safe for 'make # distcheck' since it is distributed pre-built. -$(srcdir)/m4.1: $(top_srcdir)/configure.ac $(top_srcdir)/src/m4.c +$(srcdir)/m4.1: $(top_srcdir)/.version $(top_srcdir)/src/m4.c @if test -x ../src/m4$(EXEEXT) ; then \ echo "Updating man page m4.1" ; \ - $(SHELL) $(top_srcdir)/build-aux/missing --run \ - help2man --name="macro processor" --source=FSF \ - --info-page=m4 --output=$@ ../src/m4$(EXEEXT) ; \ + $(HELP2MAN) --name="macro processor" --source='$(PACKAGE_STRING)' \ + --info-page=m4 --output=$@ ../src/m4$(EXEEXT) ; \ else \ echo "WARNING: The \`man' page \`$@' cannot be updated yet."; \ echo " Retry once the program executable is ready."; \ diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 98eba85d..8d83fb0f 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -2111,17 +2111,14 @@ the builtin @code{defn}: @deffn Builtin defn (@var{name}@dots{}) Expands to the @emph{quoted definition} of each @var{name}. If an argument is not a defined macro, the expansion for that argument is -empty. +empty and triggers a warning. If @var{name} is a user-defined macro, the quoted definition is simply -the quoted expansion text. If, instead, there is only one @var{name} -and it is a builtin, the +the quoted expansion text. If, instead, @var{name} is a builtin, the expansion is a special token, which points to the builtin's internal -definition. This token is only meaningful as the second argument to +definition. This token meaningful primarily as the second argument to @code{define} (and @code{pushdef}), and is silently converted to an -empty string in most other contexts. Using multiple @var{name} to -combine a builtin with anything else is not supported; a warning is -issued and the builtin is omitted from the final expansion. +empty string in many other contexts. The macro @code{defn} is recognized only with parameters. @end deffn @@ -2280,14 +2277,18 @@ bar @result{}0 @end example -Also note that as of M4 1.6, @code{defn} with multiple arguments can -join text with builtin tokens. However, when collecting macro -arguments, a builtin token is preserved only when it occurs in -isolation. A future version of @acronym{GNU} M4 may lift this -restriction. +A warning is issued if @var{name} is undefined. Also note that as of M4 +1.6, @code{defn} with multiple arguments can join text with builtin +tokens. However, when defining a macro via @code{define} or +@code{pushdef}, a warning is issued and the builtin token ignored if the +builtin token does not occur in isolation. A future version of +@acronym{GNU} M4 may lift this restriction. @example $ @kbd{m4 -d} +defn(`foo') +@error{}m4:stdin:1: Warning: defn: undefined macro `foo' +@result{} define(`a', `A')define(`AA', `b') @result{} traceon(`defn', `define') @@ -2297,29 +2298,28 @@ defn(`a', `divnum', `a') @result{}AA define(`mydivnum', defn(`divnum', `divnum'))mydivnum @error{}m4trace: -2- defn(`divnum', `divnum') -> `<divnum><divnum>' -@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum' -@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum' -@error{}m4trace: -1- define(`mydivnum', `') +@error{}m4:stdin:5: Warning: define: cannot concatenate builtins +@error{}m4trace: -1- define(`mydivnum', `<divnum><divnum>') @result{} -traceoff(`defn', `define') +traceoff(`defn', `define')dumpdef(`mydivnum') +@error{}mydivnum:@tabchar{}`' @result{} define(`mydivnum', defn(`divnum')defn(`divnum'))mydivnum -@error{}m4:stdin:6: Warning: define: cannot concatenate builtin `divnum' -@error{}m4:stdin:6: Warning: define: cannot concatenate builtin `divnum' +@error{}m4:stdin:7: Warning: define: cannot concatenate builtins @result{} define(`mydivnum', defn(`divnum')`a')mydivnum -@error{}m4:stdin:7: Warning: define: cannot concatenate builtin `divnum' +@error{}m4:stdin:8: Warning: define: cannot concatenate builtins @result{}A define(`mydivnum', `a'defn(`divnum'))mydivnum -@error{}m4:stdin:8: Warning: define: cannot concatenate builtin `divnum' +@error{}m4:stdin:9: Warning: define: cannot concatenate builtins @result{}A define(`q', ``$@@'') @result{} define(`foo', q(`a', defn(`divnum')))foo -@error{}m4:stdin:10: Warning: define: cannot concatenate builtins -@result{}foo +@error{}m4:stdin:11: Warning: define: cannot concatenate builtins +@result{}a, ifdef(`foo', `yes', `no') -@result{}no +@result{}yes @end example @node Pushdef @@ -2760,6 +2760,63 @@ foo(`a', `b', `c') @result{}arguments:3 @end example +Since m4 is a macro language, it is even possible to write a macro that +makes defining blind macros easier: + +@deffn Composite define_blind (@var{name}, @ovar{value}) +Defines @var{name} as a blind macro, such that @var{name} will expand to +@var{value} only when given explicit arguments. @var{value} should not +be the result of @code{defn} (@pxref{Defn}). This macro is only +recognized with parameters, and results in an empty string. +@end deffn + +Defining a macro to define another macro can be a bit tricky. We want +to use a literal @samp{$#} in the argument to the nested @code{define}. +However, if @samp{$} and @samp{#} are adjacent in the definition of +@code{define_blind}, then it would be expanded as the number of +arguments to @code{define_blind} rather than the intended number of +arguments to @var{name}. The solution is to pass the difficult +characters through extra arguments to a helper macro +@code{_define_blind}. + +As for the limitation against using @code{defn}, there are two reasons. +If a macro was previously defined with @code{define_blind}, then it can +safely be renamed to a new blind macro using plain @code{define}; using +@code{define_blind} to rename it just adds another layer of +@code{ifelse}, occupying memory and slowing down execution. And if a +macro is a builtin, then it would result in an attempt to define a macro +consisting of both text and a builtin token; this is not supported, and +the builtin token is flattened to an empty string. + +With that explanation, here's the definition, and some sample usage. +Notice that @code{define_blind} is itself a blind macro. + +@example +$ @kbd{m4 -d} +define(`define_blind', `ifelse(`$#', `0', ``$0'', +`_$0(`$1', `$2', `$'`#', `$'`0')')') +@result{} +define(`_define_blind', `define(`$1', +`ifelse(`$3', `0', ``$4'', `$2')')') +@result{} +define_blind +@result{}define_blind +define_blind(`foo', `arguments were $*') +@result{} +foo +@result{}foo +foo(`bar') +@result{}arguments were bar +define(`blah', defn(`foo')) +@result{} +blah +@result{}blah +blah(`a', `b') +@result{}arguments were a,b +defn(`blah') +@result{}ifelse(`$#', `0', ``$0'', `arguments were $*') +@end example + @cindex multibranches @cindex switch statement @cindex case statement @@ -2795,6 +2852,8 @@ ifelse(defn(`defn'), defn(`divnum'), `yes', `no') @result{}no ifelse(defn(`defn'), defn(`defn'), `yes', `no') @result{}yes +ifelse(defn(`defn', `divnum'), defn(`defn')defn(`divnum'), `yes', `no') +@result{}yes define(`foo', ifelse(`', `', defn(`divnum'))) @result{} foo @@ -2859,8 +2918,6 @@ ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no') @result{}no @end example -@comment It would be nice to allow concatenation of builtins without -@comment using $@ handling. @example define(`e', `$@@')define(`q', ``$@@'')define(`u', `$*') @result{} @@ -2870,16 +2927,12 @@ cmp(`defn(`defn')', `defn(`d')') @result{}yes cmp(`defn(`defn')', ``<defn>'') @result{}no -cmp(`q(defn(`defn'))', `q(defn(`d'))')-fixme -@error{}m4:stdin:5: Warning: ifelse: cannot quote builtin -@error{}m4:stdin:5: Warning: ifelse: cannot quote builtin -@result{}yes-fixme -cmp(`q(defn(`defn'))', `q(`<defn>')')-fixme -@error{}m4:stdin:6: Warning: ifelse: cannot quote builtin -@result{}no-fixme -cmp(`q(defn(`defn'))', ``'')-fixme -@error{}m4:stdin:7: Warning: ifelse: cannot quote builtin -@result{}no-fixme +cmp(`q(defn(`defn'))', `q(defn(`d'))') +@result{}yes +cmp(`q(defn(`defn'))', `q(`<defn>')') +@result{}no +cmp(`q(defn(`defn'))', ``'') +@result{}no cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))') @result{}yes cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')') @@ -3772,7 +3825,17 @@ debugmode() foo @error{}m4trace: -1- foo -> `FOO' @result{}FOO +debugmode(`V') +@result{} +foo(`ignored') +@error{}m4trace:stdin:6: -1- id 6: foo ... +@error{}m4trace:stdin:6: -1- id 6: foo(`ignored') -> ??? +@error{}m4trace:stdin:6: -1- id 6: foo(...) -> `FOO' +@result{}FOO debugmode +@error{}m4trace:stdin:7: -1- id 7: debugmode ... +@error{}m4trace:stdin:7: -1- id 7: debugmode -> ??? +@error{} @result{} foo @error{}m4trace: -1- foo @@ -3780,7 +3843,7 @@ foo debugmode(`+l') @result{} foo -@error{}m4trace:8: -1- foo +@error{}m4trace:10: -1- foo @result{}FOO @end example @@ -3803,6 +3866,23 @@ indir(`echo', defn(`changequote')) @result{} @end example +This example shows the effects of the debug flags that are not related +to macro tracing. + +@comment examples +@comment options: -dip +@example +$ @kbd{m4 -dip -I examples} +@error{}m4debug: input read from stdin +include(`foo')dnl +@error{}m4debug: path search for `foo' found `examples/foo' +@error{}m4debug: input read from examples/foo +@result{}bar +@error{}m4debug: input reverted to stdin, line 1 +^D +@error{}m4debug: input exhausted +@end example + @node Debug Output @section Saving debugging output @@ -4470,7 +4550,7 @@ define(`bar ', defn(`__file__')) @result{} include(`foo') -@result{}../examples/foo +@result{}examples/foo define(`bar ', defn(`__line__')) @result{} @@ -4720,7 +4800,7 @@ than computing the builtin token up front, as is done for @code{bar}. m4wrap(`define(`foo', defn(`divnum'))foo ') @result{} -m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar +m4wrap(`define(`bar', 'defn(`divnum')`)bar ') @result{} ^D @@ -4955,7 +5035,7 @@ ifdef(`__unix__', , ')m4exit(`77')')dnl changequote(`[', `]')dnl syscmd([echo 'divert(1)hi -format(%1000000d, 1)' | m4 | sed 1q])dnl +format(%1000000d, 1)' | ]__program__[ | sed -n 1p])dnl @result{}hi sysval @result{}0 @@ -5718,6 +5798,8 @@ ifelse(format(`%.1A', `1.999'), `0X1.0P+1', `success', format(`%.1A', `1.999'), `0X2.0P+0', `success', format(`%.1A', `1.999')) @result{}success +format(`%g', `0xa.P+1') +@result{}20 @end example Using the @code{forloop} macro defined earlier (@pxref{Forloop}), this @@ -6483,11 +6565,13 @@ sysval @c the expansion. @example -syscmd(`rm -f foo??????')sysval +syscmd(`rm -rf foodir')sysval @result{}0 -len(mkstemp(`fooXXXXX')) -@result{}9 -syscmd(`rm foo??????')sysval +syscmd(`mkdir foodir')sysval +@result{}0 +len(mkstemp(`foodir/fooXXXXX')) +@result{}16 +syscmd(`rm -r foodir')sysval @result{}0 @end example @@ -6606,7 +6690,7 @@ foo @result{}foo called at stdin:2 include(`incl.m4') @result{}Include file start -@result{}foo called at ../examples/incl.m4:2 +@result{}foo called at examples/incl.m4:2 @result{}Include file end @result{} @end example diff --git a/src/Makefile.am b/src/Makefile.am index 4d061c71..f2c15244 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am - template for generating Makefile via Automake ## -## Copyright (C) 2006, 2007 Free Software Foundation, Inc. +## Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. ## ## This file is part of GNU M4. ## @@ -27,4 +27,4 @@ macro.c output.c path.c symtab.c if STACKOVF m4_SOURCES += stackovf.c endif -m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) +m4_LDADD = ../lib/libm4.a $(LIBM4_LIBDEPS) $(POW_LIB) diff --git a/src/builtin.c b/src/builtin.c index 497e2b91..142f0dc6 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -675,12 +675,9 @@ define_macro (int argc, macro_arguments *argv, symbol_lookup mode) { case TOKEN_COMP: m4_warn (0, me, _("cannot concatenate builtins")); - // TODO fall through instead - break; - + /* fallthru */ case TOKEN_TEXT: - // TODO flatten TOKEN_COMP value, or support concatenation of builtins - define_user_macro (ARG (1), ARG_LEN (1), ARG (2), mode); + define_user_macro (ARG (1), ARG_LEN (1), arg_text (argv, 2, true), mode); break; case TOKEN_FUNC: @@ -763,7 +760,7 @@ static void m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) { const char *me = ARG (0); - int index; + int i; if (argc == 2 || bad_argc (me, argc, 3, -1)) return; @@ -771,14 +768,14 @@ m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) /* Diagnose excess arguments if 5, 8, 11, etc., actual arguments. */ bad_argc (me, argc, 0, argc - 2); - index = 1; + i = 1; argc--; while (true) { - if (arg_equal (argv, index, index + 1)) + if (arg_equal (argv, i, i + 1)) { - push_arg (obs, argv, index + 2); + push_arg (obs, argv, i + 2); return; } switch (argc) @@ -788,12 +785,12 @@ m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) case 4: case 5: - push_arg (obs, argv, index + 3); + push_arg (obs, argv, i + 3); return; default: argc -= 3; - index += 3; + i += 3; } } } @@ -1008,7 +1005,10 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv) } s = lookup_symbol (ARG (i), SYMBOL_LOOKUP); if (s == NULL) - continue; + { + m4_warn (0, me, _("undefined macro `%s'"), ARG (i)); + continue; + } switch (SYMBOL_TYPE (s)) { @@ -1042,10 +1042,10 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv) /* Helper macros for readability. */ #if UNIX || defined WEXITSTATUS -# define M4SYSVAL_EXITBITS(status) \ - (WIFEXITED (status) ? WEXITSTATUS (status) : 0) -# define M4SYSVAL_TERMSIGBITS(status) \ - (WIFSIGNALED (status) ? WTERMSIG (status) << 8 : 0) +# define M4SYSVAL_EXITBITS(status) \ + (WIFEXITED (status) ? WEXITSTATUS (status) : 0) +# define M4SYSVAL_TERMSIGBITS(status) \ + (WIFSIGNALED (status) ? WTERMSIG (status) << 8 : 0) #else /* !UNIX && !defined WEXITSTATUS */ /* Platforms such as mingw do not support the notion of reporting @@ -1976,7 +1976,7 @@ m4_format (struct obstack *obs, int argc, macro_arguments *argv) { if (bad_argc (ARG (0), argc, 1, -1)) return; - format (obs, argc, argv); + expand_format (obs, argc, argv); } /*-------------------------------------------------------------------------. diff --git a/src/debug.c b/src/debug.c index 4a4e915d..fde6c499 100644 --- a/src/debug.c +++ b/src/debug.c @@ -248,7 +248,7 @@ trace_format (const char *fmt, ...) char ch; int d; const char *s; - int maxlen; + size_t maxlen; va_start (args, fmt); @@ -260,7 +260,7 @@ trace_format (const char *fmt, ...) if (ch == '\0') break; - maxlen = INT_MAX; + maxlen = SIZE_MAX; switch (*fmt++) { case 'B': @@ -294,7 +294,7 @@ trace_format (const char *fmt, ...) break; } - if (obstack_print (&trace, s, SIZE_MAX, &maxlen)) + if (shipout_string_trunc (&trace, s, SIZE_MAX, &maxlen)) break; } @@ -364,7 +364,7 @@ trace_pre (const char *name, int id, macro_arguments *argv) if (arg_argc (argv) > 1 && (debug_level & DEBUG_TRACE_ARGS)) { - int len = max_debug_argument_length; + size_t len = max_debug_argument_length; trace_format ("("); arg_print (&trace, argv, 1, (debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL, @@ -401,31 +401,3 @@ trace_post (const char *name, int id, macro_arguments *argv, trace_format (" -> %l%B%r", expanded); trace_flush (); } - -/* Dump the string STR of length LEN to the obstack OBS. If LEN is - SIZE_MAX, use strlen (STR) instead. If MAX_LEN is non-NULL, - truncate the dump at MAX_LEN bytes and return true if MAX_LEN was - reached; otherwise, return false and update MAX_LEN as - appropriate. */ -bool -obstack_print (struct obstack *obs, const char *str, size_t len, int *max_len) -{ - int max = max_len ? *max_len : INT_MAX; - - if (len == SIZE_MAX) - len = strlen (str); - if (len < max) - { - obstack_grow (obs, str, len); - max -= len; - } - else - { - obstack_grow (obs, str, max); - obstack_grow (obs, "...", 3); - max = 0; - } - if (max_len) - *max_len = max; - return max == 0; -} diff --git a/src/format.c b/src/format.c index 6808ad5d..717a0702 100644 --- a/src/format.c +++ b/src/format.c @@ -122,7 +122,7 @@ arg_double (const char *me, const char *str) `------------------------------------------------------------------*/ void -format (struct obstack *obs, int argc, macro_arguments *argv) +expand_format (struct obstack *obs, int argc, macro_arguments *argv) { const char *me = ARG (0); /* Macro name. */ const char *f; /* Format control string. */ @@ -130,7 +130,7 @@ format (struct obstack *obs, int argc, macro_arguments *argv) char fstart[] = "%'+- 0#*.*hhd"; /* Current format spec. */ char *p; /* Position within fstart. */ unsigned char c; /* A simple character. */ - int index = 0; /* Index within argc used so far. */ + int i = 0; /* Index within argc used so far. */ bool valid_format = true; /* True if entire format string ok. */ /* Flags. */ @@ -161,7 +161,7 @@ format (struct obstack *obs, int argc, macro_arguments *argv) char *str; /* Malloc'd buffer of formatted text. */ enum {CHAR, INT, LONG, DOUBLE, STR} datatype; - f = fmt = ARG_STR (index, argc, argv); + f = fmt = ARG_STR (i, argc, argv); memset (ok, 0, sizeof ok); while (true) { @@ -170,7 +170,7 @@ format (struct obstack *obs, int argc, macro_arguments *argv) if (c == '\0') { if (valid_format) - bad_argc (me, argc, index, index); + bad_argc (me, argc, i, i); return; } obstack_1grow (obs, c); @@ -250,7 +250,7 @@ format (struct obstack *obs, int argc, macro_arguments *argv) *p++ = '*'; if (*fmt == '*') { - width = ARG_INT (index, argc, argv); + width = ARG_INT (i, argc, argv); fmt++; } else @@ -270,7 +270,7 @@ format (struct obstack *obs, int argc, macro_arguments *argv) ok['c'] = 0; if (*(++fmt) == '*') { - prec = ARG_INT (index, argc, argv); + prec = ARG_INT (i, argc, argv); ++fmt; } else @@ -359,27 +359,27 @@ format (struct obstack *obs, int argc, macro_arguments *argv) { case CHAR: str = asnprintf (base, &len, fstart, width, - ARG_INT (index, argc, argv)); + ARG_INT (i, argc, argv)); break; case INT: str = asnprintf (base, &len, fstart, width, prec, - ARG_INT (index, argc, argv)); + ARG_INT (i, argc, argv)); break; case LONG: str = asnprintf (base, &len, fstart, width, prec, - ARG_LONG (index, argc, argv)); + ARG_LONG (i, argc, argv)); break; case DOUBLE: str = asnprintf (base, &len, fstart, width, prec, - ARG_DOUBLE (index, argc, argv)); + ARG_DOUBLE (i, argc, argv)); break; case STR: str = asnprintf (base, &len, fstart, width, prec, - ARG_STR (index, argc, argv)); + ARG_STR (i, argc, argv)); break; default: diff --git a/src/input.c b/src/input.c index cdb7d034..f5c2deeb 100644 --- a/src/input.c +++ b/src/input.c @@ -235,14 +235,14 @@ make_text_link (struct obstack *obs, token_chain **start, token_chain **end) /*-------------------------------------------------------------------. | push_file () pushes an input file on the input stack, saving the | | current file name and line number. If next is non-NULL, this push | -| invalidates a call to push_string_init (), whose storage is | -| consequently released. If CLOSE, then close FP after EOF is | -| detected. TITLE is used as the location for text parsed from the | -| file (not necessarily the file name). | +| invalidates a call to push_string_init (), whose storage is | +| consequently released. If CLOSE_WHEN_DONE, then close FP after | +| EOF is detected. TITLE is used as the location for text parsed | +| from the file (not necessarily the file name). | `-------------------------------------------------------------------*/ void -push_file (FILE *fp, const char *title, bool close) +push_file (FILE *fp, const char *title, bool close_when_done) { input_block *i; @@ -263,7 +263,7 @@ push_file (FILE *fp, const char *title, bool close) i->u.u_f.fp = fp; i->u.u_f.end = false; - i->u.u_f.close = close; + i->u.u_f.close = close_when_done; i->u.u_f.advance = start_of_input_line; output_current_line = -1; @@ -343,18 +343,18 @@ push_string_init (void) | rather than copying everything consecutively onto the input stack. | | Must be called between push_string_init and push_string_finish. | | | -| If TOKEN contains text, then convert the current input block into | -| a chain if it is not one already, and add the contents of TOKEN as | -| a new link in the chain. LEVEL describes the current expansion | -| level, or -1 if TOKEN is composite, its contents reside entirely | -| on the current_input stack, and TOKEN lives in temporary storage. | -| If TOKEN is a simple string, then it belongs to the current macro | -| expansion. If TOKEN is composite, then each text link has a level | -| of -1 if it belongs to the current macro expansion, otherwise it | -| is a back-reference where level tracks which stack it came from. | -| The resulting input block chain contains links with a level of -1 | -| if the text belongs to the input stack, otherwise the level where | -| the back-reference comes from. | +| Convert the current input block into a chain if it is not one | +| already, and add the contents of TOKEN as a new link in the chain. | +| LEVEL describes the current expansion level, or -1 if TOKEN is | +| composite, its contents reside entirely on the current_input | +| stack, and TOKEN lives in temporary storage. If TOKEN is a simple | +| string, then it belongs to the current macro expansion. If TOKEN | +| is composite, then each text link has a level of -1 if it belongs | +| to the current macro expansion, otherwise it is a back-reference | +| where level tracks which stack it came from. The resulting input | +| block chain contains links with a level of -1 if the text belongs | +| to the input stack, otherwise the level where the back-reference | +| comes from. | | | | Return true only if a reference was created to the contents of | | TOKEN, in which case, LEVEL was non-negative and the lifetime of | @@ -660,7 +660,7 @@ pop_input (bool cleanup) return false; if (debug_level & DEBUG_TRACE_INPUT) { - if (tmp) + if (tmp != &input_eof) DEBUG_MESSAGE2 ("input reverted to %s, line %d", tmp->file, tmp->line); else @@ -737,14 +737,14 @@ pop_wrapup (void) void input_print (struct obstack *obs, const input_block *input) { - int maxlen = max_debug_argument_length; + size_t maxlen = max_debug_argument_length; token_chain *chain; assert (input); switch (input->type) { case INPUT_STRING: - obstack_print (obs, input->u.u_s.str, input->u.u_s.len, &maxlen); + shipout_string_trunc (obs, input->u.u_s.str, input->u.u_s.len, &maxlen); break; case INPUT_FILE: obstack_grow (obs, "<file: ", strlen ("<file: ")); @@ -758,8 +758,8 @@ input_print (struct obstack *obs, const input_block *input) switch (chain->type) { case CHAIN_STR: - if (obstack_print (obs, chain->u.u_s.str, chain->u.u_s.len, - &maxlen)) + if (shipout_string_trunc (obs, chain->u.u_s.str, + chain->u.u_s.len, &maxlen)) return; break; case CHAIN_FUNC: @@ -1062,19 +1062,35 @@ skip_line (const char *name) | When next_token() sees a builtin token with peek_input, this | | retrieves the value of the function pointer, stores it in TD, and | | consumes the input so the caller does not need to do next_char. | -| If TD is NULL, discard the token instead. | +| If OBS, TD will be converted to a composite token using storage | +| from OBS as necessary; otherwise, if TD is NULL, the builtin is | +| discarded. | `------------------------------------------------------------------*/ static void -init_macro_token (token_data *td) +init_macro_token (struct obstack *obs, token_data *td) { token_chain *chain; assert (isp->type == INPUT_CHAIN); chain = isp->u.u_c.chain; assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func); - if (td) + if (obs) { + assert (td); + if (TOKEN_DATA_TYPE (td) == TOKEN_VOID) + { + TOKEN_DATA_TYPE (td) = TOKEN_COMP; + td->u.u_c.chain = td->u.u_c.end = NULL; + td->u.u_c.wrapper = false; + td->u.u_c.has_func = true; + } + assert (TOKEN_DATA_TYPE (td) == TOKEN_COMP); + append_macro (obs, chain->u.func, &td->u.u_c.chain, &td->u.u_c.end); + } + else if (td) + { + assert (TOKEN_DATA_TYPE (td) == TOKEN_VOID); TOKEN_DATA_TYPE (td) = TOKEN_FUNC; TOKEN_DATA_FUNC (td) = chain->u.func; } @@ -1599,7 +1615,7 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, } if (ch == CHAR_MACRO) { - init_macro_token (td); + init_macro_token (obs, td); #ifdef DEBUG_INPUT xfprintf (stderr, "next_token -> MACDEF (%s)\n", find_builtin_by_addr (TOKEN_DATA_FUNC (td))->name); @@ -1635,10 +1651,7 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, _("end of file in comment")); if (ch == CHAR_MACRO) { - // TODO support concatenation of builtins - m4_warn_at_line (0, file, *line, caller, - _("cannot comment builtin")); - init_macro_token (NULL); + init_macro_token (obs, obs ? td : NULL); continue; } if (MATCH (ch, curr_comm.str2, true)) @@ -1734,35 +1747,8 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, _("end of file in string")); if (ch == CHAR_MACRO) - { - // TODO support concatenation of builtins - if (obstack_object_size (obs_td) == 0 - && TOKEN_DATA_TYPE (td) == TOKEN_VOID) - { - assert (quote_level == 1); - init_macro_token (td); - ch = peek_input (false); - if (MATCH (ch, curr_quote.str2, false)) - { -#ifdef DEBUG_INPUT - const builtin *bp - = find_builtin_by_addr (TOKEN_DATA_FUNC (td)); - xfprintf (stderr, "next_token -> MACDEF (%s)\n", - bp->name); -#endif - ch = next_char (false, false); - MATCH (ch, curr_quote.str2, true); - return TOKEN_MACDEF; - } - TOKEN_DATA_TYPE (td) = TOKEN_VOID; - } - else - init_macro_token (NULL); - m4_warn_at_line (0, file, *line, caller, - _("cannot quote builtin")); - continue; - } - if (ch == CHAR_QUOTE) + init_macro_token (obs, obs ? td : NULL); + else if (ch == CHAR_QUOTE) append_quote_token (obs, td); else if (MATCH (ch, curr_quote.str2, true)) { @@ -47,7 +47,7 @@ int no_gnu_extensions = 0; int prefix_all_builtins = 0; /* Max length of arguments in trace output (-lsize). */ -int max_debug_argument_length = INT_MAX; +size_t max_debug_argument_length = SIZE_MAX; /* Suppress warnings about missing arguments. */ int suppress_warnings = 0; @@ -572,9 +572,10 @@ main (int argc, char *const *argv, char *const *envp) break; case 'l': - max_debug_argument_length = atoi (optarg); - if (max_debug_argument_length <= 0) - max_debug_argument_length = INT_MAX; + { + long tmp = strtol (optarg, NULL, 10); + max_debug_argument_length = tmp <= 0 ? SIZE_MAX : (size_t) tmp; + } break; case 'o': @@ -129,7 +129,7 @@ extern int debug_level; /* -d */ extern size_t hash_table_size; /* -H */ extern int no_gnu_extensions; /* -G */ extern int prefix_all_builtins; /* -P */ -extern int max_debug_argument_length; /* -l */ +extern size_t max_debug_argument_length;/* -l */ extern int suppress_warnings; /* -Q */ extern int warning_status; /* -E */ extern int nesting_limit; /* -L */ @@ -182,7 +182,7 @@ extern FILE *debug; #define DEBUG_TRACE_CALLID 0x200 /* V: very verbose -- print everything */ -#define DEBUG_TRACE_VERBOSE 0x377 +#define DEBUG_TRACE_VERBOSE 0x3FF /* default flags -- equiv: aeq */ #define DEBUG_TRACE_DEFAULT 0x007 @@ -249,7 +249,6 @@ void trace_pre (const char *, int, macro_arguments *); void trace_post (const char *, int, macro_arguments *, const input_block *); -bool obstack_print (struct obstack *, const char *, size_t, int *); /* File: input.c --- lexical definitions. */ @@ -266,7 +265,7 @@ enum token_type TOKEN_COMMA, /* Active character `,', TOKEN_TEXT. */ TOKEN_CLOSE, /* Active character `)', TOKEN_TEXT. */ TOKEN_SIMPLE, /* Any other single character, TOKEN_TEXT. */ - TOKEN_MACDEF, /* A macro's definition (see "defn"), TOKEN_FUNC. */ + TOKEN_MACDEF, /* A builtin macro, TOKEN_FUNC or TOKEN_COMP. */ TOKEN_ARGV /* A series of parameters, TOKEN_COMP. */ }; @@ -434,7 +433,8 @@ extern int output_current_line; void output_init (void); void output_exit (void); void output_text (const char *, int); -void shipout_text (struct obstack *, const char *, int, int); +void divert_text (struct obstack *, const char *, int, int); +bool shipout_string_trunc (struct obstack *, const char *, size_t, size_t *); void make_diversion (int); void insert_diversion (int); void insert_file (FILE *); @@ -503,7 +503,7 @@ size_t adjust_refcount (int, bool); bool arg_adjust_refcount (macro_arguments *, bool); unsigned int arg_argc (macro_arguments *); token_data_type arg_type (macro_arguments *, unsigned int); -const char *arg_text (macro_arguments *, unsigned int); +const char *arg_text (macro_arguments *, unsigned int, bool); bool arg_equal (macro_arguments *, unsigned int, unsigned int); bool arg_empty (macro_arguments *, unsigned int); size_t arg_len (macro_arguments *, unsigned int); @@ -511,7 +511,7 @@ builtin_func *arg_func (macro_arguments *, unsigned int); struct obstack *arg_scratch (void); bool arg_print (struct obstack *, macro_arguments *, unsigned int, const string_pair *, bool, token_chain **, const char *, - int *, bool); + size_t *, bool); macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t, bool, bool); void push_arg (struct obstack *, macro_arguments *, unsigned int); @@ -522,7 +522,7 @@ void wrap_args (macro_arguments *); /* Grab the text at argv index I. Assumes macro_argument *argv is in scope, and aborts if the argument is not text. */ -#define ARG(i) arg_text (argv, i) +#define ARG(i) arg_text (argv, i, false) /* Grab the text length at argv index I. Assumes macro_argument *argv is in scope, and aborts if the argument is not text. */ @@ -587,7 +587,7 @@ bool evaluate (const char *, const char *, int32_t *); /* File: format.c --- printf like formatting. */ -void format (struct obstack *, int, macro_arguments *); +void expand_format (struct obstack *, int, macro_arguments *); /* File: freeze.c --- frozen state files. */ diff --git a/src/macro.c b/src/macro.c index 5b99e677..4dcbdf3e 100644 --- a/src/macro.c +++ b/src/macro.c @@ -305,10 +305,10 @@ expand_token (struct obstack *obs, token_type t, token_data *td, int line, && peek_token () != TOKEN_OPEN)) { #ifdef ENABLE_CHANGEWORD - shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td), - TOKEN_DATA_LEN (td), line); + divert_text (obs, TOKEN_DATA_ORIG_TEXT (td), + TOKEN_DATA_LEN (td), line); #else - shipout_text (obs, TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td), line); + divert_text (obs, TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td), line); #endif /* !ENABLE_CHANGEWORD */ /* The word just appended is unquoted, but the heuristics of safe_quote are applicable. */ @@ -324,23 +324,11 @@ expand_token (struct obstack *obs, token_type t, token_data *td, int line, assert (!"expand_token"); abort (); } - shipout_text (obs, TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td), line); + divert_text (obs, TOKEN_DATA_TEXT (td), TOKEN_DATA_LEN (td), line); return result; } -/*---------------------------------------------------------------. -| Helper function to print warning about concatenating FUNC with | -| text. | -`---------------------------------------------------------------*/ -static void -warn_builtin_concat (const char *caller, builtin_func *func) -{ - const builtin *bp = find_builtin_by_addr (func); - assert (bp); - m4_warn (0, caller, _("cannot concatenate builtin `%s'"), bp->name); -} - /*-------------------------------------------------------------------. | This function parses one argument to a macro call. It expects the | | first left parenthesis or the separating comma to have been read | @@ -383,15 +371,10 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) case TOKEN_CLOSE: if (paren_level == 0) { - size_t len = obstack_object_size (obs); - if (TOKEN_DATA_TYPE (argp) == TOKEN_FUNC) - { - if (!len) - return t == TOKEN_COMMA; - warn_builtin_concat (caller, TOKEN_DATA_FUNC (argp)); - } + assert (TOKEN_DATA_TYPE (argp) != TOKEN_FUNC); if (TOKEN_DATA_TYPE (argp) != TOKEN_COMP) { + size_t len = obstack_object_size (obs); TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; if (len) { @@ -404,7 +387,16 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) TOKEN_DATA_QUOTE_AGE (argp) = age; } else - make_text_link (obs, NULL, &argp->u.u_c.end); + { + make_text_link (obs, NULL, &argp->u.u_c.end); + if (argp->u.u_c.chain == argp->u.u_c.end + && argp->u.u_c.chain->type == CHAIN_FUNC) + { + builtin_func *func = argp->u.u_c.chain->u.func; + TOKEN_DATA_TYPE (argp) = TOKEN_FUNC; + TOKEN_DATA_FUNC (argp) = func; + } + } return t == TOKEN_COMMA; } /* fallthru */ @@ -427,14 +419,13 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) case TOKEN_WORD: case TOKEN_STRING: + case TOKEN_MACDEF: if (!expand_token (obs, t, &td, line, first)) age = 0; if (TOKEN_DATA_TYPE (&td) == TOKEN_COMP) { if (TOKEN_DATA_TYPE (argp) != TOKEN_COMP) { - if (TOKEN_DATA_TYPE (argp) == TOKEN_FUNC) - warn_builtin_concat (caller, TOKEN_DATA_FUNC (argp)); TOKEN_DATA_TYPE (argp) = TOKEN_COMP; argp->u.u_c.chain = td.u.u_c.chain; argp->u.u_c.wrapper = argp->u.u_c.has_func = false; @@ -450,22 +441,6 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) } break; - case TOKEN_MACDEF: - if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID - && obstack_object_size (obs) == 0) - { - TOKEN_DATA_TYPE (argp) = TOKEN_FUNC; - TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td); - } - else - { - if (TOKEN_DATA_TYPE (argp) == TOKEN_FUNC) - warn_builtin_concat (caller, TOKEN_DATA_FUNC (argp)); - warn_builtin_concat (caller, TOKEN_DATA_FUNC (&td)); - TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; - } - break; - case TOKEN_ARGV: assert (paren_level == 0 && TOKEN_DATA_TYPE (argp) == TOKEN_VOID && obstack_object_size (obs) == 0 @@ -798,6 +773,8 @@ arg_adjust_refcount (macro_arguments *argv, bool increase) if (chain->u.u_s.level >= 0) adjust_refcount (chain->u.u_s.level, increase); break; + case CHAIN_FUNC: + break; case CHAIN_ARGV: assert (chain->u.u_a.argv->inuse); arg_adjust_refcount (chain->u.u_a.argv, increase); @@ -814,30 +791,30 @@ arg_adjust_refcount (macro_arguments *argv, bool increase) } -/* Given ARGV, return the token_data that contains argument INDEX; - INDEX must be > 0, < argv->argc. If LEVEL is non-NULL, *LEVEL is - set to the obstack level that contains the token (which is not - necessarily the level of ARGV). If FLATTEN, avoid returning a - builtin function. */ +/* Given ARGV, return the token_data that contains argument ARG; ARG + must be > 0, < argv->argc. If LEVEL is non-NULL, *LEVEL is set to + the obstack level that contains the token (which is not necessarily + the level of ARGV). If FLATTEN, avoid returning a builtin + function. */ static token_data * -arg_token (macro_arguments *argv, unsigned int index, int *level, bool flatten) +arg_token (macro_arguments *argv, unsigned int arg, int *level, bool flatten) { unsigned int i; - token_data *token; + token_data *token = NULL; - assert (index && index < argv->argc); + assert (arg && arg < argv->argc); if (level) *level = argv->level; flatten |= argv->flatten; if (!argv->wrapper) { - token = argv->array[index - 1]; + token = argv->array[arg - 1]; if (flatten && TOKEN_DATA_TYPE (token) == TOKEN_FUNC) token = &empty_token; return token; } - /* Must cycle through all tokens, until we find index, since a ref + /* Must cycle through all tokens, until we find arg, since a ref may occupy multiple indices. */ for (i = 0; i < argv->arraylen; i++) { @@ -846,18 +823,18 @@ arg_token (macro_arguments *argv, unsigned int index, int *level, bool flatten) { token_chain *chain = token->u.u_c.chain; assert (!chain->next && chain->type == CHAIN_ARGV); - if (index <= (chain->u.u_a.argv->argc - chain->u.u_a.index - - chain->u.u_a.skip_last)) + if (arg <= (chain->u.u_a.argv->argc - chain->u.u_a.index + - chain->u.u_a.skip_last)) { token = arg_token (chain->u.u_a.argv, - chain->u.u_a.index - 1 + index, level, + chain->u.u_a.index - 1 + arg, level, flatten || chain->u.u_a.flatten); break; } - index -= (chain->u.u_a.argv->argc - chain->u.u_a.index - - chain->u.u_a.skip_last); + arg -= (chain->u.u_a.argv->argc - chain->u.u_a.index + - chain->u.u_a.skip_last); } - else if (--index == 0) + else if (--arg == 0) break; } return token; @@ -893,17 +870,17 @@ arg_argc (macro_arguments *argv) return argv->argc; } -/* Given ARGV, return the type of argument INDEX. Index 0 is always - text, and indices beyond argc are likewise treated as text. */ +/* Given ARGV, return the type of argument ARG. Arg 0 is always text, + and indices beyond argc are likewise treated as text. */ token_data_type -arg_type (macro_arguments *argv, unsigned int index) +arg_type (macro_arguments *argv, unsigned int arg) { token_data_type type; token_data *token; - if (argv->flatten || !argv->has_func || index == 0 || index >= argv->argc) + if (argv->flatten || !argv->has_func || arg == 0 || arg >= argv->argc) return TOKEN_TEXT; - token = arg_token (argv, index, NULL, false); + token = arg_token (argv, arg, NULL, false); type = TOKEN_DATA_TYPE (token); if (type == TOKEN_COMP && !token->u.u_c.has_func) type = TOKEN_TEXT; @@ -912,22 +889,23 @@ arg_type (macro_arguments *argv, unsigned int index) return type; } -/* Given ARGV, return the text at argument INDEX. Abort if the - argument is not text. Index 0 is always text, and indices beyond - argc return the empty string. The result is always NUL-terminated, - even if it includes embedded NUL characters. */ +/* Given ARGV, return the text at argument ARG. Abort if the argument + is not text. Arg 0 is always text, and indices beyond argc return + the empty string. If FLATTEN, builtins are ignored. The result is + always NUL-terminated, even if it includes embedded NUL + characters. */ const char * -arg_text (macro_arguments *argv, unsigned int index) +arg_text (macro_arguments *argv, unsigned int arg, bool flatten) { token_data *token; token_chain *chain; struct obstack *obs; /* Scratch space; cleaned at end of macro_expand. */ - if (index == 0) + if (arg == 0) return argv->argv0; - if (index >= argv->argc) + if (arg >= argv->argc) return ""; - token = arg_token (argv, index, NULL, false); + token = arg_token (argv, arg, NULL, flatten); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: @@ -942,13 +920,18 @@ arg_text (macro_arguments *argv, unsigned int index) case CHAIN_STR: obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len); break; + case CHAIN_FUNC: + if (flatten) + break; + assert (!"arg_text"); + abort (); case CHAIN_ARGV: - assert (!chain->u.u_a.has_func || argv->flatten); + assert (!chain->u.u_a.has_func || flatten || argv->flatten); arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), - argv->flatten || chain->u.u_a.flatten, NULL, NULL, - NULL, false); + flatten || argv->flatten || chain->u.u_a.flatten, + NULL, NULL, NULL, false); break; default: assert (!"arg_text"); @@ -969,7 +952,7 @@ arg_text (macro_arguments *argv, unsigned int index) /* Given ARGV, compare text arguments INDEXA and INDEXB for equality. Both indices must be non-zero and less than argc. Return true if the arguments contain the same contents; often more efficient than - strcmp (arg_text (argv, indexa), arg_text (argv, indexb)) == 0. */ + strcmp (arg_text (argv, a, 1), arg_text (argv, b, 1)) == 0. */ bool arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) { @@ -1115,33 +1098,33 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) return ca == cb; } -/* Given ARGV, return true if argument INDEX is the empty string. - This gives the same result as comparing arg_len against 0, but is - often faster. */ +/* Given ARGV, return true if argument ARG is the empty string. This + gives the same result as comparing arg_len against 0, but is often + faster. */ bool -arg_empty (macro_arguments *argv, unsigned int index) +arg_empty (macro_arguments *argv, unsigned int arg) { - if (index == 0) + if (arg == 0) return argv->argv0_len == 0; - if (index >= argv->argc) + if (arg >= argv->argc) return true; - return arg_token (argv, index, NULL, false) == &empty_token; + return arg_token (argv, arg, NULL, false) == &empty_token; } -/* Given ARGV, return the length of argument INDEX. Abort if the +/* Given ARGV, return the length of argument ARG. Abort if the argument is not text. Indices beyond argc return 0. */ size_t -arg_len (macro_arguments *argv, unsigned int index) +arg_len (macro_arguments *argv, unsigned int arg) { token_data *token; token_chain *chain; size_t len; - if (index == 0) + if (arg == 0) return argv->argv0_len; - if (index >= argv->argc) + if (arg >= argv->argc) return 0; - token = arg_token (argv, index, NULL, false); + token = arg_token (argv, arg, NULL, false); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: @@ -1200,14 +1183,14 @@ arg_len (macro_arguments *argv, unsigned int index) abort (); } -/* Given ARGV, return the builtin function referenced by argument - INDEX. Abort if it is not a builtin in isolation. */ +/* Given ARGV, return the builtin function referenced by argument ARG. + Abort if it is not a builtin in isolation. */ builtin_func * -arg_func (macro_arguments *argv, unsigned int index) +arg_func (macro_arguments *argv, unsigned int arg) { token_data *token; - token = arg_token (argv, index, NULL, false); + token = arg_token (argv, arg, NULL, false); assert (TOKEN_DATA_TYPE (token) == TOKEN_FUNC); return TOKEN_DATA_FUNC (token); } @@ -1223,7 +1206,7 @@ arg_scratch (void) } /* Dump a representation of ARGV to the obstack OBS, starting with - argument INDEX. If QUOTES is non-NULL, each argument is displayed + argument ARG. If QUOTES is non-NULL, each argument is displayed with those quotes. If FLATTEN, builtins are converted to empty quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise, builtins are represented by their name. Separate arguments with @@ -1236,9 +1219,9 @@ arg_scratch (void) arguments may be discarded. MAX_LEN and CHAINP may not both be specified. */ bool -arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, +arg_print (struct obstack *obs, macro_arguments *argv, unsigned int arg, const string_pair *quotes, bool flatten, token_chain **chainp, - const char *sep, int *max_len, bool quote_each) + const char *sep, size_t *max_len, bool quote_each) { int len = max_len ? *max_len : INT_MAX; unsigned int i; @@ -1249,32 +1232,37 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, size_t sep_len; size_t *plen = quote_each ? NULL : &len; + flatten |= argv->flatten; if (chainp) assert (!max_len && *chainp); if (!sep) sep = ","; sep_len = strlen (sep); - for (i = index; i < argv->argc; i++) + for (i = arg; i < argv->argc; i++) { if (quote_each && max_len) len = *max_len; - if (use_sep && obstack_print (obs, sep, sep_len, plen)) + if (use_sep && shipout_string_trunc (obs, sep, sep_len, plen)) return true; use_sep = true; token = arg_token (argv, i, NULL, flatten); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: - if (quotes && obstack_print (obs, quotes->str1, quotes->len1, plen)) + if (quotes && shipout_string_trunc (obs, quotes->str1, quotes->len1, + plen)) return true; - if (obstack_print (obs, TOKEN_DATA_TEXT (token), - TOKEN_DATA_LEN (token), &len) && !quote_each) + if (shipout_string_trunc (obs, TOKEN_DATA_TEXT (token), + TOKEN_DATA_LEN (token), &len) + && !quote_each) return true; - if (quotes && obstack_print (obs, quotes->str2, quotes->len2, plen)) + if (quotes && shipout_string_trunc (obs, quotes->str2, quotes->len2, + plen)) return true; break; case TOKEN_COMP: - if (quotes && obstack_print (obs, quotes->str1, quotes->len1, plen)) + if (quotes && shipout_string_trunc (obs, quotes->str1, quotes->len1, + plen)) return true; chain = token->u.u_c.chain; done = false; @@ -1283,8 +1271,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, switch (chain->type) { case CHAIN_STR: - if (obstack_print (obs, chain->u.u_s.str, chain->u.u_s.len, - &len)) + if (shipout_string_trunc (obs, chain->u.u_s.str, + chain->u.u_s.len, &len)) done = true; break; case CHAIN_FUNC: @@ -1306,7 +1294,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, } if (done && !quote_each) return true; - if (quotes && obstack_print (obs, quotes->str2, quotes->len2, plen)) + if (quotes && shipout_string_trunc (obs, quotes->str2, quotes->len2, + plen)) return true; break; case TOKEN_FUNC: @@ -1326,18 +1315,18 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, } /* Populate the new TOKEN as a wrapper to ARGV, starting with argument - INDEX. Allocate any data on OBS, owned by a given expansion LEVEL. + ARG. Allocate any data on OBS, owned by a given expansion LEVEL. FLATTEN determines whether to allow builtins, and QUOTES determines whether all arguments are quoted. Return TOKEN when successful, NULL when wrapping ARGV is trivially empty. */ static token_data * make_argv_ref_token (token_data *token, struct obstack *obs, int level, - macro_arguments *argv, unsigned int index, bool flatten, + macro_arguments *argv, unsigned int arg, bool flatten, const string_pair *quotes) { token_chain *chain; - if (index >= argv->argc) + if (arg >= argv->argc) return NULL; TOKEN_DATA_TYPE (token) = TOKEN_COMP; token->u.u_c.chain = token->u.u_c.end = NULL; @@ -1354,13 +1343,13 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level, && argv->array[i]->u.u_c.wrapper) || level >= 0) break; - if (index == 1) + if (arg == 1) { push_arg_quote (obs, argv, i + 1, quotes); obstack_1grow (obs, ','); } else - index--; + arg--; } assert (i < argv->arraylen); if (i + 1 == argv->arraylen) @@ -1371,11 +1360,11 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level, assert (!chain->next && chain->type == CHAIN_ARGV && !chain->u.u_a.skip_last); argv = chain->u.u_a.argv; - index += chain->u.u_a.index - 1; + arg += chain->u.u_a.index - 1; } else { - index += i; + arg += i; break; } } @@ -1393,7 +1382,7 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level, chain->type = CHAIN_ARGV; chain->quote_age = argv->quote_age; chain->u.u_a.argv = argv; - chain->u.u_a.index = index; + chain->u.u_a.index = arg; chain->u.u_a.flatten = flatten; chain->u.u_a.has_func = argv->has_func; chain->u.u_a.comma = false; @@ -1416,12 +1405,12 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, macro_arguments *new_argv; token_data *token; token_data *new_token; - unsigned int index = skip ? 2 : 1; + unsigned int i = skip ? 2 : 1; struct obstack *obs = arg_scratch (); new_token = (token_data *) obstack_alloc (obs, sizeof *token); - token = make_argv_ref_token (new_token, obs, expansion_level - 1, argv, - index, flatten, NULL); + token = make_argv_ref_token (new_token, obs, expansion_level - 1, argv, i, + flatten, NULL); if (!token) { obstack_free (obs, new_token); @@ -1444,7 +1433,7 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, new_argv->flatten = flatten; new_argv->has_func = argv->has_func; } - new_argv->argc = argv->argc - (index - 1); + new_argv->argc = argv->argc - (i - 1); new_argv->inuse = false; new_argv->argv0 = argv0; new_argv->argv0_len = argv0_len; @@ -1453,33 +1442,32 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, return new_argv; } -/* Push argument INDEX from ARGV onto the expansion stack OBS for +/* Push argument ARG from ARGV onto the expansion stack OBS for rescanning. */ void -push_arg (struct obstack *obs, macro_arguments *argv, unsigned int index) +push_arg (struct obstack *obs, macro_arguments *argv, unsigned int arg) { - if (index == 0) + if (arg == 0) { /* Always push copy of arg 0, since its lifetime is not guaranteed beyond expand_macro. */ obstack_grow (obs, argv->argv0, argv->argv0_len); return; } - if (index >= argv->argc) + if (arg >= argv->argc) return; - push_arg_quote (obs, argv, index, NULL); + push_arg_quote (obs, argv, arg, NULL); } -/* Push argument INDEX from ARGV onto the expansion stack OBS for - rescanning. INDEX must be > 0, < argc. QUOTES determines any - quote delimiters that were in effect when the reference was - created. */ +/* Push argument ARG from ARGV onto the expansion stack OBS for + rescanning. ARG must be > 0, < argc. QUOTES determines any quote + delimiters that were in effect when the reference was created. */ void -push_arg_quote (struct obstack *obs, macro_arguments *argv, unsigned int index, +push_arg_quote (struct obstack *obs, macro_arguments *argv, unsigned int arg, const string_pair *quotes) { int level; - token_data *token = arg_token (argv, index, &level, false); + token_data *token = arg_token (argv, arg, &level, false); if (quotes) obstack_grow (obs, quotes->str1, quotes->len1); diff --git a/src/output.c b/src/output.c index d252d74e..ee1907b2 100644 --- a/src/output.c +++ b/src/output.c @@ -121,10 +121,10 @@ cmp_diversion_CB (const void *elt1, const void *elt2) static bool threshold_diversion_CB (const void *elt, const void *threshold) { - const m4_diversion *div = (const m4_diversion *) elt; + const m4_diversion *diversion = (const m4_diversion *) elt; /* No need to worry about overflow, since we don't create diversions with negative divnum. */ - return div->divnum >= *(const int *) threshold; + return diversion->divnum >= *(const int *) threshold; } void @@ -198,7 +198,8 @@ m4_tmpname (int divnum) obstack_1grow (&diversion_storage, '4'); obstack_1grow (&diversion_storage, '-'); offset = obstack_object_size (&diversion_storage); - buffer = obstack_alloc (&diversion_storage, INT_BUFSIZE_BOUND (divnum)); + buffer = (char *) obstack_alloc (&diversion_storage, + INT_BUFSIZE_BOUND (divnum)); } if (snprintf (&buffer[offset], INT_BUFSIZE_BOUND (divnum), "%d", divnum) < 0) m4_error (EXIT_FAILURE, errno, NULL, @@ -385,8 +386,8 @@ make_room_for (int length) } /* The current buffer may be safely reallocated. */ - output_diversion->u.buffer - = xrealloc (output_diversion->u.buffer, (size_t) wanted_size); + output_diversion->u.buffer = xrealloc (output_diversion->u.buffer, + (size_t) wanted_size); total_buffer_size += wanted_size - output_diversion->size; output_diversion->size = wanted_size; @@ -469,7 +470,7 @@ output_text (const char *text, int length) `--------------------------------------------------------------------*/ void -shipout_text (struct obstack *obs, const char *text, int length, int line) +divert_text (struct obstack *obs, const char *text, int length, int line) { static bool start_of_output_line = true; const char *cursor; @@ -571,6 +572,35 @@ shipout_text (struct obstack *obs, const char *text, int length, int line) } } } + +/* Dump the string STR of length LEN to the obstack OBS. If LEN is + SIZE_MAX, use strlen (STR) instead. If MAX_LEN is non-NULL, + truncate the dump at MAX_LEN bytes and return true if MAX_LEN was + reached; otherwise, return false and update MAX_LEN as + appropriate. */ +bool +shipout_string_trunc (struct obstack *obs, const char *str, size_t len, + size_t *max_len) +{ + size_t max = max_len ? *max_len : INT_MAX; + + if (len == SIZE_MAX) + len = strlen (str); + if (len < max) + { + obstack_grow (obs, str, len); + max -= len; + } + else + { + obstack_grow (obs, str, max); + obstack_grow (obs, "...", 3); + max = 0; + } + if (max_len) + *max_len = max; + return max == 0; +} /* Functions for use by diversions. */ |