1 /* -*- indent-tabs-mode: nil -*-
3 * ya_getopt - Yet another getopt
4 * https://github.com/kubo/ya_getopt
6 * Copyright 2015 Kubo Takehiro <kubo@jiubao.org>
8 * Redistribution and use in source and binary forms, with or without modification, are
9 * permitted provided that the following conditions are met:
11 * 1. Redistributions of source code must retain the above copyright notice, this list of
12 * conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
15 * of conditions and the following disclaimer in the documentation and/or other materials
16 * provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * The views and conclusions contained in the software and documentation are those of the
29 * authors and should not be interpreted as representing official policies, either expressed
30 * or implied, of the authors.
37 #include "ya_getopt.h"
39 char *ya_optarg = NULL;
43 static char *ya_optnext = NULL;
44 static int posixly_correct = -1;
45 static int handle_nonopt_argv = 0;
47 static void ya_getopt_error(const char *optstring, const char *format, ...);
48 static void check_gnu_extension(const char *optstring);
49 static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only);
50 static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only);
51 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);
53 static void ya_getopt_error(const char *optstring, const char *format, ...)
55 if (ya_opterr && optstring[0] != ':') {
58 vfprintf(stderr, format, ap);
63 static void check_gnu_extension(const char *optstring)
65 if (optstring[0] == '+' || getenv("POSIXLY_CORRECT") != NULL) {
70 if (optstring[0] == '-') {
71 handle_nonopt_argv = 1;
73 handle_nonopt_argv = 0;
77 static int is_option(const char *arg)
79 return arg[0] == '-' && arg[1] != '\0';
82 int ya_getopt(int argc, char * const argv[], const char *optstring)
84 return ya_getopt_internal(argc, argv, optstring, NULL, NULL, 0);
87 int ya_getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
89 return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 0);
92 int ya_getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
94 return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 1);
97 static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only)
99 static int start, end;
101 if (ya_optopt == '?') {
105 if (posixly_correct == -1) {
106 check_gnu_extension(optstring);
109 if (ya_optind == 0) {
110 check_gnu_extension(optstring);
115 switch (optstring[0]) {
121 if (ya_optnext == NULL && start != 0) {
122 int last_pos = ya_optind - 1;
124 ya_optind -= end - start;
125 if (ya_optind <= 0) {
128 while (start < end--) {
130 char *arg = argv[end];
132 for (i = end; i < last_pos; i++) {
133 ((char **)argv)[i] = argv[i + 1];
135 ((char const **)argv)[i] = arg;
141 if (ya_optind >= argc) {
145 if (ya_optnext == NULL) {
146 const char *arg = argv[ya_optind];
147 if (!is_option(arg)) {
148 if (handle_nonopt_argv) {
149 ya_optarg = argv[ya_optind++];
152 } else if (posixly_correct) {
159 for (i = ya_optind + 1; i < argc; i++) {
160 if (is_option(argv[i])) {
170 arg = argv[ya_optind];
173 if (strcmp(arg, "--") == 0) {
177 if (longopts != NULL && arg[1] == '-') {
178 return ya_getopt_longopts(argc, argv, argv[ya_optind] + 2, optstring, longopts, longindex, NULL);
182 if (ya_optnext == NULL) {
183 ya_optnext = argv[ya_optind] + 1;
186 int long_only_flag = 0;
187 int rv = ya_getopt_longopts(argc, argv, ya_optnext, optstring, longopts, longindex, &long_only_flag);
188 if (!long_only_flag) {
194 return ya_getopt_shortopts(argc, argv, optstring, long_only);
197 static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only)
199 int opt = *ya_optnext;
200 const char *os = strchr(optstring, opt);
205 ya_getopt_error(optstring, "%s: unrecognized option '-%s'\n", argv[0], ya_optnext);
210 ya_getopt_error(optstring, "%s: invalid option -- '%c'\n", argv[0], opt);
211 if (*(++ya_optnext) == 0) {
219 if (ya_optnext[1] == 0) {
223 /* optional argument */
226 if (ya_optind == argc) {
229 ya_getopt_error(optstring, "%s: option requires an argument -- '%c'\n", argv[0], opt);
230 if (optstring[0] == ':') {
236 ya_optarg = argv[ya_optind];
240 ya_optarg = ya_optnext + 1;
246 if (ya_optnext[1] == 0) {
256 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)
259 const struct option *opt;
263 for (idx = 0; longopts[idx].name != NULL; idx++) {
264 opt = &longopts[idx];
265 namelen = strlen(opt->name);
266 if (strncmp(arg, opt->name, namelen) == 0) {
267 switch (arg[namelen]) {
269 switch (opt->has_arg) {
270 case ya_required_argument:
272 if (ya_optind == argc) {
274 ya_optopt = opt->val;
275 ya_getopt_error(optstring, "%s: option '--%s' requires an argument\n", argv[0], opt->name);
276 if (optstring[0] == ':') {
282 val = argv[ya_optind];
287 if (opt->has_arg == ya_no_argument) {
288 const char *hyphens = (argv[ya_optind][1] == '-') ? "--" : "-";
292 ya_optopt = opt->val;
293 ya_getopt_error(optstring, "%s: option '%s%s' doesn't allow an argument\n", argv[0], hyphens, opt->name);
296 val = arg + namelen + 1;
301 if (long_only_flag) {
304 ya_getopt_error(optstring, "%s: unrecognized option '%s'\n", argv[0], argv[ya_optind]);
312 *opt->flag = opt->val;
317 return opt->flag ? 0 : opt->val;