/* For terms of usage/redistribution/modification see the LICENSE file */
/* For authors and contributors see the AUTHORS file */
#include "iptraf-ng-compat.h"
#include "parse-options.h"
static int parse_opt_size(const struct options *opt)
{
unsigned size = 1;
for (; opt->type != OPTION_END; opt++)
size++;
return size;
}
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
void __noreturn parse_usage_and_die(const char *const *usage,
const struct options *opt)
{
fprintf(stderr, "usage: %s\n", *usage++);
while (*usage && **usage)
fprintf(stderr, " or: %s\n", *usage++);
if (opt->type != OPTION_GROUP)
fputc('\n', stderr);
for (; opt->type != OPTION_END; opt++) {
size_t pos;
int pad;
if (opt->type == OPTION_GROUP) {
fputc('\n', stderr);
if (*opt->help)
fprintf(stderr, "%s\n", opt->help);
continue;
}
pos = fprintf(stderr, " ");
if (opt->short_name)
pos += fprintf(stderr, "-%c", opt->short_name);
if (opt->short_name && opt->long_name)
pos += fprintf(stderr, ", ");
if (opt->long_name)
pos += fprintf(stderr, "--%s", opt->long_name);
if (opt->argh)
pos += fprintf(stderr, " <%s>", opt->argh);
if (pos <= USAGE_OPTS_WIDTH)
pad = USAGE_OPTS_WIDTH - pos;
else {
fputc('\n', stderr);
pad = USAGE_OPTS_WIDTH;
}
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opt->help);
}
fputc('\n', stderr);
exit(1);
}
static int get_value(const struct options *opt)
{
char *s = NULL;
switch (opt->type) {
case OPTION_BOOL:
*(int *) opt->value += 1;
break;
case OPTION_INTEGER:
*(int *) opt->value = strtol(optarg, (char **) &s, 10);
if (*s) {
error("invalid number -- %s", s);
return -1;
}
break;
case OPTION_STRING:
if (optarg)
*(char **) opt->value = (char *) optarg;
break;
case OPTION_GROUP:
case OPTION_END:
break;
}
return 0;
}
void parse_opts(int argc, char **argv, const struct options *opt,
const char *const usage[])
{
int size = parse_opt_size(opt);
int nr = 0, alloc = 0;
char *shortopts = NULL;
struct option *longopts = xmallocz(sizeof(longopts[0]) * (size + 2));
const struct options *curopt = opt;
struct option *curlongopts = longopts;
for (; curopt->type != OPTION_END; curopt++, curlongopts++) {
curlongopts->name = curopt->long_name;
switch (curopt->type) {
case OPTION_BOOL:
curlongopts->has_arg = no_argument;
if (curopt->short_name) {
ALLOC_GROW(shortopts, nr + 1, alloc);
shortopts[nr++] = curopt->short_name;
}
break;
case OPTION_INTEGER:
case OPTION_STRING:
curlongopts->has_arg = required_argument;
if (curopt->short_name) {
ALLOC_GROW(shortopts, nr + 2, alloc);
shortopts[nr++] = curopt->short_name;
shortopts[nr++] = ':';
}
break;
case OPTION_GROUP:
case OPTION_END:
break;
}
curlongopts->flag = 0;
curlongopts->val = curopt->short_name;
}
while (1) {
curopt = opt;
int c = getopt_long(argc, argv, shortopts, longopts, NULL);
if (c == -1)
break;
if (c == '?') {
free(longopts);
free(shortopts);
parse_usage_and_die(usage, opt);
}
for (; curopt->type != OPTION_END; curopt++) {
if (curopt->short_name != c)
continue;
/* for now it fails only when string is badly converted */
if (get_value(curopt) < 0)
parse_usage_and_die(usage, opt);
}
}
free(longopts);
free(shortopts);
}