1/* strerror.c - Describing an error code.
2   Copyright (C) 2003 g10 Code GmbH
3
4   This file is part of libgpg-error.
5
6   libgpg-error is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public License
8   as published by the Free Software Foundation; either version 2.1 of
9   the License, or (at your option) any later version.
10
11   libgpg-error is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with libgpg-error; if not, write to the Free
18   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.  */
20
21#if HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <errno.h>
29
30#include <gpg-error.h>
31
32#include "gettext.h"
33#include "err-codes.h"
34
35/* Return a pointer to a string containing a description of the error
36   code in the error value ERR.  This function is not thread-safe.  */
37const char *
38gpg_strerror (gpg_error_t err)
39{
40  gpg_err_code_t code = gpg_err_code (err);
41
42  if (code & GPG_ERR_SYSTEM_ERROR)
43    {
44      int no = gpg_err_code_to_errno (code);
45      if (no)
46	return strerror (no);
47      else
48	code = GPG_ERR_UNKNOWN_ERRNO;
49    }
50  return dgettext (PACKAGE, msgstr + msgidx[msgidxof (code)]);
51}
52
53
54#ifdef HAVE_STRERROR_R
55#ifdef STRERROR_R_CHAR_P
56/* The GNU C library and probably some other systems have this weird
57   variant of strerror_r.  */
58
59/* Return a dynamically allocated string in *STR describing the system
60   error NO.  If this call succeeds, return 1.  If this call fails due
61   to a resource shortage, set *STR to NULL and return 1.  If this
62   call fails because the error number is not valid, don't set *STR
63   and return 0.  */
64static int
65system_strerror_r (int no, char *buf, size_t buflen)
66{
67  char *errstr;
68
69  errstr = strerror_r (no, buf, buflen);
70  if (errstr != buf)
71    {
72      size_t errstr_len = strlen (errstr) + 1;
73      size_t cpy_len = errstr_len < buflen ? errstr_len : buflen;
74      memcpy (buf, errstr, cpy_len);
75
76      return cpy_len == errstr_len ? 0 : ERANGE;
77    }
78  else
79    {
80      /* We can not tell if the buffer was large enough, but we can
81	 try to make a guess.  */
82      if (strlen (buf) + 1 >= buflen)
83	return ERANGE;
84
85      return 0;
86    }
87}
88
89#else	/* STRERROR_R_CHAR_P */
90/* Now the POSIX version.  */
91
92static int
93system_strerror_r (int no, char *buf, size_t buflen)
94{
95  return strerror_r (no, buf, buflen);
96}
97
98#endif	/* STRERROR_R_CHAR_P */
99
100#else	/* HAVE_STRERROR_H */
101/* Without strerror_r(), we can still provide a non-thread-safe
102   version.  Maybe we are even lucky and the system's strerror() is
103   already thread-safe.  */
104
105static int
106system_strerror_r (int no, char *buf, size_t buflen)
107{
108  char *errstr = strerror (no);
109
110  if (!errstr)
111    {
112      int saved_errno = errno;
113
114      if (saved_errno != EINVAL)
115	snprintf (buf, buflen, "strerror failed: %i\n", errno);
116      return saved_errno;
117    }
118  else
119    {
120      size_t errstr_len = strlen (errstr) + 1;
121      size_t cpy_len = errstr_len < buflen ? errstr_len : buflen;
122      memcpy (buf, errstr, cpy_len);
123      return cpy_len == errstr_len ? 0 : ERANGE;
124    }
125}
126#endif
127
128
129/* Return the error string for ERR in the user-supplied buffer BUF of
130   size BUFLEN.  This function is, in contrast to gpg_strerror,
131   thread-safe if a thread-safe strerror_r() function is provided by
132   the system.  If the function succeeds, 0 is returned and BUF
133   contains the string describing the error.  If the buffer was not
134   large enough, ERANGE is returned and BUF contains as much of the
135   beginning of the error string as fits into the buffer.  */
136int
137gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen)
138{
139  gpg_err_code_t code = gpg_err_code (err);
140  const char *errstr;
141  size_t errstr_len;
142  size_t cpy_len;
143
144  if (code & GPG_ERR_SYSTEM_ERROR)
145    {
146      int no = gpg_err_code_to_errno (code);
147      if (no)
148	{
149	  int system_err = system_strerror_r (no, buf, buflen);
150
151	  if (system_err != EINVAL)
152	    {
153	      if (buflen)
154		buf[buflen - 1] = '\0';
155	      return system_err;
156	    }
157	}
158      code = GPG_ERR_UNKNOWN_ERRNO;
159    }
160
161  errstr = dgettext (PACKAGE, msgstr + msgidx[msgidxof (code)]);
162  errstr_len = strlen (errstr) + 1;
163  cpy_len = errstr_len < buflen ? errstr_len : buflen;
164  memcpy (buf, errstr, cpy_len);
165  if (buflen)
166    buf[buflen - 1] = '\0';
167
168  return cpy_len == errstr_len ? 0 : ERANGE;
169}
170