diff options
author | Eric Blake <ebb9@byu.net> | 2008-01-21 12:04:45 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-01-22 13:37:04 -0700 |
commit | 5307d448bacdf7f588a95f7bc44c520ce80827a6 (patch) | |
tree | f9b845b79947fde9ce7b33e2420ffcfe3d10c2e4 /m4 | |
parent | 782e3ac755755787d87a5057a6631329661be3ed (diff) | |
download | m4-5307d448bacdf7f588a95f7bc44c520ce80827a6.tar.gz |
Stage 11: full circle for single argument references.
Pass quoted strings through to argument collection in a single
action, so that an argument can be reused throughout macro
recursion if it remains unchanged.
Memory impact: noticeable improvement, due to more reuse in
argument collection stacks.
Speed impact: noticeable improvement, due to less copying.
* m4/m4module.h (m4_arg_text): Add parameter.
(M4ARG): Adjust.
* m4/m4private.h (CHAR_QUOTE): New input engine sentinel.
(m4__make_text_link): New prototype.
(struct m4_symbol_chain): Add quote_age member.
(struct m4_symbol_value): Add end member to chained symbol.
(struct m4_macro_args): Add wrapper member.
* m4/symtab.c (m4_symbol_value_print): Print composite tokens.
(m4_symbol_value_copy, m4_symbol_value_delete): Recognize
composite tokens.
* m4/input.c (make_text_link): Rename...
(m4__make_text_link): ...to this, and export.
(m4_push_string_finish): Adjust caller.
(make_text_link, m4__push_symbol): Update new field.
(file_read, builtin_read, string_read, composite_read, next_char):
Add parameter.
(m4_skip_line, match_input, consume_syntax): Adjust callers.
(append_quote_token): New function.
(m4__next_token): Pass quoted strings onto argument collection.
(m4_print_token) [DEBUG_INPUT]: Update.
* m4/macro.c (expand_argument): Collect composite arguments.
(collect_arguments): Update new field.
(expand_macro): Reduce ref-count of back-references after use.
(arg_mark, m4_arg_symbol, m4_make_argv_ref): Adjust to new member
names.
(m4_is_arg_text): Also recognize composite symbols as text.
(m4_arg_text, m4_arg_len): Merge composite symbols as needed.
(m4_arg_equal): Compare composite symbols.
(m4_push_arg, m4_push_args): Handle composite symbols.
(m4_arg_symbol): Relax assertion.
(process_macro): Use single-argument references.
* m4/output.c (m4_shipout_string_trunc): Update comment.
* tests/macros.at (Rescanning macros): Augment test.
Signed-off-by: Eric Blake <ebb9@byu.net>
Diffstat (limited to 'm4')
-rw-r--r-- | m4/input.c | 236 | ||||
-rw-r--r-- | m4/m4module.h | 9 | ||||
-rw-r--r-- | m4/m4private.h | 17 | ||||
-rw-r--r-- | m4/macro.c | 239 | ||||
-rw-r--r-- | m4/output.c | 3 | ||||
-rw-r--r-- | m4/symtab.c | 150 |
6 files changed, 495 insertions, 159 deletions
@@ -93,29 +93,28 @@ between input blocks must update the context accordingly. */ static int file_peek (m4_input_block *); -static int file_read (m4_input_block *, m4 *, bool); +static int file_read (m4_input_block *, m4 *, bool, bool); static void file_unget (m4_input_block *, int); static bool file_clean (m4_input_block *, m4 *, bool); static void file_print (m4_input_block *, m4 *, m4_obstack *); static int builtin_peek (m4_input_block *); -static int builtin_read (m4_input_block *, m4 *, bool); +static int builtin_read (m4_input_block *, m4 *, bool, bool); static void builtin_unget (m4_input_block *, int); static void builtin_print (m4_input_block *, m4 *, m4_obstack *); static int string_peek (m4_input_block *); -static int string_read (m4_input_block *, m4 *, bool); +static int string_read (m4_input_block *, m4 *, bool, bool); static void string_unget (m4_input_block *, int); static void string_print (m4_input_block *, m4 *, m4_obstack *); static int composite_peek (m4_input_block *); -static int composite_read (m4_input_block *, m4 *, bool); +static int composite_read (m4_input_block *, m4 *, bool, bool); static void composite_unget (m4_input_block *, int); static bool composite_clean (m4_input_block *, m4 *, bool); static void composite_print (m4_input_block *, m4 *, m4_obstack *); -static void make_text_link (m4_obstack *, m4_symbol_chain **, - m4_symbol_chain **); static void init_builtin_token (m4 *, m4_symbol_value *); +static void append_quote_token (m4_obstack *, m4_symbol_value *); static bool match_input (m4 *, const char *, bool); -static int next_char (m4 *, bool); +static int next_char (m4 *, bool, bool); static int peek_char (m4 *); static bool pop_input (m4 *, bool); static void unget_input (int); @@ -133,9 +132,10 @@ struct input_funcs int (*peek_func) (m4_input_block *); /* Read input, return an unsigned char, CHAR_BUILTIN if it is a - builtin, or CHAR_RETRY if none available. If SAFE, then do not - alter the current file or line. */ - int (*read_func) (m4_input_block *, m4 *, bool safe); + builtin, or CHAR_RETRY if none available. If ALLOW_QUOTE, then + CHAR_QUOTE may be returned. If SAFE, then do not alter the + current file or line. */ + int (*read_func) (m4_input_block *, m4 *, bool allow_quote, bool safe); /* Unread a single unsigned character or CHAR_BUILTIN, must be the same character previously read by read_func. */ @@ -269,7 +269,8 @@ file_peek (m4_input_block *me) } static int -file_read (m4_input_block *me, m4 *context, bool safe M4_GNUC_UNUSED) +file_read (m4_input_block *me, m4 *context, bool allow_quote M4_GNUC_UNUSED, + bool safe M4_GNUC_UNUSED) { int ch; @@ -397,7 +398,7 @@ builtin_peek (m4_input_block *me) static int builtin_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED, - bool safe M4_GNUC_UNUSED) + bool allow_quote M4_GNUC_UNUSED, bool safe M4_GNUC_UNUSED) { if (me->u.u_b.read) return CHAR_RETRY; @@ -479,7 +480,7 @@ string_peek (m4_input_block *me) static int string_read (m4_input_block *me, m4 *context M4_GNUC_UNUSED, - bool safe M4_GNUC_UNUSED) + bool allow_quote M4_GNUC_UNUSED, bool safe M4_GNUC_UNUSED) { if (!me->u.u_s.len) return CHAR_RETRY; @@ -560,7 +561,7 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, size_t level) next->funcs = &composite_funcs; next->u.u_c.chain = next->u.u_c.end = NULL; } - make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end); + m4__make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end); chain = (m4_symbol_chain *) obstack_alloc (current_input, sizeof *chain); if (next->u.u_c.end) next->u.u_c.end->next = chain; @@ -568,6 +569,7 @@ m4__push_symbol (m4 *context, m4_symbol_value *value, size_t level) next->u.u_c.chain = chain; next->u.u_c.end = chain; chain->next = NULL; + chain->quote_age = m4_get_symbol_value_quote_age (value); chain->str = m4_get_symbol_value_text (value); chain->len = m4_get_symbol_value_len (value); chain->level = level; @@ -611,7 +613,8 @@ m4_push_string_finish (void) next->u.u_s.len = len; } else - make_text_link (current_input, &next->u.u_c.chain, &next->u.u_c.end); + m4__make_text_link (current_input, &next->u.u_c.chain, + &next->u.u_c.end); next->prev = isp; ret = isp = next; input_change = true; @@ -649,15 +652,19 @@ composite_peek (m4_input_block *me) } static int -composite_read (m4_input_block *me, m4 *context, bool safe) +composite_read (m4_input_block *me, m4 *context, bool allow_quote, bool safe) { m4_symbol_chain *chain = me->u.u_c.chain; while (chain) { + if (allow_quote && chain->quote_age == m4__quote_age (M4SYNTAX)) + return CHAR_QUOTE; if (chain->str) { if (chain->len) { + /* Partial consumption invalidates quote age. */ + chain->quote_age = 0; chain->len--; return to_uchar (*chain->str++); } @@ -668,8 +675,6 @@ composite_read (m4_input_block *me, m4 *context, bool safe) assert (!"implemented yet"); abort (); } - if (safe) - return CHAR_RETRY; if (chain->level < SIZE_MAX) m4__adjust_refcount (context, chain->level, false); me->u.u_c.chain = chain = chain->next; @@ -744,9 +749,9 @@ composite_print (m4_input_block *me, m4 *context, m4_obstack *obs) /* Given an obstack OBS, capture any unfinished text as a link in the chain that starts at *START and ends at *END. START may be NULL if *END is non-NULL. */ -static void -make_text_link (m4_obstack *obs, m4_symbol_chain **start, - m4_symbol_chain **end) +void +m4__make_text_link (m4_obstack *obs, m4_symbol_chain **start, + m4_symbol_chain **end) { m4_symbol_chain *chain; size_t len = obstack_object_size (obs); @@ -762,6 +767,7 @@ make_text_link (m4_obstack *obs, m4_symbol_chain **start, *start = chain; *end = chain; chain->next = NULL; + chain->quote_age = 0; chain->str = str; chain->len = len; chain->level = SIZE_MAX; @@ -905,13 +911,43 @@ init_builtin_token (m4 *context, m4_symbol_value *token) VALUE_MAX_ARGS (token) = block->u.u_b.builtin->max_args; } +/* When a QUOTE token is seen, convert VALUE to a composite (if it is + not one already), consisting of any unfinished text on OBS, as well + as the quoted token from the top of the input stack. Use OBS for + any additional allocations needed to store the token chain. */ +static void +append_quote_token (m4_obstack *obs, m4_symbol_value *value) +{ + m4_symbol_chain *src_chain = isp->u.u_c.chain; + m4_symbol_chain *chain; + assert (isp->funcs == &composite_funcs && obs); + + if (value->type == M4_SYMBOL_VOID) + { + value->type = M4_SYMBOL_COMP; + value->u.u_c.chain = value->u.u_c.end = NULL; + } + assert (value->type == M4_SYMBOL_COMP); + m4__make_text_link (obs, &value->u.u_c.chain, &value->u.u_c.end); + chain = (m4_symbol_chain *) obstack_copy (obs, src_chain, sizeof *chain); + if (value->u.u_c.end) + value->u.u_c.end->next = chain; + else + value->u.u_c.chain = chain; + value->u.u_c.end = chain; + value->u.u_c.end->next = NULL; + isp->u.u_c.chain = src_chain->next; +} + /* Low level input is done a character at a time. The function next_char () is used to read and advance the input to the next - character. If RETRY, then avoid returning CHAR_RETRY by popping - input. */ + character. If ALLOW_QUOTE, and the current input matches the + current quote age, return CHAR_QUOTE and leave consumption of data + for append_quote_token. If RETRY, then avoid returning CHAR_RETRY + by popping input. */ static int -next_char (m4 *context, bool retry) +next_char (m4 *context, bool allow_quote, bool retry) { int ch; @@ -931,7 +967,8 @@ next_char (m4 *context, bool retry) } assert (isp->funcs->read_func); - while ((ch = isp->funcs->read_func (isp, context, !retry)) != CHAR_RETRY + while (((ch = isp->funcs->read_func (isp, context, allow_quote, !retry)) + != CHAR_RETRY) || !retry) { /* if (!IS_IGNORE (ch)) */ @@ -960,7 +997,9 @@ peek_char (m4 *context) assert (block->funcs->peek_func); if ((ch = block->funcs->peek_func (block)) != CHAR_RETRY) { - return /* (IS_IGNORE (ch)) ? next_char (context, true) : */ ch; +/* if (IS_IGNORE (ch)) */ +/* return next_char (context, false, true); */ + return ch; } block = block->prev; @@ -969,7 +1008,7 @@ peek_char (m4 *context) /* The function unget_input () puts back a character on the input stack, using an existing input_block if possible. This is not safe - to call except immediately after next_char(context, false). */ + to call except immediately after next_char(context, allow, false). */ static void unget_input (int ch) { @@ -987,7 +1026,7 @@ m4_skip_line (m4 *context, const char *name) const char *file = m4_get_current_file (context); int line = m4_get_current_line (context); - while ((ch = next_char (context, true)) != CHAR_EOF && ch != '\n') + while ((ch = next_char (context, false, true)) != CHAR_EOF && ch != '\n') ; if (ch == CHAR_EOF) /* current_file changed; use the previous value we cached. */ @@ -1032,14 +1071,14 @@ match_input (m4 *context, const char *s, bool consume) if (s[1] == '\0') { if (consume) - next_char (context, true); + next_char (context, false, true); return true; /* short match */ } - next_char (context, true); + next_char (context, false, true); for (n = 1, t = s++; (ch = peek_char (context)) == to_uchar (*s++); ) { - next_char (context, true); + next_char (context, false, true); n++; if (*s == '\0') /* long match */ { @@ -1071,29 +1110,35 @@ match_input (m4 *context, const char *s, bool consume) /* While the current input character has the given SYNTAX, append it to OBS. Take care not to pop input source unless the next source - would continue the chain. Return true unless the chain ended with + would continue the chain. Return true if the chain ended with CHAR_EOF. */ static bool consume_syntax (m4 *context, m4_obstack *obs, unsigned int syntax) { int ch; + bool allow_quote = m4__safe_quotes (M4SYNTAX); assert (syntax); while (1) { /* It is safe to call next_char without first checking peek_char, except at input source boundaries, which we detect - by CHAR_RETRY. We exploit the fact that CHAR_EOF and - CHAR_MACRO do not satisfy any syntax categories. */ - while ((ch = next_char (context, false)) != CHAR_RETRY + by CHAR_RETRY. We exploit the fact that CHAR_EOF, + CHAR_BUILTIN, and CHAR_QUOTE do not satisfy any syntax + categories. */ + while ((ch = next_char (context, allow_quote, false)) != CHAR_RETRY && m4_has_syntax (M4SYNTAX, ch, syntax)) - obstack_1grow (obs, ch); - if (ch == CHAR_RETRY) + { + assert (ch < CHAR_EOF); + obstack_1grow (obs, ch); + } + if (ch == CHAR_RETRY || ch == CHAR_QUOTE) { ch = peek_char (context); if (m4_has_syntax (M4SYNTAX, ch, syntax)) { + assert (ch < CHAR_EOF); obstack_1grow (obs, ch); - next_char (context, true); + next_char (context, false, true); continue; } return ch == CHAR_EOF; @@ -1141,13 +1186,13 @@ m4_input_exit (void) } -/* Parse and return a single token from the input stream, built in - TOKEN. See m4__token_type for the valid return types, along with a - description of what TOKEN will contain. If LINE is not NULL, set - *LINE to the line number where the token starts. If OBS, expand - safe tokens (strings and comments) directly into OBS rather than in - a temporary staging area. Report errors (unterminated comments or - strings) on behalf of CALLER, if non-NULL. +/* Parse and return a single token from the input stream, constructed + into TOKEN. See m4__token_type for the valid return types, along + with a description of what TOKEN will contain. If LINE is not + NULL, set *LINE to the line number where the token starts. If OBS, + expand safe tokens (strings and comments) directly into OBS rather + than in a temporary staging area. Report errors (unterminated + comments or strings) on behalf of CALLER, if non-NULL. If OBS is NULL or the token expansion is unknown, the token text is collected on the obstack token_stack, which never contains more @@ -1177,7 +1222,6 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, do { obstack_free (&token_stack, token_bottom); - /* Must consume an input character, but not until CHAR_BUILTIN is handled. */ ch = peek_char (context); @@ -1186,28 +1230,29 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, #ifdef DEBUG_INPUT xfprintf (stderr, "next_token -> EOF\n"); #endif - next_char (context, true); + next_char (context, false, true); return M4_TOKEN_EOF; } if (ch == CHAR_BUILTIN) /* BUILTIN TOKEN */ { init_builtin_token (context, token); - next_char (context, true); + next_char (context, false, true); #ifdef DEBUG_INPUT m4_print_token ("next_token", M4_TOKEN_MACDEF, token); #endif return M4_TOKEN_MACDEF; } - next_char (context, true); /* Consume character we already peeked at. */ + /* Consume character we already peeked at. */ + next_char (context, false, true); file = m4_get_current_file (context); *line = m4_get_current_line (context); if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ESCAPE)) { /* ESCAPED WORD */ obstack_1grow (&token_stack, ch); - if ((ch = next_char (context, true)) != CHAR_EOF) + if ((ch = next_char (context, false, true)) < CHAR_EOF) { obstack_1grow (&token_stack, ch); if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ALPHA)) @@ -1234,12 +1279,13 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, quote_level = 1; while (1) { - ch = next_char (context, true); + ch = next_char (context, obs && m4__quote_age (M4SYNTAX), true); if (ch == CHAR_EOF) m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller, _("end of file in string")); - - if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_RQUOTE)) + if (ch == CHAR_QUOTE) + append_quote_token (obs, token); + else if (m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_RQUOTE)) { if (--quote_level == 0) break; @@ -1261,9 +1307,10 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, if (obs) obs_safe = obs; quote_level = 1; + assert (!m4__quote_age (M4SYNTAX)); while (1) { - ch = next_char (context, true); + ch = next_char (context, false, true); if (ch == CHAR_EOF) m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller, _("end of file in string")); @@ -1290,11 +1337,14 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, if (obs && !m4_get_discard_comments_opt (context)) obs_safe = obs; obstack_1grow (obs_safe, ch); - while ((ch = next_char (context, true)) != CHAR_EOF + while ((ch = next_char (context, false, true)) < CHAR_EOF && !m4_has_syntax (M4SYNTAX, ch, M4_SYNTAX_ECOMM)) obstack_1grow (obs_safe, ch); if (ch != CHAR_EOF) - obstack_1grow (obs_safe, ch); + { + assert (ch < CHAR_EOF); + obstack_1grow (obs_safe, ch); + } else m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller, _("end of file in comment")); @@ -1308,12 +1358,15 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, obs_safe = obs; obstack_grow (obs_safe, context->syntax->bcomm.string, context->syntax->bcomm.length); - while ((ch = next_char (context, true)) != CHAR_EOF + while ((ch = next_char (context, false, true)) < CHAR_EOF && !MATCH (context, ch, context->syntax->ecomm.string, true)) obstack_1grow (obs_safe, ch); if (ch != CHAR_EOF) - obstack_grow (obs_safe, context->syntax->ecomm.string, - context->syntax->ecomm.length); + { + assert (ch < CHAR_EOF); + obstack_grow (obs_safe, context->syntax->ecomm.string, + context->syntax->ecomm.length); + } else m4_error_at_line (context, EXIT_FAILURE, 0, file, *line, caller, _("end of file in comment")); @@ -1343,6 +1396,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, else if (m4_is_syntax_single_quotes (M4SYNTAX) && m4_is_syntax_single_comments (M4SYNTAX)) { /* EVERYTHING ELSE (SHORT QUOTES AND COMMENTS) */ + assert (ch < CHAR_EOF); obstack_1grow (&token_stack, ch); if (m4_has_syntax (M4SYNTAX, ch, @@ -1374,6 +1428,7 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, } else /* EVERYTHING ELSE (LONG QUOTES OR COMMENTS) */ { + assert (ch < CHAR_EOF); obstack_1grow (&token_stack, ch); if (m4_has_syntax (M4SYNTAX, ch, @@ -1394,16 +1449,21 @@ m4__next_token (m4 *context, m4_symbol_value *token, int *line, } } while (type == M4_TOKEN_NONE); - if (obs_safe != obs) + if (token->type == M4_SYMBOL_VOID) { - len = obstack_object_size (&token_stack); - obstack_1grow (&token_stack, '\0'); + if (obs_safe != obs) + { + len = obstack_object_size (&token_stack); + obstack_1grow (&token_stack, '\0'); - m4_set_symbol_value_text (token, obstack_finish (&token_stack), len, - m4__quote_age (M4SYNTAX)); + m4_set_symbol_value_text (token, obstack_finish (&token_stack), len, + m4__quote_age (M4SYNTAX)); + } + else + assert (type == M4_TOKEN_STRING); } else - assert (type == M4_TOKEN_STRING); + assert (token->type == M4_SYMBOL_COMP && type == M4_TOKEN_STRING); VALUE_MAX_ARGS (token) = -1; #ifdef DEBUG_INPUT @@ -1440,46 +1500,58 @@ m4__next_token_is_open (m4 *context) int m4_print_token (const char *s, m4__token_type type, m4_symbol_value *token) { - xfprintf (stderr, "%s: ", s ? s : "m4input"); + m4_obstack obs; + size_t len; + + obstack_init (&obs); + if (!s) + s = "m4input"; + obstack_grow (&obs, s, strlen (s)); + obstack_1grow (&obs, ':'); + obstack_1grow (&obs, ' '); switch (type) { /* TOKSW */ case M4_TOKEN_EOF: - xfprintf (stderr, "eof\n"); + obstack_grow (&obs, "eof", strlen ("eof")); + token = NULL; break; case M4_TOKEN_NONE: - xfprintf (stderr, "none\n"); + obstack_grow (&obs, "none", strlen ("none")); + token = NULL; break; case M4_TOKEN_STRING: - xfprintf (stderr, "string\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "string\t", strlen ("string\t")); break; case M4_TOKEN_SPACE: - xfprintf (stderr, "space\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "space\t", strlen ("space\t")); break; case M4_TOKEN_WORD: - xfprintf (stderr, "word\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "word\t", strlen ("word\t")); break; case M4_TOKEN_OPEN: - xfprintf (stderr, "open\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "open\t", strlen ("open\t")); break; case M4_TOKEN_COMMA: - xfprintf (stderr, "comma\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "comma\t", strlen ("comma\t")); break; case M4_TOKEN_CLOSE: - xfprintf (stderr, "close\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "close\t", strlen ("close\t")); break; case M4_TOKEN_SIMPLE: - xfprintf (stderr, "simple\t\"%s\"\n", m4_get_symbol_value_text (token)); + obstack_grow (&obs, "simple\t", strlen ("simple\t")); break; case M4_TOKEN_MACDEF: - { - const m4_builtin *bp; - bp = m4_builtin_find_by_func (NULL, m4_get_symbol_value_func (token)); - assert (bp); - xfprintf (stderr, "builtin\t<%s>{%s}\n", bp->name, - m4_get_module_name (VALUE_MODULE (token))); - } + obstack_grow (&obs, "builtin\t", strlen ("builtin\t")); break; + default: + abort (); } + if (token) + m4_symbol_value_print (token, &obs, true, "\"", "\"", SIZE_MAX, NULL); + obstack_1grow (&obs, '\n'); + len = obstack_object_size (&obs); + fwrite (obstack_finish (&obs), 1, len, stderr); + obstack_free (&obs, NULL); return 0; } #endif /* DEBUG_INPUT */ diff --git a/m4/m4module.h b/m4/m4module.h index 03025af1..330a90e5 100644 --- a/m4/m4module.h +++ b/m4/m4module.h @@ -1,7 +1,7 @@ /* GNU m4 -- A simple macro processor Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1999, 2000, 2003, - 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This file is part of GNU M4. @@ -102,8 +102,9 @@ struct m4_macro m4_module_import (context, STR (M), STR (S), obs) /* Grab the text contents of argument I, or abort if the argument is - not text. Assumes that `m4_macro_args *argv' is in scope. */ -#define M4ARG(i) m4_arg_text (argv, i) + not text. Assumes that `m4 *context' and `m4_macro_args *argv' are + in scope. */ +#define M4ARG(i) m4_arg_text (context, argv, i) extern bool m4_bad_argc (m4 *, int, const char *, unsigned int, unsigned int, bool); @@ -304,7 +305,7 @@ extern unsigned int m4_arg_argc (m4_macro_args *); extern m4_symbol_value *m4_arg_symbol (m4_macro_args *, unsigned int); extern bool m4_is_arg_text (m4_macro_args *, unsigned int); extern bool m4_is_arg_func (m4_macro_args *, unsigned int); -extern const char *m4_arg_text (m4_macro_args *, unsigned int); +extern const char *m4_arg_text (m4 *, m4_macro_args *, unsigned int); extern bool m4_arg_equal (m4_macro_args *, unsigned int, unsigned int); extern bool m4_arg_empty (m4_macro_args *, unsigned int); diff --git a/m4/m4private.h b/m4/m4private.h index 630a9b77..6a084554 100644 --- a/m4/m4private.h +++ b/m4/m4private.h @@ -35,7 +35,7 @@ typedef enum { M4_SYMBOL_TEXT, /* Plain text, u.u_t is valid. */ M4_SYMBOL_FUNC, /* Builtin function, u.func is valid. */ M4_SYMBOL_PLACEHOLDER, /* Placeholder for unknown builtin from -R. */ - M4_SYMBOL_COMP /* Composite symbol, u.chain is valid. */ + M4_SYMBOL_COMP /* Composite symbol, u.u_c.c is valid. */ } m4__symbol_type; #define BIT_TEST(flags, bit) (((flags) & (bit)) == (bit)) @@ -197,6 +197,7 @@ struct m4_symbol struct m4_symbol_chain { m4_symbol_chain *next;/* Pointer to next link of chain. */ + unsigned int quote_age; /* Quote_age of this link of chain, or 0. */ const char *str; /* NUL-terminated string if text, or NULL. */ size_t len; /* Length of str, or 0. */ size_t level; /* Expansion level of content, or SIZE_MAX. */ @@ -230,7 +231,11 @@ struct m4_symbol_value unsigned int quote_age; } u_t; /* Valid when type is TEXT, PLACEHOLDER. */ const m4_builtin * builtin;/* Valid when type is FUNC. */ - m4_symbol_chain * chain; /* Valid when type is COMP. */ + struct + { + m4_symbol_chain * chain; /* First link of the chain. */ + m4_symbol_chain * end; /* Last link of the chain. */ + } u_c; /* Valid when type is COMP. */ } u; }; @@ -248,6 +253,9 @@ struct m4_macro_args bool_bitfield inuse : 1; /* False if all arguments are just text or func, true if this argv refers to another one. */ + bool_bitfield wrapper : 1; + /* False if all arguments belong to this argv, true if some of them + include references to another. */ bool_bitfield has_ref : 1; const char *argv0; /* The macro name being expanded. */ size_t argv0_len; /* Length of argv0. */ @@ -365,7 +373,8 @@ extern void m4__symtab_remove_module_references (m4_symbol_table*, all other characters and sentinels. */ #define CHAR_EOF 256 /* Character return on EOF. */ #define CHAR_BUILTIN 257 /* Character return for BUILTIN token. */ -#define CHAR_RETRY 258 /* Character return for end of input block. */ +#define CHAR_QUOTE 258 /* Character return for quoted string. */ +#define CHAR_RETRY 259 /* Character return for end of input block. */ #define DEF_LQUOTE "`" /* Default left quote delimiter. */ #define DEF_RQUOTE "\'" /* Default right quote delimiter. */ @@ -451,6 +460,8 @@ typedef enum { M4_TOKEN_MACDEF /* Macro's definition (see "defn"), M4_SYMBOL_FUNC. */ } m4__token_type; +extern void m4__make_text_link (m4_obstack *, m4_symbol_chain **, + m4_symbol_chain **); extern bool m4__push_symbol (m4 *, m4_symbol_value *, size_t); extern m4__token_type m4__next_token (m4 *, m4_symbol_value *, int *, m4_obstack *, const char *); @@ -334,9 +334,15 @@ expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp, len = obstack_object_size (obs); if (argp->type == M4_SYMBOL_FUNC && !len) return type == M4_TOKEN_COMMA; - obstack_1grow (obs, '\0'); - VALUE_MODULE (argp) = NULL; - m4_set_symbol_value_text (argp, obstack_finish (obs), len, age); + if (argp->type != M4_SYMBOL_COMP) + { + obstack_1grow (obs, '\0'); + VALUE_MODULE (argp) = NULL; + m4_set_symbol_value_text (argp, obstack_finish (obs), len, + age); + } + else + m4__make_text_link (obs, NULL, &argp->u.u_c.end); return type == M4_TOKEN_COMMA; } /* fallthru */ @@ -360,6 +366,20 @@ expand_argument (m4 *context, m4_obstack *obs, m4_symbol_value *argp, case M4_TOKEN_STRING: if (!expand_token (context, obs, type, &token, line, first)) age = 0; + if (token.type == M4_SYMBOL_COMP) + { + if (argp->type != M4_SYMBOL_COMP) + { + argp->type = M4_SYMBOL_COMP; + argp->u.u_c.chain = token.u.u_c.chain; + } + else + { + assert (argp->u.u_c.end); + argp->u.u_c.end->next = token.u.u_c.chain; + } + argp->u.u_c.end = token.u.u_c.end; + } break; case M4_TOKEN_MACDEF: @@ -502,8 +522,23 @@ recursion limit of %zu exceeded, use -L<N> to change it"), if (BIT_TEST (VALUE_FLAGS (value), VALUE_DELETED_BIT)) m4_symbol_value_delete (value); - /* If argv contains references, those refcounts can be reduced now. */ - /* TODO - support references in argv. */ + /* If argv contains references, those refcounts must be reduced now. */ + if (argv->has_ref) + { + m4_symbol_chain *chain; + size_t i; + for (i = 0; i < argv->arraylen; i++) + if (argv->array[i]->type == M4_SYMBOL_COMP) + { + chain = argv->array[i]->u.u_c.chain; + while (chain) + { + if (chain->level < SIZE_MAX) + m4__adjust_refcount (context, chain->level, false); + chain = chain->next; + } + } + } /* We no longer need argv, so reduce the refcount. Additionally, if no other references to argv were created, we can free our portion @@ -550,6 +585,7 @@ collect_arguments (m4 *context, const char *name, size_t len, args.argc = 1; args.inuse = false; + args.wrapper = false; args.has_ref = false; /* Must copy here, since we are consuming tokens, and since symbol table can be changed during argument collection. */ @@ -587,11 +623,14 @@ collect_arguments (m4 *context, const char *name, size_t len, && m4_get_symbol_value_len (tokenp) && m4_get_symbol_value_quote_age (tokenp) != args.quote_age) args.quote_age = 0; + else if (tokenp->type == M4_SYMBOL_COMP) + args.has_ref = true; } while (more_args); } argv = (m4_macro_args *) obstack_finish (argv_stack); argv->argc = args.argc; + argv->has_ref = args.has_ref; if (args.quote_age != m4__quote_age (M4SYNTAX)) argv->quote_age = 0; argv->arraylen = args.arraylen; @@ -674,8 +713,7 @@ process_macro (m4 *context, m4_symbol_value *value, m4_obstack *obs, text = endp; } if (i < argc) - m4_shipout_string (context, obs, M4ARG (i), m4_arg_len (argv, i), - false); + m4_push_arg (context, obs, argv, i); break; case '#': /* number of arguments */ @@ -947,14 +985,14 @@ static void arg_mark (m4_macro_args *argv) { argv->inuse = true; - if (argv->has_ref) + if (argv->wrapper) { /* TODO for now we support only a single-length $@ chain. */ assert (argv->arraylen == 1 && argv->array[0]->type == M4_SYMBOL_COMP - && !argv->array[0]->u.chain->next - && !argv->array[0]->u.chain->str); - argv->array[0]->u.chain->argv->inuse = true; + && !argv->array[0]->u.u_c.chain->next + && !argv->array[0]->u.u_c.chain->str); + argv->array[0]->u.u_c.chain->argv->inuse = true; } } @@ -970,7 +1008,7 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index) if (argv->argc <= index) return &empty_symbol; - if (!argv->has_ref) + if (!argv->wrapper) return argv->array[index - 1]; /* Must cycle through all array slots until we find index, since wrappers can contain multiple arguments. */ @@ -979,7 +1017,7 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index) value = argv->array[i]; if (value->type == M4_SYMBOL_COMP) { - m4_symbol_chain *chain = value->u.chain; + m4_symbol_chain *chain = value->u.u_c.chain; /* TODO - for now we support only a single $@ chain. */ assert (!chain->next && !chain->str); if (index < chain->argv->argc - (chain->index - 1)) @@ -994,7 +1032,6 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index) else if (--index == 0) break; } - assert (value->type != M4_SYMBOL_COMP); return value; } @@ -1003,9 +1040,14 @@ m4_arg_symbol (m4_macro_args *argv, unsigned int index) bool m4_is_arg_text (m4_macro_args *argv, unsigned int index) { + m4_symbol_value *value; if (index == 0 || argv->argc <= index) return true; - return m4_is_symbol_value_text (m4_arg_symbol (argv, index)); + value = m4_arg_symbol (argv, index); + /* Composite tokens are currently sequences of text only. */ + if (m4_is_symbol_value_text (value) || value->type == M4_SYMBOL_COMP) + return true; + return false; } /* Given ARGV, return true if argument INDEX is a builtin function. @@ -1020,37 +1062,125 @@ m4_is_arg_func (m4_macro_args *argv, unsigned int index) /* Given ARGV, return the text at argument INDEX. Abort if the argument is not text. Index 0 is always text, and indices beyond - argc return the empty string. */ + argc return the empty string. The result is always NUL-terminated, + even if it includes embedded NUL characters. */ const char * -m4_arg_text (m4_macro_args *argv, unsigned int index) +m4_arg_text (m4 *context, m4_macro_args *argv, unsigned int index) { m4_symbol_value *value; + m4_symbol_chain *chain; + m4_obstack *obs; if (index == 0) return argv->argv0; if (argv->argc <= index) return ""; value = m4_arg_symbol (argv, index); - return m4_get_symbol_value_text (value); + if (m4_is_symbol_value_text (value)) + return m4_get_symbol_value_text (value); + /* TODO - concatenate argv refs and functions? For now, we assume + all chain elements are text. */ + assert (value->type == M4_SYMBOL_COMP); + chain = value->u.u_c.chain; + obs = m4_arg_scratch (context); + while (chain) + { + assert (chain->str); + obstack_grow (obs, chain->str, chain->len); + chain = chain->next; + } + obstack_1grow (obs, '\0'); + return (char *) obstack_finish (obs); } /* Given ARGV, compare text arguments INDEXA and INDEXB for equality. Both indices must be non-zero. Return true if the arguments contain the same contents; often more efficient than - !strcmp (m4_arg_text (argv, indexa), m4_arg_text (argv, indexb)). */ + !strcmp (m4_arg_text (context, argv, indexa), + m4_arg_text (context, argv, indexb)). */ bool m4_arg_equal (m4_macro_args *argv, unsigned int indexa, unsigned int indexb) { m4_symbol_value *sa = m4_arg_symbol (argv, indexa); m4_symbol_value *sb = m4_arg_symbol (argv, indexb); + m4_symbol_chain tmpa; + m4_symbol_chain tmpb; + m4_symbol_chain *ca = &tmpa; + m4_symbol_chain *cb = &tmpb; + /* Quick tests. */ if (sa == &empty_symbol || sb == &empty_symbol) return sa == sb; + if (m4_is_symbol_value_text (sa) && m4_is_symbol_value_text (sb)) + return (m4_get_symbol_value_len (sa) == m4_get_symbol_value_len (sb) + && memcmp (m4_get_symbol_value_text (sa), + m4_get_symbol_value_text (sb), + m4_get_symbol_value_len (sa)) == 0); + + /* Convert both arguments to chains, if not one already. */ /* TODO - allow builtin tokens in the comparison? */ - assert (m4_is_symbol_value_text (sa) && m4_is_symbol_value_text (sb)); - return (m4_get_symbol_value_len (sa) == m4_get_symbol_value_len (sb) - && strcmp (m4_get_symbol_value_text (sa), - m4_get_symbol_value_text (sb)) == 0); + if (m4_is_symbol_value_text (sa)) + { + tmpa.next = NULL; + tmpa.str = m4_get_symbol_value_text (sa); + tmpa.len = m4_get_symbol_value_len (sa); + } + else + { + assert (sa->type == M4_SYMBOL_COMP); + ca = sa->u.u_c.chain; + } + if (m4_is_symbol_value_text (sb)) + { + tmpb.next = NULL; + tmpb.str = m4_get_symbol_value_text (sb); + tmpb.len = m4_get_symbol_value_len (sb); + } + else + { + assert (sb->type == M4_SYMBOL_COMP); + cb = sb->u.u_c.chain; + } + + /* Compare each link of the chain. */ + while (ca && cb) + { + /* TODO support comparison against $@ refs. */ + assert (ca->str && cb->str); + if (ca->len == cb->len) + { + if (memcmp (ca->str, cb->str, ca->len) != 0) + return false; + ca = ca->next; + cb = cb->next; + } + else if (ca->len < cb->len) + { + if (memcmp (ca->str, cb->str, ca->len) != 0) + return false; + tmpb.next = cb->next; + tmpb.str = cb->str + ca->len; + tmpb.len = cb->len - ca->len; + ca = ca->next; + cb = &tmpb; + } + else + { + assert (cb->len < ca->len); + if (memcmp (ca->str, cb->str, cb->len) != 0) + return false; + tmpa.next = ca->next; + tmpa.str = ca->str + cb->len; + tmpa.len = ca->len - cb->len; + ca = &tmpa; + cb = cb->next; + } + } + + /* If we get this far, the two arguments are equal only if both + chains are exhausted. */ + assert (ca != cb || !ca); + return ca == cb; } /* Given ARGV, return true if argument INDEX is the empty string. @@ -1069,13 +1199,28 @@ size_t m4_arg_len (m4_macro_args *argv, unsigned int index) { m4_symbol_value *value; + m4_symbol_chain *chain; + size_t len; if (index == 0) return argv->argv0_len; if (argv->argc <= index) return 0; value = m4_arg_symbol (argv, index); - return m4_get_symbol_value_len (value); + if (m4_is_symbol_value_text (value)) + return m4_get_symbol_value_len (value); + /* TODO - for now, we assume all chain links are text. */ + assert (value->type == M4_SYMBOL_COMP); + chain = value->u.u_c.chain; + len = 0; + while (chain) + { + assert (chain->str); + len += chain->len; + chain = chain->next; + } + assert (len); + return len; } /* Given ARGV, return the builtin function referenced by argument @@ -1105,11 +1250,11 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const char *argv0, /* When making a reference through a reference, point to the original if possible. */ - if (argv->has_ref) + if (argv->wrapper) { /* TODO for now we support only a single-length $@ chain. */ assert (argv->arraylen == 1 && argv->array[0]->type == M4_SYMBOL_COMP); - chain = argv->array[0]->u.chain; + chain = argv->array[0]->u.u_c.chain; assert (!chain->next && !chain->str); argv = chain->argv; index += chain->index - 1; @@ -1130,10 +1275,12 @@ m4_make_argv_ref (m4 *context, m4_macro_args *argv, const char *argv0, chain = (m4_symbol_chain *) obstack_alloc (obs, sizeof *chain); new_argv->arraylen = 1; new_argv->array[0] = value; + new_argv->wrapper = true; new_argv->has_ref = true; value->type = M4_SYMBOL_COMP; - value->u.chain = chain; + value->u.u_c.chain = value->u.u_c.end = chain; chain->next = NULL; + chain->quote_age = argv->quote_age; chain->str = NULL; chain->len = 0; chain->level = context->expansion_level - 1; @@ -1170,9 +1317,23 @@ m4_push_arg (m4 *context, m4_obstack *obs, m4_macro_args *argv, return; } /* TODO handle builtin tokens? */ - assert (value->type == M4_SYMBOL_TEXT); - if (m4__push_symbol (context, value, context->expansion_level - 1)) - arg_mark (argv); + if (value->type == M4_SYMBOL_TEXT) + { + if (m4__push_symbol (context, value, context->expansion_level - 1)) + arg_mark (argv); + } + else if (value->type == M4_SYMBOL_COMP) + { + /* TODO - really handle composites; for now, just flatten the + composite and push its text. */ + m4_symbol_chain *chain = value->u.u_c.chain; + while (chain) + { + assert (chain->str); + obstack_grow (obs, chain->str, chain->len); + chain = chain->next; + } + } } /* Push series of comma-separated arguments from ARGV, which should @@ -1184,6 +1345,7 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args *argv, bool skip, bool quote) { m4_symbol_value *value; + m4_symbol_chain *chain; unsigned int i = skip ? 2 : 1; const char *sep = ","; size_t sep_len = 1; @@ -1226,8 +1388,21 @@ m4_push_args (m4 *context, m4_obstack *obs, m4_macro_args *argv, bool skip, else use_sep = true; /* TODO handle builtin tokens? */ - assert (value->type == M4_SYMBOL_TEXT); - inuse |= m4__push_symbol (context, value, context->expansion_level - 1); + if (value->type == M4_SYMBOL_TEXT) + inuse |= m4__push_symbol (context, value, + context->expansion_level - 1); + else + { + /* TODO handle composite text. */ + assert (value->type == M4_SYMBOL_COMP); + chain = value->u.u_c.chain; + while (chain) + { + assert (chain->str); + obstack_grow (obs, chain->str, chain->len); + chain = chain->next; + } + } } if (quote) obstack_grow (obs, rquote, strlen (rquote)); diff --git a/m4/output.c b/m4/output.c index f745efe8..dc2194f5 100644 --- a/m4/output.c +++ b/m4/output.c @@ -602,7 +602,8 @@ m4_shipout_string (m4 *context, m4_obstack *obs, const char *s, size_t len, current quote characters around S. If LEN is SIZE_MAX, use the string length of S instead. If MAX_LEN, reduce *MAX_LEN by LEN. If LEN is larger than *MAX_LEN, then truncate output and return - true; otherwise return false. */ + true; otherwise return false. CONTEXT may be NULL if QUOTED is + false. */ bool m4_shipout_string_trunc (m4 *context, m4_obstack *obs, const char *s, size_t len, bool quoted, size_t *max_len) diff --git a/m4/symtab.c b/m4/symtab.c index 30a61eda..3ff6f0d3 100644 --- a/m4/symtab.c +++ b/m4/symtab.c @@ -1,6 +1,6 @@ /* GNU m4 -- A simple macro processor - Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2005, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2005, 2006, + 2007, 2008 Free Software Foundation, Inc. This file is part of GNU M4. @@ -326,10 +326,21 @@ m4_symbol_value_delete (m4_symbol_value *value) m4_hash_apply (VALUE_ARG_SIGNATURE (value), arg_destroy_CB, NULL); m4_hash_delete (VALUE_ARG_SIGNATURE (value)); } - if (m4_is_symbol_value_text (value)) - free ((char *) m4_get_symbol_value_text (value)); - else if (m4_is_symbol_value_placeholder (value)) - free ((char *) m4_get_symbol_value_placeholder (value)); + switch (value->type) + { + case M4_SYMBOL_TEXT: + free ((char *) m4_get_symbol_value_text (value)); + break; + case M4_SYMBOL_PLACEHOLDER: + free ((char *) m4_get_symbol_value_placeholder (value)); + break; + case M4_SYMBOL_VOID: + case M4_SYMBOL_FUNC: + break; + default: + assert (!"m4_symbol_value_delete"); + abort (); + } free (value); } } @@ -392,10 +403,21 @@ m4_symbol_value_copy (m4_symbol_value *dest, m4_symbol_value *src) assert (dest); assert (src); - if (m4_is_symbol_value_text (dest)) - free ((char *) m4_get_symbol_value_text (dest)); - else if (m4_is_symbol_value_placeholder (dest)) - free ((char *) m4_get_symbol_value_placeholder (dest)); + switch (dest->type) + { + case M4_SYMBOL_TEXT: + free ((char *) m4_get_symbol_value_text (dest)); + break; + case M4_SYMBOL_PLACEHOLDER: + free ((char *) m4_get_symbol_value_placeholder (dest)); + break; + case M4_SYMBOL_VOID: + case M4_SYMBOL_FUNC: + break; + default: + assert (!"m4_symbol_value_delete"); + abort (); + } if (VALUE_ARG_SIGNATURE (dest)) { @@ -411,19 +433,54 @@ m4_symbol_value_copy (m4_symbol_value *dest, m4_symbol_value *src) /* Caller is supposed to free text token strings, so we have to copy the string not just its address in that case. */ - if (m4_is_symbol_value_text (src)) + switch (src->type) { - size_t len = m4_get_symbol_value_len (src); - unsigned int age = m4_get_symbol_value_quote_age (src); - m4_set_symbol_value_text (dest, - xmemdup (m4_get_symbol_value_text (src), - len + 1), len, age); + case M4_SYMBOL_TEXT: + { + size_t len = m4_get_symbol_value_len (src); + unsigned int age = m4_get_symbol_value_quote_age (src); + m4_set_symbol_value_text (dest, + xmemdup (m4_get_symbol_value_text (src), + len + 1), len, age); + } + break; + case M4_SYMBOL_FUNC: + /* Nothing further to do. */ + break; + case M4_SYMBOL_PLACEHOLDER: + m4_set_symbol_value_placeholder (dest, + xstrdup (m4_get_symbol_value_placeholder + (src))); + break; + case M4_SYMBOL_COMP: + { + m4_symbol_chain *chain = src->u.u_c.chain; + size_t len = 0; + char *str; + char *p; + while (chain) + { + /* TODO for now, only text links are supported. */ + assert (chain->str); + len += chain->len; + chain = chain->next; + } + p = str = xcharalloc (len + 1); + chain = src->u.u_c.chain; + while (chain) + { + memcpy (p, chain->str, chain->len); + p += chain->len; + chain = chain->next; + } + *p = '\0'; + m4_set_symbol_value_text (dest, str, len, 0); + } + break; + default: + assert (!"m4_symbol_value_copy"); + abort (); } - else if (m4_is_symbol_value_placeholder (src)) - m4_set_symbol_value_placeholder (dest, - xstrdup (m4_get_symbol_value_placeholder - (src))); - if (VALUE_ARG_SIGNATURE (src)) VALUE_ARG_SIGNATURE (dest) = m4_hash_dup (VALUE_ARG_SIGNATURE (src), arg_copy_CB); @@ -488,8 +545,9 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs, bool quote, size_t len; bool truncated = false; - if (m4_is_symbol_value_text (value)) + switch (value->type) { + case M4_SYMBOL_TEXT: text = m4_get_symbol_value_text (value); len = m4_get_symbol_value_len (value); if (maxlen < len) @@ -497,27 +555,45 @@ m4_symbol_value_print (m4_symbol_value *value, m4_obstack *obs, bool quote, len = maxlen; truncated = true; } - } - else if (m4_is_symbol_value_func (value)) - { - const m4_builtin *bp = m4_get_symbol_value_builtin (value); - text = bp->name; - len = strlen (text); - lquote = "<"; - rquote = ">"; - quote = true; - } - else if (m4_is_symbol_value_placeholder (value)) - { + break; + case M4_SYMBOL_FUNC: + { + const m4_builtin *bp = m4_get_symbol_value_builtin (value); + text = bp->name; + len = strlen (text); + lquote = "<"; + rquote = ">"; + quote = true; + } + break; + case M4_SYMBOL_PLACEHOLDER: text = m4_get_symbol_value_placeholder (value); /* FIXME - is it worth translating "placeholder for "? */ len = strlen (text); lquote = "<placeholder for "; rquote = ">"; quote = true; - } - else - { + break; + case M4_SYMBOL_COMP: + { + m4_symbol_chain *chain = value->u.u_c.chain; + if (quote) + obstack_grow (obs, lquote, strlen (lquote)); + while (chain) + { + /* TODO for now, assume all links are text. */ + assert (chain->str); + if (m4_shipout_string_trunc (NULL, obs, chain->str, chain->len, + false, &maxlen)) + break; + chain = chain->next; + } + if (quote) + obstack_grow (obs, rquote, strlen (rquote)); + assert (!module); + return; + } + default: assert (!"invalid token in symbol_value_print"); abort (); } |