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