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