#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include "SecArgParse.h"
static int argument_status(struct argument* option) {
if(option == NULL) {
return no_argument;
}
return (!!(option->flag) && !(option->argument) ? no_argument :
(!!(option->flag) && !!(option->argument) ? optional_argument :
( !(option->flag) && !!(option->argument) ? required_argument :
no_argument)));
}
static bool fill_long_option_array(struct argument* options, size_t noptions, struct option long_options[], size_t nloptions) {
size_t i = 0;
size_t longi = 0;
for(i = 0; i <= noptions; i++) {
if(longi >= nloptions) {
return false;
}
if(options[i].longname) {
long_options[longi].name = options[i].longname;
long_options[longi].has_arg = argument_status(&options[i]);
long_options[longi].flag = options[i].flag;
long_options[longi].val = options[i].flagval;
longi++;
}
}
if(longi >= nloptions) {
return false;
}
long_options[longi].name = NULL;
long_options[longi].has_arg = 0;
long_options[longi].flag = 0;
long_options[longi].val = 0;
return true;
}
static bool fill_short_option_array(struct argument* options, size_t noptions, char* short_options, size_t nshort_options) {
size_t index = 0;
for(size_t i = 0; i < noptions; i++) {
if(options[i].shortname != '\0') {
if(index >= nshort_options) {
return false;
}
short_options[index] = options[i].shortname;
index += 1;
if(argument_status(&options[i]) == required_argument) {
if(index >= nshort_options) {
return false;
}
short_options[index] = ':';
index += 1;
}
}
}
short_options[index] = '\0';
return true;
}
static void trigger(struct argument option, char* argument) {
if(option.flag) {
*(option.flag) = option.flagval;
}
if(option.argument) {
asprintf(option.argument, "%.1048576s", argument);
}
}
static size_t num_arguments(struct arguments* args) {
size_t n = 0;
if(!args) {
return 0;
}
struct argument* a = args->arguments;
struct argument final = {};
while(a && n < 1024 && (memcmp(a, &final, sizeof(struct argument)) != 0)) {
n++;
a++;
}
return n;
}
bool options_parse(int argc, char * const *argv, struct arguments* args) {
if(!args) {
return false;
}
bool success = false;
struct arguments realargs;
realargs.programname = args->programname;
realargs.description = args->description;
size_t num_args = num_arguments(args);
size_t noptions = num_args + 1;
realargs.arguments = (struct argument*) malloc((noptions +1) * sizeof(struct argument));
struct argument help = {.shortname = 'h', .longname="help", .description="show this help message and exit"};
realargs.arguments[0] = help;
for(size_t i = 0; i < num_args + 1; i++) {
realargs.arguments[i+1] = args->arguments[i];
}
struct option* long_options = (struct option*) calloc((noptions+1), sizeof(struct option));
size_t short_options_length = 2* noptions * sizeof(char) + 2; char* short_options = (char*) malloc(short_options_length);
fill_long_option_array(realargs.arguments, noptions, long_options, noptions);
fill_short_option_array(realargs.arguments, noptions, short_options, short_options_length);
int c;
int option_index = 0;
while((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
if(c==0) {
if(option_index == 0) {
print_usage(&realargs);
exit(1);
} else {
struct option* long_option = &long_options[option_index];
for(size_t i = 0; i < noptions; i++) {
if(realargs.arguments[i].longname && long_option->name && strncmp(long_option->name, realargs.arguments[i].longname, strlen(realargs.arguments[i].longname)) == 0) {
trigger(realargs.arguments[i], optarg);
}
}
}
} else {
if(c == 'h') {
print_usage(&realargs);
exit(1);
}
size_t i = 0;
for(i = 0; i < noptions; i++) {
if(realargs.arguments[i].shortname == c) {
trigger(realargs.arguments[i], optarg);
break;
}
}
if(i == noptions) {
success = false;
goto out;
}
}
}
if(optind < argc) {
bool command_triggered = false;
size_t positional_argument_index = 0;
for(int parg = optind; parg < argc; parg++) {
for(size_t i = 0; i < noptions; i++) {
if(realargs.arguments[i].command) {
if(strcmp(argv[parg], realargs.arguments[i].command) == 0) {
trigger(realargs.arguments[i], NULL);
command_triggered = true;
break;
}
}
}
if(command_triggered) {
break;
}
while(positional_argument_index < noptions && !realargs.arguments[positional_argument_index].positional_name) {
positional_argument_index++;
}
if(positional_argument_index >= noptions) {
goto out;
} else {
if(realargs.arguments[positional_argument_index].argument) {
*(realargs.arguments[positional_argument_index].argument) = argv[parg];
positional_argument_index++;
}
}
}
}
success = true;
out:
free(realargs.arguments);
free(long_options);
free(short_options);
return success;
}
void print_usage(struct arguments* args) {
if(!args) {
return;
}
printf("usage: %s", args->programname ? args->programname : "command");
size_t num_args = num_arguments(args);
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].shortname) {
printf(" [-%c", args->arguments[i].shortname);
if(argument_status(&args->arguments[i]) != no_argument) {
printf(" %s", args->arguments[i].argname ? args->arguments[i].argname : "arg");
}
printf("]");
}
}
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].longname && !args->arguments[i].shortname) {
printf(" [--%s", args->arguments[i].longname);
if(argument_status(&args->arguments[i]) != no_argument) {
printf(" %s", args->arguments[i].argname ? args->arguments[i].argname : "arg");
}
printf("]");
}
}
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].command) {
printf(" [%s]", args->arguments[i].command);
}
}
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].positional_name) {
if(args->arguments[i].positional_optional) {
printf(" [<%s>]", args->arguments[i].positional_name);
} else {
printf(" <%s>", args->arguments[i].positional_name);
}
}
}
printf("\n");
if(args->description) {
printf("\n%s\n", args->description);
}
printf("\npositional arguments:\n");
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].positional_name) {
printf(" %-31s %s\n", args->arguments[i].positional_name, args->arguments[i].description);
}
}
printf("\noptional arguments:\n");
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].shortname) {
if(!args->arguments[i].longname) {
if(argument_status(&args->arguments[i]) != no_argument) {
printf(" -%c %-*s", args->arguments[i].shortname, 28, "arg");
} else {
printf(" -%-30c", args->arguments[i].shortname);
}
} else {
printf(" -%c", args->arguments[i].shortname);
if(argument_status(&args->arguments[i]) != no_argument) {
printf(" %s", args->arguments[i].argname ? args->arguments[i].argname : "arg");
}
if(args->arguments[i].longname) {
if(argument_status(&args->arguments[i]) == no_argument) {
printf(", --%-*s", 28 - (int) strlen(args->arguments[i].argname ? args->arguments[i].argname : "arg"), args->arguments[i].longname);
} else {
printf(", --%s %-*s", args->arguments[i].longname, 28 -5 - (int) strlen(args->arguments[i].longname) - (int) strlen(args->arguments[i].argname ? args->arguments[i].argname : "arg"), "arg");
}
}
}
printf("%s\n", args->arguments[i].description);
}
}
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].longname && !args->arguments[i].shortname) {
if(argument_status(&args->arguments[i]) != no_argument) {
printf(" --%s %-*s %s\n",
args->arguments[i].longname,
28 - (int) strlen(args->arguments[i].longname),
args->arguments[i].argname ? args->arguments[i].argname : "arg",
args->arguments[i].description);
} else {
printf(" --%-29s %s\n", args->arguments[i].longname, args->arguments[i].description);
}
}
}
printf("\noptional commands:\n");
for(size_t i = 0; i < num_args; i++) {
if(args->arguments[i].command) {
printf(" %-30s %s\n", args->arguments[i].command, args->arguments[i].description);
}
}
printf("\n");
}