diff --git a/args.c b/args.c index ec2a2fa..56779b8 100644 --- a/args.c +++ b/args.c @@ -6,6 +6,8 @@ #include "args.h" +#include "comp_features.h" + #define _PASTE_(x, y) x ## y #define _PASTE(x, y) _PASTE_(x, y) @@ -19,6 +21,115 @@ _box_name(pT); }) typedef __typeof( *((pargs_t*)0)->fd_pass ) fd_pass_t; +typedef __typeof( ((pargs_t*)0)->replace ) replace_t; + +struct input_arguments +{ + int *restrict p_argc; + char** *restrict p_argv; + char** *restrict p_envp; +}; + +typedef bool (single_arg_parse_func)(int, const char*, struct input_arguments* restrict, pargs_t*); +typedef void (single_arg_error_func)(struct arg_parse_error* restrict); + +enum argument_type { + AT_SHORT = 0, + AT_LONG, + AT_LONG_EQ, + + AT_LONG_OR_EQ = AT_LONG | AT_LONG_EQ, +}; + +struct argument_parser { + union { + const char* l; + char s; + } name; + + enum argument_type kind; + + bool is_default; + + const char* destription; + const char* example; + + single_arg_parse_func* parse; + single_arg_error_func* error; +}; + +typedef __typeof( ((struct argument_parser*)0)->name ) av_name_t; + +static bool _si_handle_replace(int idx, const char* arg, struct input_arguments* restrict in, pargs_t* out) +{ + char c; + bool* restrict repl; + switch(c = *arg) { + case 'i': + case 'I': + repl = &out->replace.in; + break; + case 'o': + case 'O': + repl = &out->replace.out; + break; + case 'e': + case 'E': + repl = &out->replace.err; + break; + default: return false; + } + *repl = !(c & 0x20); + return true; +} + +static const struct argument_parser AV_ARGS[] = { + { { .s= 'i' }, AT_SHORT, true, "Sink stdin", NULL, _si_handle_replace, NULL }, + { { .s='o' }, AT_SHORT, true, "Sink stdout", NULL, _si_handle_replace, NULL }, + { { .s='e' }, AT_SHORT, false, "Sink stderr", NULL, _si_handle_replace, NULL }, + + { { .s='I' }, AT_SHORT, false, "Keep stdin", NULL, _si_handle_replace, NULL }, + { { .s='O' }, AT_SHORT, false, "Keep stdout", NULL, _si_handle_replace, NULL }, + { { .s='E' }, AT_SHORT, true, "Keep stderr", NULL, _si_handle_replace, NULL }, +}; +#define AV_ARGS_SZ (sizeof(AV_ARGS) / sizeof(struct argument_parser)) + +static inline bool _av_long_extract_name(const char* name, enum argument_type ty, view_t* restrict out) +{ + register bool ok = true; + switch(ty) { + case AT_LONG_EQ: + ok = !!sv_split_cstr(name, '=', out, NULL); + break; + case AT_LONG_OR_EQ: + if(!sv_split_cstr(name, '=', out, NULL)) { + if(0) + case AT_SHORT: name = (const char[2]){*name, 0}; + *out = sv_from_cstr(name); + } + break; + default: return false; + } + return ok; +} + +static inline const struct argument_parser* _av_lookup(av_name_t name, enum argument_type ty) +{ + for(size_t i=0;iname.s == name.s) return current; + } else switch(ty) { + case AT_SHORT: if(current->name.s == name.s) return current; + default: { + view_t real_name; + if(!_av_long_extract_name(name.l, ty, &real_name)) continue; // Argument requires `=` but one wasn't fount, continue. + if(sv_eq_cstr(real_name, current->name.l)) return current; + } break; + } + } + return NULL; +} const pargs_t* parsed_args = NULL; static const pargs_t default_args[1] = { A_DEFAULT_ARGS }; //A_DEFAULT_ARGS_P; @@ -143,11 +254,65 @@ const pargs_t* a_get_program_args() { bool a_is_parsed() { return parsed_args != NULL; } -bool a_parse_into(union arg_parse_result *restrict parsed, int *restrict argc, char** *restrict p_argv, char** *restrict p_envp) +static bool a_parse__short(int i, const char* arg, struct input_arguments in, pargs_t* restrict out) +{ + const struct argument_parser* p = _av_lookup( (av_name_t){ .s = *arg, }, AT_SHORT ); + if(!p) return false; + else { + if(!p->parse(i, arg, &in, out)) { + if(p->error) { + //TODO: How to handle returning `struct arg_parse_error`? Make `out` union arg_parse_result* restrict` instead? + } + return false; + } + } + return true; +} + +static bool a_parse__long(int i, const char* arg, struct input_arguments in, pargs_t* restrict out) { - //TODO - parsed->err = (struct arg_parse_error) { .kind = A_PF_UNKNOWN, ._data = { .none = {} } }; - return false; + //TODO: determine if `arg` is `k=v` or not + bool is_eq = /* TODO */ false; + const struct argument_parser* p = _av_lookup( (av_name_t){ .l = arg, }, is_eq ? AT_LONG_EQ : AT_LONG ); + if(!p) return false; + else { + //TODO :if(!p->parse( + } + + return true; +} + +inline static bool _a_parse_into(union arg_parse_result *restrict parsed, struct input_arguments input) +{ + bool ok = true;; + pargs_t args = A_DEFAULT_ARGS; + + //TODO: Parse arguments + +_end: + if(ok) +_set_ok: parsed->ok = args; + else +_set_err: parsed->err = (struct arg_parse_error) { .kind = A_PF_UNKNOWN, ._data = { .none = {} } }; + + return ok; +} + + +inline bool a_parse_into(union arg_parse_result *restrict parsed, int *restrict p_argc, char** *restrict p_argv, char** *restrict p_envp) +{ +#if FEATURE_HAS_FLAG(NO_ARGS) + parsed->ok = A_DEFAULT_ARGS; + return true; +#else + struct input_arguments input = { + p_argc, + p_argv, + p_envp, + }; + + return _a_parse_into(parsed, input); +#endif } const struct arg_parse_error* a_parse(int *restrict argc, char** *restrict p_argv, char** *restrict p_envp) diff --git a/view.c b/view.c index 2d7fac3..0aa73a4 100644 --- a/view.c +++ b/view.c @@ -12,6 +12,20 @@ #define $nullchk(ptr, or) ({ __auto_type _$p = (ptr); \ __builtin_expect(_$p == NULL, false) ? ({ or; }),NULL : _$p; }) +#define min(x, y) ({ __auto_type _$min_x = (x); \ + __auto_type _$min_y = (y); \ + _$min_x < _$min_y ? _$min_x : _$min_y; }) + +#define ptrdiff(x, y) ({ \ + __auto_type _$ptrdiff_x = (x); \ + __auto_type _$ptrdiff_y = (y); \ + (ptrdiff_t)((_$ptrdiff_x < _$ptrdiff_y) \ + ? ((uintptr_t)_$ptrdiff_y) - ((uintptr_t)_$ptrdiff_x) \ + : ((uintptr_t)_$ptrdiff_x) - ((uintptr_t)_$ptrdiff_y)); \ + }) + + + typedef uint8_t byte_t; __attribute__((packed)) @@ -111,3 +125,94 @@ size_t sv_copy_to(size_t sz, char buffer[restrict static sz], view_t view) return _sv_copy_manual(sz, buffer, view); } } + +// String views // + +inline view_t sv_from_cstr(const char* p) +{ + return (view_t) { + .len = strlen(p), + .ptr = (char*)p, + }; +} + +inline view_t sv_slice_cstr(const char* p, size_t n) +{ + if(__builtin_constant_p(n) && !n ) return (view_t) { .len = 0, .ptr = (char*)p }; + else return (view_t) { .len = min(strlen(p), n), .ptr = (char*)p }; +} + +inline view_t sv_slice(view_t v, size_t s, size_t n) +{ + if(__builtin_expect((s = min(v.len, s)) == v.len,false)) return (view_t) { .len = 0, .ptr = v.ptr+v.len }; + else { + return (view_t) { + .len = n + ? min(v.len - s, n)//min(v.len,n) - s + : v.len - s, //XXX: Test this, I think it's correct, but dunno... + .ptr = v.ptr + s, + }; + } +} + +inline size_t sv_split(view_t p, char on, view_t* restrict first, view_t* restrict second) +{ + if(__builtin_constant_p(first) && __builtin_constant_p(second) && !first && !second) + return (size_t)(((uintptr_t) $nullchk(memchr(p.ptr, (int)on, p.len), return 0)) - (uintptr_t)p.ptr); + else { + char* start = memchr(p.ptr, (int)on, p.len); // `on` chr here + if(__builtin_expect(start==NULL, false)) return 0; + + size_t diff = (size_t)ptrdiff(p.ptr, start); + if(first) *first = (view_t) { + .len = diff, + .ptr = p.ptr, + }; + if(second) *second = (view_t) { + .len = (p.len - diff) + 1, //XXX: WARNING: this might be an off-by-1 error... + .ptr = start + 1, + }; + + return diff; + } +} + +size_t sv_split_cstr(const char* p, char on, view_t* restrict first, view_t* restrict second) +{ + return sv_split(sv_from_cstr(p), on, first, second); +} + +inline char* sv_to_cstr(view_t view, char* restrict buffer, size_t n) +{ + if(__builtin_constant_p(buffer) && !buffer) { + if(__builtin_constant_p(n) && !n) return strndup(view.ptr, view.len); + else return strndup(view.ptr, n ? min(n, view.len) : view.len); + } else { + if(__builtin_constant_p(n) && !n) { + return buffer; + } else { + sv_copy_to(n, buffer, view); + return buffer; + } + } +} + +__attribute__((malloc)) +char* sv_dupto_cstr(view_t view) +{ + return sv_to_cstr(view, NULL, 0); +} + +bool sv_eq_cstr(view_t a, const char* b) +{ + return strncmp(a.ptr, b, a.len) == 0 && strlen(b) == a.len; +} + +bool sv_eq(view_t a, view_t b) +{ + return a.len == b.len && (__builtin_expect(a.ptr == b.ptr, false) || memcmp(a.ptr, b.ptr, a.len)); +} + + + + diff --git a/view.h b/view.h index 379e5c1..3864441 100644 --- a/view.h +++ b/view.h @@ -3,6 +3,7 @@ #include #include +#include typedef struct string_view { size_t len; @@ -19,7 +20,31 @@ struct string; view_t s_get_str(str_info_t data) __attribute__((pure)); str_info_t s_get_info(view_t str) __attribute__((pure)); +view_t sv_slice(view_t v, size_t s, size_t n); +size_t sv_split(view_t p, char on, view_t* restrict first, view_t* restrict second); + +view_t sv_from_cstr(const char* p); +view_t sv_slice_cstr(const char* p, size_t n); + +__attribute__((__gnu_inline__)) +extern inline size_t sv_split_cstr(const char* p, char on, view_t* restrict first, view_t* restrict second) +{ + return sv_split(sv_from_cstr(p), on, first, second); +} + /// Copy `view` into nul-terminated `buffer`, up to `size` bytes. Return the number of bytes that would've been copied regardless of `sz` (incl. nul-terminator) size_t sv_copy_to(size_t sz, char buffer[restrict static sz], view_t view); +char* sv_to_cstr(view_t view, char* restrict buffer, size_t n); + +__attribute__((__gnu_inline__, malloc)) +extern inline char* sv_dupto_cstr(view_t view) +{ return sv_to_cstr(view, (char*)0, 0); } + +bool sv_eq_cstr(view_t a, const char* b); +__attribute__((__gnu_inline__)) +extern inline bool sv_eq(view_t a, view_t b) +{ + return a.len == b.len && (__builtin_expect(a.ptr == b.ptr, false) || (&sv_eq)(a, b)); +} #endif /* _VIEW_H */