1 /*
2 * Copyright © 2005-2020 Rich Felker, et al.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 /* This implementation was taken from musl and modified for RGBDS */
25
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <wchar.h>
32
33 #include "extern/getopt.h"
34
35 char *musl_optarg;
36 int musl_optind = 1, musl_opterr = 1, musl_optopt;
37 int musl_optreset = 0;
38 static int musl_optpos;
39
musl_getopt_msg(char const * a,char const * b,char const * c,size_t l)40 static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l)
41 {
42 FILE *f = stderr;
43
44 if (fputs(a, f) >= 0 &&
45 fwrite(b, strlen(b), 1, f) &&
46 fwrite(c, 1, l, f) == l)
47 putc('\n', f);
48 }
49
getopt(int argc,char * argv[],char const * optstring)50 static int getopt(int argc, char *argv[], char const *optstring)
51 {
52 int i;
53 wchar_t c, d;
54 int k, l;
55 char *optchar;
56
57 if (!musl_optind || musl_optreset) {
58 musl_optreset = 0;
59 musl_optpos = 0;
60 musl_optind = 1;
61 }
62
63 if (musl_optind >= argc || !argv[musl_optind])
64 return -1;
65
66 if (argv[musl_optind][0] != '-') {
67 if (optstring[0] == '-') {
68 musl_optarg = argv[musl_optind++];
69 return 1;
70 }
71 return -1;
72 }
73
74 if (!argv[musl_optind][1])
75 return -1;
76
77 if (argv[musl_optind][1] == '-' && !argv[musl_optind][2])
78 return musl_optind++, -1;
79
80 if (!musl_optpos)
81 musl_optpos++;
82 k = mbtowc(&c, argv[musl_optind] + musl_optpos, MB_LEN_MAX);
83 if (k < 0) {
84 k = 1;
85 c = 0xfffd; /* replacement char */
86 }
87 optchar = argv[musl_optind] + musl_optpos;
88 musl_optpos += k;
89
90 if (!argv[musl_optind][musl_optpos]) {
91 musl_optind++;
92 musl_optpos = 0;
93 }
94
95 if (optstring[0] == '-' || optstring[0] == '+')
96 optstring++;
97
98 i = 0;
99 d = 0;
100 do {
101 l = mbtowc(&d, optstring+i, MB_LEN_MAX);
102 if (l > 0)
103 i += l;
104 else
105 i++;
106 } while (l && d != c);
107
108 if (d != c || c == ':') {
109 musl_optopt = c;
110 if (optstring[0] != ':' && musl_opterr)
111 musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
112 return '?';
113 }
114 if (optstring[i] == ':') {
115 musl_optarg = 0;
116 if (optstring[i + 1] != ':' || musl_optpos) {
117 musl_optarg = argv[musl_optind++] + musl_optpos;
118 musl_optpos = 0;
119 }
120 if (musl_optind > argc) {
121 musl_optopt = c;
122 if (optstring[0] == ':')
123 return ':';
124 if (musl_opterr)
125 musl_getopt_msg(argv[0], ": option requires an argument: ",
126 optchar, k);
127 return '?';
128 }
129 }
130 return c;
131 }
132
permute(char ** argv,int dest,int src)133 static void permute(char **argv, int dest, int src)
134 {
135 char *tmp = argv[src];
136 int i;
137
138 for (i = src; i > dest; i--)
139 argv[i] = argv[i-1];
140 argv[dest] = tmp;
141 }
142
143 static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
144 const struct option *longopts, int *idx, int longonly);
145
musl_getopt_long(int argc,char ** argv,char const * optstring,const struct option * longopts,int * idx,int longonly)146 static int musl_getopt_long(int argc, char **argv, char const *optstring,
147 const struct option *longopts, int *idx, int longonly)
148 {
149 int ret, skipped, resumed;
150
151 if (!musl_optind || musl_optreset) {
152 musl_optreset = 0;
153 musl_optpos = 0;
154 musl_optind = 1;
155 }
156
157 if (musl_optind >= argc || !argv[musl_optind])
158 return -1;
159
160 skipped = musl_optind;
161 if (optstring[0] != '+' && optstring[0] != '-') {
162 int i;
163 for (i = musl_optind; ; i++) {
164 if (i >= argc || !argv[i])
165 return -1;
166 if (argv[i][0] == '-' && argv[i][1])
167 break;
168 }
169 musl_optind = i;
170 }
171 resumed = musl_optind;
172 ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
173 if (resumed > skipped) {
174 int i, cnt = musl_optind - resumed;
175
176 for (i = 0; i < cnt; i++)
177 permute(argv, skipped, musl_optind - 1);
178 musl_optind = skipped + cnt;
179 }
180 return ret;
181 }
182
musl_getopt_long_core(int argc,char ** argv,char const * optstring,const struct option * longopts,int * idx,int longonly)183 static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
184 const struct option *longopts, int *idx, int longonly)
185 {
186 musl_optarg = 0;
187 if (longopts && argv[musl_optind][0] == '-' &&
188 ((longonly && argv[musl_optind][1] && argv[musl_optind][1] != '-') ||
189 (argv[musl_optind][1] == '-' && argv[musl_optind][2]))) {
190 int colon = optstring[optstring[0] == '+' || optstring[0] == '-'] == ':';
191 int i, cnt, match = 0;
192 char *arg = 0, *opt, *start = argv[musl_optind] + 1;
193
194 for (cnt = i = 0; longopts[i].name; i++) {
195 char const *name = longopts[i].name;
196
197 opt = start;
198 if (*opt == '-')
199 opt++;
200 while (*opt && *opt != '=' && *opt == *name) {
201 name++;
202 opt++;
203 }
204 if (*opt && *opt != '=')
205 continue;
206 arg = opt;
207 match = i;
208 if (!*name) {
209 cnt = 1;
210 break;
211 }
212 cnt++;
213 }
214 if (cnt == 1 && longonly && arg - start == mblen(start, MB_LEN_MAX)) {
215 int l = arg - start;
216
217 for (i = 0; optstring[i]; i++) {
218 int j = 0;
219
220 while (j < l && start[j] == optstring[i + j])
221 j++;
222 if (j == l) {
223 cnt++;
224 break;
225 }
226 }
227 }
228 if (cnt == 1) {
229 i = match;
230 opt = arg;
231 musl_optind++;
232 if (*opt == '=') {
233 if (!longopts[i].has_arg) {
234 musl_optopt = longopts[i].val;
235 if (colon || !musl_opterr)
236 return '?';
237 musl_getopt_msg(argv[0],
238 ": option does not take an argument: ",
239 longopts[i].name,
240 strlen(longopts[i].name));
241 return '?';
242 }
243 musl_optarg = opt + 1;
244 } else if (longopts[i].has_arg == required_argument) {
245 musl_optarg = argv[musl_optind];
246 if (!musl_optarg) {
247 musl_optopt = longopts[i].val;
248 if (colon)
249 return ':';
250 if (!musl_opterr)
251 return '?';
252 musl_getopt_msg(argv[0],
253 ": option requires an argument: ",
254 longopts[i].name,
255 strlen(longopts[i].name));
256 return '?';
257 }
258 musl_optind++;
259 }
260 if (idx)
261 *idx = i;
262 if (longopts[i].flag) {
263 *longopts[i].flag = longopts[i].val;
264 return 0;
265 }
266 return longopts[i].val;
267 }
268 if (argv[musl_optind][1] == '-') {
269 musl_optopt = 0;
270 if (!colon && musl_opterr)
271 musl_getopt_msg(argv[0], cnt ?
272 ": option is ambiguous: " :
273 ": unrecognized option: ",
274 argv[musl_optind] + 2,
275 strlen(argv[musl_optind] + 2));
276 musl_optind++;
277 return '?';
278 }
279 }
280 return getopt(argc, argv, optstring);
281 }
282
musl_getopt_long_only(int argc,char ** argv,char const * optstring,const struct option * longopts,int * idx)283 int musl_getopt_long_only(int argc, char **argv, char const *optstring,
284 const struct option *longopts, int *idx)
285 {
286 return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
287 }
288