summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--NEWS8
-rw-r--r--doc/m4.texinfo37
-rw-r--r--src/macro.c52
4 files changed, 77 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 068148d5..ae487a0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/NEWS b/NEWS
index b92630b8..d988adf3 100644
--- a/NEWS
+++ b/NEWS
@@ -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: