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 second cut for the patch to support positional format
specifier for diagnostic messege print routines.


Comments/feedback welcome.


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

============================================================
Support for %d$c 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. The messages will be understandable, if we get lucky, after
a few second thoughts. However, I have to think twice
if I understand 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.


[] Given Conditions/Limitation:

   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.)

   BTW, 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 proposed patch is not thread-safe.  But thread-safety is not
   required in this particular case.  The new routine uses a
   statically assigned array to store argument info.  (It won't be
   that difficult to pass an argument to a thread-specific 
   struct that holds the
   necessary persistent (across the calls) info, but, given that other
   functions are not thread-safe from my cursorly reading, 
   I don't see the need for thread-safety. And some posters concurred.

CAVEAT: 
	
   I found out that precision for integer print implicitly defines the
   used type (%d, %ld, %lld, ... ).

   output_integer_with_precision() macro uses the following
   precision/types.

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

   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:

    output_printf (buffer, "%1$d %3$s %2$c", intvalue, charvalue, "string")

    I wish I could give an example program.
    However, I found it difficult to write a standalone
    short test program to check calls, say, warning().

    Routines in diagnostic.c requires prior envrionment initialization
    and I was not quite sure how to do that. Also, I found out
    that diagnostic.o requires linking of various
    modules. 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 specification rather well.)

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


[] 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.

    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 I 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 setting, 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.)

    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();

    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:

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

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

    We don't handle the format decoder as of now IF positional
    parameter is used. (If we don't use it, the
    support remains as is.)

    BTW, Does anyone use the format_decoder function?
    (It looks that a format_decoder, c_tree_printer in
    c-objc-common.c, is used. But it seems to be used for
    internal compiler diagnostics and so we don't need to
    use positional parameter with it. So we don't lose if I am not
    mistaken.)

Comment/feedback welcome.

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

TestCase: as noted above.

  For 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-20    <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.

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.

Index: gcc/gcc/diagnostic.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/diagnostic.c,v
retrieving revision 1.124
diff -c -3 -p -r1.124 diagnostic.c
*** gcc/gcc/diagnostic.c	15 Jul 2003 23:31:51 -0000	1.124
--- gcc/gcc/diagnostic.c	20 Jul 2003 03:39:58 -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,1052 ----
    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
+ 
+ /* 
+  Argument array[] to store type and the value.
+  This is to support 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. */
+ 	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;
+ 
+ static
+ ARGTYPE    arg_array[10+1] ; /* 0 is for format string itself, which is ignored.*/
+ static
+ int        arg_max = -1;
+ 
+ 
+ /*
+  * Aborting with error message and the format string that caused it.
+  */
+ static
+ void
+ _helper_abort(const char *a, const char *fmt)
+ {
+   fprintf(stderr,"Aborting output formatting: %s\n", a);
+   if(fmt == NULL)
+     fprintf(stderr,"format=<<%s>>\n", arg_array[0].v.cptr);
+   else
+     fprintf(stderr,"format=<<%s>>\n", fmt);
+   abort();
+ }
+ 
+ /**
+    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)
+ {
+   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( arg_array[i].typespec == UNDEFINED_FORMAT )
+     {
+       arg_array[i].typespec = type;
+       arg_array[i].precision = precision;
+ 
+       switch(precision)
+ 	{
+ 	default: abort();
+ 
+ 	case 0:
+ 	  arg_array[i].format[0] = '%';
+ 	  arg_array[i].format[1] = type;
+ 	  break;
+ 
+ 	case 1:
+ 	  arg_array[i].format[0] = '%';
+ 	  arg_array[i].format[1] = 'l';
+ 	  arg_array[i].format[2] = type;
+ 	  break;
+ 
+ 	case 2:
+ 	  arg_array[i].format[0] = '%';
+ 	  arg_array[i].format[1] = 'l';
+ 	  arg_array[i].format[2] = 'l';
+ 	  arg_array[i].format[3] = type;
+ 	  break;
+ 	}
+ 
+       return;
+     }
+ 
+   /* Check the double definition */
+   if ( compatible_and_has_same_width( arg_array[i].typespec, arg_array[i].precision, type, precision) )
+     return;
+   else
+     {
+       fprintf(stderr,"diagnostics.c: Specifying incompatible types for argument (at %d)\n", i);
+       fprintf(stderr,"format : <<%s>>\n", arg_array[0].v.cptr);
+       abort();      	
+     }
+ } 
+ 
+ /*
+  * store argument type (without precision.)
+  */
+ static
+ void
+ set_argument_type(int i, int type)
+ {
+   /* precision is 0 */
+   set_argument_type_with_precision(i, type, 0);
+ }
+ 
+ 
+ /* 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)  \
+   do                                                            \
+     switch (arg_array[ind].precision)                           \
+       {                                                         \
+       case 0:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, arg_array[ind].format, arg_array[ind].v.i);  \
+         break;                                                  \
+                                                                 \
+       case 1:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, arg_array[ind].format, arg_array[ind].v.l);  \
+         break;                                                  \
+                                                                 \
+       case 2:                                                   \
+         output_formatted_scalar                                 \
+           (BUFFER, arg_array[ind].format, 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;
+   const char *o_format = format;
+   int i;
+   int has_seen_number = 0;	/* have we seen "n$" in %n$d? */
+   
+   arg_max = 0;
+ 
+   memset(arg_array, 0, sizeof(arg_array)); /* fill in zero. UNDEFINED_FORMAT */
+ 
+   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.",  NULL );
+ 		}
+ 
+ 	      if(arg_max < arg_index)
+ 		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", NULL);
+ 	     else
+ 	       _helper_abort("We expected '$' after %% and digit as in '%%1$'", NULL);
+ 	   }
+        }
+ 
+       /* 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\" ", NULL);
+ 
+       if(arg_index >= 10 || arg_index < 0)
+ 	abort();
+ 
+       if(!has_seen_number && ! (*format == '%' || *format == 'm'))
+ 	{
+ 	  fprintf(stderr, "Use %%n$c form specifier in ALL places except for %%%% (percent sign) and %%m.\n");
+ 	  fprintf(stderr, "format=<<%s>>\n", o_format); 
+ 	  abort();
+ 	}
+ 
+       switch (*format)
+ 	{
+ 	case 'c':
+ 	  set_argument_type(arg_index, 'c');
+ 	  break;
+ 
+ 	case 'd':
+ 	case 'i':
+           if (wide)
+ 	    set_argument_type(arg_index, 'W' /* HOST_WIDE_INT*/ );
+           else
+ 	    set_argument_type_with_precision(arg_index, 'd',  precision);
+ 	  break;
+ 
+ 	case 'o':
+           if (wide)
+ 	    set_argument_type(arg_index, 'U' /*UNSIGNED_HOST_WIDE_INT*/) ;
+           else
+ 	    set_argument_type_with_precision(arg_index, 'o', precision);
+ 	  break;
+ 
+ 	case 's':
+ 	  set_argument_type(arg_index, 's'); /* char *ptr */
+ 	  break;
+ 
+         case 'p':
+ 	  set_argument_type(arg_index, 'p'); /* 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*/ );
+           else
+ 	    set_argument_type_with_precision(arg_index,  'u', precision);
+ 	  break;
+ 
+ 
+ 	case 'x':
+           if (wide)
+ 	    set_argument_type(arg_index, 'U' /*UNSIGNED_HOST_WIDE_INT*/);
+           else
+ 	    set_argument_type_with_precision(arg_index,  'x', precision);
+ 	  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,"We should have a consecutive %% s in <<%s>>\n",
+ 		      o_format);
+ 	      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");
+ 	      abort();
+ 	    }
+ 
+ 	  break;
+ 
+         case 'H':
+ 	  set_argument_type(arg_index, LOCATION_T_TYPE);
+           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 '.'", NULL);
+ 	    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 '*'", NULL);
+ 		tmp[0] = c; tmp[1] = '\0';
+ 		a = atoi(tmp);
+ 		if(a == 0)
+ 		  _helper_abort("The digit that comes after % must be non-zero.\n",  NULL);
+ 		if (arg_max < a ) 
+ 		  arg_max = a;
+ 		if (arg_max < arg_index )
+ 		  arg_max = arg_index;
+ 
+ 		set_argument_type(a, 'd'); /* host int */
+ 
+ 		set_argument_type(arg_index, 's');
+ 	      }
+ 	  }
+ 	  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
+ 	   */
+ 	  _helper_abort("We don't handle internal format decoder yet when %1$c format specifier is used.\n", NULL);
+ 
+ 	  if (!buffer->format_decoder
+               || !(*buffer->format_decoder) (buffer, text))
+ 	    {
+ 	      /* 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.  */
+ 	      abort ();
+ 	    }
+ 	}
+ 	  
+     }
+ 
+ 
+   /* 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 <= arg_max; i++)
+     {
+       switch(arg_array[i].typespec)
+ 	{
+ 	case UNDEFINED_FORMAT:
+ 	  fprintf(stderr,"argument at %d not specified in format string. <<%s>>\n", i, o_format);
+ 	  abort();
+ 	  break;
+ 
+ 	case 's': arg_array[i].v.cptr = (char *) va_arg (*text->args_ptr, const char *);
+ 	  break;
+ 
+ 	case 'i':
+ 	case 'd': 
+ 	  switch( arg_array[i].precision )
+ 	    {
+ 	    case 0:
+ 	      arg_array[i].v.i = va_arg (*text->args_ptr, int);
+ 	      break;
+ 	    case 1:	    
+ 	      arg_array[i].v.l = va_arg (*text->args_ptr, long int);
+ 	      break;
+ 	    case 2:
+ 	      arg_array[i].v.ll = va_arg (*text->args_ptr, long  long int);
+ 	      break;
+ 		    
+ 	    default: abort();
+ 	    }
+ 	  break;
+ 
+ 	case 'c': arg_array[i].v.i = va_arg (*text->args_ptr, int);
+ 	  break;
+ 
+ 
+ 	case 'W' /* HOST_WIDE_INT */ : 
+ 	  arg_array[i].v.hwi = va_arg (*text->args_ptr, HOST_WIDE_INT);
+ 	  break;
+ 
+ 	case 'U' /*UNSIGNED_HOST_WIDE_INT*/: 
+ 	  arg_array[i].v.uhwi = va_arg (*text->args_ptr, unsigned HOST_WIDE_INT);
+ 	  break;
+ 
+ 	case 'x':
+ 	case 'u': 
+ 	case 'o': 
+ 	  switch( arg_array[i].precision )
+ 	    {
+ 	    case 0:	
+ 	      arg_array[i].v.ui = va_arg (*text->args_ptr, unsigned);
+ 	      break;
+ 	    case 1:	    
+ 	      arg_array[i].v.ul = va_arg (*text->args_ptr, long unsigned);
+ 	      break;
+ 	    case 2:
+ 	      arg_array[i].v.ull = va_arg (*text->args_ptr, long  long unsigned);
+ 	      break;
+ 	    default: abort();
+ 
+ 	    }
+ 	  break;
+ 
+ 	case 'p': 
+ 	  arg_array[i].v.ptr = va_arg (*text->args_ptr, void *);
+ 	  break;
+ 
+ 	case LOCATION_T_TYPE: 
+ 	  arg_array[i].v.ptr = va_arg (*text->args_ptr, location_t *);
+ 	  break;
+ 
+ 	default:
+ 	  _helper_abort("Unknown output specifier.", NULL);
+ 	}
+ 
+     }
+ 
+   /* 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':
--- 1062,1153 ----
     %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; 		/* CI mods. */
+   int has_processed_one = 0;	/* has processed specifiers. (counter) */
+   const char *original_ptr = text->format_spec;	/* save for error message. */
+   int use_new_specifier = 0;	/* %d$c form specifier was found. */
+ 
    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", original_ptr);
+ 
+ 	      if(!use_new_specifier)
+ 		{
+ 		  if(has_processed_one > 1)
+ 		    _helper_abort("We can't mix %d and %n$d form\n", original_ptr);  
+ 		  build_argument_array(original_ptr, 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", original_ptr);
+ 	      else 
+ 		_helper_abort("we have seen %d, but it is not followed by '$'\n", original_ptr);
+ 	    }
+ 	}
+ 
+       /* 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':
--- 1164,1181 ----
          default:
            break;
          }
! 
!       /* We don't support precision beyond that of "long long".  */
        if (precision > 2)
!         _helper_abort("Too many precision 'l' specfied.", original_ptr);
  
        switch (*text->format_spec)
  	{
  	case 'c':
! 	  output_add_character (buffer, 
! 				(!use_new_specifier) ?
! 				va_arg (*text->args_ptr, int) :
! 				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 ");
--- 1183,1292 ----
            if (wide)
              output_formatted_scalar
                (buffer, HOST_WIDE_INT_PRINT_DEC,
! 	       (!use_new_specifier) ?
!                va_arg (*text->args_ptr, HOST_WIDE_INT) :
! 	       arg_array[arg_index].v.hwi);
            else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW (buffer, arg_index);
! 	      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",
! 		     arg_array[arg_index].v.uhwi);
! 		else
! 		  output_integer_with_precision_NEW(buffer, arg_index);
! 	      }
  	  break;
  
  	case 's':
! 	  if(!use_new_specifier)
! 	    output_add_string (buffer, va_arg (*text->args_ptr, const char *));
! 	  else
! 	    output_add_string (buffer, 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, 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) :
! 	       arg_array[arg_index].v.uhwi);
! 	  else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW(buffer,  arg_index);
! 	      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) :
! 	       arg_array[arg_index].v.uhwi);
! 	  else
! 	    {
! 	      if(use_new_specifier)
! 		output_integer_with_precision_NEW(buffer,  arg_index);
! 	      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 *) 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,584 ****
            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))
  	    {
--- 1295,1365 ----
            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 '.'", original_ptr);
! 	      else if (*++text->format_spec != 's')
! 		_helper_abort("We expected 's' after '*'", original_ptr);
! 	      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 '.'", original_ptr);
! 
! 	      c = *++text->format_spec;
! 	      if(!isdigit(c))
! 		_helper_abort ("We expected a digit after '*'", original_ptr);
! 	      tmp[0] = c; tmp[1] = '\0';
! 	      a = atoi(tmp);
! 	      c = *++text->format_spec;
! 	      if(c != '$')
! 		_helper_abort ("We expected a '$' after a digit", original_ptr);
! 	      c = *++text->format_spec;
! 	      if(c != 's')
! 		_helper_abort ("We expected an 's' after '$'", original_ptr);
! 	      n = 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 = arg_array[arg_index].v.cptr;
!              
! 	      output_append (buffer, s, s + n);
! 	    }
! 
  	  break;
  
  	default:
+ 	  if(use_new_specifier)
+ 	    _helper_abort("We don't handle format decoder yet with new format specifier.\n", original_ptr);
+ 
  	  if (!buffer->format_decoder
                || !(*buffer->format_decoder) (buffer, text))
  	    {
*************** output_format (output_buffer *buffer, te
*** 589,594 ****
--- 1370,1378 ----
  	      abort ();
  	    }
  	}
+ 
+       has_processed_one ++;
+ 
      }
  }
  

-- 
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]