From da93e9d6351e238ff66d04b9a7a712f161e2defc Mon Sep 17 00:00:00 2001 From: "cebka@cebka-laptop" Date: Mon, 13 Oct 2008 19:06:07 +0400 Subject: [PATCH] * Implement expression parser (convertor to inverse polish record) * Improve configure system by implementing dependencies * Add .depend files for subdirs that requre it * Write simple test for expressions parser --- configure | 172 ++++++++++++++++++++--------- main.h | 11 ++ test/.depends | 7 ++ test/Makefile.in | 4 +- test/rspamd_expression_test.c | 57 ++++++++++ test/rspamd_memcached_test.c | 2 +- test/rspamd_test_suite.c | 5 +- test/tests.h | 3 + util.c | 196 ++++++++++++++++++++++++++++++++++ utils/.depends | 7 ++ utils/Makefile.in | 4 +- 11 files changed, 407 insertions(+), 61 deletions(-) create mode 100644 test/.depends create mode 100644 test/rspamd_expression_test.c create mode 100644 utils/.depends diff --git a/configure b/configure index 20a413419..73eab3b06 100755 --- a/configure +++ b/configure @@ -45,6 +45,9 @@ PERL_MAKEFILE="perl/Makefile.PL.in" PERLCFLAGS="" PERLLDFLAGS="" +DEPENDS_FILE=".depends" +OPTS="" + TARGETS="${EXEC}" prepare_config() @@ -69,6 +72,7 @@ cleanup() have_opt() { echo "#define HAVE_$1" >> $CONFIG + OPTS="$OPTS HAVE_$1" } check_compiler() @@ -404,16 +408,49 @@ check_group() fi } -write_result() +check_depends() +{ + ifdep="" + depends="" + if [ ! -f $DEPENDS_FILE ] ; then + return 0 + fi + + while read line ; do + echo $line | grep '#if' > /dev/null + if [ $? -eq 0 ] ; then + ifdep=`echo $line | sed -e 's/^#if \([A-Za-z0-9_]*\)$/\1/'` + elif [ "F$ifdep" != "F" ] ; then + echo $line | grep "#endif" > /dev/null + if [ $? -eq 0 ] ; then + ifdep="" + else + echo $OPTS | grep $ifdep > /dev/null + if [ $? -eq 0 ] ; then + depends="$depends $line" + fi + fi + else + depends="$depends $line" + fi + done < $DEPENDS_FILE + + obj_depends=`echo $depends | sed -e 's/\([^ ]\{1,\}\)\/\([^/]\{1,\}\).c/\2.o/g'` + echo "DEPENDS=$depends" >> $MAKEFILE + echo "OBJ_DEPENDS=$obj_depends" >> $MAKEFILE + for _dep in $depends ; do + _obj_dep=`echo $_dep | sed -e 's/\([^ ]\{1,\}\)\/\([^/]\{1,\}\).c/\2.o/g'` + cat >> $MAKEFILE.dep << END +${_obj_dep}: ${_dep} + \$(CC) \$(OPT_FLAGS) \$(CFLAGS) \$(PTHREAD_CFLAGS) -o ${_obj_dep} -c ${_dep} + +END + + done +} + +write_modules() { - echo "Compiler: $GCC" >> config.log - echo "Make: $MAKE" >> config.log - echo "Sources: $SOURCES" >> config.log - echo "Cflags: $CFLAGS" >> config.log - echo "Ldflags: $LDFLAGS" >> config.log - echo "Libs: $LIBS" >> config.log - echo "#define RVERSION \"${VERSION}\"" >> $CONFIG - echo "#define HASH_COMPAT" >> $CONFIG # Write modules init function echo "#ifndef MODULES_H" > modules.h echo "#include \"config.h\"" >> modules.h @@ -430,6 +467,11 @@ write_result() echo "#define MODULES_NUM $modules_num" >> $CONFIG SOURCES="$SOURCES modules.c" OBJECTS=`echo $SOURCES | sed -e 's/\.c/\.o/g'` + +} + +write_perl() +{ # Write to perl Makefile sed -e "s|%%libs%%|${LDFLAGS} ${LIBS}|" < $PERL_MAKEFILE > .pl_tmp sed -e "s|%%include%%|${CFLAGS}|" < .pl_tmp > .pl2_tmp @@ -440,6 +482,74 @@ write_result() cd `dirname ${PERL_MAKEFILE}` $PERL Makefile.PL cd $CURDIR +} + +write_subdirs() +{ + # Write subdirs makefiles + clean_target="clean-subdirs: " + dist_clean_target="dist-clean-subdirs: " + for sub in $SUBDIRS ; do + cp $MAKEFILE $sub/$MAKEFILE + saved_pwd=`pwd` + old_objs=`echo $OBJECTS | sed -e 's/\([^. ]*\.o\)/..\/\1/g'` + old_srcs=`echo $SOURCES | sed -e 's/\([^. ]*\.c\)/..\/\1/g'` + cd $sub + sub_src="`echo *.c`" + sub_obj="`echo $sub_src | sed -e 's/\.c/\.o/g'`" + echo "SOURCES=$sub_src" >> $MAKEFILE + echo "OBJECTS=$sub_obj" >> $MAKEFILE + check_depends + cat Makefile.in >> $MAKEFILE + if [ -f $MAKEFILE.dep ] ; then + cat $MAKEFILE.dep >> $MAKEFILE + rm -f $MAKEFILE.dep + fi + for _sub_src in $sub_src ; do + _sub_obj=`echo $_sub_src | sed -e 's/\.c/\.o/g'` + cat >> $MAKEFILE << END +${_sub_obj}: ${_sub_src} + \$(CC) \$(OPT_FLAGS) \$(CFLAGS) \$(PTHREAD_CFLAGS) -o ${_sub_obj} -c ${_sub_src} + +END + done + cd $saved_pwd + dist_clean_target="$dist_clean_target ${sub}-dist-clean" + clean_target="$clean_target ${sub}-clean" + done + + # Write IN file + cat $MAKEFILE_IN >> $MAKEFILE + + # Process clean targets for all subdirs + echo $dist_clean_target >> $MAKEFILE + echo $clean_target >> $MAKEFILE + for sub in $SUBDIRS ; do + cat >> $MAKEFILE << END +${sub}-clean: + cd ${sub} && make clean && cd .. +${sub}-dist-clean: + cd ${sub} && make dist-clean && cd .. +${sub}: + cd ${sub} && make && cd .. + +END + done +} + +write_result() +{ + echo "Compiler: $GCC" >> config.log + echo "Make: $MAKE" >> config.log + echo "Sources: $SOURCES" >> config.log + echo "Cflags: $CFLAGS" >> config.log + echo "Ldflags: $LDFLAGS" >> config.log + echo "Libs: $LIBS" >> config.log + echo "#define RVERSION \"${VERSION}\"" >> $CONFIG + echo "#define HASH_COMPAT" >> $CONFIG + + write_modules + write_perl # Make CFLAGS more readable CFLAGS="$CFLAGS $PERLCFLAGS" LIBS="$LIBS $PERLLDFLAGS" @@ -495,50 +605,8 @@ MANPATH=$MANPATH SUBDIRS=$SUBDIRS END - # Write subdirs makefiles - clean_target="clean-subdirs: " - dist_clean_target="dist-clean-subdirs: " - for sub in $SUBDIRS ; do - cp $MAKEFILE $sub/$MAKEFILE - saved_pwd=`pwd` - old_objs=`echo $OBJECTS | sed -e 's/\([^. ]*\.o\)/..\/\1/g'` - old_srcs=`echo $SOURCES | sed -e 's/\([^. ]*\.c\)/..\/\1/g'` - cd $sub - sub_src="`echo *.c`" - sub_obj="`echo $sub_src | sed -e 's/\.c/\.o/g'`" - echo "SOURCES=$sub_src" >> $MAKEFILE - echo "OBJECTS=$sub_obj" >> $MAKEFILE - cat Makefile.in >> $MAKEFILE - for _sub_src in $sub_src ; do - _sub_obj=`echo $_sub_src | sed -e 's/\.c/\.o/g'` - cat >> $MAKEFILE << END -${_sub_obj}: ${_sub_src} - \$(CC) \$(OPT_FLAGS) \$(CFLAGS) \$(PTHREAD_CFLAGS) -o ${_sub_obj} -c ${_sub_src} - -END - done - cd $saved_pwd - dist_clean_target="$dist_clean_target ${sub}-dist-clean" - clean_target="$clean_target ${sub}-clean" - done - - # Write IN file - cat $MAKEFILE_IN >> $MAKEFILE - - # Process clean targets for all subdirs - echo $dist_clean_target >> $MAKEFILE - echo $clean_target >> $MAKEFILE - for sub in $SUBDIRS ; do - cat >> $MAKEFILE << END -${sub}-clean: - cd ${sub} && make clean && cd .. -${sub}-dist-clean: - cd ${sub} && make dist-clean && cd .. -${sub}: - cd ${sub} && make && cd .. -END - done + write_subdirs # Write build targets to makefile cat >> $MAKEFILE << END diff --git a/main.h b/main.h index adefa576f..f111ba1b4 100644 --- a/main.h +++ b/main.h @@ -56,6 +56,16 @@ enum script_type { SCRIPT_MESSAGE, }; +/* Logic expression */ +struct expression { + enum { EXPR_OPERAND, EXPR_OPERATION } type; + union { + void *operand; + char operation; + } content; + struct expression *next; +}; + /* Worker process structure */ struct rspamd_worker { pid_t pid; @@ -153,6 +163,7 @@ struct c_module { }; void start_worker (struct rspamd_worker *worker, int listen_sock); +struct expression* parse_expression (memory_pool_t *pool, char *line); #endif diff --git a/test/.depends b/test/.depends new file mode 100644 index 000000000..52848a04a --- /dev/null +++ b/test/.depends @@ -0,0 +1,7 @@ +#if HAVE_STRLCPY_H +../strlcpy.c +#endif +../mem_pool.c +../url.c +../util.c +../memcached.c diff --git a/test/Makefile.in b/test/Makefile.in index 14a2646b9..08f041e4e 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -2,8 +2,8 @@ all: rspamd_test_suite -rspamd_test_suite: $(OBJECTS) ../url.o ../util.o ../memcached.o - $(CC) $(PTHREAD_LDFLAGS) $(LDFLAGS) $(OBJECTS) ../url.o ../util.o ../memcached.o ../mem_pool.o $(LIBS) -o rspamd_test_suite +rspamd_test_suite: $(OBJECTS) $(OBJ_DEPENDS) + $(CC) $(PTHREAD_LDFLAGS) $(LDFLAGS) $(OBJECTS) $(OBJ_DEPENDS) $(LIBS) -o rspamd_test_suite run_test: rspamd_test_suite gtester --verbose -k -o=rspamd_test.xml ./rspamd_test_suite diff --git a/test/rspamd_expression_test.c b/test/rspamd_expression_test.c new file mode 100644 index 000000000..5d8e2a6f2 --- /dev/null +++ b/test/rspamd_expression_test.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../config.h" +#include "../main.h" +#include "../cfg_file.h" +#include "tests.h" + +/* Vector of test expressions */ +char *test_expressions[] = { + "(A&B|!C)&!(D|E)", + "/test&!/&!/\\/|/", + NULL +}; + +void +rspamd_expression_test_func () +{ + memory_pool_t *pool; + struct expression *cur; + char **line, *outstr; + int r, s; + + pool = memory_pool_new (1024); + + line = test_expressions; + while (*line) { + r = 0; + cur = parse_expression (pool, *line); + s = strlen (*line) + 1; + outstr = memory_pool_alloc (pool, s); + while (cur) { + if (cur->type == EXPR_OPERAND) { + r += snprintf (outstr + r, s - r, "%s", (char *)cur->content.operand); + } + else { + r += snprintf (outstr + r, s - r, "%c", cur->content.operation); + } + cur = cur->next; + } + msg_debug ("Parsed expression: '%s' -> '%s'", *line, outstr); + line ++; + } + + memory_pool_delete (pool); +} diff --git a/test/rspamd_memcached_test.c b/test/rspamd_memcached_test.c index 1993c756e..60ae1d3eb 100644 --- a/test/rspamd_memcached_test.c +++ b/test/rspamd_memcached_test.c @@ -33,7 +33,7 @@ memcached_callback (memcached_ctx_t *ctx, memc_error_t error, void *data) break; case CMD_READ: g_assert (error == OK); - g_assert (!strcmp(ctx->param->buf, buf)); + g_assert (!memcmp(ctx->param->buf, buf, ctx->param->bufsize)); msg_debug ("Read ok"); memc_close_ctx (ctx); tv.tv_sec = 0; diff --git a/test/rspamd_test_suite.c b/test/rspamd_test_suite.c index 90e64b112..dba448a2c 100644 --- a/test/rspamd_test_suite.c +++ b/test/rspamd_test_suite.c @@ -13,10 +13,6 @@ #include "../cfg_file.h" #include "tests.h" -#ifdef HAVE_STRLCPY_H -#include "../strlcpy.c" -#endif - int main (int argc, char **argv) { @@ -27,6 +23,7 @@ main (int argc, char **argv) g_test_add_func ("/rspamd/memcached", rspamd_memcached_test_func); g_test_add_func ("/rspamd/mem_pool", rspamd_mem_pool_test_func); g_test_add_func ("/rspamd/url", rspamd_url_test_func); + g_test_add_func ("/rspamd/expression", rspamd_expression_test_func); g_test_run (); } diff --git a/test/tests.h b/test/tests.h index 3eddf92fc..7ff763623 100644 --- a/test/tests.h +++ b/test/tests.h @@ -14,4 +14,7 @@ void rspamd_memcached_test_func (); /* Memory pools */ void rspamd_mem_pool_test_func (); +/* Expressions */ +void rspamd_expression_test_func (); + #endif diff --git a/util.c b/util.c index 78ca58e5a..0bd7f91c8 100644 --- a/util.c +++ b/util.c @@ -542,3 +542,199 @@ pidfile_remove(struct pidfh *pfh) return (_pidfile_remove(pfh, 1)); } #endif + +/* + * Functions for parsing expressions + */ + +struct expression_stack { + char op; + struct expression_stack *next; +}; + +/* + * Push operand or operator to stack + */ +static struct expression_stack* +push_expression_stack (memory_pool_t *pool, struct expression_stack *head, char op) +{ + struct expression_stack *new; + new = memory_pool_alloc (pool, sizeof (struct expression_stack)); + new->op = op; + new->next = head; + return new; +} + +/* + * Delete symbol from stack, return pointer to operand or operator (casted to void* ) + */ +static char +delete_expression_stack (struct expression_stack **head) +{ + struct expression_stack *cur; + char res; + + if(*head == NULL) return 0; + + cur = *head; + res = cur->op; + + *head = cur->next; + return res; +} + +/* + * Return operation priority + */ +static int +logic_priority (char a) +{ + switch (a) { + case '!': + return 3; + case '|': + case '&': + return 2; + case '(': + return 1; + default: + return 0; + } +} + +/* + * Return 0 if symbol is not operation symbol (operand) + * Return 1 if symbol is operation symbol + */ +static int +is_operation_symbol (char a) +{ + switch (a) { + case '!': + case '&': + case '|': + case '(': + case ')': + return 1; + default: + return 0; + } +} + +static void +insert_expression (memory_pool_t *pool, struct expression **head, int type, char op, void *operand) +{ + struct expression *new, *cur; + + new = memory_pool_alloc (pool, sizeof (struct expression)); + new->type = type; + if (new->type == EXPR_OPERAND) { + new->content.operand = operand; + } + else { + new->content.operation = op; + } + new->next = NULL; + + if (!*head) { + *head = new; + } + else { + cur = *head; + while (cur->next) { + cur = cur->next; + } + cur->next = new; + } +} + +/* + * Make inverse polish record for specified expression + * Memory is allocated from given pool + */ +struct expression* +parse_expression (memory_pool_t *pool, char *line) +{ + struct expression *expr = NULL; + struct expression_stack *stack = NULL; + char *p, *c, *str, op, in_regexp = 0; + + if (line == NULL || pool == NULL) { + return NULL; + } + + p = line; + c = p; + while (*p) { + if (is_operation_symbol (*p) && !in_regexp) { + if (c != p) { + /* Copy operand */ + str = memory_pool_alloc (pool, p - c + 1); + strlcpy (str, c, (p - c + 1)); + insert_expression (pool, &expr, EXPR_OPERAND, 0, str); + } + if (*p == ')') { + if (stack == NULL) { + return NULL; + } + /* Pop all operators from stack to nearest '(' or to head */ + while (stack->op != '(') { + op = delete_expression_stack (&stack); + if (op != '(') { + insert_expression (pool, &expr, EXPR_OPERATION, op, NULL); + } + } + } + else if (*p == '(') { + /* Push it to stack */ + stack = push_expression_stack (pool, stack, *p); + } + else { + if (stack == NULL) { + stack = push_expression_stack (pool, stack, *p); + } + /* Check priority of logic operation */ + else { + if (logic_priority (stack->op) < logic_priority (*p)) { + stack = push_expression_stack (pool, stack, *p); + } + else { + /* Pop all operations that have higher priority than this one */ + while((stack != NULL) && (logic_priority (stack->op) >= logic_priority (*p))) { + op = delete_expression_stack (&stack); + if (op != '(') { + insert_expression (pool, &expr, EXPR_OPERATION, op, NULL); + } + } + stack = push_expression_stack (pool, stack, *p); + } + } + } + c = p + 1; + } + if (*p == '/' && (p == line || *(p - 1) != '\\')) { + in_regexp = !in_regexp; + } + p++; + } + /* Write last operand if it exists */ + if (c != p) { + /* Copy operand */ + str = memory_pool_alloc (pool, p - c + 1); + strlcpy (str, c, (p - c + 1)); + insert_expression (pool, &expr, EXPR_OPERAND, 0, str); + } + /* Pop everything from stack */ + while(stack != NULL) { + op = delete_expression_stack (&stack); + if (op != '(') { + insert_expression (pool, &expr, EXPR_OPERATION, op, NULL); + } + } + + return expr; +} + +/* + * vi:ts=4 + */ diff --git a/utils/.depends b/utils/.depends new file mode 100644 index 000000000..52848a04a --- /dev/null +++ b/utils/.depends @@ -0,0 +1,7 @@ +#if HAVE_STRLCPY_H +../strlcpy.c +#endif +../mem_pool.c +../url.c +../util.c +../memcached.c diff --git a/utils/Makefile.in b/utils/Makefile.in index 4ceb18ae1..3b9aa8e07 100644 --- a/utils/Makefile.in +++ b/utils/Makefile.in @@ -2,8 +2,8 @@ all: url_extracter -url_extracter: $(OBJECTS) ../url.o ../util.o - $(CC) $(PTHREAD_LDFLAGS) $(LDFLAGS) $(OBJECTS) ../url.o ../util.o ../mem_pool.o $(LIBS) -o url_extracter +url_extracter: $(OBJECTS) $(OBJ_DEPENDS) + $(CC) $(PTHREAD_LDFLAGS) $(LDFLAGS) $(OBJECTS) $(OBJ_DEPENDS) $(LIBS) -o url_extracter clean: rm -f *.o url_extracter *.core -- 2.39.5