diff options
author | Eric Blake <ebb9@byu.net> | 2008-02-20 21:37:30 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-02-20 21:37:30 -0700 |
commit | def1f82375ed7f310bdd8d8e1ce0c2cd9c64e2c6 (patch) | |
tree | 60d00aeea4c845a23ecfaa3ebe394172da6f8ed3 | |
parent | 50fe32db83b154f2dae2f03a71a1049050a43ebe (diff) | |
download | m4-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-- | ChangeLog | 37 | ||||
-rw-r--r-- | doc/m4.texinfo | 18 | ||||
-rw-r--r-- | m4/input.c | 77 | ||||
-rw-r--r-- | m4/m4module.h | 19 | ||||
-rw-r--r-- | m4/m4private.h | 16 | ||||
-rw-r--r-- | m4/macro.c | 99 | ||||
-rw-r--r-- | m4/symtab.c | 78 | ||||
-rw-r--r-- | m4/syntax.c | 49 | ||||
-rw-r--r-- | m4/utility.c | 25 | ||||
-rw-r--r-- | modules/m4.c | 15 | ||||
-rw-r--r-- | tests/null.m4 | bin | 6508 -> 6491 bytes | |||
-rw-r--r-- | tests/null.out | bin | 400 -> 402 bytes |
12 files changed, 287 insertions, 146 deletions
@@ -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') @@ -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 --- */ @@ -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 Binary files differindex 2fa38dd9..83e7c344 100644 --- a/tests/null.m4 +++ b/tests/null.m4 diff --git a/tests/null.out b/tests/null.out Binary files differindex c42e03c1..aca4b785 100644 --- a/tests/null.out +++ b/tests/null.out |