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