diff options
Diffstat (limited to 'util/argconfig.c')
-rw-r--r-- | util/argconfig.c | 594 |
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; + } + } +} |