Merge branch 'master' into expression-parser
authorJens Axboe <axboe@fb.com>
Mon, 29 Sep 2014 22:29:32 +0000 (16:29 -0600)
committerJens Axboe <axboe@fb.com>
Mon, 29 Sep 2014 22:29:32 +0000 (16:29 -0600)
.gitignore
Makefile
configure
exp/README.md [new file with mode: 0644]
exp/expression-parser.l [new file with mode: 0644]
exp/expression-parser.y [new file with mode: 0644]
exp/fixup-buggy-yacc-output.c [new file with mode: 0644]
exp/test-expression-parser.c [new file with mode: 0644]
fio.1
parse.c

index 3993a300ffe2f5716de88972c906b8781288676b..c9d90fb6d736a0160ac85c9b18e63f39631372bf 100644 (file)
@@ -7,3 +7,5 @@
 /config.log
 /cscope.out
 /fio
+y.tab.*
+lex.yy.c
index 8c6c0561d8c8b8a662371d40ad34f5899ac0a724..1968e31dea66950f24bd461a20beb97d7d01bcbe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -158,6 +158,11 @@ endif
 OBJS = $(SOURCE:.c=.o)
 
 FIO_OBJS = $(OBJS) fio.o
+
+ifdef CONFIG_ARITHMETIC
+FIO_OBJS += lex.yy.o y.tab.o
+endif
+
 GFIO_OBJS = $(OBJS) gfio.o graph.o tickmarks.o ghelpers.o goptions.o gerror.o \
                        gclient.o gcompat.o cairo_text_helpers.o printing.o
 
@@ -259,6 +264,35 @@ override CFLAGS += -DFIO_VERSION='"$(FIO_VERSION)"'
                sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
        @rm -f $*.d.tmp
 
+ifdef CONFIG_ARITHMETIC
+lex.yy.o: lex.yy.c y.tab.h
+       $(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
+
+y.tab.o: y.tab.c y.tab.h
+       $(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
+
+y.tab.c: exp/expression-parser.y
+       $(QUIET_CC)$(YACC) -d exp/expression-parser.y
+
+y.tab.h: y.tab.c exp/fixup-buggy-yacc-output
+       @exp/fixup-buggy-yacc-output $@
+
+lex.yy.c: exp/expression-parser.l
+       $(QUIET_CC)$(LEX) exp/expression-parser.l
+
+exp/fixup-buggy-yacc-output.o: exp/fixup-buggy-yacc-output.c
+       $(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
+exp/fixup-buggy-yacc-output: exp/fixup-buggy-yacc-output.o
+       $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
+
+exp/test-expression-parser.o: exp/test-expression-parser.c
+       $(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
+exp/test-expression-parser: exp/test-expression-parser.o
+       $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) $< y.tab.o lex.yy.o -o $@ $(LIBS)
+
+parse.o: lex.yy.o y.tab.o
+endif
+
 init.o: FIO-VERSION-FILE init.c
        $(QUIET_CC)$(CC) -o init.o $(CFLAGS) $(CPPFLAGS) -c init.c
 
@@ -296,10 +330,10 @@ t/ieee754: $(T_IEEE_OBJS)
        $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_IEEE_OBJS) $(LIBS)
 
 fio: $(FIO_OBJS)
-       $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(LIBS) $(HDFSLIB)
+       $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(PARSER_OBJS) $(LIBS) $(HDFSLIB)
 
 gfio: $(GFIO_OBJS)
-       $(QUIET_LINK)$(CC) $(LDFLAGS) -o gfio $(GFIO_OBJS) $(LIBS) $(GTK_LDFLAGS)
+       $(QUIET_LINK)$(CC) $(LDFLAGS) -o gfio $(GFIO_OBJS) $(PARSER_OBJS) $(LIBS) $(GTK_LDFLAGS)
 
 t/genzipf: $(T_ZIPF_OBJS)
        $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_ZIPF_OBJS) $(LIBS)
@@ -319,7 +353,7 @@ t/dedupe: $(T_DEDUPE_OBJS)
        $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_DEDUPE_OBJS) $(LIBS)
 
 clean: FORCE
-       -rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE *.d lib/*.d crc/*.d engines/*.d profiles/*.d t/*.d config-host.mak config-host.h
+       @rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE *.d lib/*.d crc/*.d engines/*.d profiles/*.d t/*.d config-host.mak config-host.h exp/fixup-buggy-yacc-output y.tab.[ch] lex.y.c exp/*.[do]
 
 distclean: clean FORCE
        @rm -f cscope.out fio.pdf fio_generate_plots.pdf fio2gnuplot.pdf
index 33d1327ebbba5b70a001e422bb5ad9b24d7c7b49..7ce874d75275e22f12997c0151951da6954edb39 100755 (executable)
--- a/configure
+++ b/configure
@@ -1270,6 +1270,42 @@ if test "$libhdfs" = "yes" ; then
 fi
 echo "HDFS engine                   $libhdfs"
 
+# Check if we have lex/yacc available
+yacc="no"
+lex="no"
+arith="no"
+LEX=$(which lex)
+if test -x "$LEX" ; then
+  lex="yes"
+fi
+YACC=$(which yacc)
+if test -x "$YACC" ; then
+  yacc="yes"
+fi
+if test "$yacc" = "yes" && test "$lex" = "yes" ; then
+  arith="yes"
+fi
+
+if test "$arith" = "yes" ; then
+cat > $TMPC << EOF
+extern int yywrap(void);
+
+int main(int argc, char **argv)
+{
+  yywrap();
+  return 0;
+}
+EOF
+
+if compile_prog "" "-ll -ly" "lex"; then
+  LIBS="-ll -ly $LIBS"
+else
+  arith="no"
+fi
+fi
+
+echo "lex/yacc for arithmetic       $arith"
+
 #############################################################################
 
 if test "$wordsize" = "64" ; then
@@ -1414,6 +1450,9 @@ fi
 if test "$libhdfs" = "yes" ; then
   output_sym "CONFIG_LIBHDFS"
 fi
+if test "$arith" = "yes" ; then
+  output_sym "CONFIG_ARITHMETIC"
+fi
 
 if test "$zlib" = "no" ; then
   echo "Consider installing zlib-dev (zlib-devel), some fio features depend on it."
diff --git a/exp/README.md b/exp/README.md
new file mode 100644 (file)
index 0000000..48c11c9
--- /dev/null
@@ -0,0 +1,7 @@
+simple-expression-parser
+========================
+
+A simple expression parser for arithmetic expressions made with bison + flex
+
+To use, see the example test-expression-parser.c
+
diff --git a/exp/expression-parser.l b/exp/expression-parser.l
new file mode 100644 (file)
index 0000000..bb80553
--- /dev/null
@@ -0,0 +1,168 @@
+%{
+
+/*
+ * (C) Copyright 2014, Stephen M. Cameron.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "y.tab.h"
+
+#define YYSTYPE PARSER_VALUE_TYPE
+
+extern int lexer_input(char* buffer, int *nbytes, int buffersize);
+
+#undef YY_INPUT
+#define YY_INPUT(buffer, bytes_read, bytes_requested) \
+               lexer_input((buffer), &(bytes_read), (bytes_requested))
+
+extern int yyerror(long long *result, double *dresult,
+               int *has_error, int *bye, const char *msg);
+
+static void __attribute__((unused)) yyunput(int c,char *buf_ptr);
+static int __attribute__((unused)) input(void);
+
+#define set_suffix_value(yylval, i_val, d_val, has_d_val) \
+       (yylval).v.dval = (d_val); \
+       (yylval).v.ival = (i_val); \
+       (yylval).v.has_dval = (has_d_val); \
+       (yylval).v.has_error = 0;
+
+%}
+
+%%
+
+
+bye            return BYE;
+[kK]|[kK][bB]  {
+                       set_suffix_value(yylval, 1024, 1024.0, 0);
+                       return SUFFIX;
+               }
+[Mm]|[Mm][bB]  {
+                       set_suffix_value(yylval, 1024 * 1024, 1024.0 * 1024.0, 0);
+                       return SUFFIX;
+               }
+[mM][sS]       {
+                       set_suffix_value(yylval, 1000, 1000.0, 1);
+                       return SUFFIX;
+               }
+[uU][sS]       {
+                       set_suffix_value(yylval, 1, 1.0, 1);
+                       return SUFFIX;
+               }
+[gG]|[Gg][Bb]  {
+                       set_suffix_value(yylval, 1024LL * 1024 * 1024, 1024.0 * 1024.0 * 1024, 0);
+                       return SUFFIX;
+               }
+[tT]|[tT][bB]  {       
+                       set_suffix_value(yylval, 1024LL * 1024 * 1024 * 1024,
+                                               1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024, 0);
+                       return SUFFIX;
+               }
+[pP]|[pP][bB]  {       
+                       set_suffix_value(yylval, 1024LL * 1024 * 1024 * 1024 * 1024,
+                                       1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0, 0);
+                       return SUFFIX;
+               }
+[kK][iI][Bb]   {
+                       set_suffix_value(yylval, 1000LL, 1000.0, 0);
+                       return SUFFIX;
+               }
+[mM][Ii][bB]   {
+                       set_suffix_value(yylval, 1000000LL, 1000000.0 , 0);
+                       return SUFFIX;
+               }
+[gG][iI][Bb]   {
+                       set_suffix_value(yylval, 1000000000LL, 1000000000.0 , 0);
+                       return SUFFIX;
+               }
+[pP][iI][Bb]   {       
+                       set_suffix_value(yylval, 1000000000000LL, 1000000000000.0 , 0);
+                       return SUFFIX;
+               }
+[sS]           {
+                       set_suffix_value(yylval, 1000000LL, 1000000.0 , 0);
+                       return SUFFIX;
+               }
+[dD]           {
+                       set_suffix_value(yylval, 60LL * 60LL * 24LL * 1000000LL,
+                                               60.0 * 60.0 * 24.0 * 1000000.0, 0);
+                       return SUFFIX;
+               }
+[hH]           {       
+                       set_suffix_value(yylval, 60LL * 60LL * 1000000LL,
+                                       60.0 * 60.0 * 1000000.0, 0);
+                       return SUFFIX;
+               }
+[ \t] ; /* ignore whitespace */
+#.+ ; /* ignore comments */
+[0-9]*[.][0-9]+ {
+                       int rc;
+                       double dval;
+
+                       rc = sscanf(yytext, "%lf", &dval);
+                       if (rc == 1) {
+                               yylval.v.dval = dval;
+                               yylval.v.ival = (long long) dval;
+                               yylval.v.has_dval = 1;
+                               yylval.v.has_error = 0;
+                               return NUMBER;
+                       } else {
+                               yyerror(0, 0, 0, 0, "bad number\n");
+                               yylval.v.has_error = 1;
+                               return NUMBER;
+                       }
+               }
+0x[0-9a-fA-F]+ {
+               int rc, intval;
+               rc = sscanf(yytext, "%x", &intval);
+               if (rc == 1) {
+                       yylval.v.ival = intval;
+                       yylval.v.dval = (double) intval;
+                       yylval.v.has_dval = 0;
+                       yylval.v.has_error = 0;
+                       return NUMBER;
+               } else {
+                       yyerror(0, 0, 0, 0, "bad number\n");
+                       yylval.v.has_error = 1;
+                       return NUMBER;
+               }
+       }
+[0-9]+ {
+               int rc, intval;
+               rc = sscanf(yytext, "%d", &intval);
+               if (rc == 1) {
+                       yylval.v.ival = intval;
+                       yylval.v.dval = (double) intval;
+                       yylval.v.has_dval = 0;
+                       yylval.v.has_error = 0;
+                       return NUMBER;
+               } else {
+                       yyerror(0, 0, 0, 0, "bad number\n");
+                       yylval.v.has_error = 1;
+                       return NUMBER;
+               }
+       }
+\n     return 0;
+[+-/*()^%]     return yytext[0];
+
+.      {
+               yylval.v.has_error = 1;
+               return NUMBER;  
+       }
+%%
+
diff --git a/exp/expression-parser.y b/exp/expression-parser.y
new file mode 100644 (file)
index 0000000..af726a6
--- /dev/null
@@ -0,0 +1,236 @@
+%{
+
+/*
+ * (C) Copyright 2014, Stephen M. Cameron.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+struct parser_value_type {
+       double dval;
+       long long ival;
+       int has_dval;
+       int has_error;
+};
+
+typedef union valtype {
+       struct parser_value_type v;
+} PARSER_VALUE_TYPE;
+
+#define YYSTYPE PARSER_VALUE_TYPE
+
+int yyerror(__attribute__((unused)) long long *result,
+               __attribute__((unused)) double *dresult,
+               __attribute__((unused)) int *has_error,
+               __attribute__((unused)) int *bye, const char *msg);
+
+extern int yylex(void);
+extern void yyrestart(FILE *file);
+
+%}
+
+%union valtype {
+       struct parser_value_type {
+               double dval;
+               long long ival;
+               int has_dval;
+               int has_error;
+       } v;
+};
+
+%token <v> NUMBER
+%token <v> BYE
+%token <v> SUFFIX 
+%left '-' '+'
+%left '*' '/'
+%right '^'
+%left '%'
+%nonassoc UMINUS
+%parse-param { long long *result }
+%parse-param { double *dresult }
+%parse-param { int *has_error }
+%parse-param { int *bye }
+
+%type <v> expression
+%%
+
+top_level:     expression {
+                               *result = $1.ival;
+                               *dresult = $1.dval;
+                               *has_error = $1.has_error;
+                       }
+               | expression error {
+                               *result = $1.ival;
+                               *dresult = $1.dval;
+                               *has_error = 1;
+                       }
+expression:    expression '+' expression { 
+                       if (!$1.has_dval && !$3.has_dval)
+                               $$.ival = $1.ival + $3.ival;
+                       else
+                               $$.ival = (long long) ($1.dval + $3.dval);
+                       $$.dval = $1.dval + $3.dval;
+                       $$.has_error = $1.has_error || $3.has_error;
+               }
+       |       expression '-' expression {
+                       if (!$1.has_dval && !$3.has_dval)
+                               $$.ival = $1.ival - $3.ival; 
+                       else
+                               $$.ival = (long long) ($1.dval - $3.dval); 
+                       $$.dval = $1.dval - $3.dval; 
+                       $$.has_error = $1.has_error || $3.has_error;
+               }
+       |       expression '*' expression {
+                       if (!$1.has_dval && !$3.has_dval)
+                               $$.ival = $1.ival * $3.ival;
+                       else
+                               $$.ival = (long long) ($1.dval * $3.dval);
+                       $$.dval = $1.dval * $3.dval;
+                       $$.has_error = $1.has_error || $3.has_error;
+               }
+       |       expression '/' expression {
+                       if ($3.ival == 0)
+                               yyerror(0, 0, 0, 0, "divide by zero");
+                       else
+                               $$.ival = $1.ival / $3.ival;
+                       if ($3.dval < 1e-20 && $3.dval > -1e-20)
+                               yyerror(0, 0, 0, 0, "divide by zero");
+                       else
+                               $$.dval = $1.dval / $3.dval;
+                       if ($3.has_dval || $1.has_dval)
+                               $$.ival = (long long) $$.dval;
+                       $$.has_error = $1.has_error || $3.has_error;
+               }
+       |       '-' expression %prec UMINUS {
+                       $$.ival = -$2.ival;
+                       $$.dval = -$2.dval;
+                       $$.has_error = $2.has_error;
+               }
+       |       '(' expression ')' { $$ = $2; }
+       |       expression SUFFIX {
+                       if (!$1.has_dval && !$2.has_dval)
+                               $$.ival = $1.ival * $2.ival;
+                       else
+                               $$.ival = (long long) $1.dval * $2.dval;
+                       if ($1.has_dval || $2.has_dval)
+                               $$.dval = $1.dval * $2.dval;
+                       else
+                               $$.dval = $1.ival * $2.ival;
+                       $$.has_error = $1.has_error || $2.has_error;
+               }
+       |       expression '%' expression {
+                       if ($1.has_dval || $3.has_dval)
+                               yyerror(0, 0, 0, 0, "modulo on floats");
+                       if ($3.ival == 0)
+                               yyerror(0, 0, 0, 0, "divide by zero");
+                       else {
+                               $$.ival = $1.ival % $3.ival;
+                               $$.dval = $$.ival;
+                       }
+                       $$.has_error = $1.has_error || $3.has_error;
+               }
+       |       expression '^' expression {
+                       $$.has_error = $1.has_error || $3.has_error;
+                       if (!$1.has_dval && !$3.has_dval) {
+                               int i;
+
+                               if ($3.ival == 0) {
+                                       $$.ival = 1;
+                               } else if ($3.ival > 0) {
+                                       long long tmp = $1.ival;
+                                       $$.ival = 1.0;
+                                       for (i = 0; i < $3.ival; i++)
+                                               $$.ival *= tmp;
+                               }  else {
+                                       /* integers, 2^-3, ok, we now have doubles */
+                                       double tmp;
+                                       if ($1.ival == 0 && $3.ival == 0) {
+                                               tmp = 1.0;
+                                               $$.has_error = 1;
+                                       } else {
+                                               double x = (double) $1.ival;
+                                               double y = (double) $3.ival;
+                                               tmp = pow(x, y);
+                                       }
+                                       $$.ival = (long long) tmp;
+                               }
+                               $$.dval = pow($1.dval, $3.dval);
+                       } else {
+                               $$.dval = pow($1.dval, $3.dval);
+                               $$.ival = (long long) $$.dval;
+                       }
+               }
+       |       NUMBER { $$ = $1; }
+       |       BYE { $$ = $1; *bye = 1; };
+%%
+#include <stdio.h>
+
+/* Urgh.  yacc and lex are kind of horrible.  This is not thread safe, obviously. */
+static int lexer_read_offset = 0;
+static char lexer_input_buffer[1000];
+
+int lexer_input(char* buffer, int *bytes_read, int bytes_requested)
+{
+       int bytes_left = strlen(lexer_input_buffer) - lexer_read_offset;
+
+       if (bytes_requested > bytes_left )
+               bytes_requested = bytes_left;
+       memcpy(buffer, &lexer_input_buffer[lexer_read_offset], bytes_requested);
+       *bytes_read = bytes_requested;
+       lexer_read_offset += bytes_requested;
+       return 0;
+}
+
+static void setup_to_parse_string(const char *string)
+{
+       unsigned int len;
+
+       len = strlen(string);
+       if (len > sizeof(lexer_input_buffer) - 3)
+               len = sizeof(lexer_input_buffer) - 3;
+
+       strncpy(lexer_input_buffer, string, len);
+       lexer_input_buffer[len] = '\0'; 
+       lexer_input_buffer[len + 1] = '\0';  /* lex/yacc want string double null terminated! */
+       lexer_read_offset = 0;
+}
+
+int evaluate_arithmetic_expression(const char *buffer, long long *ival, double *dval)
+{
+       int rc, bye = 0, has_error = 0;
+
+       setup_to_parse_string(buffer);
+       rc = yyparse(ival, dval, &has_error, &bye);
+       yyrestart(NULL);
+       if (rc || bye || has_error) {
+               *ival = 0;
+               *dval = 0;
+               has_error = 1;
+       }
+       return has_error;
+}
+
+int yyerror(__attribute__((unused)) long long *result,
+               __attribute__((unused)) double *dresult,
+               __attribute__((unused)) int *has_error,
+               __attribute__((unused)) int *bye, const char *msg)
+{
+       fprintf(stderr, "%s\n", msg);
+       return 0;
+}
+
diff --git a/exp/fixup-buggy-yacc-output.c b/exp/fixup-buggy-yacc-output.c
new file mode 100644 (file)
index 0000000..ab9bd8e
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * (C) Copyright 2014, Stephen M. Cameron.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+static char *programname;
+
+static char *slurp_file(char *f, off_t *textsize)
+{
+       int fd;
+       struct stat statbuf;
+       off_t bytesleft, bytesread;
+       char *fileptr = NULL;
+       char *slurped_file = NULL;
+
+       fd = open(f, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "%s: Cannot open '%s': %s\n",
+                               programname, f, strerror(errno));
+               return NULL;
+       }
+       if (fstat(fd, &statbuf) != 0) {
+               fprintf(stderr, "%s: Cannot stat '%s': %s\n",
+                               programname, f, strerror(errno));
+               close(fd);
+               return NULL;
+       }
+       bytesleft = statbuf.st_size; 
+       slurped_file = malloc(bytesleft + 1);
+       fileptr = slurped_file;
+       if (!slurped_file) {
+               fprintf(stderr, "%s: malloc returned NULL, out of memory\n",
+                       programname);
+               goto bail_out;
+       }
+       memset(slurped_file, 0, bytesleft + 1);
+       do {
+               bytesread = read(fd, fileptr, bytesleft);
+               if (bytesread < 0 && errno == EAGAIN)
+                       continue;
+               if (bytesread < 0) {
+                       fprintf(stderr, "%s: error reading '%s: %s'\n",
+                               programname, f, strerror(errno));
+                       goto bail_out;
+               }
+               if (bytesread == 0) {
+                       fprintf(stderr, "%s: unexpected EOF in %s\n",
+                               programname, f); 
+                       goto bail_out;
+               }
+               fileptr += bytesread;
+               bytesleft -= bytesread;
+       } while (bytesleft > 0);
+
+       *textsize = statbuf.st_size;
+
+       close(fd);
+       return slurped_file;
+
+bail_out:
+       if (slurped_file)
+               free(slurped_file);
+       close(fd);
+       return NULL;
+}
+
+static int detect_buggy_yacc(char *text)
+{
+       char *x;
+
+       x = strstr(text, " #line ");
+       if (!x)
+               return 0;
+       return 1; 
+}
+
+static void fixup_buggy_yacc_file(char *f)
+{
+       char *slurped_file, *x;
+       off_t textsize;
+       char *newname;
+       int fd;
+       off_t bytesleft, byteswritten;
+
+       newname = alloca(strlen(f) + 10);
+       strcpy(newname, "broken-");
+       strcat(newname, f);
+
+       slurped_file = slurp_file(f, &textsize);
+       if (!slurped_file)
+               return;
+       if (!detect_buggy_yacc(slurped_file))
+               return;
+
+       x = slurped_file;
+
+
+       /*
+        * Fixup the '#line' directives which yacc botched.
+        * Note: this is vulnerable to false positives, but
+        * since this program is just a hack to make this particular
+        * program work, it is sufficient for our purposes.
+        * regexp could make this better, but the real fix needs
+        * to be made in yacc/bison.
+        */
+       while ((x = strstr(x, " #line ")) != NULL) {
+               *x = '\n';
+       }
+
+       if (rename(f, newname) != 0) {
+               fprintf(stderr, "%s: Failed to rename '%s' to '%s': %s\n",
+                               programname, f, newname, strerror(errno));
+               return;
+       }
+       fd = open(f, O_CREAT | O_TRUNC | O_RDWR, 0644);
+       if (fd < 0) {
+               fprintf(stderr, "%s: failed to create '%s': %s\n",
+                       programname, f, strerror(errno));
+               return;
+       }
+
+       bytesleft = textsize;
+       x = slurped_file;
+       do {
+               byteswritten = write(fd, x, bytesleft); 
+               if (byteswritten < 0) {
+                       fprintf(stderr, "%s: Error writing '%s': %s\n",
+                               programname, f, strerror(errno)); 
+                       return;
+               }
+               if (byteswritten == 0 && errno == EINTR)
+                       continue;
+               x += byteswritten;
+               bytesleft -= byteswritten;
+       } while (bytesleft > 0);
+       close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+
+       programname = argv[0];
+
+       for (i = 1; i < argc; i++)
+               fixup_buggy_yacc_file(argv[i]);
+       return 0;
+}
diff --git a/exp/test-expression-parser.c b/exp/test-expression-parser.c
new file mode 100644 (file)
index 0000000..022df01
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 2014, Stephen M. Cameron.
+ *
+ * The license below covers all files distributed with fio unless otherwise
+ * noted in the file itself.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../y.tab.h"
+
+extern int evaluate_arithmetic_expression(const char *buffer, long long *ival,
+                                         double *dval);
+int main(int argc, char *argv[])
+{
+       int rc, bye = 0;
+       long long result;
+       double dresult;
+       char buffer[100];
+
+       do {
+               if (fgets(buffer, 90, stdin) == NULL)
+                       break;
+               rc = strlen(buffer);
+               if (rc > 0 && buffer[rc - 1] == '\n')
+                       buffer[rc - 1] = '\0';
+               rc = evaluate_arithmetic_expression(buffer, &result, &dresult);
+               if (!rc) {
+                       printf("%lld (%20.20lf)\n", result, dresult);
+               } else {
+                       result = 0;
+                       dresult = 0;
+               }
+       } while (!bye);
+       return 0;
+}
+
diff --git a/fio.1 b/fio.1
index d64fbb7ab5e0832f61f167d7fe2b20872f9ec2c1..01c7cec03b5b345b3ac135608c5846844d145980 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -115,7 +115,15 @@ and there may be any number of global sections.  Specific job definitions
 may override any parameter set in global sections.
 .SH "JOB PARAMETERS"
 .SS Types
-Some parameters may take arguments of a specific type.  The types used are:
+Some parameters may take arguments of a specific type.
+Anywhere a numeric value is required, an arithmetic expression may be used,
+provided it is surrounded by parentheses.
+Supported operators are
+addition, subtraction, multiplication, division, modulus, and exponentiation.
+For time values in expressions
+units are microseconds by default.  This is different than for time
+values not in expressions (not enclosed in parentheses).
+The types used are:
 .TP
 .I str
 String: a sequence of alphanumeric characters.
diff --git a/parse.c b/parse.c
index 40cd46587d3e03477451fd3ef2aaa8b832c6ef1a..b632bf1adc10dfbeb580c17776a15095400fc90a 100644 (file)
--- a/parse.c
+++ b/parse.c
 #include "minmax.h"
 #include "lib/ieee754.h"
 
+#ifdef CONFIG_ARITHMETIC
+#include "y.tab.h"
+#endif
+
 static struct fio_option *__fio_options;
 
 static int vp_cmp(const void *p1, const void *p2)
@@ -264,12 +268,28 @@ static unsigned long long get_mult_bytes(const char *str, int len, void *data,
        return __get_mult_bytes(p, data, percent);
 }
 
+extern int evaluate_arithmetic_expression(const char *buffer, long long *ival,
+                                         double *dval);
+
 /*
  * Convert string into a floating number. Return 1 for success and 0 otherwise.
  */
 int str_to_float(const char *str, double *val)
 {
-       return (1 == sscanf(str, "%lf", val));
+#ifdef CONFIG_ARITHMETIC
+       int rc;
+       long long ival;
+       double dval;
+
+       if (str[0] == '(') {
+               rc = evaluate_arithmetic_expression(str, &ival, &dval);
+               if (!rc) {
+                       *val = dval;
+                       return 1;
+               }
+       }
+#endif
+       return 1 == sscanf(str, "%lf", val);
 }
 
 /*
@@ -279,19 +299,37 @@ int str_to_decimal(const char *str, long long *val, int kilo, void *data,
                   int is_seconds)
 {
        int len, base;
+       int rc = 1;
+#ifdef CONFIG_ARITHMETIC
+       long long ival;
+       double dval;
+#endif
 
        len = strlen(str);
        if (!len)
                return 1;
 
-       if (strstr(str, "0x") || strstr(str, "0X"))
-               base = 16;
-       else
-               base = 10;
+#ifdef CONFIG_ARITHMETIC
+       if (str[0] == '(')
+               rc = evaluate_arithmetic_expression(str, &ival, &dval);
+       if (str[0] == '(' && !rc) {
+               if (!kilo && is_seconds)
+                       *val = ival / 1000000LL;
+               else
+                       *val = ival;
+       }
+#endif
 
-       *val = strtoll(str, NULL, base);
-       if (*val == LONG_MAX && errno == ERANGE)
-               return 1;
+       if (rc == 1) {
+               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) {
                unsigned long long mult;