/+junk/codegen

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/%2Bjunk/codegen
1 by Gustav Hartvigsson
Beep Boop, nothin yet.
1
// flag.h -- command-line flag parsing
2
//
3
//   Inspired by Go's flag module: https://pkg.go.dev/flag
4
//
5
#ifndef FLAG_H_
6
#define FLAG_H_
7
8
#include <assert.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <stdbool.h>
12
#include <stdint.h>
13
#include <stddef.h>
14
#include <inttypes.h>
15
#include <limits.h>
16
#include <string.h>
17
#include <errno.h>
18
19
// TODO: add support for -flag=x syntax
20
// TODO: *_var function variants
21
// void flag_bool_var(bool *var, const char *name, bool def, const char *desc);
22
// void flag_bool_uint64(uint64_t *var, const char *name, bool def, const char *desc);
23
// etc.
24
// WARNING! *_var functions may break the flag_name() functionality
25
26
char *flag_name(void *val);
27
bool *flag_bool(const char *name, bool def, const char *desc);
28
uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc);
29
size_t *flag_size(const char *name, uint64_t def, const char *desc);
30
char **flag_str(const char *name, const char *def, const char *desc);
31
bool flag_parse(int argc, char **argv);
32
int flag_rest_argc(void);
33
char **flag_rest_argv(void);
34
void flag_print_error(FILE *stream);
35
void flag_print_options(FILE *stream);
36
37
#endif // FLAG_H_
38
39
//////////////////////////////
40
41
#ifdef FLAG_IMPLEMENTATION
42
43
typedef enum {
44
    FLAG_BOOL = 0,
45
    FLAG_UINT64,
46
    FLAG_SIZE,
47
    FLAG_STR,
48
    COUNT_FLAG_TYPES,
49
} Flag_Type;
50
51
static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive Flag_Value definition");
52
typedef union {
53
    char *as_str;
54
    uint64_t as_uint64;
55
    bool as_bool;
56
    size_t as_size;
57
} Flag_Value;
58
59
typedef enum {
60
    FLAG_NO_ERROR = 0,
61
    FLAG_ERROR_UNKNOWN,
62
    FLAG_ERROR_NO_VALUE,
63
    FLAG_ERROR_INVALID_NUMBER,
64
    FLAG_ERROR_INTEGER_OVERFLOW,
65
    FLAG_ERROR_INVALID_SIZE_SUFFIX,
66
    COUNT_FLAG_ERRORS,
67
} Flag_Error;
68
69
typedef struct {
70
    Flag_Type type;
71
    char *name;
72
    char *desc;
73
    Flag_Value val;
74
    Flag_Value def;
75
} Flag;
76
77
#ifndef FLAGS_CAP
78
#define FLAGS_CAP 256
79
#endif
80
81
typedef struct {
82
    Flag flags[FLAGS_CAP];
83
    size_t flags_count;
84
85
    Flag_Error flag_error;
86
    char *flag_error_name;
87
88
    int rest_argc;
89
    char **rest_argv;
90
} Flag_Context;
91
92
static Flag_Context flag_global_context;
93
94
Flag *flag_new(Flag_Type type, const char *name, const char *desc)
95
{
96
    Flag_Context *c = &flag_global_context;
97
98
    assert(c->flags_count < FLAGS_CAP);
99
    Flag *flag = &c->flags[c->flags_count++];
100
    memset(flag, 0, sizeof(*flag));
101
    flag->type = type;
102
    // NOTE: I won't touch them I promise Kappa
103
    flag->name = (char*) name;
104
    flag->desc = (char*) desc;
105
    return flag;
106
}
107
108
char *flag_name(void *val)
109
{
110
    Flag *flag = (Flag*) ((char*) val - offsetof(Flag, val));
111
    return flag->name;
112
}
113
114
bool *flag_bool(const char *name, bool def, const char *desc)
115
{
116
    Flag *flag = flag_new(FLAG_BOOL, name, desc);
117
    flag->def.as_bool = def;
118
    flag->val.as_bool = def;
119
    return &flag->val.as_bool;
120
}
121
122
uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc)
123
{
124
    Flag *flag = flag_new(FLAG_UINT64, name, desc);
125
    flag->val.as_uint64 = def;
126
    flag->def.as_uint64 = def;
127
    return &flag->val.as_uint64;
128
}
129
130
size_t *flag_size(const char *name, uint64_t def, const char *desc)
131
{
132
    Flag *flag = flag_new(FLAG_SIZE, name, desc);
133
    flag->val.as_size = def;
134
    flag->def.as_size = def;
135
    return &flag->val.as_size;
136
}
137
138
char **flag_str(const char *name, const char *def, const char *desc)
139
{
140
    Flag *flag = flag_new(FLAG_STR, name, desc);
141
    flag->val.as_str = (char*) def;
142
    flag->def.as_str = (char*) def;
143
    return &flag->val.as_str;
144
}
145
146
static char *flag_shift_args(int *argc, char ***argv)
147
{
148
    assert(*argc > 0);
149
    char *result = **argv;
150
    *argv += 1;
151
    *argc -= 1;
152
    return result;
153
}
154
155
int flag_rest_argc(void)
156
{
157
    return flag_global_context.rest_argc;
158
}
159
160
char **flag_rest_argv(void)
161
{
162
    return flag_global_context.rest_argv;
163
}
164
165
bool flag_parse(int argc, char **argv)
166
{
167
    Flag_Context *c = &flag_global_context;
168
169
    flag_shift_args(&argc, &argv);
170
171
    while (argc > 0) {
172
        char *flag = flag_shift_args(&argc, &argv);
173
174
        if (*flag != '-') {
175
            // NOTE: pushing flag back into args
176
            c->rest_argc = argc + 1;
177
            c->rest_argv = argv - 1;
178
            return true;
179
        }
180
181
        if (strcmp(flag, "--") == 0) {
182
            // NOTE: but if it's the terminator we don't need to push it back
183
            c->rest_argc = argc;
184
            c->rest_argv = argv;
185
            return true;
186
        }
187
188
        // NOTE: remove the dash
189
        flag += 1;
190
191
        bool found = false;
192
        for (size_t i = 0; i < c->flags_count; ++i) {
193
            if (strcmp(c->flags[i].name, flag) == 0) {
194
                static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type parsing");
195
                switch (c->flags[i].type) {
196
                case FLAG_BOOL: {
197
                    c->flags[i].val.as_bool = true;
198
                }
199
                break;
200
201
                case FLAG_STR: {
202
                    if (argc == 0) {
203
                        c->flag_error = FLAG_ERROR_NO_VALUE;
204
                        c->flag_error_name = flag;
205
                        return false;
206
                    }
207
                    char *arg = flag_shift_args(&argc, &argv);
208
                    c->flags[i].val.as_str = arg;
209
                }
210
                break;
211
212
                case FLAG_UINT64: {
213
                    if (argc == 0) {
214
                        c->flag_error = FLAG_ERROR_NO_VALUE;
215
                        c->flag_error_name = flag;
216
                        return false;
217
                    }
218
                    char *arg = flag_shift_args(&argc, &argv);
219
220
                    static_assert(sizeof(unsigned long long int) == sizeof(uint64_t), "The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and uint64_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments.");
221
                    char *endptr;
222
                    // TODO: replace strtoull with a custom solution
223
                    // That way we can get rid of the dependency on errno and static_assert
224
                    unsigned long long int result = strtoull(arg, &endptr, 10);
225
226
                    if (*endptr != '\0') {
227
                        c->flag_error = FLAG_ERROR_INVALID_NUMBER;
228
                        c->flag_error_name = flag;
229
                        return false;
230
                    }
231
                    
232
                    if (result == ULLONG_MAX && errno == ERANGE) {
233
                        c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
234
                        c->flag_error_name = flag;
235
                        return false;
236
                    }
237
238
                    c->flags[i].val.as_uint64 = result;
239
                }
240
                break;
241
242
                case FLAG_SIZE: {
243
                    if (argc == 0) {
244
                        c->flag_error = FLAG_ERROR_NO_VALUE;
245
                        c->flag_error_name = flag;
246
                        return false;
247
                    }
248
                    char *arg = flag_shift_args(&argc, &argv);
249
250
                    static_assert(sizeof(unsigned long long int) == sizeof(size_t), "The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and size_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments.");
251
                    char *endptr;
252
                    // TODO: replace strtoull with a custom solution
253
                    // That way we can get rid of the dependency on errno and static_assert
254
                    unsigned long long int result = strtoull(arg, &endptr, 10);
255
256
                    // TODO: handle more multiplicative suffixes like in dd(1). From the dd(1) man page:
257
                    // > N and BYTES may be followed by the following
258
                    // > multiplicative suffixes: c =1, w =2, b =512, kB =1000, K
259
                    // > =1024, MB =1000*1000, M =1024*1024, xM =M, GB
260
                    // > =1000*1000*1000, G =1024*1024*1024, and so on for T, P,
261
                    // > E, Z, Y.
262
                    if (strcmp(endptr, "K") == 0) {
263
                        result *= 1024;
264
                    } else if (strcmp(endptr, "M") == 0) {
265
                        result *= 1024*1024;
266
                    } else if (strcmp(endptr, "G") == 0) {
267
                        result *= 1024*1024*1024;
268
                    } else if (strcmp(endptr, "") != 0) {
269
                        c->flag_error = FLAG_ERROR_INVALID_SIZE_SUFFIX;
270
                        c->flag_error_name = flag;
271
                        // TODO: capability to report what exactly is the wrong suffix
272
                        return false;
273
                    }
274
275
                    if (result == ULLONG_MAX && errno == ERANGE) {
276
                        c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
277
                        c->flag_error_name = flag;
278
                        return false;
279
                    }
280
281
                    c->flags[i].val.as_size = result;
282
                }
283
                break;
284
285
                case COUNT_FLAG_TYPES:
286
                default: {
287
                    assert(0 && "unreachable");
288
                    exit(69);
289
                }
290
                }
291
292
                found = true;
293
            }
294
        }
295
296
        if (!found) {
297
            c->flag_error = FLAG_ERROR_UNKNOWN;
298
            c->flag_error_name = flag;
299
            return false;
300
        }
301
    }
302
303
    c->rest_argc = argc;
304
    c->rest_argv = argv;
305
    return true;
306
}
307
308
void flag_print_options(FILE *stream)
309
{
310
    Flag_Context *c = &flag_global_context;
311
    for (size_t i = 0; i < c->flags_count; ++i) {
312
        Flag *flag = &c->flags[i];
313
314
        fprintf(stream, "    -%s\n", flag->name);
315
        fprintf(stream, "        %s\n", flag->desc);
316
        static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type defaults printing");
317
        switch (c->flags[i].type) {
318
        case FLAG_BOOL:
319
            if (flag->def.as_bool) {
320
                fprintf(stream, "        Default: %s\n", flag->def.as_bool ? "true" : "false");
321
            }
322
            break;
323
        case FLAG_UINT64:
324
            fprintf(stream, "        Default: %" PRIu64 "\n", flag->def.as_uint64);
325
            break;
326
        case FLAG_SIZE:
327
            fprintf(stream, "        Default: %zu\n", flag->def.as_size);
328
            break;
329
        case FLAG_STR:
330
            if (flag->def.as_str) {
331
                fprintf(stream, "        Default: %s\n", flag->def.as_str);
332
            }
333
            break;
334
        default:
335
            assert(0 && "unreachable");
336
            exit(69);
337
        }
338
    }
339
}
340
341
void flag_print_error(FILE *stream)
342
{
343
    Flag_Context *c = &flag_global_context;
344
    static_assert(COUNT_FLAG_ERRORS == 6, "Exhaustive flag error printing");
345
    switch (c->flag_error) {
346
    case FLAG_NO_ERROR:
347
        // NOTE: don't call flag_print_error() if flag_parse() didn't return false, okay? ._.
348
        fprintf(stream, "Operation Failed Successfully! Please tell the developer of this software that they don't know what they are doing! :)");
349
        break;
350
    case FLAG_ERROR_UNKNOWN:
351
        fprintf(stream, "ERROR: -%s: unknown flag\n", c->flag_error_name);
352
        break;
353
    case FLAG_ERROR_NO_VALUE:
354
        fprintf(stream, "ERROR: -%s: no value provided\n", c->flag_error_name);
355
        break;
356
    case FLAG_ERROR_INVALID_NUMBER:
357
        fprintf(stream, "ERROR: -%s: invalid number\n", c->flag_error_name);
358
        break;
359
    case FLAG_ERROR_INTEGER_OVERFLOW:
360
        fprintf(stream, "ERROR: -%s: integer overflow\n", c->flag_error_name);
361
        break;
362
    case FLAG_ERROR_INVALID_SIZE_SUFFIX:
363
        fprintf(stream, "ERROR: -%s: invalid size suffix\n", c->flag_error_name);
364
        break;
365
    case COUNT_FLAG_ERRORS:
366
    default:
367
        assert(0 && "unreachable");
368
        exit(69);
369
    }
370
}
371
372
#endif
373
// Copyright 2021 Alexey Kutepov <reximkut@gmail.com>
374
//
375
// Permission is hereby granted, free of charge, to any person obtaining a copy
376
// of this software and associated documentation files (the "Software"), to
377
// deal in the Software without restriction, including without limitation the
378
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
379
// sell copies of the Software, and to permit persons to whom the Software is
380
// furnished to do so, subject to the following conditions:
381
//
382
// The above copyright notice and this permission notice shall be included in
383
// all copies or substantial portions of the Software.
384
//
385
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
386
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
387
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
388
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
389
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
390
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
391
// IN THE SOFTWARE.