static int
parse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz);
static void
opt_ambiguities(tOptions * opts, char const * name, int nm_len);
static int
opt_match_ct(tOptions * opts, char const * name, int nm_len,
int * ixp, bool * disable);
static tSuccess
opt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st);
static tSuccess
opt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st);
static tSuccess
opt_ambiguous(tOptions * opts, char const * name, int match_ct);
static tSuccess
get_opt_arg_must(tOptions * opts, tOptState * o_st);
static tSuccess
get_opt_arg_may(tOptions * pOpts, tOptState * o_st);
static tSuccess
get_opt_arg_none(tOptions * pOpts, tOptState * o_st);
static int
parse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz)
{
int res = 0;
char const * p = *nm_pp;
*arg_pp = NULL;
for (;;) {
switch (*(p++)) {
case NUL: return res;
case '=':
memcpy(buf, *nm_pp, (size_t)res);
buf[res] = NUL;
*nm_pp = buf;
*arg_pp = VOIDP(p);
return res;
default:
if (++res >= (int)bufsz)
return -1;
}
}
}
static void
opt_ambiguities(tOptions * opts, char const * name, int nm_len)
{
char const * const hyph =
NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER;
tOptDesc * pOD = opts->pOptDesc;
int idx = 0;
fputs(zambig_list_msg, stderr);
do {
if (pOD->pz_Name == NULL)
continue;
if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0)
fprintf(stderr, zambig_file, hyph, pOD->pz_Name);
else if ( (pOD->pz_DisableName != NULL)
&& (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
)
fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName);
} while (pOD++, (++idx < opts->optCt));
}
static int
opt_match_ct(tOptions * opts, char const * name, int nm_len,
int * ixp, bool * disable)
{
int matchCt = 0;
int idx = 0;
int idxLim = opts->optCt;
tOptDesc * pOD = opts->pOptDesc;
do {
if (pOD->pz_Name == NULL)
continue;
if ( SKIP_OPT(pOD)
&& (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT)))
continue;
if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) {
if (pOD->pz_Name[ nm_len ] == NUL) {
*ixp = idx;
return 1;
}
}
else if ( (pOD->pz_DisableName != NULL)
&& (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
) {
*disable = true;
if (pOD->pz_DisableName[ nm_len ] == NUL) {
*ixp = idx;
return 1;
}
}
else
continue;
*ixp = idx;
++matchCt;
} while (pOD++, (++idx < idxLim));
return matchCt;
}
static tSuccess
opt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st)
{
tOptDesc * pOD = opts->pOptDesc + idx;
if (SKIP_OPT(pOD)) {
if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
return FAILURE;
fprintf(stderr, zDisabledErr, opts->pzProgName, pOD->pz_Name);
if (pOD->pzText != NULL)
fprintf(stderr, SET_OFF_FMT, pOD->pzText);
fputc(NL, stderr);
(*opts->pUsageProc)(opts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
if (disable)
st->flags |= OPTST_DISABLED;
st->pOD = pOD;
st->pzOptArg = arg;
st->optType = TOPT_LONG;
return SUCCESS;
}
static tSuccess
opt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st)
{
if ( (arg == NULL)
&& NAMED_OPTS(opts)
&& (opts->specOptIdx.default_opt != NO_EQUIVALENT)) {
st->pOD = opts->pOptDesc + opts->specOptIdx.default_opt;
st->pzOptArg = name;
st->optType = TOPT_DEFAULT;
return SUCCESS;
}
if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
fprintf(stderr, zIllOptStr, opts->pzProgPath, name);
(*opts->pUsageProc)(opts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
return FAILURE;
}
static tSuccess
opt_ambiguous(tOptions * opts, char const * name, int match_ct)
{
if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
fprintf(stderr, zambig_opt_fmt, opts->pzProgPath, name, match_ct);
if (match_ct <= 4)
opt_ambiguities(opts, name, (int)strlen(name));
(*opts->pUsageProc)(opts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
return FAILURE;
}
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);
_exit(EXIT_FAILURE);
}
if (pOpts->fOptSet & OPTPROC_IMMEDIATE) {
if (DO_IMMEDIATELY(opt_st.flags))
(void)handle_opt(pOpts, &opt_st);
} else {
if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags))
(void)handle_opt(pOpts, &opt_st);
}
}
LOCAL tSuccess
opt_find_long(tOptions * opts, char const * opt_name, tOptState * state)
{
char name_buf[128];
char * opt_arg;
int nm_len = parse_opt(&opt_name, &opt_arg, name_buf, sizeof(name_buf));
int idx = 0;
bool disable = false;
int ct;
if (nm_len <= 1) {
if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
return FAILURE;
fprintf(stderr, zInvalOptName, opts->pzProgName, opt_name);
(*opts->pUsageProc)(opts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
ct = opt_match_ct(opts, opt_name, nm_len, &idx, &disable);
switch (ct) {
case 1: return opt_set(opts, opt_arg, idx, disable, state);
case 0: return opt_unknown(opts, opt_name, opt_arg, state);
default: return opt_ambiguous(opts, opt_name, ct);
}
}
LOCAL tSuccess
opt_find_short(tOptions * pOpts, uint_t optValue, tOptState * pOptState)
{
tOptDesc * pRes = pOpts->pOptDesc;
int ct = pOpts->optCt;
do {
if (optValue != pRes->optValue)
continue;
if (SKIP_OPT(pRes)) {
if ( (pRes->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
&& (pRes->pz_Name != NULL)) {
if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
return FAILURE;
fprintf(stderr, zDisabledErr, pOpts->pzProgPath, pRes->pz_Name);
if (pRes->pzText != NULL)
fprintf(stderr, SET_OFF_FMT, pRes->pzText);
fputc(NL, stderr);
(*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
goto short_opt_error;
}
pOptState->pOD = pRes;
pOptState->optType = TOPT_SHORT;
return SUCCESS;
} while (pRes++, --ct > 0);
if ( IS_DEC_DIGIT_CHAR(optValue)
&& (pOpts->specOptIdx.number_option != NO_EQUIVALENT) ) {
pOptState->pOD = \
pRes = pOpts->pOptDesc + pOpts->specOptIdx.number_option;
(pOpts->pzCurOpt)--;
pOptState->optType = TOPT_SHORT;
return SUCCESS;
}
short_opt_error:
if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) {
fprintf(stderr, zIllOptChr, pOpts->pzProgPath, optValue);
(*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
return FAILURE;
}
static tSuccess
get_opt_arg_must(tOptions * opts, tOptState * o_st)
{
switch (o_st->optType) {
case TOPT_SHORT:
if (*++(opts->pzCurOpt) == NUL)
opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx++ ];
o_st->pzOptArg = opts->pzCurOpt;
break;
case TOPT_LONG:
if (o_st->pzOptArg == NULL)
o_st->pzOptArg = opts->origArgVect[ opts->curOptIdx++ ];
break;
default:
#ifdef DEBUG
fputs("AutoOpts lib error: option type not selected\n", stderr);
option_exits(EXIT_FAILURE);
#endif
case TOPT_DEFAULT:
break;
}
if (opts->curOptIdx > opts->origArgCt) {
fprintf(stderr, zMisArg, opts->pzProgPath, o_st->pOD->pz_Name);
return FAILURE;
}
opts->pzCurOpt = NULL;
return SUCCESS;
}
static tSuccess
get_opt_arg_may(tOptions * pOpts, tOptState * o_st)
{
switch (o_st->optType) {
case TOPT_SHORT:
if (*++pOpts->pzCurOpt != NUL)
o_st->pzOptArg = pOpts->pzCurOpt;
else {
char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
if ((pzLA == NULL) || (*pzLA == '-'))
o_st->pzOptArg = NULL;
else {
pOpts->curOptIdx++;
o_st->pzOptArg = pzLA;
}
}
break;
case TOPT_LONG:
if ( (o_st->pzOptArg == NULL)
&& (! NAMED_OPTS(pOpts))) {
char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
if ((pzLA == NULL) || (*pzLA == '-'))
o_st->pzOptArg = NULL;
else {
pOpts->curOptIdx++;
o_st->pzOptArg = pzLA;
}
}
break;
default:
case TOPT_DEFAULT:
ao_bug(zbad_default_msg);
}
pOpts->pzCurOpt = NULL;
return SUCCESS;
}
static tSuccess
get_opt_arg_none(tOptions * pOpts, tOptState * o_st)
{
if (o_st->optType == TOPT_SHORT)
(pOpts->pzCurOpt)++;
else if (o_st->pzOptArg != NULL) {
fprintf(stderr, zNoArg, pOpts->pzProgPath, o_st->pOD->pz_Name);
return FAILURE;
}
else
pOpts->pzCurOpt = NULL;
return SUCCESS;
}
LOCAL tSuccess
get_opt_arg(tOptions * opts, tOptState * o_st)
{
o_st->flags |= (o_st->pOD->fOptState & OPTST_PERSISTENT_MASK);
if ( ((o_st->flags & OPTST_DISABLED) != 0)
|| (OPTST_GET_ARGTYPE(o_st->flags) == OPARG_TYPE_NONE))
return get_opt_arg_none(opts, o_st);
if (o_st->flags & OPTST_ARG_OPTIONAL)
return get_opt_arg_may( opts, o_st);
return get_opt_arg_must(opts, o_st);
}
LOCAL tSuccess
find_opt(tOptions * opts, tOptState * o_st)
{
if ((opts->pzCurOpt != NULL) && (*opts->pzCurOpt != NUL))
return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
if (opts->curOptIdx >= opts->origArgCt)
return PROBLEM;
opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx ];
if (NAMED_OPTS(opts)) {
char * pz = opts->pzCurOpt;
int def;
tSuccess res;
uint16_t * def_opt;
opts->curOptIdx++;
if (*pz != '-')
return opt_find_long(opts, pz, o_st);
while (*(++pz) == '-') ;
def_opt = VOIDP(&(opts->specOptIdx.default_opt));
def = *def_opt;
*def_opt = NO_EQUIVALENT;
res = opt_find_long(opts, pz, o_st);
*def_opt = (uint16_t)def;
return res;
}
if (*((opts->pzCurOpt)++) != '-')
return PROBLEM;
if (*(opts->pzCurOpt) == NUL)
return PROBLEM;
opts->curOptIdx++;
if (opts->pzCurOpt[0] == '-') {
if (*++(opts->pzCurOpt) == NUL)
return PROBLEM;
if ((opts->fOptSet & OPTPROC_LONGOPT) == 0) {
fprintf(stderr, zIllOptStr, opts->pzProgPath, opts->pzCurOpt-2);
return FAILURE;
}
return opt_find_long(opts, opts->pzCurOpt, o_st);
}
if ((opts->fOptSet & OPTPROC_SHORTOPT) != 0)
return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
return opt_find_long(opts, opts->pzCurOpt, o_st);
}