/*
* Replacement implementation of getopt.
*
* This is a replacement implementation for getopt based on the my_getopt
* distribution by Benjamin Sittler. Only the getopt interface is included,
* since remctl doesn't use GNU long options, and the code has been rearranged
* and reworked somewhat to fit with my coding style.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at .
*
* Copyright 2008, 2017 Russ Allbery
* Copyright 1997, 2000-2002 Benjamin Sittler
*
* 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.
*
* SPDX-License-Identifier: MIT
*/
#include
#include
#include
/*
* If we're running the test suite, rename getopt and the global variables to
* avoid conflicts with the system version.
*/
#if TESTING
# undef getopt
# undef optind
# undef opterr
# undef optopt
# undef optarg
# define getopt test_getopt
int test_getopt(int, char **, const char *);
# define optind test_optind
# define opterr test_opterr
# define optopt test_optopt
# define optarg test_optarg
extern int test_optind;
extern int test_opterr;
extern int test_optopt;
extern char *test_optarg;
#endif
/* Initialize global interface variables. */
int optind = 1;
int opterr = 1;
int optopt = 0;
char *optarg = NULL;
/*
* This is the plain old UNIX getopt, with GNU-style extensions. If you're
* porting some piece of UNIX software, this is all you need. It supports
* GNU-style permutation and optional arguments, but does not support the GNU
* -W extension.
*
* This function is not re-entrant or thread-safe, has static variables, and
* generally isn't a great interface, but normally you only call it once.
*/
int
getopt(int argc, char *argv[], const char *optstring)
{
const char *p;
size_t offset = 0;
char mode = '\0';
int colon_mode = 0;
int option = -1;
/* Holds the current position in the parameter being parsed. */
static int charind = 0;
/*
* By default, getopt permutes argv as it scans and leaves all non-options
* at the end. This can be changed with the first character of optstring
* or the environment variable POSIXLY_CORRECT. With a first character of
* '+' or when POSIXLY_CORRECT is set, option processing stops at the
* first non-option. If the first character is '-', each non-option argv
* element is handled as if it were the argument of an option with
* character code 1. mode holds this character.
*
* After the optional leading '+' and '-', optstring may contain ':'. If
* present, missing arguments return ':' instead of '?'. colon_mode holds
* this setting.
*/
if (getenv("POSIXLY_CORRECT") != NULL) {
mode = '+';
colon_mode = '+';
} else {
if (optstring[offset] == '+' || optstring[offset] == '-') {
mode = optstring[offset];
offset++;
}
if (optstring[offset] == ':') {
colon_mode = 1;
offset++;
}
}
/*
* charind holds where we left off. If it's set, we were in the middle
* of an argv element; if not, we pick up with the next element of
* optind.
*/
optarg = NULL;
if (charind == 0) {
if (optind >= argc)
option = -1;
else if (strcmp(argv[optind], "--") == 0) {
optind++;
option = -1;
} else if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
char *tmp;
int i, j, k, end;
if (mode == '+')
option = -1;
else if (mode == '-') {
optarg = argv[optind];
optind++;
option = 1;
} else {
for (i = optind + 1, j = optind; i < argc; i++)
if ((argv[i][0] == '-') && (argv[i][1] != '\0')) {
optind = i;
option = getopt(argc, argv, optstring);
while (i > j) {
--i;
tmp = argv[i];
end = (charind == 0) ? optind - 1 : optind;
for (k = i; k < end; k++) {
argv[k] = argv[k + 1];
}
argv[end] = tmp;
--optind;
}
break;
}
if (i == argc)
option = -1;
}
return option;
} else {
charind = 1;
}
}
if (charind != 0) {
optopt = argv[optind][charind];
for (p = optstring + offset; *p != '\0'; p++)
if (optopt == *p) {
p++;
if (*p == ':') {
if (argv[optind][charind + 1] != '\0') {
optarg = &argv[optind][charind + 1];
optind++;
charind = 0;
} else {
p++;
if (*p != ':') {
charind = 0;
optind++;
if (optind >= argc) {
if (opterr)
fprintf(stderr, "%s: option requires"
" an argument -- %c\n", argv[0],
optopt);
option = colon_mode ? ':' : '?';
goto done;
} else {
optarg = argv[optind];
optind++;
}
}
}
}
option = optopt;
}
if (option == -1) {
if (opterr)
fprintf(stderr, "%s: illegal option -- %c\n", argv[0], optopt);
option = '?';
}
}
done:
if (charind != 0) {
charind++;
if (argv[optind][charind] == '\0') {
optind++;
charind = 0;
}
}
if (optind > argc)
optind = argc;
return option;
}