diff options
Diffstat (limited to '')
-rw-r--r-- | src/opt.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/opt.c b/src/opt.c new file mode 100644 index 0000000..41dcd5e --- /dev/null +++ b/src/opt.c @@ -0,0 +1,234 @@ +/* + * Copyright 2019-2021 OARC, Inc. + * Copyright 2017-2018 Akamai Technologies + * Copyright 2006-2016 Nominum, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" + +#include "opt.h" + +#include "log.h" +#include "util.h" +#include "result.h" + +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <netinet/in.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; + bool* boolp; + unsigned int* uintp; + 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 + 1] = { 0 }; +extern 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; + + if (nopts == MAX_OPTS) { + perf_log_fatal("too many defined options"); + return; + } + opt = &opts[nopts++]; + opt->c = c; + opt->type = type; + opt->desc = desc; + opt->help = help; + if (defval != NULL) { + opt->defvalbuf[sizeof(opt->defvalbuf) - 1] = 0; + strncpy(opt->defvalbuf, defval, sizeof(opt->defvalbuf)); + if (opt->defvalbuf[sizeof(opt->defvalbuf) - 1]) { + perf_log_fatal("perf_opt_add(): defval too large"); + return; + } + opt->defval = opt->defvalbuf; + } else { + opt->defval = NULL; + } + opt->u.valp = valp; + + char newoptstr[sizeof(optstr) + 2]; + snprintf(newoptstr, sizeof(newoptstr), "%s%c%s", optstr, c, (type == perf_opt_boolean ? "" : ":")); + memcpy(optstr, newoptstr, sizeof(optstr) - 1); + optstr[sizeof(optstr) - 1] = 0; +} + +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 uint32_t +parse_uint(const char* desc, const char* str, + unsigned int min, unsigned int max) +{ + unsigned long int val; + uint32_t ret; + char* endptr = 0; + + errno = 0; + val = strtoul(str, &endptr, 10); + if (!errno && str && *str && endptr && !*endptr && val <= UINT32_MAX) { + ret = (uint32_t)val; + if (ret >= min && ret <= max) { + return ret; + } + } + + fprintf(stderr, "invalid %s: %s\n", desc, str); + perf_opt_usage(); + exit(1); +} + +static double +parse_double(const char* desc, const char* str) +{ + const char* s; + char c; + bool seen_dot = false; + + s = str; + while (*s != 0) { + c = *s++; + if (c == '.') { + if (seen_dot) + goto fail; + seen_dot = true; + } else if (c < '0' || c > '9') { + goto fail; + } + } + + return atof(str); + +fail: + fprintf(stderr, "invalid %s: %s\n", desc, str); + perf_opt_usage(); + exit(1); +} + +static uint64_t +parse_timeval(const char* desc, const char* str) +{ + return MILLION * parse_double(desc, str); +} + +void perf_opt_parse(int argc, char** argv) +{ + int c; + opt_t* opt; + unsigned int i; + + 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 = 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); + } +} |