1 /* strerror_r.c --- POSIX compatible system error routine
2 
3    Copyright (C) 2010-2012 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 #include <stdio.h>
30 #include <stdlib.h>
31 
32 #include "strerror-override.h"
33 
34 #if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
35 
36 # define USE_XPG_STRERROR_R 1
37 extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
38 
39 #elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
40 
41 /* The system's strerror_r function is OK, except that its third argument
42    is 'int', not 'size_t', or its return type is wrong.  */
43 
44 # include <limits.h>
45 
46 # define USE_SYSTEM_STRERROR_R 1
47 
48 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
49 
50 /* Use the system's strerror().  Exclude glibc and cygwin because the
51    system strerror_r has the wrong return type, and cygwin 1.7.9
52    strerror_r clobbers strerror.  */
53 # undef strerror
54 
55 # define USE_SYSTEM_STRERROR 1
56 
57 # if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
58 
59 /* No locking needed.  */
60 
61 /* Get catgets internationalization functions.  */
62 #  if HAVE_CATGETS
63 #   include <nl_types.h>
64 #  endif
65 
66 /* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
67    Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI).  */
68 #  if defined __hpux || defined __sgi
69 extern int sys_nerr;
70 extern char *sys_errlist[];
71 #  endif
72 
73 /* Get sys_nerr on Solaris.  */
74 #  if defined __sun && !defined _LP64
75 extern int sys_nerr;
76 #  endif
77 
78 # else
79 
80 #  include "glthread/lock.h"
81 
82 /* This lock protects the buffer returned by strerror().  We assume that
83    no other uses of strerror() exist in the program.  */
gl_lock_define_initialized(static,strerror_lock)84 gl_lock_define_initialized(static, strerror_lock)
85 
86 # endif
87 
88 #endif
89 
90 /* On MSVC, there is no snprintf() function, just a _snprintf().
91    It is of lower quality, but sufficient for the simple use here.
92    We only have to make sure to NUL terminate the result (_snprintf
93    does not NUL terminate, like strncpy).  */
94 #if !HAVE_SNPRINTF
95 static int
96 local_snprintf (char *buf, size_t buflen, const char *format, ...)
97 {
98   va_list args;
99   int result;
100 
101   va_start (args, format);
102   result = _vsnprintf (buf, buflen, format, args);
103   va_end (args);
104   if (buflen > 0 && (result < 0 || result >= buflen))
105     buf[buflen - 1] = '\0';
106   return result;
107 }
108 # define snprintf local_snprintf
109 #endif
110 
111 /* Copy as much of MSG into BUF as possible, without corrupting errno.
112    Return 0 if MSG fit in BUFLEN, otherwise return ERANGE.  */
113 static int
safe_copy(char * buf,size_t buflen,const char * msg)114 safe_copy (char *buf, size_t buflen, const char *msg)
115 {
116   size_t len = strlen (msg);
117   int ret;
118 
119   if (len < buflen)
120     {
121       /* Although POSIX allows memcpy() to corrupt errno, we don't
122          know of any implementation where this is a real problem.  */
123       memcpy (buf, msg, len + 1);
124       ret = 0;
125     }
126   else
127     {
128       memcpy (buf, msg, buflen - 1);
129       buf[buflen - 1] = '\0';
130       ret = ERANGE;
131     }
132   return ret;
133 }
134 
135 
136 int
strerror_r(int errnum,char * buf,size_t buflen)137 strerror_r (int errnum, char *buf, size_t buflen)
138 #undef strerror_r
139 {
140   /* Filter this out now, so that rest of this replacement knows that
141      there is room for a non-empty message and trailing NUL.  */
142   if (buflen <= 1)
143     {
144       if (buflen)
145         *buf = '\0';
146       return ERANGE;
147     }
148   *buf = '\0';
149 
150   /* Check for gnulib overrides.  */
151   {
152     char const *msg = strerror_override (errnum);
153 
154     if (msg)
155       return safe_copy (buf, buflen, msg);
156   }
157 
158   {
159     int ret;
160     int saved_errno = errno;
161 
162 #if USE_XPG_STRERROR_R
163 
164     {
165       ret = __xpg_strerror_r (errnum, buf, buflen);
166       if (ret < 0)
167         ret = errno;
168       if (!*buf)
169         {
170           /* glibc 2.13 would not touch buf on err, so we have to fall
171              back to GNU strerror_r which always returns a thread-safe
172              untruncated string to (partially) copy into our buf.  */
173           safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
174         }
175     }
176 
177 #elif USE_SYSTEM_STRERROR_R
178 
179     if (buflen > INT_MAX)
180       buflen = INT_MAX;
181 
182 # ifdef __hpux
183     /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
184        also fails to change buf on EINVAL.  */
185     {
186       char stackbuf[80];
187 
188       if (buflen < sizeof stackbuf)
189         {
190           ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
191           if (ret == 0)
192             ret = safe_copy (buf, buflen, stackbuf);
193         }
194       else
195         ret = strerror_r (errnum, buf, buflen);
196     }
197 # else
198     ret = strerror_r (errnum, buf, buflen);
199 
200     /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
201     if (ret < 0)
202       ret = errno;
203 # endif
204 
205 # ifdef _AIX
206     /* AIX returns 0 rather than ERANGE when truncating strings; try
207        again until we are sure we got the entire string.  */
208     if (!ret && strlen (buf) == buflen - 1)
209       {
210         char stackbuf[STACKBUF_LEN];
211         size_t len;
212         strerror_r (errnum, stackbuf, sizeof stackbuf);
213         len = strlen (stackbuf);
214         /* STACKBUF_LEN should have been large enough.  */
215         if (len + 1 == sizeof stackbuf)
216           abort ();
217         if (buflen <= len)
218           ret = ERANGE;
219       }
220 # else
221     /* Solaris 10 does not populate buf on ERANGE.  OpenBSD 4.7
222        truncates early on ERANGE rather than return a partial integer.
223        We prefer the maximal string.  We set buf[0] earlier, and we
224        know of no implementation that modifies buf to be an
225        unterminated string, so this strlen should be portable in
226        practice (rather than pulling in a safer strnlen).  */
227     if (ret == ERANGE && strlen (buf) < buflen - 1)
228       {
229         char stackbuf[STACKBUF_LEN];
230 
231         /* STACKBUF_LEN should have been large enough.  */
232         if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
233           abort ();
234         safe_copy (buf, buflen, stackbuf);
235       }
236 # endif
237 
238 #else /* USE_SYSTEM_STRERROR */
239 
240     /* Try to do what strerror (errnum) does, but without clobbering the
241        buffer used by strerror().  */
242 
243 # if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */
244 
245     /* NetBSD:         sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
246                        and <errno.h> above.
247        HP-UX:          sys_nerr, sys_errlist are declared explicitly above.
248        native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
249        Cygwin:         sys_nerr, sys_errlist are declared in <errno.h>.  */
250     if (errnum >= 0 && errnum < sys_nerr)
251       {
252 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
253 #   if defined __NetBSD__
254         nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
255         const char *errmsg =
256           (catd != (nl_catd)-1
257            ? catgets (catd, 1, errnum, sys_errlist[errnum])
258            : sys_errlist[errnum]);
259 #   endif
260 #   if defined __hpux
261         nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
262         const char *errmsg =
263           (catd != (nl_catd)-1
264            ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
265            : sys_errlist[errnum]);
266 #   endif
267 #  else
268         const char *errmsg = sys_errlist[errnum];
269 #  endif
270         if (errmsg == NULL || *errmsg == '\0')
271           ret = EINVAL;
272         else
273           ret = safe_copy (buf, buflen, errmsg);
274 #  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
275         if (catd != (nl_catd)-1)
276           catclose (catd);
277 #  endif
278       }
279     else
280       ret = EINVAL;
281 
282 # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
283 
284     /* For a valid error number, the system's strerror() function returns
285        a pointer to a not copied string, not to a buffer.  */
286     if (errnum >= 0 && errnum < sys_nerr)
287       {
288         char *errmsg = strerror (errnum);
289 
290         if (errmsg == NULL || *errmsg == '\0')
291           ret = EINVAL;
292         else
293           ret = safe_copy (buf, buflen, errmsg);
294       }
295     else
296       ret = EINVAL;
297 
298 # else
299 
300     gl_lock_lock (strerror_lock);
301 
302     {
303       char *errmsg = strerror (errnum);
304 
305       /* For invalid error numbers, strerror() on
306            - IRIX 6.5 returns NULL,
307            - HP-UX 11 returns an empty string.  */
308       if (errmsg == NULL || *errmsg == '\0')
309         ret = EINVAL;
310       else
311         ret = safe_copy (buf, buflen, errmsg);
312     }
313 
314     gl_lock_unlock (strerror_lock);
315 
316 # endif
317 
318 #endif
319 
320     if (ret == EINVAL && !*buf)
321       snprintf (buf, buflen, "Unknown error %d", errnum);
322 
323     errno = saved_errno;
324     return ret;
325   }
326 }
327