feat: add version generation and CLI parsing
Add auto-generated version header and implement full command-line argument parsing with getopt_long support.
This commit is contained in:
parent
c29336ea81
commit
b786e79287
7 changed files with 182 additions and 25 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -12,3 +12,4 @@ build/
|
|||
build.mk
|
||||
.cache/
|
||||
compile_commands.json
|
||||
includes/version_gen.h
|
||||
|
|
|
|||
28
Makefile
28
Makefile
|
|
@ -15,6 +15,9 @@ endif
|
|||
|
||||
NAME = ft_ping
|
||||
TEST_BIN = ft_ping.test
|
||||
VERSION := 0.0.1
|
||||
BUILD_DATE := $(shell date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "nogit")
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
|
@ -35,8 +38,30 @@ DEPS = $(OBJS:.o=.d)
|
|||
|
||||
# Build Rules
|
||||
|
||||
VERSION_HEADER = includes/version_gen.h
|
||||
|
||||
$(VERSION_HEADER): Makefile
|
||||
@echo "/* Auto-generated - DO NOT EDIT */" > $@
|
||||
@echo "#ifndef PING_VERSION_GEN_H" >> $@
|
||||
@echo "#define PING_VERSION_GEN_H" >> $@
|
||||
@echo "#define PING_VERSION \"$(VERSION)\"" >> $@
|
||||
@echo "#define PING_BUILD_DATE \"$(BUILD_DATE)\"" >> $@
|
||||
@echo "#define PING_GIT_COMMIT \"$(GIT_COMMIT)\"" >> $@
|
||||
@echo "" >> $@
|
||||
@echo "typedef struct s_prog_name" >> $@
|
||||
@echo "{" >> $@
|
||||
@echo " char *alloc;" >> $@
|
||||
@echo " const char *name;" >> $@
|
||||
@echo "} t_prog_name;" >> $@
|
||||
@echo "" >> $@
|
||||
@echo "extern t_prog_name g_prog_name;" >> $@
|
||||
@echo "" >> $@
|
||||
@echo "#endif" >> $@
|
||||
@echo "[OK] Version header generated"
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: $(LIBICMP_DEP) $(NAME)
|
||||
all: $(LIBICMP_DEP) $(VERSION_HEADER) $(NAME)
|
||||
|
||||
# Build local libicmp if needed
|
||||
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
||||
|
|
@ -125,6 +150,7 @@ endif
|
|||
.PHONY: clean
|
||||
clean:
|
||||
@echo "[CLEAN] Cleaning object files..."
|
||||
$(RM) $(VERSION_HEADER)
|
||||
$(RM) -r $(OBJ_DIR)
|
||||
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
||||
@echo "[CLEAN] Cleaning libicmp..."
|
||||
|
|
|
|||
2
configure
vendored
2
configure
vendored
|
|
@ -486,7 +486,7 @@ generate_build_mk() {
|
|||
|
||||
# Prepare compiler flags
|
||||
local cflags="-Wall -Wextra -Werror -pipe"
|
||||
local cppflags="-std=c99 -I includes"
|
||||
local cppflags="-std=c99 -I includes -D_GNU_SOURCE"
|
||||
local ldflags=""
|
||||
local debug_flags=""
|
||||
local sanitizer_flags=""
|
||||
|
|
|
|||
|
|
@ -5,6 +5,30 @@
|
|||
|
||||
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
|
||||
{
|
||||
int 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[];
|
||||
extern const size_t g_options_len;
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,25 +2,51 @@
|
|||
#include "cli.h"
|
||||
#include <getopt.h>
|
||||
|
||||
typedef struct s_option_descriptor
|
||||
{
|
||||
int short_opt;
|
||||
const char *long_opt;
|
||||
int has_arg;
|
||||
t_option_handler handler;
|
||||
const char *description;
|
||||
} t_option_descriptor;
|
||||
|
||||
const t_option_descriptor g_options[] = {
|
||||
{'h', "help", no_argument, cli_handle_help, "Display this help and exit"},
|
||||
{'V', "version", no_argument, cli_handle_version, "Display version information and exit"},
|
||||
{'v', "verbose", no_argument, cli_handle_verbose, "Verbose output"},
|
||||
{'q', "quiet", no_argument, cli_handle_quiet, "Quiet mode (only show summary)"},
|
||||
{'c', "count", required_argument, cli_handle_count, "Stop after sending N packets"},
|
||||
{'i', "interval", required_argument, cli_handle_interval, "Wait N seconds between packets"},
|
||||
{'t', "ttl", required_argument, cli_handle_ttl, "Set Time To Live"},
|
||||
{'s', "size", required_argument, cli_handle_size, "Packet size in bytes"},
|
||||
{'W', "timeout", required_argument, cli_handle_timeout, "Timeout for replies in seconds"},
|
||||
{'f', "flood", no_argument, cli_handle_flood, "Flood mode"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
{
|
||||
'h', "help", no_argument, cli_handle_help, OPT_ARG_NONE,
|
||||
"Display this help and exit"
|
||||
},
|
||||
{
|
||||
'V', "version", no_argument, cli_handle_version, OPT_ARG_NONE,
|
||||
"Display version information and exit"
|
||||
},
|
||||
{
|
||||
'v', "verbose", no_argument, cli_handle_verbose, OPT_ARG_NONE,
|
||||
"Verbose output"
|
||||
},
|
||||
{
|
||||
'q', "quiet", no_argument, cli_handle_quiet, OPT_ARG_NONE,
|
||||
"Quiet mode (only show summary)"
|
||||
},
|
||||
{
|
||||
'c', "count", required_argument, cli_handle_count, OPT_ARG_UINT,
|
||||
"Stop after sending N packets"
|
||||
},
|
||||
{
|
||||
'i', "interval", required_argument, cli_handle_interval, OPT_ARG_SECONDS,
|
||||
"Wait N seconds between packets"
|
||||
},
|
||||
{
|
||||
't', "ttl", required_argument, cli_handle_ttl, OPT_ARG_TTL,
|
||||
"Set Time To Live"
|
||||
},
|
||||
{
|
||||
's', "size", required_argument, cli_handle_size, OPT_ARG_BYTES,
|
||||
"Packet size in bytes"
|
||||
},
|
||||
{
|
||||
'W', "timeout", required_argument, cli_handle_timeout, OPT_ARG_SECONDS,
|
||||
"Timeout for replies in seconds"
|
||||
},
|
||||
{
|
||||
'f', "flood", no_argument, cli_handle_flood, OPT_ARG_NONE,
|
||||
"Flood mode"
|
||||
},
|
||||
{
|
||||
0, NULL, 0, NULL, OPT_ARG_NONE,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
const size_t g_options_len = ((sizeof(g_options) / sizeof(*g_options)) - 1);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,51 @@
|
|||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "cli.h"
|
||||
#include "ft_ping.h"
|
||||
#include "ft_ping_const.h"
|
||||
|
||||
/* Forward declatation */
|
||||
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 hanlde_one_option(int opt, char *optarg, t_ping_config *config);
|
||||
/* ------------------- */
|
||||
|
||||
int
|
||||
cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
struct option long_opts[g_options_len + 1];
|
||||
const char *opt_str = "hVvq:c:i:t:s:W:f";
|
||||
int opt;
|
||||
int res;
|
||||
|
||||
init_config(config);
|
||||
build_long_options(long_opts, g_options_len, g_options);
|
||||
while (-1 != (opt = getopt_long(argc, argv, opt_str, long_opts, NULL)))
|
||||
{
|
||||
res = hanlde_one_option(opt, optarg, config);
|
||||
if (0 != res)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hanlde_one_option(int opt, char *optarg, t_ping_config *config)
|
||||
{
|
||||
const t_option_descriptor *desc = find_option_handler(opt);
|
||||
int res;
|
||||
|
||||
if ('?' == opt)
|
||||
return 1;
|
||||
if (NULL != desc)
|
||||
{
|
||||
res = desc->handler(optarg, config);
|
||||
if (0 != res)
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -27,3 +60,26 @@ init_config(t_ping_config *config)
|
|||
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 const t_option_descriptor
|
||||
*find_option_handler(int opt)
|
||||
{
|
||||
for (size_t i = 0; i < g_options_len; ++i)
|
||||
{
|
||||
if (g_options[i].short_opt == opt)
|
||||
return &g_options[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
26
src/main.c
26
src/main.c
|
|
@ -1,6 +1,15 @@
|
|||
#include <stdio.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ft_ping.h"
|
||||
#include "version_gen.h"
|
||||
|
||||
/* Forward declarations */
|
||||
static int init_prog_name(char *name);
|
||||
t_prog_name g_prog_name = {NULL, NULL};
|
||||
/* -------------------- */
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
|
|
@ -8,11 +17,26 @@ main(int argc, char **argv)
|
|||
t_ping_config config;
|
||||
int ret;
|
||||
|
||||
if (0 != init_prog_name(argv[0]))
|
||||
return EXIT_FAILURE;
|
||||
ret = cli_parse_arguments(argc, argv, &config);
|
||||
if (ret != 0)
|
||||
return (ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
return (ret > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
||||
printf("PING: Not yet implemented\n");
|
||||
|
||||
free(g_prog_name.alloc);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
init_prog_name(char *name)
|
||||
{
|
||||
char *path_dup = strdup(name);
|
||||
if (NULL == path_dup)
|
||||
return 1;
|
||||
|
||||
g_prog_name.alloc = path_dup;
|
||||
g_prog_name.name = basename(path_dup);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue