root/src/libsir/src/sirerrors.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __sir_seterror
  2. __sir_setoserror
  3. __sir_handleerr
  4. _sir_invalidparameter
  5. __sir_handlewin32err
  6. _sir_geterror
  7. _sir_geterrorinfo
  8. _sir_reset_tls_error
  9. PRINTF_FORMAT_ATTR

   1 /*
   2  * sirerrors.c
   3  *
   4  * Version: 2.2.5
   5  *
   6  * -----------------------------------------------------------------------------
   7  *
   8  * SPDX-License-Identifier: MIT
   9  *
  10  * Copyright (c) 2018-2024 Ryan M. Lederman <lederman@gmail.com>
  11  * Copyright (c) 2018-2024 Jeffrey H. Johnson <trnsz@pobox.com>
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  14  * this software and associated documentation files (the "Software"), to deal in
  15  * the Software without restriction, including without limitation the rights to
  16  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  17  * the Software, and to permit persons to whom the Software is furnished to do so,
  18  * subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included in all
  21  * copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  25  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  26  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  27  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  28  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29  *
  30  * -----------------------------------------------------------------------------
  31  */
  32 
  33 #include "sir/errors.h"
  34 #include "sir/helpers.h"
  35 
  36 #if defined(__MACOS__) || defined(__BSD__) || \
  37     (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L && !defined(_GNU_SOURCE)))
  38 # define __HAVE_XSI_STRERROR_R__
  39 # if defined(__GLIBC__) && GLIBC_VERSION < 21300
  40 #  define __HAVE_XSI_STRERROR_R_ERRNO__
  41 # endif
  42 #elif defined(_GNU_SOURCE) && defined(__GLIBC__)
  43 # define __HAVE_GNU_STRERROR_R__
  44 #elif defined(__HAVE_STDC_SECURE_OR_EXT1__)
  45 # define __HAVE_STRERROR_S__
  46 #endif
  47 
  48 /** Per-thread error data */
  49 static _sir_thread_local sir_thread_err _sir_te = {
  50     _SIR_E_NOERROR, 0, {0}, {SIR_UNKNOWN, SIR_UNKNOWN, 0}
  51 };
  52 
  53 #define _SIR_E_PLATFORM_ERRORMSG "Platform error"
  54 #define _SIR_E_PLATFORM_ERRORFORMAT _SIR_E_PLATFORM_ERRORMSG " code %d: %s"
  55 
  56 static const struct {
  57     uint32_t e;
  58     const char* msg;
  59 } sir_errors[] = {
  60     {_SIR_E_NOERROR,   "The operation completed successfully"},
  61     {_SIR_E_NOTREADY,  "libsir has not been initialized"},
  62     {_SIR_E_ALREADY,   "libsir is already initialized"},
  63     {_SIR_E_DUPITEM,   "Item already managed by libsir"},
  64     {_SIR_E_NOITEM,    "Item not managed by libsir"},
  65     {_SIR_E_NOROOM,    "Maximum number of items already stored"},
  66     {_SIR_E_OPTIONS,   "Option flags are invalid"},
  67     {_SIR_E_LEVELS,    "Level flags are invalid"},
  68     {_SIR_E_TEXTSTYLE, "Text style is invalid"},
  69     {_SIR_E_STRING,    "Invalid string argument"},
  70     {_SIR_E_NULLPTR,   "NULL pointer argument"},
  71     {_SIR_E_INVALID,   "Invalid argument"},
  72     {_SIR_E_NODEST,    "No destinations registered for level"},
  73     {_SIR_E_UNAVAIL,   "Feature is disabled or unavailable"},
  74     {_SIR_E_INTERNAL,  "An internal error has occurred"},
  75     {_SIR_E_COLORMODE, "Color mode is invalid"},
  76     {_SIR_E_TEXTATTR,  "Text attributes are invalid"},
  77     {_SIR_E_TEXTCOLOR, "Text color is invalid for mode"},
  78     {_SIR_E_PLUGINBAD, "Plugin module is malformed"},
  79     {_SIR_E_PLUGINDAT, "Data produced by plugin is invalid"},
  80     {_SIR_E_PLUGINVER, "Plugin interface version unsupported"},
  81     {_SIR_E_PLUGINERR, "Plugin reported failure"},
  82     {_SIR_E_PLATFORM,  _SIR_E_PLATFORM_ERRORFORMAT},
  83     {_SIR_E_UNKNOWN,   "Unknown error"},
  84 };
  85 
  86 bool __sir_seterror(uint32_t err, const char* func, const char* file, uint32_t line) {
     /* [previous][next][first][last][top][bottom][index][help] */
  87     if (_sir_validerror(err)) {
  88         _sir_te.lasterror = err;
  89         _sir_te.loc.func  = func;
  90         _sir_te.loc.file  = file;
  91         _sir_te.loc.line  = line;
  92 
  93         if (_SIR_E_PLATFORM != err) {
  94             _sir_te.os_code = 0;
  95             _sir_resetstr(_sir_te.os_msg);
  96         }
  97     }
  98 #if defined(DEBUG) && defined(SIR_SELFLOG)
  99     if (_SIR_E_NOERROR != err) {
 100         char errmsg[SIR_MAXERROR] = {0};
 101         _sir_geterror(errmsg);
 102         __sir_selflog(func, file, line, "%s", errmsg);
 103     }
 104 #endif
 105     return false;
 106 }
 107 
 108 void __sir_setoserror(int code, const char* msg, const char* func, const char* file, uint32_t line) {
     /* [previous][next][first][last][top][bottom][index][help] */
 109     _sir_te.os_code = code;
 110     _sir_resetstr(_sir_te.os_msg);
 111 
 112     if (_sir_validstrnofail(msg))
 113         (void)_sir_strncpy(_sir_te.os_msg, SIR_MAXERROR, msg, SIR_MAXERROR);
 114 
 115     (void)__sir_seterror(_SIR_E_PLATFORM, func, file, line);
 116 }
 117 
 118 bool __sir_handleerr(int code, const char* func, const char* file, uint32_t line) {
     /* [previous][next][first][last][top][bottom][index][help] */
 119     if (SIR_E_NOERROR != code) {
 120         char message[SIR_MAXERROR] = {0};
 121         int finderr                = 0;
 122         errno                      = SIR_E_NOERROR;
 123 
 124 #if defined(__HAVE_XSI_STRERROR_R__)
 125         _sir_selflog("using XSI strerror_r");
 126         finderr = strerror_r(code, message, SIR_MAXERROR);
 127 # if defined(__HAVE_XSI_STRERROR_R_ERRNO__)
 128         _sir_selflog("using XSI strerror_r for glibc < 2.13");
 129         if (finderr == -1)
 130             finderr = errno;
 131 # endif
 132 #elif defined(__HAVE_GNU_STRERROR_R__)
 133         _sir_selflog("using GNU strerror_r");
 134         const char* tmp = strerror_r(code, message, SIR_MAXERROR);
 135         if (tmp != message)
 136             (void)_sir_strncpy(message, SIR_MAXERROR, tmp, SIR_MAXERROR);
 137 #elif defined(__HAVE_STRERROR_S__)
 138         _sir_selflog("using strerror_s");
 139         finderr = (int)strerror_s(message, SIR_MAXERROR, code);
 140 #else
 141         _sir_selflog("using strerror");
 142         const char* tmp = strerror(code);
 143         (void)_sir_strncpy(message, SIR_MAXERROR, tmp, strnlen(tmp, SIR_MAXERROR));
 144 #endif
 145         if (0 == finderr) { //-V547
 146             __sir_setoserror(code, message, func, file, line);
 147 #if defined(__HAVE_XSI_STRERROR_R__) || defined(__HAVE_STRERROR_S__)
 148         } else {
 149             /* Per my reading of the man pages, GNU strerror_r and normal strerror "can't fail";
 150              * they simply return a string such as "Unknown nnn error" if unable to look up an
 151              * error code.
 152              */
 153 # if defined(__HAVE_XSI_STRERROR_R__)
 154             _sir_selflog("strerror_r failed! error: %d", finderr);
 155 # elif defined(__HAVE_STRERROR_S__)
 156             _sir_selflog("strerror_s failed! error: %d", finderr);
 157 # endif
 158 #endif
 159         }
 160     }
 161     return false;
 162 }
 163 
 164 #if defined(__WIN__)
 165 void _sir_invalidparameter(const wchar_t* expr, const wchar_t* func, const wchar_t* file,
     /* [previous][next][first][last][top][bottom][index][help] */
 166     unsigned int line, uintptr_t reserved) {
 167     _sir_selflog("invalid parameter handler: expression: '%S' in %S (%S:%u)",
 168         expr, func, file, line);
 169     SIR_UNUSED(reserved);
 170 }
 171 
 172 bool __sir_handlewin32err(DWORD code, const char* func, const char* file, uint32_t line) {
     /* [previous][next][first][last][top][bottom][index][help] */
 173     char* errbuf = NULL;
 174     DWORD flags  = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
 175                    FORMAT_MESSAGE_IGNORE_INSERTS  | FORMAT_MESSAGE_MAX_WIDTH_MASK;
 176 
 177     DWORD fmtmsg = FormatMessageA(flags, NULL, code, 0, (LPSTR)&errbuf, SIR_MAXERROR, NULL);
 178     if (0 < fmtmsg && _sir_validstrnofail(errbuf)) {
 179         if (errbuf[fmtmsg - 1] == '\n' || errbuf[fmtmsg - 1] == ' ')
 180             errbuf[fmtmsg - 1] = '\0';
 181         __sir_setoserror((int)code, errbuf, func, file, line);
 182     } else {
 183         _sir_selflog("FormatMessage failed! error: %lu", GetLastError());
 184         SIR_ASSERT(false);
 185     }
 186 
 187     if (errbuf) {
 188         HLOCAL local_free = LocalFree((HLOCAL)errbuf);
 189         SIR_ASSERT_UNUSED(NULL == local_free, local_free);
 190         errbuf = NULL;
 191     }
 192     return false;
 193 }
 194 #endif /* !__WIN__ */
 195 
 196 uint32_t _sir_geterror(char message[SIR_MAXERROR]) {
     /* [previous][next][first][last][top][bottom][index][help] */
 197     _sir_resetstr(message);
 198 
 199     static const size_t low  = 0;
 200     static const size_t high = _sir_countof(sir_errors) - 1;
 201 
 202     uint32_t retval = _SIR_E_UNKNOWN;
 203 
 204     _SIR_DECLARE_BIN_SEARCH(low, high);
 205     _SIR_BEGIN_BIN_SEARCH()
 206 
 207     if (sir_errors[_mid].e == _sir_te.lasterror) {
 208         char* heap_msg = NULL;
 209 
 210         if (_SIR_E_PLATFORM == sir_errors[_mid].e) {
 211             heap_msg = calloc(SIR_MAXERROR, sizeof(char));
 212             if (_sir_validptrnofail(heap_msg)) {
 213                 _sir_snprintf_trunc(heap_msg, SIR_MAXERROR, _SIR_E_PLATFORM_ERRORFORMAT, _sir_te.os_code,
 214                     (_sir_validstrnofail(_sir_te.os_msg) ? _sir_te.os_msg : SIR_UNKNOWN));
 215             }
 216         }
 217 
 218         _sir_snprintf_trunc(message, SIR_MAXERROR, SIR_ERRORFORMAT, _sir_te.loc.func, //-V576
 219             _sir_te.loc.file, _sir_te.loc.line, (_sir_validstrnofail(heap_msg)
 220                 ? heap_msg : (_sir_validstrnofail(sir_errors[_mid].msg)
 221                 ? sir_errors[_mid].msg : SIR_UNKNOWN)));
 222 
 223         _sir_safefree(&heap_msg);
 224 
 225         retval = sir_errors[_mid].e;
 226         break;
 227     }
 228 
 229     _SIR_ITERATE_BIN_SEARCH((sir_errors[_mid].e < _sir_te.lasterror ? 1 : -1));
 230     _SIR_END_BIN_SEARCH();
 231 
 232     return retval;
 233 }
 234 
 235 void _sir_geterrorinfo(sir_errorinfo* err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 236     if (!_sir_validptr(err))
 237         return;
 238 
 239     (void)memset(err, 0, sizeof(sir_errorinfo));
 240 
 241     err->func = _sir_te.loc.func;
 242     err->file = _sir_te.loc.file;
 243     err->line = _sir_te.loc.line;
 244 
 245     err->os_code = _sir_te.os_code;
 246     (void)_sir_strncpy(err->os_msg, SIR_MAXERROR, (_sir_validstrnofail(_sir_te.os_msg)
 247         ? _sir_te.os_msg : SIR_UNKNOWN), SIR_MAXERROR);
 248 
 249     err->code = _sir_geterrcode(SIR_E_UNKNOWN);
 250 
 251     static const size_t low  = 0;
 252     static const size_t high = _sir_countof(sir_errors) - 1;
 253 
 254     _SIR_DECLARE_BIN_SEARCH(low, high);
 255     _SIR_BEGIN_BIN_SEARCH()
 256 
 257     if (sir_errors[_mid].e == _sir_te.lasterror) {
 258         err->code = _sir_geterrcode(sir_errors[_mid].e);
 259         if (_SIR_E_PLATFORM == sir_errors[_mid].e)
 260             (void)_sir_strncpy(err->msg, SIR_MAXERROR, _SIR_E_PLATFORM_ERRORMSG, SIR_MAXERROR);
 261         else
 262             (void)_sir_strncpy(err->msg, SIR_MAXERROR, sir_errors[_mid].msg, SIR_MAXERROR);
 263         break;
 264     }
 265 
 266     _SIR_ITERATE_BIN_SEARCH((sir_errors[_mid].e < _sir_te.lasterror ? 1 : -1));
 267     _SIR_END_BIN_SEARCH();
 268 }
 269 
 270 void _sir_reset_tls_error(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 271     _sir_te.lasterror = _SIR_E_NOERROR;
 272     _sir_te.os_code   = 0;
 273     _sir_resetstr(_sir_te.os_msg);
 274     _sir_te.loc.func = SIR_UNKNOWN;
 275     _sir_te.loc.file = SIR_UNKNOWN;
 276     _sir_te.loc.line = 0U;
 277 }
 278 
 279 #if defined(SIR_SELFLOG)
 280 PRINTF_FORMAT_ATTR(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 281 void __sir_selflog(const char* func, const char* file, uint32_t line,
 282     PRINTF_FORMAT const char* format, ...) {
 283     bool success = true;
 284     char prefix[256];
 285 
 286     int write1 = snprintf(prefix, 256, "%s (%s:%"PRIu32"): ", func, file, line);
 287     _sir_eqland(success, write1 > 0);
 288 
 289     if (write1 > 0) {
 290         va_list args;
 291         va_list args2;
 292         va_start(args, format);
 293         va_copy(args2, args);
 294 
 295         int write2 = vsnprintf(NULL, 0, format, args);
 296         va_end(args);
 297         _sir_eqland(success, write2 > 0);
 298 
 299         if (write2 > 0) {
 300             char* buf = (char*)malloc(write2 + 1);
 301             _sir_eqland(success, NULL != buf);
 302 
 303             if (buf) {
 304                 write2 = vsnprintf(buf, write2 + 1, format, args2);
 305                 va_end(args2);
 306                 _sir_eqland(success, write2 > 0);
 307 
 308                 bool error = false;
 309                 bool warn  = false;
 310                 if (write2 > 0) {
 311 # if !defined(__WIN__)
 312                     if (NULL != strcasestr(buf, "error") ||
 313                         NULL != strcasestr(buf, "assert")) {
 314 # else /* __WIN__ */
 315                     if (NULL != StrStrIA(buf, "error") ||
 316                         NULL != StrStrIA(buf, "assert")) {
 317 # endif
 318                         error = true;
 319 # if !defined(__WIN__)
 320                     } else if (NULL != strcasestr(buf, "warn")) {
 321 # else /* __WIN__ */
 322                     } else if (NULL != StrStrIA(buf, "warn")) {
 323 # endif
 324                         warn = true;
 325                     }
 326                     write2 = fprintf(stderr, (error ? SIR_BRED("%s%s") SIR_EOL :
 327                         (warn ? SIR_YELLOW("%s%s") SIR_EOL : "%s%s" SIR_EOL)), prefix, buf);
 328                     _sir_eqland(success, write2 > 0);
 329                 }
 330 
 331                 _sir_safefree(&buf);
 332             }
 333         }
 334     }
 335 
 336     SIR_ASSERT_UNUSED(success, success);
 337 }
 338 #endif

/* [previous][next][first][last][top][bottom][index][help] */