root/src/libsir/src/sirhelpers.c

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

DEFINITIONS

This source file includes following definitions.
  1. __sir_safefree
  2. _sir_safeclose
  3. _sir_safefclose
  4. _sir_validfd
  5. __sir_validupdatedata
  6. __sir_validlevels
  7. __sir_validlevel
  8. __sir_validopts
  9. __sir_validtextattr
  10. __sir_validtextcolor
  11. __sir_validcolormode
  12. _sir_strncpy
  13. _sir_strncat
  14. _sir_fopen
  15. _sir_getchar
  16. _sir_explicit_memset
  17. _sir_strremove
  18. _sir_strsqueeze
  19. _sir_strredact
  20. _sir_strreplace
  21. _sir_strcreplace

   1 /*
   2  * sirhelpers.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/helpers.h"
  34 #include "sir/errors.h"
  35 
  36 void __sir_safefree(void** pp) {
     /* [previous][next][first][last][top][bottom][index][help] */
  37     if (!pp || !*pp)
  38         return;
  39 
  40     free(*pp);
  41     *pp = NULL;
  42 }
  43 
  44 void _sir_safeclose(int* restrict fd) {
     /* [previous][next][first][last][top][bottom][index][help] */
  45     if (!fd || 0 > *fd)
  46         return;
  47 
  48     if (-1 == close(*fd))
  49         (void)_sir_handleerr(errno);
  50 
  51     *fd = -1;
  52 }
  53 
  54 void _sir_safefclose(FILE* restrict* restrict f) {
     /* [previous][next][first][last][top][bottom][index][help] */
  55     if (!f || !*f)
  56         return;
  57 
  58     if (0 != fclose(*f))
  59         (void)_sir_handleerr(errno);
  60 
  61     *f = NULL;
  62 }
  63 
  64 bool _sir_validfd(int fd) {
     /* [previous][next][first][last][top][bottom][index][help] */
  65     /** stdin, stdout, stderr use up 0, 1, 2 */
  66     if (2 >= fd)
  67         return _sir_handleerr(EBADF);
  68 
  69 #if !defined(__WIN__)
  70     int ret = fcntl(fd, F_GETFL);
  71 #else /* __WIN__ */
  72 # if !defined(SIR_MSVCRT_MINGW)
  73     invalparamfn old = _set_thread_local_invalid_parameter_handler(_sir_invalidparameter);
  74 # endif
  75     struct _stat st;
  76     int ret = _fstat(fd, &st);
  77 # if !defined(SIR_MSVCRT_MINGW)
  78     _set_thread_local_invalid_parameter_handler(old);
  79 # endif
  80 #endif
  81     return (-1 != ret || EBADF != errno) ? true : _sir_handleerr(errno);
  82 }
  83 
  84 /** Validates a sir_update_config_data structure. */
  85 bool __sir_validupdatedata(const sir_update_config_data* data, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
  86     const char* file, uint32_t line) {
  87     if (!_sir_validptr(data))
  88         return false;
  89 
  90     bool valid = true;
  91     if ((data->fields & SIRU_ALL) == 0U || (data->fields & ~SIRU_ALL) != 0U)
  92         valid = false;
  93 
  94     if (valid && _sir_bittest(data->fields, SIRU_LEVELS))
  95         valid = _sir_validptrnofail(data->levels) &&
  96                 _sir_validlevels(*data->levels);
  97 
  98     if (valid && _sir_bittest(data->fields, SIRU_OPTIONS))
  99         valid = _sir_validptrnofail(data->opts) &&
 100                 _sir_validopts(*data->opts);
 101 
 102     if (valid && _sir_bittest(data->fields, SIRU_SYSLOG_ID))
 103         valid = _sir_validstrnofail(data->sl_identity);
 104 
 105     if (valid && _sir_bittest(data->fields, SIRU_SYSLOG_CAT))
 106         valid = _sir_validstrnofail(data->sl_category);
 107 
 108     if (!valid) {
 109         SIR_ASSERT(valid);
 110         (void)__sir_seterror(_SIR_E_INVALID, func, file, line);
 111     }
 112 
 113     return valid;
 114 }
 115 
 116 bool __sir_validlevels(sir_levels levels, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
 117     const char* file, uint32_t line) {
 118     if ((SIRL_ALL == levels || SIRL_NONE == levels) ||
 119         ((_sir_bittest(levels, SIRL_INFO)           ||
 120          _sir_bittest(levels, SIRL_DEBUG)           ||
 121          _sir_bittest(levels, SIRL_NOTICE)          ||
 122          _sir_bittest(levels, SIRL_WARN)            ||
 123          _sir_bittest(levels, SIRL_ERROR)           ||
 124          _sir_bittest(levels, SIRL_CRIT)            ||
 125          _sir_bittest(levels, SIRL_ALERT)           ||
 126          _sir_bittest(levels, SIRL_EMERG))          &&
 127          ((levels & ~SIRL_ALL) == 0U)))
 128          return true;
 129 
 130     _sir_selflog("invalid levels: %04"PRIx16, levels);
 131     return __sir_seterror(_SIR_E_LEVELS, func, file, line);
 132 }
 133 
 134 bool __sir_validlevel(sir_level level, const char* func, const char* file,
     /* [previous][next][first][last][top][bottom][index][help] */
 135     uint32_t line) {
 136     if (SIRL_INFO   == level || SIRL_DEBUG == level ||
 137         SIRL_NOTICE == level || SIRL_WARN  == level ||
 138         SIRL_ERROR  == level || SIRL_CRIT  == level ||
 139         SIRL_ALERT  == level || SIRL_EMERG == level)
 140         return true;
 141 
 142     _sir_selflog("invalid level: %04"PRIx16, level);
 143     return __sir_seterror(_SIR_E_LEVELS, func, file, line);
 144 }
 145 
 146 bool __sir_validopts(sir_options opts, const char* func, const char* file,
     /* [previous][next][first][last][top][bottom][index][help] */
 147     uint32_t line) {
 148     if ((SIRO_ALL == opts || SIRO_MSGONLY == opts) ||
 149         ((_sir_bittest(opts, SIRO_NOTIME)          ||
 150          _sir_bittest(opts, SIRO_NOHOST)           ||
 151          _sir_bittest(opts, SIRO_NOLEVEL)          ||
 152          _sir_bittest(opts, SIRO_NONAME)           ||
 153          _sir_bittest(opts, SIRO_NOMSEC)           ||
 154          _sir_bittest(opts, SIRO_NOPID)            ||
 155          _sir_bittest(opts, SIRO_NOTID)            ||
 156          _sir_bittest(opts, SIRO_NOHDR))           &&
 157          ((opts & ~(SIRO_MSGONLY | SIRO_NOHDR)) == 0U)))
 158          return true;
 159 
 160     _sir_selflog("invalid options: %08"PRIx32, opts);
 161     return __sir_seterror(_SIR_E_OPTIONS, func, file, line);
 162 }
 163 
 164 bool __sir_validtextattr(sir_textattr attr, const char* func, const char* file,
     /* [previous][next][first][last][top][bottom][index][help] */
 165     uint32_t line) {
 166     switch(attr) {
 167         case SIRTA_NORMAL:
 168         case SIRTA_BOLD:
 169         case SIRTA_DIM:
 170         case SIRTA_EMPH:
 171         case SIRTA_ULINE:
 172             return true;
 173         default: {
 174             _sir_selflog("invalid text attr: %d", attr);
 175             return __sir_seterror(_SIR_E_TEXTATTR, func, file, line);
 176         }
 177     }
 178 }
 179 
 180 bool __sir_validtextcolor(sir_colormode mode, sir_textcolor color, const char* func,
     /* [previous][next][first][last][top][bottom][index][help] */
 181     const char* file, uint32_t line) {
 182     bool valid = false;
 183     switch (mode) {
 184         case SIRCM_16:
 185             /* in 16-color mode:
 186              * compare to 30..37, 39, 40..47, 49, 90..97, 100..107. */
 187             valid = SIRTC_DEFAULT == color ||
 188                     (color >= 30U && color <= 37U) || color == 39U ||
 189                     (color >= 40U && color <= 47U) || color == 49U ||
 190                     (color >= 90U && color <= 97U) || (color >= 100U && color <= 107U);
 191             break;
 192         case SIRCM_256:
 193             /* in 256-color mode: compare to 0..255. sir_textcolor is unsigned,
 194              * so only need to ensure it's <= 255. */
 195             valid = SIRTC_DEFAULT == color || color <= 255U;
 196             break;
 197         case SIRCM_RGB: {
 198             /* in RGB-color mode: mask and compare to 0x00ffffff. */
 199             valid = SIRTC_DEFAULT == color || ((color & 0xff000000U) == 0U);
 200             break;
 201         }
 202         case SIRCM_INVALID: // GCOVR_EXCL_START
 203         default:
 204             valid = false;
 205             break;
 206     } // GCOVR_EXCL_STOP
 207 
 208     if (!valid) {
 209         _sir_selflog("invalid text color for mode %d %08"PRIx32" (%"PRIu32")",
 210             mode, color, color);
 211         (void)__sir_seterror(_SIR_E_TEXTCOLOR, func, file, line);
 212     }
 213 
 214     return valid;
 215 }
 216 
 217 bool __sir_validcolormode(sir_colormode mode, const char* func, const char* file,
     /* [previous][next][first][last][top][bottom][index][help] */
 218     uint32_t line) {
 219     switch (mode) {
 220         case SIRCM_16:
 221         case SIRCM_256:
 222         case SIRCM_RGB:
 223             return true;
 224         case SIRCM_INVALID:
 225         default: {
 226             _sir_selflog("invalid color mode: %d", mode);
 227             return __sir_seterror(_SIR_E_COLORMODE, func, file, line);
 228         }
 229     }
 230 }
 231 
 232 int _sir_strncpy(char* restrict dest, size_t destsz, const char* restrict src,
     /* [previous][next][first][last][top][bottom][index][help] */
 233     size_t count) {
 234     if (_sir_validptr(dest) && _sir_validstr(src)) {
 235 #if defined(__HAVE_STDC_SECURE_OR_EXT1__)
 236         int ret = strncpy_s(dest, destsz, src, count);
 237         if (0 != ret) {
 238             (void)_sir_handleerr(ret);
 239             return -1;
 240         }
 241         return 0;
 242 #else
 243         SIR_UNUSED(count);
 244         size_t cpy = strlcpy(dest, src, destsz);
 245         SIR_ASSERT_UNUSED(cpy < destsz, cpy);
 246         return 0;
 247 #endif
 248     }
 249 
 250     return -1;
 251 }
 252 
 253 int _sir_strncat(char* restrict dest, size_t destsz, const char* restrict src,
     /* [previous][next][first][last][top][bottom][index][help] */
 254     size_t count) {
 255     if (_sir_validptr(dest) && _sir_validstr(src)) {
 256 #if defined(__HAVE_STDC_SECURE_OR_EXT1__)
 257         int ret = strncat_s(dest, destsz, src, count);
 258         if (0 != ret) {
 259             (void)_sir_handleerr(ret);
 260             return -1;
 261         }
 262         return 0;
 263 #else
 264         SIR_UNUSED(count);
 265         size_t cat = strlcat(dest, src, destsz);
 266         SIR_ASSERT_UNUSED(cat < destsz, cat);
 267         return 0;
 268 #endif
 269     }
 270 
 271     return -1;
 272 }
 273 
 274 int _sir_fopen(FILE* restrict* restrict streamptr, const char* restrict filename,
     /* [previous][next][first][last][top][bottom][index][help] */
 275     const char* restrict mode) {
 276     if (_sir_validptrptr(streamptr) && _sir_validstr(filename) && _sir_validstr(mode)) {
 277 #if defined(__HAVE_STDC_SECURE_OR_EXT1__)
 278         int ret = fopen_s(streamptr, filename, mode);
 279         if (0 != ret) {
 280             (void)_sir_handleerr(ret);
 281             return -1;
 282         }
 283         return 0;
 284 #else
 285         *streamptr = fopen(filename, mode);
 286         if (!*streamptr) {
 287             (void)_sir_handleerr(errno);
 288             return -1;
 289         }
 290         return 0;
 291 #endif
 292     }
 293 
 294     return -1;
 295 }
 296 
 297 bool _sir_getchar(char* input) {
     /* [previous][next][first][last][top][bottom][index][help] */
 298 #if defined(__WIN__)
 299 # if defined(__EMBARCADEROC__) && (__clang_major__ < 15)
 300     if (input)
 301         *input = (char)getch();
 302 # else
 303     if (input)
 304         *input = (char)_getch();
 305 # endif
 306      return true;
 307 #else /* !__WIN__ */
 308     struct termios cur = {0};
 309     if (0 != tcgetattr(STDIN_FILENO, &cur))
 310         return _sir_handleerr(errno);
 311 
 312     struct termios new = cur;
 313     new.c_lflag &= ~(ICANON | ECHO);
 314 
 315     if (0 != tcsetattr(STDIN_FILENO, TCSANOW, &new))
 316         return _sir_handleerr(errno);
 317 
 318     int ch = getchar();
 319 
 320     if (NULL != input)
 321         *input = (char)ch;
 322 
 323     return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &cur) ? true
 324         : _sir_handleerr(errno);
 325 #endif
 326 }
 327 
 328 #if !defined(GCC_STATIC_ANALYZER)
 329 typedef void*(*_sir_explicit_memset_t)(void*, int, size_t);
 330 static const volatile _sir_explicit_memset_t _sir_explicit_memset_impl = memset;
 331 #else
 332 # define _sir_explicit_memset_impl(s, c, n) memset(s, c, n)
 333 #endif
 334 void* _sir_explicit_memset(void* ptr, int c, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
 335 {
 336     return _sir_explicit_memset_impl(ptr, c, len);
 337 }
 338 
 339 char* _sir_strremove(char *str, const char *sub) {
     /* [previous][next][first][last][top][bottom][index][help] */
 340     if (!str)
 341         return NULL;
 342 
 343     if (!sub)
 344         return str;
 345 
 346     char* r;
 347     char* q;
 348 
 349     if (*sub && (q = r = strstr(str, sub)) != NULL) {
 350         size_t len = strnlen(sub, strlen(str));
 351         const char* p;
 352 
 353         while ((r = strstr(p = r + len, sub)) != NULL)
 354             while (p < r)
 355                 *q++ = *p++;
 356 
 357         while ((*q++ = *p++) != '\0');
 358     }
 359 
 360     return str;
 361 }
 362 
 363 char* _sir_strsqueeze(char *str) {
     /* [previous][next][first][last][top][bottom][index][help] */
 364     if (!str)
 365         return NULL;
 366 
 367     unsigned long j;
 368 
 369     for (unsigned long i = j = 0; str[i]; ++i)
 370         if ((i > 0 && !isspace((unsigned char)(str[i - 1])))
 371                    || !isspace((unsigned char)(str[i])))
 372             str[j++] = str[i];
 373 
 374     str[j] = '\0';
 375 
 376     return str;
 377 }
 378 
 379 char* _sir_strredact(char *str, const char *sub, const char c) {
     /* [previous][next][first][last][top][bottom][index][help] */
 380     if (!str)
 381         return NULL;
 382 
 383     if (!sub)
 384         return str;
 385 
 386     char *p = strstr(str, sub);
 387 
 388     if (!c || !p)
 389         return str;
 390 
 391     (void)_sir_explicit_memset(p, c, strnlen(sub, strlen(str)));
 392 
 393     return _sir_strredact(str, sub, c);
 394 }
 395 
 396 char* _sir_strreplace(char *str, const char c, const char n) {
     /* [previous][next][first][last][top][bottom][index][help] */
 397     if (!str)
 398         return NULL;
 399 
 400     char *i = str;
 401 
 402     if (!c || !n)
 403         return str;
 404 
 405     while ((i = strchr(i, c)) != NULL)
 406         *i++ = n;
 407 
 408     return str;
 409 }
 410 
 411 size_t _sir_strcreplace(char *str, const char c, const char n, int32_t max) {
     /* [previous][next][first][last][top][bottom][index][help] */
 412     char*  i   = str;
 413     size_t cnt = 0;
 414 
 415     if (!str || !c || !n || !max)
 416         return cnt;
 417 
 418     while (cnt < (size_t)max && (i = strchr(i, c)) != NULL) {
 419         *i++ = n;
 420         cnt++;
 421     }
 422 
 423     return cnt;
 424 }

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