diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | doc/m4.texinfo | 37 | ||||
-rw-r--r-- | src/macro.c | 52 |
4 files changed, 77 insertions, 28 deletions
@@ -1,3 +1,11 @@ +2007-11-14 Eric Blake <ebb9@byu.net> + + Avoid builtin concatenation in macro arguments. + * doc/m4.texinfo (Defn): Add tests. + * src/macro.c (warn_builtin_concat): New function. + (expand_argument): Use it to warn on more corner cases. + * NEWS: Mention new warning. + 2007-11-13 Eric Blake <ebb9@byu.net> Simplify previous patch. @@ -7,11 +7,13 @@ Version 1.4.11 - ?? ??? 2007, by ???? (git version 1.4.10a-*) * Fix core dump in 'm4 -F file -t undefined', present since -F was introduced in 1.3. * 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. + than 512 kibibytes are saved in diversions on platforms like NetBSD where + fopen(name,"a+") seeks to the end of the file. * 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. + 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. * Several improvements in `index', `regexp', and `patsubst' builtins to speed up typical Autoconf usage. * Memory usage is greatly reduced in recursive macros. diff --git a/doc/m4.texinfo b/doc/m4.texinfo index f028e3d6..6caa7c3e 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -2077,9 +2077,9 @@ and it is a builtin, the expansion is a special token, which points to the builtin's internal definition. This token is only meaningful as the second argument to @code{define} (and @code{pushdef}), and is silently converted to an -empty string in most other contexts. Combining a builtin with anything -else is not supported; a warning is issued and the builtin is omitted -from the final expansion. +empty string in most other contexts. Using multiple @var{name} to +combine a builtin with anything else is not supported; a warning is +issued and the builtin is omitted from the final expansion. The macro @code{defn} is recognized only with parameters. @end deffn @@ -2186,18 +2186,39 @@ define(defn(`divnum'), `cannot redefine a builtin token') @result{} divnum @result{}0 +define(`echo', `$@@') +@result{} +define(`mydivnum', echo(defn(`divnum'))) +@result{} +mydivnum +@result{} @end example Also note that @code{defn} with multiple arguments can only join text -macros, not builtins, although a future version of @acronym{GNU} M4 may -lift this restriction. +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. @example -define(`a', `A') +define(`a', `A')define(`AA', `b') @result{} -defn(`a', `defn', `a') -@error{}m4:stdin:2: Warning: cannot concatenate builtin `defn' +defn(`a', `divnum', `a') +@error{}m4:stdin:2: Warning: cannot concatenate builtin `divnum' @result{}AA +define(`mydivnum', defn(`divnum', `divnum'))mydivnum +@error{}m4:stdin:3: Warning: cannot concatenate builtin `divnum' +@error{}m4:stdin:3: Warning: cannot concatenate builtin `divnum' +@result{} +define(`mydivnum', defn(`divnum')defn(`divnum'))mydivnum +@error{}m4:stdin:4: Warning: cannot concatenate builtin `divnum' +@error{}m4:stdin:4: Warning: cannot concatenate builtin `divnum' +@result{} +define(`mydivnum', defn(`divnum')`a')mydivnum +@error{}m4:stdin:5: Warning: cannot concatenate builtin `divnum' +@result{}A +define(`mydivnum', `a'defn(`divnum'))mydivnum +@error{}m4:stdin:6: Warning: cannot concatenate builtin `divnum' +@result{}A @end example @node Pushdef diff --git a/src/macro.c b/src/macro.c index 76f9276d..9a3123ae 100644 --- a/src/macro.c +++ b/src/macro.c @@ -125,15 +125,28 @@ expand_token (struct obstack *obs, token_type t, token_data *td, int line) } +/*---------------------------------------------------------------. +| Helper function to print warning about concatenating FUNC with | +| text. | +`---------------------------------------------------------------*/ +static void +warn_builtin_concat (builtin_func *func) +{ + const builtin *bp = find_builtin_by_addr (func); + assert (bp); + M4ERROR ((warning_status, 0, "Warning: cannot concatenate builtin `%s'", + bp->name)); +} + /*-------------------------------------------------------------------. | This function parses one argument to a macro call. It expects the | | first left parenthesis or the separating comma to have been read | -| by the caller. It skips leading whitespace, then reads and | +| by the caller. It skips leading whitespace, then reads and | | expands tokens, until it finds a comma or right parenthesis at the | | same level of parentheses. It returns a flag indicating whether | -| the argument read is the last for the active macro call. The | -| argument is built on the obstack OBS, indirectly through | -| expand_token (). Report errors on behalf of CALLER. | +| the argument read is the last for the active macro call. The | +| argument is built on the obstack OBS, indirectly through | +| expand_token (). Report errors on behalf of CALLER. | `-------------------------------------------------------------------*/ static bool @@ -141,7 +154,6 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) { token_type t; token_data td; - char *text; int paren_level; const char *file = current_file; int line = current_line; @@ -166,25 +178,23 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) case TOKEN_CLOSE: if (paren_level == 0) { - /* The argument MUST be finished, whether we want it or not. */ - obstack_1grow (obs, '\0'); - text = (char *) obstack_finish (obs); - - if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID) + if (TOKEN_DATA_TYPE (argp) == TOKEN_FUNC) { - TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; - TOKEN_DATA_TEXT (argp) = text; + if (obstack_object_size (obs) == 0) + return t == TOKEN_COMMA; + warn_builtin_concat (TOKEN_DATA_FUNC (argp)); } + obstack_1grow (obs, '\0'); + TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; + TOKEN_DATA_TEXT (argp) = (char *) obstack_finish (obs); return t == TOKEN_COMMA; } /* fallthru */ case TOKEN_OPEN: case TOKEN_SIMPLE: - text = TOKEN_DATA_TEXT (&td); - - if (*text == '(') + if (t == TOKEN_OPEN) paren_level++; - else if (*text == ')') + else if (t == TOKEN_CLOSE) paren_level--; expand_token (obs, t, &td, line); break; @@ -202,11 +212,19 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) break; case TOKEN_MACDEF: - if (obstack_object_size (obs) == 0) + if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID + && obstack_object_size (obs) == 0) { TOKEN_DATA_TYPE (argp) = TOKEN_FUNC; TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td); } + else + { + if (TOKEN_DATA_TYPE (argp) == TOKEN_FUNC) + warn_builtin_concat (TOKEN_DATA_FUNC (argp)); + warn_builtin_concat (TOKEN_DATA_FUNC (&td)); + TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; + } break; default: |