/*
* Copyright (C) 2012 - 2015 Nominum, Inc.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice and this permission notice
* appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <isc/file.h>
#include <isc/parseint.h>
#include <isc/result.h>
#include "log.h"
#include "opt.h"
#include "util.h"
#define MAX_OPTS 64
#define LINE_LENGTH 80
typedef struct {
char c;
perf_opttype_t type;
const char *desc;
const char *help;
const char *defval;
char defvalbuf[32];
union {
void *valp;
char **stringp;
isc_boolean_t *boolp;
unsigned int *uintp;
isc_uint64_t *uint64p;
double *doublep;
in_port_t *portp;
} u;
} opt_t;
static opt_t opts[MAX_OPTS];
static unsigned int nopts;
static char optstr[MAX_OPTS * 2 + 2];
static const char *progname;
void
perf_opt_add(char c, perf_opttype_t type, const char *desc, const char *help,
const char *defval, void *valp)
{
opt_t *opt;
char s[3];
if (nopts == MAX_OPTS)
perf_log_fatal("too many defined options");
opt = &opts[nopts++];
opt->c = c;
opt->type = type;
opt->desc = desc;
opt->help = help;
if (defval != NULL) {
strncpy(opt->defvalbuf, defval, sizeof(opt->defvalbuf));
opt->defval = opt->defvalbuf;
} else {
opt->defval = NULL;
}
opt->u.valp = valp;
sprintf(s, "%c%s", c, (type == perf_opt_boolean ? "" : ":"));
strcat(optstr, s);
}
void
perf_opt_usage(void)
{
unsigned int prefix_len, position, arg_len, i, j;
prefix_len = fprintf(stderr, "Usage: %s", progname);
position = prefix_len;
for (i = 0; i < nopts; i++) {
arg_len = 6;
if (opts[i].desc != NULL)
arg_len += strlen(opts[i].desc) + 1;
if (LINE_LENGTH - position - 1 < arg_len) {
fprintf(stderr, "\n");
for (j = 0; j < prefix_len; j++)
fprintf(stderr, " ");
position = prefix_len;
}
fprintf(stderr, " [-%c", opts[i].c);
if (opts[i].desc != NULL)
fprintf(stderr, " %s", opts[i].desc);
fprintf(stderr, "]");
position += arg_len;
}
fprintf(stderr, "\n");
for (i = 0; i < nopts; i++) {
fprintf(stderr, " -%c %s", opts[i].c, opts[i].help);
if (opts[i].defval)
fprintf(stderr, " (default: %s)", opts[i].defval);
fprintf(stderr, "\n");
}
}
static isc_uint32_t
parse_uint(const char *desc, const char *str,
unsigned int min, unsigned int max)
{
isc_uint32_t val;
isc_result_t result;
val = 0;
result = isc_parse_uint32(&val, str, 10);
if (result != ISC_R_SUCCESS || val < min || val > max) {
fprintf(stderr, "invalid %s: %s\n", desc, str);
perf_opt_usage();
exit(1);
}
return val;
}
static isc_uint64_t
parse_timeval(const char *desc, const char *str)
{
const char *s;
char c;
isc_boolean_t seen_dot = ISC_FALSE;
s = str;
while (*s != 0) {
c = *s++;
if (c == '.') {
if (seen_dot)
goto fail;
seen_dot = ISC_TRUE;
} else if (c < '0' || c > '9') {
goto fail;
}
}
return MILLION * atof(str);
fail:
fprintf(stderr, "invalid %s: %s\n", desc, str);
perf_opt_usage();
exit(1);
}
static double
parse_double(const char *desc, const char *str) {
char c;
int index = 0;
isc_boolean_t seen_dot = ISC_FALSE;
while (str[index] != 0) {
c = str[index];
if (c == '.') {
if (seen_dot)
goto fail;
seen_dot = ISC_TRUE;
} else if (c < '0' || c > '9') {
goto fail;
}
index++;
}
return atof(str);
fail:
fprintf(stderr, "invalid %s: %s\n", desc, str);
perf_opt_usage();
exit(1);
}
void
perf_opt_parse(int argc, char **argv)
{
int c;
opt_t *opt;
unsigned int i;
progname = isc_file_basename(argv[0]);
perf_opt_add('h', perf_opt_boolean, NULL, "print this help",
NULL, NULL);
while ((c = getopt(argc, argv, optstr)) != -1) {
for (i = 0; i < nopts; i++) {
if (opts[i].c == c)
break;
}
if (i == nopts) {
perf_opt_usage();
exit(1);
}
if (c == 'h') {
perf_opt_usage();
exit(0);
}
opt = &opts[i];
switch (opt->type) {
case perf_opt_string:
*opt->u.stringp = optarg;
break;
case perf_opt_boolean:
*opt->u.boolp = ISC_TRUE;
break;
case perf_opt_uint:
*opt->u.uintp = parse_uint(opt->desc, optarg,
1, 0xFFFFFFFF);
break;
case perf_opt_timeval:
*opt->u.uint64p = parse_timeval(opt->desc, optarg);
break;
case perf_opt_double:
*opt->u.doublep = parse_double(opt->desc, optarg);
break;
case perf_opt_port:
*opt->u.portp = parse_uint(opt->desc, optarg,
0, 0xFFFF);
break;
}
}
if (optind != argc) {
fprintf(stderr, "unexpected argument %s\n", argv[optind]);
perf_opt_usage();
exit(1);
}
}