+
+ sprintf(buf, "echo '%s' | %s", tmp, BC_APP);
+ f = popen(buf, "r");
+ if (!f) {
+ return NULL;
+ }
+
+ ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
+ if (ret <= 0) {
+ return NULL;
+ }
+
+ pclose(f);
+ buf[(tmp - str) + ret - 1] = '\0';
+ memcpy(buf, str, tmp - str);
+ free(str);
+ return strdup(buf);
+}
+
+/*
+ * 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)
+{
+ 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(opt) + 1 > OPT_LEN_MAX) {
+ log_err("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);
+}
+
+/*
+ * Look for reserved variable names and replace them with real values
+ */
+static char *fio_keyword_replace(char *opt)
+{
+ char *s;
+ int i;
+ int docalc = 0;
+
+ for (i = 0; fio_keywords[i].word != NULL; i++) {
+ struct fio_keyword *kw = &fio_keywords[i];
+
+ while ((s = strstr(opt, kw->word)) != NULL) {
+ char *new = malloc(strlen(opt) + 1);
+ char *o_org = opt;
+ int olen = s - opt;
+ int len;
+
+ /*
+ * Copy part of the string before the keyword and
+ * sprintf() the replacement after it.
+ */
+ memcpy(new, opt, olen);
+ len = sprintf(new + olen, "%s", kw->replace);
+
+ /*
+ * If there's more in the original string, copy that
+ * in too
+ */
+ opt += strlen(kw->word) + olen;
+ if (strlen(opt))
+ memcpy(new + olen + len, opt, opt - o_org - 1);
+
+ /*
+ * replace opt and free the old opt
+ */
+ opt = new;
+ free(o_org);
+
+ docalc = 1;
+ }
+ }
+
+ /*
+ * Check for potential math and invoke bc, if possible
+ */
+ if (docalc)
+ opt = bc_calc(opt);
+
+ return opt;
+}
+
+static char **dup_and_sub_options(char **opts, int num_opts)
+{
+ int i;
+ char **opts_copy = malloc(num_opts * sizeof(*opts));
+ for (i = 0; i < num_opts; i++) {
+ opts_copy[i] = option_dup_subs(opts[i]);
+ if (!opts_copy[i])
+ continue;
+ opts_copy[i] = fio_keyword_replace(opts_copy[i]);
+ }
+ return opts_copy;