summaryrefslogtreecommitdiff
path: root/m4
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2008-01-21 12:04:45 -0700
committerEric Blake <ebb9@byu.net>2008-01-22 13:37:04 -0700
commit5307d448bacdf7f588a95f7bc44c520ce80827a6 (patch)
treef9b845b79947fde9ce7b33e2420ffcfe3d10c2e4 /m4
parent782e3ac755755787d87a5057a6631329661be3ed (diff)
downloadm4-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.c236
-rw-r--r--m4/m4module.h9
-rw-r--r--m4/m4private.h17
-rw-r--r--m4/macro.c239
-rw-r--r--m4/output.c3
-rw-r--r--m4/symtab.c150
6 files changed, 495 insertions, 159 deletions
diff --git a/m4/input.c b/m4/input.c
index 6dcaac03..0dcb0ae1 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -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 *);
diff --git a/m4/macro.c b/m4/macro.c
index 9963409e..683dd26e 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -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 ();
}