diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-02 20:40:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-02 20:40:30 +0000 |
commit | dc597ce8df5ae6efd2728a2d7ba7d92486028f79 (patch) | |
tree | 55b9e9257eba4579667f9522368aa29f5be6754a /util | |
parent | Initial commit. (diff) | |
download | nvme-cli-dc597ce8df5ae6efd2728a2d7ba7d92486028f79.tar.xz nvme-cli-dc597ce8df5ae6efd2728a2d7ba7d92486028f79.zip |
Adding upstream version 1.12.upstream/1.12
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | util/argconfig.c | 534 | ||||
-rw-r--r-- | util/argconfig.h | 130 | ||||
-rw-r--r-- | util/json.c | 407 | ||||
-rw-r--r-- | util/json.h | 88 | ||||
-rw-r--r-- | util/parser.c | 284 | ||||
-rw-r--r-- | util/parser.h | 34 | ||||
-rw-r--r-- | util/suffix.c | 132 | ||||
-rw-r--r-- | util/suffix.h | 41 |
8 files changed, 1650 insertions, 0 deletions
diff --git a/util/argconfig.c b/util/argconfig.c new file mode 100644 index 0000000..f647448 --- /dev/null +++ b/util/argconfig.c @@ -0,0 +1,534 @@ +/* + * 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 <string.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <inttypes.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) +{ + const int width = 76; + const char *c, *t; + int next_space = -1; + int last_line = indent; + + while (start < indent) { + putc(' ', stderr); + 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', stderr); + for (i = 0; i < indent; i++) + putc(' ', stderr); + start = indent; + continue; + } + } + putc(*c, stderr); + } +} + +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); + print_word_wrapped(option->help, 44, 44); + } + fprintf(stderr, "\n"); +} + +void argconfig_print_help(const char *program_desc, + const struct argconfig_commandline_options *options) +{ + const struct argconfig_commandline_options *s; + + printf("\033[1mUsage: %s\033[0m\n\n", + append_usage_str); + + print_word_wrapped(program_desc, 0, 0); + printf("\n"); + + if (!options || !options->option) + return; + + printf("\n\033[1mOptions:\033[0m\n"); + for (s = options; (s->option != NULL) && (s != NULL); s++) + show_option(s); +} + +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; + + if (s->argument_type == no_argument + && s->default_value != NULL) { + value_addr = (void *)(char *)s->default_value; + + long_opts[option_index].flag = value_addr; + long_opts[option_index].val = 1; + } else { + 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; + if (long_opts[option_index].flag) { + *(uint8_t *)(long_opts[option_index].flag) = 1; + 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) { + unsigned long tmp = strtoul(optarg, &endptr, 0); + if (errno || tmp >= (1 << 8) || optarg == endptr) { + fprintf(stderr, + "Expected byte argument for '%s' but got '%s'!\n", + long_opts[option_index].name, optarg); + goto out; + } + *((uint8_t *) value_addr) = tmp; + } 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))++; + } 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; + } + } + 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; +} + +unsigned argconfig_parse_comma_sep_array(char *string, int *val, + unsigned max_length) +{ + unsigned ret = 0; + char *tmp; + char *p; + + if (!string || !strlen(string)) + return 0; + + tmp = strtok(string, ","); + if (!tmp) + return 0; + + val[ret] = strtol(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] = strtol(tmp, &p, 0); + + if (*p != 0) + return -1; + ret++; + } +} + +unsigned argconfig_parse_comma_sep_array_long(char *string, + unsigned long long *val, + unsigned max_length) +{ + unsigned 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; + help_funcs[i + 1] = NULL; + break; + } + } +} diff --git a/util/argconfig.h b/util/argconfig.h new file mode 100644 index 0000000..623b832 --- /dev/null +++ b/util/argconfig.h @@ -0,0 +1,130 @@ +/* + * + * 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 <logang@deltatee.com> + * Logan Gunthorpe + * + * Date: Oct 23 2014 + * + * Description: + * Header file for argconfig.c + * + */ + +#ifndef argconfig_H +#define argconfig_H + +#include <string.h> +#include <getopt.h> +#include <stdarg.h> + +enum argconfig_types { + CFG_NONE, + CFG_STRING, + CFG_INT, + CFG_SIZE, + CFG_LONG, + CFG_LONG_SUFFIX, + CFG_DOUBLE, + CFG_BOOL, + CFG_BYTE, + CFG_SHORT, + CFG_POSITIVE, + CFG_INCREMENT, + CFG_SUBOPTS, + CFG_FILE_A, + CFG_FILE_W, + CFG_FILE_R, + CFG_FILE_AP, + CFG_FILE_WP, + CFG_FILE_RP, +}; + +#define OPT_ARGS(n) \ + const struct argconfig_commandline_options n[] + +#define OPT_END() { NULL } + +#define OPT_FLAG(l, s, v, d) \ + {l, s, NULL, CFG_NONE, v, no_argument, d} + +#define OPT_SUFFIX(l, s, v, d) \ + {l, s, "IONUM", CFG_LONG_SUFFIX, v, required_argument, d} + +#define OPT_LONG(l, s, v, d) \ + {l, s, "NUM", CFG_LONG, v, required_argument, d} + +#define OPT_UINT(l, s, v, d) \ + {l, s, "NUM", CFG_POSITIVE, v, required_argument, d} + +#define OPT_INT(l, s, v, d) \ + {l, s, "NUM", CFG_INT, v, required_argument, d} + +#define OPT_LONG(l, s, v, d) \ + {l, s, "NUM", CFG_LONG, v, required_argument, d} + +#define OPT_DOUBLE(l, s, v, d) \ + {l, s, "NUM", CFG_DOUBLE, v, required_argument, d} + +#define OPT_BYTE(l, s, v, d) \ + {l, s, "NUM", CFG_BYTE, v, required_argument, d} + +#define OPT_SHRT(l, s, v, d) \ + {l, s, "NUM", CFG_SHORT, v, required_argument, d} + +#define OPT_STRING(l, s, m, v, d) \ + {l, s, m, CFG_STRING, v, required_argument, d} + +#define OPT_FMT(l, s, v, d) OPT_STRING(l, s, "FMT", v, d) +#define OPT_FILE(l, s, v, d) OPT_STRING(l, s, "FILE", v, d) +#define OPT_LIST(l, s, v, d) OPT_STRING(l, s, "LIST", v, d) + +struct argconfig_commandline_options { + const char *option; + const char short_option; + const char *meta; + enum argconfig_types config_type; + void *default_value; + int argument_type; + const char *help; +}; + +#define CFG_MAX_SUBOPTS 500 +#define MAX_HELP_FUNC 20 + +typedef void argconfig_help_func(); +void argconfig_append_usage(const char *str); +void argconfig_print_help(const char *program_desc, + const struct argconfig_commandline_options *options); +int argconfig_parse(int argc, char *argv[], const char *program_desc, + const struct argconfig_commandline_options *options); +int argconfig_parse_subopt_string(char *string, char **options, + size_t max_options); +unsigned argconfig_parse_comma_sep_array(char *string, int *ret, + unsigned max_length); +unsigned argconfig_parse_comma_sep_array_long(char *string, + unsigned long long *ret, + unsigned max_length); +void argconfig_register_help_func(argconfig_help_func * f); + +void print_word_wrapped(const char *s, int indent, int start); +#endif diff --git a/util/json.c b/util/json.c new file mode 100644 index 0000000..2978410 --- /dev/null +++ b/util/json.c @@ -0,0 +1,407 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include "json.h" + +static inline void fail_and_notify(void) +{ + fprintf(stderr, "Allocation of memory for json object failed, aborting.\n"); + abort(); +} + +struct json_object *json_create_object(void) +{ + void *test = calloc(1, sizeof(struct json_object)); + if (!test) + fail_and_notify(); + return test; +} + +struct json_array *json_create_array(void) +{ + void *test = calloc(1, sizeof(struct json_array)); + if (!test) + fail_and_notify(); + return test; +} + +static struct json_pair *json_create_pair(const char *name, struct json_value *value) +{ + struct json_pair *pair = malloc(sizeof(struct json_pair)); + if (pair) { + pair->name = strdup(name); + pair->value = value; + + value->parent_type = JSON_PARENT_TYPE_PAIR; + value->parent_pair = pair; + } else + fail_and_notify(); + + return pair; +} + +static struct json_value *json_create_value_int(long long number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_INTEGER; + value->integer_number = number; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_uint(unsigned long long number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_UINT; + value->uint_number = number; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_float(long double number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_FLOAT; + value->float_number = number; + } else + fail_and_notify(); + + return value; +} + +static char *strdup_escape(const char *str) +{ + const char *input = str; + char *p, *ret; + int escapes; + + if (!strlen(str)) + return NULL; + + escapes = 0; + while ((input = strpbrk(input, "\\\"")) != NULL) { + escapes++; + input++; + } + + p = ret = malloc(strlen(str) + escapes + 1); + if (!ret) + fail_and_notify(); + + while (*str) { + if (*str == '\\' || *str == '\"') + *p++ = '\\'; + *p++ = *str++; + } + *p = '\0'; + + return ret; +} + +/* + * Valid JSON strings must escape '"' and '/' with a preceding '/' + */ +static struct json_value *json_create_value_string(const char *str) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_STRING; + value->string = strdup_escape(str ? str : "(null)"); + if (!value->string) { + free(value); + value = NULL; + return value; + } + } + if (!value) + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_object(struct json_object *obj) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_OBJECT; + value->object = obj; + obj->parent = value; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_array(struct json_array *array) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_ARRAY; + value->array = array; + array->parent = value; + } else + fail_and_notify(); + + return value; +} + +static void json_free_pair(struct json_pair *pair); +static void json_free_value(struct json_value *value); + +void json_free_object(struct json_object *obj) +{ + int i; + + for (i = 0; i < obj->pair_cnt; i++) + json_free_pair(obj->pairs[i]); + free(obj->pairs); + free(obj); +} + +void json_free_array(struct json_array *array) +{ + int i; + + for (i = 0; i < array->value_cnt; i++) + json_free_value(array->values[i]); + free(array->values); + free(array); +} + +static void json_free_pair(struct json_pair *pair) +{ + json_free_value(pair->value); + free(pair->name); + free(pair); +} + +static void json_free_value(struct json_value *value) +{ + switch (value->type) { + case JSON_TYPE_STRING: + free(value->string); + break; + case JSON_TYPE_OBJECT: + json_free_object(value->object); + break; + case JSON_TYPE_ARRAY: + json_free_array(value->array); + break; + } + free(value); +} + +static int json_array_add_value(struct json_array *array, struct json_value *value) +{ + struct json_value **values = realloc(array->values, + sizeof(struct json_value *) * (array->value_cnt + 1)); + + if (!values) + return ENOMEM; + values[array->value_cnt] = value; + array->value_cnt++; + array->values = values; + + value->parent_type = JSON_PARENT_TYPE_ARRAY; + value->parent_array = array; + return 0; +} + +static int json_object_add_pair(struct json_object *obj, struct json_pair *pair) +{ + struct json_pair **pairs = realloc(obj->pairs, + sizeof(struct json_pair *) * (obj->pair_cnt + 1)); + if (!pairs) + return ENOMEM; + pairs[obj->pair_cnt] = pair; + obj->pair_cnt++; + obj->pairs = pairs; + + pair->parent = obj; + return 0; +} + +int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...) +{ + struct json_value *value; + struct json_pair *pair; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long long)); + else if (type == JSON_TYPE_UINT) + value = json_create_value_uint(va_arg(args, unsigned long long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, long double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + pair = json_create_pair(name, value); + if (!pair) { + json_free_value(value); + return ENOMEM; + } + ret = json_object_add_pair(obj, pair); + if (ret) { + json_free_pair(pair); + return ENOMEM; + } + return 0; +} + +static void json_print_array(struct json_array *array, void *); +int json_array_add_value_type(struct json_array *array, int type, ...) +{ + struct json_value *value; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long long)); + else if (type == JSON_TYPE_UINT) + value = json_create_value_uint(va_arg(args, unsigned long long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + ret = json_array_add_value(array, value); + if (ret) { + json_free_value(value); + return ENOMEM; + } + return 0; +} + +static int json_value_level(struct json_value *value); +static int json_pair_level(struct json_pair *pair); +static int json_array_level(struct json_array *array); +static int json_object_level(struct json_object *object) +{ + if (object->parent == NULL) + return 0; + return json_value_level(object->parent); +} + +static int json_pair_level(struct json_pair *pair) +{ + return json_object_level(pair->parent) + 1; +} + +static int json_array_level(struct json_array *array) +{ + return json_value_level(array->parent); +} + +static int json_value_level(struct json_value *value) +{ + if (value->parent_type == JSON_PARENT_TYPE_PAIR) + return json_pair_level(value->parent_pair); + else + return json_array_level(value->parent_array) + 1; +} + +static void json_print_level(int level, void *out) +{ + while (level-- > 0) + printf(" "); +} + +static void json_print_pair(struct json_pair *pair, void *); +static void json_print_array(struct json_array *array, void *); +static void json_print_value(struct json_value *value, void *); +void json_print_object(struct json_object *obj, void *out) +{ + int i; + + printf("{\n"); + for (i = 0; i < obj->pair_cnt; i++) { + if (i > 0) + printf(",\n"); + json_print_pair(obj->pairs[i], out); + } + printf("\n"); + json_print_level(json_object_level(obj), out); + printf("}"); +} + +static void json_print_pair(struct json_pair *pair, void *out) +{ + json_print_level(json_pair_level(pair), out); + printf("\"%s\" : ", pair->name); + json_print_value(pair->value, out); +} + +static void json_print_array(struct json_array *array, void *out) +{ + int i; + + printf("[\n"); + for (i = 0; i < array->value_cnt; i++) { + if (i > 0) + printf(",\n"); + json_print_level(json_value_level(array->values[i]), out); + json_print_value(array->values[i], out); + } + printf("\n"); + json_print_level(json_array_level(array), out); + printf("]"); +} + +static void json_print_value(struct json_value *value, void *out) +{ + switch (value->type) { + case JSON_TYPE_STRING: + printf( "\"%s\"", value->string); + break; + case JSON_TYPE_INTEGER: + printf( "%lld", value->integer_number); + break; + case JSON_TYPE_UINT: + printf( "%llu", value->uint_number); + break; + case JSON_TYPE_FLOAT: + printf( "%.0Lf", value->float_number); + break; + case JSON_TYPE_OBJECT: + json_print_object(value->object, out); + break; + case JSON_TYPE_ARRAY: + json_print_array(value->array, out); + break; + } +} diff --git a/util/json.h b/util/json.h new file mode 100644 index 0000000..d78d3db --- /dev/null +++ b/util/json.h @@ -0,0 +1,88 @@ +#ifndef __JSON__H +#define __JSON__H + +struct json_object; +struct json_array; +struct json_pair; + +#define JSON_TYPE_STRING 0 +#define JSON_TYPE_INTEGER 1 +#define JSON_TYPE_FLOAT 2 +#define JSON_TYPE_OBJECT 3 +#define JSON_TYPE_ARRAY 4 +#define JSON_TYPE_UINT 5 +#define JSON_PARENT_TYPE_PAIR 0 +#define JSON_PARENT_TYPE_ARRAY 1 +struct json_value { + int type; + union { + long long integer_number; + unsigned long long uint_number; + long double float_number; + char *string; + struct json_object *object; + struct json_array *array; + }; + int parent_type; + union { + struct json_pair *parent_pair; + struct json_array *parent_array; + }; +}; + +struct json_array { + struct json_value **values; + int value_cnt; + struct json_value *parent; +}; + +struct json_object { + struct json_pair **pairs; + int pair_cnt; + struct json_value *parent; +}; + +struct json_pair { + char *name; + struct json_value *value; + struct json_object *parent; +}; + +struct json_object *json_create_object(void); +struct json_array *json_create_array(void); + +void json_free_object(struct json_object *obj); +void json_free_array(struct json_array *array); + +int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...); +#define json_object_add_value_int(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_INTEGER, (long long) (val)) +#define json_object_add_value_uint(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_UINT, (unsigned long long) (val)) +#define json_object_add_value_float(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_FLOAT, (val)) +#define json_object_add_value_string(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_STRING, (val)) +#define json_object_add_value_object(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_OBJECT, (val)) +#define json_object_add_value_array(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_ARRAY, (val)) +int json_array_add_value_type(struct json_array *array, int type, ...); +#define json_array_add_value_int(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_INTEGER, (val)) +#define json_array_add_value_uint(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_UINT, (val)) +#define json_array_add_value_float(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_FLOAT, (val)) +#define json_array_add_value_string(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_STRING, (val)) +#define json_array_add_value_object(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_OBJECT, (val)) +#define json_array_add_value_array(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_ARRAY, (val)) + +#define json_array_last_value_object(obj) \ + (obj->values[obj->value_cnt - 1]->object) + +void json_print_object(struct json_object *obj, void *); +#endif diff --git a/util/parser.c b/util/parser.c new file mode 100644 index 0000000..2dd0922 --- /dev/null +++ b/util/parser.c @@ -0,0 +1,284 @@ +/* + * lib/parser.c - simple parser for mount, etc. options. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include "parser.h" + +/** + * match_one: - Determines if a string matches a simple pattern + * @s: the string to examine for presence of the pattern + * @p: the string containing the pattern + * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match + * locations. + * + * Description: Determines if the pattern @p is present in string @s. Can only + * match extremely simple token=arg style patterns. If the pattern is found, + * the location(s) of the arguments will be returned in the @args array. + */ +static int match_one(char *s, const char *p, substring_t args[]) +{ + char *meta; + int argc = 0; + + if (!p) + return 1; + + while(1) { + int len = -1; + meta = strchr(p, '%'); + if (!meta) + return strcmp(p, s) == 0; + + if (strncmp(p, s, meta-p)) + return 0; + + s += meta - p; + p = meta + 1; + + if (isdigit(*p)) + len = strtoul(p, (char **) &p, 10); + else if (*p == '%') { + if (*s++ != '%') + return 0; + p++; + continue; + } + + if (argc >= MAX_OPT_ARGS) + return 0; + + args[argc].from = s; + switch (*p++) { + case 's': { + size_t str_len = strlen(s); + + if (str_len == 0) + return 0; + if (len == -1 || len > str_len) + len = str_len; + args[argc].to = s + len; + break; + } + case 'd': + strtol(s, &args[argc].to, 0); + goto num; + case 'u': + strtoul(s, &args[argc].to, 0); + goto num; + case 'o': + strtoul(s, &args[argc].to, 8); + goto num; + case 'x': + strtoul(s, &args[argc].to, 16); + num: + if (args[argc].to == args[argc].from) + return 0; + break; + default: + return 0; + } + s = args[argc].to; + argc++; + } +} + +/** + * match_token: - Find a token (and optional args) in a string + * @s: the string to examine for token/argument pairs + * @table: match_table_t describing the set of allowed option tokens and the + * arguments that may be associated with them. Must be terminated with a + * &struct match_token whose pattern is set to the NULL pointer. + * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match + * locations. + * + * Description: Detects which if any of a set of token strings has been passed + * to it. Tokens can include up to MAX_OPT_ARGS instances of basic c-style + * format identifiers which will be taken into account when matching the + * tokens, and whose locations will be returned in the @args array. + */ +int match_token(char *s, const match_table_t table, substring_t args[]) +{ + const struct match_token *p; + + for (p = table; !match_one(s, p->pattern, args) ; p++) + ; + + return p->token; +} + +/** + * match_number: scan a number in the given base from a substring_t + * @s: substring to be scanned + * @result: resulting integer on success + * @base: base to use when converting string + * + * Description: Given a &substring_t and a base, attempts to parse the substring + * as a number in that base. On success, sets @result to the integer represented + * by the string and returns 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure. + */ +static int match_number(substring_t *s, int *result, int base) +{ + char *endp; + char *buf; + int ret; + long val; + size_t len = s->to - s->from; + + buf = malloc(len + 1); + if (!buf) + return -ENOMEM; + memcpy(buf, s->from, len); + buf[len] = '\0'; + + ret = 0; + val = strtol(buf, &endp, base); + if (endp == buf) + ret = -EINVAL; + else if (val < (long)INT_MIN || val > (long)INT_MAX) + ret = -ERANGE; + else + *result = (int) val; + free(buf); + return ret; +} + +/** + * match_int: - scan a decimal representation of an integer from a substring_t + * @s: substring_t to be scanned + * @result: resulting integer on success + * + * Description: Attempts to parse the &substring_t @s as a decimal integer. On + * success, sets @result to the integer represented by the string and returns 0. + * Returns -ENOMEM, -EINVAL, or -ERANGE on failure. + */ +int match_int(substring_t *s, int *result) +{ + return match_number(s, result, 0); +} + +/** + * match_octal: - scan an octal representation of an integer from a substring_t + * @s: substring_t to be scanned + * @result: resulting integer on success + * + * Description: Attempts to parse the &substring_t @s as an octal integer. On + * success, sets @result to the integer represented by the string and returns + * 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure. + */ +int match_octal(substring_t *s, int *result) +{ + return match_number(s, result, 8); +} + +/** + * match_hex: - scan a hex representation of an integer from a substring_t + * @s: substring_t to be scanned + * @result: resulting integer on success + * + * Description: Attempts to parse the &substring_t @s as a hexadecimal integer. + * On success, sets @result to the integer represented by the string and + * returns 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure. + */ +int match_hex(substring_t *s, int *result) +{ + return match_number(s, result, 16); +} + +/** + * match_wildcard: - parse if a string matches given wildcard pattern + * @pattern: wildcard pattern + * @str: the string to be parsed + * + * Description: Parse the string @str to check if matches wildcard + * pattern @pattern. The pattern may contain two type wildcardes: + * '*' - matches zero or more characters + * '?' - matches one character + * If it's matched, return true, else return false. + */ +bool match_wildcard(const char *pattern, const char *str) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s) { + switch (*p) { + case '?': + s++; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (*s == *p) { + s++; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/** + * match_strlcpy: - Copy the characters from a substring_t to a sized buffer + * @dest: where to copy to + * @src: &substring_t to copy + * @size: size of destination buffer + * + * Description: Copy the characters in &substring_t @src to the + * c-style string @dest. Copy no more than @size - 1 characters, plus + * the terminating NUL. Return length of @src. + */ +size_t match_strlcpy(char *dest, const substring_t *src, size_t size) +{ + size_t ret = src->to - src->from; + + if (size) { + size_t len = ret >= size ? size - 1 : ret; + memcpy(dest, src->from, len); + dest[len] = '\0'; + } + return ret; +} + +/** + * match_strdup: - allocate a new string with the contents of a substring_t + * @s: &substring_t to copy + * + * Description: Allocates and returns a string filled with the contents of + * the &substring_t @s. The caller is responsible for freeing the returned + * string with free(). + */ +char *match_strdup(const substring_t *s) +{ + size_t sz = s->to - s->from + 1; + char *p = malloc(sz); + if (p) + match_strlcpy(p, s, sz); + return p; +} diff --git a/util/parser.h b/util/parser.h new file mode 100644 index 0000000..39d5b79 --- /dev/null +++ b/util/parser.h @@ -0,0 +1,34 @@ +/* + * linux/include/linux/parser.h + * + * Header for lib/parser.c + * Intended use of these functions is parsing filesystem argument lists, + * but could potentially be used anywhere else that simple option=arg + * parsing is required. + */ + + +/* associates an integer enumerator with a pattern string. */ +struct match_token { + int token; + const char *pattern; +}; + +typedef struct match_token match_table_t[]; + +/* Maximum number of arguments that match_token will find in a pattern */ +enum {MAX_OPT_ARGS = 3}; + +/* Describe the location within a string of a substring */ +typedef struct { + char *from; + char *to; +} substring_t; + +int match_token(char *, const match_table_t table, substring_t args[]); +int match_int(substring_t *, int *result); +int match_octal(substring_t *, int *result); +int match_hex(substring_t *, int *result); +bool match_wildcard(const char *pattern, const char *str); +size_t match_strlcpy(char *, const substring_t *, size_t); +char *match_strdup(const substring_t *); diff --git a/util/suffix.c b/util/suffix.c new file mode 100644 index 0000000..bf1485f --- /dev/null +++ b/util/suffix.c @@ -0,0 +1,132 @@ +/* + * + * 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 dealing with number suffixes + * + */ + +#include "suffix.h" + +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> + +static struct si_suffix { + double magnitude; + const char *suffix; +} si_suffixes[] = { + {1e15, "P"}, + {1e12, "T"}, + {1e9, "G"}, + {1e6, "M"}, + {1e3, "k"}, + {1e0, ""}, + {1e-3, "m"}, + {1e-6, "u"}, + {1e-9, "n"}, + {1e-12, "p"}, + {1e-15, "f"}, + {0} +}; + +const char *suffix_si_get(double *value) +{ + struct si_suffix *s; + + for (s = si_suffixes; s->magnitude != 0; s++) { + if (*value >= s->magnitude) { + *value /= s->magnitude; + return s->suffix; + } + } + + return ""; +} + +static struct binary_suffix { + int shift; + const char *suffix; +} binary_suffixes[] = { + {50, "Pi"}, + {40, "Ti"}, + {30, "Gi"}, + {20, "Mi"}, + {10, "Ki"}, + {0, ""} +}; + +const char *suffix_binary_get(long long *value) +{ + struct binary_suffix *s; + + for (s = binary_suffixes; s->shift != 0; s++) { + if (llabs(*value) >= (1LL << s->shift)) { + *value = + (*value + (1LL << (s->shift - 1))) / (1LL << s->shift); + return s->suffix; + } + } + + return ""; +} + +const char *suffix_dbinary_get(double *value) +{ + struct binary_suffix *s; + + for (s = binary_suffixes; s->shift != 0; s++) { + if (fabs(*value) >= (1LL << s->shift)) { + *value = *value / (1LL << s->shift); + return s->suffix; + } + } + + return ""; +} + +uint64_t suffix_binary_parse(const char *value) +{ + char *suffix; + errno = 0; + uint64_t ret = strtoll(value, &suffix, 0); + if (errno) + return 0; + + struct binary_suffix *s; + for (s = binary_suffixes; s->shift != 0; s++) { + if (tolower(suffix[0]) == tolower(s->suffix[0])) { + ret <<= s->shift; + return ret; + } + } + + if (suffix[0] != '\0') + errno = EINVAL; + + return ret; +} diff --git a/util/suffix.h b/util/suffix.h new file mode 100644 index 0000000..278f472 --- /dev/null +++ b/util/suffix.h @@ -0,0 +1,41 @@ +/* + * + * 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 dealing with number suffixes + * + */ + +#ifndef __ARGCONFIG_SUFFIX_H__ + +#include <inttypes.h> + +const char *suffix_si_get(double *value); +const char *suffix_binary_get(long long *value); +const char *suffix_dbinary_get(double *value); +uint64_t suffix_binary_parse(const char *value); + +#endif |