ArgParse.c (10064B)
1 /*************************************************************************************************** 2 3 Zyan Core Library (Zycore-C) 4 5 Original Author : Joel Hoener 6 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 25 ***************************************************************************************************/ 26 27 #include "zydis/Zycore/ArgParse.h" 28 #include "zydis/Zycore/LibC.h" 29 30 /* ============================================================================================== */ 31 /* Exported functions */ 32 /* ============================================================================================== */ 33 34 #ifndef ZYAN_NO_LIBC 35 36 ZyanStatus ZyanArgParse(const ZyanArgParseConfig *cfg, ZyanVector* parsed, 37 const char** error_token) 38 { 39 return ZyanArgParseEx(cfg, parsed, error_token, ZyanAllocatorDefault()); 40 } 41 42 #endif 43 44 ZyanStatus ZyanArgParseEx(const ZyanArgParseConfig *cfg, ZyanVector* parsed, 45 const char** error_token, ZyanAllocator* allocator) 46 { 47 # define ZYAN_ERR_TOK(tok) if (error_token) { *error_token = tok; } 48 49 ZYAN_ASSERT(cfg); 50 ZYAN_ASSERT(parsed); 51 52 // TODO: Once we have a decent hash map impl, refactor this to use it. The majority of for 53 // loops through the argument list could be avoided. 54 55 if (cfg->min_unnamed_args > cfg->max_unnamed_args) 56 { 57 return ZYAN_STATUS_INVALID_ARGUMENT; 58 } 59 60 // Check argument syntax. 61 for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) 62 { 63 // TODO: Duplicate check 64 65 if (!def->name) 66 { 67 return ZYAN_STATUS_INVALID_ARGUMENT; 68 } 69 70 ZyanUSize arg_len = ZYAN_STRLEN(def->name); 71 if (arg_len < 2 || def->name[0] != '-') 72 { 73 return ZYAN_STATUS_INVALID_ARGUMENT; 74 } 75 76 // Single dash arguments only accept a single char name. 77 if (def->name[1] != '-' && arg_len != 2) 78 { 79 return ZYAN_STATUS_INVALID_ARGUMENT; 80 } 81 } 82 83 // Initialize output vector. 84 ZYAN_CHECK(ZyanVectorInitEx(parsed, sizeof(ZyanArgParseArg), cfg->argc, ZYAN_NULL, allocator, 85 ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD)); 86 87 ZyanStatus err; 88 ZyanBool accept_dash_args = ZYAN_TRUE; 89 ZyanUSize num_unnamed_args = 0; 90 for (ZyanUSize i = 1; i < cfg->argc; ++i) 91 { 92 const char* cur_arg = cfg->argv[i]; 93 ZyanUSize arg_len = ZYAN_STRLEN(cfg->argv[i]); 94 95 // Double-dash argument? 96 if (accept_dash_args && arg_len >= 2 && ZYAN_MEMCMP(cur_arg, "--", 2) == 0) 97 { 98 // GNU style end of argument parsing. 99 if (arg_len == 2) 100 { 101 accept_dash_args = ZYAN_FALSE; 102 } 103 // Regular double-dash argument. 104 else 105 { 106 // Allocate parsed argument struct. 107 ZyanArgParseArg* parsed_arg; 108 ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); 109 ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); 110 111 // Find corresponding argument definition. 112 for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) 113 { 114 if (ZYAN_STRCMP(def->name, cur_arg) == 0) 115 { 116 parsed_arg->def = def; 117 break; 118 } 119 } 120 121 // Search exhausted & argument not found. RIP. 122 if (!parsed_arg->def) 123 { 124 err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD; 125 ZYAN_ERR_TOK(cur_arg); 126 goto failure; 127 } 128 129 // Does the argument expect a value? If yes, consume next token. 130 if (!parsed_arg->def->boolean) 131 { 132 if (i == cfg->argc - 1) 133 { 134 err = ZYAN_STATUS_ARG_MISSES_VALUE; 135 ZYAN_ERR_TOK(cur_arg); 136 goto failure; 137 } 138 parsed_arg->has_value = ZYAN_TRUE; 139 ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i])); 140 } 141 } 142 143 // Continue parsing at next token. 144 continue; 145 } 146 147 // Single-dash argument? 148 // TODO: How to deal with just dashes? Current code treats it as unnamed arg. 149 if (accept_dash_args && arg_len > 1 && cur_arg[0] == '-') 150 { 151 // Iterate argument token chars until there are either no more chars left 152 // or we encounter a non-boolean argument, in which case we consume the 153 // remaining chars as its value. 154 for (const char* read_ptr = cur_arg + 1; *read_ptr; ++read_ptr) 155 { 156 // Allocate parsed argument struct. 157 ZyanArgParseArg* parsed_arg; 158 ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); 159 ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); 160 161 // Find corresponding argument definition. 162 for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) 163 { 164 if (ZYAN_STRLEN(def->name) == 2 && 165 def->name[0] == '-' && 166 def->name[1] == *read_ptr) 167 { 168 parsed_arg->def = def; 169 break; 170 } 171 } 172 173 // Search exhausted, no match found? 174 if (!parsed_arg->def) 175 { 176 err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD; 177 ZYAN_ERR_TOK(cur_arg); 178 goto failure; 179 } 180 181 // Requires value? 182 if (!parsed_arg->def->boolean) 183 { 184 // If there are chars left, consume them (e.g. `-n1000`). 185 if (read_ptr[1]) 186 { 187 parsed_arg->has_value = ZYAN_TRUE; 188 ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, read_ptr + 1)); 189 } 190 // If not, consume next token (e.g. `-n 1000`). 191 else 192 { 193 if (i == cfg->argc - 1) 194 { 195 err = ZYAN_STATUS_ARG_MISSES_VALUE; 196 ZYAN_ERR_TOK(cur_arg) 197 goto failure; 198 } 199 200 parsed_arg->has_value = ZYAN_TRUE; 201 ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i])); 202 } 203 204 // Either way, continue with next argument. 205 goto continue_main_loop; 206 } 207 } 208 } 209 210 // Still here? We're looking at an unnamed argument. 211 ++num_unnamed_args; 212 if (num_unnamed_args > cfg->max_unnamed_args) 213 { 214 err = ZYAN_STATUS_TOO_MANY_ARGS; 215 ZYAN_ERR_TOK(cur_arg); 216 goto failure; 217 } 218 219 // Allocate parsed argument struct. 220 ZyanArgParseArg* parsed_arg; 221 ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); 222 ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); 223 parsed_arg->has_value = ZYAN_TRUE; 224 ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cur_arg)); 225 226 continue_main_loop:; 227 } 228 229 // All tokens processed. Do we have enough unnamed arguments? 230 if (num_unnamed_args < cfg->min_unnamed_args) 231 { 232 err = ZYAN_STATUS_TOO_FEW_ARGS; 233 // No sensible error token for this error type. 234 goto failure; 235 } 236 237 // Check whether all required arguments are present. 238 ZyanUSize num_parsed_args; 239 ZYAN_CHECK(ZyanVectorGetSize(parsed, &num_parsed_args)); 240 for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) 241 { 242 if (!def->required) continue; 243 244 ZyanBool arg_found = ZYAN_FALSE; 245 for (ZyanUSize i = 0; i < num_parsed_args; ++i) 246 { 247 const ZyanArgParseArg* arg = ZYAN_NULL; 248 ZYAN_CHECK(ZyanVectorGetPointer(parsed, i, (const void**)&arg)); 249 250 // Skip unnamed args. 251 if (!arg->def) continue; 252 253 if (arg->def == def) 254 { 255 arg_found = ZYAN_TRUE; 256 break; 257 } 258 } 259 260 if (!arg_found) 261 { 262 err = ZYAN_STATUS_REQUIRED_ARG_MISSING; 263 ZYAN_ERR_TOK(def->name); 264 goto failure; 265 } 266 } 267 268 // Yay! 269 ZYAN_ERR_TOK(ZYAN_NULL); 270 return ZYAN_STATUS_SUCCESS; 271 272 failure: 273 ZYAN_CHECK(ZyanVectorDestroy(parsed)); 274 return err; 275 276 # undef ZYAN_ERR_TOK 277 } 278 279 /* ============================================================================================== */