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
97struct opts {
98 struct opts *next;
99 long *arg;
100 int c;
101 int flags;
102 unsigned long long dex[3];
103 char type;
104 union {
105 long l;
106 FLOAT f;
107 } val[3];
108};
109
110
111
112
113
114struct longopts {
115 struct longopts *next;
116 struct opts *opt;
117 char *str;
118 int len;
119};
120
121
122struct getoptflagstate
123{
124 int argc, minargs, maxargs;
125 char *arg;
126 struct opts *opts;
127 struct longopts *longopts;
128 int noerror, nodash_now, stopearly;
129 unsigned excludes, requires;
130};
131
132
133static int gotflag(struct getoptflagstate *gof, struct opts *opt)
134{
135 int type;
136
137
138 if (!opt) {
139 if (gof->noerror) return 1;
140 help_exit("Unknown option %s", gof->arg);
141 }
142
143
144 if (toys.optflags & opt->dex[0]) {
145 struct opts *clr;
146 unsigned long long i = 1;
147
148
149 for (clr=gof->opts, i=1; clr; clr = clr->next, i<<=1)
150 if (clr->arg && (i & toys.optflags & opt->dex[0])) *clr->arg = 0;
151 toys.optflags &= ~opt->dex[0];
152 }
153
154
155 toys.optflags |= opt->dex[1];
156 gof->excludes |= opt->dex[2];
157 if (opt->flags&2) gof->stopearly=2;
158
159 if (toys.optflags & gof->excludes) {
160 struct opts *bad;
161 unsigned i = 1;
162
163 for (bad=gof->opts, i=1; bad ;bad = bad->next, i<<=1) {
164 if (opt == bad || !(i & toys.optflags)) continue;
165 if (toys.optflags & bad->dex[2]) break;
166 }
167 if (bad) help_exit("No '%c' with '%c'", opt->c, bad->c);
168 }
169
170
171 if (!gof->arg) {
172 if (opt->flags & 8) return 0;
173 gof->arg = "";
174 } else gof->arg++;
175 type = opt->type;
176
177 if (type == '@') ++*(opt->arg);
178 else if (type) {
179 char *arg = gof->arg;
180
181
182
183
184
185 if (gof->nodash_now || (!arg[0] && !(opt->flags & 8)))
186 arg = toys.argv[++gof->argc];
187 if (!arg) {
188 char *s = "Missing argument to ";
189 struct longopts *lo;
190
191 if (opt->c != -1) help_exit("%s-%c", s, opt->c);
192
193 for (lo = gof->longopts; lo->opt != opt; lo = lo->next);
194 help_exit("%s--%.*s", s, lo->len, lo->str);
195 }
196
197 if (type == ':') *(opt->arg) = (long)arg;
198 else if (type == '*') {
199 struct arg_list **list;
200
201 list = (struct arg_list **)opt->arg;
202 while (*list) list=&((*list)->next);
203 *list = xzalloc(sizeof(struct arg_list));
204 (*list)->arg = arg;
205 } else if (type == '#' || type == '-') {
206 long l = atolx(arg);
207 if (type == '-' && !ispunct(*arg)) l*=-1;
208 if (l < opt->val[0].l) help_exit("-%c < %ld", opt->c, opt->val[0].l);
209 if (l > opt->val[1].l) help_exit("-%c > %ld", opt->c, opt->val[1].l);
210
211 *(opt->arg) = l;
212 } else if (CFG_TOYBOX_FLOAT && type == '.') {
213 FLOAT *f = (FLOAT *)(opt->arg);
214
215 *f = strtod(arg, &arg);
216 if (opt->val[0].l != LONG_MIN && *f < opt->val[0].f)
217 help_exit("-%c < %lf", opt->c, (double)opt->val[0].f);
218 if (opt->val[1].l != LONG_MAX && *f > opt->val[1].f)
219 help_exit("-%c > %lf", opt->c, (double)opt->val[1].f);
220 } else if (type=='%') *(opt->arg) = xparsemillitime(arg);
221
222 if (!gof->nodash_now) gof->arg = "";
223 }
224
225 return 0;
226}
227
228
229
230void parse_optflaglist(struct getoptflagstate *gof)
231{
232 char *options = toys.which->options;
233 long *nextarg = (long *)&this;
234 struct opts *new = 0;
235 int idx;
236
237
238 memset(gof, 0, sizeof(struct getoptflagstate));
239 gof->maxargs = INT_MAX;
240 if (!options) return;
241
242
243 for (;;) {
244 if (*options == '^') gof->stopearly++;
245 else if (*options == '<') gof->minargs=*(++options)-'0';
246 else if (*options == '>') gof->maxargs=*(++options)-'0';
247 else if (*options == '?') gof->noerror++;
248 else if (*options == '&') gof->nodash_now = 1;
249 else break;
250 options++;
251 }
252
253
254
255 if (!*options) gof->stopearly++;
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 = 1L<<idx++;
331
332 if (new->c == 1) 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 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 int i;
356
357 if (CFG_TOYBOX_DEBUG && !*options) error_exit("[ without ]");
358
359 for (i=0, opt = gof->opts; ; i++, opt = opt->next) {
360 if (*options == ']') {
361 if (!opt) break;
362 if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i);
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 |= 1<<i;
369 break;
370 }
371 }
372 }
373 }
374 }
375}
376
377
378
379void get_optflags(void)
380{
381 struct getoptflagstate gof;
382 struct opts *catch;
383 unsigned long long saveflags;
384 char *letters[]={"s",""};
385
386
387
388
389 toys.exitval = toys.which->flags >> 24;
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 toys.exitval = 0;
499
500 if (CFG_TOYBOX_FREE) {
501 llist_traverse(gof.opts, free);
502 llist_traverse(gof.longopts, free);
503 }
504}
505