This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Support for %d$c format specifier in diagnostics.c


Hi,

This is the third cut for the patch to support positional format
specifier for diagnostic messege print routines.

This post is mainly to let my for protocol between diagnostic.c
(specifically output_format(), the very heart of formating engine in
the file), and custom decoders such as cp_print in cp/error.c and
c_tree_printer in c-objc-common.c be known for review.  (See "[]
Protocol between diagnostic.c and custom decoder." below for details.)

Also, my preliminary changes to cp/error.c and c-objc-common.c should
make it very easy to grasp the new protocol. (The changes to
cp/error.c and c-objc-common.c are not well tested yet in real world
conditions. But as far as positional parameter is not used,
it seems they are not broken. A good sign. )

I am posting this despite the fact that changes to diagnostic.[ch] are
pending (or may have been commited in the last few hours)  by Gabriel
Dos Reis).  I will accommodate the changes in diagnostic.[ch] when
they are submitted.

This patch can use some clean up, but it can wait
until the basic scheme is studied and approved by concensus.

Again, comments/feedback welcome.

I modified the following summary document for a few spelling mistakes,
and better exposition, etc..

---


I attach the summary document first 
and then the ChangeLog and patch, etc..

============================================================
Support for %d$c postional format specifier in diagnostic.c
============================================================

Background:

%d$c here stand for positional specifier such as %1$d, and %2$p.
This is specified in SUS printf format specifier spec.

http://www.opengroup.org/onlinepubs/007904975/functions/printf.html

In gcc mailing list, Zack Weinberg suggested the implementation of
this feature in diagnostic output routines, and I have hacked
diagnostic.c to support this.

Rationale:

Why is such positional parameter desirable?  

This is mainly for helping I18N/L10N translation work.  There are
cases where the English diagnostic messages that refer to parameters
in certain order may not translate well (or only in unnatural fashion)
in other languages.

Say, original English format string refers to int value, i, character
value, c, and still another integer value, i2.

warning ("formatstring with %d, %c, %d in this order)"  , i, c, i2)
 
There are cases when a Spanish/German/French/insert-your-language-here/Japanese
translator for the said diagnostic message finds that a translated
message would be more natural if we can refer the arguments in
different order, say, i2, i, c.  However, we can't do that in the
current framework.  In such cases, a rather unnatural rearranging of
words/pharases is done to accommodate the original argument order.

For example, I have seen a few contorted Japanese messages myself both
in GCC (current) and in an old Sun Solaris 4 (SunOS 5.4) localized
messages used by its ksh. 
The messages will be understandable, if we get lucky, after a few
second thoughts. However, I had to think twice or three times before I
was sure if I understood the meaning of such messages correctly.  We
really ought to issue easy to understand messages: the user is already
struggling with the errors/problems in the source code itself, and we
should not burden them with the extra weight of dubious messages.

Please recall that the gettext() I18N/L10N framework replaces the said
format string at run time, but at this level of I18N/L10N
transformation, we can't change the ORDER of the arguments passed to
the original diagnostic message function calls.

This is where positional format specifier to the rescue!

If we want to refer to i2, i, and c in this order in the
above example, we can now use the following:

warning ("translated string with %3$d, %1$d, %2$c in this order)"  , i, c, i2)

This meets the desire of the translators very well.

[] GCC's warning/diagnostic message handling.

   See the gcc/gcc/ABOUT-GCC-NLS for general philosophy of handling
   I18N/L10N in GCC.

   The translation is done by routines in diagnostic.c.

   From a post by Zack Weinberg.

   --- begin quote ---
    > I am not sure if there are entry points in diagnostic.c which
    > do  NOT use the translation and end up with output_format() with
    > the original format string intact.
    > But I guess there must be such entry points.

    There are not supposed to be any such entry points.  All of the
    strings that are deliberately not translated, do not go through
    diagnostic.c.  Everything that diagnostic.c prints is supposed to
    be subject to translation.

    > Such usage of output_format() won't need positional parameters.
    > I have a feeling that tree dump and RTL dump(?) won't need
    > such translation either.

    You're correct that debugging dumps do not need translation, but
    they use their own printers, so you needn't worry about it.

    Tracing through diagnostic.c can be confusing.  There is a convention
    that may help: A function with an argument 'const char *msgid' has
    responsibility for getting the string translated.  This may happen
    either by a direct call to gettext (which may be written using a
    shorthand macro: _(msgid)), or by passing the string to another
    function which will take responsibility.

    In diagnostic.c, the buck stops (mostly) with diagnostic_set_info.

    Another convention which is much less widely used: a function with
    an argument 'const char *msgstr' expects to receive a string that
    has already been translated.

    More generally, string variables named 'msgid' are intended to be
    translated, but haven't yet, and string variables named 'msgstr' have
    already been translated.
  --- end quote ---

  So by modifying diagnostic.c to support positional format specifier,
  we are done (!).
  
  Aha, not quite.  It turns out the custom decoder function to support
  extended format characters need to be modified a little bit. This is
  a price we must pay.

[] Given Conditions/Limitation: Preserving the existing infrastructure.

   I looked at the original source of diagnostic.c code and noted the
   following.

   Field width specification via passed argument is supported for only
   string 's' specifier in diagnostic.c.  I didn't change this. (and
   this simplifies the handling of %n$c format specifier a little
   bit.)

   However, it turns out when the positional specifier (i.e., %n$c
   format) is used, then the argument specifying for string width must
   be given by the %n$ form as well. (This complicates the processing
   a little bit.)

   The initial patch was not thread-safe.  However, afterward, I put
   the statically allocated argument array, and arg_max into text_info
   struct.  Now the modification works and output_format is thread
   safe to the extent that the functions it calls are thread safe.
   (But I am not entirely sure if the whole mechanism including
   wrappers for output_format is thread safe. Again, thread safety is
   not a pressing requirement for GCC diagnostic purposes now.)  I
   don't see the strong need for thread-safety.  And some posters
   concurred. However, again, the modified patch now
   is thread-safe.

CAUTION/CAVEAT, etc.: 
	
   I found out that precision for integer print defines the
   width by specifiying the 
   used type (%d, %ld, %lld, ... ).

   output_integer_with_precision() macro uses the following
   precision/types.

	%d   %u
	%ld  %lu
	%lld %llu

	%w, etc, too. 

   This must be observed by my patch also.

   A new output_integer_with_precision_NEW() macro was introduced.
   This new macro takes the index to the argument array.

[] Usage Example of positional parameters:

   Firstly, words of caution.
   We should not allow/use positional parameter in source code as much as
   possible. 
   
   Use them only in PO files initially.

   Example: in the source code we may have the following call
   to print an integer, a character, and a string:

   warning("A warning message that refers to %d %c %s in this order", 
        intvalue, 
	charvalue, 
	"string");


   Now, during the I18N/L10N processing,  this warning is translated
   into, say, non-English langauge message.
   Suppose that message is better written if we refer the
   arguments in a certain different order, say, the charvalue, "string" and
   intvalue.
   In this case, the translation for the warning message should be
   written using the positional format specifier :

   "A translation that refers to %2$c, %3$s, and %1$d in this order.".

   This will be put into corresponding PO file and will be subsituted
   when the warning() is called with the original warning format string.

[] Difficulty of creating standalone test programs.

   I wish I could create an example test program.

   However, I found it difficult to write a standalone short test
   program to check calls, say, warning().  For example, routines in
   diagnostic.c requires prior envrionment initialization and I was
   not quite sure how to do that.

   I have learned the inter module dependency of diagnostic.c will
   be lessened in the future and so a standalone test program
   would be easier to make then.

   But as of now, I found out that diagnostic.o requires linking of
   various modules. Without that, unresolved symbols appear.
   So I hacked up toplev.c to insert a test call to
   warning() with various combinations of format string and arguments.
   Now, the hacked gcc calls these functions and quits.  The function
   seem to work well and also catches errors in the format
   specification strings rather well.

   The test code snipet and the output from it is given below
   in the testcase section.

    make -k check is also invoked to make sure the compiler doesn't segfault.

[] Implementation Strategy.

    To support the above, we need to scan the whole format string to find
    out the type of each i-th value passed to output_format.

    So when we see ONE format specifier in the form of %1$d format (1
    could be 2, 3, 4...), we scan the whole format string and we build an
    internal array that defines the type of i-th argument.  Afterward we
    scan the argument list and record the value of argument themselves.

    (UNLESS one such reference to a format specifier in the form of
    %n$c is done, we won't have the overhead of the array construction.)

    Requirements/Caution:

    The size of paramaters pushed on the stack can vary (say, int vs long
    long, and/or 64 bits pointer vs 32 bit int, etc.), so we must KNOW the
    size of each argument before constructing the argument value
    list.  (This is why we use a two pass algorithm below.  one for the
    format string to look for the type of each argument, 
    and then the scan of the argument list itself for the value.)

    Passing the correct types in the format string is and has always been
    the responsibility of the caller.

[] %d$n format specifier usage rule: 

    Initially, I was not entirely sure of the usage rule of %n$c
    format specifier.

    For example, can we mix the old form and positional form?

    Standard turns out to be clear. My reading was insufficient.  We
    must either use %n$ form specifiers for ALL the specifiers, or
    NONE at all.  (Thanks to Zack and Joseph Myers for pointing this
    out.)

	========================================
	standard-wise 
	----------------------------------------
	OK	           %1$d %3$c %2$s %6$d %5$s %4$c

	NOT OK             %d %s %c %6$d %5$s %4$c

	NOT OK ambiguous   %d %s %c %6$d %s %4$c
			   1  2  3   4   5  6
	In the last example, against which argument is the format specifier
	above (the fifth one) should be used?  
	----------------------------------------

    So my patch only permit ALL or NONE usage.

[] Current implementation overview.

   I have written a preliminary code/patch.

   My current Algorithm is as follows.

    First we scan the format specifier list and records the type
    AND the PRECISION IF APPROPRIATE  of i-th argument.

    Before recording, we should check the double definition
    and incompatible double definition, etc..
    (Double definition is OK as long as the previous definition is
    compatible [check is done to make sure the data width is the
    same], etc.)

    DONE (todo/fixme): double definition was not well checked for the
    custom decoder case in an interim patch. The current one
    uses the following algorithm to catch inconsitent usage.
    One way to do this is as follows.

      - check if custom_format field of i-th arg_value[] is empty or
        not. If empty, this is the first reference and we don't have
	to worry about this. (Actually we check this afterward in
	the current patch.)

      - if it is NOT empty, then copy the 
        arg_value[i].custom_format member to
        arg_value[0].custom_format member.

      - call custom decoder 
        (after setting arg_index, and format string pointer into
        text_info variable, namely text.)
        as in
        format_decoder(buffer, text, GET_ARG_TYPE)

      - check the returned value in arg_value[i].custom_format and
        compare it against arg_value[0].custom_format.
       (These are the used format character(s) for processing argument
        by the custom decoder.)

        If it `matches', then the double usage is OK.
	Otherwise, it is an error.

	Now the semantics of `match' is a little tricky to get right.
	We probably need to ask the format decoder to decide
	the compatibility of two different format strings.

	As an initial implementation, we simply use the strncmp()
	against	the two format strings. This seems practical.
	We don't want complicated decoder much.

    Secondly, then using the freshly built array of the type of
    i-th argument, we scan the real argument list and records the
    value of each argument.  If we find undefined type for used
    arg, then we abort();

    DONE (todo/fixme): Custom Format Decoder case.
    If the arg_value]i].type is UNDEFINED_FORMAT
    then we need to check the custom_format field and
    if it is set (the first element is not NUL), then we need to call
      (after setting the arg_index)
      format_decoder(buffer, text, GET_ARG_VALUE);
    If NOT, then the type for ith-argument was never
    recorded (-> error.).

    We should abort if we find syntax error during scan.  (Someone
    can provide an off-line tool to check the PO strings for such
    static problems.)

[] LIMITATION/FEATURES:

    My first implementation will
    handle only 1-9 range. 	This should suffice for GCC usage.: 

    %1$... %9$...

[] About Custom Format Decoders.

    I have put additions to handle custom format decoder as of
    now even IF positional parameter is used. (If we don't use it, the
    support remains as is and no overhead.)

    I initially wondered if anyone uses the format_decoder function.
    It turns out it is used a lot by c++, and objc front end.

    So I checked where the custom decoder is installed in current GCC
    source.

    find . -type f -name "*.[cChHjJaA]*" -print | 
             xargs egrep -i format_decoder 

    ./gcc/c-objc-common.c:  diagnostic_format_decoder (global_dc) = &c_tree_printer;
    ./gcc/diagnostic.h:#define diagnostic_format_decoder(DC) output_format_decoder (&(DC)->buffer)
    ./gcc/cp/error.c:  diagnostic_format_decoder (global_dc) = cp_printer;

    ./gcc/diagnostic.h:#define output_format_decoder(BUFFER)     (BUFFER)->format_decoder
    ./gcc/diagnostic.h:/* Same as output_format_decoder.  Works on 'diagnostic_context *'.  */
    ./gcc/diagnostic.h:#define diagnostic_format_decoder(DC) output_format_decoder (&(DC)->buffer)


[] Summary of my finding regarding custom format decoder 

    There are basically only two custom decoders in use.
 
    They are
    - c_tree_printer in c-objc-common.c
    - cp_printer in cp/error.c

    My initial observation is as follows.
    c_tree_printer is easy to handle.
    cp_printer is a little complicated, but still manageable.

    Complication of cp/error.c:

    cp/error.c contains the following code. Namely,
    it handles '+', and '#' which can prefix the extended format characters.


    if (*text->format_spec == '+')
      ++text->format_spec;
    if (*text->format_spec == '#')
      {
        verbose = 1;
        ++text->format_spec;
      }

    switch (*text->format_spec)
      {
      ...

    So we must take care of multiple format specifier character
    as well. 

   (This complicates the skipping of substring used by custom format
    decoder. It also complicates the "match"ing of two positional
    references to the same i-th argument in format string.  We need to
    be sure that they refer to the same type.  In the above, the use
    of "#" doesn't change meaning of the following format character as
    far as the type is concerned. It simply change the output
    verboseness.  But since we use strncmp() for simplicity now, and
    so the use of "#" can change the "match"ing of type.)

    For skipping the substring properly, we also need to extract the
    length of the format specifier string used up by the custom
    decoder as well.

[] A half-hearted kludge doesn't work.

    I was wondering if we could do away with a rigid protocol between
    diagnostic.c and custom format decoders.
    I thought we might be able to use
    a loose kludge I mentioned in my post : let the decoder produce a
    dummy output and extract information from the output string itself.
    (This makes it possible to use the current custom format decoder
    WITHOUT modification.).

    But it is not usable.  A counter example is as follows.  Imagine
    the following case where ??? stands for the decoder-extended
    format specifier characters.

    Now, Suppose we allow the current decoder interface to remain and
    try to use my half-heated kludge to let the custom decoder to
    produce an output string image and trying to extract information
    from it.

    We need to pass an appropriate value to the decoder somehow when
    we ask it to process "???" part in "%3$???" in the following
    format.

    error("%3$???   %2$??? %1$???", arg1, arg2, arg3);

    But, we don't know the 3rd argument stack position until
    we know the width (on the stack) of the argument 2 and 1.
    So we can't know the value of argument 3 in my half-heated
    scheme when we need it.
    My idea of letting custom decoder to produce a dummy output
    fails here.

    (We might simply pass a dummy value assuming there is no dire side
     effect, but if the decoder uses the passed value as pointer
     value, which is often the case in the currently used frontend,
     then we might end up with segfault.)
    
    So, again, we have to ask the decoder for the size of argument 2
    and argument 1.  My simple linear scan of format string doesn't
    work unless we obtain the type/size info from the custom decoder
    using a well defined protocol.

    So again, I am now convinced that we need to ask the decoder
    explicitly using a well-defined protocol. There is no short cut.
    
    Since there are only TWO (2) custom decoders in use,	
    we should be able to modify them all without much difficulty.

[] Protocol between diagnostic.c and custom decoder.

    In diagnostic.h, I defined an enum type to enumerate the
    actions we want the custom decoders to perform.

    /* Instruct custom defined format decoder to
       return information for processing. */
    typedef enum 
    {
      GET_ARG_TYPE = 2,
      COPY_ARG_VALUE = 3,
      PRINT_ARG_VALUE = 4,
      PRINT_ARG_VALUE_AS_BEFORE = 5 /* as before: no positional support */
    } arg_decode_action;


    Now the  format_decoder takes an extra argument at the end 
    and is called as follows:
    format_decoder(buffer, text, GET_ARG_TYPE)
    format_decoder(buffer, text, COPY_ARG_VALUE)
    format_decoder(buffer, text, PRINT_ARG_VALUE)
    format_decoder(buffer, text, PRINT_ARG_VALUE_AS_BEFORE)

    text_info data type is exteneded to hold
    temporay values for positional parameter handling.

[]  What to do for each arg_decode_action value?

    The fouth  call with PRINT_ARG_VALUE_AS_BEFORE
    is a request to print argument as has been the case without the
    positional parameter. So the action should preserve the
    current (pre-postional parameter ) behavior.

    GET_ARG_TYPE:

    format_decoder is called:
    format_decoder(buffer, text, GET_ARG_TYPE)

    Input: format string and its current postion immedaitely AFTER '%'
           or immediately after '$' as in %1$.
	   argument index position in format positional spec. (e.g., 2
           for %2$)

	   passed as text_info struct member:
	   text->index_num = arg_index;
	   text->format_after_percent_or_dollar = text->format_spec;

    Return value:
       true if the format is understood.
       false if not understood.	   

    Output returned in text (text_info *).

    (a) int text->bump_format:
        How many format characters it will be consumed (cp/error.c can use
        a few characters.)

	This is necessary so that we can skip over the
	non-standard format character before continuing with
	the rest of the format spcification.

	If the skip amount is one (1), then
	the output_format loop in diagnostic.c handles it automatically, but
	there are cases where multiple characters needs to be skipped.
	In that case, we need to bump format string pointer additionally.

	(This part could be optimized. The original printing 
	by custom format decoders modifies the format string pointer
	directly, but we don't want it to modify it now
	during positional parameter processing. I will see if I can
	simplify this.)

    (b)	The format substring understood by custom decoder
        is stored in text->custom_format[];

        Basically we want to know the type/size of the second argument
	used in the  va_arg
	as in the "#define next_tree    va_arg (*text->args_ptr,
        tree)" or "#define next_int     va_arg (*text->args_ptr, int)".
        as well as  type information.

	There is no easy way to pass the type information itself, so
	we let the custom decoder stores the format string 
	itself which it groks.

	As a general comment, we have to admit that

	- C's variable argument handling is a kludge and
	  works well only when the stack is a linear array
	  and all the argumets are passed in the stack (and
	  not in registers).

	So I admit that my current quick/hack/implementation may not
	be perfect.

        From a post by Joseph Myers:

|    >    (We essentially need the width of the argument to bump the pointer
|    > for
|    >    variable argument processing  is necessary. So all we need is
|    >    the # of bytes for the argument type. The value itself can be opaque,
|    >    and will be stored in an appropriate argumet array cell.)

|    We need the actual type, not the width.  Passing the wrong type to va_arg
|    will lead to (a) aliasing issues (generating wrong code), (b) problems
|    with ABIs that handle integer/floating point/pointer arguments differently
|    in variable arguments (if such ABIs aren't currently in use, someone will
|    one day invent one), (c) problems when someone implements a safer ABI that
|    stores information about argument types and actually checks at runtime
|    that they are correct.  So you should always store information about the 
|    actual type (distinguishing "int" and "long" even if the same size, for 
|    example).
|
|    For the format decoders, I'd guess that just the character 'D', 'T', etc.,
|    is sufficient to determine what the type is, and so can be stored as an
|    opaque identifier itself.  So you need (a) a list of valid characters for
|    the front end (needn't be a function, though using a function would keep
|    more generality), (b) a function to extract and store the relevant value,
|    (c) a function to print the saved value.  Trying to call va_arg to extract
|    the value from within diagnostic.c, using some substitute type because
|    diagnostic.c shouldn't know about "tree" etc., would I think end up being
|    a mistake.


	- An attractive and type-safe architecture mentioned in 
	  Joseph Myers's post (quoted above)  is nowhere in sight at least
	  commercially. 
	  I am afraid that the popularity of ISO-C
	  is hindering the adoption of such user-friendly architecture
	  due to the difficulty of some ISO-C construct such as
	  the variable argument list handling.!

	  I propose we might as well postpone the "perfect" implementation
	  until such architecture finally arrives and we are given
	  the variable argumetn handling implementation on such
	  architecture. Then, we can hack it to support our scheme.


    COPY_ARG_VALUE action:

    Custom format_decoder is called:
    format_decoder(buffer, text, COPY_ARG_VALUE)

    This is called within a loop in diagnostic.c
    So the arg_ptr is bumped per argument as we visit it.
    for(arg_index = 1; arg_index <= arg_max; arg_index++)
    {
        ...
        ....format_decoder(buffer, text, COPY_ARG_VALUE)  
    }       

    Input: text->custom_format[];
	   arg index position.

    passed as part of text_info struct member:
	   text->index_num = arg_index;
	   text->format_after_percent_or_dollar = text->custom_format;

    Return value:
       true if the format is understood.
       false if not understood.	   

    Action: Use text->text->format_after_percent_or_dollar
            string and copy the value into 
	    text->arg_array[text->index_num].v.xxx 

	    (Custom decoder is requested to use appropriate union
	    member for the value. This union member is used
	    later during PRINT_ARG_VALUE processing.
	    We can possibly add another union member, but
	    be advised to use an existing one. See cp/error.c
	    modification.)

    PRINT_ARG_VALUE

    Custom format_decoder is called:
    format_decoder(buffer, text, PRINT_ARG_VALUE)

    Input: format string and its current postion immedaitely AFTER '%'
           or immediately after '$' as in %1$.
	   arg index position.

	   passed as text_info struct member:
	   text->index_num = arg_index;
	   text->format_after_percent_or_dollar = text->format_spec;

     Action:
     print, i.e., insert the string image of the value of
     text->arg_array[text->index_num].v.xxx into string buffer.


cp/error.c and c-objc-common.c have been modified
according to the above scheme.

Comments/feedback welcome.

[] A thought about Format checker.

   Necessity of format checker to handle postional paramter spec.

   msgfmt from GNU gettext() leaves much room for improvement.

   My current thinking: Not much.

   We should not allow positional parameter in source code as much as
   possible.
   
   Use them only in PO files initially.

   So a separate PO file checker is in order.

   (Too bad GNU gettext's msgfmt is not very good at it yet according
   to the related thread in gcc-patches mailing list.  We also need to
   add the support for GCC diagnostic format specifier extension.)

   Quote from a post by Joseph Myers			

  |> Yes, for the format checker, 
  |> I will use the data definitions, etc. as implemented in your patch, but
  |> I am not quite sure how to accommodate the 
  |> positional parameter in the checker framework.
  |
  |There is full support for such formats in the checking code - you'd need
  |to set the flag FMT_FLAG_USE_DOLLAR and possibly FMT_FLAG_DOLLAR_MULTIPLE.   
  |That doesn't mean there would be any purpose other than obfuscation for
  |actually using them directly in source files.

  |Although the support for checking such formats is probably little used -
  |as they are generally used in translations only - at least one person must
  |have used that support (and so $ formats in the source file) in the past,
  |because there was once a bug report about it (6547).

I would study this mechanism to see how we can
possibly enhance PO checking tool, etc.


[] Surprise: custom decoder used a lot.
   
   Investigating as above.

----------------------------------------

TestCase:
	Below I explain the quick test.

	But I wonder how to test extensively?

        make -k check is NOT a satisfactory test since
        the error / warning messages must be
        generated by running program source code that
        triggers such error/warning message output.

        PO files need separate sweep somehow since
        it is not untenable to switch the LANG/LC_MESSAGE/LC_ALL
        setting, etc. for all supported language environment.
 

Quick testcase: 

  For preliminary testing, I embedded the following into toplev.c 
  immediately after

	  >    /* Initialization of GCC's environment, and diagnostics.  */
	  >     general_init (argv[0]);

	  and before
	  
	  >  /* Parse the options and do minimal processing; basically just
	  >     enough to default flags appropriately.  */
	  >  decode_options (argc, argv);

	  The routines worked as expected, I think.
	  
The code snipet to test the operation of positional parameters.

#if 1

{
#include <errno.h>
  int i;
  long t;
  long long ill;
  long long xll;
  extern void warning (const char *msgid, ...);

  static char str [] = "123456789A123456789A123456789A";

  i = 1234;
  t = 0x0FFFFFFl;
  ill = (long long) t * t;
  xll = 0x0123456789ABCDEFull;

  warning("Test diagnostic.\n");

  warning("integer=%d, pointer=%p, char=%c", i, &t, 'c');

  warning("\nPointer value print\n");

  /* fprintf(stderr,"pointer=0x%08x\n", (unsigned int) &t);*/
  fprintf(stderr,"pointer=%p\n",  (void *) &t);
  warning("1st: pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
  warning("2nd: pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
  warning("3rd: pointer=%1$p, char=%3$c, integer=%2$d", &t, i, 'c');
  warning("pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');


  warning("\nOne argument");
  warning("integer=%1$d", i);
  warning("char   =%1$c", 'c');
  warning("pointer=%1$p", &t);
  warning("long=%1$ld", t);
  warning("long=%1$lx", t);


  /* long */
  warning("long=(x) %1$lx", t);
  fprintf(stderr," ordinary printf: %lx\n", t);
  warning("long=(o) %1$lo", t);
  fprintf(stderr," ordinary printf: %lo\n", t);
  warning("long=(d) %1$ld", t);
  fprintf(stderr," ordinary printf: %ld\n", t);


  /* long long */

  warning("long long=(d) %1$lld", ill);
  fprintf(stderr,"ordinary fprintf(): %lld\n", ill);


  warning("long long=(x) %1$llx", xll);
  fprintf(stderr," ordinary printf: %llx\n", xll);


  warning("long long=(o) %1$llo", xll);
  fprintf(stderr," ordinary printf: %llo\n", xll);


  warning("long long=(d) %1$lld", xll);
  fprintf(stderr," ordinary printf: %lld\n", xll);

  /* wide ? */

  warning("Wide\n");
  warning("wide=(wd) %1$wd", t);
  warning("wide=(wx) %1$wx", t);
  warning("wide=(wo) %1$wo", t);

  warning("\nTwo arguments");
  warning("integer=%1$d, char=%2$c", i, 'c');
  warning("char   =%2$c, integer=%1$d", i, 'c');

  warning("\nThree Arguments\n");
  warning("char=%3$c, integer=%1$d, pointer=%2$p", i, &t, 'c');

  warning("char=%c, integer=%d, pointer=%p", 'c', i, &t);
  warning("pointer=%3$p, char=%1$c, integer=%2$d", 'c', i, &t);
  warning("char=%1$c, integer=%2$d, pointer=%3$p", 'c', i, &t);

  warning("integer=%d, long=%ld, long long=%lld", i, t, ill);
  warning("long=%2$ld, long long=%3$lld, int=%1$d", i, t, ill);
  warning("long long=%3$lld, int=%1$d, long=%2$ld", i, t, ill);

  warning("pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');

  warning("long long=%lld, long=%ld, int=%d", ill, t, i);
  warning("long=%2$ld, int=%3$d, long long=%1$lld", ill, t, i);
  warning("int=%3$d, long long=%1$lld, long=%2$ld", ill, t, i);


  /* String and its width*/
  warning("\nString");

  warning("String =%s", str);
  warning("String =%1$s", str);  
  fprintf(stderr,"ordinary fprintf(): %s\n", str);

  warning("String = %.*s, extra=%d",           10, str, i);
  warning("String = %3$.*1$s, extra=%2$d",     10, i, str);
  warning("String = %2$.*1$s, extra=%3$d",     10, str, i);
  fprintf(stderr,"ordinary fprintf(): %.*s\n", 10, str);

  /* %% and %m */

  warning("This is one %% (percent sign)  and error code message: could be random <<%m>>");


  errno = EPIPE;
  warning("int=%1$d, long long=%2$lld,  %% (percent sign) and error message should be for EPIPE <<%m>>", i, ill);


  /* should abort: we can't mix two forms. */
  warning("Should abort: integer=%d, pointer=%p, char=%3$c\n", i, &t, 'c');

  /* should abort because %3$whatever is missing. */
  warning("Should abort: char=%4$p, integer=%1$d, pointer=%2$p\n", i, &t, 'c');

  /* should abort: we can't mix two forms */
  warning("Should abort: integer=%1$d, pointer=%2$p, char=%c\n", i, &t, 'c');

  /* should abort: 0 after %. */
  warning("Should abort: integer=%0$d, pointer=%2$p, char=%3c\n", i, &t, 'c'); 

}

#endif

OUTPUT from the above:

cc1: warning: Test diagnostic.

cc1: warning: integer=1234, pointer=0xbffff300, char=c
cc1: warning: 
   Pointer value print

pointer=0xbffff300
cc1: warning: 1st: pointer=0xbffff300, char=c, integer=1234
cc1: warning: 2nd: pointer=0xbffff300, char=c, integer=1234
cc1: warning: 3rd: pointer=0xbffff300, char=c, integer=1234
cc1: warning: pointer=0xbffff300, char=c, integer=1234
cc1: warning: 
   One argument
cc1: warning: integer=1234
cc1: warning: char   =c
cc1: warning: pointer=0xbffff300
cc1: warning: long=16777215
cc1: warning: long=ffffff
cc1: warning: long=(x) ffffff
 ordinary printf: ffffff
cc1: warning: long=(o) 77777777
 ordinary printf: 77777777
cc1: warning: long=(d) 16777215
 ordinary printf: 16777215
cc1: warning: long long=(d) 281474943156225
ordinary fprintf(): 281474943156225
cc1: warning: long long=(x) 123456789abcdef
 ordinary printf: 123456789abcdef
cc1: warning: long long=(o) 4432126361152746757
 ordinary printf: 4432126361152746757
cc1: warning: long long=(d) 81985529216486895
 ordinary printf: 81985529216486895
cc1: warning: Wide

cc1: warning: wide=(wd) 16777215
cc1: warning: wide=(wx) 0xffffff
cc1: warning: wide=(wo) 77777777
cc1: warning: 
   Two arguments
cc1: warning: integer=1234, char=c
cc1: warning: char   =c, integer=1234
cc1: warning: 
   Three Arguments

cc1: warning: char=c, integer=1234, pointer=0xbffff300
cc1: warning: char=c, integer=1234, pointer=0xbffff300
cc1: warning: pointer=0xbffff300, char=c, integer=1234
cc1: warning: char=c, integer=1234, pointer=0xbffff300
cc1: warning: integer=1234, long=16777215, long long=281474943156225
cc1: warning: long=16777215, long long=281474943156225, int=1234
cc1: warning: long long=281474943156225, int=1234, long=16777215
cc1: warning: pointer=0xbffff300, char=c, integer=1234
cc1: warning: long long=281474943156225, long=16777215, int=1234
cc1: warning: long=16777215, int=1234, long long=281474943156225
cc1: warning: int=1234, long long=281474943156225, long=16777215
cc1: warning: 
   String
cc1: warning: String =123456789A123456789A123456789A
cc1: warning: String =123456789A123456789A123456789A
ordinary fprintf(): 123456789A123456789A123456789A
cc1: warning: String = 123456789A, extra=1234
cc1: warning: String = 123456789A, extra=1234
cc1: warning: String = 123456789A, extra=1234
ordinary fprintf(): 123456789A
cc1: warning: This is one % (percent sign)  and error code message: could be random <<No such file or directory>>
cc1: warning: int=1234, long long=281474943156225,  % (percent sign) and error message should be for EPIPE <<Broken pipe>>
Aborting output formatting: We can't mix %d and %n$d form

format=<<Should abort: integer=%d, pointer=%p, char=%3$c
>>
cc1: warning: Should abort: integer=1234, pointer=0xbffff300, char=
Internal compiler error: Error reporting routines re-entered.
Please submit a full bug report,
with preprocessed source if appropriate.
See <URL:http://gcc.gnu.org/bugs.html> for instructions.
make: *** [crtbegin.o] Error 1

(CI's comment: the abort is due to the fact that
the abort is done within an error handling routine
and some function tries to invoke another diagnostic instance.)


ChangeLog
2003-07-25    <ishikawa@yk.rim.or.jp>
	
	New support for  positional format specifier.
	
	* diagnostic.c (_helper_abort): print the problematic format string
	  and abort.
	  (_type_width): returns the argument width based
	  on format specifier type.
	  (compatible_and_has_same_width): check if the double use of
	  an argument uses the same type and width.
	  (set_argument_type_with_precision): store the argument type in 
	  a static array.
	  (set_argument_type): store the argument type without precision.
	  (output_integer_with_precision_NEW): Macro based on the
	  original output_integer_with_precision_NEW. Uses the static
	  argument array freshly created to fetch the value.
	  (build_argument_array): build an array of argument to support
	  positional format specifier.
	  (output_format): modified to work with the positional format
	  speciffer support.

        * diagnostic.h : text_info now holds additional members such
          as arg_array[], etc.
	   
        * c-objc-common.c (c_tree_printer):    takes additional third 
	  argument and support  positional argument handling.
        * cp/error.c (cp_printer): ditto.
	                

Bootstrapping and Testing

      compiled and tested within the GCC object directory
      configured as
      i686-pc-linux-gnu

      It was tested using the aforementioned codelet.

      

Patch itself.

---

/u2/tools/gcc-mainline-cvs/gcc
? gcc/diagnostic-post.txt
? gcc/e.diff
? gcc/po/messages
? gcc/testsuite/gcc.dg/gmon.out
Index: gcc/c-objc-common.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/c-objc-common.c,v
retrieving revision 1.29
diff -c -3 -p -r1.29 c-objc-common.c
*** gcc/c-objc-common.c	18 Jul 2003 23:05:53 -0000	1.29
--- gcc/c-objc-common.c	25 Jul 2003 02:56:32 -0000
*************** Software Foundation, 59 Temple Place - S
*** 39,45 ****
  #include "target.h"
  #include "cgraph.h"
  
! static bool c_tree_printer (output_buffer *, text_info *);
  static tree inline_forbidden_p (tree *, int *, void *);
  static void expand_deferred_fns (void);
  static tree start_cdtor (int);
--- 39,45 ----
  #include "target.h"
  #include "cgraph.h"
  
! static bool c_tree_printer (output_buffer *, text_info *, arg_decode_action);
  static tree inline_forbidden_p (tree *, int *, void *);
  static void expand_deferred_fns (void);
  static tree start_cdtor (int);
*************** c_objc_common_finish_file (void)
*** 414,449 ****
     These format specifiers form a subset of the format specifiers set used
     by the C++ front-end.
     Please notice when called, the `%' part was already skipped by the
!    diagnostic machinery.  */
  static bool
! c_tree_printer (output_buffer *buffer, text_info *text)
  {
!   tree t = va_arg (*text->args_ptr, tree);
  
!   switch (*text->format_spec)
      {
!     case 'D':
!     case 'F':
!     case 'T':
!       {
!         const char *n = DECL_NAME (t)
!           ? (*lang_hooks.decl_printable_name) (t, 2)
!           : "({anonymous})";
!         output_add_string (buffer, n);
!       }
!       return true;
  
!     case 'E':
!        if (TREE_CODE (t) == IDENTIFIER_NODE)
!          {
!            output_add_string (buffer, IDENTIFIER_POINTER (t));
!            return true;
!          }
!        return false;
  
!     default:
!       return false;
      }
  }
  
  #include "gt-c-objc-common.h"
--- 414,553 ----
     These format specifiers form a subset of the format specifiers set used
     by the C++ front-end.
     Please notice when called, the `%' part was already skipped by the
!    diagnostic machinery.  
! 
! 
!    Comment about support for positional parameters.
!    Modifying c_tree_printer to support positional parameters was
!    relatively easy.
!    Reasons why.
!    (1) Only a single format character is used as opposed to 
!        multi-character sequence.
!    (2) Value fetched from the argument list is of only ONE type, namely
!        "tree".
! 
!    Below, I play fast and loose in handling of 'E' format.
!    I don't return "false" to indicate error until
!    PRINT_ARG_VALUE is requested, and the TREE value is not
!    found to be IDENTIFIER_NODE.
!    This is because I know for certain that the any error is
!    eventually caught during the processing of PRINT_ARG_VALUE.
!    No need to complicate procdssing during
!      - GET_ARG_TYPE, and 
!      - COPY_ARG_VALUE.
! 
! */
  static bool
! c_tree_printer (output_buffer *buffer, text_info *text, arg_decode_action a)
  {
!   tree t;
! 
  
!   if(a == PRINT_ARG_VALUE_AS_BEFORE) /* good old printing as before. */
      {
!       t = va_arg (*text->args_ptr, tree);
  
!       switch (*text->format_spec)
! 	{
! 	case 'D':
! 	case 'F':
! 	case 'T':
! 	  {
! 	    const char *n = DECL_NAME (t)
! 	      ? (*lang_hooks.decl_printable_name) (t, 2)
! 	      : "({anonymous})";
! 	    output_add_string (buffer, n);
! 	  }
! 	  return true;
! 
! 	case 'E':
! 	  if (TREE_CODE (t) == IDENTIFIER_NODE)
! 	    {
! 	      output_add_string (buffer, IDENTIFIER_POINTER (t));
! 	      return true;
! 	    }
! 	  return false;
  
! 	default:
! 	  return false;
! 	}
!     }
!   else if( a == GET_ARG_TYPE )
!     {
!       if(text->index_num > 9 || text->index_num <= 0)
! 	abort();
! 
!       text->bump_format = 0;
!       switch (*text->format_after_percent_or_dollar)
! 	{
! 	case 'D':
! 	case 'F':
! 	case 'T':
! 	case 'E':
! 	  text->bump_format = 1;
! 	  text->arg_array[text->index_num].custom_format[0]
! 	    = *text->format_after_percent_or_dollar;
! 	  return true;
! 	default:
! 	  return false;
! 	}
      }
+   else if ( a == COPY_ARG_VALUE)
+     {
+       if(text->index_num > 9 || text->index_num <= 0)
+ 	abort();
+       text->arg_array[text->index_num].v.ptr 
+ 	= (void *) va_arg (*text->args_ptr, tree);
+ 
+       switch (*text->format_after_percent_or_dollar)
+ 	{
+ 	case 'D':
+ 	case 'F':
+ 	case 'T':
+ 	  return true;
+ 
+ 	case 'E':
+ 	  return true;
+ 	default:
+ 	  return false;
+ 	}
+     }
+   else if (a == PRINT_ARG_VALUE )
+     {
+       if(text->index_num > 9 || text->index_num <= 0)
+ 	abort();
+ 
+       t = (tree) text->arg_array[text->index_num].v.ptr;
+ 
+       switch (*text->format_after_percent_or_dollar)
+ 	{
+ 	case 'D':
+ 	case 'F':
+ 	case 'T':
+ 	  {
+ 	    const char *n = DECL_NAME (t)
+ 	      ? (*lang_hooks.decl_printable_name) (t, 2)
+ 	      : "({anonymous})";
+ 	    output_add_string (buffer, n);
+ 	  }
+ 	  return true;
+ 
+ 	case 'E':
+ 	  if (TREE_CODE (t) == IDENTIFIER_NODE)
+ 	    {
+ 	      output_add_string (buffer, IDENTIFIER_POINTER (t));
+ 	      return true;
+ 	    }
+ 	  return false;
+ 
+ 	default:
+ 	  return false;
+ 	}
+     }
+   else 
+     abort();
+ 
+   return false;
  }
  
  #include "gt-c-objc-common.h"
Index: gcc/diagnostic.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/diagnostic.c,v
retrieving revision 1.126
diff -c -3 -p -r1.126 diagnostic.c
*** gcc/diagnostic.c	22 Jul 2003 23:15:25 -0000	1.126
--- gcc/diagnostic.c	25 Jul 2003 02:56:35 -0000
*************** Software Foundation, 59 Temple Place - S
*** 39,44 ****
--- 39,47 ----
  #include "langhooks.h"
  #include "langhooks-def.h"
  
+ /* for isdigit */
+ #include <ctype.h>
+ 
  #define output_text_length(BUFFER) (BUFFER)->line_length
  #define is_starting_newline(BUFFER) (output_text_length (BUFFER) == 0)
  #define line_wrap_cutoff(BUFFER) (BUFFER)->state.maximum_length
*************** static void diagnostic_action_after_outp
*** 103,108 ****
--- 106,115 ----
  					    diagnostic_info *);
  static void real_abort (void) ATTRIBUTE_NORETURN;
  
+ static void build_argument_array(const char * format, output_buffer *buffer, text_info *text);
+ 
+ 
+ 
  extern int rtl_dump_and_exit;
  
  /* A diagnostic_context surrogate for stderr.  */
*************** output_indent (output_buffer *buffer)
*** 364,369 ****
--- 371,377 ----
  }
  
  /* Wrap a text delimited by START and END into BUFFER.  */
+ /* additional comment: character at END is not put into BUFFER. */
  static void
  wrap_text (output_buffer *buffer, const char *start, const char *end)
  {
*************** output_buffer_to_stream (output_buffer *
*** 433,438 ****
--- 441,1078 ----
    output_clear_message_text (buffer);
  }
  
+ /* 
+  ========================================
+   Supporting positional format specifiers a la 
+   %2$d, %1$d, %3$c, etc..
+  ========================================
+ */
+ 
+ #define UNDEFINED_FORMAT 0
+ 
+ #define LOCATION_T_TYPE 1001
+ 
+ 
+ /*
+  * Aborting with error message and the format string that caused it.
+  */
+ static
+ void
+ _helper_abort(const char *a, text_info *text)
+ {
+   fprintf(stderr,"Aborting output formatting: %s\n", a);
+   fprintf(stderr,"format=<<%s>>\n", text->original_format_spec);
+ #ifdef ENABLE_CHECKING
+   abort();
+ #endif
+ }
+ 
+ /**
+    type compatibility info for %d$c processing.
+    Check is based only on the width, i.e., sizeof(t).
+ 
+    The whole scheme does not feel quit right in that 
+    it is not quite clear whether %lld, %ld, and/or %d  should
+    be compatible with HOST_WIDE_INT.
+ */
+ static
+ int
+ _type_width(int t)
+ {
+   switch(t)
+     {
+     default:
+       fprintf(stderr,"_type_width: unknown type spec. %d\n", t);
+       abort();
+       break;
+ 
+     case 's': return sizeof(char *); 
+ 
+     case 'c': return sizeof(int);
+ 
+     case 'i':
+     case 'd': return sizeof(int);
+ 
+       /* HOST_WIDE_INT may be blank "" on some systems such as x86 
+          So we use 'W' to stand for it. */
+     case 'W' /* for HOST_WIDE_INT */ : return  sizeof(long); 
+ 
+     case 'U' /* UNSIGNED_HOST_WIDE_INT */ : return sizeof(unsigned long int);
+ 
+     case 'x':
+     case 'u': 
+     case 'o': return sizeof(unsigned int) ;
+ 
+     case 'p': return sizeof(void *);
+ 
+     case LOCATION_T_TYPE: return sizeof(void *);
+     }
+ 
+   /* NOTREACHED */
+   abort();
+ }
+ 
+ /*
+ ** if an argument is referenced twice, we
+ ** say, the references are compatible if the type (width)
+ ** is the same, and its precisions are the same.
+ ** 
+ ** E.g. %1$d and %1$lld is not considered compatible. 
+ */
+ static
+ int
+ compatible_and_has_same_width ( int t, int prec1, int u, int prec2)
+ {
+   return _type_width(t) == _type_width(u)
+     && prec1 == prec2 ;
+ }
+ 
+ 
+ /*
+  * store argument tye with precision.
+  */
+ static
+ void
+ set_argument_type_with_precision(int i, int type, int precision, text_info *text)
+ {
+   if(i <= 0 || i >= 10)
+     abort();
+ 
+   if(precision < 0 || precision > 2)
+     abort();
+ 
+   if(type == UNDEFINED_FORMAT)
+     abort();
+ 
+   /*   fprintf(stderr,"set_argument_type_with_precision: %d, prec=%d\n",
+        i, precision); 
+   */
+ 
+   /* First definition/reference */
+   if( text->arg_array[i].typespec == UNDEFINED_FORMAT )
+     {
+       text->arg_array[i].typespec = type;
+       text->arg_array[i].precision = precision;
+ 
+       switch(precision)
+ 	{
+ 	default: abort();
+ 
+ 	case 0:
+ 	  text->arg_array[i].format[0] = '%';
+ 	  text->arg_array[i].format[1] = type;
+ 	  break;
+ 
+ 	case 1:
+ 	  text->arg_array[i].format[0] = '%';
+ 	  text->arg_array[i].format[1] = 'l';
+ 	  text->arg_array[i].format[2] = type;
+ 	  break;
+ 
+ 	case 2:
+ 	  text->arg_array[i].format[0] = '%';
+ 	  text->arg_array[i].format[1] = 'l';
+ 	  text->arg_array[i].format[2] = 'l';
+ 	  text->arg_array[i].format[3] = type;
+ 	  break;
+ 	}
+ 
+       return;
+     }
+ 
+   /* Check the double definition */
+   if ( compatible_and_has_same_width( text->arg_array[i].typespec, text->arg_array[i].precision, type, precision) )
+     return;
+   else
+     {
+       fprintf(stderr,"diagnostics.c: Specifying incompatible types for argument (at %d) twice\n", i);
+       fprintf(stderr,"format : <<%s>>\n", text->arg_array[0].v.cptr);
+ #ifdef ENABLE_CHECKING
+       abort();      	
+ #endif
+     }
+ } 
+ 
+ /*
+  * store argument type (without precision.)
+  */
+ static
+ void
+ set_argument_type(int i, int type, text_info *text)
+ {
+   /* precision is 0 */
+   set_argument_type_with_precision(i, type, 0, text);
+ }
+ 
+ 
+ /* we define the format string to be used
+    at the time of first scan (for types) 
+    by calling set_argument_type_with_precision, 
+    and do away with the dynamic construction of "%" F, "%l", F, "%ll", F
+    in output_integer_with_precision. 
+ 
+    Assumption. This is used ONLY for
+    'd', 'i', 'x', 'o', 'u', that is for value meant to be numbers.
+    So I use i, li, and lli fields.
+    CAVEAT: I am making an assumption that
+    long long int, and long long unsigned uses a same size
+    memory cell. This seems reasonable.
+ 
+    arg_array[ind] holds the value, format, and precision. 
+ */ 
+ 
+ 
+ #define output_integer_with_precision_NEW(BUFFER, ind, text)  \
+   do                                                            \
+     switch (text->arg_array[ind].precision)                           \
+       {                                                         \
+       case 0:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, text->arg_array[ind].format, text->arg_array[ind].v.i);  \
+         break;                                                  \
+                                                                 \
+       case 1:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, text->arg_array[ind].format, text->arg_array[ind].v.l);  \
+         break;                                                  \
+                                                                 \
+       case 2:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, text->arg_array[ind].format, text->arg_array[ind].v.ll); \
+         break;                                                  \
+                                                                 \
+       default:                                                  \
+         abort(); break;                                         \
+       }                                                         \
+   while (0)
+ 
+ 
+ /*
+   Building argument array to 
+   support format specifiers in the form of %1$s, %2$d %3$d, etc.
+ */
+ static
+ void
+ build_argument_array(const char * format, output_buffer *buffer, text_info *text)
+ {
+   int arg_index = 0;
+   int i;
+   int has_seen_number = 0;	/* have we seen "n$" in %n$d? */
+   
+   text->arg_max = 0;
+ 
+   memset(text->arg_array, 0, sizeof(text->arg_array)); /* fill in zero. UNDEFINED_FORMAT */
+ 
+   /* TODO/FIXME: this is somewhat redundant now that arg_array is made 
+      part of text_info. Or we can still use this value as the unmodified
+      format string. 
+    */
+   text->arg_array[0].v.cptr = (char *) format; /* record for error message. */
+ 
+   /* loop through the string just looking for the type */
+   for (; *format; ++format )
+     {
+       int precision = 0;
+       bool wide = false;
+ 
+       has_seen_number = 0;
+ 
+       /*  "Ignore text." while we build the array.  */
+       {
+ 	const char *p = format;
+ 	while (*p && *p != '%')
+ 	  ++p;
+         format = p;
+       }
+ 
+       /* assert:    format == '\0'
+ 	 || format == '%'
+       */
+ 
+       if (*format == '\0') /* we are at the end. Finish */
+ 	break;
+ 
+       /* We got a '%'.  Parse precision modifiers, if any.  */
+       /* CI mods. We now need to handle [0-9] as in %1$d */
+ 
+       /* assert:    
+ 	    we have  % [non-digit]
+ 	 || we have  % [digit] $ 
+       */
+ 
+      if ( isdigit(format[1]) )
+        {
+ 	 /* %1$ ... */
+ 	 if( format[2] == '$' )
+ 	   {
+ 	      char tmp[2];
+ 
+ 	      /* We may be handling non-ASCII like EBCDIC where
+ 		 digit character codes are not consecutive.
+ 		 Let atoi() take care of that. */
+ 
+ 	      tmp[1] = '\0';
+ 	      tmp[0] = format[1];
+ 
+ 	      arg_index = atoi(tmp);
+ 
+ 	      if(arg_index == 0)
+ 		{
+ 		  _helper_abort("The digit that comes after % must be non-zero.",  text );
+ 		}
+ 
+ 	      if(text->arg_max < arg_index)
+ 		text->arg_max = arg_index;
+ 
+ 	      has_seen_number = 1;
+ 
+ 	      format += 2;
+ 	   }
+ 	 else  /* We expected '$' after "%1" */
+ 	   {
+ 	     if(isdigit(format[2]))
+ 	       _helper_abort("we handle only 1-9 in %%d$ as of now.\n", text);
+ 	     else
+ 	       _helper_abort("We expected '$' after %% and digit as in '%%1$'", text);
+ 	   }
+        }
+ 
+       /* assert:    
+ 	    format points at the % in   % [non-digit]
+ 	 || format points at the $ in   % [digit] $ 
+       */
+ 
+       switch (*++format)
+         {
+         case 'w':
+           wide = true;
+           ++format;
+           break;
+ 
+         case 'l':
+           do
+             ++precision;
+           while (*++format == 'l');
+           break;
+ 
+ 
+         default:
+           break;
+         }
+ 
+       /* We don't support precision behond that of "long long".  */
+       if (precision > 2)
+ 	_helper_abort("We don't support precision behond that of \"long long\" ", text);
+ 
+       if(arg_index >= 10 || arg_index < 0)
+ 	abort();
+ 
+       if(!has_seen_number && ! (*format == '%' || *format == 'm'))
+ 	{
+ 	  _helper_abort("Use %n$c form specifier in ALL places except for % (percent sign) and %m.", text);
+ 	}
+ 
+       switch (*format)
+ 	{
+ 	case 'c':
+ 	  set_argument_type(arg_index, 'c', text);
+ 	  break;
+ 
+ 	case 'd':
+ 	case 'i':
+           if (wide)
+ 	    set_argument_type(arg_index, 'W' /* HOST_WIDE_INT*/, text );
+           else
+ 	    set_argument_type_with_precision(arg_index, 'd',  precision, text);
+ 	  break;
+ 
+ 	case 'o':
+           if (wide)
+ 	    set_argument_type(arg_index, 'U' /*UNSIGNED_HOST_WIDE_INT*/, text) ;
+           else
+ 	    set_argument_type_with_precision(arg_index, 'o', precision, text);
+ 	  break;
+ 
+ 	case 's':
+ 	  set_argument_type(arg_index, 's', text); /* char *ptr */
+ 	  break;
+ 
+         case 'p':
+ 	  set_argument_type(arg_index, 'p', text); /* void *p */
+           break;
+ 
+ 	case 'u': /* now we must split this from 'x' to handle
+                      precision string processing. */
+           if (wide)
+ 	    set_argument_type(arg_index, 'U' /*UNSIGNED_HOST_WIDE_INT*/, text );
+           else
+ 	    set_argument_type_with_precision(arg_index,  'u', precision, text);
+ 	  break;
+ 
+ 
+ 	case 'x':
+           if (wide)
+ 	    set_argument_type(arg_index, 'U' /*UNSIGNED_HOST_WIDE_INT*/, text);
+           else
+ 	    set_argument_type_with_precision(arg_index,  'x', precision, text);
+ 	  break;
+ 
+ 	case 'm': /* error code: handles separately. */
+ 	  break;
+ 
+ 	case '%': /* % character */
+ 	  /* arg_index --; */
+ 	  if(format[-1] != '%')
+ 	    {
+ 	      /*
+ 		Before we catch this, we saw subtle Error as in:
+ 		argument at 1 not specified in format string. <<pointer=%3$p, char=%1$%c, integer=%4$d
+ 		
+ 		%1$%c is handled as if it were %%c and thus no
+ 		information about argument was recorded. 
+ 	      */
+ 	      fprintf(stderr,"cf. char=%%1$%%c is handled as if it were char %%%%c\n");
+ 	      fprintf(stderr,"That is, 1$ is ignored when the second %% is seen.\n");
+ 
+ 	      _helper_abort("We should have a consecutive % s ", text);
+ 
+ 	    }
+ 
+ 	  break;
+ 
+         case 'H':
+ 	  set_argument_type(arg_index, LOCATION_T_TYPE, text);
+           break;
+ 
+ 	case '.':  /* Original %.*s, 
+ 	              but could be %1$.*3$s ... 1st argument with the
+ 	              width given by the third argument. */
+ 	  {
+ 	    /*
+ 
+ 	    From SUS.
+ 	    The precision takes the form of a period ( '.' ) followed
+ 	    either by an asterisk ( '*' ), described below, or an
+ 	    optional decimal digit string, where a null digit string is
+ 	    treated as zero.  
+ 	    [...]  
+ 
+ 	    In format strings containing the "%n$" form of a conversion
+ 	    specification, a field width or precision may be indicated
+ 	    by the sequence "*m$", where m is a decimal integer in the
+ 	    range [1,{NL_ARGMAX}] giving the position in the argument
+ 	    list (after the format argument) of an integer argument
+ 	    containing the field width or precision,
+ 
+ 	    */
+ 
+ 
+ 	    /* We handle no precision specifier but `%.*s'.  */
+ 	    /* This build_argument_array() is called when %n$
+ 	       specifier is used and so the width also needs to
+ 	       be specified as *m$ ! */
+ 	    
+ 	    if (*++format != '*')
+ 	      _helper_abort("We expected '*' after '.'", text);
+ 	    else 
+ 	      { /* we handle only %d$.*m$s : we have seen "*". */ 
+ 		int a;
+ 		char tmp[2];
+ 		int c;
+ 		c = *++format;
+ 		if(!isdigit(c))
+  		  _helper_abort("We expected a digit after '*'", text);
+ 		tmp[0] = c; tmp[1] = '\0';
+ 		a = atoi(tmp);
+ 		if(a == 0)
+ 		  _helper_abort("The digit that comes after % must be non-zero.\n",  text);
+ 		if (text->arg_max < a ) 
+ 		  text->arg_max = a;
+ 		if (text->arg_max < arg_index )
+ 		  text->arg_max = arg_index;
+ 
+ 		set_argument_type(a, 'd', text); /* host int */
+ 
+ 		set_argument_type(arg_index, 's', text);
+ 	      }
+ 	  }
+ 	  break;
+ 
+ 	default:
+ 	  /*
+ 	   * This problem probably is not a big deal since
+ 	   * the usage of format_decoder 
+ 	   * is mainly fr the dumping of compiler internal data
+ 	   * for debugging. (?)
+ 	   *
+ 	   * One format_decoder is c_tree_printer in c-objc-common.c
+ 	   */
+ 
+ 	  /* set up for GET_ARG_TYPE */
+ 
+ 	  text->index_num = arg_index;
+ 	  text->format_after_percent_or_dollar = format;
+ 
+ 	  /* save previously seen format, if any, for checking
+ 	     incompatible double references. */
+ 	  memcpy(text->arg_array[0].custom_format,
+ 		 text->arg_array[arg_index].custom_format,
+ 		 sizeof(text->arg_array[0].custom_format));
+ 
+ 	  if (!buffer->format_decoder
+               || !(*buffer->format_decoder) (buffer, text, GET_ARG_TYPE))
+ 	    {
+ 	      /* Ugh. Error. */
+ 
+ 	      /* Hmmm.  The front-end failed to install a format translator
+                  but called us with an unrecognized format.  Or, maybe, the
+                  translated string just contains an invalid format, or
+                  has formats in the wrong order.  Sorry.  */
+ 	      _helper_abort("Unrecognized format.", text);
+ 	    }
+ 
+ 	  /* Check for double definition for custom-extended format
+ 	     argument */
+ 	  
+ 	  if(text->arg_array[0].custom_format[0] == '\0')
+ 	    ; 			/* First reference. OK */
+ 	  else 
+ 	    {
+ 	      /*
+ 		If it `matches', then the double usage is OK.
+ 		Otherwise, it is an error.
+ 		Now the semantics of `match' is a little tricky to get right.
+ 		We probably need to ask the format decoder to decide
+ 		the compatibility of two different format strings.
+ 		
+ 		As an initial implementation, we simply use the strncmp()
+ 		against	the two format strings.
+ 	       */
+ 	      if(strncmp(text->arg_array[0].custom_format, 
+ 			text->arg_array[arg_index].custom_format, 4) == 0)
+ 		;		/* OK */
+ 	      else 
+ 		{
+ 		  fprintf(stderr,"Specifying incompatible types for argument (at %d)\n", arg_index);
+ 		  fprintf(stderr,"format:<<%s>>\n", text->arg_array[0].v.cptr);
+ 		  abort();      	
+ 		}
+ 
+ 	    }
+ 
+ 	  /* adjust */
+ 	  if(text->bump_format > 1)
+ 	    format += (text->bump_format - 1);
+ 		 
+ 	}
+ 	  
+     }
+ 
+ 
+   /* Now let us scan the argumet and fetch the value as
+      specified by the type. */
+   /* fprintf(stderr, "arg_max=%d\n", arg_max); */
+ 
+   /* NOTE: we start at 1 */
+ 
+   for(i = 1; i <= text->arg_max; i++)
+     {
+       switch(text->arg_array[i].typespec)
+ 	{
+ 	case UNDEFINED_FORMAT:
+ 	  if( text->arg_array[i].custom_format[0] == '\0')
+ 	    {
+ 	      fprintf(stderr,"argument at %d not specified in format string. <<%s>>\n", i, text->arg_array[0].v.cptr);
+ 	      abort();
+ 	    }
+ 	  else 	  /* handling custom extended format character */
+ 	    {
+ 	      /* setup for COPY_ARG_VALUE */
+ 	      text->index_num = arg_index;
+ 	      text->format_after_percent_or_dollar 
+ 		= text->arg_array[arg_index].custom_format;
+ 
+ 	      if (!buffer->format_decoder
+ 		  || !(*buffer->format_decoder) (buffer, text, COPY_ARG_VALUE))
+ 		{
+ 		  _helper_abort("Unrecognized format.", text);
+ 		}
+ 	    }
+ 
+ 	  break;
+ 
+ 	case 's': text->arg_array[i].v.cptr = (char *) va_arg (*text->args_ptr, const char *);
+ 	  break;
+ 
+ 	case 'i':
+ 	case 'd': 
+ 	  switch( text->arg_array[i].precision )
+ 	    {
+ 	    case 0:
+ 	      text->arg_array[i].v.i = va_arg (*text->args_ptr, int);
+ 	      break;
+ 	    case 1:	    
+ 	      text->arg_array[i].v.l = va_arg (*text->args_ptr, long int);
+ 	      break;
+ 	    case 2:
+ 	      text->arg_array[i].v.ll = va_arg (*text->args_ptr, long  long int);
+ 	      break;
+ 		    
+ 	    default: abort();
+ 	    }
+ 	  break;
+ 
+ 	case 'c': text->arg_array[i].v.i = va_arg (*text->args_ptr, int);
+ 	  break;
+ 
+ 
+ 	case 'W' /* HOST_WIDE_INT */ : 
+ 	  text->arg_array[i].v.hwi = va_arg (*text->args_ptr, HOST_WIDE_INT);
+ 	  break;
+ 
+ 	case 'U' /*UNSIGNED_HOST_WIDE_INT*/: 
+ 	  text->arg_array[i].v.uhwi = va_arg (*text->args_ptr, unsigned HOST_WIDE_INT);
+ 	  break;
+ 
+ 	case 'x':
+ 	case 'u': 
+ 	case 'o': 
+ 	  switch( text->arg_array[i].precision )
+ 	    {
+ 	    case 0:	
+ 	      text->arg_array[i].v.ui = va_arg (*text->args_ptr, unsigned);
+ 	      break;
+ 	    case 1:	    
+ 	      text->arg_array[i].v.ul = va_arg (*text->args_ptr, long unsigned);
+ 	      break;
+ 	    case 2:
+ 	      text->arg_array[i].v.ull = va_arg (*text->args_ptr, long  long unsigned);
+ 	      break;
+ 	    default: abort();
+ 
+ 	    }
+ 	  break;
+ 
+ 	case 'p': 
+ 	  text->arg_array[i].v.ptr = va_arg (*text->args_ptr, void *);
+ 	  break;
+ 
+ 	case LOCATION_T_TYPE: 
+ 	  text->arg_array[i].v.ptr = va_arg (*text->args_ptr, location_t *);
+ 	  break;
+ 
+ 	default:
+ 	  _helper_abort("Unknown output specifier during value copy.", text);
+ 	}
+ 
+     }
+ 
+   /* Now we know the type/value of arguments */
+   
+   return;
+ }
+ 
+ 
  /* Format a message pointed to by TEXT.  The following format specifiers are
     recognized as being language independent:
     %d, %i: (signed) integer in base ten.
*************** output_buffer_to_stream (output_buffer *
*** 448,475 ****
     %m: strerror(text->err_no) - does not consume a value from args_ptr.
     %%: `%'.
     %*.s: a substring the length of which is specified by an integer.
!    %H: location_t.  */
  static void
  output_format (output_buffer *buffer, text_info *text)
  {
    for (; *text->format_spec; ++text->format_spec)
      {
        int precision = 0;
        bool wide = false;
  
!       /* Ignore text.  */
        {
  	const char *p = text->format_spec;
  	while (*p && *p != '%')
  	  ++p;
! 	wrap_text (buffer, text->format_spec, p);
          text->format_spec = p;
        }
  
        if (*text->format_spec == '\0')
  	break;
  
        /* We got a '%'.  Parse precision modifiers, if any.  */
        switch (*++text->format_spec)
          {
          case 'w':
--- 1088,1180 ----
     %m: strerror(text->err_no) - does not consume a value from args_ptr.
     %%: `%'.
     %*.s: a substring the length of which is specified by an integer.
!    %H: location_t.  
! 
!    Addition.
!    %[0-9]${d,i,u,o,x,...,H}
! 
! */
  static void
  output_format (output_buffer *buffer, text_info *text)
  {
+   int arg_index = -1; 		
+   int has_processed_one = 0;	/* has processed specifiers. (counter) */
+   int use_new_specifier = 0;	/* %d$c form specifier was found. */
+ 
+   text->original_format_spec = text->format_spec; /* save the original. */
+ 
    for (; *text->format_spec; ++text->format_spec)
      {
        int precision = 0;
        bool wide = false;
  
!       /* Misleading old comment: Ignore text.  */
!       /* Rather We output text as is (!) */
        {
  	const char *p = text->format_spec;
  	while (*p && *p != '%')
  	  ++p;
! 	wrap_text (buffer, 
! 		   text->format_spec, /* START */
! 		   p		      /* END */
! 		   );
          text->format_spec = p;
        }
  
+       /* assert:    text->format_spec == '\0'
+ 	 || text->format_spec == '%'
+       */
+ 
        if (*text->format_spec == '\0')
  	break;
  
        /* We got a '%'.  Parse precision modifiers, if any.  */
+       /* We now need to handle [0-9] as in %1$d */
+ 
+       if(isdigit(text->format_spec[1]))
+ 	{
+ 	  if(text->format_spec[2] == '$')
+ 	    {
+ 	      char tmp[2];
+ 
+ 	      /* How about EBCDIC? */
+ 	      /* Let atoi handle the digit positions in  character set. */
+ 
+ 	      tmp[1] = '\0';
+ 	      tmp[0] = (text->format_spec)[1];
+ 	      arg_index = atoi(tmp);
+ 
+ 	      if(arg_index == 0)
+ 		_helper_abort("The digit that comes after % must be non-zero.\n", text);
+ 
+ 	      if(!use_new_specifier)
+ 		{
+ 		  if(has_processed_one > 1)
+ 		    _helper_abort("We can't mix %d and %n$d form\n", text);
+ 		  build_argument_array(text->original_format_spec, buffer, text);
+ 		}
+ 	      use_new_specifier = 1;
+ 
+ 	      /* after the above call to build_argument_array(),
+ 		  we know the value (and type) for each argument */
+ 
+ 	      text->format_spec += 2;
+ 
+ 	    }
+ 	  else /* we have seen % and digit as in '%1', but it is not followed by '$' */
+ 	    {
+ 	      if(isdigit((text->format_spec)[2]))
+ 		_helper_abort("we handle only 1-9 in %d$ as of now.\n", text);
+ 	      else 
+ 		_helper_abort("we have seen %d, but it is not followed by '$'\n", text);
+ 	    }
+ 	}
+ 
+       /* assert
+           text->format_spec points at % as in % [non-digit]
+        || text->format_spec points at $ as in % [digit] $
+       */
+ 
        switch (*++text->format_spec)
          {
          case 'w':
*************** output_format (output_buffer *buffer, te
*** 486,499 ****
          default:
            break;
          }
!       /* We don't support precision behond that of "long long".  */
        if (precision > 2)
!         abort();
  
        switch (*text->format_spec)
  	{
  	case 'c':
! 	  output_add_character (buffer, va_arg (*text->args_ptr, int));
  	  break;
  
  	case 'd':
--- 1191,1208 ----
          default:
            break;
          }
! 
!       /* We don't support precision beyond that of "long long".  */
        if (precision > 2)
!         _helper_abort("Too many precision 'l' specfied.", text);
  
        switch (*text->format_spec)
  	{
  	case 'c':
! 	  output_add_character (buffer, 
! 				(!use_new_specifier) ?
! 				va_arg (*text->args_ptr, int) :
! 				text->arg_array[arg_index].v.i );
  	  break;
  
  	case 'd':
*************** output_format (output_buffer *buffer, te
*** 501,561 ****
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_DEC,
!                va_arg (*text->args_ptr, HOST_WIDE_INT));
            else
!             output_integer_with_precision
!               (buffer, *text->args_ptr, precision, int, "d");
  	  break;
  
  	case 'o':
!           if (wide)
!             output_formatted_scalar
!               (buffer, "%" HOST_WIDE_INT_PRINT "o",
!                va_arg (*text->args_ptr, unsigned HOST_WIDE_INT));
!           else
!             output_integer_with_precision
!               (buffer, *text->args_ptr, precision, unsigned, "u");
  	  break;
  
  	case 's':
! 	  output_add_string (buffer, va_arg (*text->args_ptr, const char *));
  	  break;
  
          case 'p':
!           output_pointer (buffer, va_arg (*text->args_ptr, void *));
            break;
  
  	case 'u':
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_UNSIGNED,
!                va_arg (*text->args_ptr, unsigned HOST_WIDE_INT));
!           else
!             output_integer_with_precision
!               (buffer, *text->args_ptr, precision, unsigned, "u");
  	  break;
  
  	case 'x':
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_HEX,
!                va_arg (*text->args_ptr, unsigned HOST_WIDE_INT));
!           else
!             output_integer_with_precision
!               (buffer, *text->args_ptr, precision, unsigned, "x");
  	  break;
  
  	case 'm':
  	  output_add_string (buffer, xstrerror (text->err_no));
  	  break;
  
  	case '%':
  	  output_add_character (buffer, '%');
  	  break;
  
          case 'H':
            {
              const location_t *locus = va_arg (*text->args_ptr, location_t *);
              output_add_string (buffer, "file '");
              output_add_string (buffer, locus->file);
              output_add_string (buffer, "', line ");
--- 1210,1319 ----
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_DEC,
! 	       (!use_new_specifier) ?
!                va_arg (*text->args_ptr, HOST_WIDE_INT) :
! 	       text->arg_array[arg_index].v.hwi);
            else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW (buffer, arg_index, text);
! 	      else
! 		output_integer_with_precision (buffer, 
! 					       *text->args_ptr, 
! 					       precision, int, "d");
! 	    }
  	  break;
  
  	case 'o':
! 	  if(!use_new_specifier)
! 	    {
! 	      if (wide)
! 		output_formatted_scalar
! 		  (buffer, "%" HOST_WIDE_INT_PRINT "o",
! 		   va_arg (*text->args_ptr, unsigned HOST_WIDE_INT));
! 	      else
! 		output_integer_with_precision
! 		  (buffer, *text->args_ptr, precision, unsigned, "o");
! 	    } else
! 	      {
! 		if (wide)
! 		  output_formatted_scalar
! 		    (buffer, "%" HOST_WIDE_INT_PRINT "o",
! 		     text->arg_array[arg_index].v.uhwi );
! 		else
! 		  output_integer_with_precision_NEW(buffer, arg_index, text);
! 	      }
  	  break;
  
  	case 's':
! 	  if(!use_new_specifier)
! 	    output_add_string (buffer, va_arg (*text->args_ptr, const char *));
! 	  else
! 	    output_add_string (buffer, text->arg_array[arg_index].v.cptr);
  	  break;
  
          case 'p':
! 	  if(!use_new_specifier)
! 	    output_pointer (buffer, va_arg (*text->args_ptr, void *));
! 	  else
! 	    output_pointer (buffer, text->arg_array[arg_index].v.ptr);
            break;
  
  	case 'u':
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_UNSIGNED,
! 	       (!use_new_specifier) ?
!                va_arg (*text->args_ptr, unsigned HOST_WIDE_INT) :
! 	       text->arg_array[arg_index].v.uhwi);
! 	  else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW(buffer,  arg_index, text);
! 	      else
! 		output_integer_with_precision
! 		  (buffer, *text->args_ptr, precision, unsigned, "u");
! 
! 	    }
  	  break;
  
  	case 'x':
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_HEX,
! 	       (!use_new_specifier) ?
!                va_arg (*text->args_ptr, unsigned HOST_WIDE_INT) :
! 	       text->arg_array[arg_index].v.uhwi);
! 	  else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW(buffer,  arg_index, text);
! 	      else
! 		output_integer_with_precision
! 		  (buffer, *text->args_ptr, precision, unsigned, "x");
! 	    }
! 
  	  break;
  
  	case 'm':
  	  output_add_string (buffer, xstrerror (text->err_no));
+ 	  has_processed_one --;	/* tweak the value: we don't want to count this.   */
  	  break;
  
  	case '%':
  	  output_add_character (buffer, '%');
+ 	  has_processed_one --; /* tweak the value: we don't want to count this.   */
  	  break;
  
          case 'H':
            {
              const location_t *locus = va_arg (*text->args_ptr, location_t *);
+ 
+ 	    if(!use_new_specifier)
+ 	      locus = va_arg (*text->args_ptr, location_t *);
+ 	    else
+ 	      locus = (location_t *) text->arg_array[arg_index].v.ptr;
+ 
              output_add_string (buffer, "file '");
              output_add_string (buffer, locus->file);
              output_add_string (buffer, "', line ");
*************** output_format (output_buffer *buffer, te
*** 564,594 ****
            break;
  
  	case '.':
! 	  {
! 	    int n;
! 	    const char *s;
! 	    /* We handle no precision specifier but `%.*s'.  */
! 	    if (*++text->format_spec != '*')
! 	      abort ();
! 	    else if (*++text->format_spec != 's')
! 	      abort ();
! 	    n = va_arg (*text->args_ptr, int);
! 	    s = va_arg (*text->args_ptr, const char *);
! 	    output_append (buffer, s, s + n);
! 	  }
  	  break;
  
  	default:
  	  if (!buffer->format_decoder
!               || !(*buffer->format_decoder) (buffer, text))
  	    {
  	      /* Hmmm.  The front-end failed to install a format translator
                   but called us with an unrecognized format.  Or, maybe, the
                   translated string just contains an invalid format, or
                   has formats in the wrong order.  Sorry.  */
! 	      abort ();
  	    }
  	}
      }
  }
  
--- 1322,1419 ----
            break;
  
  	case '.':
! 
! 
! 	  if(!use_new_specifier)
! 	    {
! 	      int n;
! 	      const char *s;
! 	      /* We handle no precision specifier but `%.*s'.  */
! 	      /* Like this.  warning("String = %.*s, extra=%d\n",           10, str, i); */
! 
! 	      if (*++text->format_spec != '*')
! 		_helper_abort("We expected '*' after '.'", text);
! 	      else if (*++text->format_spec != 's')
! 		_helper_abort("We expected 's' after '*'", text);
! 	      n = va_arg (*text->args_ptr, int);
! 
! 	      if(n <= 0)		/* we may want to check this. */
! 		{
! 		  fprintf(stderr,"diagnostic: string width %d given is not positive.\n", n);
! 		  n = 0;
! 		}
! 	      s = va_arg (*text->args_ptr, const char *);
! 	      output_append (buffer, s, s + n);
! 	    }
! 	  else
! 	    {
! 	      /* we handle only %d$.*m$s : we have seen "period". */ 
! 
! 	      int a;
! 	      char tmp[2];
! 	      int c;
! 	      int n;
! 	      char *s;
! 
! 	      if (*++text->format_spec != '*')
! 		_helper_abort ("We expected '*' afer '.'", text);
! 
! 	      c = *++text->format_spec;
! 	      if(!isdigit(c))
! 		_helper_abort ("We expected a digit after '*'", text);
! 	      tmp[0] = c; tmp[1] = '\0';
! 	      a = atoi(tmp);
! 	      c = *++text->format_spec;
! 	      if(c != '$')
! 		_helper_abort ("We expected a '$' after a digit", text);
! 	      c = *++text->format_spec;
! 	      if(c != 's')
! 		_helper_abort ("We expected an 's' after '$'", text);
! 	      n = text->arg_array[a].v.i;
! 	      if(n <= 0)
! 		{
! 		  fprintf(stderr,"diagnostic: string width %d at %d is not positive.\n", n, a);
! 		  n = 0;
! 		}
! 
! 	      s = text->arg_array[arg_index].v.cptr;
!              
! 	      output_append (buffer, s, s + n);
! 	    }
! 
  	  break;
  
  	default:
+ #if 0
+ 	  if(use_new_specifier)
+ 	    _helper_abort("We don't handle format decoder yet with new format specifier.\n", text);
+ #endif
+ 	  text->index_num = arg_index;
+ 	  text->format_after_percent_or_dollar = text->format_spec;
+ 
  	  if (!buffer->format_decoder
!               || !(*buffer->format_decoder) (buffer, text, 
! 					     use_new_specifier ?
! 					     PRINT_ARG_VALUE :
! 					     PRINT_ARG_VALUE_AS_BEFORE))
  	    {
  	      /* Hmmm.  The front-end failed to install a format translator
                   but called us with an unrecognized format.  Or, maybe, the
                   translated string just contains an invalid format, or
                   has formats in the wrong order.  Sorry.  */
! 
! 	      _helper_abort ("unrecognized format.", text);
! 
  	    }
+ 
+ 	  /* adjust */
+ 	  if(use_new_specifier && text->bump_format > 1)
+ 	    text->format_spec += (text->bump_format - 1);
+ 	    
  	}
+ 
+       has_processed_one ++;
+ 
      }
  }
  
Index: gcc/diagnostic.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/diagnostic.h,v
retrieving revision 1.63
diff -c -3 -p -r1.63 diagnostic.h
*** gcc/diagnostic.h	5 Jul 2003 15:17:26 -0000	1.63
--- gcc/diagnostic.h	25 Jul 2003 02:56:35 -0000
*************** Software Foundation, 59 Temple Place - S
*** 25,30 ****
--- 25,78 ----
  #include "obstack.h"
  #include "input.h"
  
+ /* 
+  The type of variable argument arg_array[] (part of
+  text_info)  to store type and the value.
+  This is to support positional format specifiers 
+  in the form of %1$s, %2$d %3$d, etc.
+ 
+  Note that we use the arg_array[0].v.cptr to store the format string. 
+  The whole array is set to zero before each construction.
+ */
+ typedef struct 
+ {
+   int typespec ; /* type 'd', etc. */
+   int precision ; /* to handle %d, %ld, %lld */
+   char format [5]; /* %lld at the longest. */
+   char custom_format[4];  /* CUSTOM format. May contain #+, etc. */
+   union {
+     int i;
+     unsigned int ui;
+     short s;
+     unsigned short us;
+     char c;
+     unsigned char uc;
+     long l;
+     unsigned long ul;
+     long long ll;
+     unsigned long long ull;
+     HOST_WIDE_INT hwi;
+     unsigned HOST_WIDE_INT uhwi;
+     void *ptr;   /* assuming one pointer type */
+     char *cptr;
+     int  *iptr;
+     long *lptr;
+     long long *llptr;
+     location_t l_ptr;
+   } v;
+ }  argtype_t;
+ 
+ 
+ /* Instruct custom defined format decoder to
+    return information for processing. */
+ typedef enum 
+ {
+   GET_ARG_TYPE = 2,
+   COPY_ARG_VALUE = 3,
+   PRINT_ARG_VALUE = 4,
+   PRINT_ARG_VALUE_AS_BEFORE = 5 /* as before: no positional support */
+ } arg_decode_action;
+ 
  /* The type of a text to be formatted according a format specification
     along with a list of things.  */
  typedef struct
*************** typedef struct
*** 32,37 ****
--- 80,97 ----
    const char *format_spec;
    va_list *args_ptr;
    int err_no;  /* for %m */
+ 
+   /* Below is for positional argument support */
+   const char *original_format_spec; /* this remains unchanged.  */
+   argtype_t arg_array[10 + 1];	/* arg_array[0].v.cptr holds 
+ 				   the original format pointer*/
+   int arg_max;
+   /* arg_decode_action action; made into function argument */
+   const char *format_after_percent_or_dollar; /* input for GET_ARG_TYPE, COPY_ARG_VALUE */
+   int index_num; /* used input for arg_decode_action */
+   int bump_format ; /* GET_ARG_TYPE: returns
+ 		       how many bytes of format chars will be eaten by 
+ 		       custom decoder. */
  } text_info;
  
  /* Constants used to discriminate diagnostics.  */
*************** typedef struct
*** 99,105 ****
     an output_buffer.  A client-supplied formatter returns true if everything
     goes well.  */
  typedef struct output_buffer output_buffer;
! typedef bool (*printer_fn) (output_buffer *, text_info *);
  
  /* The output buffer datatype.  This is best seen as an abstract datatype
     whose fields should not be accessed directly by clients.  */
--- 159,165 ----
     an output_buffer.  A client-supplied formatter returns true if everything
     goes well.  */
  typedef struct output_buffer output_buffer;
! typedef bool (*printer_fn) (output_buffer *, text_info *, arg_decode_action);
  
  /* The output buffer datatype.  This is best seen as an abstract datatype
     whose fields should not be accessed directly by clients.  */
Index: gcc/toplev.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/toplev.c,v
retrieving revision 1.813
diff -c -3 -p -r1.813 toplev.c
*** gcc/toplev.c	19 Jul 2003 14:47:14 -0000	1.813
--- gcc/toplev.c	25 Jul 2003 02:56:40 -0000
*************** toplev_main (unsigned int argc, const ch
*** 4556,4561 ****
--- 4556,4694 ----
    /* Initialization of GCC's environment, and diagnostics.  */
    general_init (argv[0]);
  
+ #if 0
+ 
+ {
+ #include <errno.h>
+   int i;
+   long t;
+   long long ill;
+   long long xll;
+   extern void warning (const char *msgid, ...);
+ 
+   static char str [] = "123456789A123456789A123456789A";
+ 
+   i = 1234;
+   t = 0x0FFFFFFl;
+   ill = (long long) t * t;
+   xll = 0x0123456789ABCDEFull;
+ 
+   warning("Test diagnostic.\n");
+ 
+   warning("integer=%d, pointer=%p, char=%c", i, &t, 'c');
+ 
+   warning("\nPointer value print\n");
+ 
+   /* fprintf(stderr,"pointer=0x%08x\n", (unsigned int) &t);*/
+   fprintf(stderr,"pointer=%p\n",  (void *) &t);
+   warning("1st: pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
+   warning("2nd: pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
+   warning("3rd: pointer=%1$p, char=%3$c, integer=%2$d", &t, i, 'c');
+   warning("pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
+ 
+ 
+   warning("\nOne argument");
+   warning("integer=%1$d", i);
+   warning("char   =%1$c", 'c');
+   warning("pointer=%1$p", &t);
+   warning("long=%1$ld", t);
+   warning("long=%1$lx", t);
+ 
+ 
+   /* long */
+   warning("long=(x) %1$lx", t);
+   fprintf(stderr," ordinary printf: %lx\n", t);
+   warning("long=(o) %1$lo", t);
+   fprintf(stderr," ordinary printf: %lo\n", t);
+   warning("long=(d) %1$ld", t);
+   fprintf(stderr," ordinary printf: %ld\n", t);
+ 
+ 
+   /* long long */
+ 
+   warning("long long=(d) %1$lld", ill);
+   fprintf(stderr,"ordinary fprintf(): %lld\n", ill);
+ 
+ 
+   warning("long long=(x) %1$llx", xll);
+   fprintf(stderr," ordinary printf: %llx\n", xll);
+ 
+ 
+   warning("long long=(o) %1$llo", xll);
+   fprintf(stderr," ordinary printf: %llo\n", xll);
+ 
+ 
+   warning("long long=(d) %1$lld", xll);
+   fprintf(stderr," ordinary printf: %lld\n", xll);
+ 
+   /* wide ? */
+ 
+   warning("Wide\n");
+   warning("wide=(wd) %1$wd", t);
+   warning("wide=(wx) %1$wx", t);
+   warning("wide=(wo) %1$wo", t);
+ 
+   warning("\nTwo arguments");
+   warning("integer=%1$d, char=%2$c", i, 'c');
+   warning("char   =%2$c, integer=%1$d", i, 'c');
+ 
+   warning("\nThree Arguments\n");
+   warning("char=%3$c, integer=%1$d, pointer=%2$p", i, &t, 'c');
+ 
+   warning("char=%c, integer=%d, pointer=%p", 'c', i, &t);
+   warning("pointer=%3$p, char=%1$c, integer=%2$d", 'c', i, &t);
+   warning("char=%1$c, integer=%2$d, pointer=%3$p", 'c', i, &t);
+ 
+   warning("integer=%d, long=%ld, long long=%lld", i, t, ill);
+   warning("long=%2$ld, long long=%3$lld, int=%1$d", i, t, ill);
+   warning("long long=%3$lld, int=%1$d, long=%2$ld", i, t, ill);
+ 
+   warning("pointer=%2$p, char=%3$c, integer=%1$d", i, &t, 'c');
+ 
+   warning("long long=%lld, long=%ld, int=%d", ill, t, i);
+   warning("long=%2$ld, int=%3$d, long long=%1$lld", ill, t, i);
+   warning("int=%3$d, long long=%1$lld, long=%2$ld", ill, t, i);
+ 
+ 
+   /* String and its width*/
+   warning("\nString");
+ 
+   warning("String =%s", str);
+   warning("String =%1$s", str);  
+   fprintf(stderr,"ordinary fprintf(): %s\n", str);
+ 
+   warning("String = %.*s, extra=%d",           10, str, i);
+   warning("String = %3$.*1$s, extra=%2$d",     10, i, str);
+   warning("String = %2$.*1$s, extra=%3$d",     10, str, i);
+   fprintf(stderr,"ordinary fprintf(): %.*s\n", 10, str);
+ 
+   /* %% and %m */
+ 
+   warning("This is one %% (percent sign)  and error code message: could be random <<%m>>");
+ 
+ 
+   errno = EPIPE;
+   warning("int=%1$d, long long=%2$lld,  %% (percent sign) and error message should be for EPIPE <<%m>>", i, ill);
+ 
+ 
+   /* should abort: we can't mix two forms. */
+   warning("Should abort: integer=%d, pointer=%p, char=%3$c\n", i, &t, 'c');
+ 
+   /* should abort because %3$whatever is missing. */
+   warning("Should abort: char=%4$p, integer=%1$d, pointer=%2$p\n", i, &t, 'c');
+ 
+   /* should abort: we can't mix two forms */
+   warning("Should abort: integer=%1$d, pointer=%2$p, char=%c\n", i, &t, 'c');
+ 
+   /* should abort: 0 after %. */
+   warning("Should abort: integer=%0$d, pointer=%2$p, char=%3c\n", i, &t, 'c'); 
+ 
+ }
+ 
+ #endif
+ 
+ 
+ 
    /* Parse the options and do minimal processing; basically just
       enough to default flags appropriately.  */
    decode_options (argc, argv);
Index: gcc/cp/error.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/cp/error.c,v
retrieving revision 1.225
diff -c -3 -p -r1.225 error.c
*** gcc/cp/error.c	22 Jul 2003 23:30:15 -0000	1.225
--- gcc/cp/error.c	25 Jul 2003 02:56:45 -0000
*************** static void cp_diagnostic_starter (diagn
*** 109,115 ****
  static void cp_diagnostic_finalizer (diagnostic_context *, diagnostic_info *);
  static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
  
! static bool cp_printer (output_buffer *, text_info *);
  static void print_non_consecutive_character (output_buffer *, int);
  static tree locate_error (const char *, va_list);
  static location_t location_of (tree);
--- 109,115 ----
  static void cp_diagnostic_finalizer (diagnostic_context *, diagnostic_info *);
  static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
  
! static bool cp_printer (output_buffer *, text_info *, arg_decode_action );
  static void print_non_consecutive_character (output_buffer *, int);
  static tree locate_error (const char *, va_list);
  static location_t location_of (tree);
*************** print_instantiation_context (void)
*** 2482,2490 ****
     %P   function parameter whose position is indicated by an integer.
     %Q	assignment operator.
     %T   type.
!    %V   cv-qualifier.  */
  static bool
! cp_printer (output_buffer *buffer, text_info *text)
  {
    int verbose = 0;
    const char *result;
--- 2482,2493 ----
     %P   function parameter whose position is indicated by an integer.
     %Q	assignment operator.
     %T   type.
!    %V   cv-qualifier.  
! 
!    Modified to support postional format parameter.
! */
  static bool
! cp_printer (output_buffer *buffer, text_info *text, arg_decode_action a)
  {
    int verbose = 0;
    const char *result;
*************** cp_printer (output_buffer *buffer, text_
*** 2493,2530 ****
  #define next_lang    va_arg (*text->args_ptr, enum languages)
  #define next_int     va_arg (*text->args_ptr, int)
  
!   if (*text->format_spec == '+')
!     ++text->format_spec;
!   if (*text->format_spec == '#')
!     {
!       verbose = 1;
!       ++text->format_spec;
!     }
! 
!   switch (*text->format_spec)
!     {
!     case 'A': result = args_to_string (next_tree, verbose);	break;
!     case 'C': result = code_to_string (next_tcode);	        break;
!     case 'D': result = decl_to_string (next_tree, verbose);	break;
!     case 'E': result = expr_to_string (next_tree);      	break;
!     case 'F': result = fndecl_to_string (next_tree, verbose);	break;
!     case 'L': result = language_to_string (next_lang);          break;
!     case 'O': result = op_to_string (next_tcode);       	break;
!     case 'P': result = parm_to_string (next_int);	        break;
!     case 'Q': result = assop_to_string (next_tcode);	        break;
!     case 'T': result = type_to_string (next_tree, verbose);	break;
!     case 'V': result = cv_to_string (next_tree, verbose);	break;
   
!     default:
!       return false;
      }
  
-   output_add_string (buffer, result);
-   return true;
  #undef next_tree
  #undef next_tcode
  #undef next_lang
  #undef next_int
  }
  
  static void
--- 2496,2662 ----
  #define next_lang    va_arg (*text->args_ptr, enum languages)
  #define next_int     va_arg (*text->args_ptr, int)
  
! #define pos_next_tree    ((tree) text->arg_array[i].v.ptr )
! #define pos_next_tcode   ((enum tree_code) text->arg_array[i].v.i )
! #define pos_next_lang    ((enum languages) text->arg_array[i].v.i )
! #define pos_next_int     text->arg_array[i].v.i
! 
! 
! 
!   if(a == PRINT_ARG_VALUE_AS_BEFORE) /* good old printing unmodified. */
!     {
!       if (*text->format_spec == '+')
! 	++text->format_spec;
!       if (*text->format_spec == '#')
! 	{
! 	  verbose = 1;
! 	  ++text->format_spec;
! 	}
! 
!       switch (*text->format_spec)
! 	{
! 	case 'A': result = args_to_string (next_tree, verbose);	break;
! 	case 'C': result = code_to_string (next_tcode);	        break;
! 	case 'D': result = decl_to_string (next_tree, verbose);	break;
! 	case 'E': result = expr_to_string (next_tree);      	break;
! 	case 'F': result = fndecl_to_string (next_tree, verbose);	break;
! 	case 'L': result = language_to_string (next_lang);          break;
! 	case 'O': result = op_to_string (next_tcode);       	break;
! 	case 'P': result = parm_to_string (next_int);	        break;
! 	case 'Q': result = assop_to_string (next_tcode);	        break;
! 	case 'T': result = type_to_string (next_tree, verbose);	break;
! 	case 'V': result = cv_to_string (next_tree, verbose);	break;
   
! 	default:
! 	  return false;
! 	}
! 
!       output_add_string (buffer, result);
!       return true;
      }
+   else if ( a == GET_ARG_TYPE )
+     {
+       if(text->index_num > 9 || text->index_num <= 0)
+ 	abort();
+ 
+       text->bump_format = 0;
+       if (*text->format_after_percent_or_dollar == '+')
+ 	text->arg_array[text->index_num].custom_format[text->bump_format++] =
+ 	  *text->format_after_percent_or_dollar++;
+       if (*text->format_spec == '#')
+ 	{
+ 	  verbose = 1;
+ 	  text->arg_array[text->index_num].custom_format[text->bump_format++] =
+ 	    *text->format_after_percent_or_dollar++;
+ 	}
+ 
+       switch (*text->format_after_percent_or_dollar)
+ 	{
+ 	case 'A': 
+ 	case 'C': 
+ 	case 'D': 
+ 	case 'E': 
+ 	case 'F': 
+ 	case 'L': 
+ 	case 'O': 
+ 	case 'P': 
+ 	case 'Q': 
+ 	case 'T': 
+ 	case 'V': 
+ 	  text->arg_array[text->index_num].custom_format[text->bump_format++] =
+ 	    *text->format_after_percent_or_dollar;
+ 	  break;
+ 
+ 	default:
+ 	  return false;
+ 	}
+       return true;
+     }
+   else if ( a == COPY_ARG_VALUE )
+     {
+       int i = text->index_num;
+       if(i > 9 || i <= 0)
+ 	abort();
+ 
+       text->bump_format = 0;
+ 
+       if (*text->format_after_percent_or_dollar == '+')
+ 	++text->format_after_percent_or_dollar,  ++text->bump_format;
+       if (*text->format_spec == '#')
+ 	{
+ 	  verbose = 1;
+ 	  ++text->format_after_percent_or_dollar;
+ 	  ++text->bump_format;
+ 	}
+ 
+       switch (*text->format_after_percent_or_dollar)
+ 	{
+ 	case 'A': text->arg_array[i].v.ptr = (void *) next_tree ; break;
+ 	case 'C': text->arg_array[i].v.ptr = (void *) next_tcode;        break;
+ 	case 'D': text->arg_array[i].v.ptr = (void *) next_tree;	break;
+ 	case 'E': text->arg_array[i].v.ptr = (void *) next_tree;      	break;
+ 	case 'F': text->arg_array[i].v.ptr = (void *) next_tree;	break;
+ 	case 'L': text->arg_array[i].v.i = next_lang;          break;
+ 	case 'O': text->arg_array[i].v.i = next_tcode;       	break;
+ 	case 'P': text->arg_array[i].v.i = next_int;	        break;
+ 	case 'Q': text->arg_array[i].v.i = next_tcode;	        break;
+ 	case 'T': text->arg_array[i].v.ptr = next_tree; 	break;
+ 	case 'V': text->arg_array[i].v.ptr = next_tree;	break;
+  
+ 	default:
+ 	  return false;
+ 	}
+       return true;
+     }
+   else if ( a == PRINT_ARG_VALUE )
+     {
+       int i = text->index_num;
+       if(i > 9 || i <= 0)
+ 	abort();
+ 
+       if (*text->format_after_percent_or_dollar == '+')
+ 	++text->format_after_percent_or_dollar, ++text->bump_format;
+       if (*text->format_after_percent_or_dollar == '#')
+ 	{
+ 	  verbose = 1;
+ 	  ++text->format_after_percent_or_dollar;
+ 	  ++text->bump_format;
+ 	}
+ 
+       switch (*text->format_after_percent_or_dollar)
+ 	{
+ 	case 'A': result = args_to_string (pos_next_tree, verbose);	break;
+ 	case 'C': result = code_to_string (pos_next_tcode);	        break;
+ 	case 'D': result = decl_to_string (pos_next_tree, verbose);	break;
+ 	case 'E': result = expr_to_string (pos_next_tree);      	break;
+ 	case 'F': result = fndecl_to_string (pos_next_tree, verbose);	break;
+ 	case 'L': result = language_to_string (pos_next_lang);          break;
+ 	case 'O': result = op_to_string (pos_next_tcode);       	break;
+ 	case 'P': result = parm_to_string (pos_next_int);	        break;
+ 	case 'Q': result = assop_to_string (pos_next_tcode);	        break;
+ 	case 'T': result = type_to_string (pos_next_tree, verbose);	break;
+ 	case 'V': result = cv_to_string (pos_next_tree, verbose);	break;
+  
+ 	default:
+ 	  return false;
+ 	}
+ 
+       output_add_string (buffer, result);
+       return true;
+     }
+   else
+     abort();
  
  #undef next_tree
  #undef next_tcode
  #undef next_lang
  #undef next_int
+ 
+ #undef pos_next_tree
+ #undef pos_next_tcode
+ #undef pos_next_lang
+ #undef pos_next_int
+ 
  }
  
  static void

-- 
int main(void){int j=2003;/*(c)2003 cishikawa. */
char t[] ="<CI> @abcdefghijklmnopqrstuvwxyz.,\n\"";
char *i ="g>qtCIuqivb,gCwe\np@.ietCIuqi\"tqkvv is>dnamz";
while(*i)((j+=strchr(t,*i++)-(int)t),(j%=sizeof t-1),
(putchar(t[j])));return 0;}/* under GPL */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]