summaryrefslogtreecommitdiff
path: root/lib/strerror_r.c
blob: 1fa52d99da600dbf43e19cd46c23f9230296ef4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* strerror_r.c --- POSIX compatible system error routine

   Copyright (C) 2010-2011 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>, 2010.  */

#include <config.h>

/* Specification.  */
#include <string.h>

#include <errno.h>

#if HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) && !EXTEND_STRERROR_R

/* The system's strerror_r function is OK, except that its third argument
   is 'int', not 'size_t', or its return type is wrong.  */

# include <limits.h>

int
strerror_r (int errnum, char *buf, size_t buflen)
# undef strerror_r
{
  int ret;

  if (buflen > INT_MAX)
    buflen = INT_MAX;

# ifdef __hpux
  /* On HP-UX 11.31, strerror_r always fails when buflen < 80.  */
  {
    char stackbuf[80];

    if (buflen < sizeof (stackbuf))
      {
        ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
        if (ret == 0)
          {
            size_t len = strlen (stackbuf);

            if (len < buflen)
              memcpy (buf, stackbuf, len + 1);
            else
              ret = ERANGE;
          }
      }
    else
      ret = strerror_r (errnum, buf, buflen);
  }
# elif defined __CYGWIN__
  /* Cygwin only provides the glibc interface, is thread-safe, and
     always succeeds (although it may truncate). */
  strerror_r (errnum, buf, buflen);
  ret = 0;
# else
  ret = strerror_r (errnum, buf, buflen);
# endif

# ifdef _AIX
  /* On AIX 6.1, strerror_r returns -1 and sets errno to EINVAL
     if buflen <= 1.  */
  if (ret < 0 && errno == EINVAL && buflen <= 1)
    {
      /* Retry with a larger buffer.  */
      char largerbuf[10];
      ret = strerror_r (errnum, largerbuf, sizeof (largerbuf));
      if (ret < 0 && errno == EINVAL)
        {
          /* errnum was out of range.  */
          return EINVAL;
        }
      else
        {
          /* buf was too small.  */
          return ERANGE;
        }
    }
# endif

  /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
  return (ret < 0 ? errno : ret);
}

#elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */ && !EXTEND_STRERROR_R

int
strerror_r (int errnum, char *buf, size_t buflen)
{
  extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);

  int ret = __xpg_strerror_r (errnum, buf, buflen);
  return (ret < 0 ? errno : ret);
}

#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) || EXTEND_STRERROR_R */

# include "glthread/lock.h"

/* Use strerror(), with locking.  */

/* This lock protects the buffer returned by strerror().  We assume that
   no other uses of strerror() exist in the program.  */
gl_lock_define_initialized(static, strerror_lock)

int
strerror_r (int errnum, char *buf, size_t buflen)
{
  gl_lock_lock (strerror_lock);

  {
    char *errmsg = strerror (errnum);
    size_t len = strlen (errmsg);
    int ret;

    if (len < buflen)
      {
        memcpy (buf, errmsg, len + 1);
        ret = 0;
      }
    else
      ret = ERANGE;

    gl_lock_unlock (strerror_lock);

    return ret;
  }
}

#endif