#include "getopt.h"
#include <error.h>
#include <string.h>
#include <stdio.h>
enum {
OM_PERMUTE,
OM_REQUIRE_ORDER,
OM_PRESERVE_ORDER
};
int opterr __attribute__ ((weak, visibility ("default")));
int optind __attribute__ ((weak, visibility ("default")));
static int nextchar;
char *optarg __attribute__ ((weak, visibility ("default")));
int optopt __attribute__ ((weak, visibility ("default")));
void __getopt_init()
{
opterr = 1;
optind = 1;
nextchar = 0;
}
static inline int is_option(const char *arg)
{
return arg[0] == '-' && strcmp(arg, "-") && strcmp(arg, "--");
}
static inline int is_long_option(const char *arg)
{
return arg[1] == '-';
}
static void permute(char *argv[], int ind)
{
char *tmp;
int i;
tmp = argv[ind];
for (i = ind; i > optind; i--)
argv[i] = argv[i - 1];
argv[optind] = tmp;
}
static const char *search_short_option(char short_opt_char, const char *optstring)
{
return strchr(optstring, short_opt_char);
}
static const struct option *search_long_option(const char *long_opt_str, const struct option *longopts)
{
const struct option *long_opt1, *long_opt2;
size_t n;
long_opt1 = &longopts[0];
n = 0;
while (1) {
if (!long_opt1->name)
return long_opt1;
if (strncmp(long_opt_str, long_opt1->name, n))
long_opt1++;
else {
if (!long_opt_str[n] || long_opt_str[n] == '=')
break;
n++;
}
}
if (!long_opt1->name[n])
return long_opt1;
long_opt2 = long_opt1 + 1;
while (long_opt2->name) {
if (!strncmp(long_opt_str, long_opt2->name, n)) {
if (long_opt2->name[n])
return long_opt2;
else
return NULL;
}
long_opt2++;
}
return long_opt1;
}
static int getopt_internal(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only)
{
int mode;
int next_ind;
char *arg;
char short_opt_char;
const char *short_opt;
const char *long_opt_str;
const struct option *long_opt;
char *long_opt_arg;
if (!optind) {
optind = 1;
nextchar = 0;
}
if (optstring[0] == '+') {
mode = OM_REQUIRE_ORDER;
optstring++;
}
else if (optstring[0] == '-') {
mode = OM_PRESERVE_ORDER;
optstring++;
}
else
mode = OM_PERMUTE;
next_ind = optind;
if (!nextchar) {
while (1) {
if (next_ind == argc)
return -1;
if (is_option(argv[next_ind])) {
if (next_ind != optind)
permute(argv, next_ind);
break;
}
if (!strcmp(argv[next_ind], "--")) {
if (next_ind != optind)
permute(argv, next_ind);
optind++;
return -1;
}
switch (mode) {
case OM_PERMUTE:
next_ind++;
continue;
case OM_REQUIRE_ORDER:
return -1;
case OM_PRESERVE_ORDER:
short_opt_char = 1;
goto short_option_arg;
}
}
if (longopts && is_long_option(argv[optind])) {
arg = argv[optind];
long_opt_str = arg + 2;
if (!(long_opt = search_long_option(long_opt_str, longopts)))
goto error_ambiguous_long_option;
if (!long_opt->name)
goto error_unrecognized_long_option;
goto long_option;
}
nextchar = 1;
}
arg = argv[optind];
if (long_only) {
long_opt_str = arg + nextchar;
if (!(long_opt = search_long_option(long_opt_str, longopts)))
goto error_ambiguous_long_option;
if (long_opt->name)
goto long_option;
short_opt_char = arg[nextchar];
if ((short_opt = search_short_option(short_opt_char, optstring)))
goto short_option;
goto error_unrecognized_long_option;
}
else {
short_opt_char = arg[nextchar];
if (!(short_opt = search_short_option(short_opt_char, optstring)))
goto error_illegal_short_option;
goto short_option;
}
short_option:
nextchar++;
if (!arg[nextchar]) {
next_ind++;
optind++;
nextchar = 0;
}
if (short_opt[1] == ':') {
if (short_opt[2] == ':') {
if (nextchar) {
optarg = arg + nextchar;
optind++;
nextchar = 0;
}
else
optarg = NULL;
}
else {
if (nextchar) {
optarg = arg + nextchar;
optind++;
nextchar = 0;
}
else {
if (next_ind == argc)
goto error_short_option_requires_argument;
if (next_ind != optind)
permute(argv, next_ind);
short_option_arg:
optarg = argv[optind];
optind++;
}
}
}
return short_opt_char;
long_option:
next_ind++;
optind++;
nextchar = 0;
long_opt_arg = strchr(long_opt_str, '=');
switch (long_opt->has_arg) {
case no_argument:
if (long_opt_arg)
goto error_long_option_does_not_allow_argument;
break;
case required_argument:
if (long_opt_arg)
optarg = long_opt_arg + 1;
else {
if (next_ind == argc)
goto error_long_option_requires_argument;
if (next_ind != optind)
permute(argv, next_ind);
optarg = argv[optind];
optind++;
}
break;
case optional_argument:
if (long_opt_arg)
optarg = long_opt_arg + 1;
else
optarg = NULL;
break;
}
if (longindex)
*longindex = long_opt - longopts;
if (long_opt->flag) {
*long_opt->flag = long_opt->val;
return 0;
}
else
return long_opt->val;
error_illegal_short_option:
nextchar++;
if (!arg[nextchar]) {
optind++;
nextchar = 0;
}
optopt = short_opt_char;
if (opterr)
error(0, 0, "invalid option -- %c", short_opt_char);
return '?';
error_short_option_requires_argument:
optopt = short_opt_char;
if (optstring[0] == ':')
return ':';
else {
if (opterr)
error(0, 0, "option requires an argument -- %c", short_opt_char);
return '?';
}
error_ambiguous_long_option:
optind++;
nextchar = 0;
optopt = 0;
if (opterr)
error(0, 0, "option `%s' is ambiguous", arg);
return '?';
error_unrecognized_long_option:
optind++;
nextchar = 0;
optopt = 0;
if (opterr)
error(0, 0, "unrecognized option `%s'", arg);
return '?';
error_long_option_does_not_allow_argument:
optopt = 0;
if (opterr)
error(0, 0, "option `%s%s' doesn't allow an argument", &"--"[arg[1] == '-' ? 0 : 1], long_opt->name);
return '?';
error_long_option_requires_argument:
optopt = 0;
if (optstring[0] == ':')
return ':';
else {
if (opterr)
error(0, 0, "option `%s' requires an argument", arg);
return '?';
}
}
int __attribute__ ((weak, visibility ("default"))) getopt(int argc, char *argv[], const char *optstring)
{
return getopt_internal(argc, argv, optstring, NULL, NULL, 0);
}
int __attribute__ ((weak, visibility ("default"))) getopt_long(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex)
{
return getopt_internal(argc, argv, optstring, longopts, longindex, 0);
}
int __attribute__ ((weak, visibility ("default"))) getopt_long_only(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex)
{
return getopt_internal(argc, argv, optstring, longopts, longindex, 1);
}