ref: d24b42abd6b43b227fbe108a0c2e67c809435c32
dir: /src/getopt.c/
/* lsx_getopt for SoX
*
* (c) 2011 Doug Cook and SoX contributors
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "sox.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void
LSX_API lsx_getopt_init(
LSX_PARAM_IN int argc, /* Number of arguments in argv */
LSX_PARAM_IN_COUNT(argc) char * const * argv, /* Array of arguments */
LSX_PARAM_IN_Z char const * shortopts, /* Short option characters */
LSX_PARAM_IN_OPT lsx_option_t const * longopts, /* Array of long option descriptors */
LSX_PARAM_IN lsx_getopt_flags_t flags, /* Flags for longonly and opterr */
LSX_PARAM_IN int first, /* First argument to check (usually 1) */
LSX_PARAM_OUT lsx_getopt_t * state) /* State object to initialize */
{
assert(argc >= 0);
assert(argv != NULL);
assert(shortopts);
assert(first >= 0);
assert(first <= argc);
assert(state);
if (state)
{
if (argc < 0 ||
!argv ||
!shortopts ||
first < 0 ||
first > argc)
{
memset(state, 0, sizeof(*state));
}
else
{
state->argc = argc;
state->argv = argv;
state->shortopts =
(shortopts[0] == '+' || shortopts[0] == '-') /* Requesting GNU special behavior? */
? shortopts + 1 /* Ignore request. */
: shortopts; /* No special behavior requested. */
state->longopts = longopts;
state->flags = flags;
state->curpos = NULL;
state->ind = first;
state->opt = '?';
state->arg = NULL;
state->lngind = -1;
}
}
}
static void CheckCurPosEnd(
LSX_PARAM_INOUT lsx_getopt_t * state)
{
if (!state->curpos[0])
{
state->curpos = NULL;
state->ind++;
}
}
int
LSX_API lsx_getopt(
LSX_PARAM_INOUT lsx_getopt_t * state)
{
int oerr;
assert(state);
if (!state)
{
lsx_fail("lsx_getopt called with state=NULL");
return -1;
}
assert(state->argc >= 0);
assert(state->argv != NULL);
assert(state->shortopts);
assert(state->ind >= 0);
assert(state->ind <= state->argc + 1);
oerr = 0 != (state->flags & lsx_getopt_flag_opterr);
state->opt = 0;
state->arg = NULL;
state->lngind = -1;
if (state->argc < 0 ||
!state->argv ||
!state->shortopts ||
state->ind < 0)
{ /* programmer error */
lsx_fail("lsx_getopt called with invalid information");
state->curpos = NULL;
return -1;
}
else if (
state->argc <= state->ind ||
!state->argv[state->ind] ||
state->argv[state->ind][0] != '-' ||
state->argv[state->ind][1] == '\0')
{ /* return no more options */
state->curpos = NULL;
return -1;
}
else if (state->argv[state->ind][1] == '-' && state->argv[state->ind][2] == '\0')
{ /* skip "--", return no more options. */
state->curpos = NULL;
state->ind++;
return -1;
}
else
{ /* Look for the next option */
char const * current = state->argv[state->ind];
char const * param = current + 1;
if (state->curpos == NULL ||
state->curpos <= param ||
param + strlen(param) <= state->curpos)
{ /* Start parsing a new parameter - check for a long option */
state->curpos = NULL;
if (state->longopts &&
(param[0] == '-' || (state->flags & lsx_getopt_flag_longonly)))
{
size_t nameLen;
int doubleDash = param[0] == '-';
if (doubleDash)
{
param++;
}
for (nameLen = 0; param[nameLen] && param[nameLen] != '='; nameLen++)
{}
/* For single-dash, you have to specify at least two letters in the name. */
if (doubleDash || nameLen >= 2)
{
lsx_option_t const * pCur;
lsx_option_t const * pMatch = NULL;
int matches = 0;
for (pCur = state->longopts; pCur->name; pCur++)
{
if (0 == strncmp(pCur->name, param, nameLen))
{ /* Prefix match. */
matches++;
pMatch = pCur;
if (nameLen == strlen(pCur->name))
{ /* Exact match - no ambiguity, stop search. */
matches = 1;
break;
}
}
}
if (matches == 1)
{ /* Matched. */
state->ind++;
if (param[nameLen])
{ /* --name=value */
if (pMatch->has_arg)
{ /* Required or optional arg - done. */
state->arg = param + nameLen + 1;
}
else
{ /* No arg expected. */
if (oerr)
{
lsx_warn("`%s' did not expect an argument from `%s'",
pMatch->name,
current);
}
return '?';
}
}
else if (pMatch->has_arg == lsx_option_arg_required)
{ /* Arg required. */
state->arg = state->argv[state->ind];
state->ind++;
if (state->ind > state->argc)
{
if (oerr)
{
lsx_warn("`%s' requires an argument from `%s'",
pMatch->name,
current);
}
return state->shortopts[0] == ':' ? ':' : '?'; /* Missing required value. */
}
}
state->lngind = pMatch - state->longopts;
if (pMatch->flag)
{
*pMatch->flag = pMatch->val;
return 0;
}
else
{
return pMatch->val;
}
}
else if (matches == 0 && doubleDash)
{ /* No match */
if (oerr)
{
lsx_warn("parameter not recognized from `%s'", current);
}
state->ind++;
return '?';
}
else if (matches > 1)
{ /* Ambiguous. */
if (oerr)
{
lsx_warn("parameter `%s' is ambiguous:", current);
for (pCur = state->longopts; pCur->name; pCur++)
{
if (0 == strncmp(pCur->name, param, nameLen))
{
lsx_warn("parameter `%s' could be `--%s'", current, pCur->name);
}
}
}
state->ind++;
return '?';
}
}
}
state->curpos = param;
}
state->opt = state->curpos[0];
if (state->opt == ':')
{ /* ':' is never a valid short option character */
if (oerr)
{
lsx_warn("option `%c' not recognized", state->opt);
}
state->curpos++;
CheckCurPosEnd(state);
return '?'; /* unrecognized option */
}
else
{ /* Short option needs to be matched from option list */
char const * pShortopt = strchr(state->shortopts, state->opt);
state->curpos++;
if (!pShortopt)
{ /* unrecognized option */
if (oerr)
{
lsx_warn("option `%c' not recognized", state->opt);
}
CheckCurPosEnd(state);
return '?';
}
else if (pShortopt[1] == ':' && state->curpos[0])
{ /* Return the rest of the parameter as the option's value */
state->arg = state->curpos;
state->curpos = NULL;
state->ind++;
return state->opt;
}
else if (pShortopt[1] == ':' && pShortopt[2] != ':')
{ /* Option requires a value */
state->curpos = NULL;
state->ind++;
state->arg = state->argv[state->ind];
state->ind++;
if (state->ind <= state->argc)
{ /* A value was present, so we're good. */
return state->opt;
}
else
{ /* Missing required value. */
if (oerr)
{
lsx_warn("option `%c' requires an argument",
state->opt);
}
return state->shortopts[0] == ':' ? ':' : '?';
}
}
else
{ /* Option without a value. */
CheckCurPosEnd(state);
return state->opt;
}
}
}
}
#ifdef TEST_GETOPT
#include <stdio.h>
int main(int argc, char const * argv[])
{
static int help = 0;
static lsx_option_t longopts[] =
{
{"a11", 0, 0, 101},
{"a12", 0, 0, 102},
{"a122", 0, 0, 103},
{"rarg", 1, 0, 104},
{"oarg", 2, 0, 105},
{"help", 0, &help, 106},
{0}
};
int ch;
lsx_getopt_t state;
lsx_getopt_init(argc, argv, "abc:d:v::0123456789", longopts, sox_true, 1, &state);
while (-1 != (ch = lsx_getopt(&state)))
{
printf(
"H=%d ch=%d, ind=%d opt=%d lng=%d arg=%s\n",
help,
ch,
state.ind,
state.opt,
state.lngind,
state.arg ? state.arg : "NULL");
}
return 0;
}
#endif /* TEST_GETOPT */