1
2
3
4
5
6
7
8
9
10
11
12#include "toys.h"
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95struct opts {
96 struct opts *next;
97 long *arg;
98 int c;
99 int flags;
100 unsigned long long dex[3];
101 char type;
102 union {
103 long l;
104 FLOAT f;
105 } val[3];
106};
107
108
109
110
111
112struct longopts {
113 struct longopts *next;
114 struct opts *opt;
115 char *str;
116 int len;
117};
118
119
120struct getoptflagstate
121{
122 int argc, minargs, maxargs;
123 char *arg;
124 struct opts *opts;
125 struct longopts *longopts;
126 int noerror, nodash_now, stopearly;
127 unsigned excludes, requires;
128};
129
130
131static int gotflag(struct getoptflagstate *gof, struct opts *opt)
132{
133 int type;
134
135
136 if (!opt) {
137 if (gof->noerror) return 1;
138 help_exit("Unknown option %s", gof->arg);
139 }
140
141
142 if (toys.optflags & opt->dex[0]) {
143 struct opts *clr;
144 unsigned long long i = 1;
145
146
147 for (clr=gof->opts, i=1; clr; clr = clr->next, i<<=1)
148 if (clr->arg && (i & toys.optflags & opt->dex[0])) *clr->arg = 0;
149 toys.optflags &= ~opt->dex[0];
150 }
151
152
153 toys.optflags |= opt->dex[1];
154 gof->excludes |= opt->dex[2];
155 if (opt->flags&2) gof->stopearly=2;
156
157 if (toys.optflags & gof->excludes) {
158 struct opts *bad;
159 unsigned i = 1;
160
161 for (bad=gof->opts, i=1; bad ;bad = bad->next, i<<=1) {
162 if (opt == bad || !(i & toys.optflags)) continue;
163 if (toys.optflags & bad->dex[2]) break;
164 }
165 if (bad) help_exit("No '%c' with '%c'", opt->c, bad->c);
166 }
167
168
169 if (!gof->arg) {
170 if (opt->flags & 8) return 0;
171 gof->arg = "";
172 } else gof->arg++;
173 type = opt->type;
174
175 if (type == '@') ++*(opt->arg);
176 else if (type) {
177 char *arg = gof->arg;
178
179
180
181
182
183 if (gof->nodash_now || (!arg[0] && !(opt->flags & 8)))
184 arg = toys.argv[++gof->argc];
185 if (!arg) {
186 char *s = "Missing argument to ";
187 struct longopts *lo;
188
189 if (opt->c != -1) help_exit("%s-%c", s, opt->c);
190
191 for (lo = gof->longopts; lo->opt != opt; lo = lo->next);
192 help_exit("%s--%.*s", s, lo->len, lo->str);
193 }
194
195 if (type == ':') *(opt->arg) = (long)arg;
196 else if (type == '*') {
197 struct arg_list **list;
198
199 list = (struct arg_list **)opt->arg;
200 while (*list) list=&((*list)->next);
201 *list = xzalloc(sizeof(struct arg_list));
202 (*list)->arg = arg;
203 } else if (type == '#' || type == '-') {
204 long l = atolx(arg);
205 if (type == '-' && !ispunct(*arg)) l*=-1;
206 if (l < opt->val[0].l) help_exit("-%c < %ld", opt->c, opt->val[0].l);
207 if (l > opt->val[1].l) help_exit("-%c > %ld", opt->c, opt->val[1].l);
208
209 *(opt->arg) = l;
210 } else if (CFG_TOYBOX_FLOAT && type == '.') {
211 FLOAT *f = (FLOAT *)(opt->arg);
212
213 *f = strtod(arg, &arg);
214 if (opt->val[0].l != LONG_MIN && *f < opt->val[0].f)
215 help_exit("-%c < %lf", opt->c, (double)opt->val[0].f);
216 if (opt->val[1].l != LONG_MAX && *f > opt->val[1].f)
217 help_exit("-%c > %lf", opt->c, (double)opt->val[1].f);
218 }
219
220 if (!gof->nodash_now) gof->arg = "";
221 }
222
223 return 0;
224}
225
226
227
228void parse_optflaglist(struct getoptflagstate *gof)
229{
230 char *options = toys.which->options;
231 long *nextarg = (long *)&this;
232 struct opts *new = 0;
233 int idx;
234
235
236 memset(gof, 0, sizeof(struct getoptflagstate));
237 gof->maxargs = INT_MAX;
238 if (!options) return;
239
240
241 for (;;) {
242 if (*options == '^') gof->stopearly++;
243 else if (*options == '<') gof->minargs=*(++options)-'0';
244 else if (*options == '>') gof->maxargs=*(++options)-'0';
245 else if (*options == '?') gof->noerror++;
246 else if (*options == '&') toys.optflags |= FLAGS_NODASH;
247 else break;
248 options++;
249 }
250
251
252
253 if (!*options) gof->stopearly++;
254 while (*options) {
255 char *temp;
256
257
258 if (*options == '[') break;
259
260
261 if (!new) {
262 new = xzalloc(sizeof(struct opts));
263 new->next = gof->opts;
264 gof->opts = new;
265 new->val[0].l = LONG_MIN;
266 new->val[1].l = LONG_MAX;
267 }
268
269
270 if (*options == '(' && new->c != -1) {
271 char *end;
272 struct longopts *lo;
273
274
275 for (end = ++options; *end && *end != ')'; end++);
276 if (CFG_TOYBOX_DEBUG && !*end) error_exit("(longopt) didn't end");
277
278
279 lo = xmalloc(sizeof(struct longopts));
280 lo->next = gof->longopts;
281 lo->opt = new;
282 lo->str = options;
283 lo->len = end-options;
284 gof->longopts = lo;
285 options = ++end;
286
287
288 if (!new->c) new->c = -1;
289
290 continue;
291
292
293
294 } else if (strchr(":*#@.-", *options)) {
295 if (CFG_TOYBOX_DEBUG && new->type)
296 error_exit("multiple types %c:%c%c", new->c, new->type, *options);
297 new->type = *options;
298 } else if (-1 != (idx = stridx("|^ ;", *options))) new->flags |= 1<<idx;
299
300 else if (-1 != (idx = stridx("<>=", *options))) {
301 if (new->type == '#') {
302 long l = strtol(++options, &temp, 10);
303 if (temp != options) new->val[idx].l = l;
304 } else if (CFG_TOYBOX_FLOAT && new->type == '.') {
305 FLOAT f = strtod(++options, &temp);
306 if (temp != options) new->val[idx].f = f;
307 } else error_exit("<>= only after .#");
308 options = --temp;
309
310
311
312
313
314 } else if (new->c) {
315 new = 0;
316 continue;
317
318
319 } else new->c = *options;
320
321 options++;
322 }
323
324
325
326 idx = 0;
327 for (new = gof->opts; new; new = new->next) {
328 unsigned long long u = 1L<<idx++;
329
330 if (new->c == 1) new->c = 0;
331 new->dex[1] = u;
332 if (new->flags & 1) gof->requires |= u;
333 if (new->type) {
334 new->arg = (void *)nextarg;
335 *(nextarg++) = new->val[2].l;
336 }
337 }
338
339
340 while (*options) {
341 unsigned bits = 0;
342
343 if (CFG_TOYBOX_DEBUG && *options != '[') error_exit("trailing %s", options);
344
345 idx = stridx("-+!", *++options);
346 if (CFG_TOYBOX_DEBUG && idx == -1) error_exit("[ needs +-!");
347 if (CFG_TOYBOX_DEBUG && (options[1] == ']' || !options[1]))
348 error_exit("empty []");
349
350
351 while (*options++ != ']') {
352 struct opts *opt;
353 int i;
354
355 if (CFG_TOYBOX_DEBUG && !*options) error_exit("[ without ]");
356
357 for (i=0, opt = gof->opts; ; i++, opt = opt->next) {
358 if (*options == ']') {
359 if (!opt) break;
360 if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i);
361 } else {
362 if (*options==1) break;
363 if (CFG_TOYBOX_DEBUG && !opt)
364 error_exit("[] unknown target %c", *options);
365 if (opt->c == *options) {
366 bits |= 1<<i;
367 break;
368 }
369 }
370 }
371 }
372 }
373}
374
375
376
377void get_optflags(void)
378{
379 struct getoptflagstate gof;
380 struct opts *catch;
381 unsigned long long saveflags;
382 char *letters[]={"s",""};
383
384
385
386
387
388 saveflags = 0;
389 while (toys.argv[saveflags++]);
390 toys.optargs = xzalloc(sizeof(char *)*saveflags);
391
392 parse_optflaglist(&gof);
393
394
395 for (gof.argc=1; toys.argv[gof.argc]; gof.argc++) {
396 gof.arg = toys.argv[gof.argc];
397 catch = NULL;
398
399
400 if (gof.stopearly>1) goto notflag;
401
402 gof.nodash_now = 0;
403
404
405 if (*gof.arg == '-') {
406
407
408 if (!gof.arg[1]) goto notflag;
409 gof.arg++;
410 if (*gof.arg=='-') {
411 struct longopts *lo;
412
413 gof.arg++;
414
415 if (!*gof.arg) {
416 gof.stopearly += 2;
417 continue;
418 }
419
420
421 for (lo = gof.longopts; lo; lo = lo->next) {
422 if (!strncmp(gof.arg, lo->str, lo->len)) {
423 if (!gof.arg[lo->len]) gof.arg = 0;
424 else if (gof.arg[lo->len] == '=' && lo->opt->type)
425 gof.arg += lo->len;
426 else continue;
427
428 catch = lo->opt;
429 break;
430 }
431 }
432
433
434 if (!lo && gof.noerror) {
435 gof.arg -= 2;
436 goto notflag;
437 }
438
439
440 gotflag(&gof, catch);
441 continue;
442 }
443
444
445 } else {
446 if ((toys.optflags & FLAGS_NODASH) && gof.argc == 1) gof.nodash_now = 1;
447 else goto notflag;
448 }
449
450
451
452 saveflags = toys.optflags;
453 while (*gof.arg) {
454
455
456 for (catch = gof.opts; catch; catch = catch->next)
457 if (*gof.arg == catch->c)
458 if (!((catch->flags&4) && gof.arg[1])) break;
459
460
461 if (gotflag(&gof, catch) ) {
462 toys.optflags = saveflags;
463 gof.arg = toys.argv[gof.argc];
464 goto notflag;
465 }
466 }
467 continue;
468
469
470notflag:
471 if (gof.stopearly) gof.stopearly++;
472 toys.optargs[toys.optc++] = toys.argv[gof.argc];
473 }
474
475
476 if (toys.optc<gof.minargs)
477 help_exit("Need%s %d argument%s", letters[!!(gof.minargs-1)],
478 gof.minargs, letters[!(gof.minargs-1)]);
479 if (toys.optc>gof.maxargs)
480 help_exit("Max %d argument%s", gof.maxargs, letters[!(gof.maxargs-1)]);
481 if (gof.requires && !(gof.requires & toys.optflags)) {
482 struct opts *req;
483 char needs[32], *s = needs;
484
485 for (req = gof.opts; req; req = req->next)
486 if (req->flags & 1) *(s++) = req->c;
487 *s = 0;
488
489 help_exit("Needs %s-%s", s[1] ? "one of " : "", needs);
490 }
491
492 if (CFG_TOYBOX_FREE) {
493 llist_traverse(gof.opts, free);
494 llist_traverse(gof.longopts, free);
495 }
496}
497