X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=parse.c;h=8aaf7992754f1de3e42211a1e3b9c1ef05eddb06;hp=e09b1bbe42998655125d23b00c3b0b7d35214489;hb=184b4098cccb8392eb8ecdd23cdc6597b540df36;hpb=af52b3455ad892322aab2791282b6bd4efdfdbf3 diff --git a/parse.c b/parse.c index e09b1bbe..8aaf7992 100644 --- a/parse.c +++ b/parse.c @@ -8,141 +8,311 @@ #include #include #include +#include +#include #include "parse.h" +#include "debug.h" +#include "options.h" + +static struct fio_option *fio_options; +extern unsigned int fio_get_kb_base(void *); -static void show_option_range(struct fio_option *o) +static int vp_cmp(const void *p1, const void *p2) { - if (!o->minval && !o->maxval) - return; + const struct value_pair *vp1 = p1; + const struct value_pair *vp2 = p2; - printf("%20s: min=%d, max=%d\n", "range", o->minval, o->maxval); + return strlen(vp2->ival) - strlen(vp1->ival); } -static void show_option_values(struct fio_option *o) +static void posval_sort(struct fio_option *o, struct value_pair *vpmap) { - const char *msg; - int i = 0; + const struct value_pair *vp; + int entries; - do { - msg = o->posval[i].ival; - if (!msg) + memset(vpmap, 0, PARSE_MAX_VP * sizeof(struct value_pair)); + + for (entries = 0; entries < PARSE_MAX_VP; entries++) { + vp = &o->posval[entries]; + if (!vp->ival || vp->ival[0] == '\0') break; - if (!i) - printf("%20s: ", "valid values"); + memcpy(&vpmap[entries], vp, sizeof(*vp)); + } + + qsort(vpmap, entries, sizeof(struct value_pair), vp_cmp); +} + +static void show_option_range(struct fio_option *o, + int (*logger)(const char *format, ...)) +{ + if (o->type == FIO_OPT_FLOAT_LIST){ + if (isnan(o->minfp) && isnan(o->maxfp)) + return; + + logger("%20s: min=%f", "range", o->minfp); + if (!isnan(o->maxfp)) + logger(", max=%f", o->maxfp); + logger("\n"); + } else { + if (!o->minval && !o->maxval) + return; - printf("%s,", msg); - i++; - } while (i < PARSE_MAX_VP); + logger("%20s: min=%d", "range", o->minval); + if (o->maxval) + logger(", max=%d", o->maxval); + logger("\n"); + } +} + +static void show_option_values(struct fio_option *o) +{ + int i; + + for (i = 0; i < PARSE_MAX_VP; i++) { + const struct value_pair *vp = &o->posval[i]; + + if (!vp->ival) + continue; + + log_info("%20s: %-10s", i == 0 ? "valid values" : "", vp->ival); + if (vp->help) + log_info(" %s", vp->help); + log_info("\n"); + } if (i) - printf("\n"); + log_info("\n"); +} + +static void show_option_help(struct fio_option *o, int is_err) +{ + const char *typehelp[] = { + "invalid", + "string (opt=bla)", + "string (opt=bla)", + "string with possible k/m/g postfix (opt=4k)", + "string with time postfix (opt=10s)", + "string (opt=bla)", + "string with dual range (opt=1k-4k,4k-8k)", + "integer value (opt=100)", + "boolean value (opt=1)", + "list of floating point values separated by ':' (opt=5.9:7.8)", + "no argument (opt)", + "deprecated", + }; + int (*logger)(const char *format, ...); + + if (is_err) + logger = log_err; + else + logger = log_info; + + if (o->alias) + logger("%20s: %s\n", "alias", o->alias); + + logger("%20s: %s\n", "type", typehelp[o->type]); + logger("%20s: %s\n", "default", o->def ? o->def : "no default"); + if (o->prof_name) + logger("%20s: only for profile '%s'\n", "valid", o->prof_name); + show_option_range(o, logger); + show_option_values(o); } static unsigned long get_mult_time(char c) { switch (c) { - case 'm': - case 'M': - return 60; - case 'h': - case 'H': - return 60 * 60; - case 'd': - case 'D': - return 24 * 60 * 60; - default: - return 1; + case 'm': + case 'M': + return 60; + case 'h': + case 'H': + return 60 * 60; + case 'd': + case 'D': + return 24 * 60 * 60; + default: + return 1; } } -static unsigned long get_mult_bytes(char c) +static unsigned long long __get_mult_bytes(const char *p, void *data, + int *percent) { - switch (c) { - case 'k': - case 'K': - return 1024; - case 'm': - case 'M': - return 1024 * 1024; - case 'g': - case 'G': - return 1024 * 1024 * 1024; - case 'e': - case 'E': - return 1024 * 1024 * 1024 * 1024UL; - default: - return 1; + unsigned int kb_base = fio_get_kb_base(data); + unsigned long long ret = 1; + unsigned int i, pow = 0, mult = kb_base; + char *c; + + if (!p) + return 1; + + c = strdup(p); + + for (i = 0; i < strlen(c); i++) + c[i] = tolower(c[i]); + + if (!strcmp("pib", c)) { + pow = 5; + mult = 1000; + } else if (!strcmp("tib", c)) { + pow = 4; + mult = 1000; + } else if (!strcmp("gib", c)) { + pow = 3; + mult = 1000; + } else if (!strcmp("mib", c)) { + pow = 2; + mult = 1000; + } else if (!strcmp("kib", c)) { + pow = 1; + mult = 1000; + } else if (!strcmp("p", c) || !strcmp("pb", c)) + pow = 5; + else if (!strcmp("t", c) || !strcmp("tb", c)) + pow = 4; + else if (!strcmp("g", c) || !strcmp("gb", c)) + pow = 3; + else if (!strcmp("m", c) || !strcmp("mb", c)) + pow = 2; + else if (!strcmp("k", c) || !strcmp("kb", c)) + pow = 1; + else if (!strcmp("%", c)) { + *percent = 1; + return ret; + } + + while (pow--) + ret *= (unsigned long long) mult; + + free(c); + return ret; +} + +static unsigned long long get_mult_bytes(const char *str, int len, void *data, + int *percent) +{ + const char *p = str; + int digit_seen = 0; + + if (len < 2) + return __get_mult_bytes(str, data, percent); + + /* + * Go forward until we hit a non-digit, or +/- sign + */ + while ((p - str) <= len) { + if (!isdigit((int) *p) && + (((*p != '+') && (*p != '-')) || digit_seen)) + break; + digit_seen |= isdigit((int) *p); + p++; } + + if (!isalpha((int) *p) && (*p != '%')) + p = NULL; + + return __get_mult_bytes(p, data, percent); +} + +/* + * Convert string into a floating number. Return 1 for success and 0 otherwise. + */ +int str_to_float(const char *str, double *val) +{ + return (1 == sscanf(str, "%lf", val)); } /* * convert string into decimal value, noting any size suffix */ -static int str_to_decimal(const char *str, long long *val, int kilo) +int str_to_decimal(const char *str, long long *val, int kilo, void *data) { - int len; + int len, base; len = strlen(str); if (!len) return 1; - *val = strtoll(str, NULL, 10); + if (strstr(str, "0x") || strstr(str, "0X")) + base = 16; + else + base = 10; + + *val = strtoll(str, NULL, base); if (*val == LONG_MAX && errno == ERANGE) return 1; - if (kilo) - *val *= get_mult_bytes(str[len - 1]); - else + if (kilo) { + unsigned long long mult; + int perc = 0; + + mult = get_mult_bytes(str, len, data, &perc); + if (perc) + *val = -1ULL - *val; + else + *val *= mult; + } else *val *= get_mult_time(str[len - 1]); return 0; } -static int check_str_bytes(const char *p, long long *val) +static int check_str_bytes(const char *p, long long *val, void *data) { - return str_to_decimal(p, val, 1); + return str_to_decimal(p, val, 1, data); } static int check_str_time(const char *p, long long *val) { - return str_to_decimal(p, val, 0); + return str_to_decimal(p, val, 0, NULL); } void strip_blank_front(char **p) { char *s = *p; - while (isspace(*s)) + if (!strlen(s)) + return; + while (isspace((int) *s)) s++; + + *p = s; } void strip_blank_end(char *p) { - char *s = p + strlen(p) - 1; + char *start = p, *s; - while (isspace(*s) || iscntrl(*s)) + if (!strlen(p)) + return; + + s = strchr(p, ';'); + if (s) + *s = '\0'; + s = strchr(p, '#'); + if (s) + *s = '\0'; + if (s) + p = s; + + s = p + strlen(p); + while ((isspace((int) *s) || iscntrl((int) *s)) && (s > start)) s--; *(s + 1) = '\0'; } -static int check_range_bytes(const char *str, long *val) +static int check_range_bytes(const char *str, long *val, void *data) { - char suffix; - - if (!strlen(str)) - return 1; + long long __val; - if (sscanf(str, "%lu%c", val, &suffix) == 2) { - *val *= get_mult_bytes(suffix); + if (!str_to_decimal(str, &__val, 1, data)) { + *val = __val; return 0; } - if (sscanf(str, "%lu", val) == 1) - return 0; - return 1; } @@ -150,68 +320,89 @@ static int check_int(const char *p, int *val) { if (!strlen(p)) return 1; - if (sscanf(p, "%u", val) == 1) - return 0; + if (strstr(p, "0x") || strstr(p, "0X")) { + if (sscanf(p, "%x", val) == 1) + return 0; + } else { + if (sscanf(p, "%u", val) == 1) + return 0; + } return 1; } -static struct fio_option *find_option(struct fio_option *options, - const char *opt) +static int opt_len(const char *str) { - struct fio_option *o; + char *postfix; - for (o = &options[0]; o->name; o++) { - if (!strcmp(o->name, opt)) - return o; - else if (o->alias && !strcmp(o->alias, opt)) - return o; - } + postfix = strchr(str, ':'); + if (!postfix) + return strlen(str); - return NULL; + return (int)(postfix - str); } -#define val_store(ptr, val, off, data) \ +#define val_store(ptr, val, off, or, data) \ do { \ ptr = td_var((data), (off)); \ - *ptr = (val); \ + if ((or)) \ + *ptr |= (val); \ + else \ + *ptr = (val); \ } while (0) static int __handle_option(struct fio_option *o, const char *ptr, void *data, - int first, int more) + int first, int more, int curr) { int il, *ilp; + double* flp; long long ull, *ullp; long ul1, ul2; + double uf; char **cp; int ret = 0, is_time = 0; + const struct value_pair *vp; + struct value_pair posval[PARSE_MAX_VP]; + int i, all_skipped = 1; + + dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name, + o->type, ptr); - if (!ptr && o->type != FIO_OPT_STR_SET) { - fprintf(stderr, "Option %s requires an argument\n", o->name); + if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) { + log_err("Option %s requires an argument\n", o->name); return 1; } switch (o->type) { - case FIO_OPT_STR: { + case FIO_OPT_STR: + case FIO_OPT_STR_MULTI: { fio_opt_str_fn *fn = o->cb; - const struct value_pair *vp; - int i; + posval_sort(o, posval); + + ret = 1; for (i = 0; i < PARSE_MAX_VP; i++) { - vp = &o->posval[i]; + vp = &posval[i]; if (!vp->ival || vp->ival[0] == '\0') - break; - ret = 1; - if (!strncmp(vp->ival, ptr, strlen(vp->ival))) { + continue; + all_skipped = 0; + if (!strncmp(vp->ival, ptr, opt_len(ptr))) { ret = 0; - if (!o->off1) - break; - val_store(ilp, vp->oval, o->off1, data); - break; + if (o->roff1) { + if (vp->or) + *(unsigned int *) o->roff1 |= vp->oval; + else + *(unsigned int *) o->roff1 = vp->oval; + } else { + if (!o->off1) + continue; + val_store(ilp, vp->oval, o->off1, vp->or, data); + } + continue; } } - if (ret) + if (ret && !all_skipped) show_option_values(o); else if (fn) ret = fn(data, ptr); @@ -219,56 +410,146 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, } case FIO_OPT_STR_VAL_TIME: is_time = 1; - case FIO_OPT_STR_VAL: - case FIO_OPT_STR_VAL_INT: { + case FIO_OPT_INT: + case FIO_OPT_STR_VAL: { fio_opt_str_val_fn *fn = o->cb; if (is_time) ret = check_str_time(ptr, &ull); else - ret = check_str_bytes(ptr, &ull); + ret = check_str_bytes(ptr, &ull, data); if (ret) break; if (o->maxval && ull > o->maxval) { - fprintf(stderr, "max value out of range: %lld (%d max)\n", ull, o->maxval); + log_err("max value out of range: %lld" + " (%d max)\n", ull, o->maxval); return 1; } if (o->minval && ull < o->minval) { - fprintf(stderr, "min value out of range: %lld (%d min)\n", ull, o->minval); + log_err("min value out of range: %lld" + " (%d min)\n", ull, o->minval); return 1; } if (fn) ret = fn(data, &ull); else { - if (o->type == FIO_OPT_STR_VAL_INT) { - if (first) - val_store(ilp, ull, o->off1, data); - if (!more && o->off2) - val_store(ilp, ull, o->off2, data); + if (o->type == FIO_OPT_INT) { + if (first) { + if (o->roff1) + *(unsigned int *) o->roff1 = ull; + else + val_store(ilp, ull, o->off1, 0, data); + } + if (!more) { + if (o->roff2) + *(unsigned int *) o->roff2 = ull; + else if (o->off2) + val_store(ilp, ull, o->off2, 0, data); + } } else { - if (first) - val_store(ullp, ull, o->off1, data); - if (!more && o->off2) - val_store(ullp, ull, o->off2, data); + if (first) { + if (o->roff1) + *(unsigned long long *) o->roff1 = ull; + else + val_store(ullp, ull, o->off1, 0, data); + } + if (!more) { + if (o->roff2) + *(unsigned long long *) o->roff2 = ull; + else if (o->off2) + val_store(ullp, ull, o->off2, 0, data); + } } } break; } + case FIO_OPT_FLOAT_LIST: { + + if (first) { + ul2 = 1; + ilp = td_var(data, o->off2); + *ilp = ul2; + } + if (curr >= o->maxlen) { + log_err("the list exceeding max length %d\n", + o->maxlen); + return 1; + } + if(!str_to_float(ptr, &uf)){ + log_err("not a floating point value: %s\n", ptr); + return 1; + } + if (!isnan(o->maxfp) && uf > o->maxfp) { + log_err("value out of range: %f" + " (range max: %f)\n", uf, o->maxfp); + return 1; + } + if (!isnan(o->minfp) && uf < o->minfp) { + log_err("value out of range: %f" + " (range min: %f)\n", uf, o->minfp); + return 1; + } + + flp = td_var(data, o->off1); + flp[curr] = uf; + + break; + } case FIO_OPT_STR_STORE: { fio_opt_str_fn *fn = o->cb; - cp = td_var(data, o->off1); - *cp = strdup(ptr); - if (fn) { + if (o->roff1 || o->off1) { + if (o->roff1) + cp = (char **) o->roff1; + else if (o->off1) + cp = td_var(data, o->off1); + + *cp = strdup(ptr); + } else { + cp = NULL; + } + + if (fn) ret = fn(data, ptr); - if (ret) { - free(*cp); - *cp = NULL; + else if (o->posval[0].ival) { + posval_sort(o, posval); + + ret = 1; + for (i = 0; i < PARSE_MAX_VP; i++) { + vp = &posval[i]; + if (!vp->ival || vp->ival[0] == '\0') + continue; + all_skipped = 0; + if (!strncmp(vp->ival, ptr, opt_len(ptr))) { + char *rest; + + ret = 0; + if (vp->cb) + fn = vp->cb; + rest = strstr(*cp ?: ptr, ":"); + if (rest) { + if (*cp) + *rest = '\0'; + ptr = rest + 1; + } else + ptr = NULL; + break; + } } } + + if (!all_skipped) { + if (ret && !*cp) + show_option_values(o); + else if (ret && *cp) + ret = 0; + else if (fn && ptr) + ret = fn(data, ptr); + } + break; } case FIO_OPT_RANGE: { @@ -277,6 +558,11 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, strncpy(tmp, ptr, sizeof(tmp) - 1); + /* Handle bsrange with separate read,write values: */ + p1 = strchr(tmp, ','); + if (p1) + *p1 = '\0'; + p1 = strchr(tmp, '-'); if (!p1) { p1 = strchr(tmp, ':'); @@ -291,7 +577,8 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, p1 = tmp; ret = 1; - if (!check_range_bytes(p1, &ul1) && !check_range_bytes(p2, &ul2)) { + if (!check_range_bytes(p1, &ul1, data) && + !check_range_bytes(p2, &ul2, data)) { ret = 0; if (ul1 > ul2) { unsigned long foo = ul1; @@ -301,31 +588,48 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, } if (first) { - val_store(ilp, ul1, o->off1, data); - val_store(ilp, ul2, o->off2, data); + if (o->roff1) + *(unsigned int *) o->roff1 = ul1; + else + val_store(ilp, ul1, o->off1, 0, data); + if (o->roff2) + *(unsigned int *) o->roff2 = ul2; + else + val_store(ilp, ul2, o->off2, 0, data); } - if (!more && o->off3 && o->off4) { - val_store(ilp, ul1, o->off3, data); - val_store(ilp, ul2, o->off4, data); + if (o->roff3 && o->roff4) { + *(unsigned int *) o->roff3 = ul1; + *(unsigned int *) o->roff4 = ul2; + } else if (o->off3 && o->off4) { + val_store(ilp, ul1, o->off3, 0, data); + val_store(ilp, ul2, o->off4, 0, data); } } break; } - case FIO_OPT_INT: - case FIO_OPT_BOOL: { + case FIO_OPT_BOOL: + case FIO_OPT_STR_SET: { fio_opt_int_fn *fn = o->cb; - ret = check_int(ptr, &il); + if (ptr) + ret = check_int(ptr, &il); + else if (o->type == FIO_OPT_BOOL) + ret = 1; + else + il = 1; + if (ret) break; if (o->maxval && il > (int) o->maxval) { - fprintf(stderr, "max value out of range: %d (%d max)\n", il, o->maxval); + log_err("max value out of range: %d (%d max)\n", + il, o->maxval); return 1; } if (o->minval && il < o->minval) { - fprintf(stderr, "min value out of range: %d (%d min)\n", il, o->minval); + log_err("min value out of range: %d (%d min)\n", + il, o->minval); return 1; } @@ -335,66 +639,157 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, if (fn) ret = fn(data, &il); else { - if (first) - val_store(ilp, il, o->off1, data); - if (!more && o->off2) - val_store(ilp, il, o->off2, data); + if (first) { + if (o->roff1) + *(unsigned int *)o->roff1 = il; + else + val_store(ilp, il, o->off1, 0, data); + } + if (!more) { + if (o->roff2) + *(unsigned int *) o->roff2 = il; + else if (o->off2) + val_store(ilp, il, o->off2, 0, data); + } } break; } - case FIO_OPT_STR_SET: { - fio_opt_str_set_fn *fn = o->cb; - - if (fn) - ret = fn(data); - else { - if (first) - val_store(ilp, 1, o->off1, data); - if (!more && o->off2) - val_store(ilp, 1, o->off2, data); - } + case FIO_OPT_DEPRECATED: + log_info("Option %s is deprecated\n", o->name); break; - } default: - fprintf(stderr, "Bad option type %u\n", o->type); + log_err("Bad option type %u\n", o->type); ret = 1; } + if (ret) + return ret; + + if (o->verify) { + ret = o->verify(o, data); + if (ret) { + log_err("Correct format for offending option\n"); + log_err("%20s: %s\n", o->name, o->help); + show_option_help(o, 1); + } + } + return ret; } -static int handle_option(struct fio_option *o, const char *ptr, void *data) +static int handle_option(struct fio_option *o, const char *__ptr, void *data) { - const char *ptr2 = NULL; - int r1, r2; + char *o_ptr, *ptr, *ptr2; + int ret, done; + + dprint(FD_PARSE, "handle_option=%s, ptr=%s\n", o->name, __ptr); + + o_ptr = ptr = NULL; + if (__ptr) + o_ptr = ptr = strdup(__ptr); /* - * See if we have a second set of parameters, hidden after a comma. - * Do this before parsing the first round, to check if we should + * See if we have another set of parameters, hidden after a comma. + * Do this before parsing this round, to check if we should * copy set 1 options to set 2. */ - if (ptr && - (o->type != FIO_OPT_STR_STORE) && - (o->type != FIO_OPT_STR)) { - ptr2 = strchr(ptr, ','); - if (!ptr2) + done = 0; + ret = 1; + do { + int __ret; + + ptr2 = NULL; + if (ptr && + (o->type != FIO_OPT_STR_STORE) && + (o->type != FIO_OPT_STR) && + (o->type != FIO_OPT_FLOAT_LIST)) { + ptr2 = strchr(ptr, ','); + if (ptr2 && *(ptr2 + 1) == '\0') + *ptr2 = '\0'; + if (o->type != FIO_OPT_STR_MULTI) { + if (!ptr2) + ptr2 = strchr(ptr, ':'); + if (!ptr2) + ptr2 = strchr(ptr, '-'); + } + } else if (ptr && o->type == FIO_OPT_FLOAT_LIST) { ptr2 = strchr(ptr, ':'); + } + + /* + * Don't return early if parsing the first option fails - if + * we are doing multiple arguments, we can allow the first one + * being empty. + */ + __ret = __handle_option(o, ptr, data, !done, !!ptr2, done); + if (ret) + ret = __ret; + + if (!ptr2) + break; + + ptr = ptr2 + 1; + done++; + } while (1); + + if (o_ptr) + free(o_ptr); + return ret; +} + +static struct fio_option *get_option(char *opt, + struct fio_option *options, char **post) +{ + struct fio_option *o; + char *ret; + + ret = strchr(opt, '='); + if (ret) { + *post = ret; + *ret = '\0'; + ret = opt; + (*post)++; + strip_blank_end(ret); + o = find_option(options, ret); + } else { + o = find_option(options, opt); + *post = NULL; } - /* - * Don't return early if parsing the first option fails - if - * we are doing multiple arguments, we can allow the first one - * being empty. - */ - r1 = __handle_option(o, ptr, data, 1, !!ptr2); + return o; +} - if (!ptr2) - return r1; +static int opt_cmp(const void *p1, const void *p2) +{ + struct fio_option *o; + char *s, *foo; + int prio1, prio2; - ptr2++; - r2 = __handle_option(o, ptr2, data, 0, 0); + prio1 = prio2 = 0; - return r1 && r2; + if (*(char **)p1) { + s = strdup(*((char **) p1)); + o = get_option(s, fio_options, &foo); + if (o) + prio1 = o->prio; + free(s); + } + if (*(char **)p2) { + s = strdup(*((char **) p2)); + o = get_option(s, fio_options, &foo); + if (o) + prio2 = o->prio; + free(s); + } + + return prio2 - prio1; +} + +void sort_options(char **opts, struct fio_option *options, int num_opts) +{ + fio_options = options; + qsort(opts, num_opts, sizeof(char *), opt_cmp); + fio_options = NULL; } int parse_cmd_option(const char *opt, const char *val, @@ -404,46 +799,44 @@ int parse_cmd_option(const char *opt, const char *val, o = find_option(options, opt); if (!o) { - fprintf(stderr, "Bad option %s\n", opt); + log_err("Bad option <%s>\n", opt); return 1; } if (!handle_option(o, val, data)) return 0; - fprintf(stderr, "fio: failed parsing %s=%s\n", opt, val); + log_err("fio: failed parsing %s=%s\n", opt, val); return 1; } -int parse_option(const char *opt, struct fio_option *options, void *data) +int parse_option(char *opt, const char *input, + struct fio_option *options, struct fio_option **o, void *data) { - struct fio_option *o; - char *pre, *post; - char tmp[64]; - - strncpy(tmp, opt, sizeof(tmp) - 1); - - pre = strchr(tmp, '='); - if (pre) { - post = pre; - *pre = '\0'; - pre = tmp; - post++; - o = find_option(options, pre); - } else { - o = find_option(options, tmp); - post = NULL; + char *post; + + if (!opt) { + log_err("fio: failed parsing %s\n", input); + *o = NULL; + return 1; } - if (!o) { - fprintf(stderr, "Bad option %s\n", tmp); + *o = get_option(opt, options, &post); + if (!*o) { + if (post) { + int len = strlen(opt); + if (opt + len + 1 != post) + memmove(opt + len + 1, post, strlen(post)); + opt[len] = '='; + } return 1; } - if (!handle_option(o, post, data)) + if (!handle_option(*o, post, data)) { return 0; + } - fprintf(stderr, "fio: failed parsing %s\n", opt); + log_err("fio: failed parsing %s\n", input); return 1; } @@ -486,30 +879,67 @@ static int string_distance(const char *s1, const char *s2) return i; } -static void show_option_help(struct fio_option *o) +static struct fio_option *find_child(struct fio_option *options, + struct fio_option *o) { - const char *typehelp[] = { - "string (opt=bla)", - "string with possible k/m/g postfix (opt=4k)", - "string with range and postfix (opt=1k-4k)", - "string with time postfix (opt=10s)", - "string (opt=bla)", - "string with dual range (opt=1k-4k,4k-8k)", - "integer value (opt=100)", - "boolean value (opt=1)", - "no argument (opt)", - }; + struct fio_option *__o; - printf("%20s: %s\n", "type", typehelp[o->type]); - printf("%20s: %s\n", "default", o->def ? o->def : "no default"); - show_option_range(o); - show_option_values(o); + for (__o = options + 1; __o->name; __o++) + if (__o->parent && !strcmp(__o->parent, o->name)) + return __o; + + return NULL; +} + +static void __print_option(struct fio_option *o, struct fio_option *org, + int level) +{ + char name[256], *p; + int depth; + + if (!o) + return; + if (!org) + org = o; + + p = name; + depth = level; + while (depth--) + p += sprintf(p, "%s", " "); + + sprintf(p, "%s", o->name); + + log_info("%-24s: %s\n", name, o->help); +} + +static void print_option(struct fio_option *o) +{ + struct fio_option *parent; + struct fio_option *__o; + unsigned int printed; + unsigned int level; + + __print_option(o, NULL, 0); + parent = o; + level = 0; + do { + level++; + printed = 0; + + while ((__o = find_child(o, parent)) != NULL) { + __print_option(__o, o, level); + o = __o; + printed++; + } + + parent = o; + } while (printed); } int show_cmd_help(struct fio_option *options, const char *name) { struct fio_option *o, *closest; - unsigned int best_dist; + unsigned int best_dist = -1U; int found = 0; int show_all = 0; @@ -521,8 +951,14 @@ int show_cmd_help(struct fio_option *options, const char *name) for (o = &options[0]; o->name; o++) { int match = 0; + if (o->type == FIO_OPT_DEPRECATED) + continue; + if (!exec_profile && o->prof_name) + continue; + if (name) { - if (!strcmp(name, o->name)) + if (!strcmp(name, o->name) || + (o->alias && !strcmp(name, o->alias))) match = 1; else { unsigned int dist; @@ -537,27 +973,36 @@ int show_cmd_help(struct fio_option *options, const char *name) if (show_all || match) { found = 1; - printf("%20s: %s\n", o->name, o->help); - if (show_all) + if (match) + log_info("%20s: %s\n", o->name, o->help); + if (show_all) { + if (!o->parent) + print_option(o); continue; + } } if (!match) continue; - show_option_help(o); + show_option_help(o, 0); } if (found) return 0; - printf("No such command: %s", name); - if (closest) { - printf(" - showing closest match\n"); - printf("%20s: %s\n", closest->name, closest->help); - show_option_help(closest); + log_err("No such command: %s", name); + + /* + * Only print an appropriately close option, one where the edit + * distance isn't too big. Otherwise we get crazy matches. + */ + if (closest && best_dist < 3) { + log_info(" - showing closest match\n"); + log_info("%20s: %s\n", closest->name, closest->help); + show_option_help(closest, 0); } else - printf("\n"); + log_info("\n"); return 1; } @@ -569,11 +1014,40 @@ void fill_default_options(void *data, struct fio_option *options) { struct fio_option *o; + dprint(FD_PARSE, "filling default options\n"); + for (o = &options[0]; o->name; o++) if (o->def) handle_option(o, o->def, data); } +void option_init(struct fio_option *o) +{ + if (o->type == FIO_OPT_DEPRECATED) + return; + if (o->type == FIO_OPT_BOOL) { + o->minval = 0; + o->maxval = 1; + } + if (o->type == FIO_OPT_FLOAT_LIST) { + o->minfp = NAN; + o->maxfp = NAN; + } + if (o->type == FIO_OPT_STR_SET && o->def) { + log_err("Option %s: string set option with" + " default will always be true\n", o->name); + } + if (!o->cb && (!o->off1 && !o->roff1)) + log_err("Option %s: neither cb nor offset given\n", o->name); + if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE || + o->type == FIO_OPT_STR_MULTI) + return; + if (o->cb && ((o->off1 || o->off2 || o->off3 || o->off4) || + (o->roff1 || o->roff2 || o->roff3 || o->roff4))) { + log_err("Option %s: both cb and offset given\n", o->name); + } +} + /* * Sanitize the options structure. For now it just sets min/max for bool * values and whether both callback and offsets are given. @@ -582,16 +1056,27 @@ void options_init(struct fio_option *options) { struct fio_option *o; + dprint(FD_PARSE, "init options\n"); + + for (o = &options[0]; o->name; o++) + option_init(o); +} + +void options_free(struct fio_option *options, void *data) +{ + struct fio_option *o; + char **ptr; + + dprint(FD_PARSE, "free options\n"); + for (o = &options[0]; o->name; o++) { - if (o->type == FIO_OPT_BOOL) { - o->minval = 0; - o->maxval = 1; - } - if (!o->cb && !o->off1) - fprintf(stderr, "Option %s: neither cb nor offset given\n", o->name); - if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE) + if (o->type != FIO_OPT_STR_STORE || !o->off1) continue; - if (o->cb && (o->off1 || o->off2 || o->off3 || o->off4)) - fprintf(stderr, "Option %s: both cb and offset given\n", o->name); + + ptr = td_var(data, o->off1); + if (*ptr) { + free(*ptr); + *ptr = NULL; + } } }