arg parsing: rework error handling to return optional extra information with error code.

arg parsing: added freeing and cloning of internally allocated parts of `struct args_parsed` and `struct args_parse_error`.

Fortune for sink's current commit: Half blessing − 半吉
args
Avril 2 years ago
parent f2d6ee51fb
commit 9d71fa7aee
Signed by: flanchan
GPG Key ID: 284488987C31F630

150
args.c

@ -1,26 +1,162 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "args.h"
#define _PASTE_(x, y) x ## y
#define _PASTE(x, y) _PASTE_(x, y)
#define _box_name(nm) _PASTE(_box_, _PASTE(nm, __LINE__))
#define $box(expr) ({ __typeof((expr))* _box_name(p) = aligned_alloc(_Alignof(__typeof((expr))), sizeof(__typeof((expr)))); \
if(__builtin_expect(_box_name(p) != NULL, true)) *_box_name(p) = (expr); \
_box_name(p); })
#define $box_t(T, expr) ({ T* _box_name(pT) = aligned_alloc(_Alignof(T), sizeof(T)); \
if(__builtin_expect(_box_name(pT) != NULL, true)) *_box_name(pT) = (expr); \
_box_name(pT); })
typedef __typeof( *((pargs_t*)0)->fd_pass ) fd_pass_t;
const pargs_t* parsed_args = NULL;
static const pargs_t default_args[1] = { A_DEFAULT_ARGS }; //A_DEFAULT_ARGS_P;
static fd_pass_t* _a_alloc_fd_pass(size_t n)
{
fd_pass_t* pass = aligned_alloc(_Alignof(fd_pass_t), sizeof(fd_pass_t) + (sizeof(int)*n));
if(__builtin_expect(pass!=NULL,true)) {
pass->n = n;
//memset(pass->fds, 0, sizeof(int) * n);
}
return pass;
}
void* a_pass_fds(size_t n, ...)
{
va_list va;
va_start(va, n);
fd_pass_t* pass;
if(!n) {
pass = A_FD_PASS_NONE;
goto _end;
}
pass = _a_alloc_fd_pass(n);
if(__builtin_expect(pass!=NULL, true)) {
int* fds = pass->fds;
while ( n --> 0 ) {
int fd = va_arg(va, int);
*fds++ = fd;
}
}
_end:
va_end(va);
return pass;
}
inline void* a_pass_fds_a(size_t n, int ar[const restrict n])
{
if(!n) return A_FD_PASS_NONE;
fd_pass_t* pass = _a_alloc_fd_pass(n);
if(__builtin_expect(pass!=NULL, true))
memcpy(pass->fds, ar, n * sizeof(int));
return pass;
}
const pargs_t* a_get_program_args() {
return parsed_args ?: default_args;
}
bool a_is_parsed() { return parsed_args != NULL; }
enum arg_parse_result a_parse_into(pargs_t *restrict parsed, int *restrict argc, char** *restrict p_argv, char** *restrict p_envp)
bool a_parse_into(union arg_parse_result *restrict parsed, int *restrict argc, char** *restrict p_argv, char** *restrict p_envp)
{
//TODO
return A_PF_UNKNOWN;
parsed->err = (struct arg_parse_error) { .kind = A_PF_UNKNOWN, ._data = { .none = {} } };
return false;
}
enum arg_parse_result a_parse(int *restrict argc, char** *restrict p_argv, char** *restrict p_envp)
const struct arg_parse_error* a_parse(int *restrict argc, char** *restrict p_argv, char** *restrict p_envp)
{
_Thread_local static pargs_t glob_parsed_args;
enum arg_parse_result r = a_parse_into(&glob_parsed_args, argc, p_argv, p_envp);
/*XXX: Probably not needed... _Thread_local*/
static union arg_parse_result glob_parsed_args_r;
if(a_parse_into(&glob_parsed_args_r, argc, p_argv, p_envp))
parsed_args = &glob_parsed_args_r.ok; // A_P_OK
else
return &glob_parsed_args_r.err;
return NULL;
}
pargs_t* a_clone_args(const pargs_t* args)
{
if(__builtin_expect(args!=NULL, true)) {
pargs_t clone;
/* replace */ clone.replace = args->replace;
/* target */ {
const char* target_p = args->target;
clone.target = target_p ? strdup(target_p) : NULL;
};
if(!r) parsed_args = &glob_parsed_args; // A_P_OK
return r;
/* fd_pass */ {
__auto_type fd_pass_p = args->fd_pass;
switch((uintptr_t)fd_pass_p) {
case ((uintptr_t)A_FD_PASS_ALL):
case ((uintptr_t)A_FD_PASS_NONE):
clone.fd_pass = fd_pass_p;
break;
default: {
// Clone fd_pass_p.
clone.fd_pass = a_pass_fds_a(fd_pass_p->n, fd_pass_p->fds);
} break;
}
};
return $box(clone);
} else return NULL;
}
inline void a_free_args(const pargs_t* args)
{
if(__builtin_expect(args==NULL, false)) return;
/* fd_pass */ {
__auto_type fd_pass_p = args->fd_pass;
if(fd_pass_p != A_FD_PASS_NONE
&& fd_pass_p != A_FD_PASS_ALL) free(fd_pass_p);
};
/* target */ {
char* target_p = args->target;
if(target_p) free(target_p);
};
}
void a_free(pargs_t* restrict args)
{
if(__builtin_expect(args!=NULL, true)) {
a_free_args(args);
free(args);
}
}
struct arg_parse_error* a_clone_error(const struct arg_parse_error* error)
{
/* no-op, for now */
if(__builtin_expect(error != NULL, true)) {
return $box_t(struct arg_parse_error, *error);
}
else return NULL;
}
void a_free_error(const struct arg_parse_error* error)
{
(void)error; // no-op, for now;
}
__attribute__((destructor))
static void _a__free_globals()
{
a_free_args(parsed_args);
}

@ -6,19 +6,19 @@
#include <stdint.h>
#include <stdbool.h>
enum arg_parse_result {
enum arg_parse_error_kind {
A_PF_UNKNOWN = -1,
A_P_OK = 0,
// TODO: specific errors...
};
typedef struct {
typedef struct arg_parsed {
struct {
bool in,out,err;
} replace; // { 1, 1, 0 }
const char* target; // NULL -> default "/dev/null"
char* target; // NULL -> default "/dev/null"
struct {
/*struct _Alignas(uint64_t) {
@ -32,6 +32,19 @@ typedef struct {
} pargs_t;
struct arg_parse_error {
enum arg_parse_error_kind kind;
union {
struct{} none;
} _data;
};
_Static_assert(sizeof( ((struct arg_parse_error*)(0))->_data ) == 0, "Bad union ZST");
union arg_parse_result {
struct arg_parsed ok;
struct arg_parse_error err;
};
#define A_FD_PASS_ALL ((void*)(~((uintptr_t)0ul)))
#define A_FD_PASS_NONE NULL
@ -53,16 +66,28 @@ _a_inline_proto const pargs_t* a_get_program_args()
_a_inline_proto bool a_is_parsed()
{ return parsed_args != NULL; }
enum arg_parse_result a_parse_into(pargs_t *restrict parsed, int *restrict argc, char** *restrict p_argv, char** *restrict p_envp);
/// Create an `pargs_t.fd_pass` value for `n` fds.
void* a_pass_fds(size_t n, ...);
/// Create an `pargs_t.fd_pass` value for `n` fds from the array `ar`.
void* a_pass_fds_a(size_t n, int ar[const restrict n]);
_a_inline_proto // TODO: XXX: Is this worth it?
enum arg_parse_result a_parse(int *restrict argc, char** *restrict p_argv, char** *restrict p_envp)
{
static pargs_t glob_parsed_args; //XXX: Wth is this an error?
enum arg_parse_result r = a_parse_into(&glob_parsed_args, argc, p_argv, p_envp);
bool a_parse_into(union arg_parse_result *restrict parsed, int *restrict argc, char** *restrict p_argv, char** *restrict p_envp);
const struct arg_parse_error* a_parse(int *restrict argc, char** *restrict p_argv, char** *restrict p_envp);
/// Clone a `pargs_t` struct. The returned struct pointer must be freed with `a_free()`.
pargs_t* a_clone_args(const pargs_t* args);
/// Clone a `struct arg_parse_error`. The returned struct pointer must be freed with `a_free_args()`, then with stdlib `free()` itself.
struct arg_parse_error* a_clone_error(const struct arg_parse_error* error);
/// Free the internals of a `pargs_t` struct.
void a_free_args(const pargs_t* args);
/// Free the internals of an `arg_parse_error` struct.
void a_free_error(const struct arg_parse_error* error);
/// Free a `malloc()` or `a_clone_args()`'d `pargs_t` struct (NOTE: do *not* pass the result of `a_get_program_args()` or `parsed_args` to this, use `a_free_args()` for those instead.
void a_free(pargs_t* restrict args);
if(!r) parsed_args = &glob_parsed_args; // A_P_OK
return r;
}
#undef _a_inline_proto
#endif /* _ARGS_H */

@ -210,7 +210,7 @@ static void print_debug_info(int argc, char* const* argv, char* const* envp)
static bool parse_args(int *restrict argc, char** *restrict argv, char** *restrict envp)
{
return a_parse(argc, argv, envp) == A_P_OK || true; //TODO: Print proper error messages on failures.
return a_parse(argc, argv, envp) /* == NULL */ || true; //TODO: Print proper error messages on failures.
}
int main(int argc, char** argv, char** envp)

Loading…
Cancel
Save