diff --git a/includes/internal/cli/messages.h b/includes/internal/cli/messages.h index deb8375..2d00b33 100644 --- a/includes/internal/cli/messages.h +++ b/includes/internal/cli/messages.h @@ -4,4 +4,9 @@ void print_help(void); void print_version(void); +void error_unknown_opt(const char *current_opt); +void error_invalid_arg(const char *current_opt); +void error_invalid_opt(const char *current_opt); +void error_duplicate_opt(const char *current_opt); + #endif diff --git a/includes/internal/cli/options.h b/includes/internal/cli/options.h index c6e761c..7f78416 100644 --- a/includes/internal/cli/options.h +++ b/includes/internal/cli/options.h @@ -1,6 +1,20 @@ #ifndef PING_CLI_OPT_H #define PING_CLI_OPT_H +#include +#include "ft_ping_flags.h" + +#define TRACK_DUP_OPT(descriptor, tracker, opt_name, opts_array) \ + do { \ + bitmask_index = (size_t)((descriptor) - (opts_array)); \ + if (HAS_FLAG(tracker, (1ULL << bitmask_index))) \ + { \ + error_duplicate_opt(opt_name); \ + return CLI_ERROR; \ + } \ + SET_FLAG(tracker, (1ULL << bitmask_index)); \ + } while (0) + #define CLI_OPTIONS_LIST \ X( \ 'h', \ @@ -87,7 +101,6 @@ #define X(short_opt, long_opt, has_arg, handler, arg_type, desc) + 1 enum { CLI_OPT_LEN = (0 CLI_OPTIONS_LIST) }; -#include #undef X #define X(short_opt, long_opt, has_arg, handler, arg_type, desc) \ + (1 * (has_arg == no_argument) \ diff --git a/sources.mk b/sources.mk index b652f44..17a4a75 100644 --- a/sources.mk +++ b/sources.mk @@ -18,6 +18,7 @@ SRCS = $(SRC_DIR)/main.c \ $(SRC_DIR)/cli/parse_utils/get_optstr.c \ $(SRC_DIR)/cli/messages/help.c \ $(SRC_DIR)/cli/messages/version.c \ + $(SRC_DIR)/cli/messages/error.c \ TESTS_DIR = tests TESTS = $(TESTS_DIR)/test_main.c \ diff --git a/src/cli/handlers/handle_count.c b/src/cli/handlers/handle_count.c index 48c0ddc..4551b9c 100644 --- a/src/cli/handlers/handle_count.c +++ b/src/cli/handlers/handle_count.c @@ -1,4 +1,3 @@ -#include #include #include "cli.h" @@ -11,11 +10,7 @@ cli_handle_count(const char *arg, t_ping_config *config) uint64_t count; if (0 != cli_parse_uint64(arg, &count)) - { - dprintf(STDERR_FILENO, "Invalide count\n"); return CLI_ERROR; - } - config->count = count; return CLI_SUCCESS; } diff --git a/src/cli/handlers/handle_interval.c b/src/cli/handlers/handle_interval.c index 74d774b..d578b99 100644 --- a/src/cli/handlers/handle_interval.c +++ b/src/cli/handlers/handle_interval.c @@ -3,7 +3,6 @@ #include "internal/cli/parse_utils.h" #include -#include int cli_handle_interval(const char *arg, t_ping_config *config) @@ -11,10 +10,7 @@ cli_handle_interval(const char *arg, t_ping_config *config) float interval; if (0 != cli_parse_float(arg, &interval)) - { - dprintf(STDERR_FILENO, "Invalide interval\n"); return CLI_ERROR; - } config->interval = interval; return CLI_SUCCESS; diff --git a/src/cli/handlers/handle_size.c b/src/cli/handlers/handle_size.c index d06c951..5377dbb 100644 --- a/src/cli/handlers/handle_size.c +++ b/src/cli/handlers/handle_size.c @@ -2,7 +2,6 @@ #include "internal/cli/arg_handler.h" #include "internal/cli/parse_utils.h" -#include #include int @@ -11,10 +10,7 @@ cli_handle_size(const char *arg, t_ping_config *config) uint64_t size; if (0 != cli_parse_uint64(arg, &size) || size > (65535 - 8)) - { - dprintf(STDERR_FILENO, "Invalide size\n"); return CLI_ERROR; - } config->packet_size = size; return CLI_SUCCESS; diff --git a/src/cli/handlers/handle_timeout.c b/src/cli/handlers/handle_timeout.c index ee10581..a04c788 100644 --- a/src/cli/handlers/handle_timeout.c +++ b/src/cli/handlers/handle_timeout.c @@ -3,7 +3,6 @@ #include "internal/cli/parse_utils.h" #include -#include int cli_handle_timeout(const char *arg, t_ping_config *config) @@ -11,10 +10,7 @@ cli_handle_timeout(const char *arg, t_ping_config *config) float TO; if (0 != cli_parse_float(arg, &TO)) - { - dprintf(STDERR_FILENO, "Invalide timeout\n"); return CLI_ERROR; - } config->timeout = TO; return CLI_SUCCESS; diff --git a/src/cli/handlers/handle_ttl.c b/src/cli/handlers/handle_ttl.c index 6562b07..f23ea4e 100644 --- a/src/cli/handlers/handle_ttl.c +++ b/src/cli/handlers/handle_ttl.c @@ -2,7 +2,6 @@ #include "internal/cli/arg_handler.h" #include "internal/cli/parse_utils.h" -#include #include int @@ -11,10 +10,7 @@ cli_handle_ttl(const char *arg, t_ping_config *config) uint64_t ttl; if (0 != cli_parse_uint64(arg, &ttl) || ttl > 255) - { - dprintf(STDERR_FILENO, "Invalide ttl\n"); return CLI_ERROR; - } config->ttl = (uint8_t)ttl; return CLI_SUCCESS; diff --git a/src/cli/messages/error.c b/src/cli/messages/error.c index e69de29..20beb55 100644 --- a/src/cli/messages/error.c +++ b/src/cli/messages/error.c @@ -0,0 +1,43 @@ +#include +#include +#include "version_gen.h" + +#define CMD_NAME g_prog_name.name + +/* Forward declarations */ +void static inline print_suggest_help(void); +/* -------------------- */ + +void +error_unknown_opt(const char *current_opt) +{ + dprintf(STDERR_FILENO, "%s: unknown option -- '%s'\n", CMD_NAME, current_opt); + print_suggest_help(); +} + +void +error_invalid_arg(const char *current_opt) +{ + dprintf(STDERR_FILENO, "%s: option '%s' requires an argument\n", CMD_NAME, current_opt); + print_suggest_help(); +} + +void +error_invalid_opt(const char *current_opt) +{ + dprintf(STDERR_FILENO, "%s: invalid option '%s'\n", CMD_NAME, current_opt); + print_suggest_help(); +} + +void +error_duplicate_opt(const char *current_opt) +{ + dprintf(STDERR_FILENO, "%s: duplicate option '%s'\n", CMD_NAME, current_opt); + print_suggest_help(); +} + +void static inline +print_suggest_help(void) +{ + dprintf(STDERR_FILENO, "Try '%s --help' for more information.\n", CMD_NAME); +} diff --git a/src/cli/parse.c b/src/cli/parse.c index c970615..224e7a0 100644 --- a/src/cli/parse.c +++ b/src/cli/parse.c @@ -1,10 +1,13 @@ #include +#include #include #include +#include #include "cli.h" -#include "internal/cli/arg_handler.h" +#include "internal/cli/messages.h" #include "internal/cli/options.h" +#include "internal/cli/arg_handler.h" #include "ft_ping_const.h" /* Map -? to -h to support help shortcut */ @@ -18,14 +21,15 @@ static void init_config(t_ping_config *config); static void build_long_options(struct option *long_opts, size_t nb_opts, const t_option_descriptor *src); -static const t_option_descriptor *find_option_handler(int opt); -static int handle_one_option(int opt, char *arg, t_ping_config *config); +static inline const t_option_descriptor *find_option_handler(int opt); +static int handle_one_option(int opt, char **argv, t_ping_config *config, uint64_t *opt_tacker); /* ------------------- */ enum e_cli_code cli_parse_arguments(int argc, char **argv, t_ping_config *config) { struct option long_opts[CLI_OPT_LEN + 1]; + uint64_t tracker = 0; int opt; int res; @@ -34,7 +38,7 @@ cli_parse_arguments(int argc, char **argv, t_ping_config *config) while (-1 != (opt = getopt_long(argc, argv, cli_get_optstr(), long_opts, NULL))) { HANDLE_QUESTION_MARK(opt); - res = handle_one_option(opt, optarg, config); + res = handle_one_option(opt, argv, config, &tracker); if (0 != res) return res; } @@ -42,18 +46,29 @@ cli_parse_arguments(int argc, char **argv, t_ping_config *config) } static int -handle_one_option(int opt, char *arg, t_ping_config *config) +handle_one_option(int opt, char **argv, t_ping_config *config, uint64_t *opt_tracker) { const t_option_descriptor *desc = find_option_handler(opt); + const char *current_opt = argv[optind - 1]; int res; + size_t bitmask_index; + + switch (opt) + { + case '?': error_unknown_opt(current_opt); return CLI_ERROR; + case ':': error_invalid_arg(current_opt); return CLI_ERROR; + default: break; + } - if ('?' == opt) - return CLI_ERROR; if (NULL != desc) { - res = desc->handler(arg, config); + TRACK_DUP_OPT(desc, *opt_tracker, current_opt, g_options); + res = desc->handler(optarg, config); if (0 != res) + { + error_invalid_opt(current_opt); return res; + } } return CLI_SUCCESS; } @@ -82,7 +97,7 @@ build_long_options(struct option *long_opts, size_t nb_opts, long_opts[nb_opts] = (struct option){0}; } -static const t_option_descriptor +static inline const t_option_descriptor *find_option_handler(int opt) { for (size_t i = 0; i < CLI_OPT_LEN; ++i)