1
/* strerror_r.c --- POSIX compatible system error routine
3
Copyright (C) 2010-2011 Free Software Foundation, Inc.
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 3 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
/* Written by Bruno Haible <bruno@clisp.org>, 2010. */
22
/* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD. */
23
#define _NETBSD_SOURCE 1
32
#include "strerror-override.h"
34
#if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
36
# define USE_XPG_STRERROR_R 1
38
#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
40
/* The system's strerror_r function is OK, except that its third argument
41
is 'int', not 'size_t', or its return type is wrong. */
45
# define USE_SYSTEM_STRERROR_R 1
47
#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
49
/* Use the system's strerror(). Exclude glibc and cygwin because the
50
system strerror_r has the wrong return type, and cygwin 1.7.9
51
strerror_r clobbers strerror. */
54
# define USE_SYSTEM_STRERROR 1
56
# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
58
/* No locking needed. */
60
/* Get catgets internationalization functions. */
62
# include <nl_types.h>
65
/* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
66
Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI). */
67
# if defined __hpux || defined __sgi
69
extern char *sys_errlist[];
72
/* Get sys_nerr on Solaris. */
73
# if defined __sun && !defined _LP64
79
# include "glthread/lock.h"
81
/* This lock protects the buffer returned by strerror(). We assume that
82
no other uses of strerror() exist in the program. */
83
gl_lock_define_initialized(static, strerror_lock)
89
/* Copy as much of MSG into BUF as possible, without corrupting errno.
90
Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */
92
safe_copy (char *buf, size_t buflen, const char *msg)
94
size_t len = strlen (msg);
99
/* Although POSIX allows memcpy() to corrupt errno, we don't
100
know of any implementation where this is a real problem. */
101
memcpy (buf, msg, len + 1);
106
memcpy (buf, msg, buflen - 1);
107
buf[buflen - 1] = '\0';
115
strerror_r (int errnum, char *buf, size_t buflen)
118
/* Filter this out now, so that rest of this replacement knows that
119
there is room for a non-empty message and trailing NUL. */
128
/* Check for gnulib overrides. */
130
char const *msg = strerror_override (errnum);
133
return safe_copy (buf, buflen, msg);
138
int saved_errno = errno;
140
#if USE_XPG_STRERROR_R
143
extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
145
ret = __xpg_strerror_r (errnum, buf, buflen);
150
/* glibc 2.13 would not touch buf on err, so we have to fall
151
back to GNU strerror_r which always returns a thread-safe
152
untruncated string to (partially) copy into our buf. */
153
safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
157
#elif USE_SYSTEM_STRERROR_R
159
if (buflen > INT_MAX)
163
/* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
164
also fails to change buf on EINVAL. */
168
if (buflen < sizeof stackbuf)
170
ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
172
ret = safe_copy (buf, buflen, stackbuf);
175
ret = strerror_r (errnum, buf, buflen);
178
ret = strerror_r (errnum, buf, buflen);
180
/* Some old implementations may return (-1, EINVAL) instead of EINVAL. */
186
/* AIX returns 0 rather than ERANGE when truncating strings; try
187
again until we are sure we got the entire string. */
188
if (!ret && strlen (buf) == buflen - 1)
190
char stackbuf[STACKBUF_LEN];
192
strerror_r (errnum, stackbuf, sizeof stackbuf);
193
len = strlen (stackbuf);
194
/* STACKBUF_LEN should have been large enough. */
195
if (len + 1 == sizeof stackbuf)
201
/* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7
202
truncates early on ERANGE rather than return a partial integer.
203
We prefer the maximal string. We set buf[0] earlier, and we
204
know of no implementation that modifies buf to be an
205
unterminated string, so this strlen should be portable in
206
practice (rather than pulling in a safer strnlen). */
207
if (ret == ERANGE && strlen (buf) < buflen - 1)
209
char stackbuf[STACKBUF_LEN];
211
/* STACKBUF_LEN should have been large enough. */
212
if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
214
safe_copy (buf, buflen, stackbuf);
218
#else /* USE_SYSTEM_STRERROR */
220
/* Try to do what strerror (errnum) does, but without clobbering the
221
buffer used by strerror(). */
223
# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Win32, Cygwin */
225
/* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
227
HP-UX: sys_nerr, sys_errlist are declared explicitly above.
228
native Win32: sys_nerr, sys_errlist are declared in <stdlib.h>.
229
Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */
230
if (errnum >= 0 && errnum < sys_nerr)
232
# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
233
# if defined __NetBSD__
234
nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
237
? catgets (catd, 1, errnum, sys_errlist[errnum])
238
: sys_errlist[errnum]);
241
nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
244
? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
245
: sys_errlist[errnum]);
248
const char *errmsg = sys_errlist[errnum];
250
if (errmsg == NULL || *errmsg == '\0')
253
ret = safe_copy (buf, buflen, errmsg);
254
# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
255
if (catd != (nl_catd)-1)
262
# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
264
/* For a valid error number, the system's strerror() function returns
265
a pointer to a not copied string, not to a buffer. */
266
if (errnum >= 0 && errnum < sys_nerr)
268
char *errmsg = strerror (errnum);
270
if (errmsg == NULL || *errmsg == '\0')
273
ret = safe_copy (buf, buflen, errmsg);
280
gl_lock_lock (strerror_lock);
283
char *errmsg = strerror (errnum);
285
/* For invalid error numbers, strerror() on
286
- IRIX 6.5 returns NULL,
287
- HP-UX 11 returns an empty string. */
288
if (errmsg == NULL || *errmsg == '\0')
291
ret = safe_copy (buf, buflen, errmsg);
294
gl_lock_unlock (strerror_lock);
300
if (ret == EINVAL && !*buf)
301
snprintf (buf, buflen, "Unknown error %d", errnum);