+struct macro_expansion_stack
+ {
+ const struct macro_expansion_stack *next;
+ const char *name;
+ const char *file_name;
+ int first_line;
+ int last_line;
+ };
+
+static void PRINTF_FORMAT (3, 4)
+macro_error (const struct macro_expansion_stack *stack,
+ const struct macro_token *mt,
+ const char *format, ...)
+{
+ struct msg_stack **ms = NULL;
+ size_t allocated_ms = 0;
+ size_t n_ms = 0;
+
+ for (const struct macro_expansion_stack *p = stack; p; p = p->next)
+ {
+ if (n_ms >= allocated_ms)
+ ms = x2nrealloc (ms, &allocated_ms, sizeof *ms);
+
+ /* TRANSLATORS: These strings are used for explaining the context of an
+ error. The "While expanding" message appears first, followed by zero
+ or more of the "inside expansion" messages. `innermost',
+ `next_inner`, etc., are names of macros, and `foobar' is a piece of
+ PSPP syntax:
+
+ foo.sps:12: At `foobar' in the expansion of 'innermost',
+ foo.sps:23: inside the expansion of 'next_inner',
+ foo.sps:34: inside the expansion of 'next_inner2',
+ foo.sps:45: inside the expansion of 'outermost',
+ foo.sps:76: This is the actual error message. */
+ char *description;
+ if (p == stack)
+ {
+ if (mt && mt->representation.length)
+ {
+ char syntax[64];
+ lex_ellipsize (mt->representation, syntax, sizeof syntax);
+ description = xasprintf (_("At `%s' in the expansion of `%s',"),
+ syntax, p->name);
+ }
+ else
+ description = xasprintf (_("In the expansion of `%s',"), p->name);
+ }
+ else
+ description = xasprintf (_("inside the expansion of `%s',"), p->name);
+
+ ms[n_ms] = xmalloc (sizeof *ms[n_ms]);
+ *ms[n_ms] = (struct msg_stack) {
+ .location = {
+ .file_name = xstrdup_if_nonnull (p->file_name),
+ .first_line = p->first_line,
+ .last_line = p->last_line,
+ },
+ .description = description,
+ };
+ n_ms++;
+ }
+
+ va_list args;
+ va_start (args, format);
+ char *s = xvasprintf (format, args);
+ va_end (args);
+
+ struct msg *m = xmalloc (sizeof *m);
+ *m = (struct msg) {
+ .category = MSG_C_SYNTAX,
+ .severity = MSG_S_ERROR,
+ .stack = ms,
+ .n_stack = n_ms,
+ .text = s,
+ };
+ msg_emit (m);
+}
+