1 1.8 christos /* $NetBSD: find.c,v 1.9 2024/08/18 20:47:24 christos Exp $ */ 2 1.1 christos 3 1.1 christos /** 4 1.1 christos * @file check.c 5 1.1 christos * 6 1.1 christos * @brief Hunt for options in the option descriptor list 7 1.1 christos * 8 1.1 christos * This file contains the routines that deal with processing quoted strings 9 1.1 christos * into an internal format. 10 1.1 christos * 11 1.1 christos * @addtogroup autoopts 12 1.1 christos * @{ 13 1.1 christos */ 14 1.1 christos /* 15 1.1 christos * This file is part of AutoOpts, a companion to AutoGen. 16 1.1 christos * AutoOpts is free software. 17 1.9 christos * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved 18 1.1 christos * 19 1.1 christos * AutoOpts is available under any one of two licenses. The license 20 1.1 christos * in use must be one of these two and the choice is under the control 21 1.1 christos * of the user of the license. 22 1.1 christos * 23 1.1 christos * The GNU Lesser General Public License, version 3 or later 24 1.1 christos * See the files "COPYING.lgplv3" and "COPYING.gplv3" 25 1.1 christos * 26 1.1 christos * The Modified Berkeley Software Distribution License 27 1.1 christos * See the file "COPYING.mbsd" 28 1.1 christos * 29 1.1 christos * These files have the following sha256 sums: 30 1.1 christos * 31 1.1 christos * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 32 1.1 christos * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 33 1.1 christos * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 34 1.1 christos */ 35 1.1 christos 36 1.1 christos /** 37 1.1 christos * find the name and name length we are looking for 38 1.1 christos */ 39 1.1 christos static int 40 1.1 christos parse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz) 41 1.1 christos { 42 1.1 christos int res = 0; 43 1.1 christos char const * p = *nm_pp; 44 1.1 christos *arg_pp = NULL; 45 1.1 christos 46 1.1 christos for (;;) { 47 1.1 christos switch (*(p++)) { 48 1.1 christos case NUL: return res; 49 1.1 christos 50 1.1 christos case '=': 51 1.1 christos memcpy(buf, *nm_pp, (size_t)res); 52 1.1 christos 53 1.1 christos buf[res] = NUL; 54 1.1 christos *nm_pp = buf; 55 1.9 christos *arg_pp = __UNCONST(p); 56 1.1 christos return res; 57 1.1 christos 58 1.1 christos default: 59 1.1 christos if (++res >= (int)bufsz) 60 1.1 christos return -1; 61 1.1 christos } 62 1.1 christos } 63 1.1 christos } 64 1.1 christos 65 1.1 christos /** 66 1.1 christos * print out the options that match the given name. 67 1.1 christos * 68 1.1 christos * @param pOpts option data 69 1.1 christos * @param opt_name name of option to look for 70 1.1 christos */ 71 1.1 christos static void 72 1.1 christos opt_ambiguities(tOptions * opts, char const * name, int nm_len) 73 1.1 christos { 74 1.1 christos char const * const hyph = 75 1.1 christos NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER; 76 1.1 christos 77 1.1 christos tOptDesc * pOD = opts->pOptDesc; 78 1.1 christos int idx = 0; 79 1.1 christos 80 1.1 christos fputs(zambig_list_msg, stderr); 81 1.1 christos do { 82 1.1 christos if (pOD->pz_Name == NULL) 83 1.1 christos continue; /* doc option */ 84 1.1 christos 85 1.1 christos if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) 86 1.1 christos fprintf(stderr, zambig_file, hyph, pOD->pz_Name); 87 1.1 christos 88 1.1 christos else if ( (pOD->pz_DisableName != NULL) 89 1.1 christos && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0) 90 1.1 christos ) 91 1.1 christos fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName); 92 1.1 christos } while (pOD++, (++idx < opts->optCt)); 93 1.1 christos } 94 1.1 christos 95 1.1 christos /** 96 1.1 christos * Determine the number of options that match the name 97 1.1 christos * 98 1.1 christos * @param pOpts option data 99 1.1 christos * @param opt_name name of option to look for 100 1.1 christos * @param nm_len length of provided name 101 1.1 christos * @param index pointer to int for option index 102 1.1 christos * @param disable pointer to bool to mark disabled option 103 1.1 christos * @return count of options that match 104 1.1 christos */ 105 1.1 christos static int 106 1.1 christos opt_match_ct(tOptions * opts, char const * name, int nm_len, 107 1.1 christos int * ixp, bool * disable) 108 1.1 christos { 109 1.1 christos int matchCt = 0; 110 1.1 christos int idx = 0; 111 1.1 christos int idxLim = opts->optCt; 112 1.1 christos tOptDesc * pOD = opts->pOptDesc; 113 1.1 christos 114 1.1 christos do { 115 1.1 christos /* 116 1.1 christos * If option disabled or a doc option, skip to next 117 1.1 christos */ 118 1.1 christos if (pOD->pz_Name == NULL) 119 1.1 christos continue; 120 1.1 christos 121 1.1 christos if ( SKIP_OPT(pOD) 122 1.1 christos && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT))) 123 1.1 christos continue; 124 1.1 christos 125 1.1 christos if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) { 126 1.1 christos /* 127 1.1 christos * IF we have a complete match 128 1.1 christos * THEN it takes priority over any already located partial 129 1.1 christos */ 130 1.1 christos if (pOD->pz_Name[ nm_len ] == NUL) { 131 1.1 christos *ixp = idx; 132 1.1 christos return 1; 133 1.1 christos } 134 1.1 christos } 135 1.1 christos 136 1.1 christos /* 137 1.1 christos * IF there is a disable name 138 1.1 christos * *AND* the option name matches the disable name 139 1.1 christos * THEN ... 140 1.1 christos */ 141 1.1 christos else if ( (pOD->pz_DisableName != NULL) 142 1.1 christos && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0) 143 1.1 christos ) { 144 1.1 christos *disable = true; 145 1.1 christos 146 1.1 christos /* 147 1.1 christos * IF we have a complete match 148 1.1 christos * THEN it takes priority over any already located partial 149 1.1 christos */ 150 1.1 christos if (pOD->pz_DisableName[ nm_len ] == NUL) { 151 1.1 christos *ixp = idx; 152 1.1 christos return 1; 153 1.1 christos } 154 1.1 christos } 155 1.1 christos 156 1.1 christos else 157 1.1 christos continue; /* does not match any option */ 158 1.1 christos 159 1.1 christos /* 160 1.1 christos * We found a full or partial match, either regular or disabling. 161 1.1 christos * Remember the index for later. 162 1.1 christos */ 163 1.1 christos *ixp = idx; 164 1.1 christos ++matchCt; 165 1.1 christos 166 1.1 christos } while (pOD++, (++idx < idxLim)); 167 1.1 christos 168 1.1 christos return matchCt; 169 1.1 christos } 170 1.1 christos 171 1.1 christos /** 172 1.1 christos * Set the option to the indicated option number. 173 1.1 christos * 174 1.1 christos * @param opts option data 175 1.1 christos * @param arg option argument (if glued to name) 176 1.1 christos * @param idx option index 177 1.1 christos * @param disable mark disabled option 178 1.1 christos * @param st state about current option 179 1.1 christos */ 180 1.1 christos static tSuccess 181 1.1 christos opt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st) 182 1.1 christos { 183 1.1 christos tOptDesc * pOD = opts->pOptDesc + idx; 184 1.1 christos 185 1.1 christos if (SKIP_OPT(pOD)) { 186 1.1 christos if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0) 187 1.1 christos return FAILURE; 188 1.1 christos 189 1.1 christos fprintf(stderr, zDisabledErr, opts->pzProgName, pOD->pz_Name); 190 1.1 christos if (pOD->pzText != NULL) 191 1.1 christos fprintf(stderr, SET_OFF_FMT, pOD->pzText); 192 1.1 christos fputc(NL, stderr); 193 1.1 christos (*opts->pUsageProc)(opts, EXIT_FAILURE); 194 1.1 christos /* NOTREACHED */ 195 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 196 1.1 christos } 197 1.1 christos 198 1.1 christos /* 199 1.1 christos * IF we found a disablement name, 200 1.1 christos * THEN set the bit in the callers' flag word 201 1.1 christos */ 202 1.1 christos if (disable) 203 1.1 christos st->flags |= OPTST_DISABLED; 204 1.1 christos 205 1.1 christos st->pOD = pOD; 206 1.1 christos st->pzOptArg = arg; 207 1.1 christos st->optType = TOPT_LONG; 208 1.1 christos 209 1.1 christos return SUCCESS; 210 1.1 christos } 211 1.1 christos 212 1.1 christos /** 213 1.1 christos * An option was not found. Check for default option and set it 214 1.1 christos * if there is one. Otherwise, handle the error. 215 1.1 christos * 216 1.1 christos * @param opts option data 217 1.1 christos * @param name name of option to look for 218 1.1 christos * @param arg option argument 219 1.1 christos * @param st state about current option 220 1.1 christos * 221 1.1 christos * @return success status 222 1.1 christos */ 223 1.1 christos static tSuccess 224 1.1 christos opt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st) 225 1.1 christos { 226 1.1 christos /* 227 1.1 christos * IF there is no equal sign 228 1.1 christos * *AND* we are using named arguments 229 1.1 christos * *AND* there is a default named option, 230 1.1 christos * THEN return that option. 231 1.1 christos */ 232 1.1 christos if ( (arg == NULL) 233 1.1 christos && NAMED_OPTS(opts) 234 1.1 christos && (opts->specOptIdx.default_opt != NO_EQUIVALENT)) { 235 1.1 christos 236 1.1 christos st->pOD = opts->pOptDesc + opts->specOptIdx.default_opt; 237 1.1 christos st->pzOptArg = name; 238 1.1 christos st->optType = TOPT_DEFAULT; 239 1.1 christos return SUCCESS; 240 1.1 christos } 241 1.1 christos 242 1.1 christos if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) { 243 1.1 christos fprintf(stderr, zIllOptStr, opts->pzProgPath, name); 244 1.1 christos (*opts->pUsageProc)(opts, EXIT_FAILURE); 245 1.1 christos /* NOTREACHED */ 246 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 247 1.1 christos } 248 1.1 christos 249 1.1 christos return FAILURE; 250 1.1 christos } 251 1.1 christos 252 1.1 christos /** 253 1.1 christos * Several options match the provided name. 254 1.1 christos * 255 1.1 christos * @param opts option data 256 1.1 christos * @param name name of option to look for 257 1.1 christos * @param match_ct number of matching options 258 1.1 christos * 259 1.1 christos * @return success status (always FAILURE, if it returns) 260 1.1 christos */ 261 1.1 christos static tSuccess 262 1.1 christos opt_ambiguous(tOptions * opts, char const * name, int match_ct) 263 1.1 christos { 264 1.1 christos if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) { 265 1.1 christos fprintf(stderr, zambig_opt_fmt, opts->pzProgPath, name, match_ct); 266 1.1 christos if (match_ct <= 4) 267 1.1 christos opt_ambiguities(opts, name, (int)strlen(name)); 268 1.1 christos (*opts->pUsageProc)(opts, EXIT_FAILURE); 269 1.1 christos /* NOTREACHED */ 270 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 271 1.1 christos } 272 1.1 christos return FAILURE; 273 1.1 christos } 274 1.1 christos 275 1.1 christos /*=export_func optionVendorOption 276 1.1 christos * private: 277 1.1 christos * 278 1.1 christos * what: Process a vendor option 279 1.1 christos * arg: + tOptions * + pOpts + program options descriptor + 280 1.1 christos * arg: + tOptDesc * + pOptDesc + the descriptor for this arg + 281 1.1 christos * 282 1.1 christos * doc: 283 1.1 christos * For POSIX specified utilities, the options are constrained to the options, 284 1.1 christos * @xref{config attributes, Program Configuration}. AutoOpts clients should 285 1.1 christos * never specify this directly. It gets referenced when the option 286 1.1 christos * definitions contain a "vendor-opt" attribute. 287 1.1 christos =*/ 288 1.1 christos void 289 1.1 christos optionVendorOption(tOptions * pOpts, tOptDesc * pOD) 290 1.1 christos { 291 1.1 christos tOptState opt_st = OPTSTATE_INITIALIZER(PRESET); 292 1.1 christos char const * vopt_str = pOD->optArg.argString; 293 1.1 christos 294 1.1 christos if (pOpts <= OPTPROC_EMIT_LIMIT) 295 1.1 christos return; 296 1.1 christos 297 1.1 christos if ((pOD->fOptState & OPTST_RESET) != 0) 298 1.1 christos return; 299 1.1 christos 300 1.1 christos if ((pOD->fOptState & OPTPROC_IMMEDIATE) == 0) 301 1.1 christos opt_st.flags = OPTST_DEFINED; 302 1.1 christos 303 1.1 christos if ( ((pOpts->fOptSet & OPTPROC_VENDOR_OPT) == 0) 304 1.1 christos || ! SUCCESSFUL(opt_find_long(pOpts, vopt_str, &opt_st)) 305 1.1 christos || ! SUCCESSFUL(get_opt_arg(pOpts, &opt_st)) ) 306 1.1 christos { 307 1.1 christos fprintf(stderr, zIllVendOptStr, pOpts->pzProgName, vopt_str); 308 1.1 christos (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); 309 1.1 christos /* NOTREACHED */ 310 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 311 1.1 christos } 312 1.1 christos 313 1.1 christos /* 314 1.1 christos * See if we are in immediate handling state. 315 1.1 christos */ 316 1.1 christos if (pOpts->fOptSet & OPTPROC_IMMEDIATE) { 317 1.1 christos /* 318 1.1 christos * See if the enclosed option is okay with that state. 319 1.1 christos */ 320 1.1 christos if (DO_IMMEDIATELY(opt_st.flags)) 321 1.1 christos (void)handle_opt(pOpts, &opt_st); 322 1.1 christos 323 1.1 christos } else { 324 1.1 christos /* 325 1.1 christos * non-immediate direction. 326 1.1 christos * See if the enclosed option is okay with that state. 327 1.1 christos */ 328 1.1 christos if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags)) 329 1.1 christos (void)handle_opt(pOpts, &opt_st); 330 1.1 christos } 331 1.1 christos } 332 1.1 christos 333 1.1 christos /** 334 1.1 christos * Find the option descriptor by full name. 335 1.1 christos * 336 1.1 christos * @param opts option data 337 1.1 christos * @param opt_name name of option to look for 338 1.1 christos * @param state state about current option 339 1.1 christos * 340 1.1 christos * @return success status 341 1.1 christos */ 342 1.9 christos static tSuccess 343 1.1 christos opt_find_long(tOptions * opts, char const * opt_name, tOptState * state) 344 1.1 christos { 345 1.1 christos char name_buf[128]; 346 1.1 christos char * opt_arg; 347 1.1 christos int nm_len = parse_opt(&opt_name, &opt_arg, name_buf, sizeof(name_buf)); 348 1.1 christos 349 1.1 christos int idx = 0; 350 1.1 christos bool disable = false; 351 1.1 christos int ct; 352 1.1 christos 353 1.1 christos if (nm_len <= 1) { 354 1.1 christos if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0) 355 1.1 christos return FAILURE; 356 1.1 christos 357 1.1 christos fprintf(stderr, zInvalOptName, opts->pzProgName, opt_name); 358 1.1 christos (*opts->pUsageProc)(opts, EXIT_FAILURE); 359 1.1 christos /* NOTREACHED */ 360 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 361 1.1 christos } 362 1.1 christos 363 1.1 christos ct = opt_match_ct(opts, opt_name, nm_len, &idx, &disable); 364 1.1 christos 365 1.1 christos /* 366 1.1 christos * See if we found one match, no matches or multiple matches. 367 1.1 christos */ 368 1.1 christos switch (ct) { 369 1.1 christos case 1: return opt_set(opts, opt_arg, idx, disable, state); 370 1.1 christos case 0: return opt_unknown(opts, opt_name, opt_arg, state); 371 1.1 christos default: return opt_ambiguous(opts, opt_name, ct); 372 1.1 christos } 373 1.1 christos } 374 1.1 christos 375 1.1 christos 376 1.1 christos /** 377 1.1 christos * Find the short option descriptor for the current option 378 1.1 christos * 379 1.1 christos * @param pOpts option data 380 1.1 christos * @param optValue option flag character 381 1.1 christos * @param pOptState state about current option 382 1.1 christos */ 383 1.9 christos static tSuccess 384 1.5 christos opt_find_short(tOptions * pOpts, uint_t optValue, tOptState * pOptState) 385 1.1 christos { 386 1.5 christos tOptDesc * pRes = pOpts->pOptDesc; 387 1.1 christos int ct = pOpts->optCt; 388 1.1 christos 389 1.1 christos /* 390 1.1 christos * Search the option list 391 1.1 christos */ 392 1.1 christos do { 393 1.1 christos if (optValue != pRes->optValue) 394 1.1 christos continue; 395 1.1 christos 396 1.1 christos if (SKIP_OPT(pRes)) { 397 1.1 christos if ( (pRes->fOptState == (OPTST_OMITTED | OPTST_NO_INIT)) 398 1.1 christos && (pRes->pz_Name != NULL)) { 399 1.1 christos if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) 400 1.1 christos return FAILURE; 401 1.1 christos 402 1.1 christos fprintf(stderr, zDisabledErr, pOpts->pzProgPath, pRes->pz_Name); 403 1.1 christos if (pRes->pzText != NULL) 404 1.1 christos fprintf(stderr, SET_OFF_FMT, pRes->pzText); 405 1.1 christos fputc(NL, stderr); 406 1.1 christos (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); 407 1.1 christos /* NOTREACHED */ 408 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 409 1.1 christos } 410 1.1 christos goto short_opt_error; 411 1.1 christos } 412 1.1 christos 413 1.1 christos pOptState->pOD = pRes; 414 1.1 christos pOptState->optType = TOPT_SHORT; 415 1.1 christos return SUCCESS; 416 1.1 christos 417 1.1 christos } while (pRes++, --ct > 0); 418 1.1 christos 419 1.1 christos /* 420 1.1 christos * IF the character value is a digit 421 1.1 christos * AND there is a special number option ("-n") 422 1.1 christos * THEN the result is the "option" itself and the 423 1.1 christos * option is the specially marked "number" option. 424 1.1 christos */ 425 1.1 christos if ( IS_DEC_DIGIT_CHAR(optValue) 426 1.1 christos && (pOpts->specOptIdx.number_option != NO_EQUIVALENT) ) { 427 1.1 christos pOptState->pOD = \ 428 1.1 christos pRes = pOpts->pOptDesc + pOpts->specOptIdx.number_option; 429 1.1 christos (pOpts->pzCurOpt)--; 430 1.1 christos pOptState->optType = TOPT_SHORT; 431 1.1 christos return SUCCESS; 432 1.1 christos } 433 1.1 christos 434 1.1 christos short_opt_error: 435 1.1 christos 436 1.1 christos /* 437 1.1 christos * IF we are to stop on errors (the default, actually) 438 1.1 christos * THEN call the usage procedure. 439 1.1 christos */ 440 1.1 christos if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) { 441 1.1 christos fprintf(stderr, zIllOptChr, pOpts->pzProgPath, optValue); 442 1.1 christos (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); 443 1.1 christos /* NOTREACHED */ 444 1.1 christos _exit(EXIT_FAILURE); /* to be certain */ 445 1.1 christos } 446 1.1 christos 447 1.1 christos return FAILURE; 448 1.1 christos } 449 1.1 christos 450 1.1 christos /** 451 1.1 christos * Process option with a required argument. Long options can either have a 452 1.1 christos * separate command line argument, or an argument attached by the '=' 453 1.1 christos * character. Figure out which. 454 1.1 christos * 455 1.1 christos * @param[in,out] opts the program option descriptor 456 1.1 christos * @param[in,out] o_st the option processing state 457 1.1 christos * @returns SUCCESS or FAILURE 458 1.1 christos */ 459 1.1 christos static tSuccess 460 1.1 christos get_opt_arg_must(tOptions * opts, tOptState * o_st) 461 1.1 christos { 462 1.1 christos switch (o_st->optType) { 463 1.1 christos case TOPT_SHORT: 464 1.1 christos /* 465 1.1 christos * See if an arg string follows the flag character 466 1.1 christos */ 467 1.1 christos if (*++(opts->pzCurOpt) == NUL) 468 1.1 christos opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx++ ]; 469 1.1 christos o_st->pzOptArg = opts->pzCurOpt; 470 1.1 christos break; 471 1.1 christos 472 1.1 christos case TOPT_LONG: 473 1.1 christos /* 474 1.1 christos * See if an arg string has already been assigned (glued on 475 1.1 christos * with an `=' character) 476 1.1 christos */ 477 1.1 christos if (o_st->pzOptArg == NULL) 478 1.1 christos o_st->pzOptArg = opts->origArgVect[ opts->curOptIdx++ ]; 479 1.1 christos break; 480 1.1 christos 481 1.1 christos default: 482 1.1 christos #ifdef DEBUG 483 1.1 christos fputs("AutoOpts lib error: option type not selected\n", stderr); 484 1.1 christos option_exits(EXIT_FAILURE); 485 1.1 christos #endif 486 1.1 christos 487 1.1 christos case TOPT_DEFAULT: 488 1.1 christos /* 489 1.1 christos * The option was selected by default. The current token is 490 1.1 christos * the option argument. 491 1.1 christos */ 492 1.1 christos break; 493 1.1 christos } 494 1.1 christos 495 1.1 christos /* 496 1.1 christos * Make sure we did not overflow the argument list. 497 1.1 christos */ 498 1.1 christos if (opts->curOptIdx > opts->origArgCt) { 499 1.1 christos fprintf(stderr, zMisArg, opts->pzProgPath, o_st->pOD->pz_Name); 500 1.1 christos return FAILURE; 501 1.1 christos } 502 1.1 christos 503 1.1 christos opts->pzCurOpt = NULL; /* next time advance to next arg */ 504 1.1 christos return SUCCESS; 505 1.1 christos } 506 1.1 christos 507 1.1 christos /** 508 1.1 christos * Process an option with an optional argument. For short options, it looks 509 1.1 christos * at the character after the option character, or it consumes the next full 510 1.1 christos * argument. For long options, it looks for an '=' character attachment to 511 1.1 christos * the long option name before deciding to take the next command line 512 1.1 christos * argument. 513 1.1 christos * 514 1.1 christos * @param pOpts the option descriptor 515 1.1 christos * @param o_st a structure for managing the current processing state 516 1.1 christos * @returns SUCCESS or does not return 517 1.1 christos */ 518 1.1 christos static tSuccess 519 1.1 christos get_opt_arg_may(tOptions * pOpts, tOptState * o_st) 520 1.1 christos { 521 1.1 christos /* 522 1.1 christos * An option argument is optional. 523 1.1 christos */ 524 1.1 christos switch (o_st->optType) { 525 1.1 christos case TOPT_SHORT: 526 1.1 christos if (*++pOpts->pzCurOpt != NUL) 527 1.1 christos o_st->pzOptArg = pOpts->pzCurOpt; 528 1.1 christos else { 529 1.5 christos char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ]; 530 1.1 christos 531 1.1 christos /* 532 1.1 christos * BECAUSE it is optional, we must make sure 533 1.1 christos * we did not find another flag and that there 534 1.1 christos * is such an argument. 535 1.1 christos */ 536 1.1 christos if ((pzLA == NULL) || (*pzLA == '-')) 537 1.1 christos o_st->pzOptArg = NULL; 538 1.1 christos else { 539 1.1 christos pOpts->curOptIdx++; /* argument found */ 540 1.1 christos o_st->pzOptArg = pzLA; 541 1.1 christos } 542 1.1 christos } 543 1.1 christos break; 544 1.1 christos 545 1.1 christos case TOPT_LONG: 546 1.1 christos /* 547 1.1 christos * Look for an argument if we don't already have one (glued on 548 1.1 christos * with a `=' character) *AND* we are not in named argument mode 549 1.1 christos */ 550 1.1 christos if ( (o_st->pzOptArg == NULL) 551 1.1 christos && (! NAMED_OPTS(pOpts))) { 552 1.5 christos char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ]; 553 1.1 christos 554 1.1 christos /* 555 1.1 christos * BECAUSE it is optional, we must make sure 556 1.1 christos * we did not find another flag and that there 557 1.1 christos * is such an argument. 558 1.1 christos */ 559 1.1 christos if ((pzLA == NULL) || (*pzLA == '-')) 560 1.1 christos o_st->pzOptArg = NULL; 561 1.1 christos else { 562 1.1 christos pOpts->curOptIdx++; /* argument found */ 563 1.1 christos o_st->pzOptArg = pzLA; 564 1.1 christos } 565 1.1 christos } 566 1.1 christos break; 567 1.1 christos 568 1.1 christos default: 569 1.1 christos case TOPT_DEFAULT: 570 1.1 christos ao_bug(zbad_default_msg); 571 1.1 christos } 572 1.1 christos 573 1.1 christos /* 574 1.1 christos * After an option with an optional argument, we will 575 1.1 christos * *always* start with the next option because if there 576 1.1 christos * were any characters following the option name/flag, 577 1.1 christos * they would be interpreted as the argument. 578 1.1 christos */ 579 1.1 christos pOpts->pzCurOpt = NULL; 580 1.1 christos return SUCCESS; 581 1.1 christos } 582 1.1 christos 583 1.1 christos /** 584 1.1 christos * Process option that does not have an argument. 585 1.1 christos * 586 1.1 christos * @param[in,out] opts the program option descriptor 587 1.1 christos * @param[in,out] o_st the option processing state 588 1.1 christos * @returns SUCCESS or FAILURE 589 1.1 christos */ 590 1.1 christos static tSuccess 591 1.5 christos get_opt_arg_none(tOptions * pOpts, tOptState * o_st) 592 1.1 christos { 593 1.1 christos /* 594 1.1 christos * No option argument. Make sure next time around we find 595 1.1 christos * the correct option flag character for short options 596 1.1 christos */ 597 1.1 christos if (o_st->optType == TOPT_SHORT) 598 1.1 christos (pOpts->pzCurOpt)++; 599 1.1 christos 600 1.1 christos /* 601 1.1 christos * It is a long option. Make sure there was no ``=xxx'' argument 602 1.1 christos */ 603 1.1 christos else if (o_st->pzOptArg != NULL) { 604 1.1 christos fprintf(stderr, zNoArg, pOpts->pzProgPath, o_st->pOD->pz_Name); 605 1.1 christos return FAILURE; 606 1.1 christos } 607 1.1 christos 608 1.1 christos /* 609 1.1 christos * It is a long option. Advance to next command line argument. 610 1.1 christos */ 611 1.1 christos else 612 1.1 christos pOpts->pzCurOpt = NULL; 613 1.9 christos 614 1.1 christos return SUCCESS; 615 1.1 christos } 616 1.1 christos 617 1.1 christos /** 618 1.1 christos * Process option. Figure out whether or not to look for an option argument. 619 1.1 christos * 620 1.1 christos * @param[in,out] opts the program option descriptor 621 1.1 christos * @param[in,out] o_st the option processing state 622 1.1 christos * @returns SUCCESS or FAILURE 623 1.1 christos */ 624 1.9 christos static tSuccess 625 1.1 christos get_opt_arg(tOptions * opts, tOptState * o_st) 626 1.1 christos { 627 1.1 christos o_st->flags |= (o_st->pOD->fOptState & OPTST_PERSISTENT_MASK); 628 1.1 christos 629 1.1 christos /* 630 1.1 christos * Disabled options and options specified to not have arguments 631 1.1 christos * are handled with the "none" procedure. Otherwise, check the 632 1.1 christos * optional flag and call either the "may" or "must" function. 633 1.1 christos */ 634 1.9 christos if ((o_st->flags & OPTST_DISABLED) != 0) 635 1.1 christos return get_opt_arg_none(opts, o_st); 636 1.9 christos 637 1.9 christos switch (OPTST_GET_ARGTYPE(o_st->flags)) { 638 1.9 christos case OPARG_TYPE_STATIC: 639 1.9 christos { 640 1.9 christos /* 641 1.9 christos * Propagate the static arg 642 1.9 christos */ 643 1.9 christos tSuccess res = get_opt_arg_none(opts, o_st); 644 1.9 christos o_st->pzOptArg = o_st->pOD->optArg.argString; 645 1.9 christos return res; 646 1.9 christos } 647 1.9 christos 648 1.9 christos case OPARG_TYPE_NONE: 649 1.9 christos return get_opt_arg_none(opts, o_st); 650 1.9 christos } 651 1.9 christos 652 1.1 christos if (o_st->flags & OPTST_ARG_OPTIONAL) 653 1.1 christos return get_opt_arg_may( opts, o_st); 654 1.1 christos 655 1.1 christos return get_opt_arg_must(opts, o_st); 656 1.1 christos } 657 1.1 christos 658 1.1 christos /** 659 1.1 christos * Find the option descriptor for the current option. 660 1.1 christos * 661 1.1 christos * @param[in,out] opts the program option descriptor 662 1.1 christos * @param[in,out] o_st the option processing state 663 1.1 christos * @returns SUCCESS or FAILURE 664 1.1 christos */ 665 1.9 christos static tSuccess 666 1.1 christos find_opt(tOptions * opts, tOptState * o_st) 667 1.1 christos { 668 1.1 christos /* 669 1.1 christos * IF we are continuing a short option list (e.g. -xyz...) 670 1.1 christos * THEN continue a single flag option. 671 1.1 christos * OTHERWISE see if there is room to advance and then do so. 672 1.1 christos */ 673 1.1 christos if ((opts->pzCurOpt != NULL) && (*opts->pzCurOpt != NUL)) 674 1.1 christos return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st); 675 1.1 christos 676 1.1 christos if (opts->curOptIdx >= opts->origArgCt) 677 1.1 christos return PROBLEM; /* NORMAL COMPLETION */ 678 1.1 christos 679 1.1 christos opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx ]; 680 1.1 christos 681 1.1 christos /* 682 1.1 christos * IF all arguments must be named options, ... 683 1.1 christos */ 684 1.1 christos if (NAMED_OPTS(opts)) { 685 1.1 christos char * pz = opts->pzCurOpt; 686 1.1 christos int def; 687 1.1 christos tSuccess res; 688 1.1 christos uint16_t * def_opt; 689 1.1 christos 690 1.1 christos opts->curOptIdx++; 691 1.1 christos 692 1.1 christos if (*pz != '-') 693 1.1 christos return opt_find_long(opts, pz, o_st); 694 1.1 christos 695 1.1 christos /* 696 1.1 christos * The name is prefixed with one or more hyphens. Strip them off 697 1.1 christos * and disable the "default_opt" setting. Use heavy recasting to 698 1.1 christos * strip off the "const" quality of the "default_opt" field. 699 1.1 christos */ 700 1.1 christos while (*(++pz) == '-') ; 701 1.5 christos def_opt = VOIDP(&(opts->specOptIdx.default_opt)); 702 1.1 christos def = *def_opt; 703 1.1 christos *def_opt = NO_EQUIVALENT; 704 1.1 christos res = opt_find_long(opts, pz, o_st); 705 1.1 christos *def_opt = (uint16_t)def; 706 1.1 christos return res; 707 1.1 christos } 708 1.1 christos 709 1.1 christos /* 710 1.1 christos * Note the kind of flag/option marker 711 1.1 christos */ 712 1.1 christos if (*((opts->pzCurOpt)++) != '-') 713 1.1 christos return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */ 714 1.1 christos 715 1.1 christos /* 716 1.1 christos * Special hack for a hyphen by itself 717 1.1 christos */ 718 1.1 christos if (*(opts->pzCurOpt) == NUL) 719 1.1 christos return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */ 720 1.1 christos 721 1.1 christos /* 722 1.1 christos * The current argument is to be processed as an option argument 723 1.1 christos */ 724 1.1 christos opts->curOptIdx++; 725 1.1 christos 726 1.1 christos /* 727 1.1 christos * We have an option marker. 728 1.1 christos * Test the next character for long option indication 729 1.1 christos */ 730 1.1 christos if (opts->pzCurOpt[0] == '-') { 731 1.1 christos if (*++(opts->pzCurOpt) == NUL) 732 1.1 christos /* 733 1.1 christos * NORMAL COMPLETION - NOT this arg, but rest are operands 734 1.1 christos */ 735 1.1 christos return PROBLEM; 736 1.1 christos 737 1.1 christos /* 738 1.1 christos * We do not allow the hyphen to be used as a flag value. 739 1.1 christos * Therefore, if long options are not to be accepted, we punt. 740 1.1 christos */ 741 1.1 christos if ((opts->fOptSet & OPTPROC_LONGOPT) == 0) { 742 1.1 christos fprintf(stderr, zIllOptStr, opts->pzProgPath, opts->pzCurOpt-2); 743 1.1 christos return FAILURE; 744 1.1 christos } 745 1.1 christos 746 1.1 christos return opt_find_long(opts, opts->pzCurOpt, o_st); 747 1.1 christos } 748 1.1 christos 749 1.1 christos /* 750 1.1 christos * If short options are not allowed, then do long 751 1.1 christos * option processing. Otherwise the character must be a 752 1.1 christos * short (i.e. single character) option. 753 1.1 christos */ 754 1.1 christos if ((opts->fOptSet & OPTPROC_SHORTOPT) != 0) 755 1.1 christos return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st); 756 1.1 christos 757 1.1 christos return opt_find_long(opts, opts->pzCurOpt, o_st); 758 1.1 christos } 759 1.1 christos 760 1.1 christos /** @} 761 1.1 christos * 762 1.1 christos * Local Variables: 763 1.1 christos * mode: C 764 1.1 christos * c-file-style: "stroustrup" 765 1.1 christos * indent-tabs-mode: nil 766 1.1 christos * End: 767 1.1 christos * end of autoopts/find.c */ 768