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
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)
136{
137 int type;
138
139
140 if (!opt) {
141 if (gof->noerror) return 1;
142 help_exit("Unknown option %s", gof->arg);
143 }
144
145
146 if (toys.optflags & opt->dex[0]) {
147 struct opts *clr;
148 unsigned long long i = 1;
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 unsigned i = 1;
164
165 for (bad=gof->opts, i=1; bad ;bad = bad->next, i<<=1) {
166 if (opt == bad || !(i & toys.optflags)) continue;
167 if (toys.optflags & bad->dex[2]) break;
168 }
169 if (bad) help_exit("No '%c' with '%c'", opt->c, bad->c);
170 }
171
172
173 if (!gof->arg) {
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
232void 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;
238
239
240 memset(gof, 0, sizeof(struct getoptflagstate));
241 gof->maxargs = INT_MAX;
242 if (!options) return;
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 break;
252 options++;
253 }
254
255
256
257 if (!*options) gof->stopearly++;
258 while (*options) {
259 char *temp;
260
261
262 if (*options == '[') break;
263
264
265 if (!new) {
266 new = xzalloc(sizeof(struct opts));
267 new->next = gof->opts;
268 gof->opts = new;
269 new->val[0].l = LONG_MIN;
270 new->val[1].l = LONG_MAX;
271 }
272
273
274 if (*options == '(' && new->c != -1) {
275 char *end;
276 struct longopts *lo;
277
278
279 for (end = ++options; *end && *end != ')'; end++);
280 if (CFG_TOYBOX_DEBUG && !*end) error_exit("(longopt) didn't end");
281
282
283 lo = xmalloc(sizeof(struct longopts));
284 lo->next = gof->longopts;
285 lo->opt = new;
286 lo->str = options;
287 lo->len = end-options;
288 gof->longopts = lo;
289 options = ++end;
290
291
292 if (!new->c) new->c = -1;
293
294 continue;
295
296
297
298 } else if (strchr(":*#@.-%", *options)) {
299 if (CFG_TOYBOX_DEBUG && new->type)
300 error_exit("multiple types %c:%c%c", new->c, new->type, *options);
301 new->type = *options;
302 } else if (-1 != (idx = stridx("|^ ;", *options))) new->flags |= 1<<idx;
303
304 else if (-1 != (idx = stridx("<>=", *options))) {
305 if (new->type == '#' || new->type == '%') {
306 long l = strtol(++options, &temp, 10);
307 if (temp != options) new->val[idx].l = l;
308 } else if (CFG_TOYBOX_FLOAT && new->type == '.') {
309 FLOAT f = strtod(++options, &temp);
310 if (temp != options) new->val[idx].f = f;
311 } else error_exit("<>= only after .#%%");
312 options = --temp;
313
314
315
316
317
318 } else if (new->c) {
319 new = 0;
320 continue;
321
322
323 } else new->c = 127&*options;
324
325 options++;
326 }
327
328
329
330 idx = 0;
331 for (new = gof->opts; new; new = new->next) {
332 unsigned long long u = 1L<<idx++;
333
334 if (new->c == 1) new->c = 0;
335 new->dex[1] = u;
336 if (new->flags & 1) gof->requires |= u;
337 if (new->type) {
338 new->arg = (void *)nextarg;
339 *(nextarg++) = new->val[2].l;
340 }
341 }
342
343
344 while (*options) {
345 unsigned bits = 0;
346
347 if (CFG_TOYBOX_DEBUG && *options != '[') error_exit("trailing %s", options);
348
349 idx = stridx("-+!", *++options);
350 if (CFG_TOYBOX_DEBUG && idx == -1) error_exit("[ needs +-!");
351 if (CFG_TOYBOX_DEBUG && (options[1] == ']' || !options[1]))
352 error_exit("empty []");
353
354
355 while (*options++ != ']') {
356 struct opts *opt;
357 int i;
358
359 if (CFG_TOYBOX_DEBUG && !*options) error_exit("[ without ]");
360
361 for (i=0, opt = gof->opts; ; i++, opt = opt->next) {
362 if (*options == ']') {
363 if (!opt) break;
364 if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i);
365 } else {
366 if (*options==1) break;
367 if (CFG_TOYBOX_DEBUG && !opt)
368 error_exit("[] unknown target %c", *options);
369 if (opt->c == *options) {
370 bits |= 1<<i;
371 break;
372 }
373 }
374 }
375 }
376 }
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",""};
387
388
389
390
391
392 saveflags = 0;
393 while (toys.argv[saveflags++]);
394 toys.optargs = xzalloc(sizeof(char *)*saveflags);
395
396 parse_optflaglist(&gof);
397
398 if (toys.argv[1] && toys.argv[1][0] == '-') gof.nodash_now = 0;
399
400
401 for (gof.argc=1; toys.argv[gof.argc]; gof.argc++) {
402 gof.arg = toys.argv[gof.argc];
403 catch = NULL;
404
405
406 if (gof.stopearly>1) goto notflag;
407
408 if (gof.argc>1 || *gof.arg=='-') gof.nodash_now = 0;
409
410
411 if (*gof.arg == '-') {
412
413
414 if (!gof.arg[1]) goto notflag;
415 gof.arg++;
416 if (*gof.arg=='-') {
417 struct longopts *lo;
418
419 gof.arg++;
420
421 if (!*gof.arg) {
422 gof.stopearly += 2;
423 continue;
424 }
425
426
427 for (lo = gof.longopts; lo; lo = lo->next) {
428 if (!strncmp(gof.arg, lo->str, lo->len)) {
429 if (!gof.arg[lo->len]) gof.arg = 0;
430 else if (gof.arg[lo->len] == '=' && lo->opt->type)
431 gof.arg += lo->len;
432 else continue;
433
434 catch = lo->opt;
435 break;
436 }
437 }
438
439
440 if (!lo && gof.noerror) {
441 gof.arg -= 2;
442 goto notflag;
443 }
444
445
446 gotflag(&gof, catch);
447 continue;
448 }
449
450
451 } else {
452 if (gof.nodash_now) toys.optflags |= FLAGS_NODASH;
453 else goto notflag;
454 }
455
456
457
458 saveflags = toys.optflags;
459 while (*gof.arg) {
460
461
462 for (catch = gof.opts; catch; catch = catch->next)
463 if (*gof.arg == catch->c)
464 if (!((catch->flags&4) && gof.arg[1])) break;
465
466
467 if (gotflag(&gof, catch) ) {
468 toys.optflags = saveflags;
469 gof.arg = toys.argv[gof.argc];
470 goto notflag;
471 }
472 }
473 continue;
474
475
476notflag:
477 if (gof.stopearly) gof.stopearly++;
478 toys.optargs[toys.optc++] = toys.argv[gof.argc];
479 }
480
481
482 if (toys.optc<gof.minargs)
483 help_exit("Need%s %d argument%s", letters[!!(gof.minargs-1)],
484 gof.minargs, letters[!(gof.minargs-1)]);
485 if (toys.optc>gof.maxargs)
486 help_exit("Max %d argument%s", gof.maxargs, letters[!(gof.maxargs-1)]);
487 if (gof.requires && !(gof.requires & toys.optflags)) {
488 struct opts *req;
489 char needs[32], *s = needs;
490
491 for (req = gof.opts; req; req = req->next)
492 if (req->flags & 1) *(s++) = req->c;
493 *s = 0;
494
495 help_exit("Needs %s-%s", s[1] ? "one of " : "", needs);
496 }
497
498 if (CFG_TOYBOX_FREE) {
499 llist_traverse(gof.opts, free);
500 llist_traverse(gof.longopts, free);
501 }
502}
503