X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=parse.c;h=2cdac54012515e2629baa7e91a4e52308e752466;hp=8e29fabf8e64e87129d900fea6300a9b80a5daa7;hb=02c6aad501477833c7e017eec0bb14f8e6957b68;hpb=5ec10eaad3b09875b91e19a20bbdfa06f2117562 diff --git a/parse.c b/parse.c index 8e29fabf..2cdac540 100644 --- a/parse.c +++ b/parse.c @@ -8,9 +8,14 @@ #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 int vp_cmp(const void *p1, const void *p2) { @@ -38,12 +43,15 @@ static void posval_sort(struct fio_option *o, struct value_pair *vpmap) qsort(vpmap, entries, sizeof(struct value_pair), vp_cmp); } -static void show_option_range(struct fio_option *o) +static void show_option_range(struct fio_option *o, FILE *out) { if (!o->minval && !o->maxval) return; - printf("%20s: min=%d, max=%d\n", "range", o->minval, o->maxval); + fprintf(out, "%20s: min=%d", "range", o->minval); + if (o->maxval) + fprintf(out, ", max=%d", o->maxval); + fprintf(out, "\n"); } static void show_option_values(struct fio_option *o) @@ -67,6 +75,28 @@ static void show_option_values(struct fio_option *o) printf("\n"); } +static void show_option_help(struct fio_option *o, FILE *out) +{ + const char *typehelp[] = { + "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)", + "no argument (opt)", + }; + + if (o->alias) + fprintf(out, "%20s: %s\n", "alias", o->alias); + + fprintf(out, "%20s: %s\n", "type", typehelp[o->type]); + fprintf(out, "%20s: %s\n", "default", o->def ? o->def : "no default"); + show_option_range(o, stdout); + show_option_values(o); +} + static unsigned long get_mult_time(char c) { switch (c) { @@ -84,57 +114,81 @@ static unsigned long get_mult_time(char c) } } -static unsigned long get_mult_bytes(char c) +static unsigned long long get_mult_bytes(char c, void *data) { + unsigned int kb_base = fio_get_kb_base(data); + unsigned long long ret = 1; + switch (c) { - case 'k': - case 'K': - return 1024; - case 'm': - case 'M': - return 1024 * 1024; + default: + break; + case 'p': + case 'P': + ret *= (unsigned long long) kb_base; + case 't': + case 'T': + ret *= (unsigned long long) kb_base; case 'g': case 'G': - return 1024 * 1024 * 1024; - case 'e': - case 'E': - return 1024 * 1024 * 1024 * 1024UL; - default: - return 1; + ret *= (unsigned long long) kb_base; + case 'm': + case 'M': + ret *= (unsigned long long) kb_base; + case 'k': + case 'K': + ret *= (unsigned long long) kb_base; + break; } + + return ret; } /* * convert string into decimal value, noting any size suffix */ -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) { + const char *p; + /* + * if the last char is 'b' or 'B', the user likely used + * "1gb" instead of just "1g". If the second to last is also + * a letter, adjust. + */ + p = str + len - 1; + if ((*p == 'b' || *p == 'B') && isalpha(*(p - 1))) + --p; + + *val *= get_mult_bytes(*p, data); + } 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) @@ -149,7 +203,7 @@ void strip_blank_front(char **p) void strip_blank_end(char *p) { - char *s; + char *start = p, *s; s = strchr(p, ';'); if (s) @@ -161,13 +215,13 @@ void strip_blank_end(char *p) p = s; s = p + strlen(p); - while ((isspace(*s) || iscntrl(*s)) && (s > p)) + while ((isspace(*s) || iscntrl(*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; @@ -175,7 +229,7 @@ static int check_range_bytes(const char *str, long *val) return 1; if (sscanf(str, "%lu%c", val, &suffix) == 2) { - *val *= get_mult_bytes(suffix); + *val *= get_mult_bytes(suffix, data); return 0; } @@ -200,16 +254,35 @@ static int check_int(const char *p, int *val) return 1; } +static inline int o_match(struct fio_option *o, const char *opt) +{ + if (!strcmp(o->name, opt)) + return 1; + else if (o->alias && !strcmp(o->alias, opt)) + return 1; + + return 0; +} + static struct fio_option *find_option(struct fio_option *options, - const char *opt) + struct flist_head *eops, const char *opt) { + struct flist_head *n; struct fio_option *o; - for (o = &options[0]; o->name; o++) { - if (!strcmp(o->name, opt)) - return o; - else if (o->alias && !strcmp(o->alias, opt)) + for (o = &options[0]; o->name; o++) + if (o_match(o, opt)) return o; + + if (!eops) + return NULL; + + flist_for_each(n, eops) { + struct ext_option *eopt; + + eopt = flist_entry(n, struct ext_option, list); + if (o_match(&eopt->o, opt)) + return &eopt->o; } return NULL; @@ -233,7 +306,7 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name, o->type, ptr); - if (!ptr && o->type != FIO_OPT_STR_SET) { + if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) { fprintf(stderr, "Option %s requires an argument\n", o->name); return 1; } @@ -254,9 +327,13 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, ret = 1; if (!strncmp(vp->ival, ptr, strlen(vp->ival))) { ret = 0; - if (!o->off1) - break; - val_store(ilp, vp->oval, o->off1, data); + if (o->roff1) + *(unsigned int *) o->roff1 = vp->oval; + else { + if (!o->off1) + break; + val_store(ilp, vp->oval, o->off1, data); + } break; } } @@ -269,14 +346,14 @@ 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; @@ -295,16 +372,32 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, 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 long long *) o->roff1 = ull; + else + val_store(ilp, ull, o->off1, data); + } + if (!more) { + if (o->roff2) + *(unsigned long long *) o->roff2 = ull; + else if (o->off2) + val_store(ilp, ull, o->off2, 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, data); + } + if (!more) { + if (o->roff2) + *(unsigned long long *) o->roff2 = ull; + else if (o->off2) + val_store(ullp, ull, o->off2, data); + } } } break; @@ -312,7 +405,11 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, case FIO_OPT_STR_STORE: { fio_opt_str_fn *fn = o->cb; - cp = td_var(data, o->off1); + if (o->roff1) + cp = (char **) o->roff1; + else + cp = td_var(data, o->off1); + *cp = strdup(ptr); if (fn) { ret = fn(data, ptr); @@ -343,8 +440,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; @@ -354,10 +451,19 @@ 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 long *) o->roff1 = ul1; + else + val_store(ilp, ul1, o->off1, data); + if (o->roff2) + *(unsigned long *) o->roff2 = ul2; + else + val_store(ilp, ul2, o->off2, data); } - if (o->off3 && o->off4) { + if (o->roff3 && o->roff4) { + *(unsigned long *) o->roff3 = ul1; + *(unsigned long *) o->roff4 = ul2; + } else if (o->off3 && o->off4) { val_store(ilp, ul1, o->off3, data); val_store(ilp, ul2, o->off4, data); } @@ -365,7 +471,6 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, break; } - case FIO_OPT_INT: case FIO_OPT_BOOL: { fio_opt_int_fn *fn = o->cb; @@ -390,10 +495,18 @@ 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, data); + } + if (!more) { + if (o->roff2) + *(unsigned int *) o->roff2 = il; + else if (o->off2) + val_store(ilp, il, o->off2, data); + } } break; } @@ -403,27 +516,54 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data, 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); + if (first) { + if (o->roff1) + *(unsigned int *) o->roff1 = 1; + else + val_store(ilp, 1, o->off1, data); + } + if (!more) { + if (o->roff2) + *(unsigned int *) o->roff2 = 1; + else if (o->off2) + val_store(ilp, 1, o->off2, data); + } } break; } + case FIO_OPT_DEPRECATED: + fprintf(stdout, "Option %s is deprecated\n", o->name); + break; default: fprintf(stderr, "Bad option type %u\n", o->type); ret = 1; } + if (ret) + return ret; + + if (o->verify) { + ret = o->verify(o, data); + if (ret) { + fprintf(stderr,"Correct format for offending option\n"); + fprintf(stderr, "%20s: %s\n", o->name, o->help); + show_option_help(o, stderr); + } + } + 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; + char *ptr, *ptr2 = NULL; int r1, r2; - dprint(FD_PARSE, "handle_option=%s, ptr=%s\n", o->name, ptr); + dprint(FD_PARSE, "handle_option=%s, ptr=%s\n", o->name, __ptr); + + ptr = NULL; + if (__ptr) + ptr = strdup(__ptr); /* * See if we have a second set of parameters, hidden after a comma. @@ -434,6 +574,8 @@ static int handle_option(struct fio_option *o, const char *ptr, void *data) (o->type != FIO_OPT_STR_STORE) && (o->type != FIO_OPT_STR)) { ptr2 = strchr(ptr, ','); + if (ptr2 && *(ptr2 + 1) == '\0') + *ptr2 = '\0'; if (!ptr2) ptr2 = strchr(ptr, ':'); if (!ptr2) @@ -447,23 +589,82 @@ static int handle_option(struct fio_option *o, const char *ptr, void *data) */ r1 = __handle_option(o, ptr, data, 1, !!ptr2); - if (!ptr2) + if (!ptr2) { + if (ptr) + free(ptr); return r1; + } ptr2++; r2 = __handle_option(o, ptr2, data, 0, 0); + if (ptr) + free(ptr); return r1 && r2; } +static struct fio_option *get_option(const char *opt, + struct fio_option *options, + struct flist_head *eops, char **post) +{ + struct fio_option *o; + char *ret; + + ret = strchr(opt, '='); + if (ret) { + *post = ret; + *ret = '\0'; + ret = (char *) opt; + (*post)++; + strip_blank_end(ret); + o = find_option(options, eops, ret); + } else { + o = find_option(options, eops, opt); + *post = NULL; + } + + return o; +} + +static int opt_cmp(const void *p1, const void *p2) +{ + struct fio_option *o1, *o2; + char *s1, *s2, *foo; + int prio1, prio2; + + s1 = strdup(*((char **) p1)); + s2 = strdup(*((char **) p2)); + + o1 = get_option(s1, fio_options, NULL, &foo); + o2 = get_option(s2, fio_options, NULL, &foo); + + prio1 = prio2 = 0; + if (o1) + prio1 = o1->prio; + if (o2) + prio2 = o2->prio; + + free(s1); + free(s2); + 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, - struct fio_option *options, void *data) + struct fio_option *options, struct flist_head *eops, + void *data) { struct fio_option *o; - o = find_option(options, opt); + o = find_option(options, eops, opt); if (!o) { - fprintf(stderr, "Bad option %s\n", opt); + fprintf(stderr, "Bad option <%s>\n", opt); return 1; } @@ -474,28 +675,73 @@ int parse_cmd_option(const char *opt, const char *val, return 1; } -int parse_option(const char *opt, struct fio_option *options, void *data) +/* + * Return a copy of the input string with substrings of the form ${VARNAME} + * substituted with the value of the environment variable VARNAME. The + * substitution always occurs, even if VARNAME is empty or the corresponding + * environment variable undefined. + */ +static char *option_dup_subs(const char *opt) { - struct fio_option *o; - char *pre, *post; - char *tmp; - - tmp = strdup(opt); - - 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 out[OPT_LEN_MAX+1]; + char in[OPT_LEN_MAX+1]; + char *outptr = out; + char *inptr = in; + char *ch1, *ch2, *env; + ssize_t nchr = OPT_LEN_MAX; + size_t envlen; + + if (strlen(in) + 1 > OPT_LEN_MAX) { + fprintf(stderr, "OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX); + return NULL; + } + + in[OPT_LEN_MAX] = '\0'; + strncpy(in, opt, OPT_LEN_MAX); + + while (*inptr && nchr > 0) { + if (inptr[0] == '$' && inptr[1] == '{') { + ch2 = strchr(inptr, '}'); + if (ch2 && inptr+1 < ch2) { + ch1 = inptr+2; + inptr = ch2+1; + *ch2 = '\0'; + + env = getenv(ch1); + if (env) { + envlen = strlen(env); + if (envlen <= nchr) { + memcpy(outptr, env, envlen); + outptr += envlen; + nchr -= envlen; + } + } + + continue; + } + } + + *outptr++ = *inptr++; + --nchr; } + *outptr = '\0'; + return strdup(out); +} + +int parse_option(const char *opt, struct fio_option *options, + struct flist_head *ext_opt_list, void *data) +{ + struct fio_option *o; + char *post, *tmp; + + tmp = option_dup_subs(opt); + if (!tmp) + return 1; + + o = get_option(tmp, options, ext_opt_list, &post); if (!o) { - fprintf(stderr, "Bad option %s\n", tmp); + fprintf(stderr, "Bad option <%s>\n", tmp); free(tmp); return 1; } @@ -549,29 +795,6 @@ static int string_distance(const char *s1, const char *s2) return i; } -static void show_option_help(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)", - }; - - if (o->alias) - printf("%20s: %s\n", "alias", o->alias); - - 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); -} - static struct fio_option *find_child(struct fio_option *options, struct fio_option *o) { @@ -644,6 +867,9 @@ 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 (name) { if (!strcmp(name, o->name) || (o->alias && !strcmp(name, o->alias))) @@ -673,7 +899,7 @@ int show_cmd_help(struct fio_option *options, const char *name) if (!match) continue; - show_option_help(o); + show_option_help(o, stdout); } if (found) @@ -683,7 +909,7 @@ int show_cmd_help(struct fio_option *options, const char *name) if (closest) { printf(" - showing closest match\n"); printf("%20s: %s\n", closest->name, closest->help); - show_option_help(closest); + show_option_help(closest, stdout); } else printf("\n"); @@ -704,6 +930,30 @@ void fill_default_options(void *data, struct fio_option *options) 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_STR_SET && o->def) { + fprintf(stderr, "Option %s: string set option with" + " default will always be true\n", o->name); + } + 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) + return; + if (o->cb && (o->off1 || o->off2 || o->off3 || o->off4)) { + fprintf(stderr, "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. @@ -714,25 +964,6 @@ void options_init(struct fio_option *options) dprint(FD_PARSE, "init options\n"); - for (o = &options[0]; o->name; o++) { - if (o->type == FIO_OPT_BOOL) { - o->minval = 0; - o->maxval = 1; - } - if (o->type == FIO_OPT_STR_SET && o->def) { - fprintf(stderr, "Option %s: string set option with" - " default will always be true\n", - o->name); - } - 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) - continue; - if (o->cb && (o->off1 || o->off2 || o->off3 || o->off4)) { - fprintf(stderr, "Option %s: both cb and offset given\n", - o->name); - } - } + for (o = &options[0]; o->name; o++) + option_init(o); }