Files
cig/cli.h
T
2025-11-30 21:49:35 +01:00

160 lines
5.0 KiB
C

#ifndef CLI_HEADER
#define CLI_HEADER
#include <stdbool.h>
#define UNIQUE __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__
#define UNIQUE2 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef8__
typedef struct args {
int count;
const char **values;
} args_t;
args_t cli_make_args(int argc, const char **argv); // no crash
bool cli_command(args_t *args, const char *command_name); // no crash
bool cli_bool(args_t args, const char *flag_name); // no crash
bool cli_opt_str_func(args_t args, const char *flag_name, const char **output, const char *file, int line);
const char *cli_req_str_func(args_t args, const char *flag_name, const char *file, int line);
bool cli_opt_int_func(args_t args, const char *flag_name, int *output, const char *file, int line);
int cli_req_int_func(args_t args, const char *flag_name, const char *file, int line);
#define cli_opt_str(ARGS, FLAG_NAME, OUTPUT) cli_opt_str_func(ARGS, FLAG_NAME, OUTPUT, __FILE__, __LINE__)
#define cli_req_str(ARGS, FLAG_NAME) cli_req_str_func(ARGS, FLAG_NAME, __FILE__, __LINE__)
#define cli_opt_int(ARGS, FLAG_NAME, OUTPUT) cli_opt_int_func(ARGS, FLAG_NAME, OUTPUT, __FILE__, __LINE__)
#define cli_req_int(ARGS, FLAG_NAME) cli_req_int_func(ARGS, FLAG_NAME, __FILE__, __LINE__)
#define with_opt_str(ARGS, FLAG_NAME, VAR) \
for (const char *VAR = NULL; VAR == NULL;) \
for (bool UNIQUE = true; UNIQUE;) \
for ( \
bool UNIQUE2 = (UNIQUE = cli_opt_str(ARGS, FLAG_NAME, &VAR), true); \
UNIQUE2; \
UNIQUE2 = (VAR = ((const char *) 1), UNIQUE = false, false) \
) \
if (UNIQUE)
#define with_opt_int(ARGS, FLAG_NAME, VAR) \
for (int VAR = 0; VAR == 0;) \
for (bool UNIQUE = true; UNIQUE;) \
for ( \
bool UNIQUE2 = (UNIQUE = cli_opt_int(ARGS, FLAG_NAME, &VAR), true); \
UNIQUE2; \
UNIQUE2 = (VAR = 1, UNIQUE = false, false) \
) \
if (UNIQUE)
#ifdef CLI_IMPL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool cli_command(args_t *args, const char *command_name) {
if (args->count == 0) {
return false;
}
if (strcmp(args->values[0], command_name) == 0) {
args->values++;
args->count--;
return true;
}
return false;
}
bool cli_bool(args_t args, const char *flag_name) {
for ( int i = 0; i < args.count; i++ ) {
if (strcmp(args.values[i], flag_name) == 0) {
return true;
}
}
return false;
}
bool cli_opt_str_func(args_t args, const char *flag_name, const char **output, const char *file, int line) {
bool is_flag_name_found = false;
int flag_name_index;
for ( int i = 0; i < args.count; i++ ) {
if (strcmp(args.values[i], flag_name) == 0) {
is_flag_name_found = true;
flag_name_index = i;
break;
}
}
static const char *dummy = "";
if (!is_flag_name_found) {
*output = dummy;
return false;
}
int flag_value_index = flag_name_index + 1;
if (args.count <= flag_value_index) {
fprintf(stderr, "%s:%d: No value provided for %s!\n", file, line, flag_name);
exit(1);
}
*output = args.values[flag_value_index];
return true;
}
const char *cli_req_str_func(args_t args, const char *flag_name, const char *file, int line) {
const char *value;
if (cli_opt_str_func(args, flag_name, &value, file, line)) {
return value;
} else {
fprintf(stderr, "%s:%d: Required string %s not provided!\n", file, line, flag_name);
exit(1);
}
return "";
}
bool cli_opt_int_func(args_t args, const char *flag_name, int *output, const char *file, int line) {
const char *text = "";
if (cli_opt_str_func(args, flag_name, &text, file, line)) {
if (
text[0] != '-' &&
text[0] < '0' &&
'9' < text[0]
) {
fprintf(stderr, "%s:%d: Invalid integer %s!\n", file, line, text);
exit(1);
}
if (
text[0] == '-' &&
text[1] == '\0'
) {
fprintf(stderr, "%s:%d: Invalid integer %s!\n", file, line, text);
exit(1);
}
for ( const char *c = &text[1]; (*c) != '\0'; c++ ) {
if ((*c) < '0' || '9' < (*c)) {
fprintf(stderr, "%s:%d: Invalid integer %s!\n", file, line, text);
exit(1);
}
}
*output = atoi(text);
return true;
}
*output = 0;
return false;
}
int cli_req_int_func(args_t args, const char *flag_name, const char *file, int line) {
int value;
if (cli_opt_int_func(args, flag_name, &value, file, line)) {
return value;
} else {
fprintf(stderr, "%s:%d: Required int %s not provided!\n", file, line, flag_name);
exit(1);
}
return 0;
}
args_t cli_make_args(int argc, const char **argv) {
return (args_t) {
.count = argc-1,
.values = argv+1,
};
}
#endif /* CLI_IMPL */
#endif /* CLI_HEADER */