[devel] Remove png_snprintf, add formatted warning messages.

 This change adds internal APIs to allow png_warning messages to have parameters
 without requiring the host OS to implelment snprintf.  As a side effect the
 dependency of the RFC1132 code on stdio is removed and PNG_NO_WARNINGS does
 actually work now.
diff --git a/png.c b/png.c
index e86dfbd..08a803a 100644
--- a/png.c
+++ b/png.c
@@ -137,6 +137,61 @@
       png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
 }
 
+/* Check a user supplied version number, called from both read and write
+ * functions that create a png_struct
+ */
+int
+png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver)
+{
+   if (user_png_ver)
+   {
+      int i = 0;
+
+      do
+      {
+         if (user_png_ver[i] != png_libpng_ver[i])
+            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+      } while (png_libpng_ver[i++]);
+   }
+
+   else
+      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+
+   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+   {
+     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+      * we must recompile any applications that use any older library version.
+      * For versions after libpng 1.0, we will be compatible, so we need
+      * only check the first digit.
+      */
+      if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+          (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+          (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+      {
+#ifdef PNG_WARNINGS_SUPPORTED
+         size_t pos = 0;
+         char m[128];
+
+         pos = png_safecat(m, sizeof m, pos, "Application built with libpng-");
+         pos = png_safecat(m, sizeof m, pos, user_png_ver);
+         pos = png_safecat(m, sizeof m, pos, " but running with ");
+         pos = png_safecat(m, sizeof m, pos, png_libpng_ver);
+
+         png_warning(png_ptr, m);
+#endif
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+         png_ptr->flags = 0;
+#endif
+
+         return 0;
+      }
+   }
+
+   /* Success return. */
+   return 1;
+}
+
 /* Allocate the memory for an info_struct for the application.  We don't
  * really need the png_ptr, but it could potentially be useful in the
  * future.  This should be used in favour of malloc(png_sizeof(png_info))
@@ -518,28 +573,37 @@
    if (png_ptr == NULL)
       return (NULL);
 
-   if (png_ptr->time_buffer == NULL)
    {
-      png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
-         png_sizeof(char)));
+      size_t pos = 0;
+      char number_buf[5]; /* enough for a four digit year */
+
+#     define APPEND_STRING(string)\
+         pos = png_safecat(png_ptr->time_buffer, sizeof png_ptr->time_buffer,\
+            pos, (string))
+#     define APPEND_NUMBER(format, value)\
+         APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value)))
+#     define APPEND(ch)\
+         if (pos < (sizeof png_ptr->time_buffer)-1)\
+            png_ptr->time_buffer[pos++] = (ch)
+         
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->day % 32);
+      APPEND(' ');
+      APPEND_STRING(short_months[(ptime->month - 1) % 12]);
+      APPEND(' ');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year);
+      APPEND(' ');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, ptime->hour % 24);
+      APPEND(':');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, ptime->minute % 60);
+      APPEND(':');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, ptime->second % 61);
+      APPEND_STRING(" +0000"); /* This reliably terminates the buffer */
+
+#     undef APPEND
+#     undef APPEND_NUMBER
+#     undef APPEND_STRING
    }
 
-#    ifdef USE_FAR_KEYWORD
-   {
-      char near_time_buf[29];
-      png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000",
-          ptime->day % 32, short_months[(ptime->month - 1) % 12],
-          ptime->year, ptime->hour % 24, ptime->minute % 60,
-          ptime->second % 61);
-      png_memcpy(png_ptr->time_buffer, near_time_buf,
-          29*png_sizeof(char));
-   }
-#    else
-   png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000",
-       ptime->day % 32, short_months[(ptime->month - 1) % 12],
-       ptime->year, ptime->hour % 24, ptime->minute % 60,
-       ptime->second % 61);
-#    endif
    return png_ptr->time_buffer;
 }
 #  endif /* PNG_TIME_RFC1123_SUPPORTED */
diff --git a/png.h b/png.h
index c822e80..b67faad 100644
--- a/png.h
+++ b/png.h
@@ -303,16 +303,15 @@
  *    upward through 1.5.3beta05 are Y2K compliant.  It is my belief that
  *    earlier versions were also Y2K compliant.
  *
- *    Libpng only has three year fields.  One is a 2-byte unsigned integer
- *    that will hold years up to 65535.  The other two hold the date in text
+ *    Libpng only has two year fields.  One is a 2-byte unsigned integer
+ *    that will hold years up to 65535.  The other holds the date in text
  *    format, and will hold years up to 9999.
  *
  *    The integer is
  *        "png_uint_16 year" in png_time_struct.
  *
- *    The strings are
- *        "png_charp time_buffer" in png_struct and
- *        "near_time_buffer", which is a local character string in png.c.
+ *    The string is
+ *        "png_char time_buffer" in png_struct
  *
  *    There are seven time-related functions:
  *        png.c: png_convert_to_rfc_1123() in png.c
@@ -1645,6 +1644,7 @@
 PNG_EXPORTA(104, void, png_err, (png_structp png_ptr), PNG_NORETURN);
 #endif
 
+#ifdef PNG_WARNINGS_SUPPORTED
 /* Non-fatal error in libpng.  Can continue, but may have a problem. */
 PNG_EXPORT(105, void, png_warning, (png_structp png_ptr,
     png_const_charp warning_message));
@@ -1652,6 +1652,7 @@
 /* Non-fatal error in libpng, chunk name is prepended to message. */
 PNG_EXPORT(106, void, png_chunk_warning, (png_structp png_ptr,
     png_const_charp warning_message));
+#endif
 
 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
 /* Benign error in libpng.  Can continue, but may have a problem.
diff --git a/pngerror.c b/pngerror.c
index e90560c..85f62b1 100644
--- a/pngerror.c
+++ b/pngerror.c
@@ -89,15 +89,119 @@
 PNG_FUNCTION(void,PNGAPI
 png_err,(png_structp png_ptr),PNG_NORETURN)
 {
+   /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed erroneouly
+    * as '\0'.  This was apparently an error, and png_default_error will crash
+    * in this case.
+    */
    if (png_ptr != NULL && png_ptr->error_fn != NULL)
-      (*(png_ptr->error_fn))(png_ptr, '\0');
+      (*(png_ptr->error_fn))(png_ptr, "");
 
    /* If the custom handler doesn't exist, or if it returns,
       use the default handler, which will not return. */
-   png_default_error(png_ptr, '\0');
+   png_default_error(png_ptr, "");
 }
 #endif /* PNG_ERROR_TEXT_SUPPORTED */
 
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Utility to safely appends strings to a buffer.  This never errors out so
+ * error checking is not required in the caller.
+ */
+size_t
+png_safecat(png_charp buffer, size_t bufsize, size_t pos,
+   png_const_charp string)
+{
+   if (buffer != NULL && pos < bufsize)
+   {
+      if (string != NULL) while (*string != '\0' && pos < bufsize-1)
+         buffer[pos++] = *string++;
+
+      buffer[pos] = '\0';
+   }
+
+   return pos;
+}
+
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string.
+ */
+png_charp
+png_format_number(png_const_charp start, png_charp end, int format,
+   png_alloc_size_t number)
+{
+   int count = 0;    /* number of digits output */
+   int mincount = 1; /* minimum number required */
+   int output = 0;   /* digit output (for the fixed point format) */
+
+   *--end = '\0';
+
+   /* This is written so that the loop always runs at least once, even with
+    * number zero.
+    */
+   while (end > start && (number != 0 || count < mincount))
+   {
+
+      static const char digits[] = "0123456789ABCDEF";
+
+      switch (format)
+      {
+         case PNG_NUMBER_FORMAT_fixed:
+            /* Needs five digits (the fraction) */
+            mincount = 5;
+            if (output || number % 10 != 0)
+            {
+               *--end = digits[number % 10];
+               output = 1;
+            }
+            number /= 10;
+            break;
+
+         case PNG_NUMBER_FORMAT_02u:
+            /* Expects at least 2 digits. */
+            mincount = 2;
+            /* fall through */
+
+         case PNG_NUMBER_FORMAT_u:
+            *--end = digits[number % 10];
+            number /= 10;
+            break;
+
+         case PNG_NUMBER_FORMAT_02x:
+            /* This format expects at least two digits */
+            mincount = 2;
+            /* fall through */
+
+         case PNG_NUMBER_FORMAT_x:
+            *--end = digits[number & 0xf];
+            number >>= 4;
+            break;
+
+         default: /* an error */
+            number = 0;
+            break;
+      }
+
+      /* Keep track of the number of digits added */
+      ++count;
+
+      /* Float a fixed number here: */
+      if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start)
+      {
+         /* End of the fraction, but maybe nothing was output?  In that case
+          * drop the decimal point.  If the number is a true zero handle that
+          * here.
+          */
+         if (output)
+            *--end = '.';
+         else if (number == 0) /* and !output */
+            *--end = '0';
+      }
+   }
+
+   return end;
+}
+#endif
+
 #ifdef PNG_WARNINGS_SUPPORTED
 /* This function is called whenever there is a non-fatal error.  This function
  * should not be changed.  If there is a need to handle warnings differently,
@@ -128,6 +232,115 @@
    else
       png_default_warning(png_ptr, warning_message + offset);
 }
+
+/* These functions support 'formatted' warning messages with up to
+ * PNG_WARNING_PARAMETER_COUNT parameters.  In the format string the parameter
+ * is introduced by @<number>, where 'number' starts at 1.  This follows the
+ * standard established by X/Open for internationalizable error messages.
+ */
+void
+png_warning_parameter(png_warning_parameters p, int number,
+   png_const_charp string)
+{
+   if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
+      (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
+}
+
+void
+png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
+   png_alloc_size_t value)
+{
+   char buffer[PNG_NUMBER_BUFFER_SIZE];
+   png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
+}
+
+void
+png_warning_parameter_signed(png_warning_parameters p, int number, int format,
+   png_int_32 value)
+{
+   png_alloc_size_t u;
+   png_charp str;
+   char buffer[PNG_NUMBER_BUFFER_SIZE];
+
+   /* Avoid overflow by doing the negate in a png_alloc_size_t: */
+   u = (png_alloc_size_t)value;
+   if (value < 0)
+      u = ~u + 1;
+
+   str = PNG_FORMAT_NUMBER(buffer, format, u);
+
+   if (value < 0 && str > buffer)
+      *--str = '-';
+
+   png_warning_parameter(p, number, str);
+}
+
+void
+png_formatted_warning(png_structp png_ptr, png_warning_parameters p,
+   png_const_charp message)
+{
+   /* The internal buffer is just 128 bytes - enough for all our messages,
+    * overflow doesn't happen because this code checks!
+    */
+   size_t i;
+   char msg[128];
+
+   for (i=0; i<(sizeof msg)-1 && *message != '\0'; ++i)
+   {
+      if (*message == '@')
+      {
+         int parameter = -1;
+         switch (*++message)
+         {
+            case '1':
+               parameter = 0;
+               break;
+
+            case '2':
+               parameter = 1;
+               break;
+
+            case '\0':
+               continue; /* To break out of the for loop above. */
+
+            default:
+               break;
+         }
+
+         if (parameter >= 0 && parameter < PNG_WARNING_PARAMETER_COUNT)
+         {
+            /* Append this parameter */
+            png_const_charp parm = p[parameter];
+            png_const_charp pend = p[parameter] + (sizeof p[parameter]);
+
+            /* No need to copy the trailing '\0' here, but there is no guarantee
+             * that parm[] has been initialized, so there is no guarantee of a
+             * trailing '\0':
+             */
+            for (; i<(sizeof msg)-1 && parm != '\0' && parm < pend; ++i)
+               msg[i] = *parm++;
+
+            ++message;
+            continue;
+         }
+
+         /* else not a parameter and there is a character after the @ sign; just
+          * copy that.
+          */
+      }
+
+      /* At this point *message can't be '\0', even in the bad parameter case
+       * above where there is a lone '@' at the end of the message string.
+       */
+      msg[i] = *message++;
+   }
+
+   /* i is always less than (sizeof msg), so: */
+   msg[i] = '\0';
+
+   /* And this is the formatted message: */
+   png_warning(png_ptr, msg);
+}
 #endif /* PNG_WARNINGS_SUPPORTED */
 
 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
@@ -287,7 +500,8 @@
 {
 #ifdef PNG_CONSOLE_IO_SUPPORTED
 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
-   if (*error_message == PNG_LITERAL_SHARP)
+   /* Check on NULL only added in 1.5.3 */
+   if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
    {
       /* Strip "#nnnn " from beginning of error message. */
       int offset;
@@ -317,11 +531,11 @@
    else
 #endif
    {
-      fprintf(stderr, "libpng error: %s", error_message);
+      fprintf(stderr, "libpng error: %s", error_message ? error_message :
+         "undefined");
       fprintf(stderr, PNG_STRING_NEWLINE);
    }
-#endif
-#ifndef PNG_CONSOLE_IO_SUPPORTED
+#else
    PNG_UNUSED(error_message) /* Make compiler happy */
 #endif
    png_longjmp(png_ptr, 1);
@@ -414,7 +628,11 @@
 
    png_ptr->error_ptr = error_ptr;
    png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    png_ptr->warning_fn = warning_fn;
+#else
+   PNG_UNUSED(warning_fn)
+#endif
 }
 
 
diff --git a/pngpriv.h b/pngpriv.h
index 9f32ba6..c18b26c 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -38,7 +38,6 @@
  * still required (as of 2011-05-02.)
  */
 #define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
-#define _ISOC99_SOURCE 1 /* for snprintf */
 
 /* This is required for the definition of abort(), used as a last ditch
  * error handler when all else fails.
@@ -116,12 +115,20 @@
 #  define PNG_ZBUF_SIZE 65536L
 #endif
 
-/* If warnings or errors are turned off the code is disabled
- * or redirected here.
+/* If warnings or errors are turned off the code is disabled or redirected here.
+ * From 1.5.3 functions have been added to allow very limited formatting of
+ * error and warning messages - this code will also be disabled here.
  */
-#ifndef PNG_WARNINGS_SUPPORTED
-#  define png_warning(s1,s2) ((void)0)
-#  define png_chunk_warning(s1,s2) ((void)0)
+#ifdef PNG_WARNINGS_SUPPORTED
+#  define PNG_WARNING_PARAMETERS(p) png_warning_parameters p;
+#else
+#  define png_warning(s1,s2) ((void)(s1))
+#  define png_chunk_warning(s1,s2) ((void)(s1))
+#  define png_warning_parameter(p,number,string) ((void)0)
+#  define png_warning_parameter_unsigned(p,number,format,value) ((void)0)
+#  define png_warning_parameter_signed(p,number,format,value) ((void)0)
+#  define png_formatted_warning(pp,p,message) ((void)(pp))
+#  define PNG_WARNING_PARAMETERS(p) 
 #endif
 #ifndef PNG_ERROR_TEXT_SUPPORTED
 #  define png_error(s1,s2) png_err(s1)
@@ -246,29 +253,6 @@
 #  endif
 #endif
 /* End of memory model/platform independent support */
-
-#if !defined(PNG_NO_SNPRINTF) && !defined(__STRICT_ANSI__)
-#  ifdef _MSC_VER
-#    define png_snprintf _snprintf   /* Added to v 1.2.19 */
-#    define png_snprintf2 _snprintf
-#    define png_snprintf6 _snprintf
-#  else
-#    define png_snprintf snprintf   /* Added to v 1.2.19 */
-#    define png_snprintf2 snprintf
-#    define png_snprintf6 snprintf
-#  endif
-#else
-  /* You don't have or don't want to use snprintf().  Caution: Using
-   * sprintf instead of snprintf exposes your application to accidental
-   * or malevolent buffer overflows.  If you don't have snprintf()
-   * as a general rule you should provide one (you can get one from
-   * Portable OpenSSH).
-   */
-#  define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1)
-#  define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2)
-#  define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \
-      png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6)
-#endif
 /* End of 1.5.0beta36 move from pngconf.h */
 
 /* CONSTANTS and UTILITY MACROS
@@ -488,6 +472,12 @@
  * be found in the files where the functions are located.
  */
 
+/* Check the user version string for compatibility, returns false if the version
+ * numbers aren't compatible.
+ */
+PNG_EXTERN int png_user_version_check(png_structp png_ptr,
+   png_const_charp user_png_ver);
+
 /* Allocate memory for an internal libpng struct */
 PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct,PNGARG((int type)),
    PNG_ALLOCATED);
@@ -1099,6 +1089,76 @@
    png_const_charp name),PNG_NORETURN);
 #endif
 
+/* Various internal functions to handle formatted warning messages, currently
+ * only implemented for warnings.
+ */
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
+ * the end.  Always leaves the buffer nul terminated.  Never errors out (and
+ * there is no error code.)
+ */
+PNG_EXTERN size_t png_safecat(png_charp buffer, size_t bufsize, size_t pos,
+    png_const_charp string);
+
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string.  This utility only
+ * does unsigned values.
+ */
+PNG_EXTERN png_charp png_format_number(png_const_charp start, png_charp end,
+   int format, png_alloc_size_t number);
+
+/* Convenience macro that takes an array: */
+#define PNG_FORMAT_NUMBER(buffer,format,number) \
+   png_format_number(buffer, buffer + (sizeof buffer), format, number)
+
+/* Suggested size for a number buffer (enough for 64 bits and a sign!) */
+#define PNG_NUMBER_BUFFER_SIZE 24
+
+/* These are the integer formats currently supported, the name is formed from
+ * the standard printf(3) format string.
+ */
+#define PNG_NUMBER_FORMAT_u     1 /* chose unsigned API! */
+#define PNG_NUMBER_FORMAT_02u   2
+#define PNG_NUMBER_FORMAT_d     1 /* chose signed API! */
+#define PNG_NUMBER_FORMAT_02d   2
+#define PNG_NUMBER_FORMAT_x     3
+#define PNG_NUMBER_FORMAT_02x   4
+#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */
+#endif
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* New defines and members adding in libpng-1.5.3 */
+#  define PNG_WARNING_PARAMETER_SIZE 32
+#  define PNG_WARNING_PARAMETER_COUNT 8
+
+/* An l-value of this type has to be passed to the APIs below to cache the
+ * values of the parameters to a formatted warning message.
+ */
+typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
+   PNG_WARNING_PARAMETER_SIZE];
+
+PNG_EXTERN void png_warning_parameter(png_warning_parameters p, int number,
+    png_const_charp string);
+    /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
+     * including the trailing '\0'.
+     */
+PNG_EXTERN void png_warning_parameter_unsigned(png_warning_parameters p,
+    int number, int format, png_alloc_size_t value);
+    /* Use png_alloc_size_t because it is an unsigned type as big as any we
+     * need to output.  Use the following for a signed value.
+     */
+PNG_EXTERN void png_warning_parameter_signed(png_warning_parameters p,
+    int number, int format, png_int_32 value);
+
+PNG_EXTERN void png_formatted_warning(png_structp png_ptr,
+    png_warning_parameters p, png_const_charp message);
+    /* 'message' follows the X/Open approach of using @1, @2 to insert
+     * parameters previously supplied using the above functions.  Errors in
+     * specifying the paramters will simple result in garbage substitutions.
+     */
+#endif
+
 /* ASCII to FP interfaces, currently only implemented if sCAL
  * support is required.
  */
diff --git a/pngread.c b/pngread.c
index b961ac0..70c193d 100644
--- a/pngread.c
+++ b/pngread.c
@@ -51,8 +51,6 @@
 #endif
 #endif
 
-   int i;
-
    png_debug(1, "in png_create_read_struct");
 
 #ifdef PNG_USER_MEM_SUPPORTED
@@ -101,54 +99,9 @@
 
    png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
 
-   if (user_png_ver)
-   {
-      i = 0;
-
-      do
-      {
-         if (user_png_ver[i] != png_libpng_ver[i])
-            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-      } while (png_libpng_ver[i++]);
-   }
-
-   else
-      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-
-
-   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
-   {
-     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
-      * we must recompile any applications that use any older library version.
-      * For versions after libpng 1.0, we will be compatible, so we need
-      * only check the first digit.
-      */
-      if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
-          (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
-          (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
-      {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-         char msg[80];
-         if (user_png_ver)
-         {
-            png_snprintf2(msg, 80,
-                "Application built with libpng-%.20s"
-                " but running with %.20s",
-                user_png_ver,
-                png_libpng_ver);
-            png_warning(png_ptr, msg);
-         }
-#else
-         png_warning(png_ptr,
-             "Incompatible libpng version in application and library");
-#endif
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-         png_ptr->flags = 0;
-#endif
-
-         png_cleanup_needed = 1;
-      }
-   }
+   /* Call the general version checker (shared with read and write code): */
+   if (!png_user_version_check(png_ptr, user_png_ver))
+      png_cleanup_needed = 1;
 
    if (!png_cleanup_needed)
    {
@@ -1169,7 +1122,9 @@
    jmp_buf tmp_jmp;
 #endif
    png_error_ptr error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    png_error_ptr warning_fn;
+#endif
    png_voidp error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    png_free_ptr free_fn;
@@ -1255,10 +1210,6 @@
 #endif
 #endif
 
-#ifdef PNG_TIME_RFC1123_SUPPORTED
-   png_free(png_ptr, png_ptr->time_buffer);
-#endif
-
    inflateEnd(&png_ptr->zstream);
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
@@ -1279,7 +1230,9 @@
 #endif
 
    error_fn = png_ptr->error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    warning_fn = png_ptr->warning_fn;
+#endif
    error_ptr = png_ptr->error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    free_fn = png_ptr->free_fn;
@@ -1288,7 +1241,9 @@
    png_memset(png_ptr, 0, png_sizeof(png_struct));
 
    png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    png_ptr->warning_fn = warning_fn;
+#endif
    png_ptr->error_ptr = error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    png_ptr->free_fn = free_fn;
diff --git a/pngrtran.c b/pngrtran.c
index 76892ae..2980a85 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -1433,16 +1433,11 @@
 
    if (png_ptr->row_buf == NULL)
    {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-      char msg[50];
-
-      png_snprintf2(msg, 50,
-          "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number,
-          png_ptr->pass);
-      png_error(png_ptr, msg);
-#else
+      /* Prior to 1.5.3 this output row/pass where the NULL pointer is, but this
+       * error is incredibly rare and incredibly easy to debug without this
+       * information.
+       */
       png_error(png_ptr, "NULL row buffer");
-#endif
    }
 #ifdef PNG_WARN_UNINITIALIZED_ROW
    if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
diff --git a/pngrutil.c b/pngrutil.c
index f73021b..d52f100 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -369,41 +369,31 @@
        * and the error message is dumped into the uncompressed
        * buffer if available.
        */
+#     ifdef PNG_WARNINGS_SUPPORTED
       {
-         PNG_CONST char *msg;
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-         char umsg[52];
-#endif
+         png_const_charp msg;
+
          if (png_ptr->zstream.msg != 0)
             msg = png_ptr->zstream.msg;
 
-         else
+         else switch (ret)
          {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-            switch (ret)
-            {
-               case Z_BUF_ERROR:
-                  msg = "Buffer error in compressed datastream in %s chunk";
-                  break;
+            case Z_BUF_ERROR:
+               msg = "Buffer error in compressed datastream";
+               break;
 
-               case Z_DATA_ERROR:
-                  msg = "Data error in compressed datastream in %s chunk";
-                  break;
+            case Z_DATA_ERROR:
+               msg = "Data error in compressed datastream";
+               break;
 
-               default:
-                  msg = "Incomplete compressed datastream in %s chunk";
-                  break;
-            }
-
-            png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name);
-            msg = umsg;
-#else
-            msg = "Damaged compressed datastream in chunk other than IDAT";
-#endif
+            default:
+               msg = "Incomplete compressed datastream";
+               break;
          }
 
-         png_warning(png_ptr, msg);
+         png_chunk_warning(png_ptr, msg);
       }
+#     endif
 
       /* 0 means an error - notice that this code simply ignores
        * zero length compressed chunks as a result.
@@ -499,15 +489,9 @@
 
    else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
    {
-#ifdef PNG_STDIO_SUPPORTED
-      char umsg[50];
-
-      png_snprintf(umsg, sizeof umsg,
-          "Unknown zTXt compression type %d", comp_type);
-      png_warning(png_ptr, umsg);
-#else
-      png_warning(png_ptr, "Unknown zTXt compression type");
-#endif
+      PNG_WARNING_PARAMETERS(p)
+      png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d, comp_type);
+      png_formatted_warning(png_ptr, p, "Unknown zTXt compression type @1");
 
       /* The recovery is to simply drop the data. */
    }
@@ -845,12 +829,10 @@
    {
       if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
       {
-         png_warning(png_ptr,
-             "Ignoring incorrect gAMA value when sRGB is also present");
-
-#    ifdef PNG_CONSOLE_IO_SUPPORTED
-         fprintf(stderr, "gamma = (%d/100000)", (int)igamma);
-#    endif
+         PNG_WARNING_PARAMETERS(p)
+         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, igamma);
+         png_formatted_warning(png_ptr, p,
+             "Ignoring incorrect gAMA value @1 when sRGB is also present");
          return;
       }
    }
@@ -1019,16 +1001,20 @@
           PNG_OUT_OF_RANGE(x_blue,  15000,  1000) ||
           PNG_OUT_OF_RANGE(y_blue,   6000,  1000))
       {
-         png_warning(png_ptr,
-             "Ignoring incorrect cHRM value when sRGB is also present");
+         PNG_WARNING_PARAMETERS(p)
 
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-         fprintf(stderr, "wx=%d, wy=%d, rx=%d, ry=%d\n",
-             x_white, y_white, x_red, y_red);
+         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, x_white);
+         png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_fixed, y_white);
+         png_warning_parameter_signed(p, 3, PNG_NUMBER_FORMAT_fixed, x_red);
+         png_warning_parameter_signed(p, 4, PNG_NUMBER_FORMAT_fixed, y_red);
+         png_warning_parameter_signed(p, 5, PNG_NUMBER_FORMAT_fixed, x_green);
+         png_warning_parameter_signed(p, 6, PNG_NUMBER_FORMAT_fixed, y_green);
+         png_warning_parameter_signed(p, 7, PNG_NUMBER_FORMAT_fixed, x_blue);
+         png_warning_parameter_signed(p, 8, PNG_NUMBER_FORMAT_fixed, y_blue);
 
-         fprintf(stderr, "gx=%d, gy=%d, bx=%d, by=%d\n",
-             x_green, y_green, x_blue, y_blue);
-#endif /* PNG_CONSOLE_IO_SUPPORTED */
+         png_formatted_warning(png_ptr, p,
+             "Ignoring incorrect cHRM white(@1,@2) r(@3,@4)g(@5,@6)b(@7,@8) "
+             "when sRGB is also present");
       }
       return;
    }
@@ -1095,11 +1081,13 @@
    {
       if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500L, 500))
       {
-         png_warning(png_ptr,
-             "Ignoring incorrect gAMA value when sRGB is also present");
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-         fprintf(stderr, "incorrect gamma=(%d/100000)\n", info_ptr->gamma);
-#endif
+         PNG_WARNING_PARAMETERS(p)
+
+         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed,
+            info_ptr->gamma);
+
+         png_formatted_warning(png_ptr, p,
+             "Ignoring incorrect gAMA value @1 when sRGB is also present");
       }
    }
 #endif /* PNG_READ_gAMA_SUPPORTED */
@@ -1239,23 +1227,15 @@
    /* And the following guarantees that profile_size == profile_length. */
    if (profile_size > profile_length)
    {
+      PNG_WARNING_PARAMETERS(p)
+
       png_free(png_ptr, png_ptr->chunkdata);
       png_ptr->chunkdata = NULL;
-#ifdef PNG_STDIO_SUPPORTED
-      {
-         char umsg[80];
 
-         png_snprintf2(umsg, 80,
-             "Ignoring iCCP chunk with declared size = %u "
-              "and actual length = %u",
-              (unsigned int) profile_size,
-              (unsigned int) profile_length);
-         png_warning(png_ptr, umsg);
-      }
-#else
-      png_warning(png_ptr,
-         "Ignoring iCCP chunk with uncompressed size mismatch");
-#endif
+      png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_u, profile_size);
+      png_warning_parameter_unsigned(p, 2, PNG_NUMBER_FORMAT_u, profile_length);
+      png_formatted_warning(png_ptr, p,
+         "Ignoring iCCP chunk with declared size = @1 and actual length = @2");
       return;
    }
 
diff --git a/pngstruct.h b/pngstruct.h
index ca951a4..0f570aa 100644
--- a/pngstruct.h
+++ b/pngstruct.h
@@ -33,7 +33,9 @@
    png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
 #endif
    png_error_ptr error_fn;    /* function for printing errors and aborting */
+#ifdef PNG_WARNINGS_SUPPORTED
    png_error_ptr warning_fn;  /* function for printing warnings */
+#endif
    png_voidp error_ptr;       /* user supplied struct for error functions */
    png_rw_ptr write_data_fn;  /* function for writing output data */
    png_rw_ptr read_data_fn;   /* function for reading input data */
@@ -219,7 +221,7 @@
 #endif
 
 #ifdef PNG_TIME_RFC1123_SUPPORTED
-   png_charp time_buffer; /* String to hold RFC 1123 time text */
+   char time_buffer[29]; /* String to hold RFC 1123 time text */
 #endif
 
 /* New members added in libpng-1.0.6 */
diff --git a/pngvalid.c b/pngvalid.c
index 945529b..d88b561 100644
--- a/pngvalid.c
+++ b/pngvalid.c
@@ -2492,6 +2492,7 @@
  * to ensure that they get detected - it should not be possible to write an
  * invalid image with libpng!
  */
+#ifdef PNG_WARNINGS_SUPPORTED
 static void
 sBIT0_error_fn(png_structp pp, png_infop pi)
 {
@@ -2526,6 +2527,7 @@
    unsigned int    warning :1; /* the error is a warning... */
 } error_test[] =
     {
+       /* no warnings makes these errors undetectable. */
        { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
        { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
     };
@@ -2669,10 +2671,12 @@
 
    return 1; /* keep going */
 }
+#endif
 
 static void
 perform_error_test(png_modifier *pm)
 {
+#ifdef PNG_WARNINGS_SUPPORTED /* else there are no cases that work! */
    /* Need to do this here because we just write in this test. */
    safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
 
@@ -2690,6 +2694,73 @@
 
    if (!make_errors(pm, 6, 3, WRITE_BDHI))
       return;
+#else
+   UNUSED(pm)
+#endif
+}
+
+/* This is just to validate the internal PNG formatting code - if this fails
+ * then the warning messages the library outputs will probably be garbage.
+ */
+static void
+perform_formatting_test(png_store *volatile ps)
+{
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+   /* The handle into the formatting code is the RFC1123 support; this test does
+    * nothing if that is compiled out.
+    */
+   context(ps, fault);
+
+   Try
+   {
+      png_const_charp correct = "29 Aug 2079 13:53:60 +0000";
+      png_const_charp result;
+      png_structp pp;
+      png_time pt;
+
+      pp = set_store_for_write(ps, NULL, "libpng formatting test");
+
+      if (pp == NULL)
+         Throw ps;
+
+
+      /* Arbitrary settings: */
+      pt.year = 2079;
+      pt.month = 8;
+      pt.day = 29;
+      pt.hour = 13;
+      pt.minute = 53;
+      pt.second = 60; /* a leap second */
+
+      result = png_convert_to_rfc1123(pp, &pt);
+
+      if (result == NULL)
+         png_error(pp, "png_convert_to_rfc1123 failed");
+
+      if (strcmp(result, correct) != 0)
+      {
+         size_t pos = 0;
+         char msg[128];
+
+         pos = safecat(msg, sizeof msg, pos, "png_convert_to_rfc1123(");
+         pos = safecat(msg, sizeof msg, pos, correct);
+         pos = safecat(msg, sizeof msg, pos, ") returned: '");
+         pos = safecat(msg, sizeof msg, pos, result);
+         pos = safecat(msg, sizeof msg, pos, "'");
+
+         png_error(pp, msg);
+      }
+
+      store_write_reset(ps);
+   }
+
+   Catch(fault)
+   {
+      store_write_reset(fault);
+   }
+#else
+   UNUSED(ps)
+#endif
 }
 
 /* Because we want to use the same code in both the progressive reader and the
@@ -5596,8 +5667,13 @@
                      od-encoded_sample, id, sbit, isbit, od,
                      encoded_sample, is_lo, is_hi);
 
-                  png_warning(pp, msg);
+#                 ifdef PNG_WARNINGS_SUPPORTED
+                     png_warning(pp, msg);
+#                 else
+                     store_warning(pp, msg);
+#                 endif
                }
+
             }
          }
       }
@@ -6671,6 +6747,7 @@
       if (pm.test_standard)
       {
          perform_interlace_macro_validation();
+         perform_formatting_test(&pm.this);
          perform_standard_test(&pm);
          perform_error_test(&pm);
       }
diff --git a/pngwrite.c b/pngwrite.c
index bbd3de8..087f316 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -465,7 +465,6 @@
    jmp_buf tmp_jmpbuf;
 #endif
 #endif
-   int i;
 
    png_debug(1, "in png_create_write_struct");
 
@@ -504,49 +503,8 @@
 #endif /* PNG_USER_MEM_SUPPORTED */
    png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
 
-   if (user_png_ver)
-   {
-      i = 0;
-      do
-      {
-         if (user_png_ver[i] != png_libpng_ver[i])
-            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-      } while (png_libpng_ver[i++]);
-   }
-
-   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
-   {
-     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
-      * we must recompile any applications that use any older library version.
-      * For versions after libpng 1.0, we will be compatible, so we need
-      * only check the first digit.
-      */
-     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
-         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
-         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
-     {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-        char msg[80];
-
-        if (user_png_ver)
-        {
-            png_snprintf2(msg, 80,
-                "Application built with libpng-%.20s"
-                " but running with %.20s",
-                user_png_ver,
-                png_libpng_ver);
-            png_warning(png_ptr, msg);
-         }
-#else
-         png_warning(png_ptr,
-             "Incompatible libpng version in application and library");
-#endif
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
-        png_ptr->flags = 0;
-#endif
-        png_cleanup_needed = 1;
-     }
-   }
+   if (!png_user_version_check(png_ptr, user_png_ver))
+      png_cleanup_needed = 1;
 
    /* Initialize zbuf - compression buffer */
    png_ptr->zbuf_size = PNG_ZBUF_SIZE;
@@ -985,7 +943,9 @@
    jmp_buf tmp_jmp; /* Save jump buffer */
 #endif
    png_error_ptr error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    png_error_ptr warning_fn;
+#endif
    png_voidp error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    png_free_ptr free_fn;
@@ -1007,10 +967,6 @@
    png_free(png_ptr, png_ptr->paeth_row);
 #endif
 
-#ifdef PNG_TIME_RFC1123_SUPPORTED
-   png_free(png_ptr, png_ptr->time_buffer);
-#endif
-
 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
    /* Use this to save a little code space, it doesn't free the filter_costs */
    png_reset_filter_heuristics(png_ptr);
@@ -1024,7 +980,9 @@
 #endif
 
    error_fn = png_ptr->error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    warning_fn = png_ptr->warning_fn;
+#endif
    error_ptr = png_ptr->error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    free_fn = png_ptr->free_fn;
@@ -1033,7 +991,9 @@
    png_memset(png_ptr, 0, png_sizeof(png_struct));
 
    png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
    png_ptr->warning_fn = warning_fn;
+#endif
    png_ptr->error_ptr = error_ptr;
 #ifdef PNG_USER_MEM_SUPPORTED
    png_ptr->free_fn = free_fn;
diff --git a/pngwutil.c b/pngwutil.c
index 0c37e78..fb0fb1d 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -231,13 +231,11 @@
 
    if (compression >= PNG_TEXT_COMPRESSION_LAST)
    {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-      char msg[50];
-      png_snprintf(msg, 50, "Unknown compression type %d", compression);
-      png_warning(png_ptr, msg);
-#else
-      png_warning(png_ptr, "Unknown compression type");
-#endif
+      PNG_WARNING_PARAMETERS(p)
+
+      png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d,
+         compression);
+      png_formatted_warning(png_ptr, p, "Unknown compression type @1");
    }
 
    /* We can't write the chunk until we find out how much data we have,
@@ -1470,15 +1468,11 @@
       if ((png_byte)*ikp < 0x20 ||
          ((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1))
       {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
-         char msg[40];
+         PNG_WARNING_PARAMETERS(p)
 
-         png_snprintf(msg, 40,
-             "invalid keyword character 0x%02X", (png_byte)*ikp);
-         png_warning(png_ptr, msg);
-#else
-         png_warning(png_ptr, "invalid character in keyword");
-#endif
+         png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_02x,
+            *ikp);
+         png_formatted_warning(png_ptr, p, "invalid keyword character 0x@1");
          *dp = ' ';
       }
 
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index 71aab33..1202912 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -214,9 +214,9 @@
 option CONSOLE_IO requires STDIO
 
 # Note: prior to 1.5.0 this option could not be disabled if STDIO
-# was enabled.
+# was enabled.  Prior to 1.5.3 this option required STDIO
 
-option TIME_RFC1123 requires STDIO
+option TIME_RFC1123
 
 # PNG_SETJMP_NOT_SUPPORTED is an old equivalent for NO_SETJMP