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=e2b843f0aca9e296495f592e39d6a8cec69c6e8a The branch, branch-1_4 has been updated via e2b843f0aca9e296495f592e39d6a8cec69c6e8a (commit) from c947bf47f48f9132155bc623596771e4a4e3f732 (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 e2b843f0aca9e296495f592e39d6a8cec69c6e8a Author: Eric Blake <[EMAIL PROTECTED]> Date: Fri Oct 26 09:24:06 2007 -0600 Stage 9: share rather than copy single-arg refs. * m4/gnulib-cache.m4: Import quote and memmem modules. * src/m4.h (arg_scratch): New prototype. * src/input.c (INPUT_INLINE_THRESHOLD): New define. (push_token): Use it to inline short text, and save references to longer text. (input_init, next_token): Simplify obstack handling. * src/macro.c (expand_argument): Likewise. (expand_macro): Track scratch space. (arg_scratch): New function. (make_argv_ref): Use it. (push_args): Likewise, and simplify comma handling, since most separators are short enough to be inlined. * src/builtin.c (builtin_init): Avoid cast. (m4_errprint, m4_index): Transparently support NUL. (m4_translit): Use scratch space, rather than leaking memory on expansion stack. * doc/m4.texinfo (Syntax): Add new test of embedded NUL. * checks/get-them: Extract test that uses external files. * checks/check-them: Run the new test. Use unified diff if possible, and force text mode when debugging NUL handling. * examples/null.m4: New file. * examples/null.out: Likewise. * examples/null.err: Likewise. * examples/Makefile.am (EXTRA_FILES): Distribute these files. * .gitattributes: Treat new files as text, in spite of embedded NUL. (cherry picked from commit a6c94a314afa34958330b719d66c2d4e403a94af) Signed-off-by: Eric Blake <[EMAIL PROTECTED]> ----------------------------------------------------------------------- Summary of changes: ChangeLog | 30 +++++++++++++++++++ checks/check-them | 30 ++++++++++++++++--- checks/get-them | 20 +++++++++++++ doc/m4.texinfo | 12 ++++++++ examples/.gitattributes | 1 + examples/Makefile.am | 3 ++ examples/null.err | Bin 0 -> 18 bytes examples/null.m4 | Bin 0 -> 3671 bytes examples/null.out | Bin 0 -> 338 bytes m4/gnulib-cache.m4 | 4 +- src/builtin.c | 37 ++++++++++------------- src/input.c | 47 +++++++++++++++++++------------ src/m4.h | 1 + src/macro.c | 72 ++++++++++++++++++++++++++++------------------ 14 files changed, 183 insertions(+), 74 deletions(-) create mode 100644 examples/.gitattributes create mode 100644 examples/null.err create mode 100644 examples/null.m4 create mode 100644 examples/null.out diff --git a/ChangeLog b/ChangeLog index 2b2a0d0..e6fee41 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2007-12-21 Eric Blake <[EMAIL PROTECTED]> + + Stage 9: share rather than copy single-arg refs. + * m4/gnulib-cache.m4: Import quote and memmem modules. + * src/m4.h (arg_scratch): New prototype. + * src/input.c (INPUT_INLINE_THRESHOLD): New define. + (push_token): Use it to inline short text, and save references to + longer text. + (input_init, next_token): Simplify obstack handling. + * src/macro.c (expand_argument): Likewise. + (expand_macro): Track scratch space. + (arg_scratch): New function. + (make_argv_ref): Use it. + (push_args): Likewise, and simplify comma handling, since most + separators are short enough to be inlined. + * src/builtin.c (builtin_init): Avoid cast. + (m4_errprint, m4_index): Transparently support NUL. + (m4_translit): Use scratch space, rather than leaking memory on + expansion stack. + * doc/m4.texinfo (Syntax): Add new test of embedded NUL. + * checks/get-them: Extract test that uses external files. + * checks/check-them: Run the new test. Use unified diff if + possible, and force text mode when debugging NUL handling. + * examples/null.m4: New file. + * examples/null.out: Likewise. + * examples/null.err: Likewise. + * examples/Makefile.am (EXTRA_FILES): Distribute these files. + * .gitattributes: Treat new files as text, in spite of embedded + NUL. + 2007-12-18 Eric Blake <[EMAIL PROTECTED]> Stage 8: extend life of references into argv. diff --git a/checks/check-them b/checks/check-them index b446f47..daa1b00 100755 --- a/checks/check-them +++ b/checks/check-them @@ -27,6 +27,7 @@ xerr=$tmp/m4-xerr failed= skipped= strip_needed=false +diffopts=-c # Find out how the executable prints argv[0] m4=`m4 --help | sed -e 's/Usage: \(.*\) \[OPTION.*/\1/' \ @@ -49,6 +50,14 @@ if test "x$1" = x-I ; then shift; shift fi +# Find out if diff supports useful options. +if diff -u /dev/null /dev/null 2>/dev/null ; then + diffopts="-u" +fi +if diff -a /dev/null /dev/null 2>/dev/null ; then + diffopts="$diffopts -a" +fi + # Run the tests. for file do @@ -77,9 +86,20 @@ do ;; esac - sed -e '/^dnl @result{}/!d' -e 's///' -e "s|\.\./examples|$examples|" \ - "$file" > $xout - sed -e '/^dnl @error{}/!d' -e 's///' -e "s|^m4:|$m4:|" "$file" > $xerr + 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|" \ + "$file" > $xout + else + cp "$examples/$xoutfile" $xout + fi + + xerrfile=`sed -n 's/^dnl @ expected error: //p' "$file"` + if test -z "$xerrfile" ; then + sed -e '/^dnl @error{}/!d' -e 's///' -e "s|^m4:|$m4:|" "$file" > $xerr + else + cp "$examples/$xerrfile" $xerr + fi # For the benefit of mingw, normalize \r\n line endings if $strip_needed ; then @@ -99,7 +119,7 @@ do failed="$failed $file:out" echo `sed -e 's/^dnl //' -e 1q $file` echo "$file: stdout mismatch" - diff $xout $out + diff $diffopts $xout $out fi if cmp -s $err $xerr; then @@ -108,7 +128,7 @@ do failed="$failed $file:err" echo `sed -e 's/^dnl //' -e 1q $file` echo "$file: stderr mismatch" - diff $xerr $err + diff $diffopts $xerr $err fi done diff --git a/checks/get-them b/checks/get-them index 0d3e37b..e034962 100755 --- a/checks/get-them +++ b/checks/get-them @@ -16,6 +16,8 @@ BEGIN { file = "NONE"; status = 0; options = ""; + xout = ""; + xerr = ""; } /[EMAIL PROTECTED] / { @@ -39,6 +41,8 @@ BEGIN { getline; status = 0; options = ""; + xout = ""; + xout = ""; next; } @@ -51,6 +55,16 @@ BEGIN { gsub ("@comment options:", "", options); } +/[EMAIL PROTECTED] xout: / { + xout = $0; + gsub ("@comment xout: ", "", xout); +} + +/[EMAIL PROTECTED] xerr: / { + xerr = $0; + gsub ("@comment xerr: ", "", xerr); +} + /[EMAIL PROTECTED]/, /[EMAIL PROTECTED] example$/ { if (seq < 0) next; @@ -68,8 +82,14 @@ BEGIN { "dnl @ gives unlimited permission to copy and/or distribute it\n"\ "dnl @ with or without modifications, as long as this notice\n"\ "dnl @ is preserved.\n", FILENAME, NR, status, options) > file; + if (xout) + printf("dnl @ expected output: %s\n", xout) > file; + if (xerr) + printf("dnl @ expected error: %s\n", xerr) > file; status = 0; options = ""; + xout = ""; + xerr = ""; next; } if ($0 ~ /[EMAIL PROTECTED] example$/) { diff --git a/doc/m4.texinfo b/doc/m4.texinfo index eaec266..bfdb6f2 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -923,6 +923,18 @@ use [EMAIL PROTECTED] characters in quoted strings (@pxref{Changequote}), comments (@pxref{Changecom}), and macro names (@pxref{Indir}), with the exception of the @sc{nul} character (the zero byte @samp{'\0'}). [EMAIL PROTECTED] [EMAIL PROTECTED] FIXME - each builtin needs to document how it handles NUL, then [EMAIL PROTECTED] update the above paragraph to mention that NUL is now handled [EMAIL PROTECTED] transparently. Meanwhile, test that we don't regress. + [EMAIL PROTECTED] xout: null.out [EMAIL PROTECTED] xerr: null.err [EMAIL PROTECTED] +include(`null.m4')dnl [EMAIL PROTECTED] example [EMAIL PROTECTED] ignore + @menu * Names:: Macro names * Quoted strings:: Quoting input to @code{m4} diff --git a/examples/.gitattributes b/examples/.gitattributes new file mode 100644 index 0000000..b35e74c --- /dev/null +++ b/examples/.gitattributes @@ -0,0 +1 @@ +null.* diff merge diff --git a/examples/Makefile.am b/examples/Makefile.am index c1dc522..3450eac 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -45,6 +45,9 @@ indir.m4 \ loop.m4 \ misc.m4 \ multiquotes.m4 \ +null.err \ +null.m4 \ +null.out \ patsubst.m4 \ pushpop.m4 \ quote.m4 \ diff --git a/examples/null.err b/examples/null.err new file mode 100644 index 0000000..61518e8 Binary files /dev/null and b/examples/null.err differ diff --git a/examples/null.m4 b/examples/null.m4 new file mode 100644 index 0000000..904a6ef Binary files /dev/null and b/examples/null.m4 differ diff --git a/examples/null.out b/examples/null.out new file mode 100644 index 0000000..6e8a114 Binary files /dev/null and b/examples/null.out differ diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index a89650c..3112f91 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -15,11 +15,11 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer free fseeko gendocs getopt gnupload gpl-3.0 mkstemp obstack regex stdbool stdint stdlib-safer strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix +# gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer free fseeko gendocs getopt gnupload gpl-3.0 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([local]) -gl_MODULES([assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer free fseeko gendocs getopt gnupload gpl-3.0 mkstemp obstack regex stdbool stdint stdlib-safer strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix]) +gl_MODULES([assert avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer free fseeko gendocs getopt gnupload gpl-3.0 memmem mkstemp obstack quote regex stdbool stdint stdlib-safer strtol unlocked-io verror version-etc version-etc-fsf xalloc xprintf xvasprintf-posix]) gl_AVOID([]) gl_SOURCE_BASE([lib]) gl_M4_BASE([m4]) diff --git a/src/builtin.c b/src/builtin.c index e8edc4b..cb5f274 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -463,9 +463,10 @@ builtin_init (void) for (bp = &builtin_tab[0]; bp->name != NULL; bp++) if (!no_gnu_extensions || !bp->gnu_extension) { + size_t len = strlen (bp->name); if (prefix_all_builtins) { - string = (char *) xmalloc (strlen (bp->name) + 4); + string = xcharalloc (len + 4); strcpy (string, "m4_"); strcat (string, bp->name); define_builtin (string, bp, SYMBOL_INSERT); @@ -1514,12 +1515,16 @@ m4_mkstemp (struct obstack *obs, int argc, macro_arguments *argv) static void m4_errprint (struct obstack *obs, int argc, macro_arguments *argv) { + size_t len; + if (bad_argc (ARG (0), argc, 1, -1)) return; dump_args (obs, 1, argv, " ", false); - obstack_1grow (obs, '\0'); debug_flush_files (); - xfprintf (stderr, "%s", (char *) obstack_finish (obs)); + len = obstack_object_size (obs); + /* The close_stdin module makes it safe to skip checking the return + value here. */ + fwrite (obstack_finish (obs), 1, len, stderr); fflush (stderr); } @@ -1770,14 +1775,10 @@ m4_index (struct obstack *obs, int argc, macro_arguments *argv) haystack = ARG (1); needle = ARG (2); - /* Optimize searching for the empty string (always 0) and one byte - (strchr tends to be more efficient than strstr). */ - if (!needle[0]) - retval = 0; - else if (!needle[1]) - result = strchr (haystack, *needle); - else - result = strstr (haystack, needle); + /* Rely on the optimizations guaranteed by gnulib's memmem + module. */ + result = (char *) memmem (haystack, arg_len (argv, 1), + needle, arg_len (argv, 2)); if (result) retval = result - haystack; @@ -1895,19 +1896,13 @@ m4_translit (struct obstack *obs, int argc, macro_arguments *argv) from = ARG (2); if (strchr (from, '-') != NULL) - { - from = expand_ranges (from, obs); - if (from == NULL) - return; - } + from = expand_ranges (from, arg_scratch ()); to = ARG (3); if (strchr (to, '-') != NULL) - { - to = expand_ranges (to, obs); - if (to == NULL) - return; - } + to = expand_ranges (to, arg_scratch ()); + + assert (from && to); /* Calling strchr(from) for each character in data is quadratic, since both strings can be arbitrarily long. Instead, create a diff --git a/src/input.c b/src/input.c index 8386061..1753be2 100644 --- a/src/input.c +++ b/src/input.c @@ -64,6 +64,11 @@ # include "regex.h" #endif /* ENABLE_CHANGEWORD */ +/* Number of bytes where it is more efficient to inline the reference + as a string than it is to track reference bookkeeping for those + bytes. */ +#define INPUT_INLINE_THRESHOLD 16 + /* Type of an input block. */ enum input_type { @@ -319,18 +324,29 @@ push_string_init (void) | gathering input from multiple locations, rather than copying | | everything consecutively onto the input stack. Must be called | | between push_string_init and push_string_finish. Return true only | -| if LEVEL is non-negative, and a reference was created to TOKEN. | +| if LEVEL is non-negative, and a reference was created to TOKEN, in | +| which case, the lifetime of TOKEN and its contents must last as | +| long as the input engine can parse references to it. | `-------------------------------------------------------------------*/ bool push_token (token_data *token, int level) { token_chain *chain; + bool result = false; assert (next); /* TODO - also accept TOKEN_COMP chains. */ assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); - if (TOKEN_DATA_LEN (token) == 0) - return false; + + /* Speed consideration - for short enough tokens, the speed and + memory overhead of parsing another INPUT_CHAIN link outweighs the + time to inline the token text. */ + if (TOKEN_DATA_LEN (token) <= INPUT_INLINE_THRESHOLD) + { + obstack_grow (current_input, TOKEN_DATA_TEXT (token), + TOKEN_DATA_LEN (token)); + return false; + } if (next->type == INPUT_STRING) { @@ -345,21 +361,18 @@ push_token (token_data *token, int level) next->u.u_c.chain = chain; next->u.u_c.end = chain; chain->next = NULL; - if (level >= 0) - /* TODO - use token as-is, rather than copying data. This implies - lengthening lifetime of $@ arguments until the rescan is - complete, rather than the current approach of freeing them - during expand_macro. */ - chain->str = (char *) obstack_copy (current_input, TOKEN_DATA_TEXT (token), - TOKEN_DATA_LEN (token)); - else - chain->str = TOKEN_DATA_TEXT (token); + chain->str = TOKEN_DATA_TEXT (token); chain->len = TOKEN_DATA_LEN (token); - chain->level = -1; + chain->level = level; chain->argv = NULL; chain->index = 0; chain->flatten = false; - return false; /* No reference exists when text is copied. */ + if (level >= 0) + { + adjust_refcount (level, true); + result = true; + } + return result; } /*-------------------------------------------------------------------. @@ -897,8 +910,7 @@ input_init (void) will always work even if the first token parsed spills to a new chunk. */ obstack_init (&token_stack); - obstack_alloc (&token_stack, 1); - token_bottom = obstack_base (&token_stack); + token_bottom = obstack_finish (&token_stack); isp = NULL; wsp = NULL; @@ -1230,8 +1242,7 @@ next_token (token_data *td, int *line, const char *caller) if (startpos != 0 || regs.end [0] != obstack_object_size (&token_stack)) { - *(((char *) obstack_base (&token_stack) - + obstack_object_size (&token_stack)) - 1) = '\0'; + obstack_blank (&token_stack, -1); break; } next_char (); diff --git a/src/m4.h b/src/m4.h index 854f28d..ce78455 100644 --- a/src/m4.h +++ b/src/m4.h @@ -449,6 +449,7 @@ bool arg_equal (macro_arguments *, unsigned int, unsigned int); bool arg_empty (macro_arguments *, unsigned int); size_t arg_len (macro_arguments *, unsigned int); builtin_func *arg_func (macro_arguments *, unsigned int); +struct obstack *arg_scratch (void); macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t, bool, bool); void push_arg (struct obstack *, macro_arguments *, unsigned int); diff --git a/src/macro.c b/src/macro.c index 63d957d..964be5b 100644 --- a/src/macro.c +++ b/src/macro.c @@ -29,7 +29,10 @@ #endif /* DEBUG_MACRO */ /* Opaque structure describing all arguments to a macro, including the - macro name at index 0. */ + macro name at index 0. The lifetime of argv0 is only guaranteed + within a call to expand_macro, whereas the lifetime of the array + members is guaranteed as long as the input engine can parse text + with a reference to [EMAIL PROTECTED] */ struct macro_arguments { /* Number of arguments owned by this object, may be larger than @@ -368,16 +371,17 @@ 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 (obstack_object_size (obs) == 0) + if (!len) return t == TOKEN_COMMA; warn_builtin_concat (caller, TOKEN_DATA_FUNC (argp)); } - TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; - TOKEN_DATA_LEN (argp) = obstack_object_size (obs); obstack_1grow (obs, '\0'); + TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; TOKEN_DATA_TEXT (argp) = (char *) obstack_finish (obs); + TOKEN_DATA_LEN (argp) = len; TOKEN_DATA_QUOTE_AGE (argp) = age; return t == TOKEN_COMMA; } @@ -533,6 +537,7 @@ static void expand_macro (symbol *sym) { void *args_base; /* Base of stacks[i].args on entry. */ + void *args_scratch; /* Base of scratch space for call_macro. */ void *argv_base; /* Base of stacks[i].argv on entry. */ macro_arguments *argv; /* Arguments to the called macro. */ struct obstack *expansion; /* Collects the macro's expansion. */ @@ -594,6 +599,7 @@ expand_macro (symbol *sym) trace_prepre (SYMBOL_NAME (sym), my_call_id); argv = collect_arguments (sym, stacks[level].args, stacks[level].argv); + args_scratch = obstack_finish (stacks[level].args); /* The actual macro call. */ loc_close_file = current_file; @@ -633,6 +639,7 @@ expand_macro (symbol *sym) { if (argv->inuse) { + obstack_free (stacks[level].args, args_scratch); if (debug_macro_level & PRINT_ARGCOUNT_CHANGES) xfprintf (debug, "m4debug: -%d- `%s' in use, level=%d, " "refcount=%zu, argcount=%zu\n", my_call_id, argv->argv0, @@ -850,6 +857,16 @@ arg_func (macro_arguments *argv, unsigned int index) return TOKEN_DATA_FUNC (token); } +/* Return an obstack useful for scratch calculations that will not + interfere with macro expansion. The obstack will be reset when + expand_macro completes. */ +struct obstack * +arg_scratch (void) +{ + assert (obstack_object_size (stacks[expansion_level - 1].args) == 0); + return stacks[expansion_level - 1].args; +} + /* Create a new argument object using the same obstack as ARGV; thus, the new object will automatically be freed when the original is freed. Explicitly set the macro name (argv[0]) from ARGV0 with @@ -865,9 +882,8 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, token_data *token; token_chain *chain; unsigned int index = skip ? 2 : 1; - struct obstack *obs = stacks[expansion_level - 1].argv; + struct obstack *obs = arg_scratch (); - assert (obstack_object_size (obs) == 0); /* When making a reference through a reference, point to the original if possible. */ if (argv->has_ref) @@ -924,6 +940,8 @@ push_arg (struct obstack *obs, macro_arguments *argv, unsigned int index) if (index == 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; } @@ -944,44 +962,42 @@ void push_args (struct obstack *obs, macro_arguments *argv, bool skip, bool quote) { token_data *token; - token_data sep; unsigned int i = skip ? 2 : 1; + const char *sep = ","; + size_t sep_len = 1; bool use_sep = false; bool inuse = false; - static char comma[2] = ","; + struct obstack *scratch = arg_scratch (); if (i >= argv->argc) return; - TOKEN_DATA_TYPE (&sep) = TOKEN_TEXT; - TOKEN_DATA_QUOTE_AGE (&sep) = 0; - if (quote) + if (i + 1 == argv->argc) { - char *str; - obstack_grow (obs, lquote.string, lquote.length); - TOKEN_DATA_LEN (&sep) = obstack_object_size (obs); - obstack_1grow (obs, '\0'); - str = (char *) obstack_finish (obs); - TOKEN_DATA_TEXT (&sep) = str; - push_token (&sep, -1); - obstack_grow (obs, rquote.string, rquote.length); - obstack_1grow (obs, ','); - obstack_grow0 (obs, lquote.string, lquote.length); - str = (char *) obstack_finish (obs); - TOKEN_DATA_TEXT (&sep) = str; - TOKEN_DATA_LEN (&sep) = rquote.length + 1 + lquote.length; + if (quote) + obstack_grow (obs, lquote.string, lquote.length); + push_arg (obs, argv, i); + if (quote) + obstack_grow (obs, rquote.string, rquote.length); + return; } - else + + /* Compute the separator in the scratch space. */ + if (quote) { - TOKEN_DATA_TEXT (&sep) = comma; - TOKEN_DATA_LEN (&sep) = 1; + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (scratch, rquote.string, rquote.length); + obstack_1grow (scratch, ','); + obstack_grow0 (scratch, lquote.string, lquote.length); + sep = (char *) obstack_finish (scratch); + sep_len += lquote.length + rquote.length; } /* TODO push entire $@ reference, rather than pushing each arg. */ for ( ; i < argv->argc; i++) { token = arg_token (argv, i); if (use_sep) - push_token (&sep, -1); + obstack_grow (obs, sep, sep_len); else use_sep = true; /* TODO handle func tokens? */ hooks/post-receive -- GNU M4 source repository
