#pragma weak _getopt_clip = getopt_clip
#pragma weak _getopt_long = getopt_long
#pragma weak _getopt_long_only = getopt_long_only
#include "lint.h"
#include <getopt.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "_libc_gettext.h"
static int optreset = 0;
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_IS_SET(flag) ((flags & flag) != 0)
#define LONGOPT_REQUIRES_ARG(longopt) \
((((longopt).has_arg == optional_argument) && \
(!FLAG_IS_SET(FLAG_OPTIONAL_ARGS))) || \
((longopt).has_arg == required_argument))
#define FLAG_PERMUTE 0x01
#define FLAG_ALLARGS 0x02
#define FLAG_LONGONLY 0x04
#define FLAG_OPTIONAL_ARGS 0x08
#define FLAG_REQUIRE_EQUIVALENTS 0x10
#define FLAG_ABBREV 0x20
#define FLAG_W_SEMICOLON 0x40
#define FLAG_PLUS_DASH_START 0x80
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG ""
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, uint_t);
static int parse_long_options(int nargc, char * const *nargv, const char *,
const struct option *, int *, int,
uint_t flags);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG;
static int nonopt_start = -1;
static int nonopt_end = -1;
static void
warnx_getopt(const char *argv0, const char *msg, const char *arg)
{
char errbuf[256];
(void) snprintf(errbuf, sizeof (errbuf), msg, argv0, arg);
(void) write(2, errbuf, strlen(errbuf));
(void) write(2, "\n", 1);
}
static void
warnxchar(const char *argv0, const char *msg, const char c)
{
char charbuf[2];
charbuf[0] = c;
charbuf[1] = '\0';
warnx_getopt(argv0, msg, charbuf);
}
static void
warnxlen(const char *argv0, const char *msg, int argLen, const char *arg)
{
char argbuf[256];
(void) strncpy(argbuf, arg, argLen);
argbuf[argLen < (sizeof (argbuf)-1)? argLen:(sizeof (argbuf)-1)] = '\0';
warnx_getopt(argv0, msg, argbuf);
}
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
((char **)nargv)[pos] = nargv[cstart];
((char **)nargv)[cstart] = swap;
}
}
}
static int
verify_short_long_equivalents(int nargc __unused, char *const *nargv,
const char *options, const struct option *long_options, uint_t flags)
{
int short_i = 0;
int long_i = 0;
int equivFound = 0;
int ch = 0;
equivFound = 1;
for (short_i = 0; equivFound && (options[short_i] != 0); ++short_i) {
ch = options[short_i];
if (ch == ':') {
continue;
}
if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
(ch == 'W') && (options[short_i+1] == ';')) {
++short_i;
continue;
}
equivFound = 0;
if (long_options != NULL) {
for (long_i = 0; ((!equivFound) &&
(long_options[long_i].name != NULL));
++long_i) {
equivFound = (ch == long_options[long_i].val);
}
}
if ((!equivFound) && (PRINT_ERROR)) {
warnxchar(nargv[0],
_libc_gettext(
"%s: equivalent long option required -- %s"),
ch);
}
}
if (equivFound && (long_options != NULL)) {
for (long_i = 0; (equivFound &&
(long_options[long_i].name != NULL));
++long_i) {
equivFound = ((long_options[long_i].val != 0) &&
(strchr(options, long_options[long_i].val)
!= NULL));
if ((!equivFound) && (PRINT_ERROR)) {
warnx_getopt(nargv[0],
_libc_gettext("%s: equivalent short "
"option required -- %s"),
long_options[long_i].name);
}
}
}
return (equivFound? 0:-1);
}
static int
parse_long_options(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too,
uint_t flags)
{
char *current_argv = NULL;
char *argv_equal_ptr = NULL;
size_t current_argv_len = 0;
size_t long_option_len = 0;
int i = 0;
int match = 0;
current_argv = place;
match = -1;
optind++;
if ((argv_equal_ptr = strchr(current_argv, '=')) != NULL) {
current_argv_len = (argv_equal_ptr - current_argv);
argv_equal_ptr++;
} else {
current_argv_len = strlen(current_argv);
}
for (i = 0; (long_options[i].name != NULL); i++) {
if (strncmp(current_argv, long_options[i].name,
current_argv_len) != 0) {
continue;
}
long_option_len = strlen(long_options[i].name);
if ((!FLAG_IS_SET(FLAG_ABBREV)) &&
(long_option_len > current_argv_len)) {
continue;
}
if (long_option_len == current_argv_len) {
match = i;
break;
}
if (short_too && current_argv_len == 1)
continue;
if (match == -1)
match = i;
else {
if (PRINT_ERROR) {
warnxlen(nargv[0],
_libc_gettext(
"%s: ambiguous option -- %s"),
(int)current_argv_len,
current_argv);
}
optopt = 0;
return (BADCH);
}
}
if (match != -1) {
if ((long_options[match].has_arg == no_argument) &&
(argv_equal_ptr != NULL)) {
if (PRINT_ERROR) {
warnxlen(nargv[0],
_libc_gettext(
"%s: option doesn't take an argument -- %s"),
(int)current_argv_len,
current_argv);
}
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (argv_equal_ptr != NULL) {
optarg = argv_equal_ptr;
} else if (LONGOPT_REQUIRES_ARG(long_options[match])) {
if (optind < nargc) {
optarg = nargv[optind];
}
++optind;
}
}
if (LONGOPT_REQUIRES_ARG(long_options[match]) &&
(optarg == NULL)) {
if (PRINT_ERROR) {
warnx_getopt(nargv[0],
_libc_gettext(
"%s: option requires an argument -- %s"),
current_argv);
}
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else {
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR) {
warnx_getopt(nargv[0],
_libc_gettext("%s: illegal option -- %s"),
current_argv);
}
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag != NULL) {
*long_options[match].flag = long_options[match].val;
return (0);
} else {
optopt = long_options[match].val;
return (optopt);
}
}
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, uint_t flags)
{
char *oli;
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
if (posixly_correct == -1) {
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
}
if (FLAG_IS_SET(FLAG_PLUS_DASH_START)) {
if (*options == '+') {
posixly_correct = 1;
} else if (*options == '-') {
posixly_correct = 0;
flags |= FLAG_ALLARGS;
}
if ((*options == '+') || (*options == '-')) {
options++;
}
}
if (posixly_correct) {
flags &= ~FLAG_PERMUTE;
flags &= ~FLAG_ALLARGS;
}
if (optind == 0) {
optind = optreset = 1;
}
optarg = NULL;
optopt = 0;
if (optreset) {
nonopt_start = nonopt_end = -1;
}
if ((optind == 1) && FLAG_IS_SET(FLAG_REQUIRE_EQUIVALENTS)) {
if (verify_short_long_equivalents(
nargc, nargv, options, long_options, flags) < 0) {
errno = EINVAL;
return (-1);
}
}
start:
if (optreset || !*place) {
optreset = 0;
if (optind >= nargc) {
place = EMSG;
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
} else if (nonopt_start != -1) {
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) {
place = EMSG;
if (flags & FLAG_ALLARGS) {
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
return (-1);
}
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
if (place[1] != '\0' && *++place == '-' &&
(place[1] == '\0' || long_options == NULL)) {
optind++;
place = EMSG;
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (FLAG_IS_SET(FLAG_LONGONLY)))) {
short_too = 0;
if (*place == '-')
place++;
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1;
optchar = parse_long_options(nargc, nargv, options,
long_options, idx, short_too, flags);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(oli = strchr(options, optchar)) == NULL) {
if (optchar == (int)'-')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
warnxchar(nargv[0],
_libc_gettext("%s: illegal option -- %s"),
optchar);
optopt = optchar;
return (BADCH);
}
if (FLAG_IS_SET(FLAG_W_SEMICOLON) &&
(long_options != NULL) && (optchar == 'W') && (oli[1] == ';')) {
if (*place) {
;
} else if (++optind >= nargc) {
place = EMSG;
if (PRINT_ERROR)
warnxchar(nargv[0],
_libc_gettext(
"%s: option requires an argument -- %s"),
optchar);
optopt = optchar;
return (BADARG);
} else {
place = nargv[optind];
}
optchar = parse_long_options(
nargc, nargv, options, long_options,
idx, 0, flags);
if (optarg == NULL) {
optarg = nargv[optind-1];
}
place = EMSG;
return (optchar);
}
if (*++oli != ':') {
if (!*place)
++optind;
} else {
optarg = NULL;
if (*place) {
optarg = place;
} else if (!(FLAG_IS_SET(FLAG_OPTIONAL_ARGS) &&
(oli[1] == ':'))) {
if (++optind >= nargc) {
place = EMSG;
if (PRINT_ERROR) {
warnxchar(nargv[0],
_libc_gettext(
"%s: option requires an argument -- %s"),
optchar);
}
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
optopt = optchar;
return (optchar);
}
int
getopt_long(int nargc, char *const *nargv,
const char *optstring,
const struct option *long_options, int *long_index)
{
return (getopt_internal(
nargc, nargv, optstring, long_options, long_index,
FLAG_PERMUTE
| FLAG_OPTIONAL_ARGS
| FLAG_ABBREV
| FLAG_W_SEMICOLON
| FLAG_PLUS_DASH_START));
}
int
getopt_long_only(int nargc, char *const *nargv,
const char *optstring,
const struct option *long_options, int *long_index)
{
return (getopt_internal(
nargc, nargv, optstring, long_options, long_index,
FLAG_PERMUTE
| FLAG_OPTIONAL_ARGS
| FLAG_ABBREV
| FLAG_W_SEMICOLON
| FLAG_PLUS_DASH_START
| FLAG_LONGONLY));
}
int
getopt_clip(int nargc, char *const *nargv, const char *optstring,
const struct option *long_options, int *long_index)
{
return getopt_internal(
nargc, nargv, optstring, long_options, long_index,
FLAG_W_SEMICOLON
| FLAG_REQUIRE_EQUIVALENTS);
}