+ if (handle_option(o, val, data)) {
+ log_err("fio: failed parsing %s=%s\n", opt, val);
+ return 1;
+ }
+
+ add_to_dump_list(o, dump_list, val);
+ return 0;
+}
+
+int parse_option(char *opt, const char *input, const struct fio_option *options,
+ const struct fio_option **o, void *data,
+ struct flist_head *dump_list)
+{
+ char *post;
+
+ if (!opt) {
+ log_err("fio: failed parsing %s\n", input);
+ *o = NULL;
+ return 1;
+ }
+
+ *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)) {
+ log_err("fio: failed parsing %s\n", input);
+ return 1;
+ }
+
+ add_to_dump_list(*o, dump_list, post);
+ return 0;
+}
+
+/*
+ * Option match, levenshtein distance. Handy for not quite remembering what
+ * the option name is.
+ */
+int string_distance(const char *s1, const char *s2)
+{
+ unsigned int s1_len = strlen(s1);
+ unsigned int s2_len = strlen(s2);
+ unsigned int *p, *q, *r;
+ unsigned int i, j;
+
+ p = malloc(sizeof(unsigned int) * (s2_len + 1));
+ q = malloc(sizeof(unsigned int) * (s2_len + 1));
+
+ p[0] = 0;
+ for (i = 1; i <= s2_len; i++)
+ p[i] = p[i - 1] + 1;
+
+ for (i = 1; i <= s1_len; i++) {
+ q[0] = p[0] + 1;
+ for (j = 1; j <= s2_len; j++) {
+ unsigned int sub = p[j - 1];
+ unsigned int pmin;
+
+ if (s1[i - 1] != s2[j - 1])
+ sub++;
+
+ pmin = min(q[j - 1] + 1, sub);
+ q[j] = min(p[j] + 1, pmin);
+ }
+ r = p;
+ p = q;
+ q = r;
+ }
+
+ i = p[s2_len];
+ free(p);
+ free(q);
+ return i;
+}
+
+/*
+ * Make a guess of whether the distance from 's1' is significant enough
+ * to warrant printing the guess. We set this to a 1/2 match.
+ */
+int string_distance_ok(const char *opt, int distance)
+{
+ size_t len;
+
+ len = strlen(opt);
+ len = (len + 1) / 2;
+ return distance <= len;
+}
+
+static const struct fio_option *find_child(const struct fio_option *options,
+ const struct fio_option *o)
+{
+ const struct fio_option *__o;
+
+ for (__o = options + 1; __o->name; __o++)
+ if (__o->parent && !strcmp(__o->parent, o->name))
+ return __o;
+
+ return NULL;
+}
+
+static void __print_option(const struct fio_option *o,
+ const struct fio_option *org,
+ int level)
+{
+ char name[256], *p;
+ int depth;
+
+ if (!o)
+ return;
+
+ 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(const struct fio_option *o)
+{
+ const struct fio_option *parent;
+ const 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(const struct fio_option *options, const char *name)
+{
+ const struct fio_option *o, *closest;
+ unsigned int best_dist = -1U;
+ int found = 0;
+ int show_all = 0;
+
+ if (!name || !strcmp(name, "all"))
+ show_all = 1;
+
+ closest = NULL;
+ best_dist = -1;
+ for (o = &options[0]; o->name; o++) {
+ int match = 0;
+
+ if (o->type == FIO_OPT_DEPRECATED ||
+ o->type == FIO_OPT_SOFT_DEPRECATED)
+ continue;
+ if (!exec_profile && o->prof_name)
+ continue;
+ if (exec_profile && !(o->prof_name && !strcmp(exec_profile, o->prof_name)))
+ continue;
+
+ if (name) {
+ if (!strcmp(name, o->name) ||
+ (o->alias && !strcmp(name, o->alias)))
+ match = 1;
+ else {
+ unsigned int dist;
+
+ dist = string_distance(name, o->name);
+ if (dist < best_dist) {
+ best_dist = dist;
+ closest = o;
+ }
+ }
+ }
+
+ if (show_all || match) {
+ found = 1;
+ 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, 0);
+ }
+
+ if (found)
+ return 0;
+
+ 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
+ log_info("\n");
+
+ return 1;
+}
+
+/*
+ * Handle parsing of default parameters.
+ */
+void fill_default_options(void *data, const struct fio_option *options)
+{
+ const 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);
+}
+
+static void option_init(struct fio_option *o)
+{
+ if (o->type == FIO_OPT_DEPRECATED || o->type == FIO_OPT_UNSUPPORTED ||
+ o->type == FIO_OPT_SOFT_DEPRECATED)
+ return;
+ if (o->name && !o->lname)
+ log_err("Option %s: missing long option name\n", o->name);
+ if (o->type == FIO_OPT_BOOL) {
+ o->minval = 0;
+ o->maxval = 1;
+ }
+ if (o->type == FIO_OPT_INT) {
+ if (!o->maxval)
+ o->maxval = UINT_MAX;
+ }
+ if (o->type == FIO_OPT_ULL) {
+ if (!o->maxval)
+ o->maxval = ULLONG_MAX;
+ }
+ if (o->type == FIO_OPT_STR_SET && o->def && !o->no_warn_def) {
+ log_err("Option %s: string set option with"
+ " default will always be true\n", o->name);
+ }
+ if (!o->cb && !o->off1)
+ log_err("Option %s: neither cb nor offset given\n", o->name);
+ if (!o->category) {
+ log_info("Option %s: no category defined. Setting to misc\n", o->name);
+ o->category = FIO_OPT_C_GENERAL;
+ o->group = FIO_OPT_G_INVALID;
+ }
+ if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE ||
+ o->type == FIO_OPT_STR_MULTI)
+ return;
+}
+
+/*
+ * Sanitize the options structure. For now it just sets min/max for bool
+ * values and whether both callback and offsets are given.
+ */
+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);
+ if (o->inverse)
+ o->inv_opt = find_option(options, o->inverse);
+ }
+}
+
+void options_mem_dupe(const struct fio_option *options, void *data)
+{
+ const struct fio_option *o;
+ char **ptr;
+
+ dprint(FD_PARSE, "dup options\n");
+
+ for (o = &options[0]; o->name; o++) {
+ if (o->type != FIO_OPT_STR_STORE)
+ continue;
+
+ ptr = td_var(data, o, o->off1);
+ if (*ptr)
+ *ptr = strdup(*ptr);
+ }
+}
+
+void options_free(const struct fio_option *options, void *data)
+{
+ const struct fio_option *o;
+ char **ptr;
+
+ dprint(FD_PARSE, "free options\n");
+
+ for (o = &options[0]; o->name; o++) {
+ if (o->type != FIO_OPT_STR_STORE || !o->off1 || o->no_free)
+ continue;
+
+ ptr = td_var(data, o, o->off1);
+ if (*ptr) {
+ free(*ptr);
+ *ptr = NULL;
+ }
+ }