summaryrefslogtreecommitdiff
path: root/src/shared.h
blob: 94c6f06384ebb6ec2bbeebc328519a86a6a4a899 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
 * @file shared.h
 * @author syxhe (https://t.me/syxhe)
 * @brief A collection of functions and macros shared between files, or without a better place
 * @version 0.1
 * @date 2025-06-09
 * 
 * @copyright Copyright (c) 2025
 * 
 */

#ifndef __VXGG_REWRITE___SHARED_H___3880294315821___
#define __VXGG_REWRITE___SHARED_H___3880294315821___ 1

#include <stddef.h>

typedef int (*gcallback)(void*);    //!< Generic callback signature
typedef void (*fcallback)(void*);   //!< free()-like callback signature

//! Get the size of a statically/vla defined array. Doesn't work on arrays allocated via `malloc()`
#define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0]))

//! Sets errno to `errval`, then returns `retval`. Defined as a macro because it is meant for use inside functions, not as a function itself
#define ERRRET(errval, retval) do {\
    errno = (errval);\
    return (retval);\
} while (0)

/// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions
/// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` 
/// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type
#define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1

/// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. 
/// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise
#define ___VXGG___VERBOSE_ERRORS___ 1

//! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously
#define XALLOC_EXIT(msg, ...) do {\
    if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\
        abort();\
    if(!___VXGG___VERBOSE_ERRORS___)\
        exit(EXIT_FAILURE);\
    error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\
    exit(EXIT_FAILURE); /* Makes gcc happy */\
} while (0)


//! 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
#define VALLOC(nmemb, size) malloc((nmemb) * (size))

//! Error macro that gcc will not complain about
#define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0)
//! Spit out a warning using `error`
#define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0)



/**
 * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer
 * 
 * @param str Pointer to a string. Will be replaced with the malloc()'ed string
 * @param initsize The initial size of the malloc()'ed string
 * @param fd A file descriptor to read from
 * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error
 * @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
 */
int rwbuf(char **str, unsigned long int initsize, int fd);

/**
 * @brief Write the entire contents of a buffer into a file descriptor
 * 
 * @param fd The file descriptor to write to
 * @param buf The buffer to write from
 * @param len The length of the buffer
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int wwbuf(int fd, const unsigned char *buf, int len);



/**
 * @brief `dirname()` reimplementation that returns a malloc()'ed string
 * 
 * @param path The filepath to be inspected
 * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error
 */
char * vxdirname(const char * const path);



/**
 * @brief A locally defined structure designed for easier function cleanup
 * 
 */
typedef struct cl {
    fcallback *callbacks;   //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[]
    void * *arguments;      //!< An array of void pointers. Actual Type: void *arguments[]
    int size;               //!< The size of each array
    int used;               //!< The current number of used elements in each array
} cleanup;

/**
 * @brief Initialize a cleanup object
 * 
 * @param loc The cleanup object to be initialized
 * @param callbacks An array of free()-like callbacks. Must be `size` elements long
 * @param arguments An array of void pointers. Must be `size` elements long
 * @param size The number of elements the callbacks and arguments array are long
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size);

/**
 * @brief Register a new callback and argument onto a cleanup stack
 * 
 * @param loc The cleanup object to modify
 * @param cb A free()-like callback to run
 * @param arg A piece of data for the callback to run
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int cleanup_register(cleanup *loc, fcallback cb, void *arg);

/**
 * @brief Conditionally register a callback and argument
 * 
 * @param loc The cleanup object to modify
 * @param cb A free()-like callback to run
 * @param arg A piece of data for the callback to run
 * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero
 * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error
 */
int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag);

/**
 * @brief Clear a cleanup object
 * @attention Does not free any registered callbacks or arguments, just marks them as available space
 * 
 * @param loc The cleanup object to modify
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int cleanup_clear(cleanup *loc);

/**
 * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order
 * 
 * @param loc The cleanup object to fire
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int cleanup_fire(cleanup *loc);

/**
 * @brief Conditionally fires a cleanup object
 * 
 * @param loc The cleanup object in question
 * @param flag Whether the object should be fired. Will skip firing if non-zero
 * @retval (int)[-1, 0] Returns 0 on success, -1 on error
 */
int cleanup_cndfire(cleanup *loc, unsigned char flag);

/**
 * @brief Initializes a set of variables suitable for use in the cleanup macros
 * @param size The number of elements long each array should be
 */
#define cleanup_CREATE(size) \
cleanup __CLEANUP; \
fcallback __CLEANUP_FUNCS[(size)]; \
void *__CLEANUP_ARGS[(size)]; \
unsigned char __FLAG = 0; \
cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size))

//! Register a callback-argument pair using the local cleanup object
#define cleanup_REGISTER(cb, arg)       cleanup_register(&__CLEANUP, (cb), (arg))
//! Conditionally register a callback-argument pair using the local cleanup object
#define cleanup_CNDREGISTER(cb, arg)    cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG)
//! Clean the local cleanup object
#define cleanup_CLEAR()                 cleanup_clear(&__CLEANUP)
//! Fire the local cleanup object
#define cleanup_FIRE()                  cleanup_fire(&__CLEANUP)
//! Conditionally fire the local cleanup object
#define cleanup_CNDFIRE()               cleanup_cndfire(&__CLEANUP, __FLAG)
//! Set the local cleanup flag to a non-zero number
#define cleanup_MARK()                  (__FLAG = 1)
//! Set the local cleanup flag to zero
#define cleanup_UNMARK()                (__FLAG = 0)
//! Check if the local cleanup flag is non-zero
#define cleanup_ERRORFLAGGED            (__FLAG != 0)
//! Conditionally execute some `code` if the local cleanup flag has not been marked
#define cleanup_CNDEXEC(code)           while(!cleanup_ERRORFLAGGED) {code; break;}

#endif