From f54ed04e274f2118482c8fbef14ba20d89baedb8 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 20 Oct 2025 11:34:59 +0200 Subject: [PATCH] first version --- LICENSE | 24 +++ README.md | 207 +++++++++++++++++++ args.h | 581 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 812 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 args.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..efb9808 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..f032522 --- /dev/null +++ b/README.md @@ -0,0 +1,207 @@ +## args.h + +Single header command line argument parser for C and C++ +loosely based on python's argparse module. + +## Usage: + +Define implementation macro before you include this file in *one* C or C++ file: + +```c +#define ARGS_IMPLEMENTATION +``` + +Should look like this: + +```c +#include ... +#include ... + +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +This will expand the implementation into that file, in other files you can just +include the header without the macro. + +```c +#include "args.h" +``` + +## Example: + +```c +void example(int argc, char **argv) { + args_t args = { + .program = argv[0], + .description = "this program does very cool things on many files and outputs it somewhere", + .epilogue = "it also shows cool epilogue text in it's help", + }; + args_add(&args, (args_desc_t){"filenames", .nargs = "+" }); + args_add(&args, (args_desc_t){"--output", "-o" }); + args_add(&args, (args_desc_t){"--force", "-f", .store_value = "1", .default_value = "0"}); + args_add(&args, (args_desc_t){"--recursive", "-r", .store_value = "1", .default_value = "0"}); + args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); + args_parse(&args, argc, argv); + + const char **filenames = args_get(&args, "filenames"); + const char **output = args_get(&args, "--output"); + int force = atoi(args_get(&args, "-f")[0]); + int recursive = atoi(args_get(&args, "--recursive")[0]); + int help = atoi(args_get(&args, "--help")[0]); + + if (help) { + args_print_help(&args); + return; + } + + // .. + + for (int i = 0; filenames[i]; i += 1) { + // .. + } +} +``` + +Execute like this: + +> app.exe file1 file2 file3 --force --output /folder/. + +Help menu: + +``` +usage: ./a.out [options] filenames ... + +this program does very cool things on many files and outputs it somewhere + +options: + filenames ... list of filenames to process + --output -o path to the folder where output will be gathered + --force -f force the processing + --recursive -r apply recursively to files in current directory + --help -h show this help window + +it also shows cool epilogue text in it's help +``` + +## How To's + +1. Single positional argument + +> program.exe argument + +Specifying `.nargs = ""` enforces a strict number +of arguments, so supplying less or more will result in +an error. + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"argument", .nargs = "1"}); +args_parse(&args, argc, argv); +const char *argument = args_get(&args, "argument")[0]; +``` + +2. One or more positional arguments + +> program.exe a b c + +You need to set `.nargs = "+"` this will make it so that one +or more arguments are expected. Alternatively `.nargs = "*"`, +which makes it so that having even one argumenti is optional. + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"arguments", .nargs = "+"}); +args_parse(&args, argc, argv); +const char **arguments = args_get(&args, "arguments"); +``` + +3. Help menu, help flag + +> program.exe --help + +``` +args_t args = {0}; +args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); +args_parse(&args, argc, argv); +int help = atoi(args_get(&args, "help")[0]); +if (help) { + args_print_help(&args); + exit(0); +} +``` + +4. Typechecked optional list of integers + +> program.exe --list 1 2 3 +> program.exe + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"--list", "-l", .type = ARGS_TYPE_INT, .nargs = "*"}); +args_parse(&args, argc, argv); +const char **list = args_get(&args, "--list"); +``` + +5. Custom logging + +You can override the ARGS_PRINTF macro, at compile time +the library will use that. By default it uses fprintf(stderr) + +```c +#define ARGS_PRINTF(...) fprintf(stdout, __VA_ARGS__) +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +6. User defined memory allocator + +```c +#define ARGS_MALLOC(x) my_malloc(x) +#define ARGS_FREE(x) my_free(x) +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +7. C++ + +The library works in C++ but leverages special C syntax for aggregate types, +in later C++ the syntax might be simiar so no issues there but for older +versions you will either need to write some small wrapper functions or just +bare with initializing structures inline. For example: + +```c +void args_add_bool(args_t *args, const char *name, const char *alt_name, const char *help) { + args_desc_t desc = {name, alt_name}; + desc.help = help; + desc.store_value = "1"; + desc.default_value = "0"; + args_add(args, desc); +} + +void args_add_nargs(args_t *args, const char *name, const char *alt_name, const char *nargs, const char *help) { + args_desc_t desc = {name, alt_name}; + desc.nargs = nargs; + desc.help = help; + args_add(args, desc); +} + +int main(int argc, char **argv) { + args_t args = { + .program = argv[0], + .description = "this program does very cool things on many files and outputs it somewhere", + .epilogue = "it also shows cool epilogue text in it's help", + }; + args_add_nargs(&args, "filenames", NULL, "+", "list of filenames to process"); + args_add_nargs(&args, "--output", "-o", "?", "path to the folder where output will be gathered"); + args_add_bool(&args, "--force", "-f", "force the processing"); + args_add_bool(&args, "--recursive", "-r", "apply recursively to files in current directory"); + args_add_bool(&args, "--help", "-h", "show this help window"); + args_parse(&args, argc, argv); +} +``` + +## License + +Library is in public domain. See end of code file for license. + diff --git a/args.h b/args.h new file mode 100644 index 0000000..e272b28 --- /dev/null +++ b/args.h @@ -0,0 +1,581 @@ +#ifndef ARGS_HEADER +/* +args.h - v1 +Krzosa Karol - 2025 + +## args.h + +Single header command line argument parser for C and C++ +loosely based on python's argparse module. + +## Usage: + +Define implementation macro before you include this file in *one* C or C++ file: + +```c +#define ARGS_IMPLEMENTATION +``` + +Should look like this: + +```c +#include ... +#include ... + +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +This will expand the implementation into that file, in other files you can just +include the header without the macro. + +```c +#include "args.h" +``` + +## Example: + +```c +void example(int argc, char **argv) { + args_t args = { + .program = argv[0], + .description = "this program does very cool things on many files and outputs it somewhere", + .epilogue = "it also shows cool epilogue text in it's help", + }; + args_add(&args, (args_desc_t){"filenames", .nargs = "+" }); + args_add(&args, (args_desc_t){"--output", "-o" }); + args_add(&args, (args_desc_t){"--force", "-f", .store_value = "1", .default_value = "0"}); + args_add(&args, (args_desc_t){"--recursive", "-r", .store_value = "1", .default_value = "0"}); + args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); + args_parse(&args, argc, argv); + + const char **filenames = args_get(&args, "filenames"); + const char **output = args_get(&args, "--output"); + int force = atoi(args_get(&args, "-f")[0]); + int recursive = atoi(args_get(&args, "--recursive")[0]); + int help = atoi(args_get(&args, "--help")[0]); + + if (help) { + args_print_help(&args); + return; + } + + // .. + + for (int i = 0; filenames[i]; i += 1) { + // .. + } +} +``` + +Execute like this: + +> app.exe file1 file2 file3 --force --output /folder/. + +Help menu: + +``` +usage: ./a.out [options] filenames ... + +this program does very cool things on many files and outputs it somewhere + +options: + filenames ... list of filenames to process + --output -o path to the folder where output will be gathered + --force -f force the processing + --recursive -r apply recursively to files in current directory + --help -h show this help window + +it also shows cool epilogue text in it's help +``` + +## How To's + +1. Single positional argument + +> program.exe argument + +Specifying `.nargs = ""` enforces a strict number +of arguments, so supplying less or more will result in +an error. + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"argument", .nargs = "1"}); +args_parse(&args, argc, argv); +const char *argument = args_get(&args, "argument")[0]; +``` + +2. One or more positional arguments + +> program.exe a b c + +You need to set `.nargs = "+"` this will make it so that one +or more arguments are expected. Alternatively `.nargs = "*"`, +which makes it so that having even one argumenti is optional. + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"arguments", .nargs = "+"}); +args_parse(&args, argc, argv); +const char **arguments = args_get(&args, "arguments"); +``` + +3. Help menu, help flag + +> program.exe --help + +``` +args_t args = {0}; +args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"}); +args_parse(&args, argc, argv); +int help = atoi(args_get(&args, "help")[0]); +if (help) { + args_print_help(&args); + exit(0); +} +``` + +4. Typechecked optional list of integers + +> program.exe --list 1 2 3 +> program.exe + +```c +args_t args = {0}; +args_add(&args, (args_desc_t){"--list", "-l", .type = ARGS_TYPE_INT, .nargs = "*"}); +args_parse(&args, argc, argv); +const char **list = args_get(&args, "--list"); +``` + +5. Custom logging + +You can override the ARGS_PRINTF macro, at compile time +the library will use that. By default it uses fprintf(stderr) + +```c +#define ARGS_PRINTF(...) fprintf(stdout, __VA_ARGS__) +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +6. User defined memory allocator + +```c +#define ARGS_MALLOC(x) my_malloc(x) +#define ARGS_FREE(x) my_free(x) +#define ARGS_IMPLEMENTATION +#include "args.h" +``` + +7. C++ + +The library works in C++ but leverages special C syntax for aggregate types, +in later C++ the syntax might be simiar so no issues there but for older +versions you will either need to write some small wrapper functions or just +bare with initializing structures inline. For example: + +```c +void args_add_bool(args_t *args, const char *name, const char *alt_name, const char *help) { + args_desc_t desc = {name, alt_name}; + desc.help = help; + desc.store_value = "1"; + desc.default_value = "0"; + args_add(args, desc); +} + +void args_add_nargs(args_t *args, const char *name, const char *alt_name, const char *nargs, const char *help) { + args_desc_t desc = {name, alt_name}; + desc.nargs = nargs; + desc.help = help; + args_add(args, desc); +} + +int main(int argc, char **argv) { + args_t args = { + .program = argv[0], + .description = "this program does very cool things on many files and outputs it somewhere", + .epilogue = "it also shows cool epilogue text in it's help", + }; + args_add_nargs(&args, "filenames", NULL, "+", "list of filenames to process"); + args_add_nargs(&args, "--output", "-o", "?", "path to the folder where output will be gathered"); + args_add_bool(&args, "--force", "-f", "force the processing"); + args_add_bool(&args, "--recursive", "-r", "apply recursively to files in current directory"); + args_add_bool(&args, "--help", "-h", "show this help window"); + args_parse(&args, argc, argv); +} +``` + +## License + +Library is in public domain. See end of code file for license. + +*/ +#define ARGS_HEADER +#include +#include +#include + +typedef enum { + ARGS_TYPE_STRING, + ARGS_TYPE_INT, +} args_type_t; + +typedef struct args_desc_t args_desc_t; +struct args_desc_t { + const char *name; // for example: '--filename', skipping '-' sets the argument as positional + const char *alt_name; // usually the short name: '-f', invalid for positional + const char *default_value; // default value parameter will contain if no value was provided on cmd + const char *store_value; // turns processing into special flag syntax, no argument following flag is expected + // ? - 1 or 0 + // * - 0 or more + // + - 1 or more + // 1 - exactly one + // 2 - exactly two + // 3 - ... + const char *nargs; // how many arguments this command uses + const char *help; // the text describing what the command does in the help string + args_type_t type; // you can use this to add some additional checking for arguments + + // internally used data, user shouldn't modify directly + int match_count; + const char **values; + int nargs_parsed; + bool nargs_required; +}; + +typedef struct args_t args_t; +struct args_t { + const char *program; + const char *description; + const char *epilogue; + + // internall used data, user shouldn't modify directly + args_desc_t *desc; + int errors; +}; + +int args_parse(args_t *args, int argc, char **argv); +void args_add(args_t *args, args_desc_t desc); +const char **args_get(args_t *args, const char *name); +void args_print_help(args_t *args); +void args_free(args_t *args); + +#ifdef ARGS_IMPLEMENTATION +#include +#include +#include + +#ifndef ARGS_MALLOC +#define ARGS_MALLOC(s) malloc(s) +#endif + +#ifndef ARGS_FREE +#define ARGS_FREE(p) free(p) +#endif + +#ifndef ARGS_PRINTF +#define ARGS_PRINTF(...) fprintf(stderr, __VA_ARGS__) +#endif + +#define ARGS_REPORT(args, ...) (args->errors++, ARGS_PRINTF(__VA_ARGS__)) + +// excerpt from Sean Barett's stb_ds.h, simple dynamic array +typedef struct { + size_t length; + size_t capacity; +} args__array_header; + +#define args__header(t) ((args__array_header *)(t) - 1) +#define args__arrgrow(a, b, c) ((a) = args__arrgrowf_wrapper((a), sizeof *(a), (b), (c))) +#define args__arrcap(a) ((a) ? args__header(a)->capacity : 0) +#define args__arrmaybegrow(a, n) ((!(a) || args__header(a)->length + (n) > args__header(a)->capacity) ? (args__arrgrow(a, n, 0), 0) : 0) +#define args_alen(a) ((a) ? (int)args__header(a)->length : 0) +#define args_slen(x) (int)(sizeof((x)) / sizeof((x)[0])) +#define args_aput(a, v) (args__arrmaybegrow(a, 1), (a)[args__header(a)->length++] = (v)) + +static void *args__arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) { + args__array_header temp = {0}; // force debugging + size_t min_len = args_alen(a) + addlen; + (void)sizeof(temp); + + // compute the minimum capacity needed + if (min_len > min_cap) + min_cap = min_len; + + if (min_cap <= args__arrcap(a)) + return a; + + // increase needed capacity to guarantee O(1) amortized + if (min_cap < 2 * args__arrcap(a)) { + min_cap = 2 * args__arrcap(a); + } else if (min_cap < 4) { + min_cap = 4; + } + + void *b = ARGS_MALLOC(elemsize * min_cap + sizeof(args__array_header)); + b = (char *)b + sizeof(args__array_header); + if (a) { + memcpy(b, a, args_alen(a) * elemsize); + args__header(b)->length = args_alen(a); + ARGS_FREE(args__header(a)); + } else { + args__header(b)->length = 0; + } + args__header(b)->capacity = min_cap; + + return b; +} + +#ifdef __cplusplus +template static T * args__arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) { + return (T*)args__arrgrowf((void *)a, elemsize, addlen, min_cap); +} +#else +#define args__arrgrowf_wrapper args__arrgrowf +#endif + +static bool args_streq(const char *a, const char *b) { + return strcmp(a, b) == 0; +} + +static args_desc_t *args_get_desc(args_desc_t *desc, const char *name) { + for (args_desc_t *it = desc; it < desc + args_alen(desc); it += 1) { + if (args_streq(it->name, name) || (it->alt_name &&args_streq(it->alt_name, name))) { + return it; + } + } + return NULL; +} + +static args_desc_t *args_get_positional(args_desc_t *desc) { + for (args_desc_t *it = desc; it < desc + args_alen(desc); it += 1) { + if (it->name[0] != '-') return it; + } + return NULL; +} + +const char **args_get(args_t *args, const char *name) { + args_desc_t *i = args_get_desc(args->desc, name); + if (i) return i->values; + return NULL; +} + +void args_add(args_t *args, args_desc_t desc) { + args_aput(args->desc, desc); +} + +#define ARGS__MAX(x,y) ((x)>(y)?(x):(y)) +void args_print_help(args_t *args) { + ARGS_PRINTF("usage: %s [options]", args->program); + args_desc_t *a = args_get_positional(args->desc); + if (a) { + ARGS_PRINTF(" %s", a->name); + } + ARGS_PRINTF(" ... \n"); + if (args->description) { + ARGS_PRINTF("\n%s\n", args->description); + } + + ARGS_PRINTF("\noptions:\n"); + int name_len = 0; + int alt_name_len = 0; + for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) { + name_len = ARGS__MAX(strlen(it->name), name_len); + if (it->alt_name) { + alt_name_len = ARGS__MAX(strlen(it->alt_name), alt_name_len); + } + } + for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) { + ARGS_PRINTF(" %-*s", name_len + 1, it->name); + if (it->alt_name) { + ARGS_PRINTF(" %-*s", alt_name_len + 1, it->alt_name); + } else { + ARGS_PRINTF(" %-*s", alt_name_len +1, " "); + } + + bool print_arg = false; + bool print_tbc = false; + if (args_streq(it->nargs, "+") || args_streq(it->nargs, "*") || it->nargs_parsed > 1) { + print_arg = true; + print_tbc = true; + } + if (args_streq(it->nargs, "?") || it->nargs_parsed == 1) { + print_arg = true; + } + if (it->store_value) { + print_arg = false; + print_tbc = false; + } + if (print_arg) { + ARGS_PRINTF(" "); + } else { + ARGS_PRINTF(" "); + } + if (print_tbc) { + ARGS_PRINTF(" ..."); + } else { + ARGS_PRINTF(" "); + } + + if (it->help) { + ARGS_PRINTF(" %s", it->help); + } + ARGS_PRINTF("\n"); + } + + if (args->epilogue) { + ARGS_PRINTF("\n%s\n", args->epilogue); + } +} + +void args_free(args_t *args) { + for (int i = 0; i < args_alen(args->desc); i += 1) { + ARGS_FREE(args__header(args->desc[i].values)); + } + ARGS_FREE(args__header(args->desc)); +} + +int args_parse(args_t *args, int argc, char **argv) { + int pos_count = 0; + for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) { + if (it->alt_name && it->alt_name[0] != '-') { + ARGS_REPORT(args, "invalid alt_name for flag: '%s', always should begin with '-'\n", it->alt_name); + } + if (it->nargs == NULL) { + it->nargs = "?"; + } else { + bool special = args_streq(it->nargs, "*") || args_streq(it->nargs, "+") || args_streq(it->nargs, "?"); + if (!special) { + bool digit = true; + for (int i = 0; it->nargs[i]; i += 1) { + if (!isdigit(it->nargs[i])) { + digit = false; + break; + } + } + if (!digit) { + ARGS_REPORT(args, "invalid nargs: '%s', expected digit or '*', '+', '?'\n", it->nargs); + } else { + it->nargs_parsed = atoi(it->nargs); + it->nargs_required = true; + } + } + + } + if (it->name[0] != '-') { + pos_count += 1; + if (pos_count > 1) { + ARGS_REPORT(args, "more then one positional argument: '%s'\n", it->name); + } + } + } + + if (args->errors) { + goto end; + } + + for (int i = 1; i < argc;) { + const char *curr = argv[i++]; + if (curr[0] != '-') { + args_desc_t *it = args_get_positional(args->desc); + if (!it) { + ARGS_REPORT(args, "unrecognized argument: '%s'\n", curr); + continue; + } + + it->match_count += 1; + args_aput(it->values, curr); + continue; + } + + args_desc_t *it = args_get_desc(args->desc, curr); + if (it == NULL) { + ARGS_REPORT(args, "unrecognized argument: '%s'\n", curr); + continue; + } + + if (it->store_value) { + args_aput(it->values, it->store_value); + it->match_count += 1; + continue; + } + + int count = 0; + int end = i; + if (it->nargs_required) end += it->nargs_parsed; + if (args_streq(it->nargs, "?")) end += 1; + if (args_streq(it->nargs, "+")) end += argc; + if (args_streq(it->nargs, "*")) end += argc; + for (;i < end && i < argc;) { + if (argv[i] && argv[i][0] == '-') { + break; + } + count += 1; + it->match_count += 1; + args_aput(it->values, argv[i]); + i += 1; + } + if (count == 0) { + ARGS_REPORT(args, "expected a value to follow after argument: '%s'\n", curr); + } + } + + for (args_desc_t *it = args->desc; it < args->desc + args_alen(args->desc); it += 1) { + if (args_streq(it->nargs, "?") && it->match_count > 1) { + ARGS_REPORT(args, "expected argument: '%s', to appear 1 or 0 times instead of %d times\n", it->name, it->match_count); + } + if (args_streq(it->nargs, "+") && it->match_count == 0) { + ARGS_REPORT(args, "required argument: '%s'\n", it->name); + } + if (it->nargs_required && it->match_count != it->nargs_parsed) { + ARGS_REPORT(args, "expected %d arguments: '%s', got %d arguments\n", it->nargs_parsed, it->name, it->match_count); + } + if (it->default_value && it->match_count == 0) { + args_aput(it->values, it->default_value); + } + if (it->type == ARGS_TYPE_INT) { + for (int i = 0; i < args_alen(it->values); i += 1) { + const char *val = it->values[i]; + for (int idx = 0; val[idx]; idx += 1) { + char c = val[idx]; + if (!isdigit(c)) { + ARGS_REPORT(args, "failed to interpret: '%s' as integer, non integer value: '%s'\n", it->name, val); + break; + } + } + } + } + args_aput(it->values, NULL); + } +end:; + return args->errors; +} +#endif // ARGS_IMPLEMENTATION +#endif // ARGS_HEADER +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/