X-Git-Url: http://git.efficios.com/?p=argpar.git;a=blobdiff_plain;f=argpar%2Fargpar.c;h=f324d7fb130355a42ef07583cfcd520f09f2e324;hp=9e3ab7625c6f5d6f51e05d98b7d872ddcbea9da8;hb=f3ab5ca1c03d5ae622ac53b7c1da30ebd21db199;hpb=fc07e5264804963a3de6d1b929a9eb4fd37e49d4 diff --git a/argpar/argpar.c b/argpar/argpar.c index 9e3ab76..f324d7f 100644 --- a/argpar/argpar.c +++ b/argpar/argpar.c @@ -14,9 +14,13 @@ #include "argpar.h" -#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type))) -#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type))) -#define argpar_zalloc(_type) argpar_calloc(_type, 1) +#define ARGPAR_REALLOC(_ptr, _type, _nmemb) \ + ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type))) + +#define ARGPAR_CALLOC(_type, _nmemb) \ + ((_type *) calloc((_nmemb), sizeof(_type))) + +#define ARGPAR_ZALLOC(_type) ARGPAR_CALLOC(_type, 1) #define ARGPAR_ASSERT(_cond) assert(_cond) @@ -29,8 +33,8 @@ /* * An argpar iterator. * - * Such a structure contains the state of an iterator between - * calls to argpar_iter_parse_next(). + * Such a structure contains the state of an iterator between calls to + * argpar_iter_next(). */ struct argpar_iter { /* @@ -43,7 +47,7 @@ struct argpar_iter { /* * Index of the argument to process in the next - * argpar_iter_parse_next() call. + * argpar_iter_next() call. */ unsigned int i; @@ -54,20 +58,55 @@ struct argpar_iter { * Current character of the current short option group: if it's * not `NULL`, the parser is in within a short option group, * therefore it must resume there in the next - * argpar_iter_parse_next() call. + * argpar_iter_next() call. */ const char *short_opt_ch; }; +/* Base parsing item */ +struct argpar_item { + enum argpar_item_type type; +}; + +/* Option parsing item */ +struct argpar_item_opt { + struct argpar_item base; + + /* Corresponding descriptor */ + const struct argpar_opt_descr *descr; + + /* Argument, or `NULL` if none; owned by this */ + char *arg; +}; + +/* Non-option parsing item */ +struct argpar_item_non_opt { + struct argpar_item base; + + /* + * Complete argument, pointing to one of the entries of the + * original arguments (`argv`). + */ + const char *arg; + + /* + * Index of this argument amongst all original arguments + * (`argv`). + */ + unsigned int orig_index; + + /* Index of this argument amongst other non-option arguments */ + unsigned int non_opt_index; +}; + static __attribute__((format(ARGPAR_PRINTF_FORMAT, 1, 0))) -char *argpar_vasprintf(const char *fmt, va_list args) +char *argpar_vasprintf(const char * const fmt, va_list args) { int len1, len2; char *str; va_list args2; va_copy(args2, args); - len1 = vsnprintf(NULL, 0, fmt, args); if (len1 < 0) { str = NULL; @@ -80,7 +119,6 @@ char *argpar_vasprintf(const char *fmt, va_list args) } len2 = vsnprintf(str, len1 + 1, fmt, args2); - ARGPAR_ASSERT(len1 == len2); end: @@ -90,7 +128,7 @@ end: static __attribute__((format(ARGPAR_PRINTF_FORMAT, 1, 2))) -char *argpar_asprintf(const char *fmt, ...) +char *argpar_asprintf(const char * const fmt, ...) { va_list args; char *str; @@ -98,20 +136,23 @@ char *argpar_asprintf(const char *fmt, ...) va_start(args, fmt); str = argpar_vasprintf(fmt, args); va_end(args); - return str; } static __attribute__((format(ARGPAR_PRINTF_FORMAT, 2, 3))) -bool argpar_string_append_printf(char **str, const char *fmt, ...) +bool try_append_string_printf(char ** const str, const char *fmt, ...) { char *new_str = NULL; - char *addendum; + char *addendum = NULL; bool success; va_list args; - ARGPAR_ASSERT(str); + if (!str) { + success = true; + goto end; + } + ARGPAR_ASSERT(str); va_start(args, fmt); addendum = argpar_vasprintf(fmt, args); va_end(args); @@ -129,15 +170,63 @@ bool argpar_string_append_printf(char **str, const char *fmt, ...) free(*str); *str = new_str; - success = true; end: free(addendum); - return success; } +ARGPAR_HIDDEN +enum argpar_item_type argpar_item_type(const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + return item->type; +} + +ARGPAR_HIDDEN +const struct argpar_opt_descr *argpar_item_opt_descr( + const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT); + return ((const struct argpar_item_opt *) item)->descr; +} + +ARGPAR_HIDDEN +const char *argpar_item_opt_arg(const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_OPT); + return ((const struct argpar_item_opt *) item)->arg; +} + +ARGPAR_HIDDEN +const char *argpar_item_non_opt_arg(const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + return ((const struct argpar_item_non_opt *) item)->arg; +} + +ARGPAR_HIDDEN +unsigned int argpar_item_non_opt_orig_index( + const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + return ((const struct argpar_item_non_opt *) item)->orig_index; +} + +ARGPAR_HIDDEN +unsigned int argpar_item_non_opt_non_opt_index( + const struct argpar_item * const item) +{ + ARGPAR_ASSERT(item); + ARGPAR_ASSERT(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + return ((const struct argpar_item_non_opt *) item)->non_opt_index; +} + ARGPAR_HIDDEN void argpar_item_destroy(const struct argpar_item * const item) { @@ -149,7 +238,7 @@ void argpar_item_destroy(const struct argpar_item * const item) struct argpar_item_opt * const opt_item = (struct argpar_item_opt *) item; - free((void *) opt_item->arg); + free(opt_item->arg); } free((void *) item); @@ -160,7 +249,7 @@ end: static bool push_item(struct argpar_item_array * const array, - struct argpar_item * const item) + const struct argpar_item * const item) { bool success; @@ -168,11 +257,10 @@ bool push_item(struct argpar_item_array * const array, ARGPAR_ASSERT(item); if (array->n_items == array->n_alloc) { - unsigned int new_n_alloc = array->n_alloc * 2; - struct argpar_item **new_items; - - new_items = argpar_realloc(array->items, - struct argpar_item *, new_n_alloc); + const unsigned int new_n_alloc = array->n_alloc * 2; + const struct argpar_item ** const new_items = + ARGPAR_REALLOC(array->items, const struct argpar_item *, + new_n_alloc); if (!new_items) { success = false; goto end; @@ -184,7 +272,6 @@ bool push_item(struct argpar_item_array * const array, array->items[array->n_items] = item; array->n_items++; - success = true; end: @@ -207,23 +294,22 @@ void destroy_item_array(struct argpar_item_array * const array) } static -struct argpar_item_array *new_item_array(void) +struct argpar_item_array *create_item_array(void) { struct argpar_item_array *ret; const int initial_size = 10; - ret = argpar_zalloc(struct argpar_item_array); + ret = ARGPAR_ZALLOC(struct argpar_item_array); if (!ret) { goto end; } - ret->items = argpar_calloc(struct argpar_item *, initial_size); + ret->items = ARGPAR_CALLOC(const struct argpar_item *, initial_size); if (!ret->items) { goto error; } ret->n_alloc = initial_size; - goto end; error: @@ -240,7 +326,7 @@ struct argpar_item_opt *create_opt_item( const char * const arg) { struct argpar_item_opt *opt_item = - argpar_zalloc(struct argpar_item_opt); + ARGPAR_ZALLOC(struct argpar_item_opt); if (!opt_item) { goto end; @@ -272,7 +358,7 @@ struct argpar_item_non_opt *create_non_opt_item(const char * const arg, const unsigned int non_opt_index) { struct argpar_item_non_opt * const non_opt_item = - argpar_zalloc(struct argpar_item_non_opt); + ARGPAR_ZALLOC(struct argpar_item_non_opt); if (!non_opt_item) { goto end; @@ -312,8 +398,11 @@ end: enum parse_orig_arg_opt_ret { PARSE_ORIG_ARG_OPT_RET_OK, - PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -2, - PARSE_ORIG_ARG_OPT_RET_ERROR = -1, + PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -1, + PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG = -2, + PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG = -3, + PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG = -4, + PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -5, }; static @@ -330,7 +419,8 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, struct argpar_item_opt *opt_item; if (strlen(short_opts) == 0) { - argpar_string_append_printf(error, "Invalid argument"); + try_append_string_printf(error, "Invalid argument"); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG; goto error; } @@ -341,9 +431,9 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, /* Find corresponding option descriptor */ descr = find_descr(descrs, *iter->short_opt_ch, NULL); if (!descr) { + try_append_string_printf(error, "Unknown option `-%c`", + *iter->short_opt_ch); ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; - argpar_string_append_printf(error, - "Unknown option `-%c`", *iter->short_opt_ch); goto error; } @@ -361,11 +451,13 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, * We accept `-o ''` (empty option argument), but not * `-o` alone if an option argument is expected. */ - if (!opt_arg || (iter->short_opt_ch[1] && strlen(opt_arg) == 0)) { - argpar_string_append_printf(error, + if (!opt_arg || (iter->short_opt_ch[1] && + strlen(opt_arg) == 0)) { + try_append_string_printf(error, "Missing required argument for option `-%c`", *iter->short_opt_ch); used_next_orig_arg = false; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG; goto error; } } @@ -373,6 +465,7 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, /* Create and append option argument */ opt_item = create_opt_item(descr, opt_arg); if (!opt_item) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; goto error; } @@ -393,9 +486,7 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, goto end; error: - if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { - ret = PARSE_ORIG_ARG_OPT_RET_ERROR; - } + ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK); end: return ret; @@ -427,7 +518,8 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, const char *long_opt_name = long_opt_arg; if (strlen(long_opt_arg) == 0) { - argpar_string_append_printf(error, "Invalid argument"); + try_append_string_printf(error, "Invalid argument"); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG; goto error; } @@ -438,8 +530,9 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, /* Isolate the option name */ if (long_opt_name_size > max_len) { - argpar_string_append_printf(error, + try_append_string_printf(error, "Invalid argument `--%s`", long_opt_arg); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG; goto error; } @@ -451,8 +544,8 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, /* Find corresponding option descriptor */ descr = find_descr(descrs, '\0', long_opt_name); if (!descr) { - argpar_string_append_printf(error, - "Unknown option `--%s`", long_opt_name); + try_append_string_printf(error, "Unknown option `--%s`", + long_opt_name); ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; goto error; } @@ -465,9 +558,10 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, } else { /* `--long-opt arg` style */ if (!next_orig_arg) { - argpar_string_append_printf(error, + try_append_string_printf(error, "Missing required argument for option `--%s`", long_opt_name); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG; goto error; } @@ -479,8 +573,9 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, * Unexpected `--opt=arg` style for a long option which * doesn't accept an argument. */ - argpar_string_append_printf(error, + try_append_string_printf(error, "Unexpected argument for option `--%s`", long_opt_name); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG; goto error; } @@ -500,9 +595,7 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, goto end; error: - if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { - ret = PARSE_ORIG_ARG_OPT_RET_ERROR; - } + ARGPAR_ASSERT(ret != PARSE_ORIG_ARG_OPT_RET_OK); end: return ret; @@ -512,8 +605,7 @@ static enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, const char * const next_orig_arg, const struct argpar_opt_descr * const descrs, - struct argpar_iter * const iter, - char ** const error, + struct argpar_iter * const iter, char ** const error, struct argpar_item ** const item) { enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; @@ -534,15 +626,18 @@ enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, } static -bool prepend_while_parsing_arg_to_error(char **error, +bool try_prepend_while_parsing_arg_to_error(char ** const error, const unsigned int i, const char * const arg) { char *new_error; bool success; - ARGPAR_ASSERT(error); - ARGPAR_ASSERT(*error); + if (!error) { + success = true; + goto end; + } + ARGPAR_ASSERT(*error); new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s", i + 1, arg, *error); if (!new_error) { @@ -563,7 +658,7 @@ struct argpar_iter *argpar_iter_create(const unsigned int argc, const char * const * const argv, const struct argpar_opt_descr * const descrs) { - struct argpar_iter * const iter = argpar_zalloc(struct argpar_iter); + struct argpar_iter * const iter = ARGPAR_ZALLOC(struct argpar_iter); if (!iter) { goto end; @@ -584,21 +679,23 @@ void argpar_iter_destroy(struct argpar_iter * const iter) } ARGPAR_HIDDEN -enum argpar_iter_parse_next_status argpar_iter_parse_next( +enum argpar_iter_next_status argpar_iter_next( struct argpar_iter * const iter, - const struct argpar_item ** const item, - char ** const error) + const struct argpar_item ** const item, char ** const error) { - enum argpar_iter_parse_next_status status; + enum argpar_iter_next_status status; enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret; const char *orig_arg; const char *next_orig_arg; ARGPAR_ASSERT(iter->i <= iter->argc); - *error = NULL; + + if (error) { + *error = NULL; + } if (iter->i == iter->argc) { - status = ARGPAR_ITER_PARSE_NEXT_STATUS_END; + status = ARGPAR_ITER_NEXT_STATUS_END; goto end; } @@ -613,14 +710,14 @@ enum argpar_iter_parse_next_status argpar_iter_parse_next( iter->non_opt_index); if (!non_opt_item) { - status = ARGPAR_ITER_PARSE_NEXT_STATUS_ERROR; + status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY; goto end; } iter->non_opt_index++; iter->i++; *item = &non_opt_item->base; - status = ARGPAR_ITER_PARSE_NEXT_STATUS_OK; + status = ARGPAR_ITER_NEXT_STATUS_OK; goto end; } @@ -630,15 +727,35 @@ enum argpar_iter_parse_next_status argpar_iter_parse_next( (struct argpar_item **) item); switch (parse_orig_arg_opt_ret) { case PARSE_ORIG_ARG_OPT_RET_OK: - status = ARGPAR_ITER_PARSE_NEXT_STATUS_OK; + status = ARGPAR_ITER_NEXT_STATUS_OK; break; case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT: - prepend_while_parsing_arg_to_error(error, iter->i, orig_arg); - status = ARGPAR_ITER_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT; + case PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG: + case PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG: + case PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG: + try_prepend_while_parsing_arg_to_error(error, iter->i, + orig_arg); + + switch (parse_orig_arg_opt_ret) { + case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT: + status = ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT; + break; + case PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG: + status = ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG; + break; + case PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG: + status = ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG; + break; + case PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG: + status = ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG; + break; + default: + abort(); + } + break; - case PARSE_ORIG_ARG_OPT_RET_ERROR: - prepend_while_parsing_arg_to_error(error, iter->i, orig_arg); - status = ARGPAR_ITER_PARSE_NEXT_STATUS_ERROR; + case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY: + status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY; break; default: abort(); @@ -649,7 +766,7 @@ end: } ARGPAR_HIDDEN -unsigned int argpar_iter_get_ingested_orig_args( +unsigned int argpar_iter_ingested_orig_args( const struct argpar_iter * const iter) { return iter->i; @@ -665,7 +782,7 @@ struct argpar_parse_ret argpar_parse(const unsigned int argc, const struct argpar_item *item = NULL; struct argpar_iter *iter = NULL; - parse_ret.items = new_item_array(); + parse_ret.items = create_item_array(); if (!parse_ret.items) { parse_ret.error = strdup("Failed to create items array."); ARGPAR_ASSERT(parse_ret.error); @@ -680,37 +797,42 @@ struct argpar_parse_ret argpar_parse(const unsigned int argc, } while (true) { - enum argpar_iter_parse_next_status status; - - status = argpar_iter_parse_next(iter, &item, &parse_ret.error); - if (status == ARGPAR_ITER_PARSE_NEXT_STATUS_ERROR) { + const enum argpar_iter_next_status status = + argpar_iter_next(iter, &item, &parse_ret.error); + + switch (status) { + case ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG: + case ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG: + case ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG: + case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY: goto error; - } else if (status == ARGPAR_ITER_PARSE_NEXT_STATUS_END) { - break; - } else if (status == ARGPAR_ITER_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + case ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT: if (fail_on_unknown_opt) { parse_ret.ingested_orig_args = - argpar_iter_get_ingested_orig_args(iter); + argpar_iter_ingested_orig_args(iter); goto error; } free(parse_ret.error); parse_ret.error = NULL; + goto success; + case ARGPAR_ITER_NEXT_STATUS_END: + goto success; + default: + ARGPAR_ASSERT(status == ARGPAR_ITER_NEXT_STATUS_OK); break; } - ARGPAR_ASSERT(status == ARGPAR_ITER_PARSE_NEXT_STATUS_OK); - - if (!push_item(parse_ret.items, (void *) item)) { + if (!push_item(parse_ret.items, item)) { goto error; } item = NULL; } +success: ARGPAR_ASSERT(!parse_ret.error); - parse_ret.ingested_orig_args = - argpar_iter_get_ingested_orig_args(iter); + parse_ret.ingested_orig_args = argpar_iter_ingested_orig_args(iter); goto end; error: @@ -727,13 +849,11 @@ end: } ARGPAR_HIDDEN -void argpar_parse_ret_fini(struct argpar_parse_ret *ret) +void argpar_parse_ret_fini(struct argpar_parse_ret * const ret) { ARGPAR_ASSERT(ret); - destroy_item_array(ret->items); ret->items = NULL; - free(ret->error); ret->error = NULL; }