>From b31f7c5fc2b2d4451c650a54bb02e656b8fd3bbf Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sat, 7 Dec 2013 19:23:15 +0100
Subject: [PATCH 1/2] Add support for printing Size arguments to
 elog()/ereport() using %zu.

Similar to the way %m is special-case supported in elog.c routines, add
support for the z length modifier by replacing it by the platform's modifier
for the pointer size.

To do that, refactor the configure checks that define [U]INT64_FORMAT, to
instead define the used length modifier as INT64_LENGTH_MODIFIER. Currently we
don't support flag, width, precision modifiers in addition to z, but that's
fine for the current callsites.
---
 config/c-library.m4            | 36 ++++++++++++++---------------
 configure                      | 51 +++++++++++++++++-------------------------
 configure.in                   | 28 +++++++++--------------
 src/backend/utils/error/elog.c | 16 +++++++++++++
 src/include/c.h                | 10 ++++++---
 src/include/pg_config.h.in     |  7 ++----
 src/include/pg_config.h.win32  |  8 ++-----
 7 files changed, 77 insertions(+), 79 deletions(-)

diff --git a/config/c-library.m4 b/config/c-library.m4
index 1e3997b..b836c2a 100644
--- a/config/c-library.m4
+++ b/config/c-library.m4
@@ -221,22 +221,22 @@ HAVE_POSIX_SIGNALS=$pgac_cv_func_posix_signals
 AC_SUBST(HAVE_POSIX_SIGNALS)])# PGAC_FUNC_POSIX_SIGNALS
 
 
-# PGAC_FUNC_SNPRINTF_LONG_LONG_INT_FORMAT
+# PGAC_FUNC_SNPRINTF_LONG_LONG_LENGTH_MODIFIER
 # ---------------------------------------
-# Determine which format snprintf uses for long long int.  We handle
-# %lld, %qd, %I64d.  The result is in shell variable
-# LONG_LONG_INT_FORMAT.
+# Determine which format snprintf uses for long long integers.  We
+# handle %ll, %q, %I64.  The detected length modifer is stored in
+# the shell variable LONG_LONG_LENGTH_MODIFIER.
 #
-# MinGW uses '%I64d', though gcc throws an warning with -Wall,
-# while '%lld' doesn't generate a warning, but doesn't work.
+# MinGW uses '%I64', though gcc throws an warning with -Wall,
+# while '%ll' doesn't generate a warning, but doesn't work.
 #
-AC_DEFUN([PGAC_FUNC_SNPRINTF_LONG_LONG_INT_FORMAT],
-[AC_MSG_CHECKING([snprintf format for long long int])
-AC_CACHE_VAL(pgac_cv_snprintf_long_long_int_format,
-[for pgac_format in '%lld' '%qd' '%I64d'; do
+AC_DEFUN([PGAC_FUNC_SNPRINTF_LONG_LONG_LENGTH_MODIFIER],
+[AC_MSG_CHECKING([snprintf length modifier for long long])
+AC_CACHE_VAL(pgac_cv_snprintf_long_long_length_modifier,
+[for pgac_format in 'll' 'q' 'I64'; do
 AC_TRY_RUN([#include <stdio.h>
 typedef long long int ac_int64;
-#define INT64_FORMAT "$pgac_format"
+#define INT64_FORMAT "%${pgac_format}d"
 
 ac_int64 a = 20000001;
 ac_int64 b = 40000005;
@@ -258,19 +258,19 @@ int does_int64_snprintf_work()
 main() {
   exit(! does_int64_snprintf_work());
 }],
-[pgac_cv_snprintf_long_long_int_format=$pgac_format; break],
+[pgac_cv_snprintf_long_long_length_modifier=$pgac_format; break],
 [],
-[pgac_cv_snprintf_long_long_int_format=cross; break])
+[pgac_cv_snprintf_long_long_length_modifier=cross; break])
 done])dnl AC_CACHE_VAL
 
-LONG_LONG_INT_FORMAT=''
+LONG_LONG_LENGTH_MODIFIER=''
 
-case $pgac_cv_snprintf_long_long_int_format in
+case $pgac_cv_snprintf_long_long_length_modifier in
   cross) AC_MSG_RESULT([cannot test (not on host machine)]);;
-  ?*)    AC_MSG_RESULT([$pgac_cv_snprintf_long_long_int_format])
-         LONG_LONG_INT_FORMAT=$pgac_cv_snprintf_long_long_int_format;;
+  ?*)    AC_MSG_RESULT([$pgac_cv_snprintf_long_long_length_modifier])
+         LONG_LONG_LENGTH_MODIFIER=$pgac_cv_snprintf_long_long_length_modifier;;
   *)     AC_MSG_RESULT(none);;
-esac])# PGAC_FUNC_SNPRINTF_LONG_LONG_INT_FORMAT
+esac])# PGAC_FUNC_SNPRINTF_LONG_LONG_LENGTH_MODIFIER
 
 
 # PGAC_FUNC_PRINTF_ARG_CONTROL
diff --git a/configure b/configure
index 0165c3c..8a04cc9 100755
--- a/configure
+++ b/configure
@@ -24812,19 +24812,19 @@ fi
 
 # If we found "long int" is 64 bits, assume snprintf handles it.  If
 # we found we need to use "long long int", better check.  We cope with
-# snprintfs that use %lld, %qd, or %I64d as the format.  If none of these
-# work, fall back to our own snprintf emulation (which we know uses %lld).
+# snprintfs that use lld, qd, or I64d as the modifier.  If none of these
+# work, fall back to our own snprintf emulation (which we know uses ll).
 
 if test "$HAVE_LONG_LONG_INT_64" = yes ; then
   if test $pgac_need_repl_snprintf = no; then
-    { $as_echo "$as_me:$LINENO: checking snprintf format for long long int" >&5
-$as_echo_n "checking snprintf format for long long int... " >&6; }
-if test "${pgac_cv_snprintf_long_long_int_format+set}" = set; then
+    { $as_echo "$as_me:$LINENO: checking snprintf length modifier for long long" >&5
+$as_echo_n "checking snprintf length modifier for long long... " >&6; }
+if test "${pgac_cv_snprintf_long_long_length_modifier+set}" = set; then
   $as_echo_n "(cached) " >&6
 else
-  for pgac_format in '%lld' '%qd' '%I64d'; do
+  for pgac_format in 'll' 'q' 'I64'; do
 if test "$cross_compiling" = yes; then
-  pgac_cv_snprintf_long_long_int_format=cross; break
+  pgac_cv_snprintf_long_long_length_modifier=cross; break
 else
   cat >conftest.$ac_ext <<_ACEOF
 /* confdefs.h.  */
@@ -24834,7 +24834,7 @@ cat >>conftest.$ac_ext <<_ACEOF
 /* end confdefs.h.  */
 #include <stdio.h>
 typedef long long int ac_int64;
-#define INT64_FORMAT "$pgac_format"
+#define INT64_FORMAT "%${pgac_format}d"
 
 ac_int64 a = 20000001;
 ac_int64 b = 40000005;
@@ -24879,7 +24879,7 @@ $as_echo "$ac_try_echo") >&5
   ac_status=$?
   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
   (exit $ac_status); }; }; then
-  pgac_cv_snprintf_long_long_int_format=$pgac_format; break
+  pgac_cv_snprintf_long_long_length_modifier=$pgac_format; break
 else
   $as_echo "$as_me: program exited with status $ac_status" >&5
 $as_echo "$as_me: failed program was:" >&5
@@ -24894,44 +24894,35 @@ fi
 done
 fi
 
-LONG_LONG_INT_FORMAT=''
+LONG_LONG_LENGTH_MODIFIER=''
 
-case $pgac_cv_snprintf_long_long_int_format in
+case $pgac_cv_snprintf_long_long_length_modifier in
   cross) { $as_echo "$as_me:$LINENO: result: cannot test (not on host machine)" >&5
 $as_echo "cannot test (not on host machine)" >&6; };;
-  ?*)    { $as_echo "$as_me:$LINENO: result: $pgac_cv_snprintf_long_long_int_format" >&5
-$as_echo "$pgac_cv_snprintf_long_long_int_format" >&6; }
-         LONG_LONG_INT_FORMAT=$pgac_cv_snprintf_long_long_int_format;;
+  ?*)    { $as_echo "$as_me:$LINENO: result: $pgac_cv_snprintf_long_long_length_modifier" >&5
+$as_echo "$pgac_cv_snprintf_long_long_length_modifier" >&6; }
+         LONG_LONG_LENGTH_MODIFIER=$pgac_cv_snprintf_long_long_length_modifier;;
   *)     { $as_echo "$as_me:$LINENO: result: none" >&5
 $as_echo "none" >&6; };;
 esac
-    if test "$LONG_LONG_INT_FORMAT" = ""; then
+    if test "$LONG_LONG_LENGTH_MODIFIER" = ""; then
       # Force usage of our own snprintf, since system snprintf is broken
       pgac_need_repl_snprintf=yes
-      LONG_LONG_INT_FORMAT='%lld'
+      LONG_LONG_LENGTH_MODIFIER='ll'
     fi
   else
     # Here if we previously decided we needed to use our own snprintf
-    LONG_LONG_INT_FORMAT='%lld'
+    LONG_LONG_LENGTH_MODIFIER='ll'
   fi
-  LONG_LONG_UINT_FORMAT=`echo "$LONG_LONG_INT_FORMAT" | sed 's/d$/u/'`
-  INT64_FORMAT="\"$LONG_LONG_INT_FORMAT\""
-  UINT64_FORMAT="\"$LONG_LONG_UINT_FORMAT\""
+  INT64_LENGTH_MODIFIER="$LONG_LONG_LENGTH_MODIFIER"
 else
-  # Here if we are not using 'long long int' at all
-  INT64_FORMAT='"%ld"'
-  UINT64_FORMAT='"%lu"'
+  # Here if we are not using 'long long' at all
+  INT64_LENGTH_MODIFIER='l'
 fi
 
 
 cat >>confdefs.h <<_ACEOF
-#define INT64_FORMAT $INT64_FORMAT
-_ACEOF
-
-
-
-cat >>confdefs.h <<_ACEOF
-#define UINT64_FORMAT $UINT64_FORMAT
+#define INT64_LENGTH_MODIFIER $INT64_LENGTH_MODIFIER
 _ACEOF
 
 
diff --git a/configure.in b/configure.in
index 5479eab..134ea55 100644
--- a/configure.in
+++ b/configure.in
@@ -1628,35 +1628,29 @@ fi
 
 # If we found "long int" is 64 bits, assume snprintf handles it.  If
 # we found we need to use "long long int", better check.  We cope with
-# snprintfs that use %lld, %qd, or %I64d as the format.  If none of these
-# work, fall back to our own snprintf emulation (which we know uses %lld).
+# snprintfs that use lld, qd, or I64d as the modifier.  If none of these
+# work, fall back to our own snprintf emulation (which we know uses ll).
 
 if test "$HAVE_LONG_LONG_INT_64" = yes ; then
   if test $pgac_need_repl_snprintf = no; then
-    PGAC_FUNC_SNPRINTF_LONG_LONG_INT_FORMAT
-    if test "$LONG_LONG_INT_FORMAT" = ""; then
+    PGAC_FUNC_SNPRINTF_LONG_LONG_LENGTH_MODIFIER
+    if test "$LONG_LONG_LENGTH_MODIFIER" = ""; then
       # Force usage of our own snprintf, since system snprintf is broken
       pgac_need_repl_snprintf=yes
-      LONG_LONG_INT_FORMAT='%lld'
+      LONG_LONG_LENGTH_MODIFIER='ll'
     fi
   else
     # Here if we previously decided we needed to use our own snprintf
-    LONG_LONG_INT_FORMAT='%lld'
+    LONG_LONG_LENGTH_MODIFIER='ll'
   fi
-  LONG_LONG_UINT_FORMAT=`echo "$LONG_LONG_INT_FORMAT" | sed 's/d$/u/'`
-  INT64_FORMAT="\"$LONG_LONG_INT_FORMAT\""
-  UINT64_FORMAT="\"$LONG_LONG_UINT_FORMAT\""
+  INT64_LENGTH_MODIFIER="$LONG_LONG_LENGTH_MODIFIER"
 else
-  # Here if we are not using 'long long int' at all
-  INT64_FORMAT='"%ld"'
-  UINT64_FORMAT='"%lu"'
+  # Here if we are not using 'long long' at all
+  INT64_LENGTH_MODIFIER='l'
 fi
 
-AC_DEFINE_UNQUOTED(INT64_FORMAT, $INT64_FORMAT,
-                   [Define to the appropriate snprintf format for 64-bit ints.])
-
-AC_DEFINE_UNQUOTED(UINT64_FORMAT, $UINT64_FORMAT,
-                   [Define to the appropriate snprintf format for unsigned 64-bit ints.])
+AC_DEFINE_UNQUOTED(INT64_LENGTH_MODIFIER, $INT64_LENGTH_MODIFIER,
+                   [Define to the appropriate snprintf length modifier for 64-bit ints.])
 
 # Now we have checked all the reasons to replace snprintf
 if test $pgac_need_repl_snprintf = yes; then
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 65eb3bd..a2d0856 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -3185,6 +3185,22 @@ expand_fmt_string(const char *fmt, ErrorData *edata)
 					appendStringInfoCharMacro(&buf, *cp2);
 				}
 			}
+			else if (*cp == 'z')
+			{
+				/*
+				 * Replace %z by the appropriate length modifier for printing
+				 * Size/size_t or ssize_t arguments for the current
+				 * platform. Note that we don't support specifying with and
+				 * precision modifiers, but that's ok for the current
+				 * callsites.
+				 */
+#if SIZEOF_SIZE_T == 4
+				/* no modifier needed */
+#else
+				appendBinaryStringInfo(&buf, CppAsString2(INT64_LENGTH_MODIFIER),
+									   strlen(CppAsString2(INT64_LENGTH_MODIFIER)));
+#endif
+			}
 			else
 			{
 				/* copy % and next char --- this avoids trouble with %%m */
diff --git a/src/include/c.h b/src/include/c.h
index beb20b0..4e6f827 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -127,6 +127,8 @@
 /*
  * CppAsString
  *		Convert the argument to a string, using the C preprocessor.
+ * CppAsString2
+ *		Convert the argument's value to a string, using the C preprocessor.
  * CppConcat
  *		Concatenate two arguments together, using the C preprocessor.
  *
@@ -135,6 +137,8 @@
  * backward compatibility with existing PostgreSQL code.
  */
 #define CppAsString(identifier) #identifier
+/* need a second indirection because we want to stringize the macro value, not the name */
+#define CppAsString2(x) CppAsString(x)
 #define CppConcat(x, y)			x##y
 
 /*
@@ -279,6 +283,9 @@ typedef unsigned long long int uint64;
 #define UINT64CONST(x) ((uint64) x)
 #endif
 
+/* format codes for 64bit integers */
+#define INT64_FORMAT "%"CppAsString2(INT64_LENGTH_MODIFIER)"d"
+#define UINT64_FORMAT "%"CppAsString2(INT64_LENGTH_MODIFIER)"u"
 
 /* Select timestamp representation (float8 or int64) */
 #ifdef USE_INTEGER_DATETIMES
@@ -894,9 +901,6 @@ typedef NameData *Name;
  * Make sure this matches the installation rules in nls-global.mk.
  */
 
-/* need a second indirection because we want to stringize the macro value, not the name */
-#define CppAsString2(x) CppAsString(x)
-
 #ifdef SO_MAJOR_VERSION
 #define PG_TEXTDOMAIN(domain) (domain CppAsString2(SO_MAJOR_VERSION) "-" PG_MAJORVERSION)
 #else
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d482e58..52aa03d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -650,8 +650,8 @@
 /* Define to 1 if your compiler understands __VA_ARGS__ in macros. */
 #undef HAVE__VA_ARGS
 
-/* Define to the appropriate snprintf format for 64-bit ints. */
-#undef INT64_FORMAT
+/* Define to the appropriate snprintf length modifier for 64-bit ints. */
+#undef INT64_LENGTH_MODIFIER
 
 /* Define to build with Kerberos 5 support. (--with-krb5) */
 #undef KRB5
@@ -745,9 +745,6 @@
 /* Define to 1 if your <sys/time.h> declares `struct tm'. */
 #undef TM_IN_SYS_TIME
 
-/* Define to the appropriate snprintf format for unsigned 64-bit ints. */
-#undef UINT64_FORMAT
-
 /* Define to 1 to build with assertion checks. (--enable-cassert) */
 #undef USE_ASSERT_CHECKING
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index b69414f..e7eac3a 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -538,8 +538,8 @@
 /* Define to 1 if your compiler understands __VA_ARGS__ in macros. */
 #define HAVE__VA_ARGS 1
 
-/* Define to the appropriate snprintf format for 64-bit ints, if any. */
-#define INT64_FORMAT "%lld"
+/* Define to the appropriate snprintf length modifier for 64-bit ints. */
+#define INT64_LENGTH_MODIFIER ll
 
 /* Define to build with Kerberos 5 support. (--with-krb5) */
 /* #undef KRB5 */
@@ -617,10 +617,6 @@
 /* Define to 1 if your <sys/time.h> declares `struct tm'. */
 /* #undef TM_IN_SYS_TIME */
 
-/* Define to the appropriate snprintf format for unsigned 64-bit ints, if any.
-   */
-#define UINT64_FORMAT "%llu"
-
 /* Define to 1 to build with assertion checks. (--enable-cassert) */
 /* #undef USE_ASSERT_CHECKING */
 
-- 
1.8.5.rc2.dirty

