/* 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> voidlsx_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++; }} intlsx_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 */
/* 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_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_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 */