This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GNU M4 source repository".
http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=d1b6a563f559235700197ea87a9cb50accc43b95 The branch, argv_ref has been updated via d1b6a563f559235700197ea87a9cb50accc43b95 (commit) via 8b898ea37cc61f2778881de0211836e2ba6d5e66 (commit) from 3b8c64664d1bf49b525206415d091f8ed562fecd (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit d1b6a563f559235700197ea87a9cb50accc43b95 Author: Eric Blake <[EMAIL PROTECTED]> Date: Mon Dec 3 11:53:45 2007 -0700 Stage20: make m4wrap take builtins, add m4parw commit 8b898ea37cc61f2778881de0211836e2ba6d5e66 Author: Eric Blake <[EMAIL PROTECTED]> Date: Wed Nov 21 10:14:28 2007 -0700 Stage19: allow builtin tokens in more macros ----------------------------------------------------------------------- Summary of changes: NEWS | 10 ++ doc/m4.texinfo | 223 +++++++++++++++++++++++++++++------ examples/Makefile.am | 3 +- examples/wraplifo.m4 | 10 ++ src/builtin.c | 123 +++++++++++++++----- src/input.c | 321 ++++++++++++++++++++++++++++++++++++++------------ src/m4.h | 16 ++- src/macro.c | 127 ++++++++++++++------ 8 files changed, 648 insertions(+), 185 deletions(-) create mode 100644 examples/wraplifo.m4 diff --git a/NEWS b/NEWS index eec8fdd..29589a1 100644 --- a/NEWS +++ b/NEWS @@ -15,11 +15,21 @@ Version 1.4.11 - ?? ??? 2007, by ???? (git version 1.4.10a-*) possible to concatenate a builtin macro with anything else; a warning is now issued if this is attempted, although a future version of M4 may lift this restriction to match other implementations. +* Fix the `m4wrap' builtin to accumulate wrapped text in FIFO order, as + required by POSIX. Add a new builtin, `m4parw', for use in applications + that want to preserve LIFO order. * Several improvements in `index', `regexp', and `patsubst' builtins to speed up typical Autoconf usage. * Enhance the `ifelse' and `shift' builtins so that tail-recursive algorithms based on `$@' operate in linear, rather than quadratic, time and memory. +* Enhance the `ifdef', `ifelse', `shift', and `m4wrap' builtins, as well as + all user macros, to transparently handle builtin tokens generated by + `defn'. +* Enhance the `defn', `dumpdef', `ifdef', `popdef', 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 was already done for + `define', `pushdef', `builtin', and `indir'. * A number of portability improvements inherited from gnulib. Version 1.4.10 - 09 Jul 2007, by Eric Blake (CVS version 1.4.9c) diff --git a/doc/m4.texinfo b/doc/m4.texinfo index bdde3ed..67c73e7 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -2175,9 +2175,14 @@ defn([l], [r]) @cindex builtins, special tokens @cindex tokens, builtin macro -Using @code{defn} to generate special tokens for builtin macros outside -of expected contexts can sometimes trigger warnings. But most of the -time, such tokens are silently converted to the empty string. +Using @code{defn} to generate special tokens for builtin macros will +generate a warning in contexts where a macro name is expected. But in +contexts that operate on text, the builtin token is just silently +converted to an empty string. As of M4 1.4.11, expansion of user macros +will also preserve builtin tokens. However, any use of builtin tokens +outside of the second argument to @code{define} and @code{pushdef} is +generally not portable, since earlier @acronym{GNU} M4 versions, as well +as other @code{m4} implementations, vary on how such tokens are treated. @example defn(`defn') @@ -2187,38 +2192,72 @@ define(defn(`divnum'), `cannot redefine a builtin token') @result{} divnum @result{}0 +len(defn(`divnum')) [EMAIL PROTECTED] define(`echo', `$@@') @result{} -define(`mydivnum', echo(defn(`divnum'))) +define(`mydivnum', shift(echo(`', defn(`divnum')))) @result{} mydivnum [EMAIL PROTECTED] +define(`', `empty-$1') [EMAIL PROTECTED] +defn(defn(`divnum')) [EMAIL PROTECTED]:stdin:9: Warning: defn: invalid macro name ignored [EMAIL PROTECTED] +pushdef(defn(`divnum'), `oops') [EMAIL PROTECTED]:stdin:10: Warning: pushdef: invalid macro name ignored [EMAIL PROTECTED] +indir(defn(`divnum'), `string') [EMAIL PROTECTED]:stdin:11: Warning: indir: invalid macro name ignored [EMAIL PROTECTED] +indir(`', `string') [EMAIL PROTECTED] +popdef(defn(`divnum')) [EMAIL PROTECTED]:stdin:13: Warning: popdef: invalid macro name ignored [EMAIL PROTECTED] +dumpdef(defn(`divnum')) [EMAIL PROTECTED]:stdin:14: Warning: dumpdef: invalid macro name ignored [EMAIL PROTECTED] +undefine(defn(`divnum')) [EMAIL PROTECTED]:stdin:15: Warning: undefine: invalid macro name ignored [EMAIL PROTECTED] +dumpdef(`') [EMAIL PROTECTED]:@tabchar{}`empty-$1' @result{} @end example -Also note that @code{defn} with multiple arguments can only join text -macros, not builtins. Likewise, when collecting macro arguments, a -builtin token is preserved only when it occurs in isolation. A future -version of @acronym{GNU} M4 may lift these restrictions. +Also note that as of M4 1.4.11, @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. @example define(`a', `A')define(`AA', `b') @result{} +traceon(`defn', `define') [EMAIL PROTECTED] defn(`a', `divnum', `a') [EMAIL PROTECTED]:stdin:2: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]: -1- defn(`a', `divnum', `a') -> ``A'<divnum>`A'' @result{}AA define(`mydivnum', defn(`divnum', `divnum'))mydivnum [EMAIL PROTECTED]:stdin:3: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:3: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED] -define(`mydivnum', defn(`divnum')defn(`divnum'))mydivnum [EMAIL PROTECTED]: -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' [EMAIL PROTECTED]: -1- define(`mydivnum', `') [EMAIL PROTECTED] +traceoff(`defn', `define') [EMAIL PROTECTED] +define(`mydivnum', defn(`divnum')defn(`divnum'))mydivnum [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' @result{} define(`mydivnum', defn(`divnum')`a')mydivnum [EMAIL PROTECTED]:stdin:5: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:7: Warning: define: cannot concatenate builtin `divnum' @result{}A define(`mydivnum', `a'defn(`divnum'))mydivnum [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:8: Warning: define: cannot concatenate builtin `divnum' @result{}A @end example @@ -2536,6 +2575,22 @@ ifdef(`no_such_macro', `yes', `no', `extra argument') @result{}no @end example +As of M4 1.4.11, @code{ifdef} transparently handles builtin tokens +generated by @code{defn} (@pxref{Defn}) that occur in either [EMAIL PROTECTED], although a warning is issued for invalid macro names. + [EMAIL PROTECTED] +define(`', `empty') [EMAIL PROTECTED] +ifdef(defn(`defn'), `yes', `no') [EMAIL PROTECTED]:stdin:2: Warning: ifdef: invalid macro name ignored [EMAIL PROTECTED] +define(`foo', ifdef(`divnum', defn(`divnum'), `undefined')) [EMAIL PROTECTED] +foo [EMAIL PROTECTED] [EMAIL PROTECTED] example + @node Ifelse @section If-else construct, or multibranch @@ -2636,6 +2691,24 @@ ifelse(`foo', `bar', `3', `gnu', `gnats', `6', `7', `8') @result{}7 @end example +As of M4 1.4.11, @code{ifelse} transparently handles builtin tokens +generated by @code{defn} (@pxref{Defn}). Because of this, it is always +safe to compare two macro definitions, without worrying whether the +macro might be a builtin. + [EMAIL PROTECTED] +ifelse(defn(`defn'), `', `yes', `no') [EMAIL PROTECTED] +ifelse(defn(`defn'), defn(`divnum'), `yes', `no') [EMAIL PROTECTED] +ifelse(defn(`defn'), defn(`defn'), `yes', `no') [EMAIL PROTECTED] +define(`foo', ifelse(`', `', defn(`divnum'))) [EMAIL PROTECTED] +foo [EMAIL PROTECTED] [EMAIL PROTECTED] example + Naturally, the normal case will be slightly more advanced than these examples. A common use of @code{ifelse} is in macros implementing loops of various kinds. @@ -4132,12 +4205,19 @@ files. To save input text, use the builtin @code{m4wrap}: @deffn Builtin m4wrap (@var{string}, @dots{}) [EMAIL PROTECTED] Builtin m4parw (@var{string}, @dots{}) Stores @var{string} in a safe place, to be reread when end of input is reached. As a @acronym{GNU} extension, additional arguments are concatenated with a space to the @var{string}. -The expansion of @code{m4wrap} is void. -The macro @code{m4wrap} is recognized only with parameters. +Successive invocations of @code{m4wrap} accumulate saved text in +first-in, first-out order, as required by @acronym{POSIX}. As a [EMAIL PROTECTED] extension, the @code{m4parw} builtin accumulates text in +last-in, first-out order. + +The expansion of @code{m4wrap} and @code{m4parw} is void. +The macros @code{m4wrap} and @code{m4parw} are recognized only with +parameters. @end deffn @example @@ -4155,16 +4235,39 @@ This is the first and last normal input line. The saved input is only reread when the end of normal input is seen, and not if @code{m4exit} is used to exit @code{m4}. [EMAIL PROTECTED] FIXME: this contradicts POSIX, which requires that "If the [EMAIL PROTECTED] m4wrap macro is used multiple times, the arguments specified [EMAIL PROTECTED] shall be processed in the order in which the m4wrap macros were [EMAIL PROTECTED] processed." -It is safe to call @code{m4wrap} from saved text, but then the order in -which the saved text is reread is undefined. If @code{m4wrap} is not used -recursively, the saved pieces of text are reread in the opposite order -in which they were saved (LIFO---last in, first out). However, this -behavior is likely to change in a future release, to match [EMAIL PROTECTED], so you should not depend on this order. [EMAIL PROTECTED] @acronym{GNU} extensions +It is safe to call @code{m4wrap} or @code{m4parw} from wrapped text, +where all the recursively wrapped text is deferred until the current +wrapped text is exhausted. As of M4 1.4.11, when @code{m4wrap} is not +used recursively, the saved pieces of text are reread in the same order +in which they were saved (FIFO---first in, first out), as required by [EMAIL PROTECTED] However, earlier versions had reverse ordering +(LIFO---last in, first out), so M4 1.4.11 also introduced @code{m4parw} +to preserve this behavior. + [EMAIL PROTECTED] +m4parw(`1 +') [EMAIL PROTECTED] +m4wrap(`2 +') [EMAIL PROTECTED] +m4parw(`3 +') [EMAIL PROTECTED] +m4parw(`4', `5 +') [EMAIL PROTECTED] +m4wrap(`6', `7 +') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] 5 [EMAIL PROTECTED] 7 [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] example Here is an example of implementing a factorial function using @code{m4wrap}: @@ -4184,13 +4287,13 @@ Invocations of @code{m4wrap} at the same recursion level are concatenated and rescanned as usual: @example -define(`aa', `AA +define(`ab', `AB ') @result{} -m4wrap(`a')m4wrap(`a') +m4wrap(`a')m4wrap(`b') @result{} ^D [EMAIL PROTECTED] [EMAIL PROTECTED] @end example @noindent @@ -4205,6 +4308,24 @@ m4wrap(`m4wrap(`)')len(abc') @error{}m4:stdin:1: len: end of file in argument list @end example +As of M4 1.4.11, @code{m4wrap} transparently handles builtin tokens +generated by @code{defn} (@pxref{Defn}). However, for portability, it +is better to defer the evaluation of @code{defn} along with the rest of +the wrapped text, as is done for @code{foo} in the example below, rather +than computing the builtin token up front, as is done for @code{bar}. + [EMAIL PROTECTED] +m4wrap(`define(`foo', defn(`divnum'))foo +') [EMAIL PROTECTED] +m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar +') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] [EMAIL PROTECTED] [EMAIL PROTECTED] example + @node File Inclusion @chapter File inclusion @@ -6045,9 +6166,13 @@ __line__ @result{}8 __line__ @result{}11 +m4wrap(`__line__ +') [EMAIL PROTECTED] ^D @result{}6 @result{}6 [EMAIL PROTECTED] @end example The @[EMAIL PROTECTED] macro behaves like @samp{$0} in shell @@ -6489,15 +6614,17 @@ of @samp{-} on the command line. @item @acronym{POSIX} requires @code{m4wrap} (@pxref{M4wrap}) to act in FIFO -(first-in, first-out) order, but @acronym{GNU} @code{m4} currently uses +(first-in, first-out) order, and most other implementations obey this. +However, versions of @acronym{GNU} @code{m4} earlier than 1.4.11 used LIFO order. Furthermore, @acronym{POSIX} states that only the first -argument to @code{m4wrap} is saved for later evaluation, bug +argument to @code{m4wrap} is saved for later evaluation, but @acronym{GNU} @code{m4} saves and processes all arguments, with output separated by spaces. -However, it is possible to emulate @acronym{POSIX} behavior by -including the file @[EMAIL PROTECTED]/@/examples/@/wrapfifo.m4} -from the distribution: +It is possible to emulate @acronym{POSIX} behavior even with older +versions of @acronym{GNU} M4 by including the file [EMAIL PROTECTED]@value{VERSION}/@/examples/@/wrapfifo.m4} from the +distribution: @example undivert(`wrapfifo.m4')dnl @@ -6520,6 +6647,32 @@ m4wrap(`a`'m4wrap(`c @result{}abc @end example +It is likewise possible to emulate LIFO behavior without resorting to +the @acronym{GNU} M4 extensions of @code{m4parw} or @code{builtin}, by +including the file @[EMAIL PROTECTED]/@/examples/@/wraplifo.m4} +from the distribution: + [EMAIL PROTECTED] +undivert(`wraplifo.m4')dnl [EMAIL PROTECTED] Redefine m4wrap to have LIFO semantics. [EMAIL PROTECTED](`_m4wrap_level', `0')dnl [EMAIL PROTECTED](`_m4wrap', defn(`m4wrap'))dnl [EMAIL PROTECTED](`m4wrap', [EMAIL PROTECTED](`m4wrap'_m4wrap_level, [EMAIL PROTECTED] `define(`m4wrap'_m4wrap_level, [EMAIL PROTECTED] `$1'defn(`m4wrap'_m4wrap_level))', [EMAIL PROTECTED] `_m4wrap(`define(`_m4wrap_level', incr(_m4wrap_level))dnl [EMAIL PROTECTED]'_m4wrap_level)dnl [EMAIL PROTECTED](`m4wrap'_m4wrap_level, `$1')')')dnl +include(`wraplifo.m4') [EMAIL PROTECTED] +m4wrap(`a`'m4wrap(`c +', `d')')m4wrap(`b') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] [EMAIL PROTECTED] example + @item @acronym{POSIX} states that builtins that require arguments, but are called without arguments, have undefined behavior. Traditional diff --git a/examples/Makefile.am b/examples/Makefile.am index b1ef68a..c1dc522 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -58,4 +58,5 @@ translit.m4 \ undivert.incl \ undivert.m4 \ wrap.m4 \ -wrapfifo.m4 +wrapfifo.m4 \ +wraplifo.m4 diff --git a/examples/wraplifo.m4 b/examples/wraplifo.m4 new file mode 100644 index 0000000..bdbf3fb --- /dev/null +++ b/examples/wraplifo.m4 @@ -0,0 +1,10 @@ +dnl Redefine m4wrap to have LIFO semantics. +define(`_m4wrap_level', `0')dnl +define(`_m4wrap', defn(`m4wrap'))dnl +define(`m4wrap', +`ifdef(`m4wrap'_m4wrap_level, + `define(`m4wrap'_m4wrap_level, + `$1'defn(`m4wrap'_m4wrap_level))', + `_m4wrap(`define(`_m4wrap_level', incr(_m4wrap_level))dnl +m4wrap'_m4wrap_level)dnl +define(`m4wrap'_m4wrap_level, `$1')')')dnl diff --git a/src/builtin.c b/src/builtin.c index e7b5282..7801d9c 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -71,6 +71,7 @@ DECLARE (m4_index); DECLARE (m4_indir); DECLARE (m4_len); DECLARE (m4_m4exit); +DECLARE (m4_m4parw); DECLARE (m4_m4wrap); DECLARE (m4_maketemp); DECLARE (m4_mkstemp); @@ -110,31 +111,32 @@ builtin_tab[] = { "debugfile", true, false, false, m4_debugfile }, { "decr", false, false, true, m4_decr }, { "define", false, true, true, m4_define }, - { "defn", false, false, true, m4_defn }, + { "defn", false, true, true, m4_defn }, { "divert", false, false, false, m4_divert }, { "divnum", false, false, false, m4_divnum }, { "dnl", false, false, false, m4_dnl }, - { "dumpdef", false, false, false, m4_dumpdef }, + { "dumpdef", false, true, false, m4_dumpdef }, { "errprint", false, false, true, m4_errprint }, { "esyscmd", true, false, true, m4_esyscmd }, { "eval", false, false, true, m4_eval }, { "format", true, false, true, m4_format }, - { "ifdef", false, false, true, m4_ifdef }, - { "ifelse", false, false, true, m4_ifelse }, + { "ifdef", false, true, true, m4_ifdef }, + { "ifelse", false, true, true, m4_ifelse }, { "include", false, false, true, m4_include }, { "incr", false, false, true, m4_incr }, { "index", false, false, true, m4_index }, { "indir", true, true, true, m4_indir }, { "len", false, false, true, m4_len }, { "m4exit", false, false, false, m4_m4exit }, - { "m4wrap", false, false, true, m4_m4wrap }, + { "m4parw", true, true, true, m4_m4parw }, + { "m4wrap", false, true, true, m4_m4wrap }, { "maketemp", false, false, true, m4_maketemp }, { "mkstemp", false, false, true, m4_mkstemp }, { "patsubst", true, false, true, m4_patsubst }, - { "popdef", false, false, true, m4_popdef }, + { "popdef", false, true, true, m4_popdef }, { "pushdef", false, true, true, m4_pushdef }, { "regexp", true, false, true, m4_regexp }, - { "shift", false, false, true, m4_shift }, + { "shift", false, true, true, m4_shift }, { "sinclude", false, false, true, m4_sinclude }, { "substr", false, false, true, m4_substr }, { "syscmd", false, false, true, m4_syscmd }, @@ -142,7 +144,7 @@ builtin_tab[] = { "traceoff", false, false, false, m4_traceoff }, { "traceon", false, false, false, m4_traceon }, { "translit", false, false, true, m4_translit }, - { "undefine", false, false, true, m4_undefine }, + { "undefine", false, true, true, m4_undefine }, { "undivert", false, false, false, m4_undivert }, { 0, false, false, false, 0 }, @@ -441,6 +443,7 @@ define_user_macro (const char *name, size_t len, const char *text, SYMBOL_TYPE (s) = TOKEN_TEXT; SYMBOL_TEXT (s) = defn; + SYMBOL_MACRO_ARGS (s) = true; /* Implement --warn-macro-sequence. */ if (macro_sequence_inuse && text) @@ -694,11 +697,15 @@ m4_define (struct obstack *obs, int argc, macro_arguments *argv) static void m4_undefine (struct obstack *obs, int argc, macro_arguments *argv) { + const char *me = ARG (0); int i; - if (bad_argc (ARG (0), argc, 1, -1)) + if (bad_argc (me, argc, 1, -1)) return; for (i = 1; i < argc; i++) - lookup_symbol (ARG (i), SYMBOL_DELETE); + if (arg_type (argv, i) != TOKEN_TEXT) + m4_warn (0, me, _("invalid macro name ignored")); + else + lookup_symbol (ARG (i), SYMBOL_DELETE); } static void @@ -710,11 +717,15 @@ m4_pushdef (struct obstack *obs, int argc, macro_arguments *argv) static void m4_popdef (struct obstack *obs, int argc, macro_arguments *argv) { + const char *me = ARG (0); int i; - if (bad_argc (ARG (0), argc, 1, -1)) + if (bad_argc (me, argc, 1, -1)) return; for (i = 1; i < argc; i++) - lookup_symbol (ARG (i), SYMBOL_POPDEF); + if (arg_type (argv, i) != TOKEN_TEXT) + m4_warn (0, me, _("invalid macro name ignored")); + else + lookup_symbol (ARG (i), SYMBOL_POPDEF); } /*---------------------. @@ -724,10 +735,17 @@ m4_popdef (struct obstack *obs, int argc, macro_arguments *argv) static void m4_ifdef (struct obstack *obs, int argc, macro_arguments *argv) { + const char *me = ARG (0); symbol *s; - if (bad_argc (ARG (0), argc, 2, 3)) + if (bad_argc (me, argc, 2, 3)) return; + if (arg_type (argv, 1) != TOKEN_TEXT) + { + m4_warn (0, me, _("invalid macro name ignored")); + push_arg (obs, argv, 3); + return; + } s = lookup_symbol (ARG (1), SYMBOL_LOOKUP); push_arg (obs, argv, (s && SYMBOL_TYPE (s) != TOKEN_VOID) ? 2 : 3); } @@ -839,6 +857,11 @@ m4_dumpdef (struct obstack *obs, int argc, macro_arguments *argv) { for (i = 1; i < argc; i++) { + if (arg_type (argv, i) != TOKEN_TEXT) + { + m4_warn (0, me, _("invalid macro name ignored")); + continue; + } s = lookup_symbol (ARG (i), SYMBOL_LOOKUP); if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID) dump_symbol (s, &data); @@ -973,6 +996,11 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv) for (i = 1; i < argc; i++) { + if (arg_type (argv, i) != TOKEN_TEXT) + { + m4_warn (0, me, _("invalid macro name ignored")); + continue; + } s = lookup_symbol (ARG (i), SYMBOL_LOOKUP); if (s == NULL) continue; @@ -991,10 +1019,8 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv) m4_warn (0, me, _("builtin `%s' requested by frozen file not found"), ARG (i)); - else if (argc != 2) - m4_warn (0, me, _("cannot concatenate builtin `%s'"), ARG (i)); else - push_macro (b); + push_macro (obs, b); break; default: @@ -1294,10 +1320,10 @@ m4_dnl (struct obstack *obs, int argc, macro_arguments *argv) skip_line (me); } -/*-------------------------------------------------------------------------. -| Shift all argument one to the left, discarding the first argument. Each | -| output argument is quoted with the current quotes. | -`-------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------. +| Shift all arguments one to the left, discarding the first | +| argument. Each output argument is quoted with the current quotes. | +`--------------------------------------------------------------------*/ static void m4_shift (struct obstack *obs, int argc, macro_arguments *argv) @@ -1580,25 +1606,62 @@ m4_m4exit (struct obstack *obs, int argc, macro_arguments *argv) exit (exit_code); } -/*-------------------------------------------------------------------------. -| Save the argument text until EOF has been seen, allowing for user | -| specified cleanup action. GNU version saves all arguments, the standard | -| version only the first. | -`-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------. +| Save the argument text until EOF has been seen, allowing for user | +| specified cleanup action. FIFO determines whether text is | +| appended (per POSIX) or prepended (per older versions of GNU M4). | +| When obeying POSIX, save only the first argument; otherwise | +| concatenate additional arguments with a space. | +`-------------------------------------------------------------------*/ static void -m4_m4wrap (struct obstack *obs, int argc, macro_arguments *argv) +m4_wrap_helper (int argc, macro_arguments *argv, bool fifo) { + int i; + struct obstack *obs; + if (bad_argc (ARG (0), argc, 1, -1)) return; - obs = push_wrapup_init (); - if (no_gnu_extensions) + obs = push_wrapup_init (fifo); + if (arg_type (argv, 1) == TOKEN_TEXT) obstack_grow (obs, ARG (1), arg_len (argv, 1)); else - // TODO - allow builtins, rather than always flattening - arg_print (obs, argv, 1, NULL, true, false, " ", NULL); + push_macro (obs, arg_func (argv, 1)); + if (!no_gnu_extensions) + for (i = 2; i < argc; i++) + { + obstack_1grow (obs, ' '); + if (arg_type (argv, i) == TOKEN_TEXT) + obstack_grow (obs, ARG (i), arg_len (argv, i)); + else + push_macro (obs, arg_func (argv, i)); + } push_wrapup_finish (); } + +/*-----------------------------------------------------------------. +| Save the argument text in FIFO order until EOF has been seen, | +| allowing for user specified cleanup action. Extra arguments are | +| saved when not in POSIX mode. | +`-----------------------------------------------------------------*/ + +static void +m4_m4wrap (struct obstack *obs, int argc, macro_arguments *argv) +{ + m4_wrap_helper (argc, argv, true); +} + +/*-----------------------------------------------------------------. +| Save the argument text in LIFO order until EOF has been seen, | +| allowing for user specified cleanup action. Only available as a | +| GNU extension. | +`-----------------------------------------------------------------*/ + +static void +m4_m4parw (struct obstack *obs, int argc, macro_arguments *argv) +{ + m4_wrap_helper (argc, argv, false); +} /* Enable tracing of all specified macros, or all, if none is specified. Tracing is disabled by default, when a macro is defined. This can be diff --git a/src/input.c b/src/input.c index d1ddf25..94a427d 100644 --- a/src/input.c +++ b/src/input.c @@ -70,10 +70,9 @@ enum input_type { - INPUT_STRING, /* String resulting from macro expansion. */ - INPUT_FILE, /* File from command line or include. */ - INPUT_MACRO, /* Builtin resulting from defn. */ - INPUT_CHAIN /* FIFO chain of separate strings and $@ refs. */ + INPUT_STRING, /* String resulting from macro expansion. */ + INPUT_FILE, /* File from command line or include. */ + INPUT_CHAIN /* FIFO chain of separate strings, builtins, and $@ refs. */ }; /* Using an enum as a bitfield is not portable. For nicer debugging, @@ -108,7 +107,6 @@ struct input_block bool_bitfield advance : 1; /* track previous start_of_input_line */ } u_f; /* INPUT_FILE */ - builtin_func *func; /* INPUT_MACRO */ struct { token_chain *chain; /* current link in chain */ @@ -237,33 +235,65 @@ push_file (FILE *fp, const char *title, bool close) isp = i; } -/*---------------------------------------------------------------. -| push_macro () pushes a builtin macro's definition on the input | -| stack. If next is non-NULL, this push invalidates a call to | -| push_string_init (), whose storage is consequently released. | -`---------------------------------------------------------------*/ - -void -push_macro (builtin_func *func) +/*------------------------------------------------------------------. +| Given an obstack OBS, capture any unfinished text as a link, then | +| append the builtin FUNC as the next link in the input chain that | +| starts at *START and ends at *END. | +`------------------------------------------------------------------*/ +static void +append_macro (struct obstack *obs, builtin_func *func, token_chain **start, + token_chain **end) { - input_block *i; + token_chain *chain; + size_t len = obstack_object_size (obs); - if (next != NULL) + if (len) { - obstack_free (current_input, next); - next = NULL; + char *str = (char *) obstack_finish (obs); + chain = (token_chain *) obstack_alloc (obs, sizeof *chain); + if (*end) + (*end)->next = chain; + else + *start = chain; + *end = chain; + chain->next = NULL; + chain->type = CHAIN_STR; + chain->quote_age = 0; + chain->u.u_s.str = str; + chain->u.u_s.len = len; + chain->u.u_s.level = -1; } - i = (input_block *) obstack_alloc (current_input, sizeof *i); - i->type = INPUT_MACRO; - i->cache = false; - i->file = current_file; - i->line = current_line; - input_change = true; + chain = (token_chain *) obstack_alloc (obs, sizeof *chain); + if (*end) + (*end)->next = chain; + else + *start = chain; + *end = chain; + chain->next = NULL; + chain->type = CHAIN_FUNC; + chain->quote_age = 0; + chain->u.func = func; +} - i->u.func = func; - i->prev = isp; - isp = i; +/*-------------------------------------------------------------------. +| push_macro () pushes a builtin macro's definition onto the obstack | +| OBS, which is either the input or wrapup stack. | +`-------------------------------------------------------------------*/ + +void +push_macro (struct obstack *obs, builtin_func *func) +{ + input_block *block = (obs == current_input ? next : wsp); + assert (block); + if (block->type == INPUT_STRING) + { + block->type = INPUT_CHAIN; + block->u.u_c.chain = block->u.u_c.end = NULL; + } + else + assert (block->type == INPUT_CHAIN); + append_macro (obs, func, &block->u.u_c.chain, &block->u.u_c.end); } /*------------------------------------------------------------------. @@ -327,6 +357,17 @@ push_token (token_data *token, int level, bool inuse) return false; } } + else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC) + { + if (next->type == INPUT_STRING) + { + next->type = INPUT_CHAIN; + next->u.u_c.chain = next->u.u_c.end = NULL; + } + append_macro (current_input, TOKEN_DATA_FUNC (token), &next->u.u_c.chain, + &next->u.u_c.end); + return false; + } else { /* For composite tokens, if argv is already in use, creating @@ -391,6 +432,13 @@ push_token (token_data *token, int level, bool inuse) } while (src_chain) { + if (src_chain->type == CHAIN_FUNC) + { + append_macro (current_input, src_chain->u.func, &next->u.u_c.chain, + &next->u.u_c.end); + src_chain = src_chain->next; + continue; + } if (level == -1) { /* Nothing to copy, since link already lives on obstack. */ @@ -507,20 +555,57 @@ push_string_finish (void) /*--------------------------------------------------------------. | The function push_wrapup_init () returns an obstack ready for | | direct expansion of wrapup text, and should be followed by | -| push_wrapup_finish (). | +| push_wrapup_finish (). If FIFO, then wrap new text after any | +| previously wrapped text. | `--------------------------------------------------------------*/ struct obstack * -push_wrapup_init (void) +push_wrapup_init (bool fifo) { input_block *i; - i = (input_block *) obstack_alloc (wrapup_stack, sizeof *i); - i->prev = wsp; - i->type = INPUT_STRING; - i->cache = false; - i->file = current_file; - i->line = current_line; - wsp = i; + token_chain *chain; + + assert (obstack_object_size (wrapup_stack) == 0); + if (fifo && wsp) + { + i = wsp; + if (i->type == INPUT_STRING) + { + chain = (token_chain *) obstack_alloc (wrapup_stack, sizeof *chain); + chain->next = NULL; + chain->type = CHAIN_STR; + chain->quote_age = 0; + chain->u.u_s.str = i->u.u_s.string; + chain->u.u_s.len = i->u.u_s.len; + chain->u.u_s.level = -1; + i->type = INPUT_CHAIN; + i->u.u_c.chain = i->u.u_c.end = chain; + } + assert (i->type == INPUT_CHAIN); + if (i->u.u_c.end->type == CHAIN_LOC) + chain = i->u.u_c.end; + else + { + chain = (token_chain *) obstack_alloc (wrapup_stack, sizeof *chain); + i->u.u_c.end->next = chain; + i->u.u_c.end = chain; + chain->next = NULL; + chain->type = CHAIN_LOC; + chain->quote_age = 0; + } + chain->u.u_l.file = current_file; + chain->u.u_l.line = current_line; + } + else + { + i = (input_block *) obstack_alloc (wrapup_stack, sizeof *i); + i->prev = wsp; + i->type = INPUT_STRING; + i->cache = false; + i->file = current_file; + i->line = current_line; + wsp = i; + } return wrapup_stack; } @@ -532,15 +617,40 @@ void push_wrapup_finish (void) { input_block *i = wsp; - if (obstack_object_size (wrapup_stack) == 0) + size_t len = obstack_object_size (wrapup_stack); + char *str; + + if (len) + str = (char *) obstack_finish (wrapup_stack); + if (str || i->type == INPUT_CHAIN) { - wsp = i->prev; - obstack_free (wrapup_stack, i); + if (i->type == INPUT_STRING) + { + i->u.u_s.string = str; + i->u.u_s.len = len; + } + else + { + if (str) + { + token_chain *chain + = (token_chain *) obstack_alloc (wrapup_stack, sizeof *chain); + assert (i->u.u_c.end); + i->u.u_c.end->next = chain; + i->u.u_c.end = chain; + chain->next = NULL; + chain->type = CHAIN_STR; + chain->quote_age = 0; + chain->u.u_s.str = str; + chain->u.u_s.len = len; + chain->u.u_s.level = -1; + } + } } else { - i->u.u_s.len = obstack_object_size (wrapup_stack); - i->u.u_s.string = (char *) obstack_finish (wrapup_stack); + wsp = i->prev; + obstack_free (wrapup_stack, i); } } @@ -568,11 +678,6 @@ pop_input (bool cleanup) return false; break; - case INPUT_MACRO: - if (!cleanup) - return false; - break; - case INPUT_CHAIN: chain = isp->u.u_c.chain; assert (!chain || !cleanup); @@ -586,12 +691,18 @@ pop_input (bool cleanup) if (chain->u.u_s.level >= 0) adjust_refcount (chain->u.u_s.level, false); break; + case CHAIN_FUNC: + if (chain->u.func) + return false; + break; case CHAIN_ARGV: if (chain->u.u_a.comma || chain->u.u_a.index < arg_argc (chain->u.u_a.argv)) return false; arg_adjust_refcount (chain->u.u_a.argv, false); break; + case CHAIN_LOC: + return false; default: assert (!"pop_input"); abort (); @@ -693,9 +804,6 @@ input_print (struct obstack *obs, const input_block *input) obstack_grow (obs, input->file, strlen (input->file)); obstack_1grow (obs, '>'); break; - case INPUT_MACRO: - func_print (obs, find_builtin_by_addr (input->u.func), false, NULL); - break; case INPUT_CHAIN: { token_chain *chain = input->u.u_c.chain; @@ -708,6 +816,10 @@ input_print (struct obstack *obs, const input_block *input) &len)) return; break; + case CHAIN_FUNC: + func_print (obs, find_builtin_by_addr (chain->u.func), false, + NULL); + break; case CHAIN_ARGV: if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, @@ -769,9 +881,6 @@ peek_input (bool allow_argv) block->u.u_f.end = true; break; - case INPUT_MACRO: - return CHAR_MACRO; - case INPUT_CHAIN: chain = block->u.u_c.chain; while (chain) @@ -783,6 +892,10 @@ peek_input (bool allow_argv) if (chain->u.u_s.len) return to_uchar (*chain->u.u_s.str); break; + case CHAIN_FUNC: + if (chain->u.func) + return CHAR_MACRO; + break; case CHAIN_ARGV: /* Rather than directly parse argv here, we push another input block containing the next unparsed @@ -809,6 +922,8 @@ peek_input (bool allow_argv) return peek_input (allow_argv); } break; + case CHAIN_LOC: + break; default: assert (!"peek_input"); abort (); @@ -892,11 +1007,6 @@ next_char_1 (bool allow_quote) } break; - case INPUT_MACRO: - /* INPUT_MACRO input sources has only one token */ - pop_input (true); - return CHAR_MACRO; - case INPUT_CHAIN: chain = isp->u.u_c.chain; while (chain) @@ -917,6 +1027,10 @@ next_char_1 (bool allow_quote) if (chain->u.u_s.level >= 0) adjust_refcount (chain->u.u_s.level, false); break; + case CHAIN_FUNC: + if (chain->u.func) + return CHAR_MACRO; + break; case CHAIN_ARGV: /* Rather than directly parse argv here, we push another input block containing the next unparsed @@ -941,6 +1055,12 @@ next_char_1 (bool allow_quote) } arg_adjust_refcount (chain->u.u_a.argv, false); break; + case CHAIN_LOC: + isp->file = chain->u.u_l.file; + isp->line = chain->u.u_l.line; + input_change = true; + isp->u.u_c.chain = chain->next; + return next_char_1 (allow_quote); default: assert (!"next_char_1"); abort (); @@ -987,17 +1107,24 @@ skip_line (const char *name) input_change = true; } -/*-------------------------------------------------------------------. -| When a MACRO token is seen, next_token () uses init_macro_token () | -| to retrieve the value of the function pointer. | -`-------------------------------------------------------------------*/ +/*--------------------------------------------------------------. +| When a next_token() sees a MACRO token with peek_input, this | +| retrieves the value of the function pointer, and consumes the | +| input so the caller does not need to do next_char. | +`--------------------------------------------------------------*/ static void init_macro_token (token_data *td) { - assert (isp->type == INPUT_MACRO); TOKEN_DATA_TYPE (td) = TOKEN_FUNC; - TOKEN_DATA_FUNC (td) = isp->u.func; + token_chain *chain; + + next_char (false); + assert (isp->type == INPUT_CHAIN); + chain = isp->u.u_c.chain; + assert (!chain->quote_age && chain->type == CHAIN_FUNC); + TOKEN_DATA_FUNC (td) = chain->u.func; + chain->u.func = NULL; } /*-------------------------------------------------------------------. @@ -1537,7 +1664,6 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, if (ch == CHAR_MACRO) { init_macro_token (td); - next_char (false); #ifdef DEBUG_INPUT xfprintf (stderr, "next_token -> MACDEF (%s)\n", find_builtin_by_addr (TOKEN_DATA_FUNC (td))->name); @@ -1564,20 +1690,31 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, if (obs) obs_td = obs; obstack_grow (obs_td, curr_comm.str1, curr_comm.len1); - while ((ch = next_char (false)) < CHAR_EOF - && !MATCH (ch, curr_comm.str2, true)) - obstack_1grow (obs_td, ch); - if (ch != CHAR_EOF) + while (1) { + ch = next_char (false); + if (ch == CHAR_EOF) + /* current_file changed to "" if we see CHAR_EOF, use the + previous value we stored earlier. */ + m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller, + _("end of file in comment")); + if (ch == CHAR_MACRO) + { + token_data tmp; + // TODO support concatenation of builtins + m4_warn_at_line (0, file, *line, caller, + _("cannot comment builtin")); + init_macro_token (&tmp); + continue; + } + if (MATCH (ch, curr_comm.str2, true)) + { + obstack_grow (obs_td, curr_comm.str2, curr_comm.len2); + break; + } assert (ch < CHAR_EOF); - obstack_grow (obs_td, curr_comm.str2, curr_comm.len2); + obstack_1grow (obs_td, ch); } - else - /* current_file changed to "" if we see CHAR_EOF, use the - previous value we stored earlier. */ - m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller, - _("end of file in comment")); - type = TOKEN_STRING; } else if (default_word_regexp && (isalpha (ch) || ch == '_')) @@ -1633,6 +1770,7 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, else if (!MATCH (ch, curr_quote.str1, true)) { + assert (ch < CHAR_EOF); switch (ch) { case '(': @@ -1655,6 +1793,7 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, if (obs) obs_td = obs; quote_level = 1; + type = TOKEN_STRING; while (1) { ch = next_char (obs != NULL && current_quote_age); @@ -1664,6 +1803,36 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, m4_error_at_line (EXIT_FAILURE, 0, file, *line, caller, _("end of file in string")); + if (ch == CHAR_MACRO) + { + token_data tmp; + // 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); + MATCH (ch, curr_quote.str2, true); + return TOKEN_MACDEF; + } + TOKEN_DATA_TYPE (td) = TOKEN_VOID; + } + else + init_macro_token (&tmp); + m4_warn_at_line (0, file, *line, caller, + _("cannot quote builtin")); + continue; + } if (ch == CHAR_QUOTE) append_quote_token (obs, td); else if (MATCH (ch, curr_quote.str2, true)) @@ -1683,7 +1852,6 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, obstack_1grow (obs_td, ch); } } - type = TOKEN_STRING; } if (TOKEN_DATA_TYPE (td) == TOKEN_VOID) @@ -1729,6 +1897,9 @@ next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, xfprintf (stderr, "%s", chain->u.u_s.str); len += chain->u.u_s.len; break; + case CHAIN_FUNC: + xfprintf (stderr, "<func>"); + break; case CHAIN_ARGV: xfprintf (stderr, "[EMAIL PROTECTED]"); break; diff --git a/src/m4.h b/src/m4.h index 712d32f..3f4e648 100644 --- a/src/m4.h +++ b/src/m4.h @@ -279,8 +279,9 @@ enum token_data_type enum token_chain_type { CHAIN_STR, /* Link contains a string, u.u_s is valid. */ - // TODO add CHAIN_FUNC - CHAIN_ARGV /* Link contains a $@ reference, u.u_a is valid. */ + CHAIN_FUNC, /* Builtin function definition, u.func is valid. */ + CHAIN_ARGV, /* Link contains a $@ reference, u.u_a is valid. */ + CHAIN_LOC /* Link contains location of m4wrap, u.u_l is valid. */ }; /* Composite tokens are built of a linked list of chains. Each link @@ -300,6 +301,7 @@ struct token_chain int level; /* Expansion level of link content, or -1. */ } u_s; + builtin_func *func; /* Builtin token from defn. */ struct { macro_arguments *argv; /* Reference to earlier [EMAIL PROTECTED] */ @@ -310,6 +312,12 @@ struct token_chain const string_pair *quotes; /* NULL for $*, quotes for [EMAIL PROTECTED] */ } u_a; + struct + { + const char *file; /* File where subsequent links originate. */ + int line; /* Line where subsequent links originate. */ + } + u_l; } u; }; @@ -377,11 +385,11 @@ void skip_line (const char *); /* push back input */ void push_file (FILE *, const char *, bool); -void push_macro (builtin_func *); +void push_macro (struct obstack *, builtin_func *); struct obstack *push_string_init (void); bool push_token (token_data *, int, bool); const input_block *push_string_finish (void); -struct obstack *push_wrapup_init (void); +struct obstack *push_wrapup_init (bool); void push_wrapup_finish (void); bool pop_wrapup (void); void input_print (struct obstack *, const input_block *); diff --git a/src/macro.c b/src/macro.c index bb7aad5..3625717 100644 --- a/src/macro.c +++ b/src/macro.c @@ -49,6 +49,8 @@ struct macro_arguments /* False if all arguments belong to this argv, true if some of them include references to another. */ bool_bitfield has_ref : 1; + /* True to flatten builtins contained in references. */ + bool_bitfield flatten : 1; const char *argv0; /* The macro name being expanded. */ size_t argv0_len; /* Length of argv0. */ /* The value of quote_age used when parsing all arguments in this @@ -476,6 +478,7 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) assert (paren_level == 0 && TOKEN_DATA_TYPE (argp) == TOKEN_VOID && obstack_object_size (obs) == 0 && td.u.u_c.chain == td.u.u_c.end + && td.u.u_c.chain->quote_age == age && td.u.u_c.chain->type == CHAIN_ARGV); TOKEN_DATA_TYPE (argp) = TOKEN_COMP; argp->u.u_c.chain = argp->u.u_c.end = td.u.u_c.chain; @@ -516,6 +519,7 @@ collect_arguments (symbol *sym, struct obstack *arguments, args.inuse = false; args.wrapper = false; args.has_ref = false; + args.flatten = !groks_macro_args; args.argv0 = SYMBOL_NAME (sym); args.argv0_len = strlen (args.argv0); args.quote_age = quote_age (); @@ -811,19 +815,24 @@ 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). */ + 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) +arg_token (macro_arguments *argv, unsigned int index, int *level, bool flatten) { unsigned int i; token_data *token; assert (index && index < argv->argc); + flatten |= argv->flatten; if (!argv->wrapper) { if (level) *level = argv->level; - return argv->array[index - 1]; + token = argv->array[index - 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 may occupy multiple indices. */ @@ -838,10 +847,8 @@ arg_token (macro_arguments *argv, unsigned int index, int *level) + (chain->u.u_a.skip_last ? 0 : 1))) { token = arg_token (chain->u.u_a.argv, - chain->u.u_a.index - 1 + index, level); - if (chain->u.u_a.flatten - && TOKEN_DATA_TYPE (token) == TOKEN_FUNC) - token = &empty_token; + chain->u.u_a.index - 1 + index, level, + flatten || chain->u.u_a.flatten); break; } index -= (chain->u.u_a.argv->argc - chain->u.u_a.index @@ -874,7 +881,7 @@ arg_type (macro_arguments *argv, unsigned int index) if (index == 0 || index >= argv->argc) return TOKEN_TEXT; - token = arg_token (argv, index, NULL); + token = arg_token (argv, index, NULL, false); type = TOKEN_DATA_TYPE (token); /* When accessed via the arg_* interface, composite tokens are currently sequences of text only. */ @@ -883,7 +890,7 @@ arg_type (macro_arguments *argv, unsigned int index) return type; } -/* Given ARGV, return the text at argument INDEX, or NULL if the +/* 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. */ const char * @@ -897,13 +904,11 @@ arg_text (macro_arguments *argv, unsigned int index) return argv->argv0; if (index >= argv->argc) return ""; - token = arg_token (argv, index, NULL); + token = arg_token (argv, index, NULL, false); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: return TOKEN_DATA_TEXT (token); - case TOKEN_FUNC: - return NULL; case TOKEN_COMP: // TODO - concatenate functions? chain = token->u.u_c.chain; @@ -916,12 +921,16 @@ 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: + // TODO concatenate builtins + assert (!"implemented"); + abort (); case CHAIN_ARGV: arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), - chain->u.u_a.flatten, chain->u.u_a.skip_last, NULL, - NULL); + argv->flatten || chain->u.u_a.flatten, + chain->u.u_a.skip_last, NULL, NULL); break; default: assert (!"arg_text"); @@ -931,6 +940,7 @@ arg_text (macro_arguments *argv, unsigned int index) } obstack_1grow (obs, '\0'); return (char *) obstack_finish (obs); + case TOKEN_FUNC: default: break; } @@ -945,8 +955,8 @@ arg_text (macro_arguments *argv, unsigned int index) bool arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) { - token_data *ta = arg_token (argv, indexa, NULL); - token_data *tb = arg_token (argv, indexb, NULL); + token_data *ta = arg_token (argv, indexa, NULL, false); + token_data *tb = arg_token (argv, indexb, NULL, false); token_chain tmpa; token_chain tmpb; token_chain *ca = &tmpa; @@ -963,30 +973,45 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) TOKEN_DATA_LEN (ta)) == 0); /* Convert both arguments to chains, if not one already. */ - // TODO - allow builtin tokens in the comparison? - if (TOKEN_DATA_TYPE (ta) == TOKEN_TEXT) + switch (TOKEN_DATA_TYPE (ta)) { + case TOKEN_TEXT: tmpa.next = NULL; tmpa.type = CHAIN_STR; tmpa.u.u_s.str = TOKEN_DATA_TEXT (ta); tmpa.u.u_s.len = TOKEN_DATA_LEN (ta); - } - else - { - assert (TOKEN_DATA_TYPE (ta) == TOKEN_COMP); + break; + case TOKEN_FUNC: + tmpa.next = NULL; + tmpa.type = CHAIN_FUNC; + tmpa.u.func = TOKEN_DATA_FUNC (ta); + break; + case TOKEN_COMP: ca = ta->u.u_c.chain; + break; + default: + assert (!"arg_equal"); + abort (); } - if (TOKEN_DATA_TYPE (tb) == TOKEN_TEXT) + switch (TOKEN_DATA_TYPE (tb)) { + case TOKEN_TEXT: tmpb.next = NULL; tmpb.type = CHAIN_STR; tmpb.u.u_s.str = TOKEN_DATA_TEXT (tb); tmpb.u.u_s.len = TOKEN_DATA_LEN (tb); - } - else - { - assert (TOKEN_DATA_TYPE (tb) == TOKEN_COMP); + break; + case TOKEN_FUNC: + tmpb.next = NULL; + tmpb.type = CHAIN_FUNC; + tmpb.u.func = TOKEN_DATA_FUNC (tb); + break; + case TOKEN_COMP: cb = tb->u.u_c.chain; + break; + default: + assert (!"arg_equal"); + abort (); } /* Compare each link of the chain. */ @@ -1016,7 +1041,14 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) cb = &tmpb; continue; } - // TODO support comparison against $@ refs. + if (ca->type == CHAIN_FUNC) + { + if (cb->type != CHAIN_FUNC || ca->u.func != cb->u.func) + return false; + ca = ca->next; + cb = cb->next; + continue; + } assert (ca->type == CHAIN_STR && cb->type == CHAIN_STR); if (ca->u.u_s.len == cb->u.u_s.len) { @@ -1066,10 +1098,10 @@ arg_empty (macro_arguments *argv, unsigned int index) return argv->argv0_len == 0; if (index >= argv->argc) return true; - return arg_token (argv, index, NULL) == &empty_token; + return arg_token (argv, index, NULL, false) == &empty_token; } -/* Given ARGV, return the length of argument INDEX, or SIZE_MAX if the +/* Given ARGV, return the length of argument INDEX. Abort if the argument is not text. Indices beyond argc return 0. */ size_t arg_len (macro_arguments *argv, unsigned int index) @@ -1082,14 +1114,12 @@ arg_len (macro_arguments *argv, unsigned int index) return argv->argv0_len; if (index >= argv->argc) return 0; - token = arg_token (argv, index, NULL); + token = arg_token (argv, index, NULL, false); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: assert ((token == &empty_token) == (TOKEN_DATA_LEN (token) == 0)); return TOKEN_DATA_LEN (token); - case TOKEN_FUNC: - return SIZE_MAX; case TOKEN_COMP: // TODO - concatenate functions? chain = token->u.u_c.chain; @@ -1104,6 +1134,10 @@ arg_len (macro_arguments *argv, unsigned int index) case CHAIN_STR: len += chain->u.u_s.len; break; + case CHAIN_FUNC: + // TODO concatenate builtins + assert (!"implemented"); + abort (); case CHAIN_ARGV: i = chain->u.u_a.index; limit = (chain->u.u_a.argv->argc - i @@ -1114,7 +1148,14 @@ arg_len (macro_arguments *argv, unsigned int index) len += ((quotes ? (quotes->len1 + quotes->len2 + 1) : 1) * limit - 1); while (limit--) - len += arg_len (chain->u.u_a.argv, i++); + { + if (TOKEN_DATA_TYPE (arg_token (chain->u.u_a.argv, i, NULL, + false)) == TOKEN_FUNC) + assert (argv->flatten); + else + len += arg_len (chain->u.u_a.argv, i); + i++; + } break; default: assert (!"arg_len"); @@ -1124,6 +1165,7 @@ arg_len (macro_arguments *argv, unsigned int index) } assert (len); return len; + case TOKEN_FUNC: default: break; } @@ -1141,7 +1183,7 @@ arg_func (macro_arguments *argv, unsigned int index) if (index == 0 || index >= argv->argc) return NULL; - token = arg_token (argv, index, NULL); + token = arg_token (argv, index, NULL, false); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_FUNC: @@ -1197,7 +1239,7 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, return true; else use_sep = true; - token = arg_token (argv, i, NULL); + token = arg_token (argv, i, NULL, flatten); switch (TOKEN_DATA_TYPE (token)) { case TOKEN_TEXT: @@ -1222,6 +1264,10 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, &len)) return true; break; + case CHAIN_FUNC: + func_print (obs, find_builtin_by_addr (chain->u.func), + flatten, quotes); + break; case CHAIN_ARGV: if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, @@ -1387,6 +1433,7 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, new_argv->arraylen = 0; new_argv->wrapper = false; new_argv->has_ref = false; + new_argv->flatten = false; } else { @@ -1396,6 +1443,7 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, new_argv->array[0] = token; new_argv->wrapper = true; new_argv->has_ref = argv->has_ref; + new_argv->flatten = flatten; } new_argv->argc = argv->argc - (index - 1); new_argv->inuse = false; @@ -1432,9 +1480,9 @@ push_arg_quote (struct obstack *obs, macro_arguments *argv, unsigned int index, const string_pair *quotes) { int level; - token_data *token = arg_token (argv, index, &level); + token_data *token = arg_token (argv, index, &level, false); - // TODO handle func tokens? + // TODO handle func tokens inside quotes? if (quotes) obstack_grow (obs, quotes->str1, quotes->len1); argv->inuse |= push_token (token, level, argv->inuse); @@ -1464,8 +1512,7 @@ push_args (struct obstack *obs, macro_arguments *argv, bool skip, bool quote) return; } - // TODO allow shift, $@, to push builtins without flatten? - token = make_argv_ref_token (&td, obs, -1, argv, i, true, + token = make_argv_ref_token (&td, obs, -1, argv, i, argv->flatten, quote ? &curr_quote : NULL); assert (token); argv->inuse |= push_token (token, -1, argv->inuse); hooks/post-receive -- GNU M4 source repository
