summaryrefslogtreecommitdiffstats
path: root/src/opt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/opt.c')
-rw-r--r--src/opt.c140
1 files changed, 135 insertions, 5 deletions
diff --git a/src/opt.c b/src/opt.c
index ad77e03..4ea798d 100644
--- a/src/opt.c
+++ b/src/opt.c
@@ -44,7 +44,7 @@ typedef struct {
const char* desc;
const char* help;
const char* defval;
- char defvalbuf[32];
+ char defvalbuf[512];
union {
void* valp;
char** stringp;
@@ -56,7 +56,28 @@ typedef struct {
} u;
} opt_t;
+typedef struct long_opt long_opt_t;
+struct long_opt {
+ long_opt_t* next;
+ const char* name;
+ perf_opttype_t type;
+ const char* desc;
+ const char* help;
+ const char* defval;
+ char defvalbuf[512];
+ union {
+ void* valp;
+ char** stringp;
+ bool* boolp;
+ unsigned int* uintp;
+ uint64_t* uint64p;
+ double* doublep;
+ in_port_t* portp;
+ } u;
+};
+
static opt_t opts[MAX_OPTS];
+static long_opt_t* longopts = 0;
static unsigned int nopts;
static char optstr[MAX_OPTS * 2 + 2 + 1] = { 0 };
extern const char* progname;
@@ -76,13 +97,13 @@ void perf_opt_add(char c, perf_opttype_t type, const char* desc, const char* hel
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]) {
+ if (strlen(defval) > sizeof(opt->defvalbuf) - 1) {
perf_log_fatal("perf_opt_add(): defval too large");
return;
}
- opt->defval = opt->defvalbuf;
+ strncpy(opt->defvalbuf, defval, sizeof(opt->defvalbuf));
+ opt->defvalbuf[sizeof(opt->defvalbuf) - 1] = 0;
+ opt->defval = opt->defvalbuf;
} else {
opt->defval = NULL;
}
@@ -94,6 +115,37 @@ void perf_opt_add(char c, perf_opttype_t type, const char* desc, const char* hel
optstr[sizeof(optstr) - 1] = 0;
}
+void perf_long_opt_add(const char* name, perf_opttype_t type, const char* desc, const char* help, const char* defval, void* valp)
+{
+ long_opt_t* opt = calloc(1, sizeof(long_opt_t));
+
+ if (!opt) {
+ perf_log_fatal("perf_long_opt_add(): out of memory");
+ return;
+ }
+
+ opt->name = name;
+ opt->type = type;
+ opt->desc = desc;
+ opt->help = help;
+ if (defval != NULL) {
+ if (strlen(defval) > sizeof(opt->defvalbuf) - 1) {
+ perf_log_fatal("perf_opt_add(): defval too large");
+ free(opt); // fix clang scan-build
+ return;
+ }
+ strncpy(opt->defvalbuf, defval, sizeof(opt->defvalbuf));
+ opt->defvalbuf[sizeof(opt->defvalbuf) - 1] = 0;
+ opt->defval = opt->defvalbuf;
+ } else {
+ opt->defval = NULL;
+ }
+ opt->u.valp = valp;
+
+ opt->next = longopts;
+ longopts = opt;
+}
+
void perf_opt_usage(void)
{
unsigned int prefix_len, position, arg_len, i, j;
@@ -181,6 +233,70 @@ parse_timeval(const char* desc, const char* str)
return MILLION * parse_double(desc, str);
}
+static int perf_opt_long_parse(char* optarg)
+{
+ ssize_t optlen;
+ char* arg;
+
+ // TODO: Allow boolean not to have =/value
+ if (!(arg = strchr(optarg, '='))) {
+ return -1;
+ }
+ optlen = arg - optarg;
+ if (optlen < 1) {
+ return -1;
+ }
+ arg++;
+
+ long_opt_t* opt = longopts;
+ while (opt) {
+ if (!strncmp(optarg, opt->name, optlen)) {
+ switch (opt->type) {
+ case perf_opt_string:
+ *opt->u.stringp = arg;
+ break;
+ case perf_opt_boolean:
+ *opt->u.boolp = true;
+ break;
+ case perf_opt_uint:
+ *opt->u.uintp = parse_uint(opt->desc, arg, 1, 0xFFFFFFFF);
+ break;
+ case perf_opt_zpint:
+ *opt->u.uintp = parse_uint(opt->desc, arg, 0, 0xFFFFFFFF);
+ break;
+ case perf_opt_timeval:
+ *opt->u.uint64p = parse_timeval(opt->desc, arg);
+ break;
+ case perf_opt_double:
+ *opt->u.doublep = parse_double(opt->desc, arg);
+ break;
+ case perf_opt_port:
+ *opt->u.portp = parse_uint(opt->desc, arg, 0, 0xFFFF);
+ break;
+ }
+ return 0;
+ }
+ opt = opt->next;
+ }
+
+ return -1;
+}
+
+void perf_long_opt_usage(void)
+{
+ fprintf(stderr, "Usage: %s ... -O <name>=<value> ...\n\nAvailable long options:\n", progname);
+ long_opt_t* opt = longopts;
+ while (opt) {
+ fprintf(stderr, " %s: %s", opt->name, opt->help);
+ if (opt->defval) {
+ fprintf(stderr, " (default: %s)", opt->defval);
+ }
+ fprintf(stderr, "\n");
+
+ opt = opt->next;
+ }
+}
+
void perf_opt_parse(int argc, char** argv)
{
int c;
@@ -188,6 +304,8 @@ void perf_opt_parse(int argc, char** argv)
unsigned int i;
perf_opt_add('h', perf_opt_boolean, NULL, "print this help", NULL, NULL);
+ perf_opt_add('H', perf_opt_boolean, NULL, "print long options help", NULL, NULL);
+ perf_opt_add('O', perf_opt_string, NULL, "set long options: <name>=<value>", NULL, NULL);
while ((c = getopt(argc, argv, optstr)) != -1) {
for (i = 0; i < nopts; i++) {
@@ -202,6 +320,18 @@ void perf_opt_parse(int argc, char** argv)
perf_opt_usage();
exit(0);
}
+ if (c == 'H') {
+ perf_long_opt_usage();
+ exit(0);
+ }
+ if (c == 'O') {
+ if (perf_opt_long_parse(optarg)) {
+ fprintf(stderr, "invalid long option: %s\n", optarg);
+ perf_opt_usage();
+ exit(1);
+ }
+ continue;
+ }
opt = &opts[i];
switch (opt->type) {
case perf_opt_string: