parse: add support for unsupported options
[fio.git] / parse.c
... / ...
CommitLineData
1/*
2 * This file contains the ini and command liner parser main.
3 */
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <ctype.h>
8#include <string.h>
9#include <errno.h>
10#include <limits.h>
11#include <stdlib.h>
12#include <math.h>
13#include <float.h>
14
15#include "parse.h"
16#include "debug.h"
17#include "options.h"
18#include "optgroup.h"
19#include "minmax.h"
20#include "lib/ieee754.h"
21#include "lib/pow2.h"
22
23#ifdef CONFIG_ARITHMETIC
24#include "y.tab.h"
25#endif
26
27static struct fio_option *__fio_options;
28
29static int vp_cmp(const void *p1, const void *p2)
30{
31 const struct value_pair *vp1 = p1;
32 const struct value_pair *vp2 = p2;
33
34 return strlen(vp2->ival) - strlen(vp1->ival);
35}
36
37static void posval_sort(struct fio_option *o, struct value_pair *vpmap)
38{
39 const struct value_pair *vp;
40 int entries;
41
42 memset(vpmap, 0, PARSE_MAX_VP * sizeof(struct value_pair));
43
44 for (entries = 0; entries < PARSE_MAX_VP; entries++) {
45 vp = &o->posval[entries];
46 if (!vp->ival || vp->ival[0] == '\0')
47 break;
48
49 memcpy(&vpmap[entries], vp, sizeof(*vp));
50 }
51
52 qsort(vpmap, entries, sizeof(struct value_pair), vp_cmp);
53}
54
55static void show_option_range(struct fio_option *o,
56 size_t (*logger)(const char *format, ...))
57{
58 if (o->type == FIO_OPT_FLOAT_LIST) {
59 if (o->minfp == DBL_MIN && o->maxfp == DBL_MAX)
60 return;
61
62 logger("%20s: min=%f", "range", o->minfp);
63 if (o->maxfp != DBL_MAX)
64 logger(", max=%f", o->maxfp);
65 logger("\n");
66 } else if (!o->posval[0].ival) {
67 if (!o->minval && !o->maxval)
68 return;
69
70 logger("%20s: min=%d", "range", o->minval);
71 if (o->maxval)
72 logger(", max=%d", o->maxval);
73 logger("\n");
74 }
75}
76
77static void show_option_values(struct fio_option *o)
78{
79 int i;
80
81 for (i = 0; i < PARSE_MAX_VP; i++) {
82 const struct value_pair *vp = &o->posval[i];
83
84 if (!vp->ival)
85 continue;
86
87 log_info("%20s: %-10s", i == 0 ? "valid values" : "", vp->ival);
88 if (vp->help)
89 log_info(" %s", vp->help);
90 log_info("\n");
91 }
92
93 if (i)
94 log_info("\n");
95}
96
97static void show_option_help(struct fio_option *o, int is_err)
98{
99 const char *typehelp[] = {
100 "invalid",
101 "string (opt=bla)",
102 "string (opt=bla)",
103 "string with possible k/m/g postfix (opt=4k)",
104 "string with time postfix (opt=10s)",
105 "string (opt=bla)",
106 "string with dual range (opt=1k-4k,4k-8k)",
107 "integer value (opt=100)",
108 "boolean value (opt=1)",
109 "list of floating point values separated by ':' (opt=5.9:7.8)",
110 "no argument (opt)",
111 "deprecated",
112 };
113 size_t (*logger)(const char *format, ...);
114
115 if (is_err)
116 logger = log_err;
117 else
118 logger = log_info;
119
120 if (o->alias)
121 logger("%20s: %s\n", "alias", o->alias);
122
123 logger("%20s: %s\n", "type", typehelp[o->type]);
124 logger("%20s: %s\n", "default", o->def ? o->def : "no default");
125 if (o->prof_name)
126 logger("%20s: only for profile '%s'\n", "valid", o->prof_name);
127 show_option_range(o, logger);
128 show_option_values(o);
129}
130
131static unsigned long long get_mult_time(const char *str, int len,
132 int is_seconds)
133{
134 const char *p = str;
135 char *c;
136 unsigned long long mult = 1;
137
138 /*
139 * Go forward until we hit a non-digit, or +/- sign
140 */
141 while ((p - str) <= len) {
142 if (!isdigit((int) *p) && (*p != '+') && (*p != '-'))
143 break;
144 p++;
145 }
146
147 if (!isalpha((int) *p)) {
148 if (is_seconds)
149 return 1000000UL;
150 else
151 return 1;
152 }
153
154 c = strdup(p);
155 for (int i = 0; i < strlen(c); i++)
156 c[i] = tolower(c[i]);
157
158 if (!strncmp("us", c, 2) || !strncmp("usec", c, 4))
159 mult = 1;
160 else if (!strncmp("ms", c, 2) || !strncmp("msec", c, 4))
161 mult = 1000;
162 else if (!strcmp("s", c))
163 mult = 1000000;
164 else if (!strcmp("m", c))
165 mult = 60 * 1000000UL;
166 else if (!strcmp("h", c))
167 mult = 60 * 60 * 1000000UL;
168 else if (!strcmp("d", c))
169 mult = 24 * 60 * 60 * 1000000UL;
170
171 free(c);
172 return mult;
173}
174
175static int is_separator(char c)
176{
177 switch (c) {
178 case ':':
179 case '-':
180 case ',':
181 case '/':
182 return 1;
183 default:
184 return 0;
185 }
186}
187
188static unsigned long long __get_mult_bytes(const char *p, void *data,
189 int *percent)
190{
191 unsigned int kb_base = fio_get_kb_base(data);
192 unsigned long long ret = 1;
193 unsigned int i, pow = 0, mult = kb_base;
194 char *c;
195
196 if (!p)
197 return 1;
198
199 c = strdup(p);
200
201 for (i = 0; i < strlen(c); i++) {
202 c[i] = tolower(c[i]);
203 if (is_separator(c[i])) {
204 c[i] = '\0';
205 break;
206 }
207 }
208
209 if (!strncmp("pib", c, 3)) {
210 pow = 5;
211 mult = 1000;
212 } else if (!strncmp("tib", c, 3)) {
213 pow = 4;
214 mult = 1000;
215 } else if (!strncmp("gib", c, 3)) {
216 pow = 3;
217 mult = 1000;
218 } else if (!strncmp("mib", c, 3)) {
219 pow = 2;
220 mult = 1000;
221 } else if (!strncmp("kib", c, 3)) {
222 pow = 1;
223 mult = 1000;
224 } else if (!strncmp("p", c, 1) || !strncmp("pb", c, 2))
225 pow = 5;
226 else if (!strncmp("t", c, 1) || !strncmp("tb", c, 2))
227 pow = 4;
228 else if (!strncmp("g", c, 1) || !strncmp("gb", c, 2))
229 pow = 3;
230 else if (!strncmp("m", c, 1) || !strncmp("mb", c, 2))
231 pow = 2;
232 else if (!strncmp("k", c, 1) || !strncmp("kb", c, 2))
233 pow = 1;
234 else if (!strncmp("%", c, 1)) {
235 *percent = 1;
236 free(c);
237 return ret;
238 }
239
240 while (pow--)
241 ret *= (unsigned long long) mult;
242
243 free(c);
244 return ret;
245}
246
247static unsigned long long get_mult_bytes(const char *str, int len, void *data,
248 int *percent)
249{
250 const char *p = str;
251 int digit_seen = 0;
252
253 if (len < 2)
254 return __get_mult_bytes(str, data, percent);
255
256 /*
257 * Go forward until we hit a non-digit, or +/- sign
258 */
259 while ((p - str) <= len) {
260 if (!isdigit((int) *p) &&
261 (((*p != '+') && (*p != '-')) || digit_seen))
262 break;
263 digit_seen |= isdigit((int) *p);
264 p++;
265 }
266
267 if (!isalpha((int) *p) && (*p != '%'))
268 p = NULL;
269
270 return __get_mult_bytes(p, data, percent);
271}
272
273extern int evaluate_arithmetic_expression(const char *buffer, long long *ival,
274 double *dval, double implied_units,
275 int is_time);
276
277/*
278 * Convert string into a floating number. Return 1 for success and 0 otherwise.
279 */
280int str_to_float(const char *str, double *val, int is_time)
281{
282#ifdef CONFIG_ARITHMETIC
283 int rc;
284 long long ival;
285 double dval;
286
287 if (str[0] == '(') {
288 rc = evaluate_arithmetic_expression(str, &ival, &dval, 1.0, is_time);
289 if (!rc) {
290 *val = dval;
291 return 1;
292 }
293 }
294#endif
295 return 1 == sscanf(str, "%lf", val);
296}
297
298/*
299 * convert string into decimal value, noting any size suffix
300 */
301int str_to_decimal(const char *str, long long *val, int kilo, void *data,
302 int is_seconds, int is_time)
303{
304 int len, base;
305 int rc = 1;
306#ifdef CONFIG_ARITHMETIC
307 long long ival;
308 double dval;
309 double implied_units = 1.0;
310#endif
311
312 len = strlen(str);
313 if (!len)
314 return 1;
315
316#ifdef CONFIG_ARITHMETIC
317 if (is_seconds)
318 implied_units = 1000000.0;
319 if (str[0] == '(')
320 rc = evaluate_arithmetic_expression(str, &ival, &dval, implied_units, is_time);
321 if (str[0] == '(' && !rc) {
322 if (!kilo && is_seconds)
323 *val = ival / 1000000LL;
324 else
325 *val = ival;
326 }
327#endif
328
329 if (rc == 1) {
330 if (strstr(str, "0x") || strstr(str, "0X"))
331 base = 16;
332 else
333 base = 10;
334
335 *val = strtoll(str, NULL, base);
336 if (*val == LONG_MAX && errno == ERANGE)
337 return 1;
338 }
339
340 if (kilo) {
341 unsigned long long mult;
342 int perc = 0;
343
344 mult = get_mult_bytes(str, len, data, &perc);
345 if (perc)
346 *val = -1ULL - *val;
347 else
348 *val *= mult;
349 } else
350 *val *= get_mult_time(str, len, is_seconds);
351
352 return 0;
353}
354
355int check_str_bytes(const char *p, long long *val, void *data)
356{
357 return str_to_decimal(p, val, 1, data, 0, 0);
358}
359
360int check_str_time(const char *p, long long *val, int is_seconds)
361{
362 return str_to_decimal(p, val, 0, NULL, is_seconds, 1);
363}
364
365void strip_blank_front(char **p)
366{
367 char *s = *p;
368
369 if (!strlen(s))
370 return;
371 while (isspace((int) *s))
372 s++;
373
374 *p = s;
375}
376
377void strip_blank_end(char *p)
378{
379 char *start = p, *s;
380
381 if (!strlen(p))
382 return;
383
384 s = strchr(p, ';');
385 if (s)
386 *s = '\0';
387 s = strchr(p, '#');
388 if (s)
389 *s = '\0';
390 if (s)
391 p = s;
392
393 s = p + strlen(p);
394 while ((isspace((int) *s) || iscntrl((int) *s)) && (s > start))
395 s--;
396
397 *(s + 1) = '\0';
398}
399
400static int check_range_bytes(const char *str, long *val, void *data)
401{
402 long long __val;
403
404 if (!str_to_decimal(str, &__val, 1, data, 0, 0)) {
405 *val = __val;
406 return 0;
407 }
408
409 return 1;
410}
411
412static int check_int(const char *p, int *val)
413{
414 if (!strlen(p))
415 return 1;
416 if (strstr(p, "0x") || strstr(p, "0X")) {
417 if (sscanf(p, "%x", val) == 1)
418 return 0;
419 } else {
420 if (sscanf(p, "%u", val) == 1)
421 return 0;
422 }
423
424 return 1;
425}
426
427static size_t opt_len(const char *str)
428{
429 char *postfix;
430
431 postfix = strchr(str, ':');
432 if (!postfix)
433 return strlen(str);
434
435 return (int)(postfix - str);
436}
437
438static int str_match_len(const struct value_pair *vp, const char *str)
439{
440 return max(strlen(vp->ival), opt_len(str));
441}
442
443#define val_store(ptr, val, off, or, data, o) \
444 do { \
445 ptr = td_var((data), (o), (off)); \
446 if ((or)) \
447 *ptr |= (val); \
448 else \
449 *ptr = (val); \
450 } while (0)
451
452static int __handle_option(struct fio_option *o, const char *ptr, void *data,
453 int first, int more, int curr)
454{
455 int il=0, *ilp;
456 fio_fp64_t *flp;
457 long long ull, *ullp;
458 long ul1, ul2;
459 double uf;
460 char **cp = NULL;
461 int ret = 0, is_time = 0;
462 const struct value_pair *vp;
463 struct value_pair posval[PARSE_MAX_VP];
464 int i, all_skipped = 1;
465
466 dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name,
467 o->type, ptr);
468
469 if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) {
470 log_err("Option %s requires an argument\n", o->name);
471 return 1;
472 }
473
474 switch (o->type) {
475 case FIO_OPT_STR:
476 case FIO_OPT_STR_MULTI: {
477 fio_opt_str_fn *fn = o->cb;
478
479 posval_sort(o, posval);
480
481 ret = 1;
482 for (i = 0; i < PARSE_MAX_VP; i++) {
483 vp = &posval[i];
484 if (!vp->ival || vp->ival[0] == '\0')
485 continue;
486 all_skipped = 0;
487 if (!ptr)
488 break;
489 if (!strncmp(vp->ival, ptr, str_match_len(vp, ptr))) {
490 ret = 0;
491 if (o->off1)
492 val_store(ilp, vp->oval, o->off1, vp->orval, data, o);
493 continue;
494 }
495 }
496
497 if (ret && !all_skipped)
498 show_option_values(o);
499 else if (fn)
500 ret = fn(data, ptr);
501 break;
502 }
503 case FIO_OPT_STR_VAL_TIME:
504 is_time = 1;
505 case FIO_OPT_INT:
506 case FIO_OPT_STR_VAL: {
507 fio_opt_str_val_fn *fn = o->cb;
508 char tmp[128], *p;
509
510 if (!is_time && o->is_time)
511 is_time = o->is_time;
512
513 tmp[sizeof(tmp) - 1] = '\0';
514 strncpy(tmp, ptr, sizeof(tmp) - 1);
515 p = strchr(tmp, ',');
516 if (p)
517 *p = '\0';
518
519 if (is_time)
520 ret = check_str_time(tmp, &ull, o->is_seconds);
521 else
522 ret = check_str_bytes(tmp, &ull, data);
523
524 dprint(FD_PARSE, " ret=%d, out=%llu\n", ret, ull);
525
526 if (ret)
527 break;
528 if (o->pow2 && !is_power_of_2(ull)) {
529 log_err("%s: must be a power-of-2\n", o->name);
530 return 1;
531 }
532
533 if (o->maxval && ull > o->maxval) {
534 log_err("max value out of range: %llu"
535 " (%u max)\n", ull, o->maxval);
536 return 1;
537 }
538 if (o->minval && ull < o->minval) {
539 log_err("min value out of range: %llu"
540 " (%u min)\n", ull, o->minval);
541 return 1;
542 }
543 if (o->posval[0].ival) {
544 posval_sort(o, posval);
545
546 ret = 1;
547 for (i = 0; i < PARSE_MAX_VP; i++) {
548 vp = &posval[i];
549 if (!vp->ival || vp->ival[0] == '\0')
550 continue;
551 if (vp->oval == ull) {
552 ret = 0;
553 break;
554 }
555 }
556 if (ret) {
557 log_err("fio: value %llu not allowed:\n", ull);
558 show_option_values(o);
559 return 1;
560 }
561 }
562
563 if (fn)
564 ret = fn(data, &ull);
565 else {
566 if (o->type == FIO_OPT_INT) {
567 if (first)
568 val_store(ilp, ull, o->off1, 0, data, o);
569 if (curr == 1) {
570 if (o->off2)
571 val_store(ilp, ull, o->off2, 0, data, o);
572 }
573 if (curr == 2) {
574 if (o->off3)
575 val_store(ilp, ull, o->off3, 0, data, o);
576 }
577 if (!more) {
578 if (curr < 1) {
579 if (o->off2)
580 val_store(ilp, ull, o->off2, 0, data, o);
581 }
582 if (curr < 2) {
583 if (o->off3)
584 val_store(ilp, ull, o->off3, 0, data, o);
585 }
586 }
587 } else {
588 if (first)
589 val_store(ullp, ull, o->off1, 0, data, o);
590 if (!more) {
591 if (o->off2)
592 val_store(ullp, ull, o->off2, 0, data, o);
593 }
594 }
595 }
596 break;
597 }
598 case FIO_OPT_FLOAT_LIST: {
599 char *cp2;
600
601 if (first) {
602 /*
603 ** Initialize precision to 0 and zero out list
604 ** in case specified list is shorter than default
605 */
606 if (o->off2) {
607 ul2 = 0;
608 ilp = td_var(data, o, o->off2);
609 *ilp = ul2;
610 }
611
612 flp = td_var(data, o, o->off1);
613 for(i = 0; i < o->maxlen; i++)
614 flp[i].u.f = 0.0;
615 }
616 if (curr >= o->maxlen) {
617 log_err("the list exceeding max length %d\n",
618 o->maxlen);
619 return 1;
620 }
621 if (!str_to_float(ptr, &uf, 0)) { /* this breaks if we ever have lists of times */
622 log_err("not a floating point value: %s\n", ptr);
623 return 1;
624 }
625 if (uf > o->maxfp) {
626 log_err("value out of range: %f"
627 " (range max: %f)\n", uf, o->maxfp);
628 return 1;
629 }
630 if (uf < o->minfp) {
631 log_err("value out of range: %f"
632 " (range min: %f)\n", uf, o->minfp);
633 return 1;
634 }
635
636 flp = td_var(data, o, o->off1);
637 flp[curr].u.f = uf;
638
639 dprint(FD_PARSE, " out=%f\n", uf);
640
641 /*
642 ** Calculate precision for output by counting
643 ** number of digits after period. Find first
644 ** period in entire remaining list each time
645 */
646 cp2 = strchr(ptr, '.');
647 if (cp2 != NULL) {
648 int len = 0;
649
650 while (*++cp2 != '\0' && *cp2 >= '0' && *cp2 <= '9')
651 len++;
652
653 if (o->off2) {
654 ilp = td_var(data, o, o->off2);
655 if (len > *ilp)
656 *ilp = len;
657 }
658 }
659
660 break;
661 }
662 case FIO_OPT_STR_STORE: {
663 fio_opt_str_fn *fn = o->cb;
664
665 if (!strlen(ptr))
666 return 1;
667
668 if (o->off1) {
669 cp = td_var(data, o, o->off1);
670 *cp = strdup(ptr);
671 }
672
673 if (fn)
674 ret = fn(data, ptr);
675 else if (o->posval[0].ival) {
676 posval_sort(o, posval);
677
678 ret = 1;
679 for (i = 0; i < PARSE_MAX_VP; i++) {
680 vp = &posval[i];
681 if (!vp->ival || vp->ival[0] == '\0' || !cp)
682 continue;
683 all_skipped = 0;
684 if (!strncmp(vp->ival, ptr, str_match_len(vp, ptr))) {
685 char *rest;
686
687 ret = 0;
688 if (vp->cb)
689 fn = vp->cb;
690 rest = strstr(*cp ?: ptr, ":");
691 if (rest) {
692 if (*cp)
693 *rest = '\0';
694 ptr = rest + 1;
695 } else
696 ptr = NULL;
697 break;
698 }
699 }
700 }
701
702 if (!all_skipped) {
703 if (ret && !*cp)
704 show_option_values(o);
705 else if (ret && *cp)
706 ret = 0;
707 else if (fn && ptr)
708 ret = fn(data, ptr);
709 }
710
711 break;
712 }
713 case FIO_OPT_RANGE: {
714 char tmp[128];
715 char *p1, *p2;
716
717 tmp[sizeof(tmp) - 1] = '\0';
718 strncpy(tmp, ptr, sizeof(tmp) - 1);
719
720 /* Handle bsrange with separate read,write values: */
721 p1 = strchr(tmp, ',');
722 if (p1)
723 *p1 = '\0';
724
725 p1 = strchr(tmp, '-');
726 if (!p1) {
727 p1 = strchr(tmp, ':');
728 if (!p1) {
729 ret = 1;
730 break;
731 }
732 }
733
734 p2 = p1 + 1;
735 *p1 = '\0';
736 p1 = tmp;
737
738 ret = 1;
739 if (!check_range_bytes(p1, &ul1, data) &&
740 !check_range_bytes(p2, &ul2, data)) {
741 ret = 0;
742 if (ul1 > ul2) {
743 unsigned long foo = ul1;
744
745 ul1 = ul2;
746 ul2 = foo;
747 }
748
749 if (first) {
750 val_store(ilp, ul1, o->off1, 0, data, o);
751 val_store(ilp, ul2, o->off2, 0, data, o);
752 }
753 if (curr == 1) {
754 if (o->off3 && o->off4) {
755 val_store(ilp, ul1, o->off3, 0, data, o);
756 val_store(ilp, ul2, o->off4, 0, data, o);
757 }
758 }
759 if (curr == 2) {
760 if (o->off5 && o->off6) {
761 val_store(ilp, ul1, o->off5, 0, data, o);
762 val_store(ilp, ul2, o->off6, 0, data, o);
763 }
764 }
765 if (!more) {
766 if (curr < 1) {
767 if (o->off3 && o->off4) {
768 val_store(ilp, ul1, o->off3, 0, data, o);
769 val_store(ilp, ul2, o->off4, 0, data, o);
770 }
771 }
772 if (curr < 2) {
773 if (o->off5 && o->off6) {
774 val_store(ilp, ul1, o->off5, 0, data, o);
775 val_store(ilp, ul2, o->off6, 0, data, o);
776 }
777 }
778 }
779 }
780
781 break;
782 }
783 case FIO_OPT_BOOL:
784 case FIO_OPT_STR_SET: {
785 fio_opt_int_fn *fn = o->cb;
786
787 if (ptr)
788 ret = check_int(ptr, &il);
789 else if (o->type == FIO_OPT_BOOL)
790 ret = 1;
791 else
792 il = 1;
793
794 dprint(FD_PARSE, " ret=%d, out=%d\n", ret, il);
795
796 if (ret)
797 break;
798
799 if (o->maxval && il > (int) o->maxval) {
800 log_err("max value out of range: %d (%d max)\n",
801 il, o->maxval);
802 return 1;
803 }
804 if (o->minval && il < o->minval) {
805 log_err("min value out of range: %d (%d min)\n",
806 il, o->minval);
807 return 1;
808 }
809
810 if (o->neg)
811 il = !il;
812
813 if (fn)
814 ret = fn(data, &il);
815 else {
816 if (first)
817 val_store(ilp, il, o->off1, 0, data, o);
818 if (!more) {
819 if (o->off2)
820 val_store(ilp, il, o->off2, 0, data, o);
821 }
822 }
823 break;
824 }
825 case FIO_OPT_DEPRECATED:
826 log_info("Option %s is deprecated\n", o->name);
827 ret = 1;
828 break;
829 default:
830 log_err("Bad option type %u\n", o->type);
831 ret = 1;
832 }
833
834 if (ret)
835 return ret;
836
837 if (o->verify) {
838 ret = o->verify(o, data);
839 if (ret) {
840 log_err("Correct format for offending option\n");
841 log_err("%20s: %s\n", o->name, o->help);
842 show_option_help(o, 1);
843 }
844 }
845
846 return ret;
847}
848
849static int handle_option(struct fio_option *o, const char *__ptr, void *data)
850{
851 char *o_ptr, *ptr, *ptr2;
852 int ret, done;
853
854 dprint(FD_PARSE, "handle_option=%s, ptr=%s\n", o->name, __ptr);
855
856 o_ptr = ptr = NULL;
857 if (__ptr)
858 o_ptr = ptr = strdup(__ptr);
859
860 /*
861 * See if we have another set of parameters, hidden after a comma.
862 * Do this before parsing this round, to check if we should
863 * copy set 1 options to set 2.
864 */
865 done = 0;
866 ret = 1;
867 do {
868 int __ret;
869
870 ptr2 = NULL;
871 if (ptr &&
872 (o->type != FIO_OPT_STR_STORE) &&
873 (o->type != FIO_OPT_STR) &&
874 (o->type != FIO_OPT_FLOAT_LIST)) {
875 ptr2 = strchr(ptr, ',');
876 if (ptr2 && *(ptr2 + 1) == '\0')
877 *ptr2 = '\0';
878 if (o->type != FIO_OPT_STR_MULTI && o->type != FIO_OPT_RANGE) {
879 if (!ptr2)
880 ptr2 = strchr(ptr, ':');
881 if (!ptr2)
882 ptr2 = strchr(ptr, '-');
883 }
884 } else if (ptr && o->type == FIO_OPT_FLOAT_LIST) {
885 ptr2 = strchr(ptr, ':');
886 }
887
888 /*
889 * Don't return early if parsing the first option fails - if
890 * we are doing multiple arguments, we can allow the first one
891 * being empty.
892 */
893 __ret = __handle_option(o, ptr, data, !done, !!ptr2, done);
894 if (ret)
895 ret = __ret;
896
897 if (!ptr2)
898 break;
899
900 ptr = ptr2 + 1;
901 done++;
902 } while (1);
903
904 if (o_ptr)
905 free(o_ptr);
906 return ret;
907}
908
909struct fio_option *find_option(struct fio_option *options, const char *opt)
910{
911 struct fio_option *o;
912
913 for (o = &options[0]; o->name; o++) {
914 if (!o_match(o, opt))
915 continue;
916 if (o->type == FIO_OPT_UNSUPPORTED) {
917 log_err("Option <%s>: %s\n", o->name, o->help);
918 continue;
919 }
920
921 return o;
922 }
923
924 return NULL;
925}
926
927
928static struct fio_option *get_option(char *opt,
929 struct fio_option *options, char **post)
930{
931 struct fio_option *o;
932 char *ret;
933
934 ret = strchr(opt, '=');
935 if (ret) {
936 *post = ret;
937 *ret = '\0';
938 ret = opt;
939 (*post)++;
940 strip_blank_end(ret);
941 o = find_option(options, ret);
942 } else {
943 o = find_option(options, opt);
944 *post = NULL;
945 }
946
947 return o;
948}
949
950static int opt_cmp(const void *p1, const void *p2)
951{
952 struct fio_option *o;
953 char *s, *foo;
954 int prio1, prio2;
955
956 prio1 = prio2 = 0;
957
958 if (*(char **)p1) {
959 s = strdup(*((char **) p1));
960 o = get_option(s, __fio_options, &foo);
961 if (o)
962 prio1 = o->prio;
963 free(s);
964 }
965 if (*(char **)p2) {
966 s = strdup(*((char **) p2));
967 o = get_option(s, __fio_options, &foo);
968 if (o)
969 prio2 = o->prio;
970 free(s);
971 }
972
973 return prio2 - prio1;
974}
975
976void sort_options(char **opts, struct fio_option *options, int num_opts)
977{
978 __fio_options = options;
979 qsort(opts, num_opts, sizeof(char *), opt_cmp);
980 __fio_options = NULL;
981}
982
983static void add_to_dump_list(struct fio_option *o, struct flist_head *dump_list,
984 const char *post)
985{
986 struct print_option *p;
987
988 if (!dump_list)
989 return;
990
991 p = malloc(sizeof(*p));
992 p->name = strdup(o->name);
993 if (post)
994 p->value = strdup(post);
995 else
996 p->value = NULL;
997
998 flist_add_tail(&p->list, dump_list);
999}
1000
1001int parse_cmd_option(const char *opt, const char *val,
1002 struct fio_option *options, void *data,
1003 struct flist_head *dump_list)
1004{
1005 struct fio_option *o;
1006
1007 o = find_option(options, opt);
1008 if (!o) {
1009 log_err("Bad option <%s>\n", opt);
1010 return 1;
1011 }
1012
1013 if (handle_option(o, val, data)) {
1014 log_err("fio: failed parsing %s=%s\n", opt, val);
1015 return 1;
1016 }
1017
1018 add_to_dump_list(o, dump_list, val);
1019 return 0;
1020}
1021
1022int parse_option(char *opt, const char *input,
1023 struct fio_option *options, struct fio_option **o, void *data,
1024 struct flist_head *dump_list)
1025{
1026 char *post;
1027
1028 if (!opt) {
1029 log_err("fio: failed parsing %s\n", input);
1030 *o = NULL;
1031 return 1;
1032 }
1033
1034 *o = get_option(opt, options, &post);
1035 if (!*o) {
1036 if (post) {
1037 int len = strlen(opt);
1038 if (opt + len + 1 != post)
1039 memmove(opt + len + 1, post, strlen(post));
1040 opt[len] = '=';
1041 }
1042 return 1;
1043 }
1044
1045 if (handle_option(*o, post, data)) {
1046 log_err("fio: failed parsing %s\n", input);
1047 return 1;
1048 }
1049
1050 add_to_dump_list(*o, dump_list, post);
1051 return 0;
1052}
1053
1054/*
1055 * Option match, levenshtein distance. Handy for not quite remembering what
1056 * the option name is.
1057 */
1058int string_distance(const char *s1, const char *s2)
1059{
1060 unsigned int s1_len = strlen(s1);
1061 unsigned int s2_len = strlen(s2);
1062 unsigned int *p, *q, *r;
1063 unsigned int i, j;
1064
1065 p = malloc(sizeof(unsigned int) * (s2_len + 1));
1066 q = malloc(sizeof(unsigned int) * (s2_len + 1));
1067
1068 p[0] = 0;
1069 for (i = 1; i <= s2_len; i++)
1070 p[i] = p[i - 1] + 1;
1071
1072 for (i = 1; i <= s1_len; i++) {
1073 q[0] = p[0] + 1;
1074 for (j = 1; j <= s2_len; j++) {
1075 unsigned int sub = p[j - 1];
1076 unsigned int pmin;
1077
1078 if (s1[i - 1] != s2[j - 1])
1079 sub++;
1080
1081 pmin = min(q[j - 1] + 1, sub);
1082 q[j] = min(p[j] + 1, pmin);
1083 }
1084 r = p;
1085 p = q;
1086 q = r;
1087 }
1088
1089 i = p[s2_len];
1090 free(p);
1091 free(q);
1092 return i;
1093}
1094
1095/*
1096 * Make a guess of whether the distance from 's1' is significant enough
1097 * to warrant printing the guess. We set this to a 1/2 match.
1098 */
1099int string_distance_ok(const char *opt, int distance)
1100{
1101 size_t len;
1102
1103 len = strlen(opt);
1104 len = (len + 1) / 2;
1105 return distance <= len;
1106}
1107
1108static struct fio_option *find_child(struct fio_option *options,
1109 struct fio_option *o)
1110{
1111 struct fio_option *__o;
1112
1113 for (__o = options + 1; __o->name; __o++)
1114 if (__o->parent && !strcmp(__o->parent, o->name))
1115 return __o;
1116
1117 return NULL;
1118}
1119
1120static void __print_option(struct fio_option *o, struct fio_option *org,
1121 int level)
1122{
1123 char name[256], *p;
1124 int depth;
1125
1126 if (!o)
1127 return;
1128 if (!org)
1129 org = o;
1130
1131 p = name;
1132 depth = level;
1133 while (depth--)
1134 p += sprintf(p, "%s", " ");
1135
1136 sprintf(p, "%s", o->name);
1137
1138 log_info("%-24s: %s\n", name, o->help);
1139}
1140
1141static void print_option(struct fio_option *o)
1142{
1143 struct fio_option *parent;
1144 struct fio_option *__o;
1145 unsigned int printed;
1146 unsigned int level;
1147
1148 __print_option(o, NULL, 0);
1149 parent = o;
1150 level = 0;
1151 do {
1152 level++;
1153 printed = 0;
1154
1155 while ((__o = find_child(o, parent)) != NULL) {
1156 __print_option(__o, o, level);
1157 o = __o;
1158 printed++;
1159 }
1160
1161 parent = o;
1162 } while (printed);
1163}
1164
1165int show_cmd_help(struct fio_option *options, const char *name)
1166{
1167 struct fio_option *o, *closest;
1168 unsigned int best_dist = -1U;
1169 int found = 0;
1170 int show_all = 0;
1171
1172 if (!name || !strcmp(name, "all"))
1173 show_all = 1;
1174
1175 closest = NULL;
1176 best_dist = -1;
1177 for (o = &options[0]; o->name; o++) {
1178 int match = 0;
1179
1180 if (o->type == FIO_OPT_DEPRECATED)
1181 continue;
1182 if (!exec_profile && o->prof_name)
1183 continue;
1184 if (exec_profile && !(o->prof_name && !strcmp(exec_profile, o->prof_name)))
1185 continue;
1186
1187 if (name) {
1188 if (!strcmp(name, o->name) ||
1189 (o->alias && !strcmp(name, o->alias)))
1190 match = 1;
1191 else {
1192 unsigned int dist;
1193
1194 dist = string_distance(name, o->name);
1195 if (dist < best_dist) {
1196 best_dist = dist;
1197 closest = o;
1198 }
1199 }
1200 }
1201
1202 if (show_all || match) {
1203 found = 1;
1204 if (match)
1205 log_info("%20s: %s\n", o->name, o->help);
1206 if (show_all) {
1207 if (!o->parent)
1208 print_option(o);
1209 continue;
1210 }
1211 }
1212
1213 if (!match)
1214 continue;
1215
1216 show_option_help(o, 0);
1217 }
1218
1219 if (found)
1220 return 0;
1221
1222 log_err("No such command: %s", name);
1223
1224 /*
1225 * Only print an appropriately close option, one where the edit
1226 * distance isn't too big. Otherwise we get crazy matches.
1227 */
1228 if (closest && best_dist < 3) {
1229 log_info(" - showing closest match\n");
1230 log_info("%20s: %s\n", closest->name, closest->help);
1231 show_option_help(closest, 0);
1232 } else
1233 log_info("\n");
1234
1235 return 1;
1236}
1237
1238/*
1239 * Handle parsing of default parameters.
1240 */
1241void fill_default_options(void *data, struct fio_option *options)
1242{
1243 struct fio_option *o;
1244
1245 dprint(FD_PARSE, "filling default options\n");
1246
1247 for (o = &options[0]; o->name; o++)
1248 if (o->def)
1249 handle_option(o, o->def, data);
1250}
1251
1252void option_init(struct fio_option *o)
1253{
1254 if (o->type == FIO_OPT_DEPRECATED || o->type == FIO_OPT_UNSUPPORTED)
1255 return;
1256 if (o->name && !o->lname)
1257 log_err("Option %s: missing long option name\n", o->name);
1258 if (o->type == FIO_OPT_BOOL) {
1259 o->minval = 0;
1260 o->maxval = 1;
1261 }
1262 if (o->type == FIO_OPT_INT) {
1263 if (!o->maxval)
1264 o->maxval = UINT_MAX;
1265 }
1266 if (o->type == FIO_OPT_FLOAT_LIST) {
1267 o->minfp = DBL_MIN;
1268 o->maxfp = DBL_MAX;
1269 }
1270 if (o->type == FIO_OPT_STR_SET && o->def && !o->no_warn_def) {
1271 log_err("Option %s: string set option with"
1272 " default will always be true\n", o->name);
1273 }
1274 if (!o->cb && !o->off1)
1275 log_err("Option %s: neither cb nor offset given\n", o->name);
1276 if (!o->category) {
1277 log_info("Option %s: no category defined. Setting to misc\n", o->name);
1278 o->category = FIO_OPT_C_GENERAL;
1279 o->group = FIO_OPT_G_INVALID;
1280 }
1281 if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE ||
1282 o->type == FIO_OPT_STR_MULTI)
1283 return;
1284}
1285
1286/*
1287 * Sanitize the options structure. For now it just sets min/max for bool
1288 * values and whether both callback and offsets are given.
1289 */
1290void options_init(struct fio_option *options)
1291{
1292 struct fio_option *o;
1293
1294 dprint(FD_PARSE, "init options\n");
1295
1296 for (o = &options[0]; o->name; o++) {
1297 option_init(o);
1298 if (o->inverse)
1299 o->inv_opt = find_option(options, o->inverse);
1300 }
1301}
1302
1303void options_free(struct fio_option *options, void *data)
1304{
1305 struct fio_option *o;
1306 char **ptr;
1307
1308 dprint(FD_PARSE, "free options\n");
1309
1310 for (o = &options[0]; o->name; o++) {
1311 if (o->type != FIO_OPT_STR_STORE || !o->off1)
1312 continue;
1313
1314 ptr = td_var(data, o, o->off1);
1315 if (*ptr) {
1316 free(*ptr);
1317 *ptr = NULL;
1318 }
1319 }
1320}