X-Git-Url: http://git.efficios.com/?p=argpar.git;a=blobdiff_plain;f=argpar%2Fargpar.c;h=79a22f565bf99add9bb16b1eda572cd4b525526b;hp=58cbc2b9d917203be676d58e7259bb8518cf0529;hb=8b95d883334b188d05cb1967980eae628e295d10;hpb=dd757a651292048da829dd6fa085c1f107569bcc diff --git a/argpar/argpar.c b/argpar/argpar.c index 58cbc2b..79a22f5 100644 --- a/argpar/argpar.c +++ b/argpar/argpar.c @@ -55,12 +55,18 @@ struct argpar_iter { int non_opt_index; /* - * 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_next() call. + * Current character within the current short option group: if + * it's not `NULL`, the parser is within a short option group, + * therefore it must resume there in the next argpar_iter_next() + * call. */ - const char *short_opt_ch; + const char *short_opt_group_ch; + + /* Temporary character buffer which only grows */ + struct { + size_t size; + char *data; + } tmp_buf; }; /* Base parsing item */ @@ -99,83 +105,20 @@ struct argpar_item_non_opt { unsigned int non_opt_index; }; -static __attribute__((format(ARGPAR_PRINTF_FORMAT, 1, 0))) -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; - goto end; - } - - str = malloc(len1 + 1); - if (!str) { - goto end; - } - - len2 = vsnprintf(str, len1 + 1, fmt, args2); - ARGPAR_ASSERT(len1 == len2); - -end: - va_end(args2); - return str; -} - - -static __attribute__((format(ARGPAR_PRINTF_FORMAT, 1, 2))) -char *argpar_asprintf(const char * const fmt, ...) -{ - va_list args; - char *str; - - va_start(args, fmt); - str = argpar_vasprintf(fmt, args); - va_end(args); - return str; -} - -static __attribute__((format(ARGPAR_PRINTF_FORMAT, 2, 3))) -bool try_append_string_printf(char ** const str, const char *fmt, ...) -{ - char *new_str = NULL; - char *addendum = NULL; - bool success; - va_list args; - - if (!str) { - success = true; - goto end; - } - - ARGPAR_ASSERT(str); - va_start(args, fmt); - addendum = argpar_vasprintf(fmt, args); - va_end(args); - - if (!addendum) { - success = false; - goto end; - } +/* Parsing error */ +struct argpar_error { + /* Original argument index */ + unsigned int orig_index; - new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum); - if (!new_str) { - success = false; - goto end; - } + /* Name of unknown option; owned by this */ + char *unknown_opt_name; - free(*str); - *str = new_str; - success = true; + /* Option descriptor */ + const struct argpar_opt_descr *opt_descr; -end: - free(addendum); - return success; -} + /* `true` if a short option caused the error */ + bool is_short; +}; ARGPAR_HIDDEN enum argpar_item_type argpar_item_type(const struct argpar_item * const item) @@ -247,79 +190,6 @@ end: return; } -static -bool push_item(struct argpar_item_array * const array, - const struct argpar_item * const item) -{ - bool success; - - ARGPAR_ASSERT(array); - ARGPAR_ASSERT(item); - - if (array->n_items == array->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; - } - - array->n_alloc = new_n_alloc; - array->items = new_items; - } - - array->items[array->n_items] = item; - array->n_items++; - success = true; - -end: - return success; -} - -static -void destroy_item_array(struct argpar_item_array * const array) -{ - if (array) { - unsigned int i; - - for (i = 0; i < array->n_items; i++) { - argpar_item_destroy(array->items[i]); - } - - free(array->items); - free(array); - } -} - -static -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); - if (!ret) { - goto end; - } - - ret->items = ARGPAR_CALLOC(const struct argpar_item *, initial_size); - if (!ret->items) { - goto error; - } - - ret->n_alloc = initial_size; - goto end; - -error: - destroy_item_array(ret); - ret = NULL; - -end: - return ret; -} - static struct argpar_item_opt *create_opt_item( const struct argpar_opt_descr * const descr, @@ -373,6 +243,101 @@ end: return non_opt_item; } +/* + * If `error` is not `NULL`, sets the error `error` to a new parsing + * error object, setting its `unknown_opt_name`, `opt_descr`, and + * `is_short` members from the parameters. + * + * `unknown_opt_name` is the unknown option name without any `-` or `--` + * prefix: `is_short` controls which type of unknown option it is. + * + * Returns 0 on success (including if `error` is `NULL`) or -1 on memory + * error. + */ +static +int set_error(struct argpar_error ** const error, + const char * const unknown_opt_name, + const struct argpar_opt_descr * const opt_descr, + const bool is_short) +{ + int ret = 0; + + if (!error) { + goto end; + } + + *error = ARGPAR_ZALLOC(struct argpar_error); + if (!*error) { + goto error; + } + + if (unknown_opt_name) { + (*error)->unknown_opt_name = ARGPAR_CALLOC(char, + strlen(unknown_opt_name) + 1 + is_short ? 1 : 2); + if (!(*error)->unknown_opt_name) { + goto error; + } + + if (is_short) { + strcpy((*error)->unknown_opt_name, "-"); + } else { + strcpy((*error)->unknown_opt_name, "--"); + } + + strcat((*error)->unknown_opt_name, unknown_opt_name); + } + + (*error)->opt_descr = opt_descr; + (*error)->is_short = is_short; + goto end; + +error: + argpar_error_destroy(*error); + ret = -1; + +end: + return ret; +} + +ARGPAR_HIDDEN +unsigned int argpar_error_orig_index(const struct argpar_error * const error) +{ + ARGPAR_ASSERT(error); + return error->orig_index; +} + +ARGPAR_HIDDEN +const char *argpar_error_unknown_opt_name( + const struct argpar_error * const error) +{ + ARGPAR_ASSERT(error); + ARGPAR_ASSERT(error->unknown_opt_name); + return error->unknown_opt_name; +} + +ARGPAR_HIDDEN +const struct argpar_opt_descr *argpar_error_opt_descr( + const struct argpar_error * const error, bool * const is_short) +{ + ARGPAR_ASSERT(error); + ARGPAR_ASSERT(error->opt_descr); + + if (is_short) { + *is_short = error->is_short; + } + + return error->opt_descr; +} + +ARGPAR_HIDDEN +void argpar_error_destroy(const struct argpar_error * const error) +{ + if (error) { + free(error->unknown_opt_name); + free((void *) error); + } +} + static const struct argpar_opt_descr *find_descr( const struct argpar_opt_descr * const descrs, @@ -400,17 +365,18 @@ enum parse_orig_arg_opt_ret { PARSE_ORIG_ARG_OPT_RET_OK, 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 -enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, +enum parse_orig_arg_opt_ret parse_short_opt_group( + const char * const short_opt_group, const char * const next_orig_arg, const struct argpar_opt_descr * const descrs, struct argpar_iter * const iter, - char ** const error, struct argpar_item ** const item) + struct argpar_error ** const error, + struct argpar_item ** const item) { enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; bool used_next_orig_arg = false; @@ -418,25 +384,31 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, const struct argpar_opt_descr *descr; struct argpar_item_opt *opt_item; - ARGPAR_ASSERT(strlen(short_opts) != 0); + ARGPAR_ASSERT(strlen(short_opt_group) != 0); - if (!iter->short_opt_ch) { - iter->short_opt_ch = short_opts; + if (!iter->short_opt_group_ch) { + iter->short_opt_group_ch = short_opt_group; } /* Find corresponding option descriptor */ - descr = find_descr(descrs, *iter->short_opt_ch, NULL); + descr = find_descr(descrs, *iter->short_opt_group_ch, NULL); if (!descr) { - try_append_string_printf(error, "Unknown option `-%c`", - *iter->short_opt_ch); + const char unknown_opt_name[] = + {*iter->short_opt_group_ch, '\0'}; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + + if (set_error(error, unknown_opt_name, NULL, true)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } if (descr->with_arg) { - if (iter->short_opt_ch[1]) { + if (iter->short_opt_group_ch[1]) { /* `-oarg` form */ - opt_arg = &iter->short_opt_ch[1]; + opt_arg = &iter->short_opt_group_ch[1]; } else { /* `-o arg` form */ opt_arg = next_orig_arg; @@ -447,13 +419,14 @@ 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] && + if (!opt_arg || (iter->short_opt_group_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; + + if (set_error(error, NULL, descr, true)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } } @@ -466,11 +439,11 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, } *item = &opt_item->base; - iter->short_opt_ch++; + iter->short_opt_group_ch++; - if (descr->with_arg || !*iter->short_opt_ch) { + if (descr->with_arg || !*iter->short_opt_group_ch) { /* Option has an argument: no more options */ - iter->short_opt_ch = NULL; + iter->short_opt_group_ch = NULL; if (used_next_orig_arg) { iter->i += 2; @@ -493,9 +466,9 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, const char * const next_orig_arg, const struct argpar_opt_descr * const descrs, struct argpar_iter * const iter, - char ** const error, struct argpar_item ** const item) + struct argpar_error ** const error, + struct argpar_item ** const item) { - const size_t max_len = 127; enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; const struct argpar_opt_descr *descr; struct argpar_item_opt *opt_item; @@ -507,9 +480,6 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, /* Position of first `=`, if any */ const char *eq_pos; - /* Buffer holding option name when `long_opt_arg` contains `=` */ - char buf[max_len + 1]; - /* Option name */ const char *long_opt_name = long_opt_arg; @@ -521,24 +491,30 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, const size_t long_opt_name_size = eq_pos - long_opt_arg; /* Isolate the option name */ - if (long_opt_name_size > max_len) { - try_append_string_printf(error, - "Invalid argument `--%s`", long_opt_arg); - ret = PARSE_ORIG_ARG_OPT_RET_ERROR_INVALID_ARG; - goto error; + while (long_opt_name_size > iter->tmp_buf.size - 1) { + iter->tmp_buf.size *= 2; + iter->tmp_buf.data = ARGPAR_REALLOC(iter->tmp_buf.data, + char, iter->tmp_buf.size); + if (!iter->tmp_buf.data) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + goto error; + } } - memcpy(buf, long_opt_arg, long_opt_name_size); - buf[long_opt_name_size] = '\0'; - long_opt_name = buf; + memcpy(iter->tmp_buf.data, long_opt_arg, long_opt_name_size); + iter->tmp_buf.data[long_opt_name_size] = '\0'; + long_opt_name = iter->tmp_buf.data; } /* Find corresponding option descriptor */ descr = find_descr(descrs, '\0', long_opt_name); if (!descr) { - try_append_string_printf(error, "Unknown option `--%s`", - long_opt_name); ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + + if (set_error(error, long_opt_name, NULL, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -550,10 +526,12 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, } else { /* `--long-opt arg` style */ if (!next_orig_arg) { - try_append_string_printf(error, - "Missing required argument for option `--%s`", - long_opt_name); ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG; + + if (set_error(error, NULL, descr, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -565,9 +543,12 @@ 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. */ - try_append_string_printf(error, - "Unexpected argument for option `--%s`", long_opt_name); ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNEXPECTED_OPT_ARG; + + if (set_error(error, NULL, descr, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -597,7 +578,8 @@ 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, + struct argpar_error ** const error, struct argpar_item ** const item) { enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; @@ -610,47 +592,19 @@ enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, next_orig_arg, descrs, iter, error, item); } else { /* Short option */ - ret = parse_short_opts(&orig_arg[1], + ret = parse_short_opt_group(&orig_arg[1], next_orig_arg, descrs, iter, error, item); } return ret; } -static -bool try_prepend_while_parsing_arg_to_error(char ** const error, - const unsigned int i, const char * const arg) -{ - char *new_error; - bool success; - - 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) { - success = false; - goto end; - } - - free(*error); - *error = new_error; - success = true; - -end: - return success; -} - ARGPAR_HIDDEN 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 *iter = ARGPAR_ZALLOC(struct argpar_iter); if (!iter) { goto end; @@ -659,6 +613,13 @@ struct argpar_iter *argpar_iter_create(const unsigned int argc, iter->argc = argc; iter->argv = argv; iter->descrs = descrs; + iter->tmp_buf.size = 128; + iter->tmp_buf.data = ARGPAR_CALLOC(char, iter->tmp_buf.size); + if (!iter->tmp_buf.data) { + argpar_iter_destroy(iter); + iter = NULL; + goto end; + } end: return iter; @@ -667,23 +628,28 @@ end: ARGPAR_HIDDEN void argpar_iter_destroy(struct argpar_iter * const iter) { - free(iter); + if (iter) { + free(iter->tmp_buf.data); + free(iter); + } } ARGPAR_HIDDEN 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, + const struct argpar_error ** const error) { 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; + struct argpar_error ** const nc_error = (struct argpar_error **) error; ARGPAR_ASSERT(iter->i <= iter->argc); if (error) { - *error = NULL; + *nc_error = NULL; } if (iter->i == iter->argc) { @@ -716,7 +682,7 @@ enum argpar_iter_next_status argpar_iter_next( /* Option argument */ parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg, - next_orig_arg, iter->descrs, iter, error, + next_orig_arg, iter->descrs, iter, nc_error, (struct argpar_item **) item); switch (parse_orig_arg_opt_ret) { case PARSE_ORIG_ARG_OPT_RET_OK: @@ -724,10 +690,11 @@ enum argpar_iter_next_status argpar_iter_next( break; case PARSE_ORIG_ARG_OPT_RET_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); + if (error) { + ARGPAR_ASSERT(*error); + (*nc_error)->orig_index = iter->i; + } switch (parse_orig_arg_opt_ret) { case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT: @@ -736,9 +703,6 @@ enum argpar_iter_next_status argpar_iter_next( 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; @@ -764,89 +728,3 @@ unsigned int argpar_iter_ingested_orig_args( { return iter->i; } - -ARGPAR_HIDDEN -struct argpar_parse_ret argpar_parse(const unsigned int argc, - const char * const * const argv, - const struct argpar_opt_descr * const descrs, - const bool fail_on_unknown_opt) -{ - struct argpar_parse_ret parse_ret = { 0 }; - const struct argpar_item *item = NULL; - struct argpar_iter *iter = NULL; - - parse_ret.items = create_item_array(); - if (!parse_ret.items) { - parse_ret.error = strdup("Failed to create items array."); - ARGPAR_ASSERT(parse_ret.error); - goto error; - } - - iter = argpar_iter_create(argc, argv, descrs); - if (!iter) { - parse_ret.error = strdup("Failed to create argpar iter."); - ARGPAR_ASSERT(parse_ret.error); - goto error; - } - - while (true) { - 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; - case ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT: - if (fail_on_unknown_opt) { - parse_ret.ingested_orig_args = - 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; - } - - if (!push_item(parse_ret.items, item)) { - goto error; - } - - item = NULL; - } - -success: - ARGPAR_ASSERT(!parse_ret.error); - parse_ret.ingested_orig_args = argpar_iter_ingested_orig_args(iter); - goto end; - -error: - ARGPAR_ASSERT(parse_ret.error); - - /* That's how we indicate that an error occurred */ - destroy_item_array(parse_ret.items); - parse_ret.items = NULL; - -end: - argpar_iter_destroy(iter); - argpar_item_destroy(item); - return parse_ret; -} - -ARGPAR_HIDDEN -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; -}