blob: 3d3bb92ecdac82fbd56c090693d3aa799db3f469 [file] [log] [blame] [raw]
/* A getopt implementation with GNU extensions
Copyright 2007-2015 PC GO Ld.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program 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 General Public License for more details.
*/
/* Basd on "mingwrt-3.15.2-mingw32/mingwex/getopt.c" (1.8, keithmarshall) */
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <stdlib.h>
#define GETOPT_LONG_NO_MATCH 0
#define GETOPT_LONG_ABBREVIATED_MATCH 1
#define GETOPT_LONG_EXACT_MATCH 2
int optind = 1;
int opterr = 1;
int optopt = '?';
char *optarg;
static int getopt_missing_arg(const char *optstring) {
if(*optstring == '+' || *optstring == '-') optstring++;
return *optstring == ':' ? ':' : '?';
}
static void getopt_argerror(const char *format, const char *program, const struct option *opt) {
if(opterr) {
fprintf(stderr, "%s: ", program);
//fprintf(stderr, fmt, opt ? "--" : "-",
fprintf(stderr, format, "--", opt->name);
}
optopt = opt->val;
}
#define GETOPT_SET_CONVENTIONS 0x1000
#define GETOPT_POSIXLY_CORRECT 0x0010
static int getopt_conventions(int flags) {
static int conventions = 0;
if(conventions == 0 && !(flags & GETOPT_SET_CONVENTIONS)) {
conventions = GETOPT_SET_CONVENTIONS;
if(flags == '+' || getenv("POSIXLY_CORRECT")) {
conventions |= GETOPT_POSIXLY_CORRECT;
}
} else if(flags & GETOPT_SET_CONVENTIONS) {
conventions |= flags;
}
return conventions;
}
static const char *getopt_match(char lookup, const char *optstring) {
if(*optstring == '+' || *optstring == '-') optstring++;
if(*optstring == ':') optstring++;
do {
if(*optstring == lookup) return optstring;
} while(*++optstring);
return NULL;
}
static int getopt_match_long(char *nextchar, const char *optname) {
char matchc;
while((matchc = *nextchar++) && matchc == *optname) optname++;
if(matchc) {
if(matchc != '=') return GETOPT_LONG_NO_MATCH;
optarg = nextchar;
}
return *optname ? GETOPT_LONG_ABBREVIATED_MATCH : GETOPT_LONG_EXACT_MATCH;
}
static int getopt_resolved(int argc, char *const *argv, int *argind, const struct option *opt, int index, int *rindex, const char *optstring) {
if(rindex) *rindex = index;
optind = *argind + 1;
if(optarg && opt[index].has_arg == no_argument) {
getopt_argerror("option `%s%s' doesn't accept an argument\n", argv[0], opt + index);
return '?';
} else if(!optarg && opt[index].has_arg == required_argument) {
if(optind < argc) {
optarg = argv[*argind = optind++];
} else {
getopt_argerror("option `%s%s' requires an argument\n", argv[0], opt + index);
return getopt_missing_arg(optstring);
}
}
if(opt[index].flag) {
*(opt[index].flag) = opt[index].val;
return 0;
}
return opt[index].val;
}
static int getopt_parse(int argc, char *const *argv, const char *optstring, const struct option *long_opts, int *index, int long_only) {
static int argind = 0;
static char *nextchar = NULL;
static int optbase = 0;
static int optmark = 0;
if(optind < optbase) {
// Reinitialize required
if(!optind) optind = 1;
optmark = optbase = argind = optind - 1;
nextchar = NULL;
}
if(argc < 1) return -1;
if(nextchar && *nextchar) {
const char *optchar = getopt_match(optopt = *nextchar++, optstring);
if(optchar) {
if(optchar[1] == ':') {
optarg = nextchar;
if(!*nextchar) {
if(optchar[2] == ':') optarg = NULL;
else if(argc - argind > 1) optarg = argv[++argind];
else {
if(opterr) fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], optopt);
return getopt_missing_arg(optstring);
}
}
optind = argind + 1;
nextchar = NULL;
} else optarg = NULL;
optind = (nextchar && *nextchar) ? argind : argind + 1;
return optopt;
}
if(long_only) {
if(opterr) fprintf(stderr, "%s: unrecognized option `-%s'\n", argv[0], --nextchar);
nextchar = NULL;
optopt = 0;
} else if(opterr) {
fprintf(stderr, "%s: invalid option -- %c\n", argv[0], optopt);
}
optind = (nextchar && *nextchar) ? argind : argind + 1;
return '?';
}
if(optmark > optbase) {
int optspan = argind - optmark + 1;
char *this_arg[optspan];
char **arglist = (char **)argv; // 447
int i;
// Save
for(i = 0; i < optspan; i++) {
this_arg[i] = arglist[optmark + i];
}
// Move
for(optmark--; optmark >= optbase; optmark--) {
arglist[optmark + optspan] = arglist[optmark];
}
// Restore
for(i = 0; i < optspan; i++) {
arglist[optbase + i] = this_arg[i];
}
optbase += optspan;
} else optbase = argind + 1;
while(argc > ++argind) {
nextchar = argv[optmark = argind];
if(*nextchar == '-' && *++nextchar) {
if(*nextchar == '-') {
char *t = nextchar + 1;
if(*t) {
// A long option ?
if(long_opts) nextchar = t;
} else {
// End of option mark
if(optmark > optbase) {
char *this_arg = argv[optmark];
char **arglist = (char **)argv; // 517
do {
arglist[optmark] = arglist[optmark - 1];
} while(optmark-- > optbase);
arglist[optbase] = this_arg;
}
optind = ++optbase;
return -1;
}
} else if(!long_only) {
long_opts = NULL;
}
if(long_opts) {
int matched = 1;
int i;
optarg = NULL;
for(i = 0; long_opts && long_opts[i].name; i++) switch(getopt_match_long(nextchar, long_opts[i].name)) {
case GETOPT_LONG_EXACT_MATCH:
nextchar = NULL;
return getopt_resolved(argc, argv, &argind, long_opts, i, index, optstring);
case GETOPT_LONG_ABBREVIATED_MATCH:
if(matched >= 0) {
optopt = 0;
nextchar = NULL;
optind = argind + 1;
if(opterr) fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[argind]);
return '?';
}
matched = i;
}
if(matched >= 0) {
nextchar = NULL;
return getopt_resolved(argc, argv, &argind, long_opts, matched, index, optstring);
}
if(!long_only) {
optopt = 0;
nextchar = NULL;
optind = argind + 1;
if(opterr) fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[argind]);
return '?';
}
}
if(*nextchar) {
return getopt_parse(argc, argv, optstring, NULL, NULL, 0);
}
}
if(*optstring == '-') {
nextchar = NULL;
optind = argind + 1;
optarg = argv[optind];
return 1;
}
if(getopt_conventions(*optstring) & GETOPT_POSIXLY_CORRECT) break;
}
optind = optbase;
return -1;
}
int getopt(int argc, char *const *argv, const char *optstring) {
return getopt_parse(argc, argv, optstring, NULL, NULL, 0);
}
int getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex) {
return getopt_parse(argc, argv, optstring, longopts, longindex, 0);
}
int getopt_long_only(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex) {
return getopt_parse(argc, argv, optstring, longopts, longindex, 1);
}