diff options
author | Eric Blake <ebb9@byu.net> | 2007-10-25 08:27:28 -0600 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2007-12-07 16:09:05 -0700 |
commit | 6dcf7d2e3c5deac2d16ee9a29b6a307474603dc7 (patch) | |
tree | 962dcef7dded9f6883d5ac15d368f9cd7e1251ed | |
parent | 8b5b3b7a74f452fed795c063965966934a68755d (diff) | |
download | m4-6dcf7d2e3c5deac2d16ee9a29b6a307474603dc7.tar.gz |
Stage 6: convert builtins to push arg at a time
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | doc/m4.texinfo | 57 | ||||
-rw-r--r-- | src/builtin.c | 103 | ||||
-rw-r--r-- | src/debug.c | 70 | ||||
-rw-r--r-- | src/input.c | 80 | ||||
-rw-r--r-- | src/m4.c | 5 | ||||
-rw-r--r-- | src/m4.h | 14 | ||||
-rw-r--r-- | src/macro.c | 60 | ||||
-rw-r--r-- | src/output.c | 1 | ||||
-rw-r--r-- | src/symtab.c | 1 |
10 files changed, 264 insertions, 132 deletions
@@ -10,6 +10,11 @@ Version 1.4.11 - ?? ??? 2007, by ???? (git version 1.4.10a-*) * Fix regression introduced in 1.4.9b in the `divert' builtin when more than 512 kibibytes are saved in diversions on platforms like NetBSD where fopen(name,"a+") seeks to the end of the file. +* The output of the `maketemp' and `mkstemp' builtins is now quoted if a + file was created. This is a minor security fix, because it was possible + (although rather unlikely) that an unquoted string could match an + existing macro name, such that use of the `mkstemp' output would trigger + inadvertent macro expansion and operate on the wrong file name. * Enhance the `defn' builtin to support concatenation of multiple text arguments, as required by POSIX. However, at this time, it is not possible to concatenate a builtin macro with anything else; a warning is diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 803dbf05..93b76963 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -5876,7 +5876,7 @@ builtin macro, @code{mkstemp}, for making a temporary file: @deffn Builtin mkstemp (@var{template}) @deffnx Builtin maketemp (@var{template}) -Expands to a name of a new, empty file, made from the string +Expands to the quoted name of a new, empty file, made from the string @var{template}, which should end with the string @samp{XXXXXX}. The six @samp{X} characters are then replaced with random characters matching the regular expression @samp{[a-zA-Z0-9._-]}, in order to make the file @@ -5888,7 +5888,8 @@ account, and at most only the current user can read and write the file. The traditional behavior, standardized by @acronym{POSIX}, is that @code{maketemp} merely replaces the trailing @samp{X} with the process -id, without creating a file, and without ensuring that the resulting +id, without creating a file or quoting the expansion, and without +ensuring that the resulting string is a unique file name. In part, this means that using the same @var{template} twice in the same input file will result in the same expansion. This behavior is a security hole, as it is very easy for @@ -5912,6 +5913,8 @@ chosen: @comment ignore @example $ @kbd{m4} +define(`tmp', `oops') +@result{} maketemp(`/tmp/fooXXXXXX') @result{}/tmp/fooa07346 ifdef(`mkstemp', `define(`maketemp', defn(`mkstemp'))', @@ -5929,26 +5932,35 @@ Unless you use the @option{--traditional} command line option (or version of @code{maketemp} is secure. This means that using the same template to multiple calls will generate multiple files. However, we recommend that you use the new @code{mkstemp} macro, introduced in -@acronym{GNU} M4 1.4.8, which is secure even in traditional mode. +@acronym{GNU} M4 1.4.8, which is secure even in traditional mode. Also, +as of M4 1.4.11, the secure implementation quotes the resulting file +name, so that you are guaranteed to know what file was created even if +the random file name happens to match an existing macro. Notice that +this example is careful to use @code{defn} to avoid unintended expansion +of @samp{foo}. @example $ @kbd{m4} -syscmd(`rm -f foo??????')sysval +define(`foo', `errprint(`oops')') +@result{} +syscmd(`rm -f foo-??????')sysval @result{}0 -define(`file1', maketemp(`fooXXXXXX'))dnl -ifelse(esyscmd(`echo foo??????'), `foo??????', `no file', `created') +define(`file1', maketemp(`foo-XXXXXX'))dnl +ifelse(esyscmd(`echo \` foo-?????? \''), ` foo-?????? ', + `no file', `created') @result{}created -define(`file2', maketemp(`fooXX'))dnl -define(`file3', mkstemp(`fooXXXXXX'))dnl -ifelse(len(file1), len(file2), `same length', `different') +define(`file2', maketemp(`foo-XX'))dnl +define(`file3', mkstemp(`foo-XXXXXX'))dnl +ifelse(len(defn(`file1')), len(defn(`file2')), + `same length', `different') @result{}same length -ifelse(file1, file2, `same', `different file') +ifelse(defn(`file1'), defn(`file2'), `same', `different file') @result{}different file -ifelse(file2, file3, `same', `different file') +ifelse(defn(`file2'), defn(`file3'), `same', `different file') @result{}different file -ifelse(file1, file3, `same', `different file') +ifelse(defn(`file1'), defn(`file3'), `same', `different file') @result{}different file -syscmd(`rm 'file1 file2 file3) +syscmd(`rm 'defn(`file1') defn(`file2') defn(`file3')) @result{} sysval @result{}0 @@ -5966,6 +5978,25 @@ len(mkstemp(`fooXXXXX')) syscmd(`rm foo??????')sysval @result{}0 @end example + +@c Likewise, and ensure that traditional mode leaves the result unquoted +@c without creating a file. + +@comment options: -G +@example +syscmd(`rm -f foo-*')sysval +@result{}0 +len(maketemp(`foo-XXXXX')) +@error{}m4:stdin:2: Warning: maketemp: recommend using mkstemp instead +@result{}9 +define(`abc', `def') +@result{} +maketemp(`foo-abc') +@result{}foo-def +@error{}m4:stdin:4: Warning: maketemp: recommend using mkstemp instead +syscmd(`test -f foo-*')sysval +@result{}1 +@end example @end ignore @node Miscellaneous diff --git a/src/builtin.c b/src/builtin.c index d63ed722..9e44e1dd 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -731,37 +731,18 @@ static void m4_ifdef (struct obstack *obs, int argc, macro_arguments *argv) { symbol *s; - const char *result; - size_t len = 0; if (bad_argc (ARG (0), argc, 2, 3)) return; s = lookup_symbol (ARG (1), SYMBOL_LOOKUP); - - if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID) - { - result = ARG (2); - len = arg_len (argv, 2); - } - else if (argc >= 4) - { - result = ARG (3); - len = arg_len (argv, 3); - } - else - result = NULL; - - if (result != NULL) - obstack_grow (obs, result, len); + push_arg (obs, argv, (s && SYMBOL_TYPE (s) != TOKEN_VOID) ? 2 : 3); } static void m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) { - const char *result; const char *me; int index; - size_t len = 0; if (argc == 2) return; @@ -776,14 +757,13 @@ m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) index = 1; argc--; - result = NULL; - while (result == NULL) - if (arg_equal (argv, index, index + 1)) - { - result = ARG (index + 2); - len = arg_len (argv, index + 2); - } - else + while (true) + { + if (arg_equal (argv, index, index + 1)) + { + push_arg (obs, argv, index + 2); + return; + } switch (argc) { case 3: @@ -791,16 +771,14 @@ m4_ifelse (struct obstack *obs, int argc, macro_arguments *argv) case 4: case 5: - result = ARG (index + 3); - len = arg_len (argv, index + 3); - break; + push_arg (obs, argv, index + 3); + return; default: argc -= 3; index += 3; } - - obstack_grow (obs, result, len); + } } /*---------------------------------------------------------------------. @@ -1182,7 +1160,6 @@ m4_eval (struct obstack *obs, int argc, macro_arguments *argv) obstack_1grow (obs, '0'); while (value-- != 0) obstack_1grow (obs, '1'); - obstack_1grow (obs, '\0'); return; } @@ -1332,8 +1309,7 @@ m4_shift (struct obstack *obs, int argc, macro_arguments *argv) { if (bad_argc (ARG (0), argc, 1, -1)) return; - // TODO push a $@ reference - dump_args (obs, 2, argv, ",", true); + push_args (obs, argv, true, true); } /*--------------------------------------------------------------------------. @@ -1442,40 +1418,42 @@ m4_sinclude (struct obstack *obs, int argc, macro_arguments *argv) | Use the first argument as a template for a temporary file name. | `-----------------------------------------------------------------*/ -/* Add trailing 'X' to NAME of length LEN as necessary, then securely - create the file, and place the new file name on OBS. Report errors - on behalf of ME. */ +/* Add trailing 'X' to PATTERN of length LEN as necessary, then + securely create the file, and place the quoted new file name on + OBS. Report errors on behalf of ME. */ static void -mkstemp_helper (struct obstack *obs, const char *me, const char *name, +mkstemp_helper (struct obstack *obs, const char *me, const char *pattern, size_t len) { int fd; int i; + char *name; /* Guarantee that there are six trailing 'X' characters, even if the - user forgot to supply them. */ - obstack_grow (obs, name, len); + user forgot to supply them. Output must be quoted if + successful. */ + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, pattern, len); for (i = 0; len > 0 && i < 6; i++) - if (name[--len] != 'X') + if (pattern[len - i - 1] != 'X') break; - for (; i < 6; i++) - obstack_1grow (obs, 'X'); - obstack_1grow (obs, '\0'); + len += 6 - i; + obstack_grow0 (obs, "XXXXXX", 6 - i); + name = (char *) obstack_base (obs) + lquote.length; errno = 0; - fd = mkstemp ((char *) obstack_base (obs)); + fd = mkstemp (name); if (fd < 0) { - m4_warn (errno, me, _("cannot create tempfile `%s'"), name); + m4_warn (errno, me, _("cannot create tempfile `%s'"), pattern); obstack_free (obs, obstack_finish (obs)); } else { close (fd); - /* Undo trailing NUL. */ - /* FIXME - should we be quoting this name, on the tiny chance - that the random name generated matches a user's macro? */ + /* Remove NUL, then finish quote. */ obstack_blank (obs, -1); + obstack_grow (obs, rquote.string, rquote.length); } } @@ -1512,7 +1490,7 @@ m4_maketemp (struct obstack *obs, int argc, macro_arguments *argv) str = ntoa ((int32_t) getpid (), 10); len2 = strlen (str); if (len2 > len - i) - obstack_grow0 (obs, str + len2 - (len - i), len - i); + obstack_grow (obs, str + len2 - (len - i), len - i); else { while (i++ < len - len2) @@ -1830,7 +1808,7 @@ m4_substr (struct obstack *obs, int argc, macro_arguments *argv) { /* builtin(`substr') is blank, but substr(`abc') is abc. */ if (argc == 2) - obstack_grow (obs, ARG (1), arg_len (argv, 1)); + push_arg (obs, argv, 1); return; } @@ -1916,7 +1894,7 @@ m4_translit (struct obstack *obs, int argc, macro_arguments *argv) { /* builtin(`translit') is blank, but translit(`abc') is abc. */ if (argc == 2) - obstack_grow (obs, ARG (1), arg_len (argv, 1)); + push_arg (obs, argv, 1); return; } @@ -2153,7 +2131,7 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv) { /* builtin(`patsubst') is blank, but patsubst(`abc') is abc. */ if (argc == 2) - obstack_grow (obs, ARG (1), arg_len (argv, 1)); + push_arg (obs, argv, 1); return; } @@ -2165,7 +2143,7 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv) replacement, we need not waste time with it. */ if (!*regexp && !*repl) { - obstack_grow (obs, victim, arg_len (argv, 1)); + push_arg (obs, argv, 1); return; } @@ -2219,9 +2197,12 @@ m4_patsubst (struct obstack *obs, int argc, macro_arguments *argv) offset = regs->end[0]; if (regs->start[0] == regs->end[0]) - obstack_1grow (obs, victim[offset++]); + { + if (offset < length) + obstack_1grow (obs, victim[offset]); + offset++; + } } - obstack_1grow (obs, '\0'); } /* Finally, a placeholder builtin. This builtin is not installed by @@ -2283,8 +2264,7 @@ expand_user_macro (struct obstack *obs, symbol *sym, for (i = 0; isdigit (to_uchar (*text)); text++) i = i * 10 + (*text - '0'); } - if (i < argc) - obstack_grow (obs, ARG (i), arg_len (argv, i)); + push_arg (obs, argv, i); break; case '#': /* number of arguments */ @@ -2294,8 +2274,7 @@ expand_user_macro (struct obstack *obs, symbol *sym, case '*': /* all arguments */ case '@': /* ... same, but quoted */ - // TODO push a $@ reference - dump_args (obs, 1, argv, ",", *text == '@'); + push_args (obs, argv, false, *text == '@'); text++; break; diff --git a/src/debug.c b/src/debug.c index c4f701d9..2ca7a0d6 100644 --- a/src/debug.c +++ b/src/debug.c @@ -239,21 +239,21 @@ debug_message_prefix (void) output from interfering with other debug messages generated by the various builtins. */ -/*---------------------------------------------------------------------. -| Tracing output is formatted here, by a simplified printf-to-obstack | -| function trace_format (). Understands only %S, %s, %d, %l (optional | -| left quote) and %r (optional right quote). | -`---------------------------------------------------------------------*/ +/*-------------------------------------------------------------------. +| Tracing output to the obstack is formatted here, by a simplified | +| printf-like function trace_format (). Understands only %B (1 arg: | +| input block), %S (1 arg: length-limited text), %s (1 arg: text), | +| %d (1 arg: integer), %l (0 args: optional left quote) and %r (0 | +| args: optional right quote). | +`-------------------------------------------------------------------*/ static void trace_format (const char *fmt, ...) { va_list args; char ch; - int d; const char *s; - int slen; int maxlen; va_start (args, fmt); @@ -266,9 +266,14 @@ trace_format (const char *fmt, ...) if (ch == '\0') break; - maxlen = 0; + maxlen = INT_MAX; switch (*fmt++) { + case 'B': + s = ""; + input_print (&trace, va_arg (args, input_block *)); + break; + case 'S': maxlen = max_debug_argument_length; /* fall through */ @@ -295,14 +300,8 @@ trace_format (const char *fmt, ...) break; } - slen = strlen (s); - if (maxlen == 0 || maxlen > slen) - obstack_grow (&trace, s, slen); - else - { - obstack_grow (&trace, s, maxlen); - obstack_grow (&trace, "...", 3); - } + if (obstack_print (&trace, s, SIZE_MAX, &maxlen)) + break; } va_end (args); @@ -362,10 +361,11 @@ trace_prepre (const char *name, int id) `-----------------------------------------------------------------------*/ void -trace_pre (const char *name, int id, int argc, macro_arguments *argv) +trace_pre (const char *name, int id, macro_arguments *argv) { int i; const builtin *bp; + int argc = arg_argc (argv); trace_header (id); trace_format ("%s", name); @@ -417,9 +417,11 @@ trace_pre (const char *name, int id, int argc, macro_arguments *argv) `-------------------------------------------------------------------*/ void -trace_post (const char *name, int id, int argc, macro_arguments *argv, - const char *expanded) +trace_post (const char *name, int id, macro_arguments *argv, + const input_block *expanded) { + int argc = arg_argc (argv); + if (debug_level & DEBUG_TRACE_CALL) { trace_header (id); @@ -427,6 +429,34 @@ trace_post (const char *name, int id, int argc, macro_arguments *argv, } if (expanded && (debug_level & DEBUG_TRACE_EXPANSION)) - trace_format (" -> %l%S%r", expanded); + 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/input.c b/src/input.c index 551b43d6..4e5d2990 100644 --- a/src/input.c +++ b/src/input.c @@ -77,7 +77,7 @@ typedef enum input_type input_type; /* A block of input to be scanned. */ struct input_block { - struct input_block *prev; /* Previous input_block on the input stack. */ + input_block *prev; /* Previous input_block on the input stack. */ input_type type; /* See enum values. */ const char *file; /* File where this input is from. */ int line; /* Line where this input is from. */ @@ -101,8 +101,6 @@ struct input_block u; }; -typedef struct input_block input_block; - /* Current input file name. */ const char *current_file; @@ -208,8 +206,7 @@ push_file (FILE *fp, const char *title, bool close) if (debug_level & DEBUG_TRACE_INPUT) DEBUG_MESSAGE1 ("input read from %s", title); - i = (input_block *) obstack_alloc (current_input, - sizeof (struct input_block)); + i = (input_block *) obstack_alloc (current_input, sizeof *i); i->type = INPUT_FILE; i->file = (char *) obstack_copy0 (&file_names, title, strlen (title)); i->line = 1; @@ -242,8 +239,7 @@ push_macro (builtin_func *func) next = NULL; } - i = (input_block *) obstack_alloc (current_input, - sizeof (struct input_block)); + i = (input_block *) obstack_alloc (current_input, sizeof *i); i->type = INPUT_MACRO; i->file = current_file; i->line = current_line; @@ -267,8 +263,7 @@ push_string_init (void) while (isp && pop_input (false)); /* Reserve the next location on the obstack. */ - next = (input_block *) obstack_alloc (current_input, - sizeof (struct input_block)); + next = (input_block *) obstack_alloc (current_input, sizeof *next); next->type = INPUT_STRING; next->file = current_file; next->line = current_line; @@ -281,30 +276,35 @@ push_string_init (void) | push_file () or push_macro () has invalidated the previous call to | | push_string_init (), so we just give up. If the new object is | | void, we do not push it. The function push_string_finish () | -| returns a pointer to the finished object. This pointer is only | -| for temporary use, since reading the next token might release the | -| memory used for the object. | +| returns an opaque pointer to the finished object, which can then | +| be printed with input_print when tracing is enabled. This pointer | +| is only for temporary use, since reading the next token will | +| invalidate the object. | `-------------------------------------------------------------------*/ -const char * +const input_block * push_string_finish (void) { - const char *ret = NULL; + input_block *ret = NULL; + size_t len = obstack_object_size (current_input); if (next == NULL) - return NULL; + { + assert (!len); + return NULL; + } - if (obstack_object_size (current_input) > 0) + if (len) { obstack_1grow (current_input, '\0'); next->u.u_s.string = (char *) obstack_finish (current_input); next->prev = isp; isp = next; - ret = isp->u.u_s.string; /* for immediate use only */ input_change = true; + ret = isp; } else - obstack_free (current_input, next); /* people might leave garbage on it. */ + obstack_free (current_input, next); next = NULL; return ret; } @@ -322,8 +322,7 @@ void push_wrapup (const char *s) { input_block *i; - i = (input_block *) obstack_alloc (wrapup_stack, - sizeof (struct input_block)); + i = (input_block *) obstack_alloc (wrapup_stack, sizeof *i); i->prev = wsp; i->type = INPUT_STRING; i->file = current_file; @@ -421,7 +420,7 @@ pop_wrapup (void) } current_input = wrapup_stack; - wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack)); + wrapup_stack = (struct obstack *) xmalloc (sizeof *wrapup_stack); obstack_init (wrapup_stack); isp = wsp; @@ -443,6 +442,41 @@ init_macro_token (token_data *td) TOKEN_DATA_TYPE (td) = TOKEN_FUNC; TOKEN_DATA_FUNC (td) = isp->u.func; } + +/*--------------------------------------------------------------. +| Dump a representation of INPUT to the obstack OBS, for use in | +| tracing. | +`--------------------------------------------------------------*/ +void +input_print (struct obstack *obs, const input_block *input) +{ + int maxlen = max_debug_argument_length; + + assert (input); + switch (input->type) + { + case INPUT_STRING: + obstack_print (obs, input->u.u_s.string, SIZE_MAX, &maxlen); + break; + case INPUT_FILE: + obstack_grow (obs, "<file: ", strlen ("<file: ")); + obstack_grow (obs, input->file, strlen (input->file)); + obstack_1grow (obs, '>'); + break; + case INPUT_MACRO: + { + const builtin *bp = find_builtin_by_addr (input->u.func); + assert (bp); + obstack_1grow (obs, '<'); + obstack_grow (obs, bp->name, strlen (bp->name)); + obstack_1grow (obs, '>'); + } + break; + default: + assert (!"input_print"); + abort (); + } +} /*-----------------------------------------------------------------. @@ -680,9 +714,9 @@ input_init (void) current_file = ""; current_line = 0; - current_input = (struct obstack *) xmalloc (sizeof (struct obstack)); + current_input = (struct obstack *) xmalloc (sizeof *current_input); obstack_init (current_input); - wrapup_stack = (struct obstack *) xmalloc (sizeof (struct obstack)); + wrapup_stack = (struct obstack *) xmalloc (sizeof *wrapup_stack); obstack_init (wrapup_stack); obstack_init (&file_names); @@ -22,7 +22,6 @@ #include "m4.h" #include <getopt.h> -#include <limits.h> #include <signal.h> #include <stdarg.h> @@ -48,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 = 0; +int max_debug_argument_length = INT_MAX; /* Suppress warnings about missing arguments. */ int suppress_warnings = 0; @@ -553,7 +552,7 @@ main (int argc, char *const *argv, char *const *envp) case 'l': max_debug_argument_length = atoi (optarg); if (max_debug_argument_length <= 0) - max_debug_argument_length = 0; + max_debug_argument_length = INT_MAX; break; case 'o': @@ -28,6 +28,7 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <limits.h> #include <stdbool.h> #include <stdint.h> #include <string.h> @@ -96,6 +97,7 @@ typedef struct string STRING; (OBS)->object_base = (char *) (OBJECT)) /* These must come first. */ +typedef struct input_block input_block; typedef struct token_data token_data; typedef struct macro_arguments macro_arguments; typedef void builtin_func (struct obstack *, int, macro_arguments *); @@ -245,8 +247,11 @@ bool debug_set_output (const char *, const char *); void debug_message_prefix (void); void trace_prepre (const char *, int); -void trace_pre (const char *, int, int, macro_arguments *); -void trace_post (const char *, int, int, macro_arguments *, const char *); +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. */ @@ -341,9 +346,10 @@ void skip_line (const char *); void push_file (FILE *, const char *, bool); void push_macro (builtin_func *); struct obstack *push_string_init (void); -const char *push_string_finish (void); +const input_block *push_string_finish (void); void push_wrapup (const char *); bool pop_wrapup (void); +void input_print (struct obstack *, const input_block *); /* current input file, and line */ extern const char *current_file; @@ -447,6 +453,8 @@ size_t arg_len (macro_arguments *, unsigned int); builtin_func *arg_func (macro_arguments *, unsigned int); macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t, bool, bool); +void push_arg (struct obstack *, macro_arguments *, unsigned int); +void push_args (struct obstack *, macro_arguments *, bool, bool); /* File: builtin.c --- builtins. */ diff --git a/src/macro.c b/src/macro.c index 56a8571d..c4eaaddb 100644 --- a/src/macro.c +++ b/src/macro.c @@ -414,9 +414,8 @@ expand_macro (symbol *sym) unsigned int arg_size; /* Size of arg_stack on entry. */ unsigned int argv_size; /* Size of argv_stack on entry. */ macro_arguments *argv; - int argc; struct obstack *expansion; - const char *expanded; + const input_block *expanded; bool traced; int my_call_id; @@ -453,7 +452,6 @@ expand_macro (symbol *sym) trace_prepre (SYMBOL_NAME (sym), my_call_id); argv = collect_arguments (sym, &arg_stack); - argc = argv->argc; loc_close_file = current_file; loc_close_line = current_line; @@ -461,14 +459,14 @@ expand_macro (symbol *sym) current_line = loc_open_line; if (traced) - trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv); + trace_pre (SYMBOL_NAME (sym), my_call_id, argv); expansion = push_string_init (); - call_macro (sym, argc, argv, expansion); + call_macro (sym, argv->argc, argv, expansion); expanded = push_string_finish (); if (traced) - trace_post (SYMBOL_NAME (sym), my_call_id, argc, argv, expanded); + trace_post (SYMBOL_NAME (sym), my_call_id, argv, expanded); current_file = loc_close_file; current_line = loc_close_line; @@ -711,3 +709,53 @@ make_argv_ref (macro_arguments *argv, const char *argv0, size_t argv0_len, new_argv->quote_age = argv->quote_age; return new_argv; } + +/* Push argument INDEX from ARGV, which must be a text token, onto the + expansion stack OBS for rescanning. */ +void +push_arg (struct obstack *obs, macro_arguments *argv, unsigned int index) +{ + token_data *token; + + if (index == 0) + { + obstack_grow (obs, argv->argv0, argv->argv0_len); + return; + } + if (index >= argv->argc) + return; + token = arg_token (argv, index); + // TODO handle func tokens? + assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); + // TODO actually push a reference, rather than copying data + obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token)); +} + +/* Push series of comma-separated arguments from ARGV, which should + all be text, onto the expansion stack OBS for rescanning. If SKIP, + then don't push the first argument. If QUOTE, the rescan also + includes quoting around each arg. */ +void +push_args (struct obstack *obs, macro_arguments *argv, bool skip, bool quote) +{ + token_data *token; + unsigned int i; + bool comma = false; + + // TODO push reference, rather than copying data + for (i = skip ? 2 : 1; i < argv->argc; i++) + { + token = arg_token (argv, i); + if (comma) + obstack_1grow (obs, ','); + else + comma = true; + // TODO handle func tokens? + assert (TOKEN_DATA_TYPE (token) == TOKEN_TEXT); + if (quote) + obstack_grow (obs, lquote.string, lquote.length); + obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token)); + if (quote) + obstack_grow (obs, rquote.string, rquote.length); + } +} diff --git a/src/output.c b/src/output.c index 478d3b28..4c8c9deb 100644 --- a/src/output.c +++ b/src/output.c @@ -21,7 +21,6 @@ #include "m4.h" -#include <limits.h> #include <sys/stat.h> #include "gl_avltree_oset.h" diff --git a/src/symtab.c b/src/symtab.c index d65d4c5a..e8a027f0 100644 --- a/src/symtab.c +++ b/src/symtab.c @@ -32,7 +32,6 @@ will then always be the first found. */ #include "m4.h" -#include <limits.h> #ifdef DEBUG_SYM /* When evaluating hash table performance, this profiling code shows |