mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-12 12:09:03 -04:00
319 lines
10 KiB
C
319 lines
10 KiB
C
/* -*- indent-tabs-mode: nil -*-
|
|
*
|
|
* ya_getopt - Yet another getopt
|
|
* https://github.com/kubo/ya_getopt
|
|
*
|
|
* Copyright 2015 Kubo Takehiro <kubo@jiubao.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification, are
|
|
* permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
* conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* The views and conclusions contained in the software and documentation are those of the
|
|
* authors and should not be interpreted as representing official policies, either expressed
|
|
* or implied, of the authors.
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ya_getopt.h"
|
|
|
|
char *ya_optarg = NULL;
|
|
int ya_optind = 1;
|
|
int ya_opterr = 1;
|
|
int ya_optopt = '?';
|
|
static char *ya_optnext = NULL;
|
|
static int posixly_correct = -1;
|
|
static int handle_nonopt_argv = 0;
|
|
|
|
static void ya_getopt_error(const char *optstring, const char *format, ...);
|
|
static void check_gnu_extension(const char *optstring);
|
|
static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only);
|
|
static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only);
|
|
static int ya_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct option *longopts, int *longindex, int *long_only_flag);
|
|
|
|
static void ya_getopt_error(const char *optstring, const char *format, ...)
|
|
{
|
|
if (ya_opterr && optstring[0] != ':') {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
vfprintf(stderr, format, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
static void check_gnu_extension(const char *optstring)
|
|
{
|
|
if (optstring[0] == '+' || getenv("POSIXLY_CORRECT") != NULL) {
|
|
posixly_correct = 1;
|
|
} else {
|
|
posixly_correct = 0;
|
|
}
|
|
if (optstring[0] == '-') {
|
|
handle_nonopt_argv = 1;
|
|
} else {
|
|
handle_nonopt_argv = 0;
|
|
}
|
|
}
|
|
|
|
static int is_option(const char *arg)
|
|
{
|
|
return arg[0] == '-' && arg[1] != '\0';
|
|
}
|
|
|
|
int ya_getopt(int argc, char * const argv[], const char *optstring)
|
|
{
|
|
return ya_getopt_internal(argc, argv, optstring, NULL, NULL, 0);
|
|
}
|
|
|
|
int ya_getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
|
|
{
|
|
return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 0);
|
|
}
|
|
|
|
int ya_getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
|
|
{
|
|
return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 1);
|
|
}
|
|
|
|
static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only)
|
|
{
|
|
static int start, end;
|
|
|
|
if (ya_optopt == '?') {
|
|
ya_optopt = 0;
|
|
}
|
|
|
|
if (posixly_correct == -1) {
|
|
check_gnu_extension(optstring);
|
|
}
|
|
|
|
if (ya_optind == 0) {
|
|
check_gnu_extension(optstring);
|
|
ya_optind = 1;
|
|
ya_optnext = NULL;
|
|
}
|
|
|
|
switch (optstring[0]) {
|
|
case '+':
|
|
case '-':
|
|
optstring++;
|
|
}
|
|
|
|
if (ya_optnext == NULL && start != 0) {
|
|
int last_pos = ya_optind - 1;
|
|
|
|
ya_optind -= end - start;
|
|
if (ya_optind <= 0) {
|
|
ya_optind = 1;
|
|
}
|
|
while (start < end--) {
|
|
int i;
|
|
char *arg = argv[end];
|
|
|
|
for (i = end; i < last_pos; i++) {
|
|
((char **)argv)[i] = argv[i + 1];
|
|
}
|
|
((char const **)argv)[i] = arg;
|
|
last_pos--;
|
|
}
|
|
start = 0;
|
|
}
|
|
|
|
if (ya_optind >= argc) {
|
|
ya_optarg = NULL;
|
|
return -1;
|
|
}
|
|
if (ya_optnext == NULL) {
|
|
const char *arg = argv[ya_optind];
|
|
if (!is_option(arg)) {
|
|
if (handle_nonopt_argv) {
|
|
ya_optarg = argv[ya_optind++];
|
|
start = 0;
|
|
return 1;
|
|
} else if (posixly_correct) {
|
|
ya_optarg = NULL;
|
|
return -1;
|
|
} else {
|
|
int i;
|
|
|
|
start = ya_optind;
|
|
for (i = ya_optind + 1; i < argc; i++) {
|
|
if (is_option(argv[i])) {
|
|
end = i;
|
|
break;
|
|
}
|
|
}
|
|
if (i == argc) {
|
|
ya_optarg = NULL;
|
|
return -1;
|
|
}
|
|
ya_optind = i;
|
|
arg = argv[ya_optind];
|
|
}
|
|
}
|
|
if (strcmp(arg, "--") == 0) {
|
|
ya_optind++;
|
|
return -1;
|
|
}
|
|
if (longopts != NULL && arg[1] == '-') {
|
|
return ya_getopt_longopts(argc, argv, argv[ya_optind] + 2, optstring, longopts, longindex, NULL);
|
|
}
|
|
}
|
|
|
|
if (ya_optnext == NULL) {
|
|
ya_optnext = argv[ya_optind] + 1;
|
|
}
|
|
if (long_only) {
|
|
int long_only_flag = 0;
|
|
int rv = ya_getopt_longopts(argc, argv, ya_optnext, optstring, longopts, longindex, &long_only_flag);
|
|
if (!long_only_flag) {
|
|
ya_optnext = NULL;
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return ya_getopt_shortopts(argc, argv, optstring, long_only);
|
|
}
|
|
|
|
static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only)
|
|
{
|
|
int opt = *ya_optnext;
|
|
const char *os = strchr(optstring, opt);
|
|
|
|
if (os == NULL) {
|
|
ya_optarg = NULL;
|
|
if (long_only) {
|
|
ya_getopt_error(optstring, "%s: unrecognized option '-%s'\n", argv[0], ya_optnext);
|
|
ya_optind++;
|
|
ya_optnext = NULL;
|
|
} else {
|
|
ya_optopt = opt;
|
|
ya_getopt_error(optstring, "%s: invalid option -- '%c'\n", argv[0], opt);
|
|
if (*(++ya_optnext) == 0) {
|
|
ya_optind++;
|
|
ya_optnext = NULL;
|
|
}
|
|
}
|
|
return '?';
|
|
}
|
|
if (os[1] == ':') {
|
|
if (ya_optnext[1] == 0) {
|
|
ya_optind++;
|
|
ya_optnext = NULL;
|
|
if (os[2] == ':') {
|
|
/* optional argument */
|
|
ya_optarg = NULL;
|
|
} else {
|
|
if (ya_optind == argc) {
|
|
ya_optarg = NULL;
|
|
ya_optopt = opt;
|
|
ya_getopt_error(optstring, "%s: option requires an argument -- '%c'\n", argv[0], opt);
|
|
if (optstring[0] == ':') {
|
|
return ':';
|
|
} else {
|
|
return '?';
|
|
}
|
|
}
|
|
ya_optarg = argv[ya_optind];
|
|
ya_optind++;
|
|
}
|
|
} else {
|
|
ya_optarg = ya_optnext + 1;
|
|
ya_optind++;
|
|
}
|
|
ya_optnext = NULL;
|
|
} else {
|
|
ya_optarg = NULL;
|
|
if (ya_optnext[1] == 0) {
|
|
ya_optnext = NULL;
|
|
ya_optind++;
|
|
} else {
|
|
ya_optnext++;
|
|
}
|
|
}
|
|
return opt;
|
|
}
|
|
|
|
static int ya_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct option *longopts, int *longindex, int *long_only_flag)
|
|
{
|
|
char *val = NULL;
|
|
const struct option *opt;
|
|
size_t namelen;
|
|
int idx;
|
|
|
|
for (idx = 0; longopts[idx].name != NULL; idx++) {
|
|
opt = &longopts[idx];
|
|
namelen = strlen(opt->name);
|
|
if (strncmp(arg, opt->name, namelen) == 0) {
|
|
switch (arg[namelen]) {
|
|
case '\0':
|
|
switch (opt->has_arg) {
|
|
case ya_required_argument:
|
|
ya_optind++;
|
|
if (ya_optind == argc) {
|
|
ya_optarg = NULL;
|
|
ya_optopt = opt->val;
|
|
ya_getopt_error(optstring, "%s: option '--%s' requires an argument\n", argv[0], opt->name);
|
|
if (optstring[0] == ':') {
|
|
return ':';
|
|
} else {
|
|
return '?';
|
|
}
|
|
}
|
|
val = argv[ya_optind];
|
|
break;
|
|
}
|
|
goto found;
|
|
case '=':
|
|
if (opt->has_arg == ya_no_argument) {
|
|
const char *hyphens = (argv[ya_optind][1] == '-') ? "--" : "-";
|
|
|
|
ya_optind++;
|
|
ya_optarg = NULL;
|
|
ya_optopt = opt->val;
|
|
ya_getopt_error(optstring, "%s: option '%s%s' doesn't allow an argument\n", argv[0], hyphens, opt->name);
|
|
return '?';
|
|
}
|
|
val = arg + namelen + 1;
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
if (long_only_flag) {
|
|
*long_only_flag = 1;
|
|
} else {
|
|
ya_getopt_error(optstring, "%s: unrecognized option '%s'\n", argv[0], argv[ya_optind]);
|
|
ya_optind++;
|
|
}
|
|
return '?';
|
|
found:
|
|
ya_optarg = val;
|
|
ya_optind++;
|
|
if (opt->flag) {
|
|
*opt->flag = opt->val;
|
|
}
|
|
if (longindex) {
|
|
*longindex = idx;
|
|
}
|
|
return opt->flag ? 0 : opt->val;
|
|
}
|