summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2008-02-20 21:37:30 -0700
committerEric Blake <ebb9@byu.net>2008-02-20 21:37:30 -0700
commitdef1f82375ed7f310bdd8d8e1ce0c2cd9c64e2c6 (patch)
tree60d00aeea4c845a23ecfaa3ebe394172da6f8ed3
parent50fe32db83b154f2dae2f03a71a1049050a43ebe (diff)
downloadm4-def1f82375ed7f310bdd8d8e1ce0c2cd9c64e2c6.tar.gz
Stage 16: cache quotes and improve m4_arg_print.
* m4/m4module.h (m4_symbol_value_print, m4_symbol_print) (m4_arg_print): Adjust prototypes. (m4_dump_args): Delete. (m4_push_wrapup): Split... (m4_push_wrapup_init, m4_push_wrapup_finish): ...into these prototypes. * m4/m4private.h (struct m4_syntax_table): Add cached_quote member. (m4__quote_cache, m4__quote_uncache): New prototypes. * m4/syntax.c (m4_syntax_create): Initialize the cache. (m4__quote_cache): New function. (m4_set_syntax): Update caller. * m4/symtab.c (m4_symbol_value_print): Add parameter. (m4_symbol_print, dump_symbol_CB): Adjust all callers. * m4/utility.c (m4_dump_args): Delete; callers should use m4_arg_print instead. * m4/input.c (m4_push_wrapup_init, m4_push_wrapup_finish): Split implementation, and allow embedded NUL. (m4_print_token, pop_input, composite_print, composite_peek): (composite_read, append_quote_token): Adjust all callers. * m4/macro.c (trace_prepre, m4_arg_text, make_argv_ref): Likewise. (m4_arg_print): Add parameters. (trace_pre): Rewrite in terms of m4_arg_print. * modules/m4.c (errprint): Likewise. (m4wrap): Rewrite to allow embedded NUL. (dumpdef): Adjust caller. * doc/m4.texinfo (Debuglen): Enhance debuglen test. * tests/null.m4: Test for NUL in m4wrap. * tests/null.out: Update expected output. Signed-off-by: Eric Blake <ebb9@byu.net>
-rw-r--r--ChangeLog37
-rw-r--r--doc/m4.texinfo18
-rw-r--r--m4/input.c77
-rw-r--r--m4/m4module.h19
-rw-r--r--m4/m4private.h16
-rw-r--r--m4/macro.c99
-rw-r--r--m4/symtab.c78
-rw-r--r--m4/syntax.c49
-rw-r--r--m4/utility.c25
-rw-r--r--modules/m4.c15
-rw-r--r--tests/null.m4bin6508 -> 6491 bytes
-rw-r--r--tests/null.outbin400 -> 402 bytes
12 files changed, 287 insertions, 146 deletions
diff --git a/ChangeLog b/ChangeLog
index d0f4e27a..b5ae203a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,42 @@
2008-02-20 Eric Blake <ebb9@byu.net>
+ Stage 16: cache quotes and improve m4_arg_print.
+ Cache rather than always copying quotes when pushing $@ refs; in
+ particular, reconstruct single-byte quotes on the fly. Allow NUL
+ through m4wrap. Improve sharing of code that prints arguments.
+ Memory impact: slight improvement, due to cached quotes.
+ Speed impact: slight improvement, due to less copying.
+ * m4/m4module.h (m4_symbol_value_print, m4_symbol_print)
+ (m4_arg_print): Adjust prototypes.
+ (m4_dump_args): Delete.
+ (m4_push_wrapup): Split...
+ (m4_push_wrapup_init, m4_push_wrapup_finish): ...into these
+ prototypes.
+ * m4/m4private.h (struct m4_syntax_table): Add cached_quote
+ member.
+ (m4__quote_cache, m4__quote_uncache): New prototypes.
+ * m4/syntax.c (m4_syntax_create): Initialize the cache.
+ (m4__quote_cache): New function.
+ (m4_set_syntax): Update caller.
+ * m4/symtab.c (m4_symbol_value_print): Add parameter.
+ (m4_symbol_print, dump_symbol_CB): Adjust all callers.
+ * m4/utility.c (m4_dump_args): Delete; callers should use
+ m4_arg_print instead.
+ * m4/input.c (m4_push_wrapup_init, m4_push_wrapup_finish): Split
+ implementation, and allow embedded NUL.
+ (m4_print_token, pop_input, composite_print, composite_peek):
+ (composite_read, append_quote_token): Adjust all callers.
+ * m4/macro.c (trace_prepre, m4_arg_text, make_argv_ref):
+ Likewise.
+ (m4_arg_print): Add parameters.
+ (trace_pre): Rewrite in terms of m4_arg_print.
+ * modules/m4.c (errprint): Likewise.
+ (m4wrap): Rewrite to allow embedded NUL.
+ (dumpdef): Adjust caller.
+ * doc/m4.texinfo (Debuglen): Enhance debuglen test.
+ * tests/null.m4: Test for NUL in m4wrap.
+ * tests/null.out: Update expected output.
+
Fix out-of-bounds read for sanitized macro names, from 2008-02-06.
* m4/utility.c (m4_verror_at_line): Properly terminate the string.
Reported by Ralf Wildenhues.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 5ed20ea7..ffc19495 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -3860,17 +3860,25 @@ parsed as an integer.
The macro @code{debuglen} is recognized only with parameters.
@end deffn
-@comment options: -l4 -techo
+The following example demonstrates the behavior of length truncation.
+Note that each argument and the final result are individually truncated.
+Also, the special tokens for builtin functions are not truncated.
+
+@comment options: -l6 -techo -tdefn
@example
-$ @kbd{m4 -d -l 4 -t echo}
+$ @kbd{m4 -d -l 6 -t echo -t defn}
debuglen(`oops')
@error{}m4:stdin:1: Warning: debuglen: non-numeric argument `oops'
@result{}
define(`echo', `$@@')
@result{}
-echo(`long string')
-@error{}m4trace: -1- echo(`long...') -> ``lon...'
-@result{}long string
+echo(`1', `long string')
+@error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...'
+@result{}1,long string
+echo(defn(`changequote'))
+@error{}m4trace: -2- defn(`change...') -> <changequote>
+@error{}m4trace: -1- echo(`') -> ``''
+@result{}
debuglen
@result{}debuglen
debuglen(`0')
diff --git a/m4/input.c b/m4/input.c
index b5d50a18..381f38d5 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -60,7 +60,7 @@
"wrapup_stack" to "current_input" can continue indefinitely, even
generating infinite loops (e.g. "define(`f',`m4wrap(`f')')f"),
without memory leaks. Adding wrapped data is done through
- m4_push_wrapup().
+ m4_push_wrapup_init/m4_push_wrapup_finish().
Pushing new input on the input stack is done by m4_push_file(), the
conceptual m4_push_string(), and m4_push_builtin() (for builtin
@@ -124,7 +124,8 @@ static bool consume_syntax (m4 *, m4_obstack *, unsigned int);
#ifdef DEBUG_INPUT
# include "quotearg.h"
-static int m4_print_token (const char *, m4__token_type, m4_symbol_value *);
+static int m4_print_token (m4 *, const char *, m4__token_type,
+ m4_symbol_value *);
#endif
/* Vtable of callbacks for each input method. */
@@ -753,7 +754,10 @@ composite_peek (m4_input_block *me, m4 *context, bool allow_argv)
argv. */
m4_push_string_init (context);
m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
- chain->u.u_a.index, chain->u.u_a.quotes);
+ chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL,
+ chain->quote_age,
+ chain->u.u_a.quotes));
chain->u.u_a.index++;
chain->u.u_a.comma = true;
m4_push_string_finish ();
@@ -804,7 +808,10 @@ composite_read (m4_input_block *me, m4 *context, bool allow_quote, bool safe)
argv. */
m4_push_string_init (context);
m4__push_arg_quote (context, current_input, chain->u.u_a.argv,
- chain->u.u_a.index, chain->u.u_a.quotes);
+ chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL,
+ chain->quote_age,
+ chain->u.u_a.quotes));
chain->u.u_a.index++;
chain->u.u_a.comma = true;
m4_push_string_finish ();
@@ -898,8 +905,12 @@ composite_print (m4_input_block *me, m4 *context, m4_obstack *obs)
break;
case M4__CHAIN_ARGV:
assert (!chain->u.u_a.comma);
- if (m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
- chain->u.u_a.quotes, &maxlen, module))
+ if (m4_arg_print (context, obs, chain->u.u_a.argv,
+ chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
+ chain->u.u_a.quotes),
+ chain->u.u_a.flatten, NULL, &maxlen, false,
+ module))
done = true;
break;
default:
@@ -965,16 +976,13 @@ m4_input_print (m4 *context, m4_obstack *obs, m4_input_block *input)
}
}
-/* The function m4_push_wrapup () pushes a string on the wrapup stack.
- When the normal input stack gets empty, the wrapup stack will become
- the input stack, and m4_push_string () and m4_push_file () will
- operate on wrapup_stack. M4_push_wrapup should be done as
- m4_push_string (), but this will suffice, as long as arguments to
- m4_m4wrap () are moderate in size.
+/* The function m4_push_wrapup_init () returns an obstack ready for
+ direct expansion of wrapup text, and should be followed by
+ m4_push_wrapup_finish ().
FIXME - we should allow pushing builtins as well as text. */
-void
-m4_push_wrapup (m4 *context, const char *s)
+m4_obstack *
+m4_push_wrapup_init (m4 *context)
{
m4_input_block *i;
@@ -984,11 +992,25 @@ m4_push_wrapup (m4 *context, const char *s)
i->funcs = &string_funcs;
i->file = m4_get_current_file (context);
i->line = m4_get_current_line (context);
-
- i->u.u_s.len = strlen (s);
- i->u.u_s.str = obstack_copy (wrapup_stack, s, i->u.u_s.len);
-
wsp = i;
+ return wrapup_stack;
+}
+
+/* After pushing wrapup text, this completes the bookkeeping. */
+void
+m4_push_wrapup_finish (void)
+{
+ m4_input_block *i = wsp;
+ if (obstack_object_size (wrapup_stack) == 0)
+ {
+ wsp = i->prev;
+ obstack_free (wrapup_stack, i);
+ }
+ else
+ {
+ i->u.u_s.len = obstack_object_size (wrapup_stack);
+ i->u.u_s.str = (char *) obstack_finish (wrapup_stack);
+ }
}
@@ -1010,6 +1032,7 @@ pop_input (m4 *context, bool cleanup)
if (tmp != NULL)
{
obstack_free (current_input, isp);
+ m4__quote_uncache (M4SYNTAX);
next = NULL; /* might be set in m4_push_string_init () */
}
@@ -1099,8 +1122,11 @@ append_quote_token (m4 *context, m4_obstack *obs, m4_symbol_value *value)
/* TODO preserve $@ through quotes. */
if (src_chain->type == M4__CHAIN_ARGV)
{
- m4_arg_print (obs, src_chain->u.u_a.argv, src_chain->u.u_a.index,
- src_chain->u.u_a.quotes, NULL, false);
+ m4_arg_print (context, obs, src_chain->u.u_a.argv,
+ src_chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL, src_chain->quote_age,
+ src_chain->u.u_a.quotes),
+ src_chain->u.u_a.flatten, NULL, NULL, false, false);
m4__arg_adjust_refcount (context, src_chain->u.u_a.argv, false);
return;
}
@@ -1484,7 +1510,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
init_builtin_token (context, token);
next_char (context, false, true);
#ifdef DEBUG_INPUT
- m4_print_token ("next_token", M4_TOKEN_MACDEF, token);
+ m4_print_token (context, "next_token", M4_TOKEN_MACDEF, token);
#endif
return M4_TOKEN_MACDEF;
}
@@ -1492,7 +1518,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
{
init_argv_symbol (context, obs, token);
#ifdef DEBUG_INPUT
- m4_print_token ("next_token", M4_TOKEN_ARGV, token);
+ m4_print_token (context, "next_token", M4_TOKEN_ARGV, token);
#endif
return M4_TOKEN_ARGV;
}
@@ -1720,7 +1746,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line,
VALUE_MAX_ARGS (token) = -1;
#ifdef DEBUG_INPUT
- m4_print_token ("next_token", type, token);
+ m4_print_token (context, "next_token", type, token);
#endif
return type;
@@ -1751,7 +1777,8 @@ m4__next_token_is_open (m4 *context)
#ifdef DEBUG_INPUT
int
-m4_print_token (const char *s, m4__token_type type, m4_symbol_value *token)
+m4_print_token (m4 *context, const char *s, m4__token_type type,
+ m4_symbol_value *token)
{
m4_obstack obs;
size_t len;
@@ -1802,7 +1829,7 @@ m4_print_token (const char *s, m4__token_type type, m4_symbol_value *token)
if (token)
{
obstack_init (&obs);
- m4_symbol_value_print (token, &obs, NULL, NULL, true);
+ m4_symbol_value_print (context, token, &obs, NULL, false, NULL, true);
len = obstack_object_size (&obs);
xfprintf (stderr, "%s\n", quotearg_style_mem (c_maybe_quoting_style,
obstack_finish (&obs),
diff --git a/m4/m4module.h b/m4/m4module.h
index a807e70b..13f5b4b6 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -126,8 +126,6 @@ struct m4_string_pair
extern bool m4_bad_argc (m4 *, int, const char *, size_t, size_t,
bool);
extern bool m4_numeric_arg (m4 *, const char *, const char *, int *);
-extern void m4_dump_args (m4 *, m4_obstack *, size_t,
- m4_macro_args *, const char *, bool);
extern bool m4_parse_truth_arg (m4 *, const char *, const char *, bool);
/* Error handling. */
@@ -248,10 +246,10 @@ extern m4_symbol_value *m4_get_symbol_value (m4_symbol*);
extern bool m4_get_symbol_traced (m4_symbol*);
extern bool m4_set_symbol_name_traced (m4_symbol_table*,
const char *, bool);
-extern bool m4_symbol_value_print (m4_symbol_value *, m4_obstack *,
- const m4_string_pair *, size_t *,
- bool);
-extern void m4_symbol_print (m4_symbol *, m4_obstack *,
+extern bool m4_symbol_value_print (m4 *, m4_symbol_value *, m4_obstack *,
+ const m4_string_pair *, bool,
+ size_t *, bool);
+extern void m4_symbol_print (m4 *, m4_symbol *, m4_obstack *,
const m4_string_pair *, bool, size_t,
bool);
extern bool m4_symbol_value_groks_macro (m4_symbol_value *);
@@ -327,9 +325,9 @@ extern bool m4_arg_empty (m4_macro_args *, size_t);
extern size_t m4_arg_len (m4_macro_args *, size_t);
extern m4_builtin_func *m4_arg_func (m4_macro_args *, size_t);
extern m4_obstack *m4_arg_scratch (m4 *);
-extern bool m4_arg_print (m4_obstack *, m4_macro_args *,
- size_t, const m4_string_pair *,
- size_t *, bool);
+extern bool m4_arg_print (m4 *, m4_obstack *, m4_macro_args *,
+ size_t, const m4_string_pair *, bool,
+ const char *, size_t *, bool, bool);
extern m4_macro_args *m4_make_argv_ref (m4 *, m4_macro_args *, const char *,
size_t, bool, bool);
extern void m4_push_arg (m4 *, m4_obstack *, m4_macro_args *,
@@ -466,7 +464,8 @@ extern void m4_push_file (m4 *, FILE *, const char *, bool);
extern void m4_push_builtin (m4 *, m4_symbol_value *);
extern m4_obstack *m4_push_string_init (m4 *);
extern m4_input_block *m4_push_string_finish (void);
-extern void m4_push_wrapup (m4 *, const char *);
+extern m4_obstack *m4_push_wrapup_init (m4 *);
+extern void m4_push_wrapup_finish (void);
extern bool m4_pop_wrapup (m4 *);
extern void m4_input_print (m4 *, m4_obstack *, m4_input_block *);
diff --git a/m4/m4private.h b/m4/m4private.h
index 22017033..ee6dc6ae 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -437,6 +437,14 @@ struct m4_syntax_table {
these can alter the rescan of a prior parameter in a quoted
context. */
unsigned int quote_age;
+
+ /* Track a cached quote pair on the input obstack. */
+ m4_string_pair *cached_quote;
+
+ /* Storage for a simple cached quote that can be recreated on the fly. */
+ char cached_lquote[2];
+ char cached_rquote[2];
+ m4_string_pair cached_simple;
};
/* Fast macro versions of syntax table accessor functions,
@@ -462,6 +470,14 @@ struct m4_syntax_table {
age will give the same parse. */
#define m4__safe_quotes(S) (((S)->quote_age & 0xffff) != 0)
+/* Set or refresh the cached quote. */
+extern const m4_string_pair *m4__quote_cache (m4_syntax_table *,
+ m4_obstack *obs, unsigned int,
+ const m4_string_pair *);
+
+/* Clear the cached quote. */
+#define m4__quote_uncache(S) ((S)->cached_quote = NULL)
+
/* --- MACRO MANAGEMENT --- */
diff --git a/m4/macro.c b/m4/macro.c
index d6f81d80..2288dea7 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -903,8 +903,8 @@ trace_prepre (m4 *context, const char *name, size_t id, m4_symbol_value *value)
quotes = m4_get_syntax_quotes (M4SYNTAX);
trace_header (context, id);
trace_format (context, "%s ... = ", name);
- m4_symbol_value_print (value, &context->trace_messages, quotes, &arg_length,
- module);
+ m4_symbol_value_print (context, value, &context->trace_messages, quotes,
+ false, &arg_length, module);
trace_flush (context);
}
@@ -913,13 +913,10 @@ trace_prepre (m4 *context, const char *name, size_t id, m4_symbol_value *value)
static void
trace_pre (m4 *context, size_t id, m4_macro_args *argv)
{
- size_t i;
- size_t argc = m4_arg_argc (argv);
-
trace_header (context, id);
trace_format (context, "%s", M4ARG (0));
- if (1 < argc && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
+ if (1 < m4_arg_argc (argv) && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
{
const m4_string_pair *quotes = NULL;
size_t arg_length = m4_get_max_debug_arg_length_opt (context);
@@ -928,16 +925,8 @@ trace_pre (m4 *context, size_t id, m4_macro_args *argv)
if (m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE))
quotes = m4_get_syntax_quotes (M4SYNTAX);
trace_format (context, "(");
- for (i = 1; i < argc; i++)
- {
- size_t len = arg_length;
- if (i != 1)
- trace_format (context, ", ");
-
- m4_symbol_value_print (m4_arg_symbol (argv, i),
- &context->trace_messages, quotes, &len,
- module);
- }
+ m4_arg_print (context, &context->trace_messages, argv, 1, quotes, false,
+ ", ", &arg_length, true, module);
trace_format (context, ")");
}
}
@@ -1062,8 +1051,8 @@ arg_mark (m4_macro_args *argv)
Return TOKEN when successful, NULL when wrapping ARGV is trivially
empty. */
static m4_symbol_value *
-make_argv_ref (m4_symbol_value *value, m4_obstack *obs, size_t level,
- m4_macro_args *argv, size_t index, bool flatten,
+make_argv_ref (m4 *context, m4_symbol_value *value, m4_obstack *obs,
+ size_t level, m4_macro_args *argv, size_t index, bool flatten,
const m4_string_pair *quotes)
{
m4__symbol_chain *chain;
@@ -1093,19 +1082,8 @@ make_argv_ref (m4_symbol_value *value, m4_obstack *obs, size_t level,
chain->u.u_a.flatten = flatten;
chain->u.u_a.comma = false;
chain->u.u_a.skip_last = false;
- if (quotes)
- {
- /* Clone the quotes into the obstack, since changequote can
- occur before this $@ is rescanned. */
- /* TODO - optimize when quote_age is nonzero? */
- m4_string_pair *tmp = (m4_string_pair *) obstack_copy (obs, quotes,
- sizeof *quotes);
- tmp->str1 = (char *) obstack_copy0 (obs, quotes->str1, quotes->len1);
- tmp->str2 = (char *) obstack_copy0 (obs, quotes->str2, quotes->len2);
- chain->u.u_a.quotes = tmp;
- }
- else
- chain->u.u_a.quotes = NULL;
+ chain->u.u_a.quotes = m4__quote_cache (M4SYNTAX, obs, chain->quote_age,
+ quotes);
return value;
}
@@ -1218,8 +1196,10 @@ m4_arg_text (m4 *context, m4_macro_args *argv, size_t index)
obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len);
break;
case M4__CHAIN_ARGV:
- m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
- chain->u.u_a.quotes, NULL, false);
+ m4_arg_print (context, obs, chain->u.u_a.argv, chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL, chain->quote_age,
+ chain->u.u_a.quotes),
+ chain->u.u_a.flatten, NULL, NULL, false, false);
break;
default:
assert (!"m4_arg_text");
@@ -1373,32 +1353,48 @@ m4_arg_func (m4_macro_args *argv, size_t index)
/* 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 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
- MODULE, print any details about originating modules. QUOTES count
- against the truncation length, but not module names. */
+ 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. If MODULE, print
+ any details about originating modules; modules do not count against
+ truncation length. */
bool
-m4_arg_print (m4_obstack *obs, m4_macro_args *argv, size_t index,
- const m4_string_pair *quotes, size_t *max_len, bool module)
+m4_arg_print (m4 *context, m4_obstack *obs, m4_macro_args *argv, size_t index,
+ const m4_string_pair *quotes, bool flatten, const char *sep,
+ size_t *max_len, bool quote_each, bool module)
{
size_t len = max_len ? *max_len : SIZE_MAX;
size_t i;
- bool comma = false;
+ bool use_sep = false;
+ size_t sep_len;
+ size_t *plen = quote_each ? NULL : &len;
+ if (!sep)
+ sep = ",";
+ sep_len = strlen (sep);
for (i = index; i < argv->argc; i++)
{
- if (comma && m4_shipout_string_trunc (obs, ",", 1, NULL, &len))
+ if (quote_each && max_len)
+ len = *max_len;
+ if (use_sep && m4_shipout_string_trunc (obs, sep, sep_len, NULL, plen))
return true;
- comma = true;
- if (quotes && m4_shipout_string_trunc (obs, quotes->str1, quotes->len1,
- NULL, &len))
+ use_sep = true;
+ if (quotes && !quote_each
+ && m4_shipout_string_trunc (obs, quotes->str1, quotes->len1, NULL,
+ plen))
return true;
- if (m4_symbol_value_print (m4_arg_symbol (argv, i), obs, NULL, &len,
+ if (m4_symbol_value_print (context, m4_arg_symbol (argv, i), obs,
+ quote_each ? quotes : NULL, flatten, &len,
module))
return true;
- if (quotes && m4_shipout_string_trunc (obs, quotes->str2, quotes->len2,
- NULL, &len))
+ if (quotes && !quote_each
+ && m4_shipout_string_trunc (obs, quotes->str2, quotes->len2, NULL,
+ plen))
return true;
}
if (max_len)
@@ -1424,8 +1420,8 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const char *argv0,
m4_obstack *obs = m4_arg_scratch (context);
new_value = (m4_symbol_value *) obstack_alloc (obs, sizeof *value);
- value = make_argv_ref (new_value, obs, context->expansion_level - 1, argv,
- index, flatten, NULL);
+ value = make_argv_ref (context, new_value, obs, context->expansion_level - 1,
+ argv, index, flatten, NULL);
if (!value)
{
obstack_free (obs, new_value);
@@ -1527,7 +1523,8 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args *argv, bool skip,
}
/* TODO allow shift, $@, to push builtins without flatten. */
- value = make_argv_ref (&tmp, obs, -1, argv, i, true, quote ? quotes : NULL);
+ value = make_argv_ref (context, &tmp, obs, -1, argv, i, true,
+ quote ? quotes : NULL);
assert (value == &tmp);
if (len)
{
diff --git a/m4/symtab.c b/m4/symtab.c
index 9636f9df..dc05c1b3 100644
--- a/m4/symtab.c
+++ b/m4/symtab.c
@@ -533,15 +533,16 @@ m4_set_symbol_name_traced (m4_symbol_table *symtab, const char *name,
}
/* Grow OBS with a text representation of VALUE. If QUOTES, then use
- it to surround a text definition. If MAXLEN, then truncate text
+ it to surround a text definition. If FLATTEN, then flatten builtin
+ macros to the empty string. If MAXLEN, then truncate text
definitions to *MAXLEN, and adjust by how many characters are
printed. If MODULE, then include which module defined a builtin.
Return true if the output was truncated. QUOTES and MODULE do not
count against the truncation length. */
bool
-m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs,
- const m4_string_pair *quotes, size_t *maxlen,
- bool module)
+m4_symbol_value_print (m4 *context, m4_symbol_value *value, m4_obstack *obs,
+ const m4_string_pair *quotes, bool flatten,
+ size_t *maxlen, bool module)
{
const char *text;
const m4_builtin *bp;
@@ -558,18 +559,42 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs,
result = true;
break;
case M4_SYMBOL_FUNC:
- bp = m4_get_symbol_value_builtin (value);
- obstack_1grow (obs, '<');
- obstack_grow (obs, bp->name, strlen (bp->name));
- obstack_1grow (obs, '>');
+ if (flatten)
+ {
+ if (quotes)
+ {
+ obstack_grow (obs, quotes->str1, quotes->len1);
+ obstack_grow (obs, quotes->str2, quotes->len2);
+ }
+ module = false;
+ }
+ else
+ {
+ bp = m4_get_symbol_value_builtin (value);
+ obstack_1grow (obs, '<');
+ obstack_grow (obs, bp->name, strlen (bp->name));
+ obstack_1grow (obs, '>');
+ }
break;
case M4_SYMBOL_PLACEHOLDER:
- text = m4_get_symbol_value_placeholder (value);
- obstack_1grow (obs, '<');
- obstack_1grow (obs, '<');
- obstack_grow (obs, text, strlen (text));
- obstack_1grow (obs, '>');
- obstack_1grow (obs, '>');
+ if (flatten)
+ {
+ if (quotes)
+ {
+ obstack_grow (obs, quotes->str1, quotes->len1);
+ obstack_grow (obs, quotes->str2, quotes->len2);
+ }
+ module = false;
+ }
+ else
+ {
+ text = m4_get_symbol_value_placeholder (value);
+ obstack_1grow (obs, '<');
+ obstack_1grow (obs, '<');
+ obstack_grow (obs, text, strlen (text));
+ obstack_1grow (obs, '>');
+ obstack_1grow (obs, '>');
+ }
break;
case M4_SYMBOL_COMP:
chain = value->u.u_c.chain;
@@ -585,8 +610,13 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs,
result = true;
break;
case M4__CHAIN_ARGV:
- if (m4_arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index,
- chain->u.u_a.quotes, &len, module))
+ if (m4_arg_print (context, obs, chain->u.u_a.argv,
+ chain->u.u_a.index,
+ m4__quote_cache (M4SYNTAX, NULL,
+ chain->quote_age,
+ chain->u.u_a.quotes),
+ chain->u.u_a.flatten, NULL, &len, false,
+ module))
result = true;
break;
default:
@@ -623,7 +653,7 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs,
MODULE, then include which module defined a builtin. QUOTES and
MODULE do not count toward truncation. */
void
-m4_symbol_print (m4_symbol *symbol, m4_obstack *obs,
+m4_symbol_print (m4 *context, m4_symbol *symbol, m4_obstack *obs,
const m4_string_pair *quotes, bool stack, size_t arg_length,
bool module)
{
@@ -634,7 +664,7 @@ m4_symbol_print (m4_symbol *symbol, m4_obstack *obs,
assert (obs);
value = m4_get_symbol_value (symbol);
- m4_symbol_value_print (value, obs, quotes, &len, module);
+ m4_symbol_value_print (context, value, obs, quotes, false, &len, module);
if (stack)
{
value = VALUE_NEXT (value);
@@ -643,7 +673,8 @@ m4_symbol_print (m4_symbol *symbol, m4_obstack *obs,
obstack_1grow (obs, ',');
obstack_1grow (obs, ' ');
len = arg_length;
- m4_symbol_value_print (value, obs, quotes, &len, module);
+ m4_symbol_value_print (context, value, obs, quotes, false, &len,
+ module);
value = VALUE_NEXT (value);
}
}
@@ -820,15 +851,16 @@ m4_set_symbol_value_placeholder (m4_symbol_value *value, const char *text)
static void *dump_symbol_CB (m4_symbol_table *symtab, const char *name,
m4_symbol *symbol, void *userdata);
static M4_GNUC_UNUSED void *
-symtab_dump (m4_symbol_table *symtab)
+symtab_dump (m4 *context, m4_symbol_table *symtab)
{
- return m4_symtab_apply (symtab, true, dump_symbol_CB, NULL);
+ return m4_symtab_apply (symtab, true, dump_symbol_CB, context);
}
static void *
dump_symbol_CB (m4_symbol_table *symtab, const char *name,
- m4_symbol *symbol, void *ignored)
+ m4_symbol *symbol, void *ptr)
{
+ m4 * context = (m4 *) ptr;
m4_symbol_value *value = m4_get_symbol_value (symbol);
int flags = value ? SYMBOL_FLAGS (symbol) : 0;
m4_module * module = value ? SYMBOL_MODULE (symbol) : NULL;
@@ -845,7 +877,7 @@ dump_symbol_CB (m4_symbol_table *symtab, const char *name,
{
m4_obstack obs;
obstack_init (&obs);
- m4_symbol_value_print (value, &obs, NULL, NULL, true);
+ m4_symbol_value_print (context, value, &obs, NULL, false, NULL, true);
xfprintf (stderr, "%s", (char *) obstack_finish (&obs));
obstack_free (&obs, NULL);
}
diff --git a/m4/syntax.c b/m4/syntax.c
index 115884ee..74793883 100644
--- a/m4/syntax.c
+++ b/m4/syntax.c
@@ -160,6 +160,10 @@ m4_syntax_create (void)
/* Set up current table to match default. */
m4_set_syntax (syntax, '\0', '\0', NULL);
+ syntax->cached_simple.str1 = syntax->cached_lquote;
+ syntax->cached_simple.len1 = 1;
+ syntax->cached_simple.str2 = syntax->cached_rquote;
+ syntax->cached_simple.len2 = 1;
return syntax;
}
@@ -420,6 +424,7 @@ m4_set_syntax (m4_syntax_table *syntax, char key, char action,
assert (false);
}
set_quote_age (syntax, false, true);
+ m4__quote_uncache (syntax);
return code;
}
@@ -758,6 +763,50 @@ set_quote_age (m4_syntax_table *syntax, bool reset, bool change)
syntax->quote_age = 0;
}
+/* Interface for caching frequently used quote pairs, independently of
+ the current quote delimiters (for example, consider a text macro
+ expansion that includes several copies of $@), and using AGE for
+ optimization. If QUOTES is NULL, don't use quoting. If OBS is
+ non-NULL, AGE should be the current quote age, and QUOTES should be
+ m4_get_syntax_quotes; the return value will be a cached quote pair,
+ where the pointer is valid at least as long as OBS is not reset,
+ but whose contents are only guaranteed until the next changequote
+ or quote_cache. Otherwise, OBS is NULL, AGE should be the same as
+ before, and QUOTES should be a previously returned cache value;
+ used to refresh the contents of the result. */
+const m4_string_pair *
+m4__quote_cache (m4_syntax_table *syntax, m4_obstack *obs, unsigned int age,
+ const m4_string_pair *quotes)
+{
+ /* Implementation - if AGE is non-zero, then the implementation of
+ set_quote_age guarantees that we can recreate the return value on
+ the fly; so we use static storage, and the contents must be used
+ immediately. If AGE is zero, then we must copy QUOTES onto OBS,
+ but we might as well cache that copy. */
+ if (!quotes)
+ return NULL;
+ if (age)
+ {
+ *syntax->cached_lquote = (age >> 8) & 0xff;
+ *syntax->cached_rquote = age & 0xff;
+ return &syntax->cached_simple;
+ }
+ if (!obs)
+ return quotes;
+ assert (quotes == &syntax->quote);
+ if (!syntax->cached_quote)
+ {
+ assert (obstack_object_size (obs) == 0);
+ syntax->cached_quote = (m4_string_pair *) obstack_copy (obs, quotes,
+ sizeof *quotes);
+ syntax->cached_quote->str1 = (char *) obstack_copy0 (obs, quotes->str1,
+ quotes->len1);
+ syntax->cached_quote->str2 = (char *) obstack_copy0 (obs, quotes->str2,
+ quotes->len2);
+ }
+ return syntax->cached_quote;
+}
+
/* Define these functions at the end, so that calls in the file use the
faster macro version from m4module.h. */
diff --git a/m4/utility.c b/m4/utility.c
index 89b4083c..0a3296be 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -95,31 +95,6 @@ m4_numeric_arg (m4 *context, const char *caller, const char *arg, int *valuep)
return true;
}
-
-/* Print arguments from the table ARGV to obstack OBS, starting at
- index START, separated by SEP, and quoted by the current quotes, if
- QUOTED is true. */
-void
-m4_dump_args (m4 *context, m4_obstack *obs, size_t start, m4_macro_args *argv,
- const char *sep, bool quoted)
-{
- size_t i;
- size_t len = strlen (sep);
- bool need_sep = false;
- size_t argc = m4_arg_argc (argv);
-
- for (i = start; i < argc; i++)
- {
- if (need_sep)
- obstack_grow (obs, sep, len);
- else
- need_sep = true;
-
- m4_shipout_string (context, obs, M4ARG (i), M4ARGLEN (i), quoted);
- }
-}
-
-
/* Parse ARG as a truth value. If unrecognized, issue a warning on
behalf of ME and return PREVIOUS; otherwise return the parsed
value. */
diff --git a/modules/m4.c b/modules/m4.c
index afb9d0c2..eb6540b4 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -358,7 +358,8 @@ M4BUILTIN_HANDLER (dumpdef)
obstack_grow (obs, data.base[0], strlen (data.base[0]));
obstack_1grow (obs, ':');
obstack_1grow (obs, '\t');
- m4_symbol_print (symbol, obs, quotes, stack, arg_length, module);
+ m4_symbol_print (context, symbol, obs, quotes, stack, arg_length,
+ module);
obstack_1grow (obs, '\n');
}
@@ -792,7 +793,7 @@ M4BUILTIN_HANDLER (errprint)
size_t len;
assert (obstack_object_size (obs) == 0);
- m4_dump_args (context, obs, 1, argv, " ", false);
+ m4_arg_print (context, obs, argv, 1, NULL, true, " ", NULL, false, false);
m4_sysval_flush (context, false);
len = obstack_object_size (obs);
/* The close_stdin module makes it safe to skip checking the return
@@ -845,13 +846,13 @@ M4BUILTIN_HANDLER (m4exit)
version only the first. */
M4BUILTIN_HANDLER (m4wrap)
{
- assert (obstack_object_size (obs) == 0);
+ obs = m4_push_wrapup_init (context);
if (m4_get_posixly_correct_opt (context))
- m4_shipout_string (context, obs, M4ARG (1), M4ARGLEN (1), false);
+ obstack_grow (obs, M4ARG (1), M4ARGLEN (1));
else
- m4_dump_args (context, obs, 1, argv, " ", false);
- obstack_1grow (obs, '\0');
- m4_push_wrapup (context, obstack_finish (obs));
+ /* TODO allow pushing builtins. */
+ m4_arg_print (context, obs, argv, 1, NULL, true, " ", NULL, false, false);
+ m4_push_wrapup_finish ();
}
/* Enable tracing of all specified macros, or all, if none is specified.
diff --git a/tests/null.m4 b/tests/null.m4
index 2fa38dd9..83e7c344 100644
--- a/tests/null.m4
+++ b/tests/null.m4
Binary files differ
diff --git a/tests/null.out b/tests/null.out
index c42e03c1..aca4b785 100644
--- a/tests/null.out
+++ b/tests/null.out
Binary files differ