feat(cli): add error functions and duplicate option tracking for CLI arguments
This commit is contained in:
parent
78ca539b99
commit
de872facd3
10 changed files with 87 additions and 31 deletions
|
|
@ -4,4 +4,9 @@
|
||||||
void print_help(void);
|
void print_help(void);
|
||||||
void print_version(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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
#ifndef PING_CLI_OPT_H
|
#ifndef PING_CLI_OPT_H
|
||||||
#define PING_CLI_OPT_H
|
#define PING_CLI_OPT_H
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#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 \
|
#define CLI_OPTIONS_LIST \
|
||||||
X( \
|
X( \
|
||||||
'h', \
|
'h', \
|
||||||
|
|
@ -87,7 +101,6 @@
|
||||||
#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) + 1
|
#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) + 1
|
||||||
enum { CLI_OPT_LEN = (0 CLI_OPTIONS_LIST) };
|
enum { CLI_OPT_LEN = (0 CLI_OPTIONS_LIST) };
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
#undef X
|
#undef X
|
||||||
#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) \
|
#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) \
|
||||||
+ (1 * (has_arg == no_argument) \
|
+ (1 * (has_arg == no_argument) \
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ SRCS = $(SRC_DIR)/main.c \
|
||||||
$(SRC_DIR)/cli/parse_utils/get_optstr.c \
|
$(SRC_DIR)/cli/parse_utils/get_optstr.c \
|
||||||
$(SRC_DIR)/cli/messages/help.c \
|
$(SRC_DIR)/cli/messages/help.c \
|
||||||
$(SRC_DIR)/cli/messages/version.c \
|
$(SRC_DIR)/cli/messages/version.c \
|
||||||
|
$(SRC_DIR)/cli/messages/error.c \
|
||||||
|
|
||||||
TESTS_DIR = tests
|
TESTS_DIR = tests
|
||||||
TESTS = $(TESTS_DIR)/test_main.c \
|
TESTS = $(TESTS_DIR)/test_main.c \
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
|
@ -11,11 +10,7 @@ cli_handle_count(const char *arg, t_ping_config *config)
|
||||||
uint64_t count;
|
uint64_t count;
|
||||||
|
|
||||||
if (0 != cli_parse_uint64(arg, &count))
|
if (0 != cli_parse_uint64(arg, &count))
|
||||||
{
|
|
||||||
dprintf(STDERR_FILENO, "Invalide count\n");
|
|
||||||
return CLI_ERROR;
|
return CLI_ERROR;
|
||||||
}
|
|
||||||
|
|
||||||
config->count = count;
|
config->count = count;
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "internal/cli/parse_utils.h"
|
#include "internal/cli/parse_utils.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cli_handle_interval(const char *arg, t_ping_config *config)
|
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;
|
float interval;
|
||||||
|
|
||||||
if (0 != cli_parse_float(arg, &interval))
|
if (0 != cli_parse_float(arg, &interval))
|
||||||
{
|
|
||||||
dprintf(STDERR_FILENO, "Invalide interval\n");
|
|
||||||
return CLI_ERROR;
|
return CLI_ERROR;
|
||||||
}
|
|
||||||
|
|
||||||
config->interval = interval;
|
config->interval = interval;
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include "internal/cli/arg_handler.h"
|
#include "internal/cli/arg_handler.h"
|
||||||
#include "internal/cli/parse_utils.h"
|
#include "internal/cli/parse_utils.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -11,10 +10,7 @@ cli_handle_size(const char *arg, t_ping_config *config)
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
|
||||||
if (0 != cli_parse_uint64(arg, &size) || size > (65535 - 8))
|
if (0 != cli_parse_uint64(arg, &size) || size > (65535 - 8))
|
||||||
{
|
|
||||||
dprintf(STDERR_FILENO, "Invalide size\n");
|
|
||||||
return CLI_ERROR;
|
return CLI_ERROR;
|
||||||
}
|
|
||||||
|
|
||||||
config->packet_size = size;
|
config->packet_size = size;
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "internal/cli/parse_utils.h"
|
#include "internal/cli/parse_utils.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cli_handle_timeout(const char *arg, t_ping_config *config)
|
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;
|
float TO;
|
||||||
|
|
||||||
if (0 != cli_parse_float(arg, &TO))
|
if (0 != cli_parse_float(arg, &TO))
|
||||||
{
|
|
||||||
dprintf(STDERR_FILENO, "Invalide timeout\n");
|
|
||||||
return CLI_ERROR;
|
return CLI_ERROR;
|
||||||
}
|
|
||||||
|
|
||||||
config->timeout = TO;
|
config->timeout = TO;
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include "internal/cli/arg_handler.h"
|
#include "internal/cli/arg_handler.h"
|
||||||
#include "internal/cli/parse_utils.h"
|
#include "internal/cli/parse_utils.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -11,10 +10,7 @@ cli_handle_ttl(const char *arg, t_ping_config *config)
|
||||||
uint64_t ttl;
|
uint64_t ttl;
|
||||||
|
|
||||||
if (0 != cli_parse_uint64(arg, &ttl) || ttl > 255)
|
if (0 != cli_parse_uint64(arg, &ttl) || ttl > 255)
|
||||||
{
|
|
||||||
dprintf(STDERR_FILENO, "Invalide ttl\n");
|
|
||||||
return CLI_ERROR;
|
return CLI_ERROR;
|
||||||
}
|
|
||||||
|
|
||||||
config->ttl = (uint8_t)ttl;
|
config->ttl = (uint8_t)ttl;
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include "internal/cli/arg_handler.h"
|
#include "internal/cli/messages.h"
|
||||||
#include "internal/cli/options.h"
|
#include "internal/cli/options.h"
|
||||||
|
#include "internal/cli/arg_handler.h"
|
||||||
#include "ft_ping_const.h"
|
#include "ft_ping_const.h"
|
||||||
|
|
||||||
/* Map -? to -h to support help shortcut */
|
/* Map -? to -h to support help shortcut */
|
||||||
|
|
@ -18,14 +21,15 @@
|
||||||
static void init_config(t_ping_config *config);
|
static void init_config(t_ping_config *config);
|
||||||
static void build_long_options(struct option *long_opts, size_t nb_opts,
|
static void build_long_options(struct option *long_opts, size_t nb_opts,
|
||||||
const t_option_descriptor *src);
|
const t_option_descriptor *src);
|
||||||
static const t_option_descriptor *find_option_handler(int opt);
|
static inline const t_option_descriptor *find_option_handler(int opt);
|
||||||
static int handle_one_option(int opt, char *arg, t_ping_config *config);
|
static int handle_one_option(int opt, char **argv, t_ping_config *config, uint64_t *opt_tacker);
|
||||||
/* ------------------- */
|
/* ------------------- */
|
||||||
|
|
||||||
enum e_cli_code
|
enum e_cli_code
|
||||||
cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
||||||
{
|
{
|
||||||
struct option long_opts[CLI_OPT_LEN + 1];
|
struct option long_opts[CLI_OPT_LEN + 1];
|
||||||
|
uint64_t tracker = 0;
|
||||||
int opt;
|
int opt;
|
||||||
int res;
|
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)))
|
while (-1 != (opt = getopt_long(argc, argv, cli_get_optstr(), long_opts, NULL)))
|
||||||
{
|
{
|
||||||
HANDLE_QUESTION_MARK(opt);
|
HANDLE_QUESTION_MARK(opt);
|
||||||
res = handle_one_option(opt, optarg, config);
|
res = handle_one_option(opt, argv, config, &tracker);
|
||||||
if (0 != res)
|
if (0 != res)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -42,18 +46,29 @@ cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 t_option_descriptor *desc = find_option_handler(opt);
|
||||||
|
const char *current_opt = argv[optind - 1];
|
||||||
int res;
|
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)
|
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)
|
if (0 != res)
|
||||||
|
{
|
||||||
|
error_invalid_opt(current_opt);
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return CLI_SUCCESS;
|
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};
|
long_opts[nb_opts] = (struct option){0};
|
||||||
}
|
}
|
||||||
|
|
||||||
static const t_option_descriptor
|
static inline const t_option_descriptor
|
||||||
*find_option_handler(int opt)
|
*find_option_handler(int opt)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < CLI_OPT_LEN; ++i)
|
for (size_t i = 0; i < CLI_OPT_LEN; ++i)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue