refactor: complete multi-binary CLI reorganization

This commit is contained in:
lohhiiccc 2026-03-12 18:45:54 +01:00
parent ab90dbb07f
commit 0dc1e09205
40 changed files with 264 additions and 227 deletions

42
includes/cli.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef CLI_H
#define CLI_H
#include <stddef.h>
#include <getopt.h>
#include "compiler.h"
enum e_cli_code {
CLI_EXIT_SUCCESS = -1,
CLI_SUCCESS = 0,
CLI_ERROR = 1
};
typedef int (*t_option_handler)(const char *arg, void *config);
typedef enum e_option_arg_type
{
OPT_ARG_NONE = 0,
OPT_ARG_INT,
OPT_ARG_UINT,
OPT_ARG_SECONDS,
OPT_ARG_BYTES,
OPT_ARG_TTL,
OPT_ARG_STRING
} t_option_arg_type;
typedef struct s_option_descriptor
{
char short_opt;
const char *long_opt;
int has_arg;
t_option_handler handler;
t_option_arg_type arg_type;
const char *description;
} t_option_descriptor;
enum e_cli_code cli_parse(int argc, char **argv, void *config,
const t_option_descriptor *opts, size_t nb_opts,
char *opt_str, struct option *long_opts);
#endif

6
includes/compiler.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef COMPILER_H
#define COMPILER_H
#define __unused __attribute__((unused))
#endif

View file

@ -1,45 +1,7 @@
#ifndef CLI_INTERNAL_ARG_HANDLER
#define CLI_INTERNAL_ARG_HANDLER
#include "ping/ft_ping.h"
typedef int (*t_option_handler)(const char *arg, t_ping_config *config);
typedef enum e_option_arg_type
{
OPT_ARG_NONE = 0,
OPT_ARG_INT,
OPT_ARG_UINT,
OPT_ARG_SECONDS,
OPT_ARG_BYTES,
OPT_ARG_TTL,
OPT_ARG_STRING
} t_option_arg_type;
typedef struct s_option_descriptor
{
char short_opt;
const char *long_opt;
int has_arg;
t_option_handler handler;
t_option_arg_type arg_type;
const char *description;
} t_option_descriptor;
extern const t_option_descriptor g_options[];
const char *cli_get_optstr(void);
int cli_handle_count(const char *arg, t_ping_config *config);
int cli_handle_flood(const char *arg, t_ping_config *config);
int cli_handle_help(const char *arg, t_ping_config *config);
int cli_handle_interval(const char *arg, t_ping_config *config);
int cli_handle_quiet(const char *arg, t_ping_config *config);
int cli_handle_size(const char *arg, t_ping_config *config);
int cli_handle_deadline(const char *arg, t_ping_config *config);
int cli_handle_timeout(const char *arg, t_ping_config *config);
int cli_handle_ttl(const char *arg, t_ping_config *config);
int cli_handle_version(const char *arg, t_ping_config *config);
int cli_handle_verbose(const char *arg, t_ping_config *config);
#include "cli.h"
#include "internal/ping/cli_handlers.h"
#endif

View file

@ -1,7 +1,7 @@
#ifndef CLI_INTERNAL_MESSAGES
#define CLI_INTERNAL_MESSAGES
#include "ping/cli.h"
#include "cli.h"
void print_help(void);
void print_version(void);

View file

@ -2,12 +2,8 @@
#define CLI_INTERNAL_PARSE_UTILS
#include <stdint.h>
#include <netinet/in.h>
#include "ping/ft_ping.h"
int cli_parse_uint64(const char *s, uint64_t *out);
int cli_parse_float(const char *s, float *out);
int cli_parse_inet_addr(const char *s, struct in_addr *out);
int cli_parse_destinations(int argc, char **argv, int optind, t_ping_config *config);
#endif

View file

@ -0,0 +1,20 @@
#ifndef PING_CLI_HANDLERS_H
#define PING_CLI_HANDLERS_H
#include "cli.h"
extern const t_option_descriptor g_options[];
int cli_handle_count(const char *arg, void *config);
int cli_handle_deadline(const char *arg, void *config);
int cli_handle_flood(const char *arg, void *config);
int cli_handle_help(const char *arg, void *config);
int cli_handle_interval(const char *arg, void *config);
int cli_handle_quiet(const char *arg, void *config);
int cli_handle_size(const char *arg, void *config);
int cli_handle_timeout(const char *arg, void *config);
int cli_handle_ttl(const char *arg, void *config);
int cli_handle_verbose(const char *arg, void *config);
int cli_handle_version(const char *arg, void *config);
#endif

View file

@ -0,0 +1,11 @@
#ifndef PING_CLI_PARSE_UTILS_H
#define PING_CLI_PARSE_UTILS_H
#include <netinet/in.h>
#include "ping/ft_ping.h"
int cli_parse_inet_addr(const char *s, struct in_addr *out);
int cli_parse_destinations(int argc, char **argv, int first_arg,
t_ping_config *config);
#endif

View file

@ -1,14 +1,9 @@
#ifndef PING_CLI_H
#define PING_CLI_H
#include "../cli.h"
#include "ping/ft_ping.h"
enum e_cli_code {
CLI_EXIT_SUCCESS = -1,
CLI_SUCCESS = 0,
CLI_ERROR = 1
};
enum e_cli_code cli_parse_arguments(int argc, char **argv,
t_ping_config *config);
void cli_config_free(t_ping_config *config);

6
sources/cli.mk Normal file
View file

@ -0,0 +1,6 @@
CLI_SRC_DIR = src/cli
CLI_SRCS = $(CLI_SRC_DIR)/parse.c \
$(CLI_SRC_DIR)/parse_utils/parse_int.c \
$(CLI_SRC_DIR)/parse_utils/parse_float.c \

View file

@ -1,6 +1,9 @@
include sources/cli.mk
PING_SRC_DIR = src/ping
PING_SRCS = $(PING_SRC_DIR)/main.c \
PING_SRCS = $(CLI_SRCS) \
$(PING_SRC_DIR)/main.c \
$(PING_SRC_DIR)/cli/parse.c \
$(PING_SRC_DIR)/cli/handlers/handle_count.c \
$(PING_SRC_DIR)/cli/handlers/handle_deadline.c \
@ -14,11 +17,8 @@ PING_SRCS = $(PING_SRC_DIR)/main.c \
$(PING_SRC_DIR)/cli/handlers/handle_ttl.c \
$(PING_SRC_DIR)/cli/handlers/handle_version.c \
$(PING_SRC_DIR)/cli/handlers/handle_verbose.c \
$(PING_SRC_DIR)/cli/parse_utils/parse_int.c \
$(PING_SRC_DIR)/cli/parse_utils/parse_float.c \
$(PING_SRC_DIR)/cli/parse_utils/parse_inet_addr.c \
$(PING_SRC_DIR)/cli/parse_utils/parse_destinations.c \
$(PING_SRC_DIR)/cli/parse_utils/get_optstr.c \
$(PING_SRC_DIR)/cli/config_free.c \
$(PING_SRC_DIR)/cli/messages/help.c \
$(PING_SRC_DIR)/cli/messages/version.c \

117
src/cli/parse.c Normal file
View file

@ -0,0 +1,117 @@
#include <stdint.h>
#include <getopt.h>
#include "cli.h"
#include "internal/cli/messages.h"
/* Map -? to -h to support help shortcut */
#define HANDLE_QUESTION_MARK(opt) \
do { \
if (opt == '?' && (optopt) == '?') \
opt = 'h'; \
} while (0)
/* Forward declarations */
static void build_long_options(struct option *long_opts, size_t nb_opts,
const t_option_descriptor *src);
static void build_optstr(char *dest, const t_option_descriptor *opts,
size_t nb_opts);
static const t_option_descriptor *find_option_handler(int opt,
const t_option_descriptor *opts, size_t nb_opts);
static int handle_one_option(int opt, char **argv, void *config,
uint64_t *opt_tracker,
const t_option_descriptor *opts, size_t nb_opts);
/* ------------------- */
enum e_cli_code
cli_parse(int argc, char **argv, void *config,
const t_option_descriptor *opts, size_t nb_opts,
char *opt_str, struct option *long_opts)
{
uint64_t tracker = 0;
int opt;
int res;
build_optstr(opt_str, opts, nb_opts);
build_long_options(long_opts, nb_opts, opts);
while (-1 != (opt = getopt_long(argc, argv, opt_str, long_opts, NULL)))
{
HANDLE_QUESTION_MARK(opt);
res = handle_one_option(opt, argv, config, &tracker, opts, nb_opts);
if (CLI_SUCCESS != res)
return (enum e_cli_code)res;
}
return CLI_SUCCESS;
}
static void
build_optstr(char *dest, const t_option_descriptor *opts, size_t nb_opts)
{
size_t str_i = 1;
/* Mute default error messages */
dest[0] = ':';
for (size_t opt_i = 0; opt_i < nb_opts; ++opt_i)
{
dest[str_i++] = opts[opt_i].short_opt;
switch (opts[opt_i].has_arg)
{
case optional_argument: dest[str_i++] = ':'; /* fall through */
case required_argument: dest[str_i++] = ':'; break;
default: break;
}
}
dest[str_i] = '\0';
}
static int
handle_one_option(int opt, char **argv, void *config,
uint64_t *opt_tracker,
const t_option_descriptor *opts, size_t nb_opts)
{
const char *current_opt = argv[optind - 1];
const t_option_descriptor *desc;
size_t bitmask_index;
int res;
if ('?' == opt)
return error_unknown_opt(current_opt);
if (':' == opt)
return error_invalid_opt(current_opt);
desc = find_option_handler(opt, opts, nb_opts);
bitmask_index = (size_t)(desc - opts);
if (*opt_tracker & (1ULL << bitmask_index))
return error_duplicate_opt(current_opt);
*opt_tracker |= (1ULL << bitmask_index);
res = desc->handler(optarg, config);
if (CLI_SUCCESS != res)
return error_invalid_opt(current_opt);
return CLI_SUCCESS;
}
static void
build_long_options(struct option *long_opts, size_t nb_opts,
const t_option_descriptor *src)
{
for (size_t i = 0; i < nb_opts; ++i) {
long_opts[i].name = src[i].long_opt;
long_opts[i].has_arg = src[i].has_arg;
long_opts[i].flag = NULL;
long_opts[i].val = src[i].short_opt;
}
long_opts[nb_opts] = (struct option){0};
}
static const t_option_descriptor *
find_option_handler(int opt, const t_option_descriptor *opts, size_t nb_opts)
{
for (size_t i = 0; i < nb_opts; ++i)
{
if (opts[i].short_opt == opt)
return &opts[i];
}
return NULL;
}

View file

@ -1,12 +1,12 @@
#include <unistd.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_count(const char *arg, t_ping_config *config)
cli_handle_count(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
uint64_t count;
if (0 != cli_parse_uint64(arg, &count))
@ -14,4 +14,3 @@ cli_handle_count(const char *arg, t_ping_config *config)
config->count = count;
return CLI_SUCCESS;
}

View file

@ -1,10 +1,10 @@
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_deadline(const char *arg, t_ping_config *config)
cli_handle_deadline(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
float deadline;
if (0 != cli_parse_float(arg, &deadline))

View file

@ -2,9 +2,9 @@
#include "ping/ft_ping_flags.h"
int
cli_handle_flood(const char *arg, t_ping_config *config)
cli_handle_flood(__unused const char *arg, void *config_void)
{
(void)arg;
t_ping_config *config = (t_ping_config *)config_void;
SET_FLAG(config->flags, FLAG_FLOOD);
return CLI_SUCCESS;
}

View file

@ -1,13 +1,9 @@
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/messages.h"
int
cli_handle_help(const char *arg, t_ping_config *config)
cli_handle_help(__unused const char *arg, __unused void *config_void)
{
(void)arg;
(void)config;
print_help();
return CLI_EXIT_SUCCESS;

View file

@ -1,12 +1,12 @@
#include <unistd.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_interval(const char *arg, t_ping_config *config)
cli_handle_interval(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
float interval;
if (0 != cli_parse_float(arg, &interval))

View file

@ -2,9 +2,9 @@
#include "ping/ft_ping_flags.h"
int
cli_handle_quiet(const char *arg, t_ping_config *config)
cli_handle_quiet(__unused const char *arg, void *config_void)
{
(void)arg;
t_ping_config *config = (t_ping_config *)config_void;
SET_FLAG(config->flags, FLAG_QUIET);
return CLI_SUCCESS;
}

View file

@ -1,12 +1,12 @@
#include <unistd.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_size(const char *arg, t_ping_config *config)
cli_handle_size(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
uint64_t size;
if (0 != cli_parse_uint64(arg, &size) || size > (65535 - 8))

View file

@ -1,12 +1,12 @@
#include <unistd.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_timeout(const char *arg, t_ping_config *config)
cli_handle_timeout(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
float TO;
if (0 != cli_parse_float(arg, &TO))

View file

@ -1,12 +1,12 @@
#include <unistd.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
int
cli_handle_ttl(const char *arg, t_ping_config *config)
cli_handle_ttl(const char *arg, void *config_void)
{
t_ping_config *config = (t_ping_config *)config_void;
uint64_t ttl;
if (0 != cli_parse_uint64(arg, &ttl) || ttl > 255)

View file

@ -1,11 +1,10 @@
#include "ping/cli.h"
#include "ping/ft_ping.h"
#include "ping/ft_ping_flags.h"
int
cli_handle_verbose(const char *arg, t_ping_config *config)
cli_handle_verbose(__unused const char *arg, void *config_void)
{
(void)arg;
t_ping_config *config = (t_ping_config *)config_void;
SET_FLAG(config->flags, FLAG_VERBOSE);
return CLI_SUCCESS;
}

View file

@ -1,13 +1,9 @@
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/messages.h"
int
cli_handle_version(const char *arg, t_ping_config *config)
cli_handle_version(__unused const char *arg, __unused void *config_void)
{
(void)arg;
(void)config;
print_version();
return CLI_EXIT_SUCCESS;
}

View file

@ -1,4 +1,4 @@
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include "internal/cli/options.h"
#undef X

View file

@ -1,6 +1,6 @@
#include <stdio.h>
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include "internal/cli/options.h"
/* Forward declarations */

View file

@ -1,75 +1,31 @@
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "ping/cli.h"
#include "ping/ft_ping_flags.h"
#include "ping/ft_ping_const.h"
#include "internal/cli/messages.h"
#include "internal/ping/cli_handlers.h"
#include "internal/ping/parse_utils.h"
#include "internal/cli/options.h"
#include "internal/cli/arg_handler.h"
#include "internal/cli/parse_utils.h"
/* Map -? to -h to support help shortcut */
#define HANDLE_QUESTION_MARK(opt) \
do { \
if (opt == '?' && (optopt) == '?') \
opt = 'h'; \
} while (0)
/* Forward declatation */
/* Forward declaration */
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 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)
{
char opt_str[CLI_OPT_STR_LEN];
struct option long_opts[CLI_OPT_LEN + 1];
const char *opt_str = cli_get_optstr();
uint64_t tracker = 0;
int opt;
int res;
enum e_cli_code ret;
init_config(config);
build_long_options(long_opts, CLI_OPT_LEN, g_options);
while (-1 != (opt = getopt_long(argc, argv, opt_str, long_opts, NULL)))
{
HANDLE_QUESTION_MARK(opt);
res = handle_one_option(opt, argv, config, &tracker);
if (CLI_SUCCESS != res)
return res;
}
ret = cli_parse(argc, argv, config, g_options, CLI_OPT_LEN,
opt_str, long_opts);
if (CLI_SUCCESS != ret)
return ret;
return cli_parse_destinations(argc, argv, optind, config);
}
static int
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 size_t bitmask_index = (size_t)((desc) - (g_options));
const char *current_opt = argv[optind - 1];
int res = CLI_SUCCESS;
if ('?' == opt)
return error_unknown_opt(current_opt);
if (':' == opt)
return error_invalid_opt(current_opt);
if (HAS_FLAG(*opt_tracker, (1ULL << bitmask_index)))
return error_duplicate_opt(current_opt);
SET_FLAG(*opt_tracker, (1ULL) << bitmask_index);
res = desc->handler(optarg, config);
if (CLI_SUCCESS != res)
return error_invalid_opt(current_opt);
return CLI_SUCCESS;
}
static void
init_config(t_ping_config *config)
{
@ -80,27 +36,3 @@ init_config(t_ping_config *config)
config->packet_size = DEFAULT_PACKET_SIZE;
config->timeout = DEFAULT_TIMEOUT;
}
static void
build_long_options(struct option *long_opts, size_t nb_opts,
const t_option_descriptor *src)
{
for (size_t i = 0; i < nb_opts; ++i) {
long_opts[i].name = src[i].long_opt;
long_opts[i].has_arg = src[i].has_arg;
long_opts[i].flag = NULL;
long_opts[i].val = src[i].short_opt;
}
long_opts[nb_opts] = (struct option){0};
}
static inline const t_option_descriptor
*find_option_handler(int opt)
{
for (size_t i = 0; i < CLI_OPT_LEN; ++i)
{
if (g_options[i].short_opt == opt)
return &g_options[i];
}
return NULL;
}

View file

@ -1,40 +0,0 @@
#include "internal/cli/arg_handler.h"
#include "internal/cli/options.h"
/* Forward declarations */
static void init_opt_str(char *str);
/* -------------------- */
const char *
cli_get_optstr(void)
{
/* Static string to act like a global */
static char opt_str[CLI_OPT_STR_LEN] = "";
/* Lazy init */
if ('\0' == *opt_str)
init_opt_str(opt_str);
return opt_str;
}
static void
init_opt_str(char *dest)
{
/* Mute default error messages */
dest[0] = ':';
size_t str_i = 1;
for (size_t opt_i = 0; opt_i < CLI_OPT_LEN; ++opt_i)
{
dest[str_i++] = g_options[opt_i].short_opt;
switch (g_options[opt_i].has_arg)
{
case optional_argument: dest[str_i++] = ':'; /* fall through */
case required_argument: dest[str_i++] = ':'; break;
default: break;
}
}
dest[str_i] = '\0';
return;
}

View file

@ -2,22 +2,22 @@
#include "ping/cli.h"
#include "internal/cli/messages.h"
#include "internal/cli/parse_utils.h"
#include "internal/ping/parse_utils.h"
int
cli_parse_destinations(int argc, char **argv, int optind, t_ping_config *config)
cli_parse_destinations(int argc, char **argv, int first_arg, t_ping_config *config)
{
if (optind >= argc)
if (first_arg >= argc)
return error_missing_dest();
config->nb_destinations = (size_t)(argc - optind);
config->nb_destinations = (size_t)(argc - first_arg);
config->destinations = malloc(config->nb_destinations * sizeof(struct in_addr));
if (NULL == config->destinations)
return CLI_ERROR;
for (int i = optind; i < argc; i++)
for (int i = first_arg; i < argc; i++)
{
if (0 != cli_parse_inet_addr(argv[i], &config->destinations[i - optind]))
if (0 != cli_parse_inet_addr(argv[i], &config->destinations[i - first_arg]))
{
free(config->destinations);
config->destinations = NULL;

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: basic test */
Test(cli_handle_count, valid_number)

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include "ping/ft_ping_flags.h"
/* Test 1: test flag*/

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: exit code test */
Test(handle_help, exit_code)

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: basic test */
Test(cli_handle_interval, valid_number)

View file

@ -1,6 +1,6 @@
#include "ping/cli.h"
#include "ping/ft_ping_flags.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include <criterion/criterion.h>
/* Test 1: test flag*/

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: basic test */
Test(cli_handle_size, valid_number)

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: basic test */
Test(cli_handle_timeout, valid_number)

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: basic test */
Test(cli_handle_ttl, valid_number)

View file

@ -1,6 +1,6 @@
#include "ping/cli.h"
#include "ping/ft_ping_flags.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include <criterion/criterion.h>
/* Test 1: test flag*/

View file

@ -1,6 +1,6 @@
#include <criterion/criterion.h>
#include "ping/cli.h"
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
/* Test 1: exit code test */
Test(handle_version, exit_code)

View file

@ -1,4 +1,4 @@
#include "internal/cli/arg_handler.h"
#include "internal/ping/cli_handlers.h"
#include <criterion/criterion.h>
#include <stddef.h>