#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <utils/arg_parser.h>
#include <utils/memory.h>
#define is_lower_alpha(x) (x >= 97 && x <= 122)
#define xstr(s) #s
#define str(s) xstr(s)
#define PAD str(AP_HELP_SPACING)
const char *ap_app_name;
const char *ap_app_desc;
void ap_print_help(struct ap_option *ap_opts, int exit_code)
{
int count;
char opt_str[64];
struct ap_option *ap_opt;
if (ap_app_name && exit_code == 0)
printf("%s - %s\n", ap_app_name, ap_app_desc);
printf("\nUsage: %s [OPTIONS...] <COMMAND> [CMD_ARGS[0] ...]\n"
"\nOPTIONS:\n", ap_app_name);
ap_opt = ap_opts;
while (ap_opt->short_name != '\0') {
if (ap_opt->short_name == -1) {
ap_opt++;
continue;
}
if (ap_opt->type == AP_TYPE_BOOL ||
ap_opt->type == AP_TYPE_BOOL_HANDLER) {
snprintf(opt_str, 64, "%s", ap_opt->long_name);
} else {
snprintf(opt_str, 64, "%s <%s>",
ap_opt->long_name, ap_opt->opt_name);
}
printf(" -%c, --%-"PAD"s %s\n", ap_opt->short_name, opt_str,
ap_opt->help);
ap_opt++;
}
printf(" -%c, --%-"PAD"s Fork to background\n", 'f', "fork");
printf(" -%c, --%-"PAD"s Prevent writing to tty\n", 'q', "quite");
printf(" -%c, --%-"PAD"s Print this help message\n", 'h', "help");
count = 0;
ap_opt = ap_opts;
while (ap_opt->short_name != '\0') {
if (ap_opt->short_name != -1) {
ap_opt++;
continue;
}
if (count == 0)
printf("\nCOMMANDS:\n");
printf(" %-"PAD"s %s\n", ap_opt->long_name, ap_opt->help);
ap_opt++; count++;
}
exit(exit_code);
}
void ap_init(const char *app_name, const char *app_desc)
{
ap_app_name = app_name;
ap_app_desc = app_desc;
}
int ap_parse(int argc, char *argv[], struct ap_option *ap_opts, void *data)
{
char **cval;
char ostr[128];
struct option *opts, *opt;
struct ap_option *ap_opt;
int i, c, ret, *ival, pid;
int opts_len = 0, cmd_len = 0, opt_idx = 0, olen = 0, do_fork = 0;
int ap_opts_len = 0;
ap_opt = ap_opts;
while (ap_opt->short_name != '\0') {
if (ap_opt->short_name != -1)
opts_len++;
else
cmd_len++;
ap_opts_len++;
ap_opt++;
}
opts = safe_malloc(sizeof (struct option) * (opts_len + 3));
if (opts == NULL) {
printf("Error: alloc error\n");
exit(-1);
}
i = 0;
ap_opt = ap_opts;
while (ap_opt->short_name != '\0') {
if (ap_opt->short_name == -1) {
ap_opt++;
continue;
}
opt = opts + i++;
opt->name = ap_opt->long_name;
if (ap_opt->type == AP_TYPE_BOOL ||
ap_opt->type == AP_TYPE_BOOL_HANDLER) {
opt->has_arg = no_argument;
assert(olen + 2 < 128);
ostr[olen++] = ap_opt->short_name;
} else {
opt->has_arg = required_argument;
assert(olen + 3 < 128);
ostr[olen++] = ap_opt->short_name;
ostr[olen++] = ':';
}
opt->flag = 0;
opt->val = ap_opt->short_name;
ap_opt++;
}
assert(olen + 4 < 128);
opt = opts + i + 0;
opt->name = "help";
opt->val = 'h';
ostr[olen++] = 'h';
opt = opts + i + 1;
opt->name = "quite";
opt->val = 'q';
ostr[olen++] = 'q';
opt = opts + i + 2;
opt->val = 'f';
ostr[olen++] = 'f';
ostr[olen] = '\0';
while ((c = getopt_long(argc, argv, ostr, opts, &opt_idx)) >= 0) {
for (i = 0; i < ap_opts_len && c != ap_opts[i].short_name; i++);
if (c == 'h')
ap_print_help(ap_opts, 0);
if (c == 'q') {
close(STDOUT_FILENO);
close(STDERR_FILENO);
continue;
}
if (c == 'f') {
do_fork = 1;
continue;
}
if (c == '?')
ap_print_help(ap_opts, -1);
switch (ap_opts[i].type) {
case AP_TYPE_BOOL:
ival = (int *)((char *)data + ap_opts[i].offset);
*ival = 1;
break;
case AP_TYPE_STR:
cval = (char **)(((char *)data) + ap_opts[i].offset);
*cval = safe_strdup(optarg);
break;
case AP_TYPE_INT:
ival = (int *)((char *)data + ap_opts[i].offset);
*ival = atoi(optarg);
break;
case AP_TYPE_BOOL_HANDLER:
ap_opts[i].bool_handler();
exit(0);
default:
printf("Error: invalid arg type %d\n", ap_opts[i].type);
exit(-1);
}
if (ap_opts[i].validator) {
ret = ap_opts[i].validator(data);
if (ret != 0)
exit(-1);
}
ap_opts[i].flags |= AP_OPT_SEEN;
}
safe_free(opts);
ap_opt = ap_opts;
while (ap_opt->short_name != '\0') {
if (ap_opt->short_name != -1) {
if (ap_opt->flags & AP_OPT_REQUIRED &&
(ap_opt->flags & AP_OPT_SEEN) == 0U) {
printf("Error: arg '%c' is mandatory\n\n",
ap_opt->short_name);
ap_print_help(ap_opts, -1);
}
} else if (argv[optind]) {
if (strcmp(ap_opt->long_name, argv[optind]) == 0) {
if (do_fork) {
if ((pid = fork()) < 0)
return -1;
if (pid != 0)
exit (0);
}
return ap_opt->handler(argc - optind - 1,
argv + optind + 1, data);
}
}
ap_opt++;
}
if (cmd_len) {
printf("Error: unknown command '%s'\n\n", argv[optind]);
ap_print_help(ap_opts, -1);
}
return optind;
}