+#define BC_APP "bc"
+
+static char *bc_calc(char *str)
+{
+ char buf[128], *tmp;
+ FILE *f;
+ int ret;
+
+ /*
+ * No math, just return string
+ */
+ if ((!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') &&
+ !strchr(str, '/')) || strchr(str, '\''))
+ return str;
+
+ /*
+ * Split option from value, we only need to calculate the value
+ */
+ tmp = strchr(str, '=');
+ if (!tmp)
+ return str;
+
+ tmp++;
+
+ /*
+ * Prevent buffer overflows; such a case isn't reasonable anyway
+ */
+ if (strlen(str) >= 128 || strlen(tmp) > 100)
+ return str;
+
+ sprintf(buf, "which %s > /dev/null", BC_APP);
+ if (system(buf)) {
+ log_err("fio: bc is needed for performing math\n");
+ return NULL;
+ }
+
+ 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);
+}
+