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);
}
}