1
0
mirror of https://github.com/ScrelliCopter/tmx2gba.git synced 2025-02-21 03:29:25 +11:00
Files
tmx2gba/ext/ultragetopt/ultragetopt.c

827 lines
24 KiB
C

/* Ultra-Getopt - A replacement for getopt() with support for many common
* extensions, MS-DOS formatted option strings, and much more.
*
* To use this library as a replacement for vendor-provided getopt() functions,
* define ULTRAGETOPT_REPLACE_GETOPT and include "ultragetopt.h" after the
* vendor-provided headers for getopt() functions.
*
* Copyright (C) 2007-2011, Kevin Locke <kevin@kevinlocke.name>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include <ctype.h> /* islower() isupper() tolower() toupper() */
#include <stdarg.h>
#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* getenv() */
#include <string.h> /* strcmp(), strncmp(), strchr() */
#if HAVE_STRINGS_H
# include <strings.h> /* strcasecmp(), strncasecmp() */
#endif
#undef ULTRAGETOPT_REPLACE_GETOPT /* Protect against project-wide defines */
#include "ultragetopt.h"
/* Define replacements for missing functions */
#if !HAVE_STRCASECMP && HAVE__STRICMP
# define strcasecmp _stricmp
#endif
#if !HAVE_STRNCASECMP && HAVE__STRNICMP
# define strncasecmp _strnicmp
#endif
#if !HAVE_STRCHR && HAVE_INDEX
# define strchr index
#endif
/* Supported defines:
* ULTRAGETOPT_LIKE_BSD Behave like BSD getopt()
* ULTRAGETOPT_LIKE_DARWIN Behave like Darwin (Mac OS) getopt()
* ULTRAGETOPT_LIKE_GNU Behave like GNU getopt()
* ULTRAGETOPT_LIKE_POSIX Behave like POSIX definition of getopt()
*
* ULTRAGETOPT_ASSIGNSPACE Parse "-o value" as "value" rather than " value"
* Note: Only applicable when argv[x] == "-o value"
* Not for argv[x] == "-o" [x+1] == "value"
* ULTRAGETOPT_DEFAULTOPTOPT Set optopt to this value by default on each
* call to getopt()
* ULTRAGETOPT_HYPHENARG Accept -option -arg as -option with argument
* "-arg" rather than -option missing argument
* Note: A single "-" is always accepted as an
* argument
* ULTRAGETOPT_LONGOPTADJACENT Accept adjacent arguments to long options
* (e.g. --optionarg) based on first longest-match
* ULTRAGETOPT_OPTIONPERMUTE Permute options, do not stop at first non-option
* Behaves like GNU getopt where leading '+' or
* $POSIXLY_CORRECT both stop this @ runtime
* ULTRAGETOPT_SHORTOPTASSIGN Support -o=file syntax for short options
* ULTRAGETOPT_SEPARATEDOPTIONAL Accept separated optional arguments
* Parse -o file as -o with argument file
* ULTRAGETOPT_DOS_DASH Support - and -- options in ultragetopt*_dos()
* ULTRAGETOPT_BSD_ERRORS Print error messages matching BSD getopt
* ULTRAGETOPT_DARWIN_ERRORS Print error messages matching Darwin getopt
* ULTRAGETOPT_GNU_ERRORS Print error messages matching GNU getopt
* ULTRAGETOPT_NO_EATDASHDASH Do not increment optind when argv[optind] is --
* ULTRAGETOPT_NO_OPTIONALARG Do not support GNU "::" optional argument
* Always supported in *_long*()
* ULTRAGETOPT_NO_OPTIONASSIGN Do not support --option=value syntax
*/
#ifdef ULTRAGETOPT_LIKE_POSIX
# define ULTRAGETOPT_NO_OPTIONALARG
# define ULTRAGETOPT_NO_OPTIONASSIGN
# undef ULTRAGETOPT_NO_EATDASHDASH
# undef ULTRAGETOPT_ASSIGNSPACE
# undef ULTRAGETOPT_BSD_ERRORS
# undef ULTRAGETOPT_DARWIN_ERRORS
# undef ULTRAGETOPT_GNU_ERRORS
# undef ULTRAGETOPT_OPTIONPERMUTE
# undef ULTRAGETOPT_SHORTOPTASSIGN
#elif defined(ULTRAGETOPT_LIKE_GNU)
# define ULTRAGETOPT_GNU_ERRORS
# define ULTRAGETOPT_HYPHENARG
# define ULTRAGETOPT_OPTIONPERMUTE
# undef ULTRAGETOPT_ASSIGNSPACE
# undef ULTRAGETOPT_NO_OPTIONALARG
# undef ULTRAGETOPT_NO_OPTIONASSIGN
# undef ULTRAGETOPT_NO_EATDASHDASH
# undef ULTRAGETOPT_SHORTOPTASSIGN
# undef ULTRAGETOPT_SEPARATEOPTIONAL
# undef ULTRAGETOPT_LONGOPTADJACENT
#elif defined(ULTRAGETOPT_LIKE_BSD)
# define ULTRAGETOPT_BSD_ERRORS
# define ULTRAGETOPT_OPTIONPERMUTE
# define ULTRAGETOPT_DEFAULTOPTOPT '?'
# undef ULTRAGETOPT_ASSIGNSPACE
# undef ULTRAGETOPT_NO_OPTIONALARG
# undef ULTRAGETOPT_NO_OPTIONASSIGN
# undef ULTRAGETOPT_NO_EATDASHDASH
# undef ULTRAGETOPT_SHORTOPTASSIGN
# undef ULTRAGETOPT_SEPARATEOPTIONAL
# undef ULTRAGETOPT_LONGOPTADJACENT
#elif defined(ULTRAGETOPT_LIKE_DARWIN)
# define ULTRAGETOPT_DARWIN_ERRORS
# define ULTRAGETOPT_OPTIONPERMUTE
# undef ULTRAGETOPT_ASSIGNSPACE
# undef ULTRAGETOPT_NO_OPTIONALARG
# undef ULTRAGETOPT_NO_OPTIONASSIGN
# undef ULTRAGETOPT_SHORTOPTASSIGN
# undef ULTRAGETOPT_NO_EATDASHDASH
# undef ULTRAGETOPT_SEPARATEOPTIONAL
# undef ULTRAGETOPT_LONGOPTADJACENT
#endif
#ifdef ULTRAGETOPT_NO_OPTIONASSIGN
static const char *const unixassigners = "";
static const char *const dosassigners = ":";
#elif defined(ULTRAGETOPT_OPTIONSPACE)
static const char *const unixassigners = "= ";
static const char *const dosassigners = ":= ";
#else
static const char *const unixassigners = "=";
static const char *const dosassigners = ":=";
#endif
#ifdef ULTRAGETOPT_DOS_DASH
static const char *const unixleaders = "-";
static const char *const dosleaders = "/-";
#else
static const char *const unixleaders = "-";
static const char *const dosleaders = "/";
#endif
/* Flags for all variants of ultragetopt*() */
static const int getoptflags = 0
#ifdef ULTRAGETOPT_SEPARATEDOPTIONAL
| UGO_SEPARATEDOPTIONAL
#endif
#ifdef ULTRAGETOPT_SHORTOPTASSIGN
| UGO_SHORTOPTASSIGN
#endif
#ifdef ULTRAGETOPT_NO_EATDASHDASH
| UGO_NOEATDASHDASH
#endif
#ifdef ULTRAGETOPT_HYPHENARG
| UGO_HYPHENARG
#endif
;
#ifdef ULTRAGETOPT_GNU_ERRORS
static const char *const errorarg =
"%s: option `%.*s' doesn't allow an argument\n";
static const char *const errornoarg =
"%s: option `%.*s' requires an argument \n";
static const char *const erroropt =
"%s: unrecognized option `%.*s'\n";
static const char *const errorargc =
"%s: option `-%c' does not take an argument\n";
static const char *const errornoargc =
"%s: option requires an argument -- `-%c'\n";
static const char *const erroroptc =
"%s: invalid option -- %c\n";
#elif defined(ULTRAGETOPT_BSD_ERRORS)
static const char *const errorarg =
"%s: option doesn't take an argument -- %.*s\n";
static const char *const errornoarg =
"%s: option requires an argument -- %.*s\n";
static const char *const erroropt =
"%s: unknown option -- %.*s\n";
static const char *const errorargc =
"%s: option doesn't take an argument -- %c\n";
static const char *const errornoargc =
"%s: option requires an argument -- %c\n";
static const char *const erroroptc =
"%s: unknown option -- %c\n";
#elif defined(ULTRAGETOPT_DARWIN_ERRORS)
static const char *const errorarg =
"%s: option `%.*s' doesn't allow an argument\n"; /* with -- */
static const char *const errornoarg =
"%s: option `%.*s' requires an argument\n";
static const char *const erroropt =
"%s: unrecognized option `%.*s'\n"; /* with -- */
static const char *const errorargc =
"%s: option doesn't take an argument -- %c\n";
static const char *const errornoargc =
"%s: option requires an argument -- %c\n";
static const char *const erroroptc =
"%s: invalid option -- %c\n";
#else /* POSIX-like */
static const char *const errorarg =
"%s: option does not take an argument -- %.*s\n";
static const char *const errornoarg =
"%s: option requires an argument -- %.*s\n";
static const char *const erroropt =
"%s: illegal option -- %.*s\n";
static const char *const errorargc =
"%s: option does not take an argument -- %c\n";
static const char *const errornoargc =
"%s: option requires an argument -- %c\n";
static const char *const erroroptc =
"%s: illegal option -- %c\n";
#endif
/* Globals to match optarg, optind, opterr, optopt, optreset */
char *ultraoptarg = NULL;
int ultraoptind = 1;
int ultraopterr = 1;
int ultraoptreset = 0;
#ifdef ULTRAGETOPT_DEFAULTOPTOPT
int ultraoptopt = ULTRAGETOPT_DEFAULTOPTOPT -0;
#else
int ultraoptopt = 0;
#endif
static int ultraoptnum = 0; /* How many options of the current multi-option
argument have been processed? (e.g. -vvv) */
/* Add format error checking for gcc versions that support it */
#if defined(__GNUC__) && __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR > 6)
static void print_error(int flags, const char *template, ...)
__attribute__ ((format (printf, 2, 3)));
#endif
/* Print errors only if not suppressed */
static void print_error(int flags, const char *template, ...)
{
va_list ap;
va_start(ap, template);
if (ultraopterr != 0 && !(flags & UGO_NOPRINTERR))
vfprintf(stderr, template, ap);
va_end(ap);
}
/* Check if an argument string looks like an option string */
static inline int like_option(const char *arg, const char *optleaders)
{
return arg != NULL &&
arg[0] != '\0' && /* >= 2 characters long */
arg[1] != '\0' &&
strchr(optleaders, arg[0]) && /* Starts with optleader */
(arg[2] != '\0' || arg[0] != arg[1]); /* Not -- */
}
/* Check if an argument string looks like the option terminator string */
static inline int like_optterm(const char *arg, const char *optleaders)
{
return arg != NULL &&
arg[0] != '\0' &&
arg[1] != '\0' &&
arg[2] == '\0' &&
arg[0] == arg[1] &&
strchr(optleaders, arg[0]);
}
/* Check if an argument string looks like an option argument string */
static inline int like_optarg(const char *arg, const char *optleaders,
int allow_option)
{
return arg != NULL &&
(allow_option ||
(!like_option(arg, optleaders) && !like_optterm(arg, optleaders)));
}
/* If argv[curopt] matches a long option, return the index of that option
* Otherwise, return -1
* If it has an adjacent argument, return pointer to it in longarg, else NULL
*/
static int match_longopt(int curopt, char *const argv[],
const struct option *longopts, const char *assigners,
const char *optleaders, int flags, char **longarg)
{
size_t alen, optnamelen = 0;
char *optname;
char *temp;
int i;
int (*optncmp)(const char *s1, const char *s2, size_t n);
if (longarg == NULL)
longarg = &temp;
*longarg = NULL;
if (flags & UGO_CASEINSENSITIVE)
optncmp = strncasecmp;
else
optncmp = strncmp;
if (longopts == NULL)
return -1;
if (!like_option(argv[curopt], optleaders))
return -1;
if (flags & UGO_SINGLELEADERONLY) {
optname = argv[curopt]+1;
} else if (!strchr(optleaders, argv[curopt][1])) {
/* Possible short option */
if (flags & UGO_SINGLELEADERLONG)
optname = argv[curopt]+1;
else
return -1;
} else {
optname = argv[curopt]+2;
}
/* Do first longest-match search if requested */
if (flags & UGO_LONGOPTADJACENT) {
size_t matchlen = 0;
int matchind = -1;
for (i=0; longopts[i].name != NULL; i++) {
size_t longnamelen = strlen(longopts[i].name);
if (longnamelen > matchlen
&& optncmp(optname, longopts[i].name, longnamelen) == 0) {
matchind = i;
matchlen = longnamelen;
}
}
if (matchlen > 0) {
/* See if our match has an adjacent argument */
if (optname[matchlen] != '\0') {
/* Strip assigner character if present */
if (strchr(assigners, optname[matchlen]))
*longarg = optname+matchlen+1;
else
*longarg = optname+matchlen;
}
return matchind;
}
return -1;
}
/* Check for assigner in the option */
alen = strlen(assigners);
for (i=0; (unsigned)i < alen; i++) {
char *asn = strchr(optname, assigners[i]);
if (asn != NULL) {
optnamelen = asn - optname;
*longarg = asn+1;
break;
}
}
if (optnamelen == 0)
optnamelen = strlen(optname);
for (i=0; longopts[i].name != NULL; i++)
if (optncmp(optname, longopts[i].name, optnamelen) == 0
&& strlen(longopts[i].name) == optnamelen)
return i;
return -1;
}
/* Check if an option has a separate argument (in the following argv[] index) */
static int has_separate_argument(int curopt, int argc, char *const argv[],
const char *shortopts,
const struct option *longopts,
const char *assigners, const char *optleaders,
int flags)
{
int longind;
char *longarg;
assert(curopt < argc && like_option(argv[curopt], optleaders));
/* Check if we have a long option */
longind = match_longopt(ultraoptind, argv, longopts, assigners, optleaders,
flags, &longarg);
if (longind >= 0) {
if (longopts[longind].has_arg == no_argument
|| longarg != NULL
|| (longopts[longind].has_arg == optional_argument
&& !(flags & UGO_SEPARATEDOPTIONAL)))
return 0;
return like_optarg(argv[curopt+1], optleaders,
(flags & UGO_HYPHENARG) &&
longopts[longind].has_arg == required_argument);
} else if (!strchr(optleaders, argv[curopt][1])) {
/* Short option */
char *optpos;
optpos = strchr(shortopts, argv[curopt][1]);
if ((flags & UGO_CASEINSENSITIVE) && optpos == NULL) {
if (islower(argv[curopt][1]))
optpos = strchr(shortopts, toupper(argv[curopt][1]));
else
optpos = strchr(shortopts, tolower(argv[curopt][1]));
}
return optpos != NULL /* Option found */
&& optpos[1] == ':' /* Option takes argument */
&& (optpos[2] != ':' || (flags & UGO_SEPARATEDOPTIONAL))
&& argv[curopt][2] == '\0' /* Argument is not adjacent */
&& like_optarg(argv[curopt+1], /* Is an argument */
optleaders,
(flags & UGO_HYPHENARG) && optpos[2] != ':');
}
/* No match */
return 0;
}
/* Bring the next option, or terminator, up to ultraoptind if there is one
* Returns number of words shifted forward
*/
static int permute_options(int argc, char *argv[], const char *shortopts,
const struct option *longopts,
const char *assigners, const char *optleaders,
int flags)
{
int curopt = ultraoptind;
/* If we already have an option or no more possible, give up */
if (curopt >= argc || like_option(argv[curopt], optleaders))
return 0;
for ( ; curopt < argc && argv[curopt]; curopt++) {
int shiftarg = 0;
int i;
/* Permute options and the option terminator */
if (like_option(argv[curopt], optleaders)) {
/* Check if we need to shift argument too */
shiftarg = has_separate_argument(curopt, argc, argv, shortopts,
longopts, assigners, optleaders,
flags);
} else if (!like_optterm(argv[curopt], optleaders)) {
continue;
}
/* Shift option */
for (i=curopt; i>ultraoptind; i--) {
char *temp = argv[i];
argv[i] = argv[i-1];
argv[i-1] = temp;
if (shiftarg) {
temp = argv[i+1];
argv[i+1] = argv[i];
argv[i] = temp;
}
}
/* All done */
if (shiftarg)
return 2;
else
return 1;
}
/* Couldn't find an option, bummer */
return 0;
}
/* Handle a longopts[longind] matches argv[ultraoptind] actions */
static int handle_longopt(int longind, char *longarg, int noseparg,
char *const argv[],
const struct option *longopts, int *indexptr,
const char *optleaders, int flags)
{
/* Handle assignment arguments */
if (longarg && longopts[longind].has_arg == no_argument) {
print_error(flags, errorarg, argv[0],
longarg-argv[ultraoptind]-1, argv[ultraoptind]);
/* TODO: What is a good value to put in ultraoptopt? */
/* Looks like GNU getopt() uses val */
ultraoptopt = longopts[longind].val;
ultraoptind++;
return '?';
} else if (longarg) {
ultraoptind++;
ultraoptarg = longarg;
if (indexptr)
*indexptr = longind;
if (longopts[longind].flag) {
*(longopts[longind].flag) = longopts[longind].val;
return 0;
} else
return longopts[longind].val;
}
/* Handle missing required argument */
if (longopts[longind].has_arg == required_argument
&& (noseparg
|| !like_optarg(argv[ultraoptind+1],
optleaders,
flags & UGO_HYPHENARG))) {
print_error(flags, errornoarg, argv[0],
strlen(argv[ultraoptind]), argv[ultraoptind]);
ultraoptind++;
if (flags & UGO_MISSINGCOLON)
return ':';
else
return '?';
}
/* Handle available argument */
if ((longopts[longind].has_arg == required_argument
|| (longopts[longind].has_arg == optional_argument
&& (flags & UGO_SEPARATEDOPTIONAL)))
&& !noseparg
&& like_optarg(argv[ultraoptind+1],
optleaders,
(flags & UGO_HYPHENARG) &&
longopts[longind].has_arg == required_argument)) {
ultraoptarg = argv[ultraoptind+1];
ultraoptind += 2;
} else
ultraoptind++;
if (indexptr)
*indexptr = longind;
if (longopts[longind].flag) {
*(longopts[longind].flag) = longopts[longind].val;
return 0;
} else
return longopts[longind].val;
}
int ultragetopt_tunable(int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *indexptr,
const char *assigners, const char *optleaders,
int flags)
{
char *opt; /* Option we are processing */
char *optpos; /* Pointer to opt in shortopts */
int noseparg = 0; /* Force option not to have a separate argument */
if (ultraoptreset) {
ultraoptind = 1;
ultraopterr = 1;
ultraoptnum = 0;
ultraoptreset = 0;
}
ultraoptarg = NULL;
#ifdef ULTRAGETOPT_DEFAULTOPTOPT
ultraoptopt = ULTRAGETOPT_DEFAULTOPTOPT -0;
#endif
/* Sanity check (These are specified verbatim in SUS) */
if (ultraoptind > argc
|| argv[ultraoptind] == NULL)
return -1;
/* No permuting when $POSIXLY_CORRECT is set (to match GNU getopt) */
if (getenv("POSIXLY_CORRECT"))
flags &= ~UGO_OPTIONPERMUTE;
/* Get flags from shortopts */
for ( ; shortopts && *shortopts; shortopts++) {
if (*shortopts == '+')
flags &= ~UGO_OPTIONPERMUTE;
else if (*shortopts == '-')
flags |= UGO_NONOPTARG;
else if (*shortopts == ':') {
flags |= UGO_NOPRINTERR;
flags |= UGO_MISSINGCOLON;
} else
break;
}
/* Found non-option */
if (!like_option(argv[ultraoptind], optleaders)) {
int shifted;
if (like_optterm(argv[ultraoptind], optleaders)) {
if (!(flags & UGO_NOEATDASHDASH))
ultraoptind++;
return -1;
}
if (flags & UGO_NONOPTARG) {
ultraoptarg = argv[ultraoptind];
ultraoptind++;
return 1;
}
if (!(flags & UGO_OPTIONPERMUTE))
return -1;
shifted = permute_options(argc, (char **)argv, shortopts, longopts,
assigners, optleaders, flags);
if (shifted == 0)
return -1;
else if (shifted == 1)
noseparg = 1;
if (like_optterm(argv[ultraoptind], optleaders)) {
if (!(flags & UGO_NOEATDASHDASH))
ultraoptind++;
return -1;
}
}
/* At this point we must have an option of some sort */
assert(like_option(argv[ultraoptind], optleaders));
/* Handle --* */
if (argv[ultraoptind][0] == argv[ultraoptind][1]) {
int longind;
char *longarg;
/* Handle long option */
longind = match_longopt(ultraoptind, argv, longopts, assigners,
optleaders, flags, &longarg);
if (longind < 0) {
if (longarg == NULL)
print_error(flags, erroropt, argv[0],
strlen(argv[ultraoptind]), argv[ultraoptind]);
else
print_error(flags, erroropt, argv[0],
longarg - argv[ultraoptind] - 1, argv[ultraoptind]);
/* TODO: What is a good value for optopt in this case? */
/* Looks like BSD uses 0 */
ultraoptopt = 0;
ultraoptind++;
return '?';
}
return handle_longopt(longind, longarg, noseparg, argv,
longopts, indexptr, optleaders, flags);
}
/* See if it matches a long-only option */
if (longopts != NULL &&
ultraoptnum == 0 &&
((flags & UGO_SINGLELEADERLONG) ||
(flags & UGO_SINGLELEADERONLY))) {
int longind;
char *longarg;
longind = match_longopt(ultraoptind, argv, longopts, assigners,
optleaders, flags, &longarg);
if (longind >= 0)
return handle_longopt(longind, longarg, noseparg, argv,
longopts, indexptr, optleaders, flags);
}
/* No long matches, process short option */
opt = argv[ultraoptind] + ultraoptnum + 1;
optpos = strchr(shortopts, opt[0]);
if (optpos == NULL && (flags & UGO_CASEINSENSITIVE)) {
if (islower(opt[0]))
optpos = strchr(shortopts, toupper(opt[0]));
else
optpos = strchr(shortopts, tolower(opt[0]));
}
/* This could indicate ultraoptnum not being reset properly */
assert(opt[0] != '\0');
/* Check for invalid or unrecognized option */
if (optpos == NULL || opt[0] == ':') {
print_error(flags, erroroptc, argv[0], opt[0]);
ultraoptopt = opt[0];
if (opt[1] != '\0')
ultraoptnum++;
else {
ultraoptnum = 0;
ultraoptind++;
}
return '?';
}
/* Handle arguments */
if (optpos[1] == ':') {
ultraoptnum = 0;
/* Handle adjacent arguments -ofile.txt */
if (opt[1] != '\0') {
/* Skip over assignment character */
if ((flags & UGO_SHORTOPTASSIGN) && strchr(assigners, opt[1]))
ultraoptarg = opt + 2;
else
ultraoptarg = opt + 1;
ultraoptind++;
return optpos[0];
}
/* Handle optional argument not present */
if ((flags & UGO_OPTIONALARG) /* accept optionals */
&& optpos[2] == ':' /* opt takes optional */
&& (argv[ultraoptind+1] == NULL /* optional doesn't exist */
|| !(flags & UGO_SEPARATEDOPTIONAL) /* separated not accepted */
|| like_option(argv[ultraoptind+1], optleaders))) {
ultraoptind++;
return optpos[0];
}
/* Handle separated argument missing */
if (ultraoptind+2 > argc
|| noseparg
|| !like_optarg(argv[ultraoptind+1],
optleaders,
(flags & UGO_HYPHENARG))) {
ultraoptind++;
print_error(flags, errornoargc, argv[0], opt[0]);
ultraoptopt = opt[0];
if (flags & UGO_MISSINGCOLON)
return ':';
else
return '?';
}
ultraoptind += 2;
ultraoptarg = argv[ultraoptind-1];
return optpos[0];
}
/* Handle argumentless option with assigned option */
if ((flags & UGO_SHORTOPTASSIGN)
&& opt[1] != '\0' && strchr(assigners, opt[1])) {
print_error(flags, errorargc, argv[0], opt[0]);
ultraoptnum = 0;
ultraoptopt = opt[0];
ultraoptind++;
return '?';
}
if (opt[1] != '\0') {
ultraoptnum++;
} else {
ultraoptnum = 0;
ultraoptind++;
}
return optpos[0];
}
/* POSIX-compliant getopt
*
* Handles optional argument '::' specifier as an extension for compatibility
* with glibc
*/
int ultragetopt(int argc, char * const argv[], const char *optstring)
{
int flags = getoptflags;
#ifdef ULTRAGETOPT_OPTIONPERMUTE
flags |= UGO_OPTIONPERMUTE;
#endif
#ifndef ULTRAGETOPT_NO_OPTIONALARG
flags |= UGO_OPTIONALARG;
#endif
return ultragetopt_tunable(argc, argv, optstring, NULL, NULL,
unixassigners, unixleaders, flags);
}
/* GNU getopt_long workalike
*
* Argument reordering not yet implemented
* Leading + and - under consideration (behavior violates POSIX...)
*/
int ultragetopt_long(int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *indexptr)
{
return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr,
unixassigners, unixleaders,
getoptflags | UGO_OPTIONPERMUTE | UGO_OPTIONALARG);
}
/* GNU getopt_long_only workalike */
int ultragetopt_long_only(int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *indexptr)
{
return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr,
unixassigners, unixleaders,
getoptflags | UGO_SINGLELEADERLONG
| UGO_OPTIONPERMUTE | UGO_OPTIONALARG);
}
int ultragetopt_dos(int argc, char * const argv[], const char *optstring)
{
return ultragetopt_tunable(argc, argv, optstring, NULL, NULL,
dosassigners, dosleaders,
getoptflags | UGO_CASEINSENSITIVE);
}
int ultragetopt_long_dos(int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *indexptr)
{
return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr,
dosassigners, dosleaders,
getoptflags | UGO_CASEINSENSITIVE
| UGO_SINGLELEADERLONG | UGO_SINGLELEADERONLY
| UGO_OPTIONPERMUTE | UGO_OPTIONALARG);
}
/* vim:set sts=4 sw=4: */