Skip to content

Commit e3bcbcb

Browse files
committed
Add ya_getopt, a 2-clause-BSD-licensed implementation of getopt_long()
This satisfies the dependency for MSVC. Systems with Glibc, or a suitable getopt_long() available in libc (e.g. macOS/*BSD) will continue using that.
1 parent aa6edca commit e3bcbcb

File tree

8 files changed

+459
-6
lines changed

8 files changed

+459
-6
lines changed

libvmaf/meson.build

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ if not cc.check_header('stdatomic.h')
4646
endif
4747
endif
4848

49+
if cc.check_header('getopt.h', args: test_args)
50+
getopt_dependency = declare_dependency(compile_args : '-DHAVE_GETOPT_H')
51+
else
52+
getopt_dependency = declare_dependency(
53+
compile_args : '-DHAVE_GETOPT_H',
54+
sources: ['src/compat/getopt/getopt.c'],
55+
include_directories : include_directories('src/compat/getopt'),
56+
)
57+
endif
58+
4959
subdir('include')
5060
subdir('src')
5161
subdir('tools')

libvmaf/src/compat/getopt/README.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
ya_getopt - Yet another getopt
2+
==============================
3+
4+
What is ya_getopt.
5+
------------------
6+
7+
Ya_getopt is a drop-in replacement of [GNU C library getopt](http://man7.org/linux/man-pages/man3/getopt.3.html).
8+
`getopt()`, `getopt_long()` and `getopt_long_only()` are implemented excluding the following GNU extension features.
9+
10+
1. If *optstring* contains **W** followed by a semicolon, then **-W** **foo** is
11+
treated as the long option **--foo**.
12+
13+
2. \_\<PID>\_GNU\_nonoption\_argv\_flags\_
14+
15+
The license is 2-clause BSD-style license. You can use the Linux getopt compatible function
16+
under Windows, Solaris and so on without having to worry about license issue.
17+
18+
Note for contributors
19+
---------------------
20+
21+
Don't send me a patch if you have looked at GNU C library getopt source code.
22+
That's because I made this with clean room design to avoid the influence of the GNU LGPL.
23+
24+
Please make a test script passed by the GNU C library getopt but not by ya_getopt instead.
25+
26+
License
27+
-------
28+
29+
2-clause BSD-style license
30+
31+
Other getopt functions
32+
----------------------
33+
34+
* [public domain AT&T getopt](https://www.google.co.jp/search?q=public+domain+at%26t+getopt) public domain, no getopt_long, no getopt_long_only, no argv permutation
35+
* [Free Getopt](http://freegetopt.sourceforge.net/) 3-clause BSD-style licence, no getopt_long, no getopt_long_only
36+
* [getopt_port](https://github.com/kimgr/getopt_port/) 3-clause BSD-style licence, no getopt_long_only, no argv permutation
37+
* [XGetopt - A Unix-compatible getopt() for MFC and Win32](http://www.codeproject.com/Articles/1940/XGetopt-A-Unix-compatible-getopt-for-MFC-and-Win32)
38+
* [Full getopt Port for Unicode and Multibyte Microsoft Visual C, C++, or MFC Projects](http://www.codeproject.com/Articles/157001/Full-getopt-Port-for-Unicode-and-Multibyte-Microso) LGPL

libvmaf/src/compat/getopt/getopt.c

+318
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/* -*- indent-tabs-mode: nil -*-
2+
*
3+
* ya_getopt - Yet another getopt
4+
* https://github.com/kubo/ya_getopt
5+
*
6+
* Copyright 2015 Kubo Takehiro <[email protected]>
7+
*
8+
* Redistribution and use in source and binary forms, with or without modification, are
9+
* permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice, this list of
12+
* conditions and the following disclaimer.
13+
*
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.
17+
*
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.
27+
*
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.
31+
*
32+
*/
33+
#include <stdio.h>
34+
#include <stdarg.h>
35+
#include <stdlib.h>
36+
#include <string.h>
37+
#include "getopt.h"
38+
39+
char *ya_optarg = NULL;
40+
int ya_optind = 1;
41+
int ya_opterr = 1;
42+
int ya_optopt = '?';
43+
static char *ya_optnext = NULL;
44+
static int posixly_correct = -1;
45+
static int handle_nonopt_argv = 0;
46+
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);
52+
53+
static void ya_getopt_error(const char *optstring, const char *format, ...)
54+
{
55+
if (ya_opterr && optstring[0] != ':') {
56+
va_list ap;
57+
va_start(ap, format);
58+
vfprintf(stderr, format, ap);
59+
va_end(ap);
60+
}
61+
}
62+
63+
static void check_gnu_extension(const char *optstring)
64+
{
65+
if (optstring[0] == '+' || getenv("POSIXLY_CORRECT") != NULL) {
66+
posixly_correct = 1;
67+
} else {
68+
posixly_correct = 0;
69+
}
70+
if (optstring[0] == '-') {
71+
handle_nonopt_argv = 1;
72+
} else {
73+
handle_nonopt_argv = 0;
74+
}
75+
}
76+
77+
static int is_option(const char *arg)
78+
{
79+
return arg[0] == '-' && arg[1] != '\0';
80+
}
81+
82+
int ya_getopt(int argc, char * const argv[], const char *optstring)
83+
{
84+
return ya_getopt_internal(argc, argv, optstring, NULL, NULL, 0);
85+
}
86+
87+
int ya_getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
88+
{
89+
return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 0);
90+
}
91+
92+
int ya_getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
93+
{
94+
return ya_getopt_internal(argc, argv, optstring, longopts, longindex, 1);
95+
}
96+
97+
static int ya_getopt_internal(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, int long_only)
98+
{
99+
static int start, end;
100+
101+
if (ya_optopt == '?') {
102+
ya_optopt = 0;
103+
}
104+
105+
if (posixly_correct == -1) {
106+
check_gnu_extension(optstring);
107+
}
108+
109+
if (ya_optind == 0) {
110+
check_gnu_extension(optstring);
111+
ya_optind = 1;
112+
ya_optnext = NULL;
113+
}
114+
115+
switch (optstring[0]) {
116+
case '+':
117+
case '-':
118+
optstring++;
119+
}
120+
121+
if (ya_optnext == NULL && start != 0) {
122+
int last_pos = ya_optind - 1;
123+
124+
ya_optind -= end - start;
125+
if (ya_optind <= 0) {
126+
ya_optind = 1;
127+
}
128+
while (start < end--) {
129+
int i;
130+
char *arg = argv[end];
131+
132+
for (i = end; i < last_pos; i++) {
133+
((char **)argv)[i] = argv[i + 1];
134+
}
135+
((char const **)argv)[i] = arg;
136+
last_pos--;
137+
}
138+
start = 0;
139+
}
140+
141+
if (ya_optind >= argc) {
142+
ya_optarg = NULL;
143+
return -1;
144+
}
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++];
150+
start = 0;
151+
return 1;
152+
} else if (posixly_correct) {
153+
ya_optarg = NULL;
154+
return -1;
155+
} else {
156+
int i;
157+
158+
start = ya_optind;
159+
for (i = ya_optind + 1; i < argc; i++) {
160+
if (is_option(argv[i])) {
161+
end = i;
162+
break;
163+
}
164+
}
165+
if (i == argc) {
166+
ya_optarg = NULL;
167+
return -1;
168+
}
169+
ya_optind = i;
170+
arg = argv[ya_optind];
171+
}
172+
}
173+
if (strcmp(arg, "--") == 0) {
174+
ya_optind++;
175+
return -1;
176+
}
177+
if (longopts != NULL && arg[1] == '-') {
178+
return ya_getopt_longopts(argc, argv, argv[ya_optind] + 2, optstring, longopts, longindex, NULL);
179+
}
180+
}
181+
182+
if (ya_optnext == NULL) {
183+
ya_optnext = argv[ya_optind] + 1;
184+
}
185+
if (long_only) {
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) {
189+
ya_optnext = NULL;
190+
return rv;
191+
}
192+
}
193+
194+
return ya_getopt_shortopts(argc, argv, optstring, long_only);
195+
}
196+
197+
static int ya_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only)
198+
{
199+
int opt = *ya_optnext;
200+
const char *os = strchr(optstring, opt);
201+
202+
if (os == NULL) {
203+
ya_optarg = NULL;
204+
if (long_only) {
205+
ya_getopt_error(optstring, "%s: unrecognized option '-%s'\n", argv[0], ya_optnext);
206+
ya_optind++;
207+
ya_optnext = NULL;
208+
} else {
209+
ya_optopt = opt;
210+
ya_getopt_error(optstring, "%s: invalid option -- '%c'\n", argv[0], opt);
211+
if (*(++ya_optnext) == 0) {
212+
ya_optind++;
213+
ya_optnext = NULL;
214+
}
215+
}
216+
return '?';
217+
}
218+
if (os[1] == ':') {
219+
if (ya_optnext[1] == 0) {
220+
ya_optind++;
221+
ya_optnext = NULL;
222+
if (os[2] == ':') {
223+
/* optional argument */
224+
ya_optarg = NULL;
225+
} else {
226+
if (ya_optind == argc) {
227+
ya_optarg = NULL;
228+
ya_optopt = opt;
229+
ya_getopt_error(optstring, "%s: option requires an argument -- '%c'\n", argv[0], opt);
230+
if (optstring[0] == ':') {
231+
return ':';
232+
} else {
233+
return '?';
234+
}
235+
}
236+
ya_optarg = argv[ya_optind];
237+
ya_optind++;
238+
}
239+
} else {
240+
ya_optarg = ya_optnext + 1;
241+
ya_optind++;
242+
}
243+
ya_optnext = NULL;
244+
} else {
245+
ya_optarg = NULL;
246+
if (ya_optnext[1] == 0) {
247+
ya_optnext = NULL;
248+
ya_optind++;
249+
} else {
250+
ya_optnext++;
251+
}
252+
}
253+
return opt;
254+
}
255+
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)
257+
{
258+
char *val = NULL;
259+
const struct option *opt;
260+
size_t namelen;
261+
int idx;
262+
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]) {
268+
case '\0':
269+
switch (opt->has_arg) {
270+
case ya_required_argument:
271+
ya_optind++;
272+
if (ya_optind == argc) {
273+
ya_optarg = NULL;
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] == ':') {
277+
return ':';
278+
} else {
279+
return '?';
280+
}
281+
}
282+
val = argv[ya_optind];
283+
break;
284+
}
285+
goto found;
286+
case '=':
287+
if (opt->has_arg == ya_no_argument) {
288+
const char *hyphens = (argv[ya_optind][1] == '-') ? "--" : "-";
289+
290+
ya_optind++;
291+
ya_optarg = NULL;
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);
294+
return '?';
295+
}
296+
val = arg + namelen + 1;
297+
goto found;
298+
}
299+
}
300+
}
301+
if (long_only_flag) {
302+
*long_only_flag = 1;
303+
} else {
304+
ya_getopt_error(optstring, "%s: unrecognized option '%s'\n", argv[0], argv[ya_optind]);
305+
ya_optind++;
306+
}
307+
return '?';
308+
found:
309+
ya_optarg = val;
310+
ya_optind++;
311+
if (opt->flag) {
312+
*opt->flag = opt->val;
313+
}
314+
if (longindex) {
315+
*longindex = idx;
316+
}
317+
return opt->flag ? 0 : opt->val;
318+
}

0 commit comments

Comments
 (0)