diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:41:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:41:05 +0000 |
commit | 1cc8413aaf5f8fa6595aece1933462c096e88639 (patch) | |
tree | e97b4f25c511372d73bdd96c389c5f468d99138a /util | |
parent | Initial commit. (diff) | |
download | nvme-cli-upstream/2.4+really2.3.tar.xz nvme-cli-upstream/2.4+really2.3.zip |
Adding upstream version 2.4+really2.3.upstream/2.4+really2.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | util/argconfig.c | 594 | ||||
-rw-r--r-- | util/argconfig.h | 136 | ||||
-rw-r--r-- | util/base64.c | 107 | ||||
-rw-r--r-- | util/base64.h | 8 | ||||
-rw-r--r-- | util/cleanup.c | 5 | ||||
-rw-r--r-- | util/cleanup.h | 19 | ||||
-rw-r--r-- | util/json.c | 60 | ||||
-rw-r--r-- | util/json.h | 51 | ||||
-rw-r--r-- | util/meson.build | 10 | ||||
-rw-r--r-- | util/suffix.c | 168 | ||||
-rw-r--r-- | util/suffix.h | 45 | ||||
-rw-r--r-- | util/types.c | 138 | ||||
-rw-r--r-- | util/types.h | 36 |
13 files changed, 1377 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; + } + } +} diff --git a/util/argconfig.h b/util/argconfig.h new file mode 100644 index 0000000..6ef3b6a --- /dev/null +++ b/util/argconfig.h @@ -0,0 +1,136 @@ +/* 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 <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> +#include <stdio.h> + +enum argconfig_types { + CFG_FLAG, + 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_FLAG, v, no_argument, d} + +#define OPT_SUFFIX(l, s, v, d) \ + {l, s, "IONUM", CFG_LONG_SUFFIX, 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_INCR(l, s, v, d) \ + {l, s, "NUM", CFG_INCREMENT, v, no_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) +#define OPT_STR(l, s, v, d) OPT_STRING(l, s, "STRING", 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); +int argconfig_parse_comma_sep_array(char *string, int *ret, + unsigned max_length); +int argconfig_parse_comma_sep_array_short(char *string, unsigned short *ret, + unsigned max_length); +int argconfig_parse_comma_sep_array_long(char *string, + unsigned long long *ret, + unsigned max_length); +int argconfig_parse_byte(const char *opt, const char *str, unsigned char *val); +void argconfig_register_help_func(argconfig_help_func * f); + +void print_word_wrapped(const char *s, int indent, int start, FILE *stream); +#endif diff --git a/util/base64.c b/util/base64.c new file mode 100644 index 0000000..07f975c --- /dev/null +++ b/util/base64.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * base64.c - RFC4648-compliant base64 encoding + * + * Copyright (c) 2020 Hannes Reinecke, SUSE + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> + +static const char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode() - base64-encode some bytes + * @src: the bytes to encode + * @srclen: number of bytes to encode + * @dst: (output) the base64-encoded string. Not NUL-terminated. + * + * Encodes the input string using characters from the set [A-Za-z0-9+,]. + * The encoded string is roughly 4/3 times the size of the input string. + * + * Return: length of the encoded string + */ +int base64_encode(const unsigned char *src, int srclen, char *dst) +{ + int i, bits = 0; + u_int32_t ac = 0; + char *cp = dst; + + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; + bits += 8; + do { + bits -= 6; + *cp++ = base64_table[(ac >> bits) & 0x3f]; + } while (bits >= 6); + } + if (bits) { + *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; + bits -= 6; + } + while (bits < 0) { + *cp++ = '='; + bits += 2; + } + + return cp - dst; +} + +/** + * base64_decode() - base64-decode some bytes + * @src: the base64-encoded string to decode + * @len: number of bytes to decode + * @dst: (output) the decoded bytes. + * + * Decodes the base64-encoded bytes @src according to RFC 4648. + * + * Return: number of decoded bytes + */ +int base64_decode(const char *src, int srclen, unsigned char *dst) +{ + u_int32_t ac = 0; + int i, bits = 0; + unsigned char *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64_table, src[i]); + + if (src[i] == '=') { + ac = (ac << 6); + bits += 6; + if (bits >= 8) + bits -= 8; + continue; + } + if (p == NULL || src[i] == 0) + return -EINVAL; + ac = (ac << 6) | (p - base64_table); + bits += 6; + if (bits >= 8) { + bits -= 8; + *bp++ = (unsigned char)(ac >> bits); + } + } + if (ac && ((1 << bits) - 1)) + return -EAGAIN; + + return bp - dst; +} diff --git a/util/base64.h b/util/base64.h new file mode 100644 index 0000000..c0f62e2 --- /dev/null +++ b/util/base64.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _BASE64_H +#define _BASE64_H + +int base64_encode(const unsigned char *src, int len, char *dst); +int base64_decode(const char *src, int len, unsigned char *dst); + +#endif /* _BASE64_H */ diff --git a/util/cleanup.c b/util/cleanup.c new file mode 100644 index 0000000..d6ac7c6 --- /dev/null +++ b/util/cleanup.c @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <stdlib.h> +#include "cleanup.h" + +DEFINE_CLEANUP_FUNC(cleanup_charp, char *, free); diff --git a/util/cleanup.h b/util/cleanup.h new file mode 100644 index 0000000..575a25d --- /dev/null +++ b/util/cleanup.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __CLEANUP_H +#define __CLEANUP_H + +#define __cleanup__(fn) __attribute__((cleanup(fn))) + +#define DECLARE_CLEANUP_FUNC(name, type) \ + void name(type *__p) + +#define DEFINE_CLEANUP_FUNC(name, type, free_fn)\ +DECLARE_CLEANUP_FUNC(name, type) \ +{ \ + if (*__p) \ + free_fn(*__p); \ +} + +DECLARE_CLEANUP_FUNC(cleanup_charp, char *); + +#endif diff --git a/util/json.c b/util/json.c new file mode 100644 index 0000000..84d43e5 --- /dev/null +++ b/util/json.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <stdio.h> +#include <errno.h> + +#include "json.h" +#include "types.h" + +struct json_object *util_json_object_new_double(long double d) +{ + struct json_object *obj; + char *str; + + if (asprintf(&str, "%Lf", d) < 0) + return NULL; + + obj = json_object_new_string(str); + + free(str); + return obj; + +} + +struct json_object *util_json_object_new_uint64(uint64_t i) +{ + struct json_object *obj; + char *str; + + if (asprintf(&str, "%" PRIu64, i) < 0) + return NULL; + + obj = json_object_new_string(str); + + free(str); + return obj; + +} + +struct json_object *util_json_object_new_uint128(nvme_uint128_t val) +{ + struct json_object *obj; + obj = json_object_new_string(uint128_t_to_string(val)); + return obj; +} + +uint64_t util_json_object_get_uint64(struct json_object *obj) +{ + uint64_t val = 0; + + if (json_object_is_type(obj, json_type_string)) { + char *end = NULL; + const char *buf; + + buf = json_object_get_string(obj); + val = strtoull(buf, &end, 10); + if ((val == 0 && errno != 0) || (end == buf)) + return 0; + } + + return val; +} diff --git a/util/json.h b/util/json.h new file mode 100644 index 0000000..1312cb8 --- /dev/null +++ b/util/json.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __JSON__H +#define __JSON__H + +#include <json.h> +#include "util/types.h" + +/* Wrappers around json-c's API */ + +#define json_create_object(o) json_object_new_object(o) +#define json_create_array(a) json_object_new_array(a) +#define json_free_object(o) json_object_put(o) +#define json_free_array(a) json_object_put(a) +#define json_object_add_value_uint(o, k, v) \ + json_object_object_add(o, k, json_object_new_int(v)) +#define json_object_add_value_int(o, k, v) \ + json_object_object_add(o, k, json_object_new_int(v)) +#ifndef CONFIG_JSONC_14 +#define json_object_new_uint64(v) util_json_object_new_uint64(v) +#define json_object_get_uint64(v) util_json_object_get_uint64(v) +#endif +#define json_object_add_value_uint64(o, k, v) \ + json_object_object_add(o, k, json_object_new_uint64(v)) +#define json_object_add_value_uint128(o, k, v) \ + json_object_object_add(o, k, util_json_object_new_uint128(v)) +#define json_object_add_value_double(o, k, v) \ + json_object_object_add(o, k, util_json_object_new_double(v)) +#define json_object_add_value_float(o, k, v) \ + json_object_object_add(o, k, json_object_new_double(v)) +#define json_object_add_value_string(o, k, v) \ + json_object_object_add(o, k, json_object_new_string(v)) +#define json_object_add_value_array(o, k, v) \ + json_object_object_add(o, k, v) +#define json_object_add_value_object(o, k, v) \ + json_object_object_add(o, k, v) +#define json_array_add_value_object(o, k) \ + json_object_array_add(o, k) +#define json_array_add_value_string(o, v) \ + json_object_array_add(o, json_object_new_string(v)) +#define json_print_object(o, u) \ + printf("%s", json_object_to_json_string_ext(o, \ + JSON_C_TO_STRING_PRETTY | \ + JSON_C_TO_STRING_NOSLASHESCAPE)) + +struct json_object *util_json_object_new_double(long double d); +struct json_object *util_json_object_new_uint64(uint64_t i); +struct json_object *util_json_object_new_uint128(nvme_uint128_t val); +struct json_object *util_json_object_new_uint128(nvme_uint128_t val); + +uint64_t util_json_object_get_uint64(struct json_object *obj); +#endif diff --git a/util/meson.build b/util/meson.build new file mode 100644 index 0000000..349c70c --- /dev/null +++ b/util/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +sources += [ + 'util/argconfig.c', + 'util/cleanup.c', + 'util/json.c', + 'util/suffix.c', + 'util/base64.c', + 'util/types.c', +] diff --git a/util/suffix.c b/util/suffix.c new file mode 100644 index 0000000..4106958 --- /dev/null +++ b/util/suffix.c @@ -0,0 +1,168 @@ +// 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 dealing with number suffixes + * + */ + +#include "suffix.h" + +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> + +static struct si_suffix { + long double magnitude; + const char *suffix; +} si_suffixes[] = { + {1e30, "Q"}, + {1e27, "R"}, + {1e24, "Y"}, + {1e21, "Z"}, + {1e18, "E"}, + {1e15, "P"}, + {1e12, "T"}, + {1e9, "G"}, + {1e6, "M"}, + {1e3, "k"}, + {1e0, ""}, + {0} +}; + +const char *suffix_si_get(double *value) +{ + long double value_ld = *value; + const char *suffix = suffix_si_get_ld(&value_ld); + + *value = value_ld; + + return suffix; +} + +uint64_t suffix_si_parse(const char *value, bool *suffixed) +{ + char *suffix; + double ret; + struct si_suffix *s; + + errno = 0; + ret = strtod(value, &suffix); + if (errno) + return 0; + + for (s = si_suffixes; s->magnitude != 0; s++) { + if (suffix[0] == s->suffix[0]) { + if (suffixed && suffix[0] != '\0') + *suffixed = true; + return ret *= s->magnitude; + } + } + + if (suffix[0] != '\0') + errno = EINVAL; + + return (uint64_t)ret; +} + +const char *suffix_si_get_ld(long 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 = strtoull(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..b367ce4 --- /dev/null +++ b/util/suffix.h @@ -0,0 +1,45 @@ +/* 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 dealing with number suffixes + * + */ + +#ifndef __ARGCONFIG_SUFFIX_H__ + +#include <inttypes.h> +#include <stdbool.h> + +const char *suffix_si_get(double *value); +uint64_t suffix_si_parse(const char *value, bool *suffixed); +const char *suffix_si_get_ld(long 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 diff --git a/util/types.c b/util/types.c new file mode 100644 index 0000000..18ced77 --- /dev/null +++ b/util/types.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> + +#include <ccan/endian/endian.h> + +#include "types.h" +#include "util/suffix.h" + +nvme_uint128_t le128_to_cpu(__u8 *data) +{ + nvme_uint128_t u; + nvme_uint128_t tmp; + memcpy(tmp.bytes, data, 16); + u.words[0] = le32_to_cpu(tmp.words[3]); + u.words[1] = le32_to_cpu(tmp.words[2]); + u.words[2] = le32_to_cpu(tmp.words[1]); + u.words[3] = le32_to_cpu(tmp.words[0]); + return u; +} + +long double int128_to_double(__u8 *data) +{ + int i; + long double result = 0; + + for (i = 0; i < 16; i++) { + result *= 256; + result += data[15 - i]; + } + return result; +} + +uint64_t int48_to_long(__u8 *data) +{ + int i; + uint64_t result = 0; + + for (i = 0; i < 6; i++) { + result *= 256; + result += data[5 - i]; + } + return result; +} + +char *uint128_t_to_string(nvme_uint128_t val) +{ + static char str[60]; + int idx = 60; + __u64 div, rem; + char *sep = localeconv()->thousands_sep; + int len = sep ? strlen(sep) : 0; + int i; + + /* terminate at the end, and build up from the ones */ + str[--idx] = '\0'; + + do { + if (len && !((sizeof(str) - idx) % (3 + len))) { + for (i = 0; i < len; i++) + str[--idx] = sep[i]; + } + + rem = val.words[0]; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[1]; + val.words[0] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[2]; + val.words[1] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[3]; + val.words[2] = div; + + div = rem / 10; + rem = rem - div * 10; + val.words[3] = div; + + str[--idx] = '0' + rem; + } while (val.words[0] || val.words[1] || val.words[2] || val.words[3]); + + return str + idx; +} + +static long double uint128_t_to_double(nvme_uint128_t data) +{ + int i; + long double result = 0; + + for (i = 0; i < sizeof(data.words) / sizeof(*data.words); i++) { + result *= 4294967296; + result += data.words[i]; + } + + return result; +} + +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit) +{ + static char str[40]; + long double bytes = uint128_t_to_double(val) * bytes_per_unit; + const char *suffix = suffix_si_get_ld(&bytes); + int n = snprintf(str, sizeof(str), "%.2Lf %sB", bytes, suffix); + + if (n <= 0) + return ""; + + if (n >= sizeof(str)) + str[sizeof(str) - 1] = '\0'; + + return str; +} + +const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]) +{ + static char uuid_str[NVME_UUID_LEN_STRING]; + + nvme_uuid_to_string(uuid, uuid_str); + + return uuid_str; +} + +const char *util_fw_to_string(char *c) +{ + static char ret[9]; + int i; + + for (i = 0; i < 8; i++) + ret[i] = c[i] >= '!' && c[i] <= '~' ? c[i] : '.'; + ret[i] = '\0'; + return ret; +} diff --git a/util/types.h b/util/types.h new file mode 100644 index 0000000..2e88717 --- /dev/null +++ b/util/types.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MISC_H +#define _MISC_H + +/* type conversion helpers */ + +#include <stdint.h> +#include <linux/types.h> + +#include <libnvme.h> + +#define ABSOLUTE_ZERO_CELSIUS -273 + +static inline long kelvin_to_celsius(long t) +{ + return t + ABSOLUTE_ZERO_CELSIUS; +} + +/* uint128_t is not always available, define our own. */ +union nvme_uint128 { + __u8 bytes[16]; + __u32 words[4]; /* [0] is most significant word */ +}; + +typedef union nvme_uint128 nvme_uint128_t; + +nvme_uint128_t le128_to_cpu(__u8 *data); +long double int128_to_double(__u8 *data); +uint64_t int48_to_long(__u8 *data); + +char *uint128_t_to_string(nvme_uint128_t val); +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit); +const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]); +const char *util_fw_to_string(char *c); + +#endif /* _MISC_H */ |