X-Git-Url: http://git.efficios.com/?p=argpar.git;a=blobdiff_plain;f=argpar%2Fargpar.c;h=10f6ee8d42e93a780a65ad4c5cf66347d667c42d;hp=9112e4c359b8a4bfaeae625ae6bc7cebd8b1c406;hb=10aefab2119c89f4afa4f46a619eae20d6be7512;hpb=d7a82d7fcdd5d975b716da6e2907f04cbceb9352 diff --git a/argpar/argpar.c b/argpar/argpar.c index 9112e4c..10f6ee8 100644 --- a/argpar/argpar.c +++ b/argpar/argpar.c @@ -24,12 +24,6 @@ #define ARGPAR_ASSERT(_cond) assert(_cond) -#ifdef __MINGW_PRINTF_FORMAT -# define ARGPAR_PRINTF_FORMAT __MINGW_PRINTF_FORMAT -#else -# define ARGPAR_PRINTF_FORMAT printf -#endif - /* * An argpar iterator. * @@ -41,9 +35,11 @@ struct argpar_iter { * Data provided by the user to argpar_iter_create(); immutable * afterwards. */ - unsigned int argc; - const char * const *argv; - const struct argpar_opt_descr *descrs; + struct { + unsigned int argc; + const char * const *argv; + const struct argpar_opt_descr *descrs; + } user; /* * Index of the argument to process in the next @@ -105,83 +101,23 @@ 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); +/* Parsing error */ +struct argpar_error { + /* Error type */ + enum argpar_error_type type; - if (!addendum) { - success = false; - goto end; - } + /* 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) @@ -253,6 +189,12 @@ end: return; } +/* + * Creates and returns an option parsing item for the descriptor `descr` + * and having the argument `arg` (copied; may be `NULL`). + * + * Returns `NULL` on memory error. + */ static struct argpar_item_opt *create_opt_item( const struct argpar_opt_descr * const descr, @@ -285,6 +227,13 @@ end: return opt_item; } +/* + * Creates and returns a non-option parsing item for the original + * argument `arg` having the original index `orig_index` and the + * non-option index `non_opt_index`. + * + * Returns `NULL` on memory error. + */ static struct argpar_item_non_opt *create_non_opt_item(const char * const arg, const unsigned int orig_index, @@ -306,6 +255,126 @@ 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, + enum argpar_error_type type, + 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; + } + + (*error)->type = type; + + 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 +enum argpar_error_type argpar_error_type( + const struct argpar_error * const error) +{ + ARGPAR_ASSERT(error); + return error->type; +} + +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->type == ARGPAR_ERROR_TYPE_UNKNOWN_OPT); + 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->type == ARGPAR_ERROR_TYPE_MISSING_OPT_ARG || + error->type == ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG); + 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); + } +} + +/* + * Finds and returns the _first_ descriptor having the short option name + * `short_name` or the long option name `long_name` within the option + * descriptors `descrs`. + * + * `short_name` may be `'\0'` to not consider it. + * + * `long_name` may be `NULL` to not consider it. + * + * Returns `NULL` if no descriptor is found. + */ static const struct argpar_opt_descr *find_descr( const struct argpar_opt_descr * const descrs, @@ -329,21 +398,30 @@ end: return !descr->short_name && !descr->long_name ? NULL : descr; } +/* Return type of parse_short_opt_group() and parse_long_opt() */ 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_UNEXPECTED_OPT_ARG = -4, - PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -5, + PARSE_ORIG_ARG_OPT_RET_ERROR = -1, + PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY = -2, }; +/* + * Parses the short option group argument `short_opt_group`, starting + * where needed depending on the state of `iter`. + * + * On success, sets `*item`. + * + * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets + * `*error`. + */ static 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; @@ -360,9 +438,16 @@ enum parse_orig_arg_opt_ret parse_short_opt_group( /* Find corresponding option descriptor */ descr = find_descr(descrs, *iter->short_opt_group_ch, NULL); if (!descr) { - try_append_string_printf(error, "Unknown option `-%c`", - *iter->short_opt_group_ch); - ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + const char unknown_opt_name[] = + {*iter->short_opt_group_ch, '\0'}; + + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + + if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT, + unknown_opt_name, NULL, true)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -382,11 +467,13 @@ enum parse_orig_arg_opt_ret parse_short_opt_group( */ 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_group_ch); - used_next_orig_arg = false; - ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MISSING_OPT_ARG; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + + if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG, + NULL, descr, true)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } } @@ -421,12 +508,21 @@ end: return ret; } +/* + * Parses the long option argument `long_opt_arg`. + * + * On success, sets `*item`. + * + * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets + * `*error`. + */ static 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) { enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; const struct argpar_opt_descr *descr; @@ -468,9 +564,13 @@ 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) { - try_append_string_printf(error, "Unknown option `--%s`", - long_opt_name); - ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + + if (set_error(error, ARGPAR_ERROR_TYPE_UNKNOWN_OPT, + long_opt_name, NULL, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -482,10 +582,13 @@ 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; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + + if (set_error(error, ARGPAR_ERROR_TYPE_MISSING_OPT_ARG, + NULL, descr, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -497,9 +600,13 @@ 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; + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + + if (set_error(error, ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG, + NULL, descr, false)) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY; + } + goto error; } @@ -525,11 +632,20 @@ end: return ret; } +/* + * Parses the original argument `orig_arg`. + * + * On success, sets `*item`. + * + * On error (except for `PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY`), sets + * `*error`. + */ 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; @@ -549,34 +665,6 @@ enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, 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, @@ -588,9 +676,9 @@ struct argpar_iter *argpar_iter_create(const unsigned int argc, goto end; } - iter->argc = argc; - iter->argv = argv; - iter->descrs = descrs; + iter->user.argc = argc; + iter->user.argv = argv; + iter->user.descrs = descrs; iter->tmp_buf.size = 128; iter->tmp_buf.data = ARGPAR_CALLOC(char, iter->tmp_buf.size); if (!iter->tmp_buf.data) { @@ -615,27 +703,30 @@ void argpar_iter_destroy(struct argpar_iter * const 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); + ARGPAR_ASSERT(iter->i <= iter->user.argc); if (error) { - *error = NULL; + *nc_error = NULL; } - if (iter->i == iter->argc) { + if (iter->i == iter->user.argc) { status = ARGPAR_ITER_NEXT_STATUS_END; goto end; } - orig_arg = iter->argv[iter->i]; + orig_arg = iter->user.argv[iter->i]; next_orig_arg = - iter->i < (iter->argc - 1) ? iter->argv[iter->i + 1] : NULL; + iter->i < (iter->user.argc - 1) ? + iter->user.argv[iter->i + 1] : NULL; if (strcmp(orig_arg, "-") == 0 || strcmp(orig_arg, "--") == 0 || orig_arg[0] != '-') { @@ -658,32 +749,18 @@ 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->user.descrs, iter, nc_error, (struct argpar_item **) item); switch (parse_orig_arg_opt_ret) { case PARSE_ORIG_ARG_OPT_RET_OK: status = ARGPAR_ITER_NEXT_STATUS_OK; 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_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_UNEXPECTED_OPT_ARG: - status = ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG; - break; - default: - abort(); + case PARSE_ORIG_ARG_OPT_RET_ERROR: + if (error) { + ARGPAR_ASSERT(*error); + (*nc_error)->orig_index = iter->i; } - + status = ARGPAR_ITER_NEXT_STATUS_ERROR; break; case PARSE_ORIG_ARG_OPT_RET_ERROR_MEMORY: status = ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY;