Nicolai Tufar wrote:
> Hello all,
> 
> I would like to submit my changes to src/port/snprintf.c to
> enable %n$ format placeholder replacement in snprintf() and
> vsnprintf(). Additionally I implemented a trivial printf().
> 
> I also attach a diff for configure.in to include snprintf.o
> in pgport but I am sure it is not the right thing to do.
> Could someone give a hint on where I need to place such a
> definition.
> 
> Please review my patch. as Tom Lane pointed out there
> are 150 messages in the following files that do not print
> properly:

It took me a while to understand this but I get it now.  This is the
best explanation I have seen, from Linux 2.6:

        One can also specify explicitly which argument is taken, at each place
        where an argument is required, by writing '%m$' instead of '%' and '*m$'
        instead of '*', where the decimal integer m denotes the position in the
        argument list of the desired argument, indexed starting from 1. Thus,
        
                printf("%*d", width, num);
        
        and
        
                printf("%2$*1$d", width, num);
        
        are  equivalent.  The  second style allows repeated references to the
        same argument. The C99 standard does not include the style using '$',
        which comes from the Single Unix Specification.  If the style using '$'
        is used, it must be  used throughout for all conversions taking an
        argument and all width and precision arguments, but it may be mixed with
        '%%' formats which do not consume an argument.  There may be no gaps in
        the numbers of arguments specified using  '$';  for example, if
        arguments 1 and 3 are specified, argument 2 must also be specified
        somewhere in the format string.

I can see why this would be useful for translations because it uncouples
the order of the printf arguments from the printf string.  However, I
have learned that Win32, HP-UX, NetBSD 2.0, and BSD/OS do not support
this.  This is probably because it is not in C99 but in SUS (see above).

Anyway, this is too large to put into 8.0, but I am attaching a patch
for 8.1 that has the proper configure tests to check if the C library
supports this behavior.  If it does not, the build will use our
port/snprintf.c.

One problem with that is that our snprintf.c is not thread-safe.  Seems
the increases use of it will require us to fix this soon.  I have added
to TODO:

        * Make src/port/snprintf.c thread-safe

One change to the original port is that there was a define of a union
with no name:

+               union{
+                       void*   value;
+                       long_long       numvalue;
+                       double          fvalue;
+                       int             charvalue;
+               };

As far as I know a union with no name is illegal.  I just removed the
"union {" and the closing brace.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: configure
===================================================================
RCS file: /cvsroot/pgsql/configure,v
retrieving revision 1.425
diff -c -c -r1.425 configure
*** configure   18 Jan 2005 05:23:35 -0000      1.425
--- configure   13 Feb 2005 23:50:46 -0000
***************
*** 12162,12167 ****
--- 12162,12224 ----
  done
  
  
+ echo "$as_me:$LINENO: checking printf supports argument control" >&5
+ echo $ECHO_N "checking printf supports argument control... $ECHO_C" >&6
+ if test "${pgac_cv_printf_arg_control+set}" = set; then
+   echo $ECHO_N "(cached) $ECHO_C" >&6
+ else
+   if test "$cross_compiling" = yes; then
+   pgac_cv_printf_arg_control=cross
+ else
+   cat >conftest.$ac_ext <<_ACEOF
+ #line $LINENO "configure"
+ #include "confdefs.h"
+ #include <stdio.h>
+ 
+ int does_printf_have_arg_control()
+ {
+   char buf[100];
+ 
+   /* can it swap arguments? */
+   snprintf(buf, 100, "%2$d|%1$d", 3, 4);
+   if (strcmp(buf, "4|3") != 0)
+     return 0;
+   return 1;
+ }
+ main() {
+   exit(! does_printf_have_arg_control());
+ }
+ _ACEOF
+ rm -f conftest$ac_exeext
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+   (eval $ac_link) 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+   { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+   (eval $ac_try) 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }; }; then
+   pgac_cv_printf_arg_control=yes
+ else
+   echo "$as_me: program exited with status $ac_status" >&5
+ echo "$as_me: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ( exit $ac_status )
+ pgac_cv_printf_arg_control=no
+ fi
+ rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext 
conftest.$ac_ext
+ fi
+ 
+ fi
+ echo "$as_me:$LINENO: result: $pgac_cv_printf_arg_control" >&5
+ echo "${ECHO_T}$pgac_cv_printf_arg_control" >&6
+ 
+ # cross compiler should use our snprintf too
+ if test $pgac_cv_printf_arg_control != yes ; then
+   pgac_need_repl_snprintf=yes
+ fi
  
  # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
  # include/c.h will provide declarations.  Note this is a separate test
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql/configure.in,v
retrieving revision 1.399
diff -c -c -r1.399 configure.in
*** configure.in        18 Jan 2005 05:23:36 -0000      1.399
--- configure.in        13 Feb 2005 23:50:47 -0000
***************
*** 881,886 ****
--- 881,891 ----
  AC_CHECK_FUNCS(snprintf, [], pgac_need_repl_snprintf=yes)
  AC_CHECK_FUNCS(vsnprintf, [], pgac_need_repl_snprintf=yes)
  
+ PGAC_FUNC_PRINTF_ARG_CONTROL
+ # cross compiler should use our snprintf too
+ if test $pgac_cv_printf_arg_control != yes ; then
+   pgac_need_repl_snprintf=yes
+ fi
  
  # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
  # include/c.h will provide declarations.  Note this is a separate test
***************
*** 1069,1074 ****
--- 1074,1081 ----
  [AC_MSG_RESULT([cross-compiling])])
  
  
+ dnl  64-bit section
+ dnl
  dnl Check to see if we have a working 64-bit integer type.
  dnl This breaks down into two steps:
  dnl (1) figure out if the compiler has a 64-bit int type with working
Index: config/c-library.m4
===================================================================
RCS file: /cvsroot/pgsql/config/c-library.m4,v
retrieving revision 1.29
diff -c -c -r1.29 c-library.m4
*** config/c-library.m4 16 Dec 2004 17:48:26 -0000      1.29
--- config/c-library.m4 13 Feb 2005 23:50:48 -0000
***************
*** 266,268 ****
--- 266,301 ----
           LONG_LONG_INT_FORMAT=$pgac_cv_snprintf_long_long_int_format;;
    *)     AC_MSG_RESULT(none);;
  esac])# PGAC_FUNC_SNPRINTF_LONG_LONG_INT_FORMAT
+ 
+ 
+ # PGAC_FUNC_PRINTF_ARG_CONTROL
+ # ---------------------------------------
+ # Determine if printf supports %1$ argument selection, e.g. %5$ selects
+ # the fifth argument after the printf print string.
+ # This is not in the C99 standard, but in the Single Unix Specification (SUS).
+ # It is used in our langauge translation strings.
+ #
+ AC_DEFUN([PGAC_FUNC_PRINTF_ARG_CONTROL],
+ [AC_MSG_CHECKING([printf supports argument control])
+ AC_CACHE_VAL(pgac_cv_printf_arg_control,
+ [AC_TRY_RUN([#include <stdio.h>
+ 
+ int does_printf_have_arg_control()
+ {
+   char buf[100];
+ 
+   /* can it swap arguments? */
+   snprintf(buf, 100, "%2$d|%1$d", 3, 4);
+   if (strcmp(buf, "4|3") != 0)
+     return 0;
+   return 1;
+ }
+ main() {
+   exit(! does_printf_have_arg_control());
+ }],
+ [pgac_cv_printf_arg_control=yes],
+ [pgac_cv_printf_arg_control=no],
+ [pgac_cv_printf_arg_control=cross])
+ ])dnl AC_CACHE_VAL
+ AC_MSG_RESULT([$pgac_cv_printf_arg_control])
+ ])# PGAC_FUNC_PRINTF_ARG_CONTROL
Index: src/port/snprintf.c
===================================================================
RCS file: /cvsroot/pgsql/src/port/snprintf.c,v
retrieving revision 1.4
diff -c -c -r1.4 snprintf.c
*** src/port/snprintf.c 29 Aug 2004 05:07:02 -0000      1.4
--- src/port/snprintf.c 13 Feb 2005 23:50:56 -0000
***************
*** 57,62 ****
--- 57,66 ----
  typedef unsigned long ulong_long;
  #endif
  
+ #ifndef NL_ARGMAX
+ #define NL_ARGMAX 4096
+ #endif
+ 
  /*
  **    SNPRINTF, VSNPRINT -- counted versions of printf
  **
***************
*** 85,93 ****
--- 89,115 ----
  
  int                   snprintf(char *str, size_t count, const char *fmt,...);
  int                   vsnprintf(char *str, size_t count, const char *fmt, 
va_list args);
+ int                   printf(const char *format, ...);
  static void dopr(char *buffer, const char *format, va_list args);
  
  int
+ printf(const char *fmt,...)
+ {
+       int                     len;
+       va_list                 args;
+       static char*            buffer[4096];
+       char*                   p;
+ 
+       va_start(args, fmt);
+       len = vsnprintf((char*)buffer, (size_t)4096, fmt, args);
+       va_end(args);
+       p = (char*)buffer;
+       for(;*p;p++)
+               putchar(*p);
+       return len;
+ }
+ 
+ int
  snprintf(char *str, size_t count, const char *fmt,...)
  {
        int                     len;
***************
*** 124,129 ****
--- 146,155 ----
  
  static char *output;
  
+ #define       FMTSTR          1
+ #define       FMTNUM          2
+ #define       FMTFLOAT        3
+ #define       FMTCHAR         4
  
  static void
  dopr(char *buffer, const char *format, va_list args)
***************
*** 139,145 ****
--- 165,198 ----
        int                     ljust;
        int                     len;
        int                     zpad;
+       int                     i;
+       const char*             format_save;
+       const char*             fmtbegin;
+       int                     fmtpos = 1;
+       int                     realpos = 0;
+       int                     position;
+       static struct{
+               const char*     fmtbegin;
+               const char*     fmtend;
+               void*   value;
+               long_long       numvalue;
+               double  fvalue;
+               int     charvalue;
+               int     ljust;
+               int     len;
+               int     zpad;
+               int     maxwidth;
+               int     base;
+               int     dosign;
+               char    type;
+               int     precision;
+               int     pointflag;
+               char    func;
+               int     realpos;
+       } fmtpar[NL_ARGMAX+1], *fmtparptr[NL_ARGMAX+1];
+ 
  
+       format_save = format;
        output = buffer;
        while ((ch = *format++))
        {
***************
*** 148,161 ****
                        case '%':
                                ljust = len = zpad = maxwidth = 0;
                                longflag = longlongflag = pointflag = 0;
                nextch:
                                ch = *format++;
                                switch (ch)
                                {
                                        case 0:
!                                               dostr("**end of format**", 0);
!                                               *output = '\0';
!                                               return;
                                        case '-':
                                                ljust = 1;
                                                goto nextch;
--- 201,215 ----
                        case '%':
                                ljust = len = zpad = maxwidth = 0;
                                longflag = longlongflag = pointflag = 0;
+                               fmtbegin = format - 1;
+                               realpos = 0;
+                               position = 0;
                nextch:
                                ch = *format++;
                                switch (ch)
                                {
                                        case 0:
!                                               goto performpr;
                                        case '-':
                                                ljust = 1;
                                                goto nextch;
***************
*** 174,180 ****
--- 228,241 ----
                                                if (pointflag)
                                                        maxwidth = maxwidth * 
10 + ch - '0';
                                                else
+                                               { 
                                                        len = len * 10 + ch - 
'0';
+                                                       position = position * 
10 + ch - '0';
+                                               }
+                                               goto nextch;
+                                       case '$':
+                                               realpos = position;
+                                               len = 0;
                                                goto nextch;
                                        case '*':
                                                if (pointflag)
***************
*** 203,209 ****
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtnum(value, 10, 0, ljust, 
len, zpad);
                                                break;
                                        case 'o':
                                        case 'O':
--- 264,280 ----
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].numvalue = value;
!                                               fmtpar[fmtpos].base = 10;
!                                               fmtpar[fmtpos].dosign = 0;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].zpad = zpad;
!                                               fmtpar[fmtpos].func = FMTNUM;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 'o':
                                        case 'O':
***************
*** 217,223 ****
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtnum(value, 8, 0, ljust, len, 
zpad);
                                                break;
                                        case 'd':
                                        case 'D':
--- 288,304 ----
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].numvalue = value;
!                                               fmtpar[fmtpos].base = 8;
!                                               fmtpar[fmtpos].dosign = 0;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].zpad = zpad;
!                                               fmtpar[fmtpos].func = FMTNUM;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 'd':
                                        case 'D':
***************
*** 230,236 ****
                                                }
                                                else
                                                        value = va_arg(args, 
int);
!                                               fmtnum(value, 10, 1, ljust, 
len, zpad);
                                                break;
                                        case 'x':
                                                if (longflag)
--- 311,327 ----
                                                }
                                                else
                                                        value = va_arg(args, 
int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].numvalue = value;
!                                               fmtpar[fmtpos].base = 10;
!                                               fmtpar[fmtpos].dosign = 1;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].zpad = zpad;
!                                               fmtpar[fmtpos].func = FMTNUM;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 'x':
                                                if (longflag)
***************
*** 242,248 ****
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtnum(value, 16, 0, ljust, 
len, zpad);
                                                break;
                                        case 'X':
                                                if (longflag)
--- 333,349 ----
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].numvalue = value;
!                                               fmtpar[fmtpos].base = 16;
!                                               fmtpar[fmtpos].dosign = 0;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].zpad = zpad;
!                                               fmtpar[fmtpos].func = FMTNUM;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 'X':
                                                if (longflag)
***************
*** 254,260 ****
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtnum(value, -16, 0, ljust, 
len, zpad);
                                                break;
                                        case 's':
                                                strvalue = va_arg(args, char *);
--- 355,371 ----
                                                }
                                                else
                                                        value = va_arg(args, 
unsigned int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].numvalue = value;
!                                               fmtpar[fmtpos].base = -16;
!                                               fmtpar[fmtpos].dosign = 1;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].zpad = zpad;
!                                               fmtpar[fmtpos].func = FMTNUM;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 's':
                                                strvalue = va_arg(args, char *);
***************
*** 262,273 ****
                                                {
                                                        if (pointflag && len > 
maxwidth)
                                                                len = maxwidth; 
/* Adjust padding */
!                                                       fmtstr(strvalue, ljust, 
len, zpad, maxwidth);
                                                }
                                                break;
                                        case 'c':
                                                ch = va_arg(args, int);
!                                               dopr_outch(ch);
                                                break;
                                        case 'e':
                                        case 'E':
--- 373,398 ----
                                                {
                                                        if (pointflag && len > 
maxwidth)
                                                                len = maxwidth; 
/* Adjust padding */
!                                                       fmtpar[fmtpos].fmtbegin 
= fmtbegin;
!                                                       fmtpar[fmtpos].fmtend = 
format;
!                                                       fmtpar[fmtpos].value = 
strvalue;
!                                                       fmtpar[fmtpos].ljust = 
ljust;
!                                                       fmtpar[fmtpos].len = 
len;
!                                                       fmtpar[fmtpos].zpad = 
zpad;
!                                                       fmtpar[fmtpos].maxwidth 
= maxwidth;
!                                                       fmtpar[fmtpos].func = 
FMTSTR;
!                                                       fmtpar[fmtpos].realpos 
= realpos?realpos:fmtpos;
!                                                       fmtpos++;
                                                }
                                                break;
                                        case 'c':
                                                ch = va_arg(args, int);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].charvalue = ch;
!                                               fmtpar[fmtpos].func = FMTCHAR;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case 'e':
                                        case 'E':
***************
*** 275,285 ****
                                        case 'g':
                                        case 'G':
                                                fvalue = va_arg(args, double);
!                                               fmtfloat(fvalue, ch, ljust, 
len, maxwidth, pointflag);
                                                break;
                                        case '%':
!                                               dopr_outch(ch);
!                                               continue;
                                        default:
                                                dostr("???????", 0);
                                }
--- 400,419 ----
                                        case 'g':
                                        case 'G':
                                                fvalue = va_arg(args, double);
!                                               fmtpar[fmtpos].fmtbegin = 
fmtbegin;
!                                               fmtpar[fmtpos].fmtend = format;
!                                               fmtpar[fmtpos].fvalue = fvalue;
!                                               fmtpar[fmtpos].type = ch;
!                                               fmtpar[fmtpos].ljust = ljust;
!                                               fmtpar[fmtpos].len = len;
!                                               fmtpar[fmtpos].maxwidth = 
maxwidth;
!                                               fmtpar[fmtpos].pointflag = 
pointflag;
!                                               fmtpar[fmtpos].func = FMTFLOAT;
!                                               fmtpar[fmtpos].realpos = 
realpos?realpos:fmtpos;
!                                               fmtpos++;
                                                break;
                                        case '%':
!                                               break;
                                        default:
                                                dostr("???????", 0);
                                }
***************
*** 289,294 ****
--- 423,475 ----
                                break;
                }
        }
+ performpr:
+       /* shuffle pointers */
+       for(i = 1; i < fmtpos; i++)
+       {
+               fmtparptr[i] = &fmtpar[fmtpar[i].realpos];
+       }
+       output = buffer;
+       format = format_save;
+       while ((ch = *format++))
+       {
+               for(i = 1; i < fmtpos; i++)
+               {
+                       if(ch == '%' && *format == '%')
+                       {
+                               format++;
+                               continue;
+                       }
+                       if(fmtpar[i].fmtbegin == format - 1)
+                       {
+                               switch(fmtparptr[i]->func){
+                               case FMTSTR:
+                                       fmtstr(fmtparptr[i]->value, 
fmtparptr[i]->ljust,
+                                               fmtparptr[i]->len, 
fmtparptr[i]->zpad,
+                                               fmtparptr[i]->maxwidth);
+                                       break;
+                               case FMTNUM:
+                                       fmtnum(fmtparptr[i]->numvalue, 
fmtparptr[i]->base,
+                                               fmtparptr[i]->dosign, 
fmtparptr[i]->ljust,
+                                               fmtparptr[i]->len, 
fmtparptr[i]->zpad);
+                                       break;
+                               case FMTFLOAT:
+                                       fmtfloat(fmtparptr[i]->fvalue, 
fmtparptr[i]->type,
+                                               fmtparptr[i]->ljust, 
fmtparptr[i]->len,
+                                               fmtparptr[i]->precision, 
fmtparptr[i]->pointflag);
+                                       break;
+                               case FMTCHAR:
+                                       dopr_outch(fmtparptr[i]->charvalue);
+                                       break;
+                               }
+                               format = fmtpar[i].fmtend;
+                               goto nochar;
+                       }
+               }
+               dopr_outch(ch);
+ nochar:
+       /* nothing */
+       }
        *output = '\0';
  }
  
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to