summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2007-12-06 13:33:40 -0700
committerEric Blake <ebb9@byu.net>2008-04-14 22:05:40 -0600
commit6b67f52903f191b8e0b9c295182a8e6504fb4f96 (patch)
tree84e6f9b82e7dc8419cff128c7f886bf37e2603c8
parentfb04a26fa6ac38cbe5517372b1a984af0c851876 (diff)
downloadm4-6b67f52903f191b8e0b9c295182a8e6504fb4f96.tar.gz
Stage 21: $@ concatenates builtins, m4wrap takes builtins.
* src/m4.h (append_macro): New prototype. (push_macro, push_wrapup_init, arg_print, func_print): Alter prototypes. * src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN. (INPUT_EOF): New input block type, for efficiency. (struct input_block): Remove u.func member. (input_eof): New input sentinel. (append_macro): New function. (push_macro, push_wrapup_init): Add parameter. (push_token): Support builtin tokens. (init_macro_token): Always use chain for builtin tokens. (push_string_init, pop_input, pop_wrapup, input_print) (peek_input, next_char, next_char_1, input_init): Adjust callers. * src/macro.c (arg_equal, wrap_args): Handle builtin tokens. (arg_print): Add parameter. (collect_arguments, arg_type, arg_text): Adjust callers. * src/builtin.c (m4_m4wrap): Handle builtin tokens. (func_print): Add parameter. (m4_defn): Allow pushing builtin alongside other text. (m4_errprint): Adjust caller. * src/debug.c (trace_pre): Likewise. * doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new behavior. (M4wrap): New test. (cherry picked from commit 32d4bf4d447e0e252c809897b795b57b3bfd74de) Signed-off-by: Eric Blake <ebb9@byu.net>
-rw-r--r--.cvsignore2
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog35
-rw-r--r--doc/m4.texinfo84
-rw-r--r--src/builtin.c26
-rw-r--r--src/debug.c2
-rw-r--r--src/input.c203
-rw-r--r--src/m4.h12
-rw-r--r--src/macro.c86
9 files changed, 253 insertions, 199 deletions
diff --git a/.cvsignore b/.cvsignore
index 8fc44d60..493776c5 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -17,6 +17,7 @@ configure.lineno
COPYING
depcomp
gendocs.sh
+GNUmakefile
gnupload
INSTALL
install-sh
@@ -30,4 +31,3 @@ stamp-h
stamp-h1
stamp-h.in
tests
-GNUmakefile
diff --git a/.gitignore b/.gitignore
index b581eac0..fae2fd39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ COPYING
CVS
depcomp
gendocs.sh
+GNUmakefile
gnupload
INSTALL
install-sh
@@ -34,4 +35,3 @@ stamp-h
stamp-h1
stamp-h.in
tests
-GNUmakefile
diff --git a/ChangeLog b/ChangeLog
index 8501d11c..a9fc7f5c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2008-04-14 Eric Blake <ebb9@byu.net>
+
+ Stage 21: $@ concatenates builtins, m4wrap takes builtins.
+ Create a new input block type, which always fails with CHAR_EOF,
+ so that remaining input routines no longer have to check for NULL
+ input block. Improve arg_print to handle builtin tokens when
+ printing to a known chain, rather than always flattening builtins,
+ allowing m4wrap and $@ references to handle embedded builtins.
+ Memory impact: none.
+ Speed impact: noticeable improvement, from fewer conditionals.
+ * src/m4.h (append_macro): New prototype.
+ (push_macro, push_wrapup_init, arg_print, func_print): Alter
+ prototypes.
+ * src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN.
+ (INPUT_EOF): New input block type, for efficiency.
+ (struct input_block): Remove u.func member.
+ (input_eof): New input sentinel.
+ (append_macro): New function.
+ (push_macro, push_wrapup_init): Add parameter.
+ (push_token): Support builtin tokens.
+ (init_macro_token): Always use chain for builtin tokens.
+ (push_string_init, pop_input, pop_wrapup, input_print)
+ (peek_input, next_char, next_char_1, input_init): Adjust callers.
+ * src/macro.c (arg_equal, wrap_args): Handle builtin tokens.
+ (arg_print): Add parameter.
+ (collect_arguments, arg_type, arg_text): Adjust callers.
+ * src/builtin.c (m4_m4wrap): Handle builtin tokens.
+ (func_print): Add parameter.
+ (m4_defn): Allow pushing builtin alongside other text.
+ (m4_errprint): Adjust caller.
+ * src/debug.c (trace_pre): Likewise.
+ * doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new
+ behavior.
+ (M4wrap): New test.
+
2008-04-11 Eric Blake <ebb9@byu.net>
Ensure --program-prefix doesn't regress.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 4a523c6c..52fc77b7 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -2281,10 +2281,11 @@ bar
@result{}0
@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.6, @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
$ @kbd{m4 -d}
@@ -2293,13 +2294,12 @@ define(`a', `A')define(`AA', `b')
traceon(`defn', `define')
@result{}
defn(`a', `divnum', `a')
-@error{}m4:stdin:3: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'`A''
+@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'<divnum>`A''
@result{}AA
define(`mydivnum', defn(`divnum', `divnum'))mydivnum
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum'
-@error{}m4trace: -2- defn(`divnum', `divnum')
+@error{}m4trace: -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'
@error{}m4trace: -1- define(`mydivnum', `')
@result{}
traceoff(`defn', `define')
@@ -2317,10 +2317,10 @@ define(`mydivnum', `a'defn(`divnum'))mydivnum
define(`q', ``$@@'')
@result{}
define(`foo', q(`a', defn(`divnum')))foo
-@error{}m4:stdin:10: Warning: define: cannot quote builtin
-@result{}a,
+@error{}m4:stdin:10: Warning: define: cannot concatenate builtins
+@result{}foo
ifdef(`foo', `yes', `no')
-@result{}yes
+@result{}no
@end example
@node Pushdef
@@ -2860,8 +2860,8 @@ ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no')
@result{}no
@end example
-@comment It would be nice to pass builtin tokens through m4wrap, as well
-@comment as allowing concatenation of builtins in ifelse and user macros.
+@comment It would be nice to allow concatenation of builtins without
+@comment using $@ handling.
@example
define(`e', `$@@')define(`q', ``$@@'')define(`u', `$*')
@result{}
@@ -2881,33 +2881,25 @@ cmp(`q(defn(`defn'))', `q(`<defn>')')-fixme
cmp(`q(defn(`defn'))', ``'')-fixme
@error{}m4:stdin:7: Warning: ifelse: cannot quote builtin
@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')-fixme
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
-cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')-fixme
-@error{}m4:stdin:9: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')-fixme
-@error{}m4:stdin:10: Warning: ifelse: cannot quote builtin
-@result{}no-fixme
-cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')-fixme
-@error{}m4:stdin:11: Warning: ifelse: cannot quote builtin
-@result{}yes-fixme
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')
+@result{}yes
+cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')
+@result{}no
+cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')
+@result{}no
define(`cat', `$1`'ifelse(`$#', `1', `', `$0(shift($@@))')')
@result{}
-cat(`define(`foo',', defn(`divnum'), `)foo')-fixme
-@error{}m4:stdin:13: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-cat(e(`define(`bar',', defn(`divnum'), `)bar'))-fixme
-@error{}m4:stdin:14: Warning: ifelse: cannot quote builtin
-@result{}-fixme
-m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)-fixme
+cat(`define(`foo',', defn(`divnum'), `)foo')
+@result{}0
+cat(e(`define(`bar',', defn(`divnum'), `)bar'))
+@result{}0
+m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)
')
-@error{}m4:stdin:15: Warning: m4wrap: cannot quote builtin
@result{}
^D
-@result{}-fixme
+@result{}0
@end example
@end ignore
@@ -3811,7 +3803,7 @@ echo(`1', `long string')
@error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
@result{}1,long string
indir(`echo', defn(`changequote'))
-@error{}m4trace: -2- defn(`change...')
+@error{}m4trace: -2- defn(`change...') -> `<changequote>'
@error{}m4trace: -1- indir(`echo', <changequote>) -> ``<changequote>''
@result{}
@end example
@@ -4723,6 +4715,24 @@ m4wrap(`m4wrap(`)')len(abc')
@error{}m4:stdin:1: len: end of file in argument list
@end example
+As of M4 1.6, @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}.
+
+@example
+m4wrap(`define(`foo', defn(`divnum'))foo
+')
+@result{}
+m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar
+')
+@result{}
+^D
+@result{}0
+@result{}0
+@end example
+
@node File Inclusion
@chapter File inclusion
diff --git a/src/builtin.c b/src/builtin.c
index e9856a8b..07d2ce09 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -123,7 +123,7 @@ builtin_tab[] =
{ "indir", true, true, true, m4_indir },
{ "len", false, false, true, m4_len },
{ "m4exit", false, false, false, m4_m4exit },
- { "m4wrap", false, false, true, m4_m4wrap },
+ { "m4wrap", false, true, true, m4_m4wrap },
{ "maketemp", false, false, true, m4_maketemp },
{ "mkstemp", false, false, true, m4_mkstemp },
{ "patsubst", true, false, true, m4_patsubst },
@@ -204,19 +204,25 @@ find_builtin_by_name (const char *name)
/*------------------------------------------------------------------.
| Print a representation of FUNC to OBS. If FLATTEN, output QUOTES |
-| around an empty string instead. |
+| around an empty string instead; else if CHAIN, append the builtin |
+| to the chain; otherwise print the name of FUNC. |
`------------------------------------------------------------------*/
void
func_print (struct obstack *obs, const builtin *func, bool flatten,
- const string_pair *quotes)
+ token_chain **chain, const string_pair *quotes)
{
assert (func);
- if (flatten && quotes)
+ if (flatten)
{
- obstack_grow (obs, quotes->str1, quotes->len1);
- obstack_grow (obs, quotes->str2, quotes->len2);
+ if (quotes)
+ {
+ obstack_grow (obs, quotes->str1, quotes->len1);
+ obstack_grow (obs, quotes->str2, quotes->len2);
+ }
}
- else if (!flatten)
+ else if (chain)
+ append_macro (obs, func->func, NULL, chain);
+ else
{
obstack_1grow (obs, '<');
obstack_grow (obs, func->name, strlen (func->name));
@@ -1022,10 +1028,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:
@@ -1548,7 +1552,7 @@ m4_errprint (struct obstack *obs, int argc, macro_arguments *argv)
if (bad_argc (ARG (0), argc, 1, -1))
return;
- arg_print (obs, argv, 1, NULL, true, " ", NULL, false);
+ arg_print (obs, argv, 1, NULL, true, NULL, " ", NULL, false);
debug_flush_files ();
len = obstack_object_size (obs);
/* The close_stdin module makes it safe to skip checking the return
diff --git a/src/debug.c b/src/debug.c
index 46e1306f..fde6c499 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -368,7 +368,7 @@ trace_pre (const char *name, int id, macro_arguments *argv)
trace_format ("(");
arg_print (&trace, argv, 1,
(debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL,
- false, ", ", &len, true);
+ false, NULL, ", ", &len, true);
trace_format (")");
}
diff --git a/src/input.c b/src/input.c
index 86db7041..3a913b71 100644
--- a/src/input.c
+++ b/src/input.c
@@ -72,10 +72,10 @@
/* Type of an input block. */
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. */
+ INPUT_EOF /* Placeholder at bottom of input stack. */
};
typedef enum input_type input_type;
@@ -103,7 +103,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. */
@@ -136,15 +135,19 @@ static struct obstack *current_input;
/* Bottom of token_stack, for obstack_free. */
static void *token_bottom;
-/* Pointer to top of current_input. */
+/* Pointer to top of current_input, never NULL. */
static input_block *isp;
-/* Pointer to top of wrapup_stack. */
+/* Pointer to top of wrapup_stack, never NULL. */
static input_block *wsp;
-/* Aux. for handling split push_string (). */
+/* Auxiliary for handling split push_string (), NULL if not pushing
+ text for rescanning. */
static input_block *next;
+/* Marker at the end of the input stack. */
+static input_block input_eof = { NULL, INPUT_EOF, "", 0 };
+
/* Flag for next_char () to increment current_line. */
static bool start_of_input_line;
@@ -265,33 +268,50 @@ push_file (FILE *fp, const char *title, bool close)
isp = i;
}
-/*-----------------------------------------------------------------.
-| push_macro () pushes the builtin macro FUNC on the input stack. |
-| If next is non-NULL, this push invalidates a call to |
-| push_string_init (), whose storage is consequently released. |
-`-----------------------------------------------------------------*/
-
+/*------------------------------------------------------------------.
+| Given an obstack OBS, capture any unfinished text as a link, then |
+| append the builtin FUNC as the next link in the chain that starts |
+| at *START and ends at *END. START may be NULL if *END is |
+| non-NULL. |
+`------------------------------------------------------------------*/
void
-push_macro (builtin_func *func)
+append_macro (struct obstack *obs, builtin_func *func, token_chain **start,
+ token_chain **end)
{
- input_block *i;
-
- if (next != NULL)
- {
- obstack_free (current_input, next);
- next = NULL;
- }
+ token_chain *chain;
assert (func);
- i = (input_block *) obstack_alloc (current_input, sizeof *i);
- i->type = INPUT_MACRO;
- i->file = current_file;
- i->line = current_line;
- input_change = true;
+ make_text_link (obs, start, end);
+ 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 the builtin FUNC 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);
}
/*--------------------------------------------------------------.
@@ -304,7 +324,7 @@ push_string_init (void)
{
/* Free any memory occupied by completely parsed strings. */
assert (next == NULL);
- while (isp && pop_input (false));
+ while (pop_input (false));
/* Reserve the next location on the obstack. */
next = (input_block *) obstack_alloc (current_input, sizeof *next);
@@ -361,7 +381,18 @@ push_token (token_data *token, int level, bool inuse)
return false;
}
}
- else if (TOKEN_DATA_TYPE (token) != TOKEN_FUNC)
+ 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
additional references for long text segments is more
@@ -407,23 +438,15 @@ push_token (token_data *token, int level, bool inuse)
adjust_refcount (level, true);
inuse = true;
}
- else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC)
- {
- chain = (token_chain *) obstack_alloc (current_input, sizeof *chain);
- if (next->u.u_c.end)
- next->u.u_c.end->next = chain;
- else
- next->u.u_c.chain = chain;
- next->u.u_c.end = chain;
- chain->next = NULL;
- chain->type = CHAIN_FUNC;
- chain->quote_age = 0;
- chain->u.func = TOKEN_DATA_FUNC (token);
- }
while (src_chain)
{
- /* TODO support func concatenation. */
- assert (src_chain->type != CHAIN_FUNC);
+ 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. */
@@ -526,13 +549,13 @@ push_string_finish (void)
`--------------------------------------------------------------*/
struct obstack *
-push_wrapup_init (void)
+push_wrapup_init (token_chain ***end)
{
input_block *i;
token_chain *chain;
assert (obstack_object_size (wrapup_stack) == 0);
- if (wsp)
+ if (wsp != &input_eof)
{
i = wsp;
assert (i->type == INPUT_CHAIN && i->u.u_c.end
@@ -559,6 +582,7 @@ push_wrapup_init (void)
chain->quote_age = 0;
chain->u.u_l.file = current_file;
chain->u.u_l.line = current_line;
+ *end = &i->u.u_c.end;
return wrapup_stack;
}
@@ -596,12 +620,6 @@ pop_input (bool cleanup)
return false;
break;
- case INPUT_MACRO:
- assert (!isp->u.func || !cleanup);
- if (isp->u.func)
- return false;
- break;
-
case INPUT_CHAIN:
chain = isp->u.u_c.chain;
assert (!chain || !cleanup);
@@ -658,6 +676,9 @@ pop_input (bool cleanup)
output_current_line = -1;
break;
+ case INPUT_EOF:
+ return false;
+
default:
assert (!"pop_input");
abort ();
@@ -684,7 +705,7 @@ pop_wrapup (void)
obstack_free (current_input, NULL);
free (current_input);
- if (wsp == NULL)
+ if (wsp == &input_eof)
{
/* End of the program. Free all memory even though we are about
to exit, since it makes leak detection easier. */
@@ -703,7 +724,7 @@ pop_wrapup (void)
obstack_init (wrapup_stack);
isp = wsp;
- wsp = NULL;
+ wsp = &input_eof;
input_change = true;
return true;
@@ -730,9 +751,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:
chain = input->u.u_c.chain;
while (chain)
@@ -746,14 +764,14 @@ input_print (struct obstack *obs, const input_block *input)
break;
case CHAIN_FUNC:
func_print (obs, find_builtin_by_addr (chain->u.func), false,
- NULL);
+ NULL, NULL);
break;
case CHAIN_ARGV:
assert (!chain->u.u_a.comma);
if (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, NULL, &maxlen, false))
+ chain->u.u_a.flatten, NULL, NULL, &maxlen, false))
return;
break;
default:
@@ -789,9 +807,7 @@ peek_input (bool allow_argv)
while (1)
{
- if (block == NULL)
- return CHAR_EOF;
-
+ assert (block);
switch (block->type)
{
case INPUT_STRING:
@@ -809,11 +825,6 @@ peek_input (bool allow_argv)
block->u.u_f.end = true;
break;
- case INPUT_MACRO:
- if (block->u.func)
- return CHAR_MACRO;
- break;
-
case INPUT_CHAIN:
chain = block->u.u_c.chain;
while (chain)
@@ -863,6 +874,9 @@ peek_input (bool allow_argv)
}
break;
+ case INPUT_EOF:
+ return CHAR_EOF;
+
default:
assert (!"peek_input");
abort ();
@@ -887,7 +901,7 @@ peek_input (bool allow_argv)
`-------------------------------------------------------------------*/
#define next_char(AQ, AA) \
- (isp && isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \
+ (isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \
? (isp->u.u_s.len--, to_uchar (*isp->u.u_s.str++)) \
: next_char_1 (AQ, AA))
@@ -899,13 +913,7 @@ next_char_1 (bool allow_quote, bool allow_argv)
while (1)
{
- if (isp == NULL)
- {
- current_file = "";
- current_line = 0;
- return CHAR_EOF;
- }
-
+ assert (isp);
if (input_change)
{
current_file = isp->file;
@@ -940,11 +948,6 @@ next_char_1 (bool allow_quote, bool allow_argv)
}
break;
- case INPUT_MACRO:
- if (isp->u.func)
- return CHAR_MACRO;
- break;
-
case INPUT_CHAIN:
chain = isp->u.u_c.chain;
while (chain)
@@ -1013,6 +1016,9 @@ next_char_1 (bool allow_quote, bool allow_argv)
}
break;
+ case INPUT_EOF:
+ return CHAR_EOF;
+
default:
assert (!"next_char_1");
abort ();
@@ -1064,28 +1070,15 @@ init_macro_token (token_data *td)
{
token_chain *chain;
- if (isp->type == INPUT_MACRO)
+ assert (isp->type == INPUT_CHAIN);
+ chain = isp->u.u_c.chain;
+ assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
+ if (td)
{
- assert (isp->u.func);
- if (td)
- {
- TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
- TOKEN_DATA_FUNC (td) = isp->u.func;
- }
- isp->u.func = NULL;
- }
- else
- {
- assert (isp->type == INPUT_CHAIN);
- chain = isp->u.u_c.chain;
- assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func);
- if (td)
- {
- TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
- TOKEN_DATA_FUNC (td) = chain->u.func;
- }
- chain->u.func = NULL;
+ TOKEN_DATA_TYPE (td) = TOKEN_FUNC;
+ TOKEN_DATA_FUNC (td) = chain->u.func;
}
+ chain->u.func = NULL;
}
/*-------------------------------------------------------------------.
@@ -1282,8 +1275,8 @@ input_init (void)
obstack_init (&token_stack);
token_bottom = obstack_finish (&token_stack);
- isp = NULL;
- wsp = NULL;
+ isp = &input_eof;
+ wsp = &input_eof;
next = NULL;
start_of_input_line = false;
diff --git a/src/m4.h b/src/m4.h
index 54dd9da1..3e1fdbbb 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -393,11 +393,13 @@ void skip_line (const char *);
/* push back input */
void make_text_link (struct obstack *, token_chain **, token_chain **);
void push_file (FILE *, const char *, bool);
-void push_macro (builtin_func *);
+void append_macro (struct obstack *, builtin_func *, token_chain **,
+ token_chain **);
+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 (token_chain ***);
void push_wrapup_finish (void);
bool pop_wrapup (void);
void input_print (struct obstack *, const input_block *);
@@ -509,7 +511,8 @@ size_t arg_len (macro_arguments *, unsigned int);
builtin_func *arg_func (macro_arguments *, unsigned int);
struct obstack *arg_scratch (void);
bool arg_print (struct obstack *, macro_arguments *, unsigned int,
- const string_pair *, bool, const char *, size_t *, bool);
+ const string_pair *, bool, token_chain **, const char *,
+ size_t *, bool);
macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t,
bool, bool);
void push_arg (struct obstack *, macro_arguments *, unsigned int);
@@ -569,7 +572,8 @@ const char *ntoa (int32_t, int);
const builtin *find_builtin_by_addr (builtin_func *);
const builtin *find_builtin_by_name (const char *);
-void func_print (struct obstack *, const builtin *, bool, const string_pair *);
+void func_print (struct obstack *, const builtin *, bool, token_chain **,
+ const string_pair *);
/* File: path.c --- path search for include files. */
diff --git a/src/macro.c b/src/macro.c
index 6a6a90c9..c4356444 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -582,8 +582,7 @@ collect_arguments (symbol *sym, struct obstack *arguments,
argv->wrapper = args.wrapper;
argv->has_ref = args.has_ref;
argv->has_func = args.has_func;
- /* TODO allow funcs without crippling quote age. */
- if (args.quote_age != quote_age () || args.has_func)
+ if (args.quote_age != quote_age ())
argv->quote_age = 0;
argv->arraylen = args.arraylen;
return argv;
@@ -910,8 +909,6 @@ arg_type (macro_arguments *argv, unsigned int index)
type = TOKEN_TEXT;
if (type != TOKEN_TEXT)
assert (argv->has_func);
- /* TODO support TOKEN_COMP meaning concatenation of builtins. */
- assert (type != TOKEN_COMP);
return type;
}
@@ -936,7 +933,6 @@ arg_text (macro_arguments *argv, unsigned int index)
case TOKEN_TEXT:
return TOKEN_DATA_TEXT (token);
case TOKEN_COMP:
- /* TODO - concatenate functions. */
chain = token->u.u_c.chain;
obs = arg_scratch ();
while (chain)
@@ -952,7 +948,7 @@ arg_text (macro_arguments *argv, unsigned int index)
quote_cache (NULL, chain->quote_age,
chain->u.u_a.quotes),
argv->flatten || chain->u.u_a.flatten, NULL, NULL,
- false);
+ NULL, false);
break;
default:
assert (!"arg_text");
@@ -983,6 +979,7 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
token_chain tmpb;
token_chain *ca = &tmpa;
token_chain *cb = &tmpb;
+ token_chain *chain;
struct obstack *obs = arg_scratch ();
/* Quick tests. */
@@ -1041,30 +1038,34 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb)
{
if (ca->type == CHAIN_ARGV)
{
- tmpa.next = ca->next;
+ tmpa.next = NULL;
tmpa.type = CHAIN_STR;
- /* TODO support $@ with funcs. */
- assert (!ca->u.u_a.has_func || argv->flatten || ca->u.u_a.flatten);
+ tmpa.u.u_s.str = NULL;
+ tmpa.u.u_s.len = 0;
+ chain = &tmpa;
arg_print (obs, ca->u.u_a.argv, ca->u.u_a.index,
quote_cache (NULL, ca->quote_age, ca->u.u_a.quotes),
- argv->flatten || ca->u.u_a.flatten, NULL, NULL, false);
- tmpa.u.u_s.len = obstack_object_size (obs);
- tmpa.u.u_s.str = (char *) obstack_finish (obs);
- ca = &tmpa;
+ argv->flatten || ca->u.u_a.flatten, &chain, NULL, NULL,
+ false);
+ assert (obstack_object_size (obs) == 0 && chain != &tmpa);
+ chain->next = ca->next;
+ ca = tmpa.next;
continue;
}
if (cb->type == CHAIN_ARGV)
{
- tmpb.next = cb->next;
+ tmpb.next = NULL;
tmpb.type = CHAIN_STR;
- /* TODO support $@ with funcs. */
- assert (!cb->u.u_a.has_func || argv->flatten || cb->u.u_a.flatten);
+ tmpb.u.u_s.str = NULL;
+ tmpb.u.u_s.len = 0;
+ chain = &tmpb;
arg_print (obs, cb->u.u_a.argv, cb->u.u_a.index,
quote_cache (NULL, cb->quote_age, cb->u.u_a.quotes),
- argv->flatten || cb->u.u_a.flatten, NULL, NULL, false);
- tmpb.u.u_s.len = obstack_object_size (obs);
- tmpb.u.u_s.str = (char *) obstack_finish (obs);
- cb = &tmpb;
+ argv->flatten || cb->u.u_a.flatten, &chain, NULL, NULL,
+ false);
+ assert (obstack_object_size (obs) == 0 && chain != &tmpb);
+ chain->next = cb->next;
+ cb = tmpb.next;
continue;
}
if (ca->type == CHAIN_FUNC)
@@ -1223,18 +1224,21 @@ arg_scratch (void)
/* Dump a representation of ARGV to the obstack OBS, starting with
argument INDEX. If QUOTES is non-NULL, each argument is displayed
- with those quotes. If FLATTEN, builtins are ignored. Separate
- arguments with SEP, which defaults to a comma. If MAX_LEN is
- non-NULL, truncate the output after *MAX_LEN bytes are output and
- return true; otherwise, return false, and reduce *MAX_LEN by the
- number of bytes output. If QUOTE_EACH, the truncation length is
- reset for each argument, quotes do not count against length, and
- all arguments are printed; otherwise, quotes count against the
- length and trailing arguments may be discarded. */
+ with those quotes. If FLATTEN, builtins are converted to empty
+ quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise,
+ builtins are represented by their name. Separate arguments with
+ SEP, which defaults to a comma. If MAX_LEN is non-NULL, truncate
+ the output after *MAX_LEN bytes are output and return true;
+ otherwise, return false, and reduce *MAX_LEN by the number of bytes
+ output. If QUOTE_EACH, the truncation length is reset for each
+ argument, quotes do not count against length, and all arguments are
+ printed; otherwise, quotes count against the length and trailing
+ arguments may be discarded. MAX_LEN and CHAINP may not both be
+ specified. */
bool
arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
- const string_pair *quotes, bool flatten, const char *sep,
- size_t *max_len, bool quote_each)
+ const string_pair *quotes, bool flatten, token_chain **chainp,
+ const char *sep, size_t *max_len, bool quote_each)
{
size_t len = max_len ? *max_len : INT_MAX;
unsigned int i;
@@ -1245,6 +1249,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
size_t sep_len;
size_t *plen = quote_each ? NULL : &len;
+ if (chainp)
+ assert (!max_len && *chainp);
if (!sep)
sep = ",";
sep_len = strlen (sep);
@@ -1287,13 +1293,13 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
break;
case CHAIN_FUNC:
func_print (obs, find_builtin_by_addr (chain->u.func),
- flatten, quotes);
+ flatten, chainp, 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,
chain->u.u_a.quotes),
- flatten, NULL, &len, false))
+ flatten, chainp, NULL, &len, false))
done = true;
break;
default:
@@ -1310,7 +1316,7 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
break;
case TOKEN_FUNC:
func_print (obs, find_builtin_by_addr (TOKEN_DATA_FUNC (token)),
- flatten, quotes);
+ flatten, chainp, quotes);
break;
default:
assert (!"arg_print");
@@ -1319,6 +1325,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index,
}
if (max_len)
*max_len = len;
+ else if (chainp)
+ make_text_link (obs, NULL, chainp);
return false;
}
@@ -1524,11 +1532,12 @@ wrap_args (macro_arguments *argv)
struct obstack *obs;
token_data *token;
token_chain *chain;
+ token_chain **end;
if ((argv->argc == 2 || no_gnu_extensions) && arg_empty (argv, 1))
return;
- obs = push_wrapup_init ();
+ obs = push_wrapup_init (&end);
for (i = 1; i < (no_gnu_extensions ? 2 : argv->argc); i++)
{
if (i != 1)
@@ -1540,8 +1549,8 @@ wrap_args (macro_arguments *argv)
obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token));
break;
case TOKEN_FUNC:
- /* TODO allow builtins through m4wrap. */
- assert (false);
+ append_macro (obs, TOKEN_DATA_FUNC (token), NULL, end);
+ break;
case TOKEN_COMP:
chain = token->u.u_c.chain;
while (chain)
@@ -1552,14 +1561,13 @@ wrap_args (macro_arguments *argv)
obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
break;
case CHAIN_FUNC:
- /* TODO allow builtins through m4wrap. */
- assert (false);
+ append_macro (obs, chain->u.func, NULL, end);
break;
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, NULL, NULL, false);
+ chain->u.u_a.flatten, end, NULL, NULL, false);
break;
default:
assert (!"wrap_args");