>    Now suppose @email is itself called from a macro.  In that case, @@ will 
>    be passed to execute_string more than once, and bad things will happen 
>    the second time.  Under -E, the macro-expanded output is also incorrect 
>    in some cases like this.
> 
> Okay, I'll go along with this.

Actually, I didn't mention the most important reason that macro
expansion in @email (and @xref, for that matter) cannot be postponed
until execute_string time.  (It's been a long time since I made those
changes, sorry.)

The problem is that get_xref_token relies on commas to find the
arguments of @email, and a macro inside @email can change the commas.
Consider the following fragment:

  @macro foo
  bar, baz
  @end macro

  @email{@foo{}}

Unless you expand @foo *before* you look for the commas, you get
incorrect results.

Here's another disaster like that:

  @macro addr{user, server}
  \user\@\server\
  @end macro

  @email{foo, bar}

So you MUST expand all the macros (and only macros!) before looking
for the commas.

>                             So why isn't NAME treated the same way
> as ADDR, i.e., get_xref_token (1)?

It *is* treated the same way.  get_xref_token (1) actually expands
macros inside the entire brace-delimited text, and only then gets the
first argument.  So when the next call to get_xref_token (0) is made,
all the macros are already expanded, and the expansion has replaced
the original text in the input stream.

Here's the code from get_xref_token, for your reference.  As you see,
when EXPAND is non-zero, it replaces the entire braced argument list
with its expansion (that's what the call to replace_with_expansion
does):

    char *
    get_xref_token (expand)
         int expand;
    {
      char *string;

      if (expand)
        {
          int old_offset = input_text_offset;
          int old_lineno = line_number;

          get_until_in_braces ("}", &string);
          if (curchar () == '}')    /* as opposed to end of text */
            input_text_offset++;
          if (input_text_offset > old_offset)
            {
              int limit = input_text_offset;

              input_text_offset = old_offset;
              line_number = old_lineno;
              only_macro_expansion++;
              replace_with_expansion (input_text_offset, &limit);
              only_macro_expansion--;
            }
          free (string);
        }

      get_until_in_braces (",", &string);
      if (curchar () == ',')
        input_text_offset++;
      fix_whitespace (string);
      return string;
    }

Reply via email to