diff options
| author | @syxhe <https://t.me/syxhe> | 2025-10-20 15:51:49 -0500 |
|---|---|---|
| committer | @syxhe <https://t.me/syxhe> | 2025-10-20 15:51:49 -0500 |
| commit | 33647dd2c4f4af7c7cb7d99c1a6dac7f1d059a67 (patch) | |
| tree | 12925795f7ecc39e566ff78f18c3bfab5539e32f /src/shared.c | |
| parent | d5de3e243a8481ad879dd8effc5759d406dc90b7 (diff) | |
Switch to unity build
Diffstat (limited to 'src/shared.c')
| -rw-r--r-- | src/shared.c | 177 |
1 files changed, 165 insertions, 12 deletions
diff --git a/src/shared.c b/src/shared.c index 40732f0..0a2121b 100644 --- a/src/shared.c +++ b/src/shared.c | |||
| @@ -1,15 +1,65 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * @file shared.c | 2 | * @file shared.c |
| 3 | * @author syxhe (https://t.me/syxhe) | 3 | * @author syxhe (https://t.me/syxhe) |
| 4 | * @brief *Implementing `shared.h`* | 4 | * @brief A collection of functions and macros shared between files, or without a better place |
| 5 | * @version 0.1 | 5 | * @version 0.1 |
| 6 | * @date 2025-06-09 | 6 | * @date 2025-06-09 |
| 7 | * | 7 | * |
| 8 | * @copyright Copyright (c) 2025 | 8 | * @copyright Copyright (c) 2025 |
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include "shared.h" | 12 | #ifndef __VXGG_REWRITE___SHARED_C___3880294315821___ |
| 13 | #define __VXGG_REWRITE___SHARED_C___3880294315821___ 1 | ||
| 14 | |||
| 15 | #define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) | ||
| 16 | |||
| 17 | #define ERRRET(errval, retval) do {\ | ||
| 18 | errno = (errval);\ | ||
| 19 | return (retval);\ | ||
| 20 | } while (0) | ||
| 21 | |||
| 22 | /// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions | ||
| 23 | /// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` | ||
| 24 | /// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type | ||
| 25 | #define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1 | ||
| 26 | |||
| 27 | /// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. | ||
| 28 | /// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise | ||
| 29 | #define ___VXGG___VERBOSE_ERRORS___ 1 | ||
| 30 | |||
| 31 | //! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously | ||
| 32 | #define XALLOC_EXIT(msg, ...) do {\ | ||
| 33 | if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\ | ||
| 34 | abort();\ | ||
| 35 | if(!___VXGG___VERBOSE_ERRORS___)\ | ||
| 36 | exit(EXIT_FAILURE);\ | ||
| 37 | error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\ | ||
| 38 | exit(EXIT_FAILURE); /* Makes gcc happy */\ | ||
| 39 | } while (0) | ||
| 40 | |||
| 41 | //! Holdover macro because I'm lazy. Used to call either malloc or xmalloc, but the xalloc functions were a bad idea, so I removed them | ||
| 42 | #define VALLOC(nmemb, size) malloc((nmemb) * (size)) | ||
| 43 | |||
| 44 | //! Error macro that gcc will not complain about | ||
| 45 | #define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) | ||
| 46 | //! Spit out a warning using `error` | ||
| 47 | #define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) | ||
| 48 | |||
| 49 | |||
| 50 | typedef int (*gcallback)(void*); //!< Generic callback signature | ||
| 51 | typedef void (*fcallback)(void*); //!< free()-like callback signature | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief A locally defined structure designed for easier function cleanup | ||
| 55 | * | ||
| 56 | */ | ||
| 57 | typedef struct cl { | ||
| 58 | fcallback *callbacks; //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[] | ||
| 59 | void * *arguments; //!< An array of void pointers. Actual Type: void *arguments[] | ||
| 60 | int size; //!< The size of each array | ||
| 61 | int used; //!< The current number of used elements in each array | ||
| 62 | } cleanup; | ||
| 13 | 63 | ||
| 14 | #include <stdlib.h> | 64 | #include <stdlib.h> |
| 15 | #include <string.h> | 65 | #include <string.h> |
| @@ -17,6 +67,17 @@ | |||
| 17 | #include <errno.h> | 67 | #include <errno.h> |
| 18 | #include <error.h> | 68 | #include <error.h> |
| 19 | 69 | ||
| 70 | #include <threads.h> | ||
| 71 | |||
| 72 | /** | ||
| 73 | * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer | ||
| 74 | * | ||
| 75 | * @param str Pointer to a string. Will be replaced with the malloc()'ed string | ||
| 76 | * @param initsize The initial size of the malloc()'ed string | ||
| 77 | * @param fd A file descriptor to read from | ||
| 78 | * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error | ||
| 79 | * @note The allocated buffer will expand and contract as necessary, but it's recommended to set `initsize` to some value close to or equal to the size of the file being read to reduce the number of resizes | ||
| 80 | */ | ||
| 20 | int rwbuf(char **str, unsigned long int initsize, int fd) { | 81 | int rwbuf(char **str, unsigned long int initsize, int fd) { |
| 21 | // Try to read bytes from fd into str | 82 | // Try to read bytes from fd into str |
| 22 | // Bytes read == 0, return 0 | 83 | // Bytes read == 0, return 0 |
| @@ -32,7 +93,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 32 | lstr = calloc(initsize, sizeof(char)); | 93 | lstr = calloc(initsize, sizeof(char)); |
| 33 | if(!lstr) | 94 | if(!lstr) |
| 34 | return -1; | 95 | return -1; |
| 35 | 96 | ||
| 36 | while((bytesread = read(fd, lstr + (csize - ccap), ccap)) > 0) { | 97 | while((bytesread = read(fd, lstr + (csize - ccap), ccap)) > 0) { |
| 37 | ccap -= bytesread; | 98 | ccap -= bytesread; |
| 38 | if(ccap <= 0) { | 99 | if(ccap <= 0) { |
| @@ -43,7 +104,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 43 | if(!tmp) { | 104 | if(!tmp) { |
| 44 | if(___VXGG___VERBOSE_ERRORS___) | 105 | if(___VXGG___VERBOSE_ERRORS___) |
| 45 | error(0, errno, "Could not reallocate enough space for lstr"); | 106 | error(0, errno, "Could not reallocate enough space for lstr"); |
| 46 | 107 | ||
| 47 | free(lstr); | 108 | free(lstr); |
| 48 | lstr = NULL; // Need to set this because of the break | 109 | lstr = NULL; // Need to set this because of the break |
| 49 | bytesread = ECODE; | 110 | bytesread = ECODE; |
| @@ -81,6 +142,15 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 81 | } | 142 | } |
| 82 | 143 | ||
| 83 | 144 | ||
| 145 | /** | ||
| 146 | * @brief Write the entire contents of a buffer into a file descriptor | ||
| 147 | * | ||
| 148 | * @param fd The file descriptor to write to | ||
| 149 | * @param buf The buffer to write from | ||
| 150 | * @param len The length of the buffer | ||
| 151 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 152 | */ | ||
| 153 | |||
| 84 | int wwbuf(int fd, const unsigned char *buf, int len) { | 154 | int wwbuf(int fd, const unsigned char *buf, int len) { |
| 85 | if(!buf || len <= 0) ERRRET(EINVAL, -1); | 155 | if(!buf || len <= 0) ERRRET(EINVAL, -1); |
| 86 | 156 | ||
| @@ -89,7 +159,7 @@ int wwbuf(int fd, const unsigned char *buf, int len) { | |||
| 89 | int n = -1; | 159 | int n = -1; |
| 90 | 160 | ||
| 91 | while(total < len) { | 161 | while(total < len) { |
| 92 | if((n = write(fd, buf + total, left)) < 0) | 162 | if((n = write(fd, buf + total, left)) < 0) |
| 93 | break; | 163 | break; |
| 94 | 164 | ||
| 95 | total += n; | 165 | total += n; |
| @@ -102,7 +172,12 @@ int wwbuf(int fd, const unsigned char *buf, int len) { | |||
| 102 | // https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#sendall | 172 | // https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#sendall |
| 103 | // Thanks Beej! | 173 | // Thanks Beej! |
| 104 | 174 | ||
| 105 | // dirname but less retarded hopefully | 175 | /** |
| 176 | * @brief `dirname()` reimplementation that returns a malloc()'ed string | ||
| 177 | * | ||
| 178 | * @param path The filepath to be inspected | ||
| 179 | * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error | ||
| 180 | */ | ||
| 106 | char * vxdirname(const char * const path) { | 181 | char * vxdirname(const char * const path) { |
| 107 | char *tmp = NULL; | 182 | char *tmp = NULL; |
| 108 | if(!path) { // Path being null is a special case which should return early, before anything else (as to avoid null dereference) | 183 | if(!path) { // Path being null is a special case which should return early, before anything else (as to avoid null dereference) |
| @@ -201,6 +276,15 @@ char * vxdirname(const char * const path) { | |||
| 201 | } | 276 | } |
| 202 | 277 | ||
| 203 | 278 | ||
| 279 | /** | ||
| 280 | * @brief Initialize a cleanup object | ||
| 281 | * | ||
| 282 | * @param loc The cleanup object to be initialized | ||
| 283 | * @param callbacks An array of free()-like callbacks. Must be `size` elements long | ||
| 284 | * @param arguments An array of void pointers. Must be `size` elements long | ||
| 285 | * @param size The number of elements the callbacks and arguments array are long | ||
| 286 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 287 | */ | ||
| 204 | int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { | 288 | int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { |
| 205 | if(!loc || !callbacks || !arguments || size <= 0) ERRRET(EINVAL, -1); | 289 | if(!loc || !callbacks || !arguments || size <= 0) ERRRET(EINVAL, -1); |
| 206 | 290 | ||
| @@ -212,6 +296,14 @@ int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int siz | |||
| 212 | return 0; | 296 | return 0; |
| 213 | } | 297 | } |
| 214 | 298 | ||
| 299 | /** | ||
| 300 | * @brief Register a new callback and argument onto a cleanup stack | ||
| 301 | * | ||
| 302 | * @param loc The cleanup object to modify | ||
| 303 | * @param cb A free()-like callback to run | ||
| 304 | * @param arg A piece of data for the callback to run | ||
| 305 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 306 | */ | ||
| 215 | int cleanup_register(cleanup *loc, fcallback cb, void *arg) { | 307 | int cleanup_register(cleanup *loc, fcallback cb, void *arg) { |
| 216 | if(!loc || !cb) ERRRET(EINVAL, -1); | 308 | if(!loc || !cb) ERRRET(EINVAL, -1); |
| 217 | if(loc->used >= loc->size || loc->used < 0) ERRRET(ENOMEM, -1); | 309 | if(loc->used >= loc->size || loc->used < 0) ERRRET(ENOMEM, -1); |
| @@ -223,18 +315,39 @@ int cleanup_register(cleanup *loc, fcallback cb, void *arg) { | |||
| 223 | return 0; | 315 | return 0; |
| 224 | } | 316 | } |
| 225 | 317 | ||
| 226 | // registers if flag is NOT set | 318 | /** |
| 319 | * @brief Conditionally register a callback and argument | ||
| 320 | * | ||
| 321 | * @param loc The cleanup object to modify | ||
| 322 | * @param cb A free()-like callback to run | ||
| 323 | * @param arg A piece of data for the callback to run | ||
| 324 | * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero | ||
| 325 | * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error | ||
| 326 | */ | ||
| 227 | int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { | 327 | int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { |
| 228 | if(flag) return 0; | 328 | if(flag) return 0; |
| 229 | return cleanup_register(loc, cb, arg); | 329 | return cleanup_register(loc, cb, arg); |
| 230 | } | 330 | } |
| 231 | 331 | ||
| 332 | /** | ||
| 333 | * @brief Clear a cleanup object | ||
| 334 | * @attention Does not free any registered callbacks or arguments, just marks them as available space | ||
| 335 | * | ||
| 336 | * @param loc The cleanup object to modify | ||
| 337 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 338 | */ | ||
| 232 | int cleanup_clear(cleanup *loc) { | 339 | int cleanup_clear(cleanup *loc) { |
| 233 | if(!loc) ERRRET(EINVAL, -1); | 340 | if(!loc) ERRRET(EINVAL, -1); |
| 234 | loc->used = 0; | 341 | loc->used = 0; |
| 235 | return 0; | 342 | return 0; |
| 236 | } | 343 | } |
| 237 | 344 | ||
| 345 | /** | ||
| 346 | * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order | ||
| 347 | * | ||
| 348 | * @param loc The cleanup object to fire | ||
| 349 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 350 | */ | ||
| 238 | int cleanup_fire(cleanup *loc) { | 351 | int cleanup_fire(cleanup *loc) { |
| 239 | if(!loc) ERRRET(EINVAL, -1); | 352 | if(!loc) ERRRET(EINVAL, -1); |
| 240 | 353 | ||
| @@ -251,9 +364,49 @@ int cleanup_fire(cleanup *loc) { | |||
| 251 | return 0; | 364 | return 0; |
| 252 | } | 365 | } |
| 253 | 366 | ||
| 254 | // Fires if flag is set | 367 | /** |
| 368 | * @brief Conditionally fires a cleanup object | ||
| 369 | * | ||
| 370 | * @param loc The cleanup object in question | ||
| 371 | * @param flag Whether the object should be fired. Will skip firing if non-zero | ||
| 372 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 373 | */ | ||
| 255 | int cleanup_cndfire(cleanup *loc, unsigned char flag) { | 374 | int cleanup_cndfire(cleanup *loc, unsigned char flag) { |
| 256 | if(flag) | 375 | if(flag) |
| 257 | return cleanup_fire(loc); | 376 | return cleanup_fire(loc); |
| 258 | return 0; | 377 | return 0; |
| 259 | } \ No newline at end of file | 378 | } |
| 379 | |||
| 380 | /** | ||
| 381 | * @brief Initializes a set of variables suitable for use in the cleanup macros | ||
| 382 | * @param size The number of elements long each array should be | ||
| 383 | */ | ||
| 384 | #define cleanup_CREATE(size) \ | ||
| 385 | cleanup __CLEANUP; \ | ||
| 386 | fcallback __CLEANUP_FUNCS[(size)]; \ | ||
| 387 | void *__CLEANUP_ARGS[(size)]; \ | ||
| 388 | unsigned char __FLAG = 0; \ | ||
| 389 | cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size)) | ||
| 390 | |||
| 391 | //! Register a callback-argument pair using the local cleanup object | ||
| 392 | #define cleanup_REGISTER(cb, arg) cleanup_register(&__CLEANUP, (cb), (arg)) | ||
| 393 | //! Conditionally register a callback-argument pair using the local cleanup object | ||
| 394 | #define cleanup_CNDREGISTER(cb, arg) cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG) | ||
| 395 | //! Clean the local cleanup object | ||
| 396 | #define cleanup_CLEAR() cleanup_clear(&__CLEANUP) | ||
| 397 | //! Fire the local cleanup object | ||
| 398 | #define cleanup_FIRE() cleanup_fire(&__CLEANUP) | ||
| 399 | //! Conditionally fire the local cleanup object | ||
| 400 | #define cleanup_CNDFIRE() cleanup_cndfire(&__CLEANUP, __FLAG) | ||
| 401 | //! Set the local cleanup flag to a non-zero number | ||
| 402 | #define cleanup_MARK() (__FLAG = 1) | ||
| 403 | //! Set the local cleanup flag to zero | ||
| 404 | #define cleanup_UNMARK() (__FLAG = 0) | ||
| 405 | //! Check if the local cleanup flag is non-zero | ||
| 406 | #define cleanup_ERRORFLAGGED (__FLAG != 0) | ||
| 407 | //! Conditionally execute some `code` if the local cleanup flag has not been marked | ||
| 408 | #define cleanup_CNDEXEC(code) while(!cleanup_ERRORFLAGGED) {code; break;} | ||
| 409 | //! Conditionally fire the local cleanup object and return `ret` | ||
| 410 | #define cleanup_CNDFIRERET(ret) do {cleanup_CNDFIRE(); return ret;} while (0) | ||
| 411 | |||
| 412 | #endif \ No newline at end of file | ||
