Re: [pgsql-hackers-win32] Repleacement for src/port/snprintf.c

Bruce Momjian <[email protected]>
Newsgroups gmane.comp.db.postgresql.devel.patches,gmane.comp.db.postgresql.devel.general,gmane.comp.db.postgresql.devel.win32
Message-ID <[email protected]>
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
  [email protected]               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073


---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [email protected])
/pgpatches/snprintf (text/plain, 14.8 KB)
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';
  }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.