Support simple math on reserved keyword expressions

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/HOWTO b/HOWTO
index cdd6473..9b3a684 100644
--- a/HOWTO
+++ b/HOWTO
@@ -203,7 +203,13 @@
 
 These can be used on the command line or in the job file, and will be
 automatically substituted with the current system values when the job
-is run.
+is run. Simple math is also supported on these keywords, so you can
+perform actions like:
+
+size=8*$mb_memory
+
+and get that properly expanded to 8 times the size of memory in the
+machine.
 
 
 5.0 Detailed list of parameters
diff --git a/options.c b/options.c
index 5b88255..211bca7 100644
--- a/options.c
+++ b/options.c
@@ -1788,6 +1788,61 @@
 	fio_keywords[2].replace = strdup(buf);
 }
 
+#define BC_APP		"bc"
+
+static char *bc_calc(char *str)
+{
+	char *buf, *tmp, opt[80];
+	FILE *f;
+	int ret;
+
+	/*
+	 * No math, just return string
+	 */
+	if (!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++;
+	strncpy(opt, str, tmp - str);
+
+	buf = malloc(128);
+
+	sprintf(buf, "which %s > /dev/null", BC_APP);
+	if (system(buf)) {
+		log_err("fio: bc is needed for performing math\n");
+		free(buf);
+		return NULL;
+	}
+
+	sprintf(buf, "echo %s | %s", tmp, BC_APP);
+	f = popen(buf, "r");
+	if (!f) {
+		free(buf);
+		return NULL;
+	}
+
+	ret = fread(buf, 1, 128, f);
+	if (ret <= 0) {
+		free(buf);
+		return NULL;
+	}
+
+	buf[ret - 1] = '\0';
+	strcat(opt, buf);
+	strcpy(buf, opt);
+	pclose(f);
+	free(str);
+	return buf;
+}
+
 /*
  * Look for reserved variable names and replace them with real values
  */
@@ -1828,7 +1883,10 @@
 		}
 	}
 
-	return opt;
+	/*
+	 * Check for potential math and invoke bc, if possible
+	 */
+	return bc_calc(opt);
 }
 
 int fio_options_parse(struct thread_data *td, char **opts, int num_opts)