/** * Process all the options from our current position onward. (This allows * interspersed options and arguments for the few non-standard programs that * require it.) Thus, do not rewind option indexes because some programs * choose to re-invoke after a non-option. */ LOCAL tSuccess doRegularOpts(tOptions* pOpts) { for (;;) { tOptState optState = OPTSTATE_INITIALIZER(DEFINED); switch (nextOption(pOpts, &optState)) { case FAILURE: goto failed_option; case PROBLEM: return SUCCESS; /* no more args */ case SUCCESS: break; } /* * IF this is an immediate action option, * THEN skip it (unless we are supposed to do it a second time). */ if (! DO_NORMALLY(optState.flags)) { if (! DO_SECOND_TIME(optState.flags)) continue; optState.pOD->optOccCt--; /* don't count this repetition */ } if (! SUCCESSFUL(handle_opt(pOpts, &optState))) break; } failed_option:; if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); return FAILURE; }
/*=export_func optionResetOpt * private: * * what: Reset the value of an option * arg: + tOptions* + pOpts + program options descriptor + * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + * * doc: * This code will cause another option to be reset to its initial state. * For example, --reset=foo will cause the --foo option to be reset. =*/ void optionResetOpt( tOptions* pOpts, tOptDesc* pOD ) { static ag_bool reset_active = AG_FALSE; tOptState opt_state = OPTSTATE_INITIALIZER(DEFINED); char const * pzArg = pOD->optArg.argString; tSuccess succ; if (reset_active) return; if ( (! HAS_originalOptArgArray(pOpts)) || (pOpts->originalOptArgCookie == NULL)) { fputs(zResetNotConfig, stderr); _exit(EX_SOFTWARE); } if ((pzArg == NULL) || (*pzArg == NUL)) { fputs(zNoResetArg, stderr); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } reset_active = AG_TRUE; if (pzArg[1] == NUL) { if (*pzArg == '*') { optionResetEverything(pOpts); reset_active = AG_FALSE; return; } succ = shortOptionFind(pOpts, (tAoUC)*pzArg, &opt_state); if (! SUCCESSFUL(succ)) { fprintf(stderr, zIllOptChr, pOpts->pzProgPath, *pzArg); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } } else { succ = longOptionFind(pOpts, (char *)pzArg, &opt_state); if (! SUCCESSFUL(succ)) { fprintf(stderr, zIllOptStr, pOpts->pzProgPath, pzArg); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } } /* * We've found the indicated option. Turn off all non-persistent * flags because we're forcing the option back to its initialized state. * Call any callout procedure to handle whatever it needs to. * Finally, clear the reset flag, too. */ optionReset(pOpts, opt_state.pOD); reset_active = AG_FALSE; }
/** * scan the command line for immediate action options. * This is only called the first time through. */ LOCAL tSuccess doImmediateOpts(tOptions* pOpts) { pOpts->curOptIdx = 1; /* start by skipping program name */ pOpts->pzCurOpt = NULL; /* * Examine all the options from the start. We process any options that * are marked for immediate processing. */ for (;;) { tOptState optState = OPTSTATE_INITIALIZER(PRESET); switch (nextOption(pOpts, &optState)) { case FAILURE: goto failed_option; case PROBLEM: return SUCCESS; /* no more args */ case SUCCESS: break; } /* * IF this is an immediate-attribute option, then do it. */ if (! DO_IMMEDIATELY(optState.flags)) continue; if (! SUCCESSFUL(handle_opt(pOpts, &optState))) break; } failed_option:; if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); return FAILURE; }
/*=export_func optionLoadLine * * what: process a string for an option name and value * * arg: tOptions*, pOpts, program options descriptor * arg: char const*, pzLine, NUL-terminated text * * doc: * * This is a client program callable routine for setting options from, for * example, the contents of a file that they read in. Only one option may * appear in the text. It will be treated as a normal (non-preset) option. * * When passed a pointer to the option struct and a string, it will find * the option named by the first token on the string and set the option * argument to the remainder of the string. The caller must NUL terminate * the string. Any embedded new lines will be included in the option * argument. If the input looks like one or more quoted strings, then the * input will be "cooked". The "cooking" is identical to the string * formation used in AutoGen definition files (@pxref{basic expression}), * except that you may not use backquotes. * * err: Invalid options are silently ignored. Invalid option arguments * will cause a warning to print, but the function should return. =*/ void optionLoadLine(tOptions * pOpts, char const * pzLine) { tOptState st = OPTSTATE_INITIALIZER(SET); char* pz; AGDUPSTR(pz, pzLine, "user option line"); loadOptionLine(pOpts, &st, pz, DIRECTION_PROCESS, OPTION_LOAD_COOKED); AGFREE(pz); }
/*=export_func optionLoadLine * * what: process a string for an option name and value * * arg: tOptions *, opts, program options descriptor * arg: char const *, line, NUL-terminated text * * doc: * * This is a client program callable routine for setting options from, for * example, the contents of a file that they read in. Only one option may * appear in the text. It will be treated as a normal (non-preset) option. * * When passed a pointer to the option struct and a string, it will find * the option named by the first token on the string and set the option * argument to the remainder of the string. The caller must NUL terminate * the string. The caller need not skip over any introductory hyphens. * Any embedded new lines will be included in the option * argument. If the input looks like one or more quoted strings, then the * input will be "cooked". The "cooking" is identical to the string * formation used in AutoGen definition files (@pxref{basic expression}), * except that you may not use backquotes. * * err: Invalid options are silently ignored. Invalid option arguments * will cause a warning to print, but the function should return. =*/ void optionLoadLine(tOptions * opts, char const * line) { tOptState st = OPTSTATE_INITIALIZER(SET); char * pz; proc_state_mask_t sv_flags = opts->fOptSet; opts->fOptSet &= ~OPTPROC_ERRSTOP; AGDUPSTR(pz, line, "opt line"); load_opt_line(opts, &st, pz, DIRECTION_CALLED, OPTION_LOAD_COOKED); AGFREE(pz); opts->fOptSet = sv_flags; }
/*=export_func optionVendorOption * private: * * what: Process a vendor option * arg: + tOptions * + pOpts + program options descriptor + * arg: + tOptDesc * + pOptDesc + the descriptor for this arg + * * doc: * For POSIX specified utilities, the options are constrained to the options, * @xref{config attributes, Program Configuration}. AutoOpts clients should * never specify this directly. It gets referenced when the option * definitions contain a "vendor-opt" attribute. =*/ void optionVendorOption(tOptions * pOpts, tOptDesc * pOD) { tOptState opt_st = OPTSTATE_INITIALIZER(PRESET); char const * vopt_str = pOD->optArg.argString; if (pOpts <= OPTPROC_EMIT_LIMIT) return; if ((pOD->fOptState & OPTST_RESET) != 0) return; if ((pOD->fOptState & OPTPROC_IMMEDIATE) == 0) opt_st.flags = OPTST_DEFINED; if ( ((pOpts->fOptSet & OPTPROC_VENDOR_OPT) == 0) || ! SUCCESSFUL(opt_find_long(pOpts, vopt_str, &opt_st)) || ! SUCCESSFUL(get_opt_arg(pOpts, &opt_st)) ) { fprintf(stderr, zIllVendOptStr, pOpts->pzProgName, vopt_str); (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); /* NOTREACHED */ _exit(EXIT_FAILURE); /* to be certain */ } /* * See if we are in immediate handling state. */ if (pOpts->fOptSet & OPTPROC_IMMEDIATE) { /* * See if the enclosed option is okay with that state. */ if (DO_IMMEDIATELY(opt_st.flags)) (void)handle_opt(pOpts, &opt_st); } else { /* * non-immediate direction. * See if the enclosed option is okay with that state. */ if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags)) (void)handle_opt(pOpts, &opt_st); } }
/** * scan the command line for immediate action options. * This is only called the first time through. * While this procedure is active, the OPTPROC_IMMEDIATE is true. * * @param pOpts program options descriptor * @returns SUCCESS or FAILURE */ LOCAL tSuccess immediate_opts(tOptions * opts) { tSuccess res; opts->fOptSet |= OPTPROC_IMMEDIATE; opts->curOptIdx = 1; /* start by skipping program name */ opts->pzCurOpt = NULL; /* * Examine all the options from the start. We process any options that * are marked for immediate processing. */ for (;;) { tOptState opt_st = OPTSTATE_INITIALIZER(PRESET); res = next_opt(opts, &opt_st); switch (res) { case FAILURE: goto failed_option; case PROBLEM: res = SUCCESS; goto leave; case SUCCESS: break; } /* * IF this is an immediate-attribute option, then do it. */ if (! DO_IMMEDIATELY(opt_st.flags)) continue; if (! SUCCESSFUL(handle_opt(opts, &opt_st))) break; } failed_option:; if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) (*opts->pUsageProc)(opts, EXIT_FAILURE); leave: opts->fOptSet &= ~OPTPROC_IMMEDIATE; return res; }
/** * Load a file containing presetting information (a configuration file). */ static void file_preset(tOptions * opts, char const * fname, int dir) { tmap_info_t cfgfile; tOptState optst = OPTSTATE_INITIALIZER(PRESET); opt_state_mask_t st_flags = optst.flags; char * ftext = text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile); if (TEXT_MMAP_FAILED_ADDR(ftext)) return; if (dir == DIRECTION_CALLED) { st_flags = OPTST_DEFINED; dir = DIRECTION_PROCESS; } /* * IF this is called via "optionProcess", then we are presetting. * This is the default and the PRESETTING bit will be set. * If this is called via "optionFileLoad", then the bit is not set * and we consider stuff set herein to be "set" by the client program. */ if ((opts->fOptSet & OPTPROC_PRESETTING) == 0) st_flags = OPTST_SET; do { optst.flags = st_flags; ftext = SPN_WHITESPACE_CHARS(ftext); if (IS_VAR_FIRST_CHAR(*ftext)) { ftext = handle_cfg(opts, &optst, ftext, dir); } else switch (*ftext) { case '<': if (IS_VAR_FIRST_CHAR(ftext[1])) ftext = handle_struct(opts, &optst, ftext, dir); else switch (ftext[1]) { case '?': ftext = handle_directive(opts, ftext); break; case '!': ftext = handle_comment(ftext); break; case '/': ftext = strchr(ftext + 2, '>'); if (ftext++ != NULL) break; default: ftext = NULL; } if (ftext == NULL) goto all_done; break; case '[': ftext = handle_section(opts, ftext); break; case '#': ftext = strchr(ftext + 1, NL); break; default: goto all_done; /* invalid format */ } } while (ftext != NULL); all_done: text_munmap(&cfgfile); }
/* * If the program wants sorted options (separated operands and options), * then this routine will to the trick. */ LOCAL void optionSort( tOptions* pOpts ) { char** ppzOpts; char** ppzOpds; int optsIdx = 0; int opdsIdx = 0; tOptState os = OPTSTATE_INITIALIZER(DEFINED); /* * Disable for POSIX conformance, or if there are no operands. */ if ( (getenv( "POSIXLY_CORRECT" ) != NULL) || NAMED_OPTS(pOpts)) return; /* * Make sure we can allocate two full-sized arg vectors. */ ppzOpts = malloc( pOpts->origArgCt * sizeof( char* )); if (ppzOpts == NULL) goto exit_no_mem; ppzOpds = malloc( pOpts->origArgCt * sizeof( char* )); if (ppzOpds == NULL) { free( ppzOpts ); goto exit_no_mem; } pOpts->curOptIdx = 1; pOpts->pzCurOpt = NULL; /* * Now, process all the options from our current position onward. * (This allows interspersed options and arguments for the few * non-standard programs that require it.) */ for (;;) { char* pzArg; tSuccess res; /* * If we're out of arguments, we're done. Join the option and * operand lists into the original argument vector. */ if (pOpts->curOptIdx >= pOpts->origArgCt) { errno = 0; goto joinLists; } pzArg = pOpts->origArgVect[ pOpts->curOptIdx ]; if (*pzArg != '-') { ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ]; continue; } switch (pzArg[1]) { case NUL: /* * A single hyphen is an operand. */ ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ]; continue; case '-': /* * Two consecutive hypens. Put them on the options list and then * _always_ force the remainder of the arguments to be operands. */ if (pzArg[2] == NUL) { ppzOpts[ optsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ]; goto restOperands; } res = longOptionFind( pOpts, pzArg+2, &os ); break; default: /* * If short options are not allowed, then do long * option processing. Otherwise the character must be a * short (i.e. single character) option. */ if ((pOpts->fOptSet & OPTPROC_SHORTOPT) == 0) { res = longOptionFind( pOpts, pzArg+1, &os ); } else { res = shortOptionFind( pOpts, (tAoUC)pzArg[1], &os ); } break; } if (FAILED( res )) { errno = EINVAL; goto freeTemps; } /* * We've found an option. Add the argument to the option list. * Next, we have to see if we need to pull another argument to be * used as the option argument. */ ppzOpts[ optsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ]; if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) { /* * No option argument. If we have a short option here, * then scan for short options until we get to the end * of the argument string. */ if ( (os.optType == TOPT_SHORT) && FAILED( checkShortOpts( pOpts, pzArg+2, &os, ppzOpts, &optsIdx )) ) { errno = EINVAL; goto freeTemps; } } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) { switch (mayHandleArg( pOpts, pzArg+2, &os, ppzOpts, &optsIdx )) { case FAILURE: errno = EIO; goto freeTemps; case PROBLEM: errno = 0; goto joinLists; } } else { switch (mustHandleArg( pOpts, pzArg+2, &os, ppzOpts, &optsIdx )) { case PROBLEM: case FAILURE: errno = EIO; goto freeTemps; } } } /* for (;;) */ restOperands: while (pOpts->curOptIdx < pOpts->origArgCt) ppzOpds[ opdsIdx++ ] = pOpts->origArgVect[ (pOpts->curOptIdx)++ ]; joinLists: if (optsIdx > 0) memcpy( pOpts->origArgVect + 1, ppzOpts, optsIdx * sizeof( char* )); if (opdsIdx > 0) memcpy( pOpts->origArgVect + 1 + optsIdx, ppzOpds, opdsIdx * sizeof( char* )); freeTemps: free( ppzOpts ); free( ppzOpds ); return; exit_no_mem: errno = ENOMEM; return; }
/* filePreset * * Load a file containing presetting information (a configuration file). */ static void filePreset( tOptions* pOpts, char const* pzFileName, int direction ) { tmap_info_t cfgfile; tOptState st = OPTSTATE_INITIALIZER(PRESET); char* pzFileText = text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile ); if (TEXT_MMAP_FAILED_ADDR(pzFileText)) return; if (direction == DIRECTION_CALLED) { st.flags = OPTST_DEFINED; direction = DIRECTION_PROCESS; } /* * IF this is called via "optionProcess", then we are presetting. * This is the default and the PRESETTING bit will be set. * If this is called via "optionFileLoad", then the bit is not set * and we consider stuff set herein to be "set" by the client program. */ if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0) st.flags = OPTST_SET; do { while (isspace( (int)*pzFileText )) pzFileText++; if (isalpha( (int)*pzFileText )) { pzFileText = handleConfig( pOpts, &st, pzFileText, direction ); } else switch (*pzFileText) { case '<': if (isalpha( (int)pzFileText[1] )) pzFileText = handleStructure(pOpts, &st, pzFileText, direction); else switch (pzFileText[1]) { case '?': pzFileText = handleDirective( pOpts, pzFileText ); break; case '!': pzFileText = handleComment( pzFileText ); break; case '/': pzFileText = strchr( pzFileText+2, '>' ); if (pzFileText++ != NULL) break; default: goto all_done; } break; case '[': pzFileText = handleProgramSection( pOpts, pzFileText ); break; case '#': pzFileText = strchr( pzFileText+1, '\n' ); break; default: goto all_done; /* invalid format */ } } while (pzFileText != NULL); all_done: text_munmap( &cfgfile ); }
/*=export_func optionResetOpt * private: * * what: Reset the value of an option * arg: + tOptions* + pOpts + program options descriptor + * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + * * doc: * This code will cause another option to be reset to its initial state. * For example, --reset=foo will cause the --foo option to be reset. =*/ void optionResetOpt(tOptions * pOpts, tOptDesc * pOD) { static bool reset_active = false; tOptState opt_state = OPTSTATE_INITIALIZER(DEFINED); char const * pzArg = pOD->optArg.argString; tSuccess succ; if (pOpts <= OPTPROC_EMIT_LIMIT) return; if (reset_active) return; if ( (! HAS_originalOptArgArray(pOpts)) || (pOpts->originalOptArgCookie == NULL)) ao_bug(zno_reset); if ((pzArg == NULL) || (*pzArg == NUL)) { fprintf(stderr, zreset_arg, pOpts->pzProgName, pOD->pz_Name); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } reset_active = true; if (pzArg[1] == NUL) { if (*pzArg == '*') { optionResetEverything(pOpts); reset_active = false; return; } succ = opt_find_short(pOpts, (uint8_t)*pzArg, &opt_state); if (! SUCCESSFUL(succ)) { fprintf(stderr, zIllOptChr, pOpts->pzProgPath, *pzArg); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } } else { succ = opt_find_long(pOpts, (char *)pzArg, &opt_state); if (! SUCCESSFUL(succ)) { fprintf(stderr, zIllOptStr, pOpts->pzProgPath, pzArg); pOpts->pUsageProc(pOpts, EXIT_FAILURE); /* NOTREACHED */ assert(0 == 1); } } /* * We've found the indicated option. Turn off all non-persistent * flags because we're forcing the option back to its initialized state. * Call any callout procedure to handle whatever it needs to. * Finally, clear the reset flag, too. */ optionReset(pOpts, opt_state.pOD); reset_active = false; }