changeset 14765:8e23facf1d9e

strerror_r: Avoid clobbering the strerror buffer when possible. * lib/strerror.c: Define _NETBSD_SOURCE. Include <nl_types.h>. (sys_nerr, sys_errlist): New declarations. (strerror_r): Be careful not to clobber the strerror buffer on NetBSD, HP-UX, native Win32, IRIX, and 32-bit Solaris. * m4/strerror_r.m4 (gl_PREREQ_STRERROR_R): Test whether catgets exists.
author Bruno Haible <bruno@clisp.org>
date Thu, 19 May 2011 20:57:21 +0200
parents 2b72df632b91
children 3618a75b519d
files ChangeLog lib/strerror_r.c m4/strerror_r.m4
diffstat 3 files changed, 128 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2011-05-19  Bruno Haible  <bruno@clisp.org>
+
+	strerror_r: Avoid clobbering the strerror buffer when possible.
+	* lib/strerror.c: Define _NETBSD_SOURCE. Include <nl_types.h>.
+	(sys_nerr, sys_errlist): New declarations.
+	(strerror_r): Be careful not to clobber the strerror buffer on NetBSD,
+	HP-UX, native Win32, IRIX, and 32-bit Solaris.
+	* m4/strerror_r.m4 (gl_PREREQ_STRERROR_R): Test whether catgets exists.
+
 2011-05-19  Bruno Haible  <bruno@clisp.org>
 
 	strerror_r: Fix test failure on mingw.
--- a/lib/strerror_r.c
+++ b/lib/strerror_r.c
@@ -19,6 +19,9 @@
 
 #include <config.h>
 
+/* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD.  */
+#define _NETBSD_SOURCE 1
+
 /* Specification.  */
 #include <string.h>
 
@@ -46,17 +49,45 @@
 
 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
 
-# include "glthread/lock.h"
-
-/* Use strerror(), with locking.  */
+/* Use the system's strerror().  */
 # undef strerror
 
 # define USE_SYSTEM_STRERROR 1
 
+# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64)
+
+/* No locking needed.  */
+
+/* Get catgets internationalization functions.  */
+#  if HAVE_CATGETS
+#   include <nl_types.h>
+#  endif
+
+/* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
+   Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI).  */
+#  if defined __hpux || defined __sgi
+extern int sys_nerr;
+extern char *sys_errlist[];
+#  endif
+
+/* Get sys_nerr on Solaris.  */
+#  if defined __sun && !defined _LP64
+extern int sys_nerr;
+#  endif
+
+/* Get sys_nerr, sys_errlist on native Windows.  */
+#  include <stdlib.h>
+
+# else
+
+#  include "glthread/lock.h"
+
 /* This lock protects the buffer returned by strerror().  We assume that
    no other uses of strerror() exist in the program.  */
 gl_lock_define_initialized(static, strerror_lock)
 
+# endif
+
 #endif
 
 
@@ -476,6 +507,87 @@
 
 #else /* USE_SYSTEM_STRERROR */
 
+    /* Try to do what strerror (errnum) does, but without clobbering the
+       buffer used by strerror().  */
+
+# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) /* NetBSD, HP-UX, native Win32 */
+
+    /* NetBSD:        sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
+                      and <errno.h> above.
+       HP-UX:         sys_nerr, sys_errlist are declared explicitly above.
+       native Win32:  sys_nerr, sys_errlist are declared in <stdlib.h>.  */
+    if (errnum >= 0 && errnum < sys_nerr)
+      {
+#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
+        int saved_errno = errno;
+#   if defined __NetBSD__
+        nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
+        const char *errmsg =
+          (catd != (nl_catd)-1
+           ? catgets (catd, 1, errnum, sys_errlist[errnum])
+           : sys_errlist[errnum]);
+#   endif
+#   if defined __hpux
+        nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
+        const char *errmsg =
+          (catd != (nl_catd)-1
+           ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
+           : sys_errlist[errnum]);
+#   endif
+#  else
+        const char *errmsg = sys_errlist[errnum];
+#  endif
+        if (errmsg == NULL || *errmsg == '\0')
+          ret = EINVAL;
+        else
+          {
+            size_t len = strlen (errmsg);
+
+            if (len < buflen)
+              {
+                memcpy (buf, errmsg, len + 1);
+                ret = 0;
+              }
+            else
+              ret = ERANGE;
+          }
+#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
+        if (catd != (nl_catd)-1)
+          catclose (catd);
+        errno = saved_errno;
+#  endif
+      }
+    else
+      ret = EINVAL;
+
+# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
+
+    /* For a valid error number, the system's strerror() function returns
+       a pointer to a not copied string, not to a buffer.  */
+    if (errnum >= 0 && errnum < sys_nerr)
+      {
+        char *errmsg = strerror (errnum);
+
+        if (errmsg == NULL || *errmsg == '\0')
+          ret = EINVAL;
+        else
+          {
+            size_t len = strlen (errmsg);
+
+            if (len < buflen)
+              {
+                memcpy (buf, errmsg, len + 1);
+                ret = 0;
+              }
+            else
+              ret = ERANGE;
+          }
+      }
+    else
+      ret = EINVAL;
+
+# else
+
     gl_lock_lock (strerror_lock);
 
     {
@@ -502,6 +614,8 @@
 
     gl_lock_unlock (strerror_lock);
 
+# endif
+
 #endif
 
     return ret;
--- a/m4/strerror_r.m4
+++ b/m4/strerror_r.m4
@@ -1,4 +1,4 @@
-# strerror_r.m4 serial 5
+# strerror_r.m4 serial 6
 dnl Copyright (C) 2002, 2007-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -103,5 +103,6 @@
 
 # Prerequisites of lib/strerror_r.c.
 AC_DEFUN([gl_PREREQ_STRERROR_R], [
+  AC_CHECK_FUNCS_ONCE([catgets])
   :
 ])