1 /*
2  * Copyright 2019 Uber Technologies, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *         http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /** @file args.h
17  * @brief   Miscellaneous functions and constants.
18  */
19 
20 #ifndef ARGS_H
21 #define ARGS_H
22 
23 #include <inttypes.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 
27 #include "utility.h"
28 
29 /** Maximum number of names an argument may have. */
30 #define NUM_ARG_NAMES 2
31 
32 /**
33  * An argument accepted by on the command line of an H3 application. Specifies
34  * how the argument is presented, parsed, and where parsed values are stored.
35  */
36 typedef struct {
37     /**
38      * Both short and long names of the argument. A name may be null, but the
39      * first name must be non-null.
40      */
41     const char* const names[NUM_ARG_NAMES];
42 
43     /**
44      * If true, this argument must be specified. If the argument is not
45      * specified, argument parsing will fail.
46      */
47     const bool required;
48 
49     /**
50      * Scan format for the argument, which will be passed to sscanf. May be null
51      * to indicate the argument does not take a value.
52      */
53     const char* const scanFormat;
54 
55     /**
56      * Name to present the value as when printing help.
57      */
58     const char* const valueName;
59 
60     /**
61      * Value will be placed here if the argument is present and scanFormat is
62      * not null.
63      */
64     void* const value;
65 
66     /**
67      * Will be set to true if the argument is present. Should be false when
68      * passed in to parseArgs.
69      */
70     bool found;
71 
72     /**
73      * Help text for this argument.
74      */
75     const char* const helpText;
76 } Arg;
77 
78 // prototypes
79 
80 int parseArgs(int argc, char* argv[], int numArgs, Arg* args[],
81               const Arg* helpArg, const char* helpText);
82 void printHelp(FILE* out, const char* programName, const char* helpText,
83                int numArgs, Arg* args[], const char* errorMessage,
84                const char* errorDetails);
85 
86 int _parseArgsList(int argc, char* argv[], int numArgs, Arg* args[],
87                    const Arg* helpArg, const char** errorMessage,
88                    const char** errorDetail);
89 
90 // common arguments
91 
92 #define ARG_HELP \
93     { .names = {"-h", "--help"}, .helpText = "Show this help message." }
94 #define DEFINE_INDEX_ARG(varName, argName) \
95     H3Index varName = 0;                   \
96     Arg argName = {                        \
97         .names = {"-i", "--index"},        \
98         .scanFormat = "%" PRIx64,          \
99         .valueName = "index",              \
100         .value = &varName,                 \
101         .helpText =                        \
102             "Index, or not specified to read indexes from standard input."}
103 #define ARG_KML \
104     { .names = {"-k", "--kml"}, .helpText = "Print output in KML format." }
105 #define DEFINE_KML_NAME_ARG(varName, argName)      \
106     char varName[BUFF_SIZE] = {0};                 \
107     Arg argName = {                                \
108         .names = {"--kn", "--kml-name"},           \
109         .scanFormat = "%255c", /* BUFF_SIZE - 1 */ \
110         .valueName = "name",                       \
111         .value = &varName,                         \
112         .helpText = "Text for the KML name tag, if --kml is specified."}
113 #define DEFINE_KML_DESC_ARG(varName, argName)      \
114     char varName[BUFF_SIZE] = {0};                 \
115     Arg argName = {                                \
116         .names = {"--kd", "--kml-description"},    \
117         .scanFormat = "%255c", /* BUFF_SIZE - 1 */ \
118         .valueName = "description",                \
119         .value = &varName,                         \
120         .helpText =                                \
121             "Text for the KML description tag, if --kml is specified."}
122 
123 #endif
124