ext/h3/src/src/apps/applib/lib/utility.c in h3-3.4.0 vs ext/h3/src/src/apps/applib/lib/utility.c in h3-3.4.4

- old
+ new

@@ -18,18 +18,210 @@ */ #include "utility.h" #include <assert.h> #include <inttypes.h> -#include <stackAlloc.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "coordijk.h" #include "geoCoord.h" #include "h3Index.h" #include "h3api.h" +/* + * Return codes from parseArgs. + */ + +#define PARSE_ARGS_SUCCESS 0 +#define PARSE_ARGS_HELP 1 +#define PARSE_ARGS_REPEATED_ARGUMENT 2 +#define PARSE_ARGS_MISSING_VALUE 3 +#define PARSE_ARGS_FAILED_PARSE 4 +#define PARSE_ARGS_UNKNOWN_ARGUMENT 5 +#define PARSE_ARGS_MISSING_REQUIRED 6 + +/** + * Parse command line arguments and prints help, if needed. + * + * Uses the provided arguments to populate argument values and records in the + * argument if it is found. + * + * Returns non-zero if all required arguments are not present, an argument fails + * to parse, is missing its associated value, or arguments are specified more + * than once. + * + * Help is printed to stdout if a argument with isHelp = true is found, and help + * is printed to stderr if argument parsing fails. + * + * @param argc argc from main + * @param argv argv from main + * @param numArgs Number of elements in the args array + * @param args Pointer to each argument to parse + * @param helpArg Pointer to the argument for "--help" + * @param helpText Explanatory text for this program printed with help + * @return 0 if argument parsing succeeded, otherwise non-0. If help is printed, + * return value is non-0. + */ +int parseArgs(int argc, char* argv[], int numArgs, Arg* args[], + const Arg* helpArg, const char* helpText) { + const char* errorMessage = NULL; + const char* errorDetails = NULL; + + int failed = _parseArgsList(argc, argv, numArgs, args, helpArg, + &errorMessage, &errorDetails); + + if (failed || helpArg->found) { + printHelp(helpArg->found ? stdout : stderr, argv[0], helpText, numArgs, + args, errorMessage, errorDetails); + return failed != PARSE_ARGS_SUCCESS ? failed : PARSE_ARGS_HELP; + } + return PARSE_ARGS_SUCCESS; +} + +/** + * Parse command line arguments. + * + * Uses the provided arguments to populate argument values. + * + * Returns non-zero if all required arguments are not present, an argument fails + * to parse, is missing its associated value, or arguments are specified more + * than once. + * + * @param argc argc from main + * @param argv argv from main + * @param numArgs Number of elements in the args array + * @param args Pointer to each argument to parse. + * @param helpArg Pointer to the argument for "--help" that suppresses checking + * for required arguments. + * @param errorMessage Error message to display, if returning non-zero. + * @param errorDetail Additional error details, if returning non-zero. May be + * null, and may be a pointer from `argv` or `args`. + * @return 0 if argument parsing succeeded, otherwise non-0. + */ +int _parseArgsList(int argc, char* argv[], int numArgs, Arg* args[], + const Arg* helpArg, const char** errorMessage, + const char** errorDetail) { + // Whether help was found and required arguments do not need to be checked + bool foundHelp = false; + + for (int i = 1; i < argc; i++) { + bool foundMatch = false; + + for (int j = 0; j < numArgs; j++) { + // Test this argument, which may have multiple names, for whether it + // matches. argName will be set to the name used for this argument + // if it matches. + const char* argName = NULL; + for (int k = 0; k < NUM_ARG_NAMES; k++) { + if (args[j]->names[k] == NULL) continue; + + if (strcmp(argv[i], args[j]->names[k]) == 0) { + argName = args[j]->names[k]; + break; + } + } + // argName unchanged from NULL indicates this didn't match, try the + // next argument. + if (argName == NULL) continue; + + if (args[j]->found) { + *errorMessage = "Argument specified multiple times"; + *errorDetail = argName; + return PARSE_ARGS_REPEATED_ARGUMENT; + } + + if (args[j]->scanFormat != NULL) { + // Argument has a value, need to advance one and read the value. + i++; + if (i >= argc) { + *errorMessage = "Argument value not present"; + *errorDetail = argName; + return PARSE_ARGS_MISSING_VALUE; + } + + if (!sscanf(argv[i], args[j]->scanFormat, args[j]->value)) { + *errorMessage = "Failed to parse argument"; + *errorDetail = argName; + return PARSE_ARGS_FAILED_PARSE; + } + } + + if (args[j] == helpArg) { + foundHelp = true; + } + + args[j]->found = true; + foundMatch = true; + break; + } + + if (!foundMatch) { + *errorMessage = "Unknown argument"; + // Don't set errorDetail, since the input could be unprintable. + return PARSE_ARGS_UNKNOWN_ARGUMENT; + } + } + + // Check for missing required arguments. + if (!foundHelp) { + for (int i = 0; i < numArgs; i++) { + if (args[i]->required && !args[i]->found) { + *errorMessage = "Required argument missing"; + *errorDetail = args[i]->names[0]; + return PARSE_ARGS_MISSING_REQUIRED; + } + } + } + + return PARSE_ARGS_SUCCESS; +} + +/** + * Print a help message. + * + * @param out Stream to print to, e.g. stdout + * @param programName Program name, such as from argv[0] + * @param helpText Explanation of what the program does + * @param numArgs Number of arguments to print help for + * @param args Pointer to arguments to print help for + * @param errorMessage Error message, or null + * @param errorDetails Additional error detail message, or null + */ +void printHelp(FILE* out, const char* programName, const char* helpText, + int numArgs, Arg* args[], const char* errorMessage, + const char* errorDetails) { + if (errorMessage != NULL) { + fprintf(out, "%s: %s", programName, errorMessage); + if (errorDetails != NULL) { + fprintf(out, ": %s", errorDetails); + } + fprintf(out, "\n"); + } + fprintf(out, "%s: %s\n", programName, helpText); + fprintf(out, "H3 %d.%d.%d\n\n", H3_VERSION_MAJOR, H3_VERSION_MINOR, + H3_VERSION_PATCH); + + for (int i = 0; i < numArgs; i++) { + fprintf(out, "\t"); + for (int j = 0; j < NUM_ARG_NAMES; j++) { + if (args[i]->names[j] == NULL) continue; + if (j != 0) fprintf(out, ", "); + fprintf(out, "%s", args[i]->names[j]); + } + if (args[i]->scanFormat != NULL) { + fprintf(out, " <%s>", args[i]->valueName); + } + fprintf(out, "\t"); + if (args[i]->required) { + fprintf(out, "Required. "); + } + fprintf(out, "%s\n", args[i]->helpText); + } +} + void error(const char* msg) { fflush(stdout); fflush(stderr); fprintf(stderr, "ERROR: %s.\n", msg); exit(1); @@ -191,17 +383,19 @@ assert(baseCells <= NUM_BASE_CELLS); for (int i = 0; i < baseCells; i++) { H3Index bc; setH3Index(&bc, 0, i, 0); int childrenSz = H3_EXPORT(maxUncompactSize)(&bc, 1, res); - STACK_ARRAY_CALLOC(H3Index, children, childrenSz); + H3Index* children = calloc(childrenSz, sizeof(H3Index)); H3_EXPORT(uncompact)(&bc, 1, children, childrenSz, res); for (int j = 0; j < childrenSz; j++) { if (children[j] == 0) { continue; } (*callback)(children[j]); } + + free(children); } }