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
|
build.mk
|
||||||
.cache/
|
.cache/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
includes/version_gen.h
|
||||||
|
|
|
||||||
28
Makefile
28
Makefile
|
|
@ -15,6 +15,9 @@ endif
|
||||||
|
|
||||||
NAME = ft_ping
|
NAME = ft_ping
|
||||||
TEST_BIN = ft_ping.test
|
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
|
.DEFAULT_GOAL := all
|
||||||
MAKEFLAGS += --no-print-directory
|
MAKEFLAGS += --no-print-directory
|
||||||
|
|
@ -35,8 +38,30 @@ DEPS = $(OBJS:.o=.d)
|
||||||
|
|
||||||
# Build Rules
|
# 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
|
.PHONY: all
|
||||||
all: $(LIBICMP_DEP) $(NAME)
|
all: $(LIBICMP_DEP) $(VERSION_HEADER) $(NAME)
|
||||||
|
|
||||||
# Build local libicmp if needed
|
# Build local libicmp if needed
|
||||||
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
||||||
|
|
@ -125,6 +150,7 @@ endif
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@echo "[CLEAN] Cleaning object files..."
|
@echo "[CLEAN] Cleaning object files..."
|
||||||
|
$(RM) $(VERSION_HEADER)
|
||||||
$(RM) -r $(OBJ_DIR)
|
$(RM) -r $(OBJ_DIR)
|
||||||
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
ifeq ($(BUILD_LOCAL_LIBICMP),yes)
|
||||||
@echo "[CLEAN] Cleaning libicmp..."
|
@echo "[CLEAN] Cleaning libicmp..."
|
||||||
|
|
|
||||||
2
configure
vendored
2
configure
vendored
|
|
@ -486,7 +486,7 @@ generate_build_mk() {
|
||||||
|
|
||||||
# Prepare compiler flags
|
# Prepare compiler flags
|
||||||
local cflags="-Wall -Wextra -Werror -pipe"
|
local cflags="-Wall -Wextra -Werror -pipe"
|
||||||
local cppflags="-std=c99 -I includes"
|
local cppflags="-std=c99 -I includes -D_GNU_SOURCE"
|
||||||
local ldflags=""
|
local ldflags=""
|
||||||
local debug_flags=""
|
local debug_flags=""
|
||||||
local sanitizer_flags=""
|
local sanitizer_flags=""
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,30 @@
|
||||||
|
|
||||||
typedef int (*t_option_handler)(const char *arg, t_ping_config *config);
|
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_count(const char *arg, t_ping_config *config);
|
||||||
int cli_handle_flood(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_help(const char *arg, t_ping_config *config);
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,51 @@
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include <getopt.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[] = {
|
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"},
|
'h', "help", no_argument, cli_handle_help, OPT_ARG_NONE,
|
||||||
{'v', "verbose", no_argument, cli_handle_verbose, "Verbose output"},
|
"Display this help and exit"
|
||||||
{'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"},
|
'V', "version", no_argument, cli_handle_version, OPT_ARG_NONE,
|
||||||
{'t', "ttl", required_argument, cli_handle_ttl, "Set Time To Live"},
|
"Display version information and exit"
|
||||||
{'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"},
|
'v', "verbose", no_argument, cli_handle_verbose, OPT_ARG_NONE,
|
||||||
{0, NULL, 0, NULL, NULL}
|
"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 <string.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include "cli.h"
|
||||||
#include "ft_ping.h"
|
#include "ft_ping.h"
|
||||||
#include "ft_ping_const.h"
|
#include "ft_ping_const.h"
|
||||||
|
|
||||||
/* Forward declatation */
|
/* Forward declatation */
|
||||||
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,
|
||||||
|
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
|
int
|
||||||
cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
cli_parse_arguments(int argc, char **argv, t_ping_config *config)
|
||||||
{
|
{
|
||||||
(void)argc;
|
struct option long_opts[g_options_len + 1];
|
||||||
(void)argv;
|
const char *opt_str = "hVvq:c:i:t:s:W:f";
|
||||||
|
int opt;
|
||||||
|
int res;
|
||||||
|
|
||||||
init_config(config);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,3 +60,26 @@ init_config(t_ping_config *config)
|
||||||
config->timeout = DEFAULT_TIMEOUT;
|
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 <stdio.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "ft_ping.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
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
|
|
@ -8,11 +17,26 @@ main(int argc, char **argv)
|
||||||
t_ping_config config;
|
t_ping_config config;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (0 != init_prog_name(argv[0]))
|
||||||
|
return EXIT_FAILURE;
|
||||||
ret = cli_parse_arguments(argc, argv, &config);
|
ret = cli_parse_arguments(argc, argv, &config);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return (ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
|
return (ret > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
|
||||||
printf("PING: Not yet implemented\n");
|
printf("PING: Not yet implemented\n");
|
||||||
|
|
||||||
|
free(g_prog_name.alloc);
|
||||||
return EXIT_SUCCESS;
|
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