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