getugroups: Fix module description.
[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 HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) && !EXTEND_STRERROR_R
28
29 /* The system's strerror_r function is OK, except that its third argument
30    is 'int', not 'size_t', or its return type is wrong.  */
31
32 # include <limits.h>
33
34 int
35 strerror_r (int errnum, char *buf, size_t buflen)
36 # undef strerror_r
37 {
38   int ret;
39
40   if (buflen > INT_MAX)
41     buflen = INT_MAX;
42
43 # ifdef __hpux
44   /* On HP-UX 11.31, strerror_r always fails when buflen < 80.  */
45   {
46     char stackbuf[80];
47
48     if (buflen < sizeof (stackbuf))
49       {
50         ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
51         if (ret == 0)
52           {
53             size_t len = strlen (stackbuf);
54
55             if (len < buflen)
56               memcpy (buf, stackbuf, len + 1);
57             else
58               ret = ERANGE;
59           }
60       }
61     else
62       ret = strerror_r (errnum, buf, buflen);
63   }
64 # elif defined __CYGWIN__
65   /* Cygwin only provides the glibc interface, is thread-safe, and
66      always succeeds (although it may truncate). */
67   strerror_r (errnum, buf, buflen);
68   ret = 0;
69 # else
70   ret = strerror_r (errnum, buf, buflen);
71 # endif
72
73 # ifdef _AIX
74   /* On AIX 6.1, strerror_r returns -1 and sets errno to EINVAL
75      if buflen <= 1.  */
76   if (ret < 0 && errno == EINVAL && buflen <= 1)
77     {
78       /* Retry with a larger buffer.  */
79       char largerbuf[10];
80       ret = strerror_r (errnum, largerbuf, sizeof (largerbuf));
81       if (ret < 0 && errno == EINVAL)
82         {
83           /* errnum was out of range.  */
84           return EINVAL;
85         }
86       else
87         {
88           /* buf was too small.  */
89           return ERANGE;
90         }
91     }
92 # endif
93
94   /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
95   return (ret < 0 ? errno : ret);
96 }
97
98 #elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */ && !EXTEND_STRERROR_R
99
100 int
101 strerror_r (int errnum, char *buf, size_t buflen)
102 {
103   extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
104
105   int ret = __xpg_strerror_r (errnum, buf, buflen);
106   return (ret < 0 ? errno : ret);
107 }
108
109 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) || EXTEND_STRERROR_R */
110
111 # include "glthread/lock.h"
112
113 /* Use strerror(), with locking.  */
114
115 /* This lock protects the buffer returned by strerror().  We assume that
116    no other uses of strerror() exist in the program.  */
117 gl_lock_define_initialized(static, strerror_lock)
118
119 int
120 strerror_r (int errnum, char *buf, size_t buflen)
121 {
122   gl_lock_lock (strerror_lock);
123
124   {
125     char *errmsg = strerror (errnum);
126     size_t len = strlen (errmsg);
127     int ret;
128
129     if (len < buflen)
130       {
131         memcpy (buf, errmsg, len + 1);
132         ret = 0;
133       }
134     else
135       ret = ERANGE;
136
137     gl_lock_unlock (strerror_lock);
138
139     return ret;
140   }
141 }
142
143 #endif