summaryrefslogtreecommitdiffstats
path: root/util/argconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/argconfig.c')
-rw-r--r--util/argconfig.c594
1 files changed, 594 insertions, 0 deletions
diff --git a/util/argconfig.c b/util/argconfig.c
new file mode 100644
index 0000000..231a704
--- /dev/null
+++ b/util/argconfig.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2014 PMC-Sierra, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ *
+ * Author: Logan Gunthorpe
+ *
+ * Date: Oct 23 2014
+ *
+ * Description:
+ * Functions for parsing command line options.
+ *
+ */
+
+#include "argconfig.h"
+#include "suffix.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+
+static argconfig_help_func *help_funcs[MAX_HELP_FUNC] = { NULL };
+
+static char END_DEFAULT[] = "__end_default__";
+
+static const char *append_usage_str = "";
+
+void argconfig_append_usage(const char *str)
+{
+ append_usage_str = str;
+}
+
+void print_word_wrapped(const char *s, int indent, int start, FILE *stream)
+{
+ const int width = 76;
+ const char *c, *t;
+ int next_space = -1;
+ int last_line = indent;
+
+ while (start < indent) {
+ putc(' ', stream);
+ start++;
+ }
+
+ for (c = s; *c != 0; c++) {
+ if (*c == '\n')
+ goto new_line;
+
+ if (*c == ' ' || next_space < 0) {
+ next_space = 0;
+ for (t = c + 1; *t != 0 && *t != ' '; t++)
+ next_space++;
+
+ if (((int)(c - s) + start + next_space) > (last_line - indent + width)) {
+ int i;
+new_line:
+ last_line = (int) (c-s) + start;
+ putc('\n', stream);
+ for (i = 0; i < indent; i++)
+ putc(' ', stream);
+ start = indent;
+ continue;
+ }
+ }
+ putc(*c, stream);
+ }
+}
+
+static void show_option(const struct argconfig_commandline_options *option)
+{
+ char buffer[0x1000];
+ char *b = buffer;
+
+ b += sprintf(b, " [ ");
+ if (option->option) {
+ b += sprintf(b, " --%s", option->option);
+ if (option->argument_type == optional_argument)
+ b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg");
+ if (option->argument_type == required_argument)
+ b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg");
+ if (option->short_option)
+ b += sprintf(b, ",");
+ }
+ if (option->short_option) {
+ b += sprintf(b, " -%c", option->short_option);
+ if (option->argument_type == optional_argument)
+ b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg");
+ if (option->argument_type == required_argument)
+ b += sprintf(b, " <%s>", option->meta ? option->meta : "arg");
+ }
+ b += sprintf(b, " ] ");
+
+ fprintf(stderr, "%s", buffer);
+ if (option->help) {
+ print_word_wrapped("--- ", 40, b - buffer, stderr);
+ print_word_wrapped(option->help, 44, 44, stderr);
+ }
+ fprintf(stderr, "\n");
+}
+
+void argconfig_print_help(const char *program_desc,
+ const struct argconfig_commandline_options *options)
+{
+ const struct argconfig_commandline_options *s;
+
+ fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n",
+ append_usage_str);
+
+ print_word_wrapped(program_desc, 0, 0, stderr);
+ fprintf(stderr, "\n");
+
+ if (!options || !options->option)
+ return;
+
+ fprintf(stderr, "\n\033[1mOptions:\033[0m\n");
+ for (s = options; (s != NULL) && (s->option != NULL); s++)
+ show_option(s);
+}
+
+int argconfig_parse_byte(const char *opt, const char *str, unsigned char *val)
+{
+ char *endptr;
+ unsigned long tmp = strtoul(str, &endptr, 0);
+
+ if (errno || tmp >= 1 << 8 || str == endptr) {
+ fprintf(stderr,
+ "Expected byte argument for '%s' but got '%s'!\n", opt,
+ str);
+ return -EINVAL;
+ }
+
+ *val = tmp;
+
+ return 0;
+}
+
+int argconfig_parse(int argc, char *argv[], const char *program_desc,
+ const struct argconfig_commandline_options *options)
+{
+ char *short_opts;
+ char *endptr;
+ struct option *long_opts;
+ const struct argconfig_commandline_options *s;
+ int c, option_index = 0, short_index = 0, options_count = 0;
+ void *value_addr;
+ int ret = -EINVAL;
+
+ errno = 0;
+ for (s = options; s->option != NULL; s++)
+ options_count++;
+
+ long_opts = malloc(sizeof(struct option) * (options_count + 2));
+ short_opts = malloc(sizeof(*short_opts) * (options_count * 3 + 4));
+
+ if (!long_opts || !short_opts) {
+ fprintf(stderr, "failed to allocate memory for opts: %s\n",
+ strerror(errno));
+ ret = -errno;
+ goto out;
+ }
+
+ for (s = options; (s->option != NULL) && (option_index < options_count);
+ s++) {
+ if (s->short_option != 0) {
+ short_opts[short_index++] = s->short_option;
+ if (s->argument_type == required_argument ||
+ s->argument_type == optional_argument)
+ short_opts[short_index++] = ':';
+ if (s->argument_type == optional_argument)
+ short_opts[short_index++] = ':';
+ }
+ if (s->option && strlen(s->option)) {
+ long_opts[option_index].name = s->option;
+ long_opts[option_index].has_arg = s->argument_type;
+ long_opts[option_index].flag = NULL;
+ long_opts[option_index].val = 0;
+ }
+ option_index++;
+ }
+
+ long_opts[option_index].name = "help";
+ long_opts[option_index].flag = NULL;
+ long_opts[option_index].val = 'h';
+ option_index++;
+
+ long_opts[option_index].name = NULL;
+ long_opts[option_index].flag = NULL;
+ long_opts[option_index].val = 0;
+
+ short_opts[short_index++] = '?';
+ short_opts[short_index++] = 'h';
+ short_opts[short_index] = 0;
+
+ optind = 0;
+ while ((c = getopt_long_only(argc, argv, short_opts, long_opts,
+ &option_index)) != -1) {
+ if (c != 0) {
+ if (c == '?' || c == 'h') {
+ argconfig_print_help(program_desc, options);
+ goto out;
+ }
+ for (option_index = 0; option_index < options_count;
+ option_index++) {
+ if (c == options[option_index].short_option)
+ break;
+ }
+ if (option_index == options_count)
+ continue;
+ }
+
+ s = &options[option_index];
+ value_addr = (void *)(char *)s->default_value;
+ if (s->config_type == CFG_STRING) {
+ *((char **)value_addr) = optarg;
+ } else if (s->config_type == CFG_SIZE) {
+ *((size_t *) value_addr) = strtol(optarg, &endptr, 0);
+ if (errno || optarg == endptr) {
+ fprintf(stderr,
+ "Expected integer argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ } else if (s->config_type == CFG_INT) {
+ *((int *)value_addr) = strtol(optarg, &endptr, 0);
+ if (errno || optarg == endptr) {
+ fprintf(stderr,
+ "Expected integer argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ } else if (s->config_type == CFG_BOOL) {
+ int tmp = strtol(optarg, &endptr, 0);
+ if (errno || tmp < 0 || tmp > 1 || optarg == endptr) {
+ fprintf(stderr,
+ "Expected 0 or 1 argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ *((int *)value_addr) = tmp;
+ } else if (s->config_type == CFG_BYTE) {
+ if (argconfig_parse_byte(long_opts[option_index].name,
+ optarg, (uint8_t *)value_addr))
+ goto out;
+ } else if (s->config_type == CFG_SHORT) {
+ unsigned long tmp = strtoul(optarg, &endptr, 0);
+ if (errno || tmp >= (1 << 16) || optarg == endptr) {
+ fprintf(stderr,
+ "Expected short argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ *((uint16_t *) value_addr) = tmp;
+ } else if (s->config_type == CFG_POSITIVE) {
+ uint32_t tmp = strtoul(optarg, &endptr, 0);
+ if (errno || optarg == endptr) {
+ fprintf(stderr,
+ "Expected word argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ *((uint32_t *) value_addr) = tmp;
+ } else if (s->config_type == CFG_INCREMENT) {
+ *((int *)value_addr) += 1;
+ } else if (s->config_type == CFG_LONG) {
+ *((unsigned long *)value_addr) = strtoul(optarg, &endptr, 0);
+ if (errno || optarg == endptr) {
+ fprintf(stderr,
+ "Expected long integer argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ } else if (s->config_type == CFG_LONG_SUFFIX) {
+ *((uint64_t *)value_addr) = suffix_binary_parse(optarg);
+ if (errno) {
+ fprintf(stderr,
+ "Expected long suffixed integer argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ } else if (s->config_type == CFG_DOUBLE) {
+ *((double *)value_addr) = strtod(optarg, &endptr);
+ if (errno || optarg == endptr) {
+ fprintf(stderr,
+ "Expected float argument for '%s' but got '%s'!\n",
+ long_opts[option_index].name, optarg);
+ goto out;
+ }
+ } else if (s->config_type == CFG_SUBOPTS) {
+ char **opts = ((char **)value_addr);
+ int remaining_space = CFG_MAX_SUBOPTS;
+ int enddefault = 0;
+ int r;
+ while (0 && *opts != NULL) {
+ if (*opts == END_DEFAULT)
+ enddefault = 1;
+ remaining_space--;
+ opts++;
+ }
+
+ if (!enddefault) {
+ *opts = END_DEFAULT;
+ remaining_space -= 2;
+ opts += 2;
+ }
+
+ r = argconfig_parse_subopt_string(optarg, opts,
+ remaining_space);
+ if (r == 2) {
+ fprintf(stderr,
+ "Error Parsing Sub-Options: Too many options!\n");
+ goto out;
+ } else if (r) {
+ fprintf(stderr, "Error Parsing Sub-Options\n");
+ goto out;
+ }
+ } else if (s->config_type == CFG_FILE_A ||
+ s->config_type == CFG_FILE_R ||
+ s->config_type == CFG_FILE_W ||
+ s->config_type == CFG_FILE_AP ||
+ s->config_type == CFG_FILE_RP ||
+ s->config_type == CFG_FILE_WP) {
+ const char *fopts = "";
+ FILE *f;
+ if (s->config_type == CFG_FILE_A)
+ fopts = "a";
+ else if (s->config_type == CFG_FILE_R)
+ fopts = "r";
+ else if (s->config_type == CFG_FILE_W)
+ fopts = "w";
+ else if (s->config_type == CFG_FILE_AP)
+ fopts = "a+";
+ else if (s->config_type == CFG_FILE_RP)
+ fopts = "r+";
+ else if (s->config_type == CFG_FILE_WP)
+ fopts = "w+";
+
+ f = fopen(optarg, fopts);
+ if (f == NULL) {
+ fprintf(stderr, "Unable to open %s file: %s\n",
+ s->option, optarg);
+ goto out;
+ }
+ *((FILE **) value_addr) = f;
+ } else if (s->config_type == CFG_FLAG) {
+ *((bool *)value_addr) = true;
+ }
+ }
+ free(short_opts);
+ free(long_opts);
+
+ return 0;
+ out:
+ free(short_opts);
+ free(long_opts);
+ return ret;
+}
+
+int argconfig_parse_subopt_string(char *string, char **options,
+ size_t max_options)
+{
+ char **o = options;
+ char *tmp;
+ size_t toklen;
+
+ if (!string || !strlen(string)) {
+ *(o++) = NULL;
+ *(o++) = NULL;
+ return 0;
+ }
+
+ tmp = calloc(strlen(string) + 2, 1);
+ if (!tmp)
+ return 1;
+ strcpy(tmp, string);
+
+ toklen = strcspn(tmp, "=");
+
+ if (!toklen) {
+ free(tmp);
+ return 1;
+ }
+
+ *(o++) = tmp;
+ tmp[toklen] = 0;
+ tmp += toklen + 1;
+
+ while (1) {
+ if (*tmp == '"' || *tmp == '\'' || *tmp == '[' || *tmp == '(' ||
+ *tmp == '{') {
+
+ tmp++;
+ toklen = strcspn(tmp, "\"'])}");
+
+ if (!toklen)
+ return 1;
+
+ *(o++) = tmp;
+ tmp[toklen] = 0;
+ tmp += toklen + 1;
+
+ toklen = strcspn(tmp, ";:,");
+ tmp[toklen] = 0;
+ tmp += toklen + 1;
+ } else {
+ toklen = strcspn(tmp, ";:,");
+
+ if (!toklen)
+ return 1;
+
+ *(o++) = tmp;
+ tmp[toklen] = 0;
+ tmp += toklen + 1;
+ }
+
+ toklen = strcspn(tmp, "=");
+
+ if (!toklen)
+ break;
+
+ *(o++) = tmp;
+ tmp[toklen] = 0;
+ tmp += toklen + 1;
+
+ if ((o - options) > (max_options - 2))
+ return 2;
+ }
+
+ *(o++) = NULL;
+ *(o++) = NULL;
+
+ return 0;
+}
+
+int argconfig_parse_comma_sep_array(char *string, int *val,
+ unsigned max_length)
+{
+ int ret = 0;
+ unsigned long v;
+ char *tmp;
+ char *p;
+
+ if (!string || !strlen(string))
+ return 0;
+
+ tmp = strtok(string, ",");
+ if (!tmp)
+ return 0;
+
+ v = strtoul(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ if (v > UINT_MAX) {
+ fprintf(stderr, "%s out of range\n", tmp);
+ return -1;
+ }
+ val[ret] = v;
+
+ ret++;
+ while (1) {
+ tmp = strtok(NULL, ",");
+
+ if (tmp == NULL)
+ return ret;
+
+ if (ret >= max_length)
+ return -1;
+
+ v = strtoul(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ if (v > UINT_MAX) {
+ fprintf(stderr, "%s out of range\n", tmp);
+ return -1;
+ }
+ val[ret] = v;
+ ret++;
+ }
+}
+
+int argconfig_parse_comma_sep_array_short(char *string, unsigned short *val,
+ unsigned max_length)
+{
+ int ret = 0;
+ unsigned long v;
+ char *tmp;
+ char *p;
+
+ if (!string || !strlen(string))
+ return 0;
+
+ tmp = strtok(string, ",");
+ if (!tmp)
+ return 0;
+
+ v = strtoul(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ if (v > UINT16_MAX) {
+ fprintf(stderr, "%s out of range\n", tmp);
+ return -1;
+ }
+ val[ret] = v;
+ ret++;
+
+ while (1) {
+ tmp = strtok(NULL, ",");
+ if (tmp == NULL)
+ return ret;
+
+ if (ret >= max_length)
+ return -1;
+
+ v = strtoul(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ if (v > UINT16_MAX) {
+ fprintf(stderr, "%s out of range\n", tmp);
+ return -1;
+ }
+ val[ret] = v;
+ ret++;
+ }
+}
+
+int argconfig_parse_comma_sep_array_long(char *string,
+ unsigned long long *val,
+ unsigned max_length)
+{
+ int ret = 0;
+ char *tmp;
+ char *p;
+
+ if (!string || !strlen(string))
+ return 0;
+
+ tmp = strtok(string, ",");
+ if (tmp == NULL)
+ return 0;
+
+ val[ret] = strtoll(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ ret++;
+ while (1) {
+ tmp = strtok(NULL, ",");
+
+ if (tmp == NULL)
+ return ret;
+
+ if (ret >= max_length)
+ return -1;
+
+ val[ret] = strtoll(tmp, &p, 0);
+ if (*p != 0)
+ return -1;
+ ret++;
+ }
+}
+
+void argconfig_register_help_func(argconfig_help_func * f)
+{
+ int i;
+ for (i = 0; i < MAX_HELP_FUNC; i++) {
+ if (help_funcs[i] == NULL) {
+ help_funcs[i] = f;
+ if (i < MAX_HELP_FUNC - 1)
+ help_funcs[i + 1] = NULL;
+ break;
+ }
+ }
+}