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.  */
37 const char *
_gpg_strerror(gpg_error_t err)38 _gpg_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.  */
64 static int
system_strerror_r(int no,char * buf,size_t buflen)65 system_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 
92 static int
system_strerror_r(int no,char * buf,size_t buflen)93 system_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 #elif defined (HAVE_STRERROR_S)
100 /* Now the Windows version.  */
101 
102 static int
system_strerror_r(int no,char * buf,size_t buflen)103 system_strerror_r (int no, char *buf, size_t buflen)
104 {
105   return strerror_s (buf, buflen, no);
106 }
107 
108 #else	/* ! HAVE_STRERROR_R && ! HAVE_STRERROR_S */
109 /* Without strerror_r(), we can still provide a non-thread-safe
110    version.  Maybe we are even lucky and the system's strerror() is
111    already thread-safe.  */
112 
113 static int
system_strerror_r(int no,char * buf,size_t buflen)114 system_strerror_r (int no, char *buf, size_t buflen)
115 {
116   char *errstr = strerror (no);
117 
118   if (!errstr)
119     {
120       int saved_errno = errno;
121 
122       if (saved_errno != EINVAL)
123 	snprintf (buf, buflen, "strerror failed: %i\n", errno);
124       return saved_errno;
125     }
126   else
127     {
128       size_t errstr_len = strlen (errstr) + 1;
129       size_t cpy_len = errstr_len < buflen ? errstr_len : buflen;
130       memcpy (buf, errstr, cpy_len);
131       return cpy_len == errstr_len ? 0 : ERANGE;
132     }
133 }
134 #endif
135 
136 
137 /* Return the error string for ERR in the user-supplied buffer BUF of
138    size BUFLEN.  This function is, in contrast to gpg_strerror,
139    thread-safe if a thread-safe strerror_r() function is provided by
140    the system.  If the function succeeds, 0 is returned and BUF
141    contains the string describing the error.  If the buffer was not
142    large enough, ERANGE is returned and BUF contains as much of the
143    beginning of the error string as fits into the buffer.  */
144 int
_gpg_strerror_r(gpg_error_t err,char * buf,size_t buflen)145 _gpg_strerror_r (gpg_error_t err, char *buf, size_t buflen)
146 {
147   gpg_err_code_t code = gpg_err_code (err);
148   const char *errstr;
149   size_t errstr_len;
150   size_t cpy_len;
151 
152   if (code & GPG_ERR_SYSTEM_ERROR)
153     {
154       int no = gpg_err_code_to_errno (code);
155       if (no)
156 	{
157 	  int system_err = system_strerror_r (no, buf, buflen);
158 
159 	  if (system_err != EINVAL)
160 	    {
161 	      if (buflen)
162 		buf[buflen - 1] = '\0';
163 	      return system_err;
164 	    }
165 	}
166       code = GPG_ERR_UNKNOWN_ERRNO;
167     }
168 
169   errstr = dgettext (PACKAGE, msgstr + msgidx[msgidxof (code)]);
170   errstr_len = strlen (errstr) + 1;
171   cpy_len = errstr_len < buflen ? errstr_len : buflen;
172   memcpy (buf, errstr, cpy_len);
173   if (buflen)
174     buf[buflen - 1] = '\0';
175 
176   return cpy_len == errstr_len ? 0 : ERANGE;
177 }
178