strerror_r: avoid clobbering strerror on cygwin
[pspp] / lib / strerror_r.c
1 /* strerror_r.c --- POSIX compatible system error routine
2
3    Copyright (C) 2010-2011 Free Software Foundation, Inc.
4
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.
9
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.
14
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/>.  */
17
18 /* Written by Bruno Haible <bruno@clisp.org>, 2010.  */
19
20 #include <config.h>
21
22 /* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD.  */
23 #define _NETBSD_SOURCE 1
24
25 /* Specification.  */
26 #include <string.h>
27
28 #include <errno.h>
29
30 # if GNULIB_defined_ESOCK /* native Windows platforms */
31 #  if HAVE_WINSOCK2_H
32 #   include <winsock2.h>
33 #  endif
34 # endif
35
36
37 #if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
38
39 # define USE_XPG_STRERROR_R 1
40
41 #elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
42
43 /* The system's strerror_r function is OK, except that its third argument
44    is 'int', not 'size_t', or its return type is wrong.  */
45
46 # include <limits.h>
47
48 # define USE_SYSTEM_STRERROR_R 1
49
50 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
51
52 /* Use the system's strerror().  Exclude glibc and cygwin because the
53    system strerror_r has the wrong return type, and cygwin 1.7.9
54    strerror_r clobbers strerror.  */
55 # undef strerror
56
57 # define USE_SYSTEM_STRERROR 1
58
59 # if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
60
61 /* No locking needed.  */
62
63 /* Get catgets internationalization functions.  */
64 #  if HAVE_CATGETS
65 #   include <nl_types.h>
66 #  endif
67
68 /* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
69    Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI).  */
70 #  if defined __hpux || defined __sgi
71 extern int sys_nerr;
72 extern char *sys_errlist[];
73 #  endif
74
75 /* Get sys_nerr on Solaris.  */
76 #  if defined __sun && !defined _LP64
77 extern int sys_nerr;
78 #  endif
79
80 /* Get sys_nerr, sys_errlist on native Windows and Cygwin.  */
81 #  include <stdlib.h>
82
83 # else
84
85 #  include "glthread/lock.h"
86
87 /* This lock protects the buffer returned by strerror().  We assume that
88    no other uses of strerror() exist in the program.  */
89 gl_lock_define_initialized(static, strerror_lock)
90
91 # endif
92
93 #endif
94
95
96 int
97 strerror_r (int errnum, char *buf, size_t buflen)
98 #undef strerror_r
99 {
100   /* Filter this out now, so that rest of this replacement knows that
101      there is room for a non-empty message and trailing NUL.  */
102   if (buflen <= 1)
103     {
104       if (buflen)
105         *buf = 0;
106       return ERANGE;
107     }
108
109 #if GNULIB_defined_ETXTBSY \
110     || GNULIB_defined_ESOCK \
111     || GNULIB_defined_ENOMSG \
112     || GNULIB_defined_EIDRM \
113     || GNULIB_defined_ENOLINK \
114     || GNULIB_defined_EPROTO \
115     || GNULIB_defined_EMULTIHOP \
116     || GNULIB_defined_EBADMSG \
117     || GNULIB_defined_EOVERFLOW \
118     || GNULIB_defined_ENOTSUP \
119     || GNULIB_defined_ESTALE \
120     || GNULIB_defined_EDQUOT \
121     || GNULIB_defined_ECANCELED
122   {
123     char const *msg = NULL;
124     /* These error messages are taken from glibc/sysdeps/gnu/errlist.c.  */
125     switch (errnum)
126       {
127 # if GNULIB_defined_ETXTBSY
128       case ETXTBSY:
129         msg = "Text file busy";
130         break;
131 # endif
132
133 # if GNULIB_defined_ESOCK /* native Windows platforms */
134       /* EWOULDBLOCK is the same as EAGAIN.  */
135       case EINPROGRESS:
136         msg = "Operation now in progress";
137         break;
138       case EALREADY:
139         msg = "Operation already in progress";
140         break;
141       case ENOTSOCK:
142         msg = "Socket operation on non-socket";
143         break;
144       case EDESTADDRREQ:
145         msg = "Destination address required";
146         break;
147       case EMSGSIZE:
148         msg = "Message too long";
149         break;
150       case EPROTOTYPE:
151         msg = "Protocol wrong type for socket";
152         break;
153       case ENOPROTOOPT:
154         msg = "Protocol not available";
155         break;
156       case EPROTONOSUPPORT:
157         msg = "Protocol not supported";
158         break;
159       case ESOCKTNOSUPPORT:
160         msg = "Socket type not supported";
161         break;
162       case EOPNOTSUPP:
163         msg = "Operation not supported";
164         break;
165       case EPFNOSUPPORT:
166         msg = "Protocol family not supported";
167         break;
168       case EAFNOSUPPORT:
169         msg = "Address family not supported by protocol";
170         break;
171       case EADDRINUSE:
172         msg = "Address already in use";
173         break;
174       case EADDRNOTAVAIL:
175         msg = "Cannot assign requested address";
176         break;
177       case ENETDOWN:
178         msg = "Network is down";
179         break;
180       case ENETUNREACH:
181         msg = "Network is unreachable";
182         break;
183       case ENETRESET:
184         msg = "Network dropped connection on reset";
185         break;
186       case ECONNABORTED:
187         msg = "Software caused connection abort";
188         break;
189       case ECONNRESET:
190         msg = "Connection reset by peer";
191         break;
192       case ENOBUFS:
193         msg = "No buffer space available";
194         break;
195       case EISCONN:
196         msg = "Transport endpoint is already connected";
197         break;
198       case ENOTCONN:
199         msg = "Transport endpoint is not connected";
200         break;
201       case ESHUTDOWN:
202         msg = "Cannot send after transport endpoint shutdown";
203         break;
204       case ETOOMANYREFS:
205         msg = "Too many references: cannot splice";
206         break;
207       case ETIMEDOUT:
208         msg = "Connection timed out";
209         break;
210       case ECONNREFUSED:
211         msg = "Connection refused";
212         break;
213       case ELOOP:
214         msg = "Too many levels of symbolic links";
215         break;
216       case EHOSTDOWN:
217         msg = "Host is down";
218         break;
219       case EHOSTUNREACH:
220         msg = "No route to host";
221         break;
222       case EPROCLIM:
223         msg = "Too many processes";
224         break;
225       case EUSERS:
226         msg = "Too many users";
227         break;
228       case EDQUOT:
229         msg = "Disk quota exceeded";
230         break;
231       case ESTALE:
232         msg = "Stale NFS file handle";
233         break;
234       case EREMOTE:
235         msg = "Object is remote";
236         break;
237 #  if HAVE_WINSOCK2_H
238       /* WSA_INVALID_HANDLE maps to EBADF */
239       /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */
240       /* WSA_INVALID_PARAMETER maps to EINVAL */
241       case WSA_OPERATION_ABORTED:
242         msg = "Overlapped operation aborted";
243         break;
244       case WSA_IO_INCOMPLETE:
245         msg = "Overlapped I/O event object not in signaled state";
246         break;
247       case WSA_IO_PENDING:
248         msg = "Overlapped operations will complete later";
249         break;
250       /* WSAEINTR maps to EINTR */
251       /* WSAEBADF maps to EBADF */
252       /* WSAEACCES maps to EACCES */
253       /* WSAEFAULT maps to EFAULT */
254       /* WSAEINVAL maps to EINVAL */
255       /* WSAEMFILE maps to EMFILE */
256       /* WSAEWOULDBLOCK maps to EWOULDBLOCK */
257       /* WSAEINPROGRESS is EINPROGRESS */
258       /* WSAEALREADY is EALREADY */
259       /* WSAENOTSOCK is ENOTSOCK */
260       /* WSAEDESTADDRREQ is EDESTADDRREQ */
261       /* WSAEMSGSIZE is EMSGSIZE */
262       /* WSAEPROTOTYPE is EPROTOTYPE */
263       /* WSAENOPROTOOPT is ENOPROTOOPT */
264       /* WSAEPROTONOSUPPORT is EPROTONOSUPPORT */
265       /* WSAESOCKTNOSUPPORT is ESOCKTNOSUPPORT */
266       /* WSAEOPNOTSUPP is EOPNOTSUPP */
267       /* WSAEPFNOSUPPORT is EPFNOSUPPORT */
268       /* WSAEAFNOSUPPORT is EAFNOSUPPORT */
269       /* WSAEADDRINUSE is EADDRINUSE */
270       /* WSAEADDRNOTAVAIL is EADDRNOTAVAIL */
271       /* WSAENETDOWN is ENETDOWN */
272       /* WSAENETUNREACH is ENETUNREACH */
273       /* WSAENETRESET is ENETRESET */
274       /* WSAECONNABORTED is ECONNABORTED */
275       /* WSAECONNRESET is ECONNRESET */
276       /* WSAENOBUFS is ENOBUFS */
277       /* WSAEISCONN is EISCONN */
278       /* WSAENOTCONN is ENOTCONN */
279       /* WSAESHUTDOWN is ESHUTDOWN */
280       /* WSAETOOMANYREFS is ETOOMANYREFS */
281       /* WSAETIMEDOUT is ETIMEDOUT */
282       /* WSAECONNREFUSED is ECONNREFUSED */
283       /* WSAELOOP is ELOOP */
284       /* WSAENAMETOOLONG maps to ENAMETOOLONG */
285       /* WSAEHOSTDOWN is EHOSTDOWN */
286       /* WSAEHOSTUNREACH is EHOSTUNREACH */
287       /* WSAENOTEMPTY maps to ENOTEMPTY */
288       /* WSAEPROCLIM is EPROCLIM */
289       /* WSAEUSERS is EUSERS */
290       /* WSAEDQUOT is EDQUOT */
291       /* WSAESTALE is ESTALE */
292       /* WSAEREMOTE is EREMOTE */
293       case WSASYSNOTREADY:
294         msg = "Network subsystem is unavailable";
295         break;
296       case WSAVERNOTSUPPORTED:
297         msg = "Winsock.dll version out of range";
298         break;
299       case WSANOTINITIALISED:
300         msg = "Successful WSAStartup not yet performed";
301         break;
302       case WSAEDISCON:
303         msg = "Graceful shutdown in progress";
304         break;
305       case WSAENOMORE: case WSA_E_NO_MORE:
306         msg = "No more results";
307         break;
308       case WSAECANCELLED: case WSA_E_CANCELLED:
309         msg = "Call was canceled";
310         break;
311       case WSAEINVALIDPROCTABLE:
312         msg = "Procedure call table is invalid";
313         break;
314       case WSAEINVALIDPROVIDER:
315         msg = "Service provider is invalid";
316         break;
317       case WSAEPROVIDERFAILEDINIT:
318         msg = "Service provider failed to initialize";
319         break;
320       case WSASYSCALLFAILURE:
321         msg = "System call failure";
322         break;
323       case WSASERVICE_NOT_FOUND:
324         msg = "Service not found";
325         break;
326       case WSATYPE_NOT_FOUND:
327         msg = "Class type not found";
328         break;
329       case WSAEREFUSED:
330         msg = "Database query was refused";
331         break;
332       case WSAHOST_NOT_FOUND:
333         msg = "Host not found";
334         break;
335       case WSATRY_AGAIN:
336         msg = "Nonauthoritative host not found";
337         break;
338       case WSANO_RECOVERY:
339         msg = "Nonrecoverable error";
340         break;
341       case WSANO_DATA:
342         msg = "Valid name, no data record of requested type";
343         break;
344       /* WSA_QOS_* omitted */
345 #  endif
346 # endif
347
348 # if GNULIB_defined_ENOMSG
349       case ENOMSG:
350         msg = "No message of desired type";
351         break;
352 # endif
353
354 # if GNULIB_defined_EIDRM
355       case EIDRM:
356         msg = "Identifier removed";
357         break;
358 # endif
359
360 # if GNULIB_defined_ENOLINK
361       case ENOLINK:
362         msg = "Link has been severed";
363         break;
364 # endif
365
366 # if GNULIB_defined_EPROTO
367       case EPROTO:
368         msg = "Protocol error";
369         break;
370 # endif
371
372 # if GNULIB_defined_EMULTIHOP
373       case EMULTIHOP:
374         msg = "Multihop attempted";
375         break;
376 # endif
377
378 # if GNULIB_defined_EBADMSG
379       case EBADMSG:
380         msg = "Bad message";
381         break;
382 # endif
383
384 # if GNULIB_defined_EOVERFLOW
385       case EOVERFLOW:
386         msg = "Value too large for defined data type";
387         break;
388 # endif
389
390 # if GNULIB_defined_ENOTSUP
391       case ENOTSUP:
392         msg = "Not supported";
393         break;
394 # endif
395
396 # if GNULIB_defined_ESTALE
397       case ESTALE:
398         msg = "Stale NFS file handle";
399         break;
400 # endif
401
402 # if GNULIB_defined_EDQUOT
403       case EDQUOT:
404         msg = "Disk quota exceeded";
405         break;
406 # endif
407
408 # if GNULIB_defined_ECANCELED
409       case ECANCELED:
410         msg = "Operation canceled";
411         break;
412 # endif
413       }
414
415     if (msg)
416       {
417         int saved_errno = errno;
418         size_t len = strlen (msg);
419         int ret = ERANGE;
420
421         if (len < buflen)
422           {
423             memcpy (buf, msg, len + 1);
424             ret = 0;
425           }
426         errno = saved_errno;
427         return ret;
428       }
429   }
430 #endif
431
432   {
433     int ret;
434     int saved_errno = errno;
435
436 #if USE_XPG_STRERROR_R
437
438     {
439       extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
440
441       ret = __xpg_strerror_r (errnum, buf, buflen);
442       if (ret < 0)
443         ret = errno;
444     }
445
446 #elif USE_SYSTEM_STRERROR_R
447
448     if (buflen > INT_MAX)
449       buflen = INT_MAX;
450
451 # ifdef __hpux
452     /* On HP-UX 11.31, strerror_r always fails when buflen < 80.  */
453     {
454       char stackbuf[80];
455
456       if (buflen < sizeof (stackbuf))
457         {
458           ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
459           if (ret == 0)
460             {
461               size_t len = strlen (stackbuf);
462
463               if (len < buflen)
464                 memcpy (buf, stackbuf, len + 1);
465               else
466                 ret = ERANGE;
467             }
468         }
469       else
470         ret = strerror_r (errnum, buf, buflen);
471     }
472 # else
473     ret = strerror_r (errnum, buf, buflen);
474 # endif
475
476     /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
477     if (ret < 0)
478       ret = errno;
479
480     /* FreeBSD rejects 0; see http://austingroupbugs.net/view.php?id=382.  */
481     if (errnum == 0 && ret == EINVAL)
482       {
483         if (buflen <= strlen ("Success"))
484           {
485             ret = ERANGE;
486             if (buflen)
487               buf[0] = 0;
488           }
489         else
490           {
491             ret = 0;
492             strcpy (buf, "Success");
493           }
494       }
495
496 #else /* USE_SYSTEM_STRERROR */
497
498     /* Try to do what strerror (errnum) does, but without clobbering the
499        buffer used by strerror().  */
500
501 # if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Win32, Cygwin */
502
503     /* NetBSD:        sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
504                       and <errno.h> above.
505        HP-UX:         sys_nerr, sys_errlist are declared explicitly above.
506        native Win32:  sys_nerr, sys_errlist are declared in <stdlib.h>.
507        Cygwin:        sys_nerr, sys_errlist are declared in <stdlib.h>.  */
508     if (errnum >= 0 && errnum < sys_nerr)
509       {
510 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
511 #   if defined __NetBSD__
512         nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
513         const char *errmsg =
514           (catd != (nl_catd)-1
515            ? catgets (catd, 1, errnum, sys_errlist[errnum])
516            : sys_errlist[errnum]);
517 #   endif
518 #   if defined __hpux
519         nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
520         const char *errmsg =
521           (catd != (nl_catd)-1
522            ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
523            : sys_errlist[errnum]);
524 #   endif
525 #  else
526         const char *errmsg = sys_errlist[errnum];
527 #  endif
528         if (errmsg == NULL || *errmsg == '\0')
529           ret = EINVAL;
530         else
531           {
532             size_t len = strlen (errmsg);
533
534             if (len < buflen)
535               {
536                 memcpy (buf, errmsg, len + 1);
537                 ret = 0;
538               }
539             else
540               ret = ERANGE;
541           }
542 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
543         if (catd != (nl_catd)-1)
544           catclose (catd);
545 #  endif
546       }
547     else
548       ret = EINVAL;
549
550 # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
551
552     /* For a valid error number, the system's strerror() function returns
553        a pointer to a not copied string, not to a buffer.  */
554     if (errnum >= 0 && errnum < sys_nerr)
555       {
556         char *errmsg = strerror (errnum);
557
558         if (errmsg == NULL || *errmsg == '\0')
559           ret = EINVAL;
560         else
561           {
562             size_t len = strlen (errmsg);
563
564             if (len < buflen)
565               {
566                 memcpy (buf, errmsg, len + 1);
567                 ret = 0;
568               }
569             else
570               ret = ERANGE;
571           }
572       }
573     else
574       ret = EINVAL;
575
576 # else
577
578     gl_lock_lock (strerror_lock);
579
580     {
581       char *errmsg = strerror (errnum);
582
583       /* For invalid error numbers, strerror() on
584            - IRIX 6.5 returns NULL,
585            - HP-UX 11 returns an empty string.  */
586       if (errmsg == NULL || *errmsg == '\0')
587         ret = EINVAL;
588       else
589         {
590           size_t len = strlen (errmsg);
591
592           if (len < buflen)
593             {
594               memcpy (buf, errmsg, len + 1);
595               ret = 0;
596             }
597           else
598             ret = ERANGE;
599         }
600     }
601
602     gl_lock_unlock (strerror_lock);
603
604 # endif
605
606 #endif
607
608     errno = saved_errno;
609     return ret;
610   }
611 }