summaryrefslogtreecommitdiff
path: root/src/regex
diff options
context:
space:
mode:
authorBertrand Marc <beberking@gmail.com>2012-06-06 20:47:48 +0200
committerBertrand Marc <beberking@gmail.com>2012-06-06 20:47:48 +0200
commit740b30688bd745a527f96f9116c19acb3480971a (patch)
tree2709a3f4dba11c174aa9e1ba3612e30c578e76a9 /src/regex
parent2b81464a43485fcc8ce079fafdee7b7a171835f4 (diff)
Imported Upstream version 0.9.3upstream/0.9.3
Diffstat (limited to 'src/regex')
-rw-r--r--src/regex/Makefile.am42
-rw-r--r--src/regex/Makefile.in800
-rw-r--r--src/regex/regex.c2252
-rw-r--r--src/regex/test_regex_eval_api.c311
-rw-r--r--src/regex/test_regex_iterate_api.c71
5 files changed, 3476 insertions, 0 deletions
diff --git a/src/regex/Makefile.am b/src/regex/Makefile.am
new file mode 100644
index 0000000..8c73c60
--- /dev/null
+++ b/src/regex/Makefile.am
@@ -0,0 +1,42 @@
+INCLUDES = -I$(top_srcdir)/src/include
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage
+endif
+
+lib_LTLIBRARIES = libgnunetregex.la
+
+libgnunetregex_la_SOURCES = \
+ regex.c
+libgnunetregex_la_LIBADD = -lm \
+ $(top_builddir)/src/util/libgnunetutil.la
+libgnunetregex_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) \
+ -version-info 0:0:0
+
+check_PROGRAMS = \
+ test_regex_eval_api \
+ test_regex_iterate_api
+
+if ENABLE_TEST_RUN
+TESTS = $(check_PROGRAMS)
+endif
+
+test_regex_eval_api_SOURCES = \
+ test_regex_eval_api.c
+test_regex_eval_api_LDADD = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_iterate_api_SOURCES = \
+ test_regex_iterate_api.c
+test_regex_iterate_api_LDADD = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST =
+# test_regex_data.conf
diff --git a/src/regex/Makefile.in b/src/regex/Makefile.in
new file mode 100644
index 0000000..c0c0704
--- /dev/null
+++ b/src/regex/Makefile.in
@@ -0,0 +1,800 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+check_PROGRAMS = test_regex_eval_api$(EXEEXT) \
+ test_regex_iterate_api$(EXEEXT)
+subdir = src/regex
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \
+ $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/gnunet_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libgnunetregex_la_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_libgnunetregex_la_OBJECTS = regex.lo
+libgnunetregex_la_OBJECTS = $(am_libgnunetregex_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libgnunetregex_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libgnunetregex_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+am_test_regex_eval_api_OBJECTS = test_regex_eval_api.$(OBJEXT)
+test_regex_eval_api_OBJECTS = $(am_test_regex_eval_api_OBJECTS)
+test_regex_eval_api_DEPENDENCIES = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_regex_iterate_api_OBJECTS = test_regex_iterate_api.$(OBJEXT)
+test_regex_iterate_api_OBJECTS = $(am_test_regex_iterate_api_OBJECTS)
+test_regex_iterate_api_DEPENDENCIES = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libgnunetregex_la_SOURCES) $(test_regex_eval_api_SOURCES) \
+ $(test_regex_iterate_api_SOURCES)
+DIST_SOURCES = $(libgnunetregex_la_SOURCES) \
+ $(test_regex_eval_api_SOURCES) \
+ $(test_regex_iterate_api_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ARGZ_H = @ARGZ_H@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_INTERFACE = @DEFAULT_INTERFACE@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLDIR = @DLLDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXT_LIBS = @EXT_LIBS@
+EXT_LIB_PATH = @EXT_LIB_PATH@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUNETDNS_GROUP = @GNUNETDNS_GROUP@
+GN_DAEMON_CONFIG_DIR = @GN_DAEMON_CONFIG_DIR@
+GN_DAEMON_HOME_DIR = @GN_DAEMON_HOME_DIR@
+GN_INTLINCL = @GN_INTLINCL@
+GN_LIBINTL = @GN_LIBINTL@
+GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@
+GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@
+GN_USER_HOME_DIR = @GN_USER_HOME_DIR@
+GREP = @GREP@
+HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@
+INCLTDL = @INCLTDL@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+JAVAPORT = @JAVAPORT@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBLTDL = @LIBLTDL@
+LIBOBJS = @LIBOBJS@
+LIBPREFIX = @LIBPREFIX@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTDLDEPS = @LTDLDEPS@
+LTDLINCL = @LTDLINCL@
+LTDLOPEN = @LTDLOPEN@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBUNISTRING = @LTLIBUNISTRING@
+LT_CONFIG_H = @LT_CONFIG_H@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MONKEYPREFIX = @MONKEYPREFIX@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LDFLAGS = @MYSQL_LDFLAGS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJC = @OBJC@
+OBJCDEPMODE = @OBJCDEPMODE@
+OBJCFLAGS = @OBJCFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@
+POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CPPFLAGS = @SQLITE_CPPFLAGS@
+SQLITE_LDFLAGS = @SQLITE_LDFLAGS@
+STRIP = @STRIP@
+SUDO_BINARY = @SUDO_BINARY@
+UNIXONLY = @UNIXONLY@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+_libcurl_config = @_libcurl_config@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_OBJC = @ac_ct_OBJC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_target = @build_target@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+ltdl_LIBOBJS = @ltdl_LIBOBJS@
+ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sys_symbol_underscore = @sys_symbol_underscore@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I$(top_srcdir)/src/include
+@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage
+lib_LTLIBRARIES = libgnunetregex.la
+libgnunetregex_la_SOURCES = \
+ regex.c
+
+libgnunetregex_la_LIBADD = -lm \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+libgnunetregex_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) \
+ -version-info 0:0:0
+
+@ENABLE_TEST_RUN_TRUE@TESTS = $(check_PROGRAMS)
+test_regex_eval_api_SOURCES = \
+ test_regex_eval_api.c
+
+test_regex_eval_api_LDADD = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_iterate_api_SOURCES = \
+ test_regex_iterate_api.c
+
+test_regex_iterate_api_LDADD = \
+ $(top_builddir)/src/regex/libgnunetregex.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST =
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/regex/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/regex/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgnunetregex.la: $(libgnunetregex_la_OBJECTS) $(libgnunetregex_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgnunetregex_la_LINK) -rpath $(libdir) $(libgnunetregex_la_OBJECTS) $(libgnunetregex_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+test_regex_eval_api$(EXEEXT): $(test_regex_eval_api_OBJECTS) $(test_regex_eval_api_DEPENDENCIES)
+ @rm -f test_regex_eval_api$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_regex_eval_api_OBJECTS) $(test_regex_eval_api_LDADD) $(LIBS)
+test_regex_iterate_api$(EXEEXT): $(test_regex_iterate_api_OBJECTS) $(test_regex_iterate_api_DEPENDENCIES)
+ @rm -f test_regex_iterate_api$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_regex_iterate_api_OBJECTS) $(test_regex_iterate_api_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_regex_eval_api.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_regex_iterate_api.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ echo "$$grn$$dashes"; \
+ else \
+ echo "$$red$$dashes"; \
+ fi; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes$$std"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-libLTLIBRARIES
+
+# test_regex_data.conf
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/regex/regex.c b/src/regex/regex.c
new file mode 100644
index 0000000..5244c26
--- /dev/null
+++ b/src/regex/regex.c
@@ -0,0 +1,2252 @@
+/*
+ This file is part of GNUnet
+ (C) 2012 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file src/regex/regex.c
+ * @brief library to create automatons from regular expressions
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_regex_lib.h"
+#include "regex.h"
+
+#define initial_bits 10
+
+/**
+ * Context that contains an id counter for states and transitions as well as a
+ * DLL of automatons used as a stack for NFA construction.
+ */
+struct GNUNET_REGEX_Context
+{
+ /**
+ * Unique state id.
+ */
+ unsigned int state_id;
+
+ /**
+ * Unique transition id.
+ */
+ unsigned int transition_id;
+
+ /**
+ * Unique SCC (Strongly Connected Component) id.
+ */
+ unsigned int scc_id;
+
+ /**
+ * DLL of GNUNET_REGEX_Automaton's used as a stack.
+ */
+ struct GNUNET_REGEX_Automaton *stack_head;
+
+ /**
+ * DLL of GNUNET_REGEX_Automaton's used as a stack.
+ */
+ struct GNUNET_REGEX_Automaton *stack_tail;
+};
+
+/**
+ * Type of an automaton.
+ */
+enum GNUNET_REGEX_automaton_type
+{
+ NFA,
+ DFA
+};
+
+/**
+ * Automaton representation.
+ */
+struct GNUNET_REGEX_Automaton
+{
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_REGEX_Automaton *prev;
+
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_REGEX_Automaton *next;
+
+ /**
+ * First state of the automaton. This is mainly used for constructing an NFA,
+ * where each NFA itself consists of one or more NFAs linked together.
+ */
+ struct GNUNET_REGEX_State *start;
+
+ /**
+ * End state of the automaton.
+ */
+ struct GNUNET_REGEX_State *end;
+
+ /**
+ * Number of states in the automaton.
+ */
+ unsigned int state_count;
+
+ /**
+ * DLL of states.
+ */
+ struct GNUNET_REGEX_State *states_head;
+
+ /**
+ * DLL of states
+ */
+ struct GNUNET_REGEX_State *states_tail;
+
+ /**
+ * Type of the automaton.
+ */
+ enum GNUNET_REGEX_automaton_type type;
+};
+
+/**
+ * A state. Can be used in DFA and NFA automatons.
+ */
+struct GNUNET_REGEX_State
+{
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_REGEX_State *prev;
+
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_REGEX_State *next;
+
+ /**
+ * Unique state id.
+ */
+ unsigned int id;
+
+ /**
+ * If this is an accepting state or not.
+ */
+ int accepting;
+
+ /**
+ * Marking of the state. This is used for marking all visited states when
+ * traversing all states of an automaton and for cases where the state id
+ * cannot be used (dfa minimization).
+ */
+ int marked;
+
+ /**
+ * Marking the state as contained. This is used for checking, if the state is
+ * contained in a set in constant time
+ */
+ int contained;
+
+ /**
+ * Marking the state as part of an SCC (Strongly Connected Component). All
+ * states with the same scc_id are part of the same SCC. scc_id is 0, if state
+ * is not a part of any SCC.
+ */
+ unsigned int scc_id;
+
+ /**
+ * Used for SCC detection.
+ */
+ int index;
+
+ /**
+ * Used for SCC detection.
+ */
+ int lowlink;
+
+ /**
+ * Human readable name of the automaton. Used for debugging and graph
+ * creation.
+ */
+ char *name;
+
+ /**
+ * Hash of the state.
+ */
+ GNUNET_HashCode hash;
+
+ /**
+ * Proof for this state.
+ */
+ char *proof;
+
+ /**
+ * Number of transitions from this state to other states.
+ */
+ unsigned int transition_count;
+
+ /**
+ * DLL of transitions.
+ */
+ struct Transition *transitions_head;
+
+ /**
+ * DLL of transitions.
+ */
+ struct Transition *transitions_tail;
+
+ /**
+ * Set of states on which this state is based on. Used when creating a DFA out
+ * of several NFA states.
+ */
+ struct GNUNET_REGEX_StateSet *nfa_set;
+};
+
+/**
+ * Transition between two states. Each state can have 0-n transitions. If label
+ * is 0, this is considered to be an epsilon transition.
+ */
+struct Transition
+{
+ /**
+ * This is a linked list.
+ */
+ struct Transition *prev;
+
+ /**
+ * This is a linked list.
+ */
+ struct Transition *next;
+
+ /**
+ * Unique id of this transition.
+ */
+ unsigned int id;
+
+ /**
+ * Label for this transition. This is basically the edge label for the graph.
+ */
+ char label;
+
+ /**
+ * State to which this transition leads.
+ */
+ struct GNUNET_REGEX_State *to_state;
+
+ /**
+ * State from which this transition origins.
+ */
+ struct GNUNET_REGEX_State *from_state;
+
+ /**
+ * Mark this transition. For example when reversing the automaton.
+ */
+ int mark;
+};
+
+/**
+ * Set of states.
+ */
+struct GNUNET_REGEX_StateSet
+{
+ /**
+ * Array of states.
+ */
+ struct GNUNET_REGEX_State **states;
+
+ /**
+ * Length of the 'states' array.
+ */
+ unsigned int len;
+};
+
+/*
+ * Debug helper functions
+ */
+void
+debug_print_transitions (struct GNUNET_REGEX_State *);
+
+void
+debug_print_state (struct GNUNET_REGEX_State *s)
+{
+ char *proof;
+
+ if (NULL == s->proof)
+ proof = "NULL";
+ else
+ proof = s->proof;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "State %i: %s marked: %i accepting: %i scc_id: %i transitions: %i proof: %s\n",
+ s->id, s->name, s->marked, s->accepting, s->scc_id,
+ s->transition_count, proof);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transitions:\n");
+ debug_print_transitions (s);
+}
+
+void
+debug_print_states (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ debug_print_state (s);
+}
+
+void
+debug_print_transition (struct Transition *t)
+{
+ char *to_state;
+ char *from_state;
+ char label;
+
+ if (NULL == t)
+ return;
+
+ if (0 == t->label)
+ label = '0';
+ else
+ label = t->label;
+
+ if (NULL == t->to_state)
+ to_state = "NULL";
+ else
+ to_state = t->to_state->name;
+
+ if (NULL == t->from_state)
+ from_state = "NULL";
+ else
+ from_state = t->from_state->name;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transition %i: From %s on %c to %s\n",
+ t->id, from_state, label, to_state);
+}
+
+void
+debug_print_transitions (struct GNUNET_REGEX_State *s)
+{
+ struct Transition *t;
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ debug_print_transition (t);
+}
+
+/**
+ * Recursive function doing DFS with 'v' as a start, detecting all SCCs inside
+ * the subgraph reachable from 'v'. Used with scc_tarjan function to detect all
+ * SCCs inside an automaton.
+ *
+ * @param ctx context
+ * @param v start vertex
+ * @param index current index
+ * @param stack stack for saving all SCCs
+ * @param stack_size current size of the stack
+ */
+static void
+scc_tarjan_strongconnect (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_State *v, int *index,
+ struct GNUNET_REGEX_State **stack,
+ unsigned int *stack_size)
+{
+ struct GNUNET_REGEX_State *w;
+ struct Transition *t;
+
+ v->index = *index;
+ v->lowlink = *index;
+ (*index)++;
+ stack[(*stack_size)++] = v;
+ v->contained = 1;
+
+ for (t = v->transitions_head; NULL != t; t = t->next)
+ {
+ w = t->to_state;
+ if (NULL != w && w->index < 0)
+ {
+ scc_tarjan_strongconnect (ctx, w, index, stack, stack_size);
+ v->lowlink = (v->lowlink > w->lowlink) ? w->lowlink : v->lowlink;
+ }
+ else if (0 != w->contained)
+ v->lowlink = (v->lowlink > w->index) ? w->index : v->lowlink;
+ }
+
+ if (v->lowlink == v->index)
+ {
+ w = stack[--(*stack_size)];
+ w->contained = 0;
+
+ if (v != w)
+ {
+ ctx->scc_id++;
+ while (v != w)
+ {
+ w->scc_id = ctx->scc_id;
+ w = stack[--(*stack_size)];
+ w->contained = 0;
+ }
+ w->scc_id = ctx->scc_id;
+ }
+ }
+}
+
+/**
+ * Detect all SCCs (Strongly Connected Components) inside the given automaton.
+ * SCCs will be marked using the scc_id on each state.
+ *
+ * @param ctx context
+ * @param a automaton
+ */
+static void
+scc_tarjan (struct GNUNET_REGEX_Context *ctx, struct GNUNET_REGEX_Automaton *a)
+{
+ int index;
+ struct GNUNET_REGEX_State *v;
+ struct GNUNET_REGEX_State *stack[a->state_count];
+ unsigned int stack_size;
+
+ for (v = a->states_head; NULL != v; v = v->next)
+ {
+ v->contained = 0;
+ v->index = -1;
+ v->lowlink = -1;
+ }
+
+ stack_size = 0;
+ index = 0;
+
+ for (v = a->states_head; NULL != v; v = v->next)
+ {
+ if (v->index < 0)
+ scc_tarjan_strongconnect (ctx, v, &index, stack, &stack_size);
+ }
+}
+
+/**
+ * Adds a transition from one state to another on 'label'. Does not add
+ * duplicate states.
+ *
+ * @param ctx context
+ * @param from_state starting state for the transition
+ * @param label transition label
+ * @param to_state state to where the transition should point to
+ */
+static void
+state_add_transition (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_State *from_state, const char label,
+ struct GNUNET_REGEX_State *to_state)
+{
+ int is_dup;
+ struct Transition *t;
+
+ if (NULL == from_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not create Transition.\n");
+ return;
+ }
+
+ // Do not add duplicate state transitions
+ is_dup = GNUNET_NO;
+ for (t = from_state->transitions_head; NULL != t; t = t->next)
+ {
+ if (t->to_state == to_state && t->label == label &&
+ t->from_state == from_state)
+ {
+ is_dup = GNUNET_YES;
+ break;
+ }
+ }
+
+ if (is_dup)
+ return;
+
+ t = GNUNET_malloc (sizeof (struct Transition));
+ t->id = ctx->transition_id++;
+ t->label = label;
+ t->to_state = to_state;
+ t->from_state = from_state;
+
+ // Add outgoing transition to 'from_state'
+ from_state->transition_count++;
+ GNUNET_CONTAINER_DLL_insert (from_state->transitions_head,
+ from_state->transitions_tail, t);
+}
+
+/**
+ * Compare two states. Used for sorting.
+ *
+ * @param a first state
+ * @param b second state
+ *
+ * @return an integer less than, equal to, or greater than zero
+ * if the first argument is considered to be respectively
+ * less than, equal to, or greater than the second.
+ */
+static int
+state_compare (const void *a, const void *b)
+{
+ struct GNUNET_REGEX_State **s1;
+ struct GNUNET_REGEX_State **s2;
+
+ s1 = (struct GNUNET_REGEX_State **) a;
+ s2 = (struct GNUNET_REGEX_State **) b;
+
+ return (*s1)->id - (*s2)->id;
+}
+
+/**
+ * Get all edges leaving state 's'.
+ *
+ * @param s state.
+ * @param edges all edges leaving 's'.
+ *
+ * @return number of edges.
+ */
+static unsigned int
+state_get_edges (struct GNUNET_REGEX_State *s, struct GNUNET_REGEX_Edge *edges)
+{
+ struct Transition *t;
+ unsigned int count;
+
+ if (NULL == s)
+ return 0;
+
+ count = 0;
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ {
+ if (NULL != t->to_state)
+ {
+ edges[count].label = &t->label;
+ edges[count].destination = t->to_state->hash;
+ count++;
+ }
+ }
+ return count;
+}
+
+/**
+ * Compare to state sets by comparing the id's of the states that are contained
+ * in each set. Both sets are expected to be sorted by id!
+ *
+ * @param sset1 first state set
+ * @param sset2 second state set
+ *
+ * @return an integer less than, equal to, or greater than zero
+ * if the first argument is considered to be respectively
+ * less than, equal to, or greater than the second.
+ */
+static int
+state_set_compare (struct GNUNET_REGEX_StateSet *sset1,
+ struct GNUNET_REGEX_StateSet *sset2)
+{
+ int result;
+ int i;
+
+ if (NULL == sset1 || NULL == sset2)
+ return 1;
+
+ result = sset1->len - sset2->len;
+
+ for (i = 0; i < sset1->len; i++)
+ {
+ if (0 != result)
+ break;
+
+ result = state_compare (&sset1->states[i], &sset2->states[i]);
+ }
+ return result;
+}
+
+/**
+ * Clears the given StateSet 'set'
+ *
+ * @param set set to be cleared
+ */
+static void
+state_set_clear (struct GNUNET_REGEX_StateSet *set)
+{
+ if (NULL != set)
+ {
+ if (NULL != set->states)
+ GNUNET_free (set->states);
+ GNUNET_free (set);
+ }
+}
+
+/**
+ * Clears an automaton fragment. Does not destroy the states inside the
+ * automaton.
+ *
+ * @param a automaton to be cleared
+ */
+static void
+automaton_fragment_clear (struct GNUNET_REGEX_Automaton *a)
+{
+ if (NULL == a)
+ return;
+
+ a->start = NULL;
+ a->end = NULL;
+ a->states_head = NULL;
+ a->states_tail = NULL;
+ a->state_count = 0;
+ GNUNET_free (a);
+}
+
+/**
+ * Frees the memory used by State 's'
+ *
+ * @param s state that should be destroyed
+ */
+static void
+automaton_destroy_state (struct GNUNET_REGEX_State *s)
+{
+ struct Transition *t;
+ struct Transition *next_t;
+
+ if (NULL == s)
+ return;
+
+ if (NULL != s->name)
+ GNUNET_free (s->name);
+
+ if (NULL != s->proof)
+ GNUNET_free (s->proof);
+
+ for (t = s->transitions_head; NULL != t; t = next_t)
+ {
+ next_t = t->next;
+ GNUNET_CONTAINER_DLL_remove (s->transitions_head, s->transitions_tail, t);
+ GNUNET_free (t);
+ }
+
+ state_set_clear (s->nfa_set);
+
+ GNUNET_free (s);
+}
+
+/**
+ * Remove a state from the given automaton 'a'. Always use this function when
+ * altering the states of an automaton. Will also remove all transitions leading
+ * to this state, before destroying it.
+ *
+ * @param a automaton
+ * @param s state to remove
+ */
+static void
+automaton_remove_state (struct GNUNET_REGEX_Automaton *a,
+ struct GNUNET_REGEX_State *s)
+{
+ struct GNUNET_REGEX_State *ss;
+ struct GNUNET_REGEX_State *s_check;
+ struct Transition *t_check;
+
+ if (NULL == a || NULL == s)
+ return;
+
+ // remove state
+ ss = s;
+ GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s);
+ a->state_count--;
+
+ // remove all transitions leading to this state
+ for (s_check = a->states_head; NULL != s_check; s_check = s_check->next)
+ {
+ for (t_check = s_check->transitions_head; NULL != t_check;
+ t_check = t_check->next)
+ {
+ if (t_check->to_state == ss)
+ {
+ GNUNET_CONTAINER_DLL_remove (s_check->transitions_head,
+ s_check->transitions_tail, t_check);
+ s_check->transition_count--;
+ }
+ }
+ }
+
+ automaton_destroy_state (ss);
+}
+
+/**
+ * Merge two states into one. Will merge 's1' and 's2' into 's1' and destroy
+ * 's2'.
+ *
+ * @param ctx context
+ * @param a automaton
+ * @param s1 first state
+ * @param s2 second state, will be destroyed
+ */
+static void
+automaton_merge_states (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_Automaton *a,
+ struct GNUNET_REGEX_State *s1,
+ struct GNUNET_REGEX_State *s2)
+{
+ struct GNUNET_REGEX_State *s_check;
+ struct Transition *t_check;
+ char *new_name;
+
+ GNUNET_assert (NULL != ctx && NULL != a && NULL != s1 && NULL != s2);
+
+ if (s1 == s2)
+ return;
+
+ // 1. Make all transitions pointing to s2 point to s1
+ for (s_check = a->states_head; NULL != s_check; s_check = s_check->next)
+ {
+ for (t_check = s_check->transitions_head; NULL != t_check;
+ t_check = t_check->next)
+ {
+ if (s2 == t_check->to_state)
+ t_check->to_state = s1;
+ }
+ }
+
+ // 2. Add all transitions from s2 to sX to s1
+ for (t_check = s2->transitions_head; NULL != t_check; t_check = t_check->next)
+ {
+ if (t_check->to_state != s1)
+ state_add_transition (ctx, s1, t_check->label, t_check->to_state);
+ }
+
+ // 3. Rename s1 to {s1,s2}
+ new_name = GNUNET_strdup (s1->name);
+ if (NULL != s1->name)
+ {
+ GNUNET_free (s1->name);
+ s1->name = NULL;
+ }
+ GNUNET_asprintf (&s1->name, "{%s,%s}", new_name, s2->name);
+ GNUNET_free (new_name);
+
+ // remove state
+ GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s2);
+ a->state_count--;
+ automaton_destroy_state (s2);
+}
+
+/**
+ * Add a state to the automaton 'a', always use this function to alter the
+ * states DLL of the automaton.
+ *
+ * @param a automaton to add the state to
+ * @param s state that should be added
+ */
+static void
+automaton_add_state (struct GNUNET_REGEX_Automaton *a,
+ struct GNUNET_REGEX_State *s)
+{
+ GNUNET_CONTAINER_DLL_insert (a->states_head, a->states_tail, s);
+ a->state_count++;
+}
+
+/**
+ * Function that is called with each state, when traversing an automaton.
+ *
+ * @param cls closure
+ * @param s state
+ */
+typedef void (*GNUNET_REGEX_traverse_action) (void *cls,
+ struct GNUNET_REGEX_State * s);
+
+/**
+ * Traverses all states that are reachable from state 's'. Expects the states to
+ * be unmarked (s->marked == GNUNET_NO). Performs 'action' on each visited
+ * state.
+ *
+ * @param cls closure.
+ * @param s start state.
+ * @param action action to be performed on each state.
+ */
+static void
+automaton_state_traverse (void *cls, struct GNUNET_REGEX_State *s,
+ GNUNET_REGEX_traverse_action action)
+{
+ struct Transition *t;
+
+ if (GNUNET_NO == s->marked)
+ {
+ s->marked = GNUNET_YES;
+
+ if (action > 0)
+ action (cls, s);
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ automaton_state_traverse (cls, t->to_state, action);
+ }
+}
+
+/**
+ * Traverses the given automaton from it's start state, visiting all reachable
+ * states and calling 'action' on each one of them.
+ *
+ * @param cls closure.
+ * @param a automaton.
+ * @param action action to be performed on each state.
+ */
+static void
+automaton_traverse (void *cls, struct GNUNET_REGEX_Automaton *a,
+ GNUNET_REGEX_traverse_action action)
+{
+ struct GNUNET_REGEX_State *s;
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ s->marked = GNUNET_NO;
+
+ automaton_state_traverse (cls, a->start, action);
+}
+
+/**
+ * Reverses all transitions of the given automaton.
+ *
+ * @param a automaton.
+ */
+static void
+automaton_reverse (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+ struct Transition *t;
+ struct Transition *t_next;
+ struct GNUNET_REGEX_State *s_swp;
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ t->mark = GNUNET_NO;
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ {
+ for (t = s->transitions_head; NULL != t; t = t_next)
+ {
+ t_next = t->next;
+
+ if (GNUNET_YES == t->mark || t->from_state == t->to_state)
+ continue;
+
+ t->mark = GNUNET_YES;
+
+ GNUNET_CONTAINER_DLL_remove (t->from_state->transitions_head,
+ t->from_state->transitions_tail, t);
+ t->from_state->transition_count--;
+ GNUNET_CONTAINER_DLL_insert (t->to_state->transitions_head,
+ t->to_state->transitions_tail, t);
+ t->to_state->transition_count++;
+
+ s_swp = t->from_state;
+ t->from_state = t->to_state;
+ t->to_state = s_swp;
+ }
+ }
+}
+
+/**
+ * Create proof for the given state.
+ *
+ * @param cls closure.
+ * @param s state.
+ */
+static void
+automaton_create_proofs_step (void *cls, struct GNUNET_REGEX_State *s)
+{
+ struct Transition *t;
+ int i;
+ char *tmp;
+
+ for (i = 0, t = s->transitions_head; NULL != t; t = t->next, i++)
+ {
+ if (t->to_state == s)
+ GNUNET_asprintf (&tmp, "%c*", t->label);
+ else if (i != s->transition_count - 1)
+ GNUNET_asprintf (&tmp, "%c|", t->label);
+ else
+ GNUNET_asprintf (&tmp, "%c", t->label);
+
+ if (NULL != s->proof)
+ s->proof =
+ GNUNET_realloc (s->proof, strlen (s->proof) + strlen (tmp) + 1);
+ else
+ s->proof = GNUNET_malloc (strlen (tmp) + 1);
+ strcat (s->proof, tmp);
+ GNUNET_free (tmp);
+ }
+}
+
+/**
+ * Create proofs for all states in the given automaton.
+ *
+ * @param a automaton.
+ */
+static void
+automaton_create_proofs (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+
+ automaton_reverse (a);
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ automaton_create_proofs_step (NULL, s);
+
+ automaton_reverse (a);
+}
+
+/**
+ * Creates a new DFA state based on a set of NFA states. Needs to be freed using
+ * automaton_destroy_state.
+ *
+ * @param ctx context
+ * @param nfa_states set of NFA states on which the DFA should be based on
+ *
+ * @return new DFA state
+ */
+static struct GNUNET_REGEX_State *
+dfa_state_create (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_StateSet *nfa_states)
+{
+ struct GNUNET_REGEX_State *s;
+ char *name;
+ int len = 0;
+ struct GNUNET_REGEX_State *cstate;
+ struct Transition *ctran;
+ int insert = 1;
+ struct Transition *t;
+ int i;
+
+ s = GNUNET_malloc (sizeof (struct GNUNET_REGEX_State));
+ s->id = ctx->state_id++;
+ s->accepting = 0;
+ s->marked = 0;
+ s->name = NULL;
+ s->scc_id = 0;
+ s->index = -1;
+ s->lowlink = -1;
+ s->contained = 0;
+ s->proof = NULL;
+
+ if (NULL == nfa_states)
+ {
+ GNUNET_asprintf (&s->name, "s%i", s->id);
+ return s;
+ }
+
+ s->nfa_set = nfa_states;
+
+ if (nfa_states->len < 1)
+ return s;
+
+ // Create a name based on 'sset'
+ s->name = GNUNET_malloc (sizeof (char) * 2);
+ strcat (s->name, "{");
+ name = NULL;
+
+ for (i = 0; i < nfa_states->len; i++)
+ {
+ cstate = nfa_states->states[i];
+ GNUNET_asprintf (&name, "%i,", cstate->id);
+
+ if (NULL != name)
+ {
+ len = strlen (s->name) + strlen (name) + 1;
+ s->name = GNUNET_realloc (s->name, len);
+ strcat (s->name, name);
+ GNUNET_free (name);
+ name = NULL;
+ }
+
+ // Add a transition for each distinct label to NULL state
+ for (ctran = cstate->transitions_head; NULL != ctran; ctran = ctran->next)
+ {
+ if (0 != ctran->label)
+ {
+ insert = 1;
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ {
+ if (t->label == ctran->label)
+ {
+ insert = 0;
+ break;
+ }
+ }
+
+ if (insert)
+ state_add_transition (ctx, s, ctran->label, NULL);
+ }
+ }
+
+ // If the nfa_states contain an accepting state, the new dfa state is also
+ // accepting
+ if (cstate->accepting)
+ s->accepting = 1;
+ }
+
+ s->name[strlen (s->name) - 1] = '}';
+
+ return s;
+}
+
+/**
+ * Move from the given state 's' to the next state on transition 'label'
+ *
+ * @param s starting state
+ * @param label edge label to follow
+ *
+ * @return new state or NULL, if transition on label not possible
+ */
+static struct GNUNET_REGEX_State *
+dfa_move (struct GNUNET_REGEX_State *s, const char label)
+{
+ struct Transition *t;
+ struct GNUNET_REGEX_State *new_s;
+
+ if (NULL == s)
+ return NULL;
+
+ new_s = NULL;
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ {
+ if (label == t->label)
+ {
+ new_s = t->to_state;
+ break;
+ }
+ }
+
+ return new_s;
+}
+
+/**
+ * Remove all unreachable states from DFA 'a'. Unreachable states are those
+ * states that are not reachable from the starting state.
+ *
+ * @param a DFA automaton
+ */
+static void
+dfa_remove_unreachable_states (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+ struct GNUNET_REGEX_State *s_next;
+
+ // 1. unmark all states
+ for (s = a->states_head; NULL != s; s = s->next)
+ s->marked = GNUNET_NO;
+
+ // 2. traverse dfa from start state and mark all visited states
+ automaton_traverse (NULL, a, NULL);
+
+ // 3. delete all states that were not visited
+ for (s = a->states_head; NULL != s; s = s_next)
+ {
+ s_next = s->next;
+ if (GNUNET_NO == s->marked)
+ automaton_remove_state (a, s);
+ }
+}
+
+/**
+ * Remove all dead states from the DFA 'a'. Dead states are those states that do
+ * not transition to any other state but themselfes.
+ *
+ * @param a DFA automaton
+ */
+static void
+dfa_remove_dead_states (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+ struct Transition *t;
+ int dead;
+
+ GNUNET_assert (DFA == a->type);
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ {
+ if (s->accepting)
+ continue;
+
+ dead = 1;
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ {
+ if (NULL != t->to_state && t->to_state != s)
+ {
+ dead = 0;
+ break;
+ }
+ }
+
+ if (0 == dead)
+ continue;
+
+ // state s is dead, remove it
+ automaton_remove_state (a, s);
+ }
+}
+
+/**
+ * Merge all non distinguishable states in the DFA 'a'
+ *
+ * @param ctx context
+ * @param a DFA automaton
+ */
+static void
+dfa_merge_nondistinguishable_states (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_Automaton *a)
+{
+ int i;
+ int table[a->state_count][a->state_count];
+ struct GNUNET_REGEX_State *s1;
+ struct GNUNET_REGEX_State *s2;
+ struct Transition *t1;
+ struct Transition *t2;
+ struct GNUNET_REGEX_State *s1_next;
+ struct GNUNET_REGEX_State *s2_next;
+ int change;
+ int num_equal_edges;
+
+ for (i = 0, s1 = a->states_head; i < a->state_count && NULL != s1;
+ i++, s1 = s1->next)
+ {
+ s1->marked = i;
+ }
+
+ // Mark all pairs of accepting/!accepting states
+ for (s1 = a->states_head; NULL != s1; s1 = s1->next)
+ {
+ for (s2 = a->states_head; NULL != s2; s2 = s2->next)
+ {
+ table[s1->marked][s2->marked] = 0;
+
+ if ((s1->accepting && !s2->accepting) ||
+ (!s1->accepting && s2->accepting))
+ {
+ table[s1->marked][s2->marked] = 1;
+ }
+ }
+ }
+
+ // Find all equal states
+ change = 1;
+ while (0 != change)
+ {
+ change = 0;
+ for (s1 = a->states_head; NULL != s1; s1 = s1->next)
+ {
+ for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2->next)
+ {
+ if (0 != table[s1->marked][s2->marked])
+ continue;
+
+ num_equal_edges = 0;
+ for (t1 = s1->transitions_head; NULL != t1; t1 = t1->next)
+ {
+ for (t2 = s2->transitions_head; NULL != t2; t2 = t2->next)
+ {
+ if (t1->label == t2->label)
+ {
+ num_equal_edges++;
+ if (0 != table[t1->to_state->marked][t2->to_state->marked] ||
+ 0 != table[t2->to_state->marked][t1->to_state->marked])
+ {
+ table[s1->marked][s2->marked] = t1->label != 0 ? t1->label : 1;
+ change = 1;
+ }
+ }
+ }
+ }
+ if (num_equal_edges != s1->transition_count ||
+ num_equal_edges != s2->transition_count)
+ {
+ // Make sure ALL edges of possible equal states are the same
+ table[s1->marked][s2->marked] = -2;
+ }
+ }
+ }
+ }
+
+ // Merge states that are equal
+ for (s1 = a->states_head; NULL != s1; s1 = s1_next)
+ {
+ s1_next = s1->next;
+ for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2_next)
+ {
+ s2_next = s2->next;
+ if (table[s1->marked][s2->marked] == 0)
+ automaton_merge_states (ctx, a, s1, s2);
+ }
+ }
+}
+
+/**
+ * Minimize the given DFA 'a' by removing all unreachable states, removing all
+ * dead states and merging all non distinguishable states
+ *
+ * @param ctx context
+ * @param a DFA automaton
+ */
+static void
+dfa_minimize (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_Automaton *a)
+{
+ if (NULL == a)
+ return;
+
+ GNUNET_assert (DFA == a->type);
+
+ // 1. remove unreachable states
+ dfa_remove_unreachable_states (a);
+
+ // 2. remove dead states
+ dfa_remove_dead_states (a);
+
+ // 3. Merge nondistinguishable states
+ dfa_merge_nondistinguishable_states (ctx, a);
+}
+
+/**
+ * Creates a new NFA fragment. Needs to be cleared using
+ * automaton_fragment_clear.
+ *
+ * @param start starting state
+ * @param end end state
+ *
+ * @return new NFA fragment
+ */
+static struct GNUNET_REGEX_Automaton *
+nfa_fragment_create (struct GNUNET_REGEX_State *start,
+ struct GNUNET_REGEX_State *end)
+{
+ struct GNUNET_REGEX_Automaton *n;
+
+ n = GNUNET_malloc (sizeof (struct GNUNET_REGEX_Automaton));
+
+ n->type = NFA;
+ n->start = NULL;
+ n->end = NULL;
+
+ if (NULL == start && NULL == end)
+ return n;
+
+ automaton_add_state (n, end);
+ automaton_add_state (n, start);
+
+ n->start = start;
+ n->end = end;
+
+ return n;
+}
+
+/**
+ * Adds a list of states to the given automaton 'n'.
+ *
+ * @param n automaton to which the states should be added
+ * @param states_head head of the DLL of states
+ * @param states_tail tail of the DLL of states
+ */
+static void
+nfa_add_states (struct GNUNET_REGEX_Automaton *n,
+ struct GNUNET_REGEX_State *states_head,
+ struct GNUNET_REGEX_State *states_tail)
+{
+ struct GNUNET_REGEX_State *s;
+
+ if (NULL == n || NULL == states_head)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not add states\n");
+ return;
+ }
+
+ if (NULL == n->states_head)
+ {
+ n->states_head = states_head;
+ n->states_tail = states_tail;
+ return;
+ }
+
+ if (NULL != states_head)
+ {
+ n->states_tail->next = states_head;
+ n->states_tail = states_tail;
+ }
+
+ for (s = states_head; NULL != s; s = s->next)
+ n->state_count++;
+}
+
+/**
+ * Creates a new NFA state. Needs to be freed using automaton_destroy_state.
+ *
+ * @param ctx context
+ * @param accepting is it an accepting state or not
+ *
+ * @return new NFA state
+ */
+static struct GNUNET_REGEX_State *
+nfa_state_create (struct GNUNET_REGEX_Context *ctx, int accepting)
+{
+ struct GNUNET_REGEX_State *s;
+
+ s = GNUNET_malloc (sizeof (struct GNUNET_REGEX_State));
+ s->id = ctx->state_id++;
+ s->accepting = accepting;
+ s->marked = 0;
+ s->contained = 0;
+ s->index = -1;
+ s->lowlink = -1;
+ s->scc_id = 0;
+ s->name = NULL;
+ GNUNET_asprintf (&s->name, "s%i", s->id);
+
+ return s;
+}
+
+/**
+ * Calculates the NFA closure set for the given state.
+ *
+ * @param nfa the NFA containing 's'
+ * @param s starting point state
+ * @param label transitioning label on which to base the closure on,
+ * pass 0 for epsilon transition
+ *
+ * @return sorted nfa closure on 'label' (epsilon closure if 'label' is 0)
+ */
+static struct GNUNET_REGEX_StateSet *
+nfa_closure_create (struct GNUNET_REGEX_Automaton *nfa,
+ struct GNUNET_REGEX_State *s, const char label)
+{
+ struct GNUNET_REGEX_StateSet *cls;
+ struct GNUNET_REGEX_StateSet *cls_check;
+ struct GNUNET_REGEX_State *clsstate;
+ struct GNUNET_REGEX_State *currentstate;
+ struct Transition *ctran;
+
+ if (NULL == s)
+ return NULL;
+
+ cls = GNUNET_malloc (sizeof (struct GNUNET_REGEX_StateSet));
+ cls_check = GNUNET_malloc (sizeof (struct GNUNET_REGEX_StateSet));
+
+ for (clsstate = nfa->states_head; NULL != clsstate; clsstate = clsstate->next)
+ clsstate->contained = 0;
+
+ // Add start state to closure only for epsilon closure
+ if (0 == label)
+ GNUNET_array_append (cls->states, cls->len, s);
+
+ GNUNET_array_append (cls_check->states, cls_check->len, s);
+ while (cls_check->len > 0)
+ {
+ currentstate = cls_check->states[cls_check->len - 1];
+ GNUNET_array_grow (cls_check->states, cls_check->len, cls_check->len - 1);
+
+ for (ctran = currentstate->transitions_head; NULL != ctran;
+ ctran = ctran->next)
+ {
+ if (NULL != ctran->to_state && label == ctran->label)
+ {
+ clsstate = ctran->to_state;
+
+ if (NULL != clsstate && 0 == clsstate->contained)
+ {
+ GNUNET_array_append (cls->states, cls->len, clsstate);
+ GNUNET_array_append (cls_check->states, cls_check->len, clsstate);
+ clsstate->contained = 1;
+ }
+ }
+ }
+ }
+ GNUNET_assert (0 == cls_check->len);
+ GNUNET_free (cls_check);
+
+ if (cls->len > 1)
+ qsort (cls->states, cls->len, sizeof (struct GNUNET_REGEX_State *),
+ state_compare);
+
+ return cls;
+}
+
+/**
+ * Calculates the closure set for the given set of states.
+ *
+ * @param nfa the NFA containing 's'
+ * @param states list of states on which to base the closure on
+ * @param label transitioning label for which to base the closure on,
+ * pass 0 for epsilon transition
+ *
+ * @return sorted nfa closure on 'label' (epsilon closure if 'label' is 0)
+ */
+static struct GNUNET_REGEX_StateSet *
+nfa_closure_set_create (struct GNUNET_REGEX_Automaton *nfa,
+ struct GNUNET_REGEX_StateSet *states, const char label)
+{
+ struct GNUNET_REGEX_State *s;
+ struct GNUNET_REGEX_StateSet *sset;
+ struct GNUNET_REGEX_StateSet *cls;
+ int i;
+ int j;
+ int k;
+ int contains;
+
+ if (NULL == states)
+ return NULL;
+
+ cls = GNUNET_malloc (sizeof (struct GNUNET_REGEX_StateSet));
+
+ for (i = 0; i < states->len; i++)
+ {
+ s = states->states[i];
+ sset = nfa_closure_create (nfa, s, label);
+
+ for (j = 0; j < sset->len; j++)
+ {
+ contains = 0;
+ for (k = 0; k < cls->len; k++)
+ {
+ if (sset->states[j]->id == cls->states[k]->id)
+ {
+ contains = 1;
+ break;
+ }
+ }
+ if (!contains)
+ GNUNET_array_append (cls->states, cls->len, sset->states[j]);
+ }
+ state_set_clear (sset);
+ }
+
+ if (cls->len > 1)
+ qsort (cls->states, cls->len, sizeof (struct GNUNET_REGEX_State *),
+ state_compare);
+
+ return cls;
+}
+
+/**
+ * Pops two NFA fragments (a, b) from the stack and concatenates them (ab)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_concatenation (struct GNUNET_REGEX_Context *ctx)
+{
+ struct GNUNET_REGEX_Automaton *a;
+ struct GNUNET_REGEX_Automaton *b;
+ struct GNUNET_REGEX_Automaton *new;
+
+ b = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
+ a = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+ state_add_transition (ctx, a->end, 0, b->start);
+ a->end->accepting = 0;
+ b->end->accepting = 1;
+
+ new = nfa_fragment_create (NULL, NULL);
+ nfa_add_states (new, a->states_head, a->states_tail);
+ nfa_add_states (new, b->states_head, b->states_tail);
+ new->start = a->start;
+ new->end = b->end;
+ automaton_fragment_clear (a);
+ automaton_fragment_clear (b);
+
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+}
+
+/**
+ * Pops a NFA fragment from the stack (a) and adds a new fragment (a*)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_star_op (struct GNUNET_REGEX_Context *ctx)
+{
+ struct GNUNET_REGEX_Automaton *a;
+ struct GNUNET_REGEX_Automaton *new;
+ struct GNUNET_REGEX_State *start;
+ struct GNUNET_REGEX_State *end;
+
+ a = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+ if (NULL == a)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "nfa_add_star_op failed, because there was no element on the stack");
+ return;
+ }
+
+ start = nfa_state_create (ctx, 0);
+ end = nfa_state_create (ctx, 1);
+
+ state_add_transition (ctx, start, 0, a->start);
+ state_add_transition (ctx, start, 0, end);
+ state_add_transition (ctx, a->end, 0, a->start);
+ state_add_transition (ctx, a->end, 0, end);
+
+ a->end->accepting = 0;
+ end->accepting = 1;
+
+ new = nfa_fragment_create (start, end);
+ nfa_add_states (new, a->states_head, a->states_tail);
+ automaton_fragment_clear (a);
+
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+}
+
+/**
+ * Pops an NFA fragment (a) from the stack and adds a new fragment (a+)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_plus_op (struct GNUNET_REGEX_Context *ctx)
+{
+ struct GNUNET_REGEX_Automaton *a;
+
+ a = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+ state_add_transition (ctx, a->end, 0, a->start);
+
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, a);
+}
+
+/**
+ * Pops an NFA fragment (a) from the stack and adds a new fragment (a?)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_question_op (struct GNUNET_REGEX_Context *ctx)
+{
+ struct GNUNET_REGEX_Automaton *a;
+ struct GNUNET_REGEX_Automaton *new;
+ struct GNUNET_REGEX_State *start;
+ struct GNUNET_REGEX_State *end;
+
+ a = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+ if (NULL == a)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "nfa_add_question_op failed, because there was no element on the stack");
+ return;
+ }
+
+ start = nfa_state_create (ctx, 0);
+ end = nfa_state_create (ctx, 1);
+
+ state_add_transition (ctx, start, 0, a->start);
+ state_add_transition (ctx, start, 0, end);
+ state_add_transition (ctx, a->end, 0, end);
+
+ a->end->accepting = 0;
+
+ new = nfa_fragment_create (start, end);
+ nfa_add_states (new, a->states_head, a->states_tail);
+ automaton_fragment_clear (a);
+
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+}
+
+/**
+ * Pops two NFA fragments (a, b) from the stack and adds a new NFA fragment that
+ * alternates between a and b (a|b)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_alternation (struct GNUNET_REGEX_Context *ctx)
+{
+ struct GNUNET_REGEX_Automaton *a;
+ struct GNUNET_REGEX_Automaton *b;
+ struct GNUNET_REGEX_Automaton *new;
+ struct GNUNET_REGEX_State *start;
+ struct GNUNET_REGEX_State *end;
+
+ b = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
+ a = ctx->stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+ start = nfa_state_create (ctx, 0);
+ end = nfa_state_create (ctx, 1);
+ state_add_transition (ctx, start, 0, a->start);
+ state_add_transition (ctx, start, 0, b->start);
+
+ state_add_transition (ctx, a->end, 0, end);
+ state_add_transition (ctx, b->end, 0, end);
+
+ a->end->accepting = 0;
+ b->end->accepting = 0;
+ end->accepting = 1;
+
+ new = nfa_fragment_create (start, end);
+ nfa_add_states (new, a->states_head, a->states_tail);
+ nfa_add_states (new, b->states_head, b->states_tail);
+ automaton_fragment_clear (a);
+ automaton_fragment_clear (b);
+
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+}
+
+/**
+ * Adds a new nfa fragment to the stack
+ *
+ * @param ctx context
+ * @param lit label for nfa transition
+ */
+static void
+nfa_add_label (struct GNUNET_REGEX_Context *ctx, const char lit)
+{
+ struct GNUNET_REGEX_Automaton *n;
+ struct GNUNET_REGEX_State *start;
+ struct GNUNET_REGEX_State *end;
+
+ GNUNET_assert (NULL != ctx);
+
+ start = nfa_state_create (ctx, 0);
+ end = nfa_state_create (ctx, 1);
+ state_add_transition (ctx, start, lit, end);
+ n = nfa_fragment_create (start, end);
+ GNUNET_assert (NULL != n);
+ GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, n);
+}
+
+/**
+ * Initialize a new context
+ *
+ * @param ctx context
+ */
+static void
+GNUNET_REGEX_context_init (struct GNUNET_REGEX_Context *ctx)
+{
+ if (NULL == ctx)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Context was NULL!");
+ return;
+ }
+ ctx->state_id = 0;
+ ctx->transition_id = 0;
+ ctx->scc_id = 0;
+ ctx->stack_head = NULL;
+ ctx->stack_tail = NULL;
+}
+
+/**
+ * Construct an NFA by parsing the regex string of length 'len'.
+ *
+ * @param regex regular expression string
+ * @param len length of the string
+ *
+ * @return NFA, needs to be freed using GNUNET_REGEX_destroy_automaton
+ */
+struct GNUNET_REGEX_Automaton *
+GNUNET_REGEX_construct_nfa (const char *regex, const size_t len)
+{
+ struct GNUNET_REGEX_Context ctx;
+ struct GNUNET_REGEX_Automaton *nfa;
+ const char *regexp;
+ char *error_msg;
+ unsigned int count;
+ unsigned int altcount;
+ unsigned int atomcount;
+ unsigned int pcount;
+ struct
+ {
+ int altcount;
+ int atomcount;
+ } *p;
+
+ GNUNET_REGEX_context_init (&ctx);
+
+ regexp = regex;
+ p = NULL;
+ error_msg = NULL;
+ altcount = 0;
+ atomcount = 0;
+ pcount = 0;
+
+ for (count = 0; count < len && *regexp; count++, regexp++)
+ {
+ switch (*regexp)
+ {
+ case '(':
+ if (atomcount > 1)
+ {
+ --atomcount;
+ nfa_add_concatenation (&ctx);
+ }
+ GNUNET_array_grow (p, pcount, pcount + 1);
+ p[pcount - 1].altcount = altcount;
+ p[pcount - 1].atomcount = atomcount;
+ altcount = 0;
+ atomcount = 0;
+ break;
+ case '|':
+ if (0 == atomcount)
+ {
+ error_msg = "Cannot append '|' to nothing";
+ goto error;
+ }
+ while (--atomcount > 0)
+ nfa_add_concatenation (&ctx);
+ altcount++;
+ break;
+ case ')':
+ if (0 == pcount)
+ {
+ error_msg = "Missing opening '('";
+ goto error;
+ }
+ if (0 == atomcount)
+ {
+ // Ignore this: "()"
+ pcount--;
+ altcount = p[pcount].altcount;
+ atomcount = p[pcount].atomcount;
+ break;
+ }
+ while (--atomcount > 0)
+ nfa_add_concatenation (&ctx);
+ for (; altcount > 0; altcount--)
+ nfa_add_alternation (&ctx);
+ pcount--;
+ altcount = p[pcount].altcount;
+ atomcount = p[pcount].atomcount;
+ atomcount++;
+ break;
+ case '*':
+ if (atomcount == 0)
+ {
+ error_msg = "Cannot append '*' to nothing";
+ goto error;
+ }
+ nfa_add_star_op (&ctx);
+ break;
+ case '+':
+ if (atomcount == 0)
+ {
+ error_msg = "Cannot append '+' to nothing";
+ goto error;
+ }
+ nfa_add_plus_op (&ctx);
+ break;
+ case '?':
+ if (atomcount == 0)
+ {
+ error_msg = "Cannot append '?' to nothing";
+ goto error;
+ }
+ nfa_add_question_op (&ctx);
+ break;
+ case 92: /* escape: \ */
+ regexp++;
+ count++;
+ default:
+ if (atomcount > 1)
+ {
+ --atomcount;
+ nfa_add_concatenation (&ctx);
+ }
+ nfa_add_label (&ctx, *regexp);
+ atomcount++;
+ break;
+ }
+ }
+ if (0 != pcount)
+ {
+ error_msg = "Unbalanced parenthesis";
+ goto error;
+ }
+ while (--atomcount > 0)
+ nfa_add_concatenation (&ctx);
+ for (; altcount > 0; altcount--)
+ nfa_add_alternation (&ctx);
+
+ if (NULL != p)
+ GNUNET_free (p);
+
+ nfa = ctx.stack_tail;
+ GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail, nfa);
+
+ if (NULL != ctx.stack_head)
+ {
+ error_msg = "Creating the NFA failed. NFA stack was not empty!";
+ goto error;
+ }
+
+ return nfa;
+
+error:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse regex\n");
+ if (NULL != error_msg)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", error_msg);
+ if (NULL != p)
+ GNUNET_free (p);
+ while (NULL != ctx.stack_tail)
+ {
+ GNUNET_REGEX_automaton_destroy (ctx.stack_tail);
+ GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail,
+ ctx.stack_tail);
+ }
+ return NULL;
+}
+
+/**
+ * Create DFA states based on given 'nfa' and starting with 'dfa_state'.
+ *
+ * @param ctx context.
+ * @param nfa NFA automaton.
+ * @param dfa DFA automaton.
+ * @param dfa_state current dfa state, pass epsilon closure of first nfa state
+ * for starting.
+ */
+static void
+construct_dfa_states (struct GNUNET_REGEX_Context *ctx,
+ struct GNUNET_REGEX_Automaton *nfa,
+ struct GNUNET_REGEX_Automaton *dfa,
+ struct GNUNET_REGEX_State *dfa_state)
+{
+ struct Transition *ctran;
+ struct GNUNET_REGEX_State *state_iter;
+ struct GNUNET_REGEX_State *new_dfa_state;
+ struct GNUNET_REGEX_State *state_contains;
+ struct GNUNET_REGEX_StateSet *tmp;
+ struct GNUNET_REGEX_StateSet *nfa_set;
+
+ for (ctran = dfa_state->transitions_head; NULL != ctran; ctran = ctran->next)
+ {
+ if (0 == ctran->label || NULL != ctran->to_state)
+ continue;
+
+ tmp = nfa_closure_set_create (nfa, dfa_state->nfa_set, ctran->label);
+ nfa_set = nfa_closure_set_create (nfa, tmp, 0);
+ state_set_clear (tmp);
+ new_dfa_state = dfa_state_create (ctx, nfa_set);
+ state_contains = NULL;
+ for (state_iter = dfa->states_head; NULL != state_iter;
+ state_iter = state_iter->next)
+ {
+ if (0 == state_set_compare (state_iter->nfa_set, new_dfa_state->nfa_set))
+ state_contains = state_iter;
+ }
+
+ if (NULL == state_contains)
+ {
+ automaton_add_state (dfa, new_dfa_state);
+ ctran->to_state = new_dfa_state;
+ construct_dfa_states (ctx, nfa, dfa, new_dfa_state);
+ }
+ else
+ {
+ ctran->to_state = state_contains;
+ automaton_destroy_state (new_dfa_state);
+ }
+ }
+}
+
+/**
+ * Construct DFA for the given 'regex' of length 'len'
+ *
+ * @param regex regular expression string
+ * @param len length of the regular expression
+ *
+ * @return DFA, needs to be freed using GNUNET_REGEX_destroy_automaton
+ */
+struct GNUNET_REGEX_Automaton *
+GNUNET_REGEX_construct_dfa (const char *regex, const size_t len)
+{
+ struct GNUNET_REGEX_Context ctx;
+ struct GNUNET_REGEX_Automaton *dfa;
+ struct GNUNET_REGEX_Automaton *nfa;
+ struct GNUNET_REGEX_StateSet *nfa_set;
+
+ GNUNET_REGEX_context_init (&ctx);
+
+ // Create NFA
+ nfa = GNUNET_REGEX_construct_nfa (regex, len);
+
+ if (NULL == nfa)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not create DFA, because NFA creation failed\n");
+ return NULL;
+ }
+
+ dfa = GNUNET_malloc (sizeof (struct GNUNET_REGEX_Automaton));
+ dfa->type = DFA;
+
+ // Create DFA start state from epsilon closure
+ nfa_set = nfa_closure_create (nfa, nfa->start, 0);
+ dfa->start = dfa_state_create (&ctx, nfa_set);
+ automaton_add_state (dfa, dfa->start);
+
+ construct_dfa_states (&ctx, nfa, dfa, dfa->start);
+
+ GNUNET_REGEX_automaton_destroy (nfa);
+
+ // Minimize DFA
+ dfa_minimize (&ctx, dfa);
+
+ // Calculate SCCs
+ scc_tarjan (&ctx, dfa);
+
+ // Create proofs for all states
+ automaton_create_proofs (dfa);
+
+ return dfa;
+}
+
+/**
+ * Free the memory allocated by constructing the GNUNET_REGEX_Automaton data
+ * structure.
+ *
+ * @param a automaton to be destroyed
+ */
+void
+GNUNET_REGEX_automaton_destroy (struct GNUNET_REGEX_Automaton *a)
+{
+ struct GNUNET_REGEX_State *s;
+ struct GNUNET_REGEX_State *next_state;
+
+ if (NULL == a)
+ return;
+
+ for (s = a->states_head; NULL != s;)
+ {
+ next_state = s->next;
+ automaton_destroy_state (s);
+ s = next_state;
+ }
+
+ GNUNET_free (a);
+}
+
+/**
+ * Save the given automaton as a GraphViz dot file
+ *
+ * @param a the automaton to be saved
+ * @param filename where to save the file
+ */
+void
+GNUNET_REGEX_automaton_save_graph (struct GNUNET_REGEX_Automaton *a,
+ const char *filename)
+{
+ struct GNUNET_REGEX_State *s;
+ struct Transition *ctran;
+ char *s_acc = NULL;
+ char *s_tran = NULL;
+ char *start;
+ char *end;
+ FILE *p;
+
+ if (NULL == a)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print NFA, was NULL!");
+ return;
+ }
+
+ if (NULL == filename || strlen (filename) < 1)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No Filename given!");
+ return;
+ }
+
+ p = fopen (filename, "w");
+
+ if (NULL == p)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not open file for writing: %s",
+ filename);
+ return;
+ }
+
+ start = "digraph G {\nrankdir=LR\n";
+ fwrite (start, strlen (start), 1, p);
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ {
+ if (s->accepting)
+ {
+ GNUNET_asprintf (&s_acc,
+ "\"%s\" [shape=doublecircle, color=\"0.%i 0.8 0.95\"];\n",
+ s->name, s->scc_id);
+ }
+ else
+ {
+ GNUNET_asprintf (&s_acc, "\"%s\" [color=\"0.%i 0.8 0.95\"];\n", s->name,
+ s->scc_id);
+ }
+
+ if (NULL == s_acc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print state %s\n",
+ s->name);
+ return;
+ }
+ fwrite (s_acc, strlen (s_acc), 1, p);
+ GNUNET_free (s_acc);
+ s_acc = NULL;
+
+ for (ctran = s->transitions_head; NULL != ctran; ctran = ctran->next)
+ {
+ if (NULL == ctran->to_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Transition from State %i has has no state for transitioning\n",
+ s->id);
+ continue;
+ }
+
+ if (ctran->label == 0)
+ {
+ GNUNET_asprintf (&s_tran,
+ "\"%s\" -> \"%s\" [label = \"epsilon\", color=\"0.%i 0.8 0.95\"];\n",
+ s->name, ctran->to_state->name, s->scc_id);
+ }
+ else
+ {
+ GNUNET_asprintf (&s_tran,
+ "\"%s\" -> \"%s\" [label = \"%c\", color=\"0.%i 0.8 0.95\"];\n",
+ s->name, ctran->to_state->name, ctran->label,
+ s->scc_id);
+ }
+
+ if (NULL == s_tran)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print state %s\n",
+ s->name);
+ return;
+ }
+
+ fwrite (s_tran, strlen (s_tran), 1, p);
+ GNUNET_free (s_tran);
+ s_tran = NULL;
+ }
+ }
+
+ end = "\n}\n";
+ fwrite (end, strlen (end), 1, p);
+ fclose (p);
+}
+
+/**
+ * Evaluates the given string using the given DFA automaton
+ *
+ * @param a automaton, type must be DFA
+ * @param string string that should be evaluated
+ *
+ * @return 0 if string matches, non 0 otherwise
+ */
+static int
+evaluate_dfa (struct GNUNET_REGEX_Automaton *a, const char *string)
+{
+ const char *strp;
+ struct GNUNET_REGEX_State *s;
+
+ if (DFA != a->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Tried to evaluate DFA, but NFA automaton given");
+ return -1;
+ }
+
+ s = a->start;
+
+ for (strp = string; NULL != strp && *strp; strp++)
+ {
+ s = dfa_move (s, *strp);
+ if (NULL == s)
+ break;
+ }
+
+ if (NULL != s && s->accepting)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Evaluates the given string using the given NFA automaton
+ *
+ * @param a automaton, type must be NFA
+ * @param string string that should be evaluated
+ *
+ * @return 0 if string matches, non 0 otherwise
+ */
+static int
+evaluate_nfa (struct GNUNET_REGEX_Automaton *a, const char *string)
+{
+ const char *strp;
+ struct GNUNET_REGEX_State *s;
+ struct GNUNET_REGEX_StateSet *sset;
+ struct GNUNET_REGEX_StateSet *new_sset;
+ int i;
+ int result;
+
+ if (NFA != a->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Tried to evaluate NFA, but DFA automaton given");
+ return -1;
+ }
+
+ result = 1;
+ strp = string;
+ sset = nfa_closure_create (a, a->start, 0);
+
+ for (strp = string; NULL != strp && *strp; strp++)
+ {
+ new_sset = nfa_closure_set_create (a, sset, *strp);
+ state_set_clear (sset);
+ sset = nfa_closure_set_create (a, new_sset, 0);
+ state_set_clear (new_sset);
+ }
+
+ for (i = 0; i < sset->len; i++)
+ {
+ s = sset->states[i];
+ if (NULL != s && s->accepting)
+ {
+ result = 0;
+ break;
+ }
+ }
+
+ state_set_clear (sset);
+ return result;
+}
+
+/**
+ * Evaluates the given 'string' against the given compiled regex
+ *
+ * @param a automaton
+ * @param string string to check
+ *
+ * @return 0 if string matches, non 0 otherwise
+ */
+int
+GNUNET_REGEX_eval (struct GNUNET_REGEX_Automaton *a, const char *string)
+{
+ int result;
+
+ switch (a->type)
+ {
+ case DFA:
+ result = evaluate_dfa (a, string);
+ break;
+ case NFA:
+ result = evaluate_nfa (a, string);
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Evaluating regex failed, automaton has no type!\n");
+ result = GNUNET_SYSERR;
+ break;
+ }
+
+ return result;
+}
+
+/**
+ * Get the first key for the given 'input_string'. This hashes the first x bits
+ * of the 'input_strings'.
+ *
+ * @param input_string string.
+ * @param string_len length of the 'input_string'.
+ * @param key pointer to where to write the hash code.
+ *
+ * @return number of bits of 'input_string' that have been consumed
+ * to construct the key
+ */
+unsigned int
+GNUNET_REGEX_get_first_key (const char *input_string, unsigned int string_len,
+ GNUNET_HashCode * key)
+{
+ unsigned int size;
+
+ size = string_len < initial_bits ? string_len : initial_bits;
+
+ if (NULL == input_string)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Given input string was NULL!\n");
+ return 0;
+ }
+
+ GNUNET_CRYPTO_hash (input_string, size, key);
+
+ return size;
+}
+
+/**
+ * Check if the given 'proof' matches the given 'key'.
+ *
+ * @param proof partial regex
+ * @param key hash
+ *
+ * @return GNUNET_OK if the proof is valid for the given key
+ */
+int
+GNUNET_REGEX_check_proof (const char *proof, const GNUNET_HashCode * key)
+{
+ return GNUNET_OK;
+}
+
+/**
+ * Iterate over all edges helper function starting from state 's', calling
+ * iterator on for each edge.
+ *
+ * @param s state.
+ * @param iterator iterator function called for each edge.
+ * @param iterator_cls closure.
+ */
+static void
+iterate_edge (struct GNUNET_REGEX_State *s, GNUNET_REGEX_KeyIterator iterator,
+ void *iterator_cls)
+{
+ struct Transition *t;
+ struct GNUNET_REGEX_Edge edges[s->transition_count];
+ unsigned int num_edges;
+
+ if (GNUNET_YES != s->marked)
+ {
+ s->marked = GNUNET_YES;
+
+ num_edges = state_get_edges (s, edges);
+
+ iterator (iterator_cls, &s->hash, s->proof, s->accepting, num_edges, edges);
+
+ for (t = s->transitions_head; NULL != t; t = t->next)
+ iterate_edge (t->to_state, iterator, iterator_cls);
+ }
+}
+
+/**
+ * Iterate over all edges starting from start state of automaton 'a'. Calling
+ * iterator for each edge.
+ *
+ * @param a automaton.
+ * @param iterator iterator called for each edge.
+ * @param iterator_cls closure.
+ */
+void
+GNUNET_REGEX_iterate_all_edges (struct GNUNET_REGEX_Automaton *a,
+ GNUNET_REGEX_KeyIterator iterator,
+ void *iterator_cls)
+{
+ struct GNUNET_REGEX_State *s;
+
+ for (s = a->states_head; NULL != s; s = s->next)
+ s->marked = GNUNET_NO;
+
+ iterate_edge (a->start, iterator, iterator_cls);
+}
diff --git a/src/regex/test_regex_eval_api.c b/src/regex/test_regex_eval_api.c
new file mode 100644
index 0000000..c63e97c
--- /dev/null
+++ b/src/regex/test_regex_eval_api.c
@@ -0,0 +1,311 @@
+/*
+ This file is part of GNUnet
+ (C) 2012 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file regex/test_regex_eval_api.c
+ * @brief test for regex.c
+ * @author Maximilian Szengel
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "gnunet_regex_lib.h"
+
+enum Match_Result
+{
+ match = 0,
+ nomatch = 1
+};
+
+struct Regex_String_Pair
+{
+ char *regex;
+ int string_count;
+ char *strings[20];
+ enum Match_Result expected_results[20];
+};
+
+static const char allowed_literals[] =
+ "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz";
+
+int
+test_random (unsigned int rx_length, unsigned int max_str_len,
+ unsigned int str_count)
+{
+ int i;
+ int j;
+ int rx_exp;
+ char rand_rx[rx_length + 1];
+ char matching_str[str_count][max_str_len + 1];
+ char *rand_rxp;
+ char *matching_strp;
+ int char_op_switch;
+ int last_was_op;
+ char current_char;
+ int eval;
+ int eval_check;
+ struct GNUNET_REGEX_Automaton *dfa;
+ regex_t rx;
+ regmatch_t matchptr[1];
+ char error[200];
+ int result;
+ unsigned int str_len;
+
+ // At least one string is needed for matching
+ GNUNET_assert (str_count > 0);
+ // The string should be at least as long as the regex itself
+ GNUNET_assert (max_str_len >= rx_length);
+
+ rand_rxp = rand_rx;
+ matching_strp = matching_str[0];
+ current_char = 0;
+ last_was_op = 1;
+
+ // Generate random regex and a string that matches the regex
+ for (i = 0; i < rx_length; i++)
+ {
+ char_op_switch = 0 + (int) (1.0 * rand () / (RAND_MAX + 1.0));
+
+ if (0 == char_op_switch && !last_was_op)
+ {
+ last_was_op = 1;
+ rx_exp = rand () % 4;
+
+ switch (rx_exp)
+ {
+ case 0:
+ current_char = '+';
+ break;
+ case 1:
+ current_char = '*';
+ break;
+ case 2:
+ current_char = '?';
+ break;
+ case 3:
+ if (i < rx_length - 1) // '|' cannot be at the end
+ current_char = '|';
+ else
+ current_char =
+ allowed_literals[rand () % (sizeof (allowed_literals) - 1)];
+ break;
+ }
+ }
+ else
+ {
+ current_char =
+ allowed_literals[rand () % (sizeof (allowed_literals) - 1)];
+ last_was_op = 0;
+ }
+
+ if (current_char != '+' && current_char != '*' && current_char != '?' &&
+ current_char != '|')
+ {
+ *matching_strp = current_char;
+ matching_strp++;
+ }
+
+ *rand_rxp = current_char;
+ rand_rxp++;
+ }
+ *rand_rxp = '\0';
+ *matching_strp = '\0';
+
+ // Generate some random strings for matching...
+ // Start at 1, because the first string is generated above during regex generation
+ for (i = 1; i < str_count; i++)
+ {
+ str_len = rand () % max_str_len;
+ for (j = 0; j < str_len; j++)
+ matching_str[i][j] =
+ allowed_literals[rand () % (sizeof (allowed_literals) - 1)];
+ matching_str[i][str_len] = '\0';
+ }
+
+ // Now match
+ result = 0;
+ for (i = 0; i < str_count; i++)
+ {
+ // Match string using DFA
+ dfa = GNUNET_REGEX_construct_dfa (rand_rx, strlen (rand_rx));
+ if (NULL == dfa)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Constructing DFA failed\n");
+ return -1;
+ }
+
+ eval = GNUNET_REGEX_eval (dfa, matching_str[i]);
+ GNUNET_REGEX_automaton_destroy (dfa);
+
+ // Match string using glibc regex
+ if (0 != regcomp (&rx, rand_rx, REG_EXTENDED))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not compile regex using regcomp\n");
+ return -1;
+ }
+
+ eval_check = regexec (&rx, matching_str[i], 1, matchptr, 0);
+ regfree (&rx);
+
+ // We only want to match the whole string, because that's what our DFA does, too.
+ if (eval_check == 0 &&
+ (matchptr[0].rm_so != 0 ||
+ matchptr[0].rm_eo != strlen (matching_str[i])))
+ eval_check = 1;
+
+ // compare result
+ if (eval_check != eval)
+ {
+ regerror (eval_check, &rx, error, sizeof error);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected result:\nregex: %s\nstring: %s\ngnunet regex: %i\nglibc regex: %i\nglibc error: %s\n\n",
+ rand_rx, matching_str, eval, eval_check, error);
+ result += 1;
+ }
+ }
+ return result;
+}
+
+int
+test_automaton (struct GNUNET_REGEX_Automaton *a, regex_t * rx,
+ struct Regex_String_Pair *rxstr)
+{
+ int result;
+ int eval;
+ int eval_check;
+ char error[200];
+ regmatch_t matchptr[1];
+ int i;
+
+ if (NULL == a)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Automaton was NULL\n");
+ return 1;
+ }
+
+ result = 0;
+
+ for (i = 0; i < rxstr->string_count; i++)
+ {
+ eval = GNUNET_REGEX_eval (a, rxstr->strings[i]);
+ eval_check = regexec (rx, rxstr->strings[i], 1, matchptr, 0);
+
+ // We only want to match the whole string, because that's what our DFA does, too.
+ if (eval_check == 0 &&
+ (matchptr[0].rm_so != 0 ||
+ matchptr[0].rm_eo != strlen (rxstr->strings[i])))
+ eval_check = 1;
+
+ if ((rxstr->expected_results[i] == match && (0 != eval || 0 != eval_check))
+ || (rxstr->expected_results[i] == nomatch &&
+ (0 == eval || 0 == eval_check)))
+ {
+ result = 1;
+ regerror (eval_check, rx, error, sizeof error);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected result:\nregex: %s\nstring: %s\nexpected result: %i\n"
+ "gnunet regex: %i\nglibc regex: %i\nglibc error: %s\nrm_so: %i\nrm_eo: %i\n\n",
+ rxstr->regex, rxstr->strings[i], rxstr->expected_results[i],
+ eval, eval_check, error, matchptr[0].rm_so,
+ matchptr[0].rm_eo);
+ }
+ }
+ return result;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GNUNET_log_setup ("test-regex",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+
+ struct GNUNET_REGEX_Automaton *a;
+ regex_t rx;
+ int i;
+ int check_nfa;
+ int check_dfa;
+ int check_rand;
+
+ struct Regex_String_Pair rxstr[8] = {
+ {"ab?(abcd)?", 5,
+ {"ababcd", "abab", "aabcd", "a", "abb"},
+ {match, nomatch, match, match, nomatch}},
+ {"ab(c|d)+c*(a(b|c)d)+", 5,
+ {"abcdcdcdcdddddabd", "abcd", "abcddddddccccccccccccccccccccccccabdacdabd",
+ "abccccca", "abcdcdcdccdabdabd"},
+ {match, nomatch, match, nomatch, match}},
+ {"ab+c*(a(bx|c)d)+", 5,
+ {"abcdcdcdcdddddabd", "abcd", "abcddddddccccccccccccccccccccccccabdacdabd",
+ "abccccca", "abcdcdcdccdabdabd"},
+ {nomatch, nomatch, nomatch, nomatch, nomatch}},
+ {"a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*", 1,
+ {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"},
+ {nomatch}},
+ {"k|a+X*y+c|Q*e|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*g|N+V|t+L|P*j*3*9+X*h*J|J*6|b|E*i*f*R+S|Z|R|Y*Z|g*", 1,
+ {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"},
+ {nomatch}},
+ {"F?W+m+2*6*c*s|P?U?a|B|y*i+t+A|V|6*C*7*e?Z*n*i|J?5+g?W*V?7*j?p?1|r?B?C+E+3+6*i+W*P?K?0|D+7?y*m+3?g?K?", 1,
+ {"osfjsodfonONONOnosndfsdnfsd"},
+ {nomatch}},
+ {"V|M*o?x*p*d+h+b|E*m?h?Y*E*O?W*W*P+o?Z+H*M|I*q+C*a+5?5*9|b?z|G*y*k?R|p+u|8*h?B+l*H|e|L*O|1|F?v*0?5|C+", 1,
+ {"VMoxpdhbEmhYEOWWPoZHMIqCa559bzGykRpu8hBlHeLO1Fv05C"},
+ {nomatch}},
+ {"ab(c|d)+c*(a(b|c)d)+", 1,
+ {"abacd"},
+ {nomatch}}
+ };
+
+ check_nfa = 0;
+ check_dfa = 0;
+ check_rand = 0;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (0 != regcomp (&rx, rxstr[i].regex, REG_EXTENDED))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not compile regex using regcomp()\n");
+ return 1;
+ }
+
+ // NFA test
+ a = GNUNET_REGEX_construct_nfa (rxstr[i].regex, strlen (rxstr[i].regex));
+ check_nfa += test_automaton (a, &rx, &rxstr[i]);
+ GNUNET_REGEX_automaton_destroy (a);
+
+ // DFA test
+ a = GNUNET_REGEX_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex));
+ check_dfa += test_automaton (a, &rx, &rxstr[i]);
+ GNUNET_REGEX_automaton_destroy (a);
+
+ regfree (&rx);
+ }
+
+ srand (time (NULL));
+ for (i = 0; i < 150; i++)
+ check_rand += test_random (150, 200, 25);
+
+ return check_nfa + check_dfa + check_rand;
+}
diff --git a/src/regex/test_regex_iterate_api.c b/src/regex/test_regex_iterate_api.c
new file mode 100644
index 0000000..b214d6a
--- /dev/null
+++ b/src/regex/test_regex_iterate_api.c
@@ -0,0 +1,71 @@
+/*
+ This file is part of GNUnet
+ (C) 2012 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file regex/test_regex_iterate_api.c
+ * @brief test for regex.c
+ * @author Maximilian Szengel
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "gnunet_regex_lib.h"
+
+void
+key_iterator (void *cls, const GNUNET_HashCode * key, const char *proof,
+ int accepting, unsigned int num_edges,
+ const struct GNUNET_REGEX_Edge *edges)
+{
+ int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iterating...\n");
+ for (i = 0; i < num_edges; i++)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Edge %i: %s\n", i, edges[i].label);
+ }
+
+ if (NULL != proof)
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proof: %s\n", proof);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GNUNET_log_setup ("test-regex",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+
+ int error;
+ const char *regex;
+ struct GNUNET_REGEX_Automaton *dfa;
+
+ error = 0;
+ regex = "ab(c|d)+c*(a(b|c)d)+";
+
+ dfa = GNUNET_REGEX_construct_dfa (regex, strlen (regex));
+ GNUNET_REGEX_automaton_save_graph (dfa, "dfa.dot");
+ GNUNET_REGEX_iterate_all_edges (dfa, key_iterator, NULL);
+ GNUNET_REGEX_automaton_destroy (dfa);
+
+ return error;
+}