diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 798a823..df7e01f 100644
*** a/src/backend/lib/stringinfo.c
--- b/src/backend/lib/stringinfo.c
*************** resetStringInfo(StringInfo str)
*** 77,88 ****
--- 77,91 ----
  void
  appendStringInfo(StringInfo str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
+ 
  	for (;;)
  	{
  		va_list		args;
  		int			needed;
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		needed = appendStringInfoVA(str, fmt, args);
  		va_end(args);
*************** appendStringInfo(StringInfo str, const c
*** 105,110 ****
--- 108,116 ----
   * pass the return value to enlargeStringInfo() before trying again; see
   * appendStringInfo for standard usage pattern.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * XXX This API is ugly, but there seems no alternative given the C spec's
   * restrictions on what can portably be done with va_list arguments: you have
   * to redo va_start before you can rescan the argument list, and we can't do
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 7d1d439..ca73a55 100644
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** archputs(const char *s, Archive *AH)
*** 1507,1512 ****
--- 1507,1513 ----
  int
  archprintf(Archive *AH, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** archprintf(Archive *AH, const char *fmt,
*** 1519,1524 ****
--- 1520,1526 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
*************** RestoreOutput(ArchiveHandle *AH, OutputC
*** 1640,1645 ****
--- 1642,1648 ----
  int
  ahprintf(ArchiveHandle *AH, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** ahprintf(ArchiveHandle *AH, const char *
*** 1652,1657 ****
--- 1655,1661 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 007be12..407a56d 100644
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
*************** _EndBlobs(ArchiveHandle *AH, TocEntry *t
*** 1026,1031 ****
--- 1026,1032 ----
  static int
  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** tarPrintf(ArchiveHandle *AH, TAR_MEMBER 
*** 1038,1043 ****
--- 1039,1045 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 04465a1..2cf100f 100644
*** a/src/common/psprintf.c
--- b/src/common/psprintf.c
***************
*** 45,50 ****
--- 45,51 ----
  char *
  psprintf(const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	size_t		len = 128;		/* initial assumption about buffer size */
  
  	for (;;)
*************** psprintf(const char *fmt,...)
*** 60,65 ****
--- 61,67 ----
  		result = (char *) palloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		newlen = pvsnprintf(result, len, fmt, args);
  		va_end(args);
*************** psprintf(const char *fmt,...)
*** 89,94 ****
--- 91,99 ----
   * Other error cases do not return, but exit via elog(ERROR) or exit().
   * Hence, this shouldn't be used inside libpq.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * Note that the semantics of the return value are not exactly C99's.
   * First, we don't promise that the estimated buffer size is exactly right;
   * callers must be prepared to loop multiple times to get the right size.
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 0814ec6..43c36c3 100644
*** a/src/interfaces/libpq/pqexpbuffer.c
--- b/src/interfaces/libpq/pqexpbuffer.c
*************** enlargePQExpBuffer(PQExpBuffer str, size
*** 233,238 ****
--- 233,239 ----
  void
  printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	va_list		args;
  	bool		done;
  
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 244,249 ****
--- 245,251 ----
  	/* Loop in case we have to retry after enlarging the buffer. */
  	do
  	{
+ 		errno = save_errno;
  		va_start(args, fmt);
  		done = appendPQExpBufferVA(str, fmt, args);
  		va_end(args);
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 261,266 ****
--- 263,269 ----
  void
  appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	va_list		args;
  	bool		done;
  
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 270,275 ****
--- 273,279 ----
  	/* Loop in case we have to retry after enlarging the buffer. */
  	do
  	{
+ 		errno = save_errno;
  		va_start(args, fmt);
  		done = appendPQExpBufferVA(str, fmt, args);
  		va_end(args);
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 281,286 ****
--- 285,293 ----
   * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
   * Attempt to format data and append it to str.  Returns true if done
   * (either successful or hard failure), false if need to retry.
+  *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
   */
  static bool
  appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index e244104..3814a6c 100644
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** static bool set_string_attr(PyObject *ob
*** 46,51 ****
--- 46,52 ----
  void
  PLy_elog_impl(int elevel, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *xmsg;
  	char	   *tbmsg;
  	int			tb_depth;
*************** PLy_elog_impl(int elevel, const char *fm
*** 96,101 ****
--- 97,103 ----
  			va_list		ap;
  			int			needed;
  
+ 			errno = save_errno;
  			va_start(ap, fmt);
  			needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
  			va_end(ap);
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 851e2ae..63cec5d 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 64,69 ****
--- 64,77 ----
   *
   * 5. Space and '#' flags are not implemented.
   *
+  * In addition, we support some extensions over C99:
+  *
+  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
+  *
+  * 2. "%m" expands to the value of strerror(errno), where errno is the
+  * value that variable had at the start of the call.  This is a glibc
+  * extension, but a very useful one.
+  *
   *
   * Historically the result values of sprintf/snprintf varied across platforms.
   * This implementation now follows the C99 standard:
*************** static void flushbuffer(PrintfTarget *ta
*** 155,160 ****
--- 163,175 ----
  static void dopr(PrintfTarget *target, const char *format, va_list args);
  
  
+ /*
+  * Externally visible entry points.
+  *
+  * All of these are just wrappers around dopr().  Note it's essential that
+  * they not change the value of "errno" before reaching dopr().
+  */
+ 
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** static void trailing_pad(int *padlen, Pr
*** 315,325 ****
  
  
  /*
!  * dopr(): poor man's version of doprintf
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
  	const char *format_start = format;
  	int			ch;
  	bool		have_dollar;
--- 330,341 ----
  
  
  /*
!  * dopr(): the guts of *printf for all cases.
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
+ 	int			save_errno = errno;
  	const char *format_start = format;
  	int			ch;
  	bool		have_dollar;
*************** nextch1:
*** 497,502 ****
--- 513,519 ----
  				else
  					have_non_dollar = true;
  				break;
+ 			case 'm':
  			case '%':
  				break;
  		}
*************** nextch2:
*** 802,807 ****
--- 819,831 ----
  						 precision, pointflag,
  						 target);
  				break;
+ 			case 'm':
+ 				{
+ 					const char *errm = strerror(save_errno);
+ 
+ 					dostr(errm, strlen(errm), target);
+ 				}
+ 				break;
  			case '%':
  				dopr_outch('%', target);
  				break;
