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