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