]> git.pld-linux.org Git - packages/apparmor-parser.git/commitdiff
- rel 6; more bzr fixes auto/th/apparmor-parser-2_5-6
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Wed, 4 Aug 2010 13:10:58 +0000 (13:10 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    apparmor-parser-bzr.patch -> 1.3
    apparmor-parser.spec -> 1.32

apparmor-parser-bzr.patch
apparmor-parser.spec

index 8e06e37043d3a9c038af9517aec261ef945428bd..e3240af91f5eebba5fc31ac7a12bce6d588d901a 100644 (file)
@@ -1,7 +1,7 @@
 === added file '.bzrignore'
 --- .bzrignore 1970-01-01 00:00:00 +0000
-+++ .bzrignore 2010-06-05 01:39:20 +0000
-@@ -0,0 +1,25 @@
++++ .bzrignore 2010-08-03 17:27:13 +0000
+@@ -0,0 +1,165 @@
 +parser/po/*.mo
 +parser/af_names.h
 +parser/cap_names.h
 +parser/techdoc.log
 +parser/techdoc.pdf
 +parser/techdoc.toc
++libraries/libapparmor/Makefile
++libraries/libapparmor/Makefile.in
++libraries/libapparmor/aclocal.m4
++libraries/libapparmor/audit.log
++libraries/libapparmor/autom4te.cache
++libraries/libapparmor/compile
++libraries/libapparmor/config.guess
++libraries/libapparmor/config.log
++libraries/libapparmor/config.status
++libraries/libapparmor/config.sub
++libraries/libapparmor/configure
++libraries/libapparmor/depcomp
++libraries/libapparmor/install-sh
++libraries/libapparmor/libtool
++libraries/libapparmor/ltmain.sh
++libraries/libapparmor/missing
++libraries/libapparmor/ylwrap
++libraries/libapparmor/doc/Makefile
++libraries/libapparmor/doc/Makefile.in
++libraries/libapparmor/doc/aa_change_hat.2
++libraries/libapparmor/src/.deps
++libraries/libapparmor/src/.libs
++libraries/libapparmor/src/Makefile
++libraries/libapparmor/src/Makefile.in
++libraries/libapparmor/src/af_protos.h
++libraries/libapparmor/src/change_hat.lo
++libraries/libapparmor/src/grammar.lo
++libraries/libapparmor/src/libaalogparse.lo
++libraries/libapparmor/src/libimmunix_warning.lo
++libraries/libapparmor/src/scanner.lo
++libraries/libapparmor/src/libapparmor.la
++libraries/libapparmor/src/libimmunix.la
++libraries/libapparmor/src/grammar.c
++libraries/libapparmor/src/grammar.h
++libraries/libapparmor/src/scanner.c
++libraries/libapparmor/src/scanner.h
++libraries/libapparmor/src/tst_aalogmisc
++libraries/libapparmor/swig/Makefile
++libraries/libapparmor/swig/Makefile.in
++libraries/libapparmor/swig/perl/LibAppArmor.bs
++libraries/libapparmor/swig/perl/LibAppArmor.pm
++libraries/libapparmor/swig/perl/Makefile
++libraries/libapparmor/swig/perl/Makefile.PL
++libraries/libapparmor/swig/perl/Makefile.in
++libraries/libapparmor/swig/perl/Makefile.perl
++libraries/libapparmor/swig/perl/blib
++libraries/libapparmor/swig/perl/libapparmor_wrap.c
++libraries/libapparmor/swig/perl/pm_to_blib
++libraries/libapparmor/swig/python/Makefile
++libraries/libapparmor/swig/python/Makefile.in
++libraries/libapparmor/swig/python/setup.py
++libraries/libapparmor/swig/ruby/Makefile
++libraries/libapparmor/swig/ruby/Makefile.in
++libraries/libapparmor/testsuite/.deps
++libraries/libapparmor/testsuite/.libs
++libraries/libapparmor/testsuite/Makefile
++libraries/libapparmor/testsuite/Makefile.in
++libraries/libapparmor/testsuite/libaalogparse.log
++libraries/libapparmor/testsuite/libaalogparse.sum
++libraries/libapparmor/testsuite/site.exp
++libraries/libapparmor/testsuite/test_multi.multi
++libraries/libapparmor/testsuite/config/Makefile
++libraries/libapparmor/testsuite/config/Makefile.in
++libraries/libapparmor/testsuite/lib/Makefile
++libraries/libapparmor/testsuite/lib/Makefile.in
++libraries/libapparmor/testsuite/libaalogparse.test/Makefile
++libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in
++libraries/libapparmor/testsuite/test_multi/out
++changehat/mod_apparmor/.libs
++changehat/mod_apparmor/common
++changehat/pam_apparmor/common
++changehat/tomcat_apparmor/common
++utils/common
++utils/*.8
++utils/*.8.html
++utils/*.5
++utils/*.5.html
++utils/*.tmp
++utils/po/*.mo
++tests/regression/apparmor/access
++tests/regression/apparmor/changehat
++tests/regression/apparmor/changehat_fail
++tests/regression/apparmor/changehat_fork
++tests/regression/apparmor/changehat_misc
++tests/regression/apparmor/changehat_misc2
++tests/regression/apparmor/changehat_pthread
++tests/regression/apparmor/changehat_twice
++tests/regression/apparmor/changehat_wrapper
++tests/regression/apparmor/changeprofile
++tests/regression/apparmor/chdir
++tests/regression/apparmor/chgrp
++tests/regression/apparmor/chmod
++tests/regression/apparmor/chown
++tests/regression/apparmor/clone
++tests/regression/apparmor/deleted
++tests/regression/apparmor/env_check
++tests/regression/apparmor/environ
++tests/regression/apparmor/exec
++tests/regression/apparmor/exec_qual
++tests/regression/apparmor/exec_qual2
++tests/regression/apparmor/fchdir
++tests/regression/apparmor/fchgrp
++tests/regression/apparmor/fchmod
++tests/regression/apparmor/fchown
++tests/regression/apparmor/fork
++tests/regression/apparmor/link
++tests/regression/apparmor/link_subset
++tests/regression/apparmor/mkdir
++tests/regression/apparmor/mmap
++tests/regression/apparmor/mount
++tests/regression/apparmor/named_pipe
++tests/regression/apparmor/net_raw
++tests/regression/apparmor/open
++tests/regression/apparmor/openat
++tests/regression/apparmor/pipe
++tests/regression/apparmor/ptrace
++tests/regression/apparmor/ptrace_helper
++tests/regression/apparmor/pwrite
++tests/regression/apparmor/readdir
++tests/regression/apparmor/rename
++tests/regression/apparmor/rw
++tests/regression/apparmor/swap
++tests/regression/apparmor/symlink
++tests/regression/apparmor/syscall_chroot
++tests/regression/apparmor/syscall_mknod
++tests/regression/apparmor/syscall_mlockall
++tests/regression/apparmor/syscall_ptrace
++tests/regression/apparmor/syscall_reboot
++tests/regression/apparmor/syscall_setdomainname
++tests/regression/apparmor/syscall_sethostname
++tests/regression/apparmor/syscall_setpriority
++tests/regression/apparmor/syscall_setscheduler
++tests/regression/apparmor/syscall_sysctl
++tests/regression/apparmor/sysctl_proc
++tests/regression/apparmor/tcp
++tests/regression/apparmor/unix_fd_client
++tests/regression/apparmor/unix_fd_server
++tests/regression/apparmor/unlink
++tests/regression/apparmor/xattrs
++tests/regression/apparmor/coredump
+
+=== added file 'README'
+--- README     1970-01-01 00:00:00 +0000
++++ README     2010-08-03 17:27:13 +0000
+@@ -0,0 +1,155 @@
++------------
++Introduction
++------------
++AppArmor protects systems from insecure or untrusted processes by
++running them in restricted confinement, while still allowing processes
++to share files, exercise privilege and communicate with other processes.
++AppArmor is a Mandatory Access Control (MAC) mechanism which uses the
++Linux Security Module (LSM) framework. The confinement's restrictions
++are mandatory and are not bound to identity, group membership, or object
++ownership. The protections provided are in addition to the kernel's
++regular access control mechanisms (including DAC) and can be used to
++restrict the superuser.
++
++The AppArmor kernel module and accompanying user-space tools are
++available under the GPL license (the exception is the libapparmor
++library, available under the LGPL license, which allows change_hat(2)
++and change_profile(2) to be used by non-GPL binaries).
++
++For more information, you can read the techdoc.pdf (available after
++building the parser) and http://apparmor.wiki.kernel.org.
++
++
++-------------
++Source Layout
++-------------
++
++AppArmor consists of several different parts:
++
++changehat/    source for using changehat with Apache, PAM and Tomcat
++common/               common makefile rules
++desktop/      empty
++kernel-patches/       patches for various kernel versions
++libraries/    libapparmor source and language bindings
++parser/               source for parser/loader and corresponding documentation
++profiles/     configuration files, reference profiles and abstractions
++tests/                regression and stress testsuites
++utils/                high-level utilities for working with AppArmor
++
++
++------------------------------------------
++Building and Installing AppArmor Userspace
++------------------------------------------
++
++To build and install AppArmor userspace on your system, build and install in
++the following order.
++
++
++libapparmor:
++$ cd ./libraries/libapparmor
++$ sh ./autogen.sh
++$ sh ./configure --prefix=/usr --with-perl
++$ make
++$ make check
++
++
++Utilities:
++$ cd utils
++$ make
++$ make install
++
++
++parser:
++$ cd parser
++$ make
++$ make tests  # not strictly necessary as they are run during the
++              # build by default
++$ make install
++
++
++Apache mod_apparmor:
++$ cd changehat/mod_apparmor
++$ LIBS="-lapparmor" make
++$ make install
++
++
++PAM AppArmor:
++$ cd changehat/pam_apparmor
++$ LIBS="-lapparmor -lpam" make
++$ make install
++
++
++Profiles:
++$ cd profiles
++$ make
++$ make install
++
++
++
++-------------------
++AppArmor Testsuites
++-------------------
++
++A number of testsuites are in the AppArmor sources. Most have documentation on
++usage and how to update and add tests. Below is a quick overview of their
++location and how to run them.
++
++
++Regression tests
++----------------
++For details on structure and adding tests, see
++tests/regression/apparmor/README.
++
++To run:
++$ cd tests/regression/apparmor (requires root)
++$ make
++$ sudo make tests
++$ sudo bash open.sh -r         # runs and saves the last testcase from open.sh
++
++
++Parser tests
++------------
++For details on structure and adding tests, see parser/tst/README.
++
++To run:
++$ cd parser/tst
++$ make
++$ make tests
++
++
++Libapparmor
++-----------
++For details on structure and adding tests, see libraries/libapparmor/README.
++$ cd libraries/libapparmor
++$ make check
++
++
++Stress Tests
++------------
++To run AppArmor stress tests:
++$ make all
++
++Use these:
++$ ./change_hat
++$ ./child
++$ ./kill.sh
++$ ./open
++$ ./s.sh
++
++Or run all at once:
++$ ./stress.sh
++
++Please note that the above will stress the system so much it may end up
++invoking the OOM killer.
++
++To run parser stress tests (requires /usr/bin/ruby):
++$ ./stress.sh
++
++(see stress.sh -h for options)
++
++-----------------------------------------------
++Building and Installing AppArmor Kernel Patches
++-----------------------------------------------
++
++TODO
++
+
+=== modified file 'common/Make.rules'
+--- common/Make.rules  2010-03-11 07:07:29 +0000
++++ common/Make.rules  2010-08-03 17:27:13 +0000
+@@ -48,7 +48,7 @@
+                   echo "/tmp/${NAME}"  ; \
+                 fi ;)
+ endif
+-RPMHOSTVENDOR=$(shell rpm --eval "%{_host_vendor}")
++RPMHOSTVENDOR=$(shell which rpm && rpm --eval "%{_host_vendor}")
+ ifndef DISTRO
+ DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
+                 echo slackware ; \
+@@ -92,30 +92,16 @@
+ ifndef SPECFILE
+ SPECFILE        = $(NAME).spec
+ endif
+-RELEASE = $(shell rpm -q --specfile --define "_sourcedir ." ${RPMARG} --qf "%{RELEASE}" ${SPECFILE})
++RELEASE               = $(shell lsb_release -is) $(shell lsb_release -rs)
+ RELEASE_DIR   = $(NAME)-$(VERSION)
+ TARBALL               = $(NAME)-$(VERSION)-${REPO_VERSION}.tar.gz
+-TAR           = /bin/tar czvp -h --exclude .svn --exclude CVS --exclude .cvsignore --exclude ${TARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR}  $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
++TAR           = /bin/tar czvp -h --exclude .svn --exclude .bzr --exclude .bzrignore --exclude ${TARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR}  $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
+ LDCONFIG      = /sbin/ldconfig
+-CVSPKG_VERSION=$(shell rpm -q --specfile --define "_sourcedir ." ${RPMARG} ${SPECFILE} | head -1 | tr "." "_")
+-
+ RPMSUBDIRS=SOURCES SPECS BUILD BUILDROOT SRPMS RPMS/i386 RPMS/i586 \
+         RPMS/i686 RPMS/athlon RPMS/noarch RPMS/x86_64
+ BUILDRPMSUBDIRS=$(foreach subdir, $(RPMSUBDIRS), $(BUILDDIR:/=)/$(subdir))
+-.PHONY: cvs_tag
+-cvs_tag:
+-      cvs tag IMMUNIX-${CVSPKG_VERSION}
+-
+-.PHONY: checkin
+-checkin:
+-      if cvs -q up -d | grep -q "^\?" ; then echo "Hey! You have" \
+-              "files in the directory you have not added into cvs."; exit 1; \
+-      fi
+-      cvs ci
+-      make cvs_tag
+-
+ ifdef EXTERNAL_PACKAGE
+ .PHONY: rpm
+ rpm: clean $(BUILDRPMSUBDIRS)
+
+=== modified file 'libraries/libapparmor/src/aalogparse.h'
+--- libraries/libapparmor/src/aalogparse.h     2009-09-18 21:13:04 +0000
++++ libraries/libapparmor/src/aalogparse.h     2010-08-03 17:27:13 +0000
+@@ -129,6 +129,7 @@
+       unsigned long fsuid;            /* fsuid of task - if logged */
+       unsigned long ouid;             /* ouid of task - if logged */
+       char *profile;                  /* The name of the profile */
++      char *comm;                     /* Command that triggered msg */
+       char *name;
+       char *name2;
+       char *namespace;
+
+=== modified file 'libraries/libapparmor/src/change_hat.c'
+--- libraries/libapparmor/src/change_hat.c     2010-02-11 23:38:24 +0000
++++ libraries/libapparmor/src/change_hat.c     2010-08-03 17:27:13 +0000
+@@ -194,7 +194,7 @@
+       /* setup command string which is of the form
+        * changehat <token>^hat1\0hat2\0hat3\0..\0
+        */
+-      sprintf(buf, "%s %016x^", cmd, token);
++      sprintf(buf, "%s %016lx^", cmd, token);
+       pos = buf + strlen(buf);
+       if (subprofiles) {
+               for (hats = subprofiles; *hats; hats++) {
+
+=== modified file 'libraries/libapparmor/src/grammar.y'
+--- libraries/libapparmor/src/grammar.y        2009-09-18 21:13:04 +0000
++++ libraries/libapparmor/src/grammar.y        2010-08-03 17:27:13 +0000
+@@ -1,6 +1,7 @@
+ /*
+  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+  *   NOVELL (All rights reserved)
++ *   Copyright (c) 2010, Canonical, Ltd.
+  *
+  *   This program is free software; you can redistribute it and/or
+  *   modify it under the terms of version 2 of the GNU General Public
+@@ -96,6 +97,13 @@
+ %token TOK_TYPE_HINT
+ %token TOK_TYPE_STATUS
+ %token TOK_TYPE_ERROR
++%token TOK_TYPE_AA_REJECT
++%token TOK_TYPE_AA_AUDIT
++%token TOK_TYPE_AA_COMPLAIN
++%token TOK_TYPE_AA_HINT
++%token TOK_TYPE_AA_STATUS
++%token TOK_TYPE_AA_ERROR
++%token TOK_TYPE_LSM_AVC
+ %token TOK_OLD_TYPE_APPARMOR
+ %token TOK_OLD_APPARMOR_REJECT
+ %token TOK_OLD_APPARMOR_PERMIT
+@@ -123,6 +131,7 @@
+ %token TOK_OLD_FORK
+ %token TOK_OLD_CHILD
++%token TOK_KEY_APPARMOR
+ %token TOK_KEY_TYPE
+ %token TOK_KEY_MSG
+ %token TOK_KEY_OPERATION
+@@ -146,6 +155,7 @@
+ %token TOK_KEY_ERROR
+ %token TOK_KEY_FSUID
+ %token TOK_KEY_OUID
++%token TOK_KEY_COMM
+ %token TOK_SYSLOG_KERNEL
+@@ -168,13 +178,14 @@
+       ;
+ new_syntax:
+-        TOK_TYPE_REJECT audit_msg key_list { ret_record->event = AA_RECORD_DENIED; }
+-      | TOK_TYPE_AUDIT audit_msg key_list { ret_record->event = AA_RECORD_AUDIT; }
+-      | TOK_TYPE_COMPLAIN audit_msg key_list { ret_record->event = AA_RECORD_ALLOWED; }
+-      | TOK_TYPE_HINT audit_msg key_list { ret_record->event = AA_RECORD_HINT; }
+-      | TOK_TYPE_STATUS audit_msg key_list { ret_record->event = AA_RECORD_STATUS; }
+-      | TOK_TYPE_ERROR audit_msg key_list { ret_record->event = AA_RECORD_ERROR; }
++        TOK_TYPE_AA_REJECT audit_msg key_list { ret_record->event = AA_RECORD_DENIED; }
++      | TOK_TYPE_AA_AUDIT audit_msg key_list { ret_record->event = AA_RECORD_AUDIT; }
++      | TOK_TYPE_AA_COMPLAIN audit_msg key_list { ret_record->event = AA_RECORD_ALLOWED; }
++      | TOK_TYPE_AA_HINT audit_msg key_list { ret_record->event = AA_RECORD_HINT; }
++      | TOK_TYPE_AA_STATUS audit_msg key_list { ret_record->event = AA_RECORD_STATUS; }
++      | TOK_TYPE_AA_ERROR audit_msg key_list { ret_record->event = AA_RECORD_ERROR; }
+       | TOK_TYPE_UNKNOWN audit_msg key_list { ret_record->event = lookup_aa_event($1); }
++      | TOK_TYPE_LSM_AVC audit_msg key_list
+       ;
+ other_audit: TOK_TYPE_OTHER audit_msg TOK_MSG_REST
+@@ -366,7 +377,8 @@
+ audit_id: TOK_AUDIT TOK_OPEN_PAREN TOK_AUDIT_DIGITS TOK_PERIOD TOK_AUDIT_DIGITS TOK_COLON TOK_AUDIT_DIGITS TOK_CLOSE_PAREN TOK_COLON
+       {
+-              asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7);
++              if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7))
++                      yyerror(scanner, YY_("Out of memory"));
+               ret_record->epoch = atol($3);
+               ret_record->audit_sub_id = atoi($7);
+               free($3);
+@@ -420,6 +432,18 @@
+       { ret_record->fsuid = $3;}
+       | TOK_KEY_OUID TOK_EQUALS TOK_DIGITS
+       { ret_record->ouid = $3;}
++      | TOK_KEY_COMM TOK_EQUALS TOK_QUOTED_STRING
++      { ret_record->comm = $3;}
++      | TOK_KEY_APPARMOR TOK_EQUALS apparmor_event
++      ;
++
++apparmor_event:
++        TOK_TYPE_REJECT       { ret_record->event = AA_RECORD_DENIED; }
++      | TOK_TYPE_AUDIT        { ret_record->event = AA_RECORD_AUDIT; }
++      | TOK_TYPE_COMPLAIN     { ret_record->event = AA_RECORD_ALLOWED; }
++      | TOK_TYPE_HINT         { ret_record->event = AA_RECORD_HINT; }
++      | TOK_TYPE_STATUS       { ret_record->event = AA_RECORD_STATUS; }
++      | TOK_TYPE_ERROR        { ret_record->event = AA_RECORD_ERROR; }
+       ;
+ key_pid: TOK_KEY_PID TOK_EQUALS TOK_DIGITS { ret_record->pid = $3; }
+
+=== modified file 'libraries/libapparmor/src/libaalogparse.c'
+--- libraries/libapparmor/src/libaalogparse.c  2009-09-18 21:13:04 +0000
++++ libraries/libapparmor/src/libaalogparse.c  2010-08-03 17:27:13 +0000
+@@ -56,6 +56,8 @@
+                       free(record->denied_mask);
+               if (record->profile != NULL)
+                       free(record->profile);
++              if (record->comm != NULL)
++                      free(record->comm);
+               if (record->name != NULL)
+                       free(record->name);
+               if (record->name2 != NULL)
+@@ -151,7 +153,8 @@
+       if (current->protocol_name) {
+               ret = strdup(current->protocol_name);
+       } else {
+-              asprintf(&ret, "unknown(%u)", proto);
++              if (!asprintf(&ret, "unknown(%u)", proto))
++                      ret = NULL;
+       }
+       return ret;
+
+=== modified file 'libraries/libapparmor/src/scanner.l'
+--- libraries/libapparmor/src/scanner.l        2010-02-10 23:13:55 +0000
++++ libraries/libapparmor/src/scanner.l        2010-08-03 17:27:13 +0000
+@@ -1,6 +1,7 @@
+ /*
+  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+  *   NOVELL (All rights reserved)
++ *   Copyright (c) 2010, Canonical, Ltd.
+  *
+  *   This program is free software; you can redistribute it and/or
+  *   modify it under the terms of version 2 of the GNU General Public
+@@ -30,6 +31,8 @@
+ #include <assert.h>
++#define YY_NO_INPUT
++
+ unsigned int string_buf_alloc = 0;
+ unsigned int string_buf_len = 0;
+ char *string_buf = NULL;
+@@ -84,12 +87,19 @@
+ modes         ({mode_chars}+)|({mode_chars}+::{mode_chars}*)|(::{mode_chars}*)
+ /* New message types */
+-reject_type           "APPARMOR_DENIED"
+-audit_type            "APPARMOR_AUDIT"
+-complain_type         "APPARMOR_ALLOWED"
+-hint_type             "APPARMOR_HINT"
+-status_type           "APPARMOR_STATUS"
+-error_type            "APPARMOR_ERROR"
++aa_reject_type                "APPARMOR_DENIED"
++aa_audit_type         "APPARMOR_AUDIT"
++aa_complain_type      "APPARMOR_ALLOWED"
++aa_hint_type          "APPARMOR_HINT"
++aa_status_type                "APPARMOR_STATUS"
++aa_error_type         "APPARMOR_ERROR"
++reject_type           "\"DENIED\""
++audit_type            "\"AUDIT\""
++complain_type         "\"ALLOWED\""
++hint_type             "\"HINT\""
++status_type           "\"STATUS\""
++error_type            "\"ERROR\""
++lsm_avc_type          "AVC"
+ unknown_type          UNKNOWN\[{digits}+\]
+ other_audit_type      [[:alnum:]\[\]_-]+
+@@ -125,6 +135,7 @@
+ /* Key tokens */
++key_apparmor          "apparmor"
+ key_type              "type"
+ key_msg                       "msg"
+ key_operation         "operation"
+@@ -147,6 +158,7 @@
+ key_error             "error"
+ key_fsuid             "fsuid"
+ key_ouid              "ouid"
++key_comm              "comm"
+ audit                 "audit"
+ /* syslog tokens */
+@@ -240,6 +252,13 @@
+       {hint_type}     { BEGIN(INITIAL); return(TOK_TYPE_HINT); }
+       {status_type}   { BEGIN(INITIAL); return(TOK_TYPE_STATUS); }
+       {error_type}    { BEGIN(INITIAL); return(TOK_TYPE_ERROR); }
++      {aa_reject_type}        { BEGIN(INITIAL); return(TOK_TYPE_AA_REJECT); }
++      {aa_audit_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_AUDIT); }
++      {aa_complain_type}      { BEGIN(INITIAL); return(TOK_TYPE_AA_COMPLAIN); }
++      {aa_hint_type}  { BEGIN(INITIAL); return(TOK_TYPE_AA_HINT); }
++      {aa_status_type}        { BEGIN(INITIAL); return(TOK_TYPE_AA_STATUS); }
++      {aa_error_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_ERROR); }
++      {lsm_avc_type}  { BEGIN(INITIAL); return(TOK_TYPE_LSM_AVC); }
+       {unknown_type}  { char *yptr = yytext;
+                         while (*yptr && *yptr != '[')
+                               yptr++;
+@@ -300,6 +319,7 @@
+       {key_attribute} { BEGIN(sub_id); return(TOK_KEY_ATTRIBUTE); }
+ }
++{key_apparmor}                { BEGIN(audit_types); return(TOK_KEY_APPARMOR); }
+ {key_type}            { BEGIN(audit_types); return(TOK_KEY_TYPE); }
+ {key_msg}             { return(TOK_KEY_MSG); }
+ {key_operation}               { return(TOK_KEY_OPERATION); }
+@@ -321,6 +341,7 @@
+ {key_error}           { return(TOK_KEY_ERROR); }
+ {key_fsuid}           { return(TOK_KEY_FSUID); }
+ {key_ouid}            { return(TOK_KEY_OUID); }
++{key_comm}            { return(TOK_KEY_COMM); }
+ {syslog_kernel}               { BEGIN(dmesg_timestamp); return(TOK_SYSLOG_KERNEL); }
+ {syslog_month}                { yylval->t_str = strdup(yytext); return(TOK_DATE_MONTH); }
 
 === modified file 'libraries/libapparmor/swig/perl/Makefile.am'
 --- libraries/libapparmor/swig/perl/Makefile.am        2009-05-12 21:56:56 +0000
-+++ libraries/libapparmor/swig/perl/Makefile.am        2010-03-16 22:00:26 +0000
++++ libraries/libapparmor/swig/perl/Makefile.am        2010-05-31 18:58:40 +0000
 @@ -1,7 +1,8 @@
  EXTRA_DIST =Makefile.PL libapparmor_wrap.c LibAppArmor.pm examples/*.pl
 +
 \ No newline at end of file
 +endif
 
+=== modified file 'libraries/libapparmor/testsuite/test_multi.c'
+--- libraries/libapparmor/testsuite/test_multi.c       2009-09-18 21:13:04 +0000
++++ libraries/libapparmor/testsuite/test_multi.c       2010-08-03 17:27:13 +0000
+@@ -129,6 +129,10 @@
+               {
+                       printf("Name: %s\n", record->name);
+               }
++              if (record->comm != NULL)
++              {
++                      printf("Command: %s\n", record->comm);
++              }
+               if (record->name2 != NULL)
+               {
+                       printf("Name2: %s\n", record->name2);
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_01.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_01.in 1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_01.in 2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++type=AVC msg=audit(1279948288.415:39): apparmor="DENIED" operation="open" parent=12332 profile="/usr/sbin/cupsd" name="/home/user/.ssh/" pid=12333 comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=1000
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_01.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_01.out        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_01.out        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,16 @@
++START
++File: test_multi/avc_audit_01.in
++Event type: AA_RECORD_DENIED
++Audit ID: 1279948288.415:39
++Operation: open
++Mask: r
++Denied Mask: r
++fsuid: 0
++ouid: 1000
++Profile: /usr/sbin/cupsd
++Name: /home/user/.ssh/
++Command: ls
++Parent: 12332
++PID: 12333
++Epoch: 1279948288
++Audit subid: 39
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_02.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_02.in 1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_02.in 2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++type=AVC msg=audit(1279948227.175:27): apparmor="STATUS" operation="profile_replace" name="/sbin/dhclient3" pid=12291 comm="apparmor_parser"
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_02.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_02.out        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_02.out        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,10 @@
++START
++File: test_multi/avc_audit_02.in
++Event type: AA_RECORD_STATUS
++Audit ID: 1279948227.175:27
++Operation: profile_replace
++Name: /sbin/dhclient3
++Command: apparmor_parser
++PID: 12291
++Epoch: 1279948227
++Audit subid: 27
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_03.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_03.in 1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_03.in 2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++type=AVC msg=audit(1279968846.035:77): apparmor="ALLOWED" operation="open" parent=7014 profile="/tmp/cat" name="/etc/passwd" pid=21645 comm="cat" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_03.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_audit_03.out        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_audit_03.out        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,16 @@
++START
++File: test_multi/avc_audit_03.in
++Event type: AA_RECORD_ALLOWED
++Audit ID: 1279968846.035:77
++Operation: open
++Mask: r
++Denied Mask: r
++fsuid: 1000
++ouid: 0
++Profile: /tmp/cat
++Name: /etc/passwd
++Command: cat
++Parent: 7014
++PID: 21645
++Epoch: 1279968846
++Audit subid: 77
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++Jul 24 12:25:33 spriggan kernel: [42416.178567] type=1400 audit(1279967133.365:54): apparmor="DENIED" operation="open" parent=19650 profile="/usr/sbin/cupsd" name="/boot/" pid=19651 comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out       1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out       2010-08-03 17:27:13 +0000
+@@ -0,0 +1,16 @@
++START
++File: test_multi/avc_syslog_01.in
++Event type: AA_RECORD_DENIED
++Audit ID: 1279967133.365:54
++Operation: open
++Mask: r
++Denied Mask: r
++fsuid: 0
++ouid: 0
++Profile: /usr/sbin/cupsd
++Name: /boot/
++Command: ls
++Parent: 19650
++PID: 19651
++Epoch: 1279967133
++Audit subid: 54
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++Jul 24 12:24:41 spriggan kernel: [42364.269117] type=1400 audit(1279967081.455:42): apparmor="STATUS" operation="profile_replace" name="/sbin/dhclient3" pid=19610 comm="apparmor_parser"
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out       1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out       2010-08-03 17:27:13 +0000
+@@ -0,0 +1,10 @@
++START
++File: test_multi/avc_syslog_02.in
++Event type: AA_RECORD_STATUS
++Audit ID: 1279967081.455:42
++Operation: profile_replace
++Name: /sbin/dhclient3
++Command: apparmor_parser
++PID: 19610
++Epoch: 1279967081
++Audit subid: 42
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in        1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in        2010-08-03 17:27:13 +0000
+@@ -0,0 +1,1 @@
++Jul 24 12:54:06 spriggan kernel: [44128.842691] type=1400 audit(1279968846.035:77): apparmor="ALLOWED" operation="open" parent=7014 profile="/tmp/cat" name="/etc/passwd" pid=21645 comm="cat" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
+
+=== added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out'
+--- libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out       1970-01-01 00:00:00 +0000
++++ libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out       2010-08-03 17:27:13 +0000
+@@ -0,0 +1,16 @@
++START
++File: test_multi/avc_syslog_03.in
++Event type: AA_RECORD_ALLOWED
++Audit ID: 1279968846.035:77
++Operation: open
++Mask: r
++Denied Mask: r
++fsuid: 1000
++ouid: 0
++Profile: /tmp/cat
++Name: /etc/passwd
++Command: cat
++Parent: 7014
++PID: 21645
++Epoch: 1279968846
++Audit subid: 77
+
+=== modified file 'parser/COPYING.GPL'
+--- parser/COPYING.GPL 2006-04-11 21:52:54 +0000
++++ parser/COPYING.GPL 2010-08-03 17:27:13 +0000
+@@ -1,6 +1,5 @@
+ This license applies to all source files within the SubDomain parser
+-package, with the exception of md5.c and the files in the pcre/
+-subdirectory, as they are covered under their own respective licenses.
++package.
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
 === modified file 'parser/Makefile'
 --- parser/Makefile    2009-11-11 18:58:57 +0000
-+++ parser/Makefile    2010-03-16 22:18:55 +0000
-@@ -125,9 +125,20 @@
++++ parser/Makefile    2010-08-03 17:27:13 +0000
+@@ -82,9 +82,6 @@
+ AAREDIR= libapparmor_re
+ AAREOBJECTS = ${AAREDIR}/libapparmor_re.a
+-PCREDIR= pcre
+-PCREOBJECTS = ${PCREDIR}/pcre.o
+-
+ LEX_C_FILES   = parser_lex.c
+ YACC_C_FILES  = parser_yacc.c parser_yacc.h
+@@ -94,7 +91,7 @@
+ TEST_OBJECTS = $(filter-out parser_lex.o, \
+              $(filter-out parser_yacc.o, \
+-             $(filter-out parser_main.o, ${OBJECTS} ${PCREOBJECTS})))
++             $(filter-out parser_main.o, ${OBJECTS})))
+ ifdef V
+   VERBOSE = 1
+@@ -125,14 +122,25 @@
  techdoc.txt: techdoc/index.html
        w3m -dump $< > $@
  
 +main:         $(TOOLS)
        $(Q)make -C po all
 -      $(Q)make -s tests
+-
+-apparmor_parser: $(OBJECTS) $(PCREOBJECTS) $(AAREOBJECTS)
 +
 +manpages:     $(MANPAGES)
 +
 +docs: manpages htmlmanpages pdf
 +
 +all:  main docs tests
- apparmor_parser: $(OBJECTS) $(PCREOBJECTS) $(AAREOBJECTS)
++
++apparmor_parser: $(OBJECTS) $(AAREOBJECTS)
        rm -f ./libstdc++.a
-@@ -191,7 +202,7 @@
+       ln -s `g++ -print-file-name=libstdc++.a`
+-      g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(PCREOBJECTS) $(LIBS) \
++      g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
+             ${LEXLIB}  $(AAREOBJECTS) -static-libgcc -L.
+ parser_yacc.c parser_yacc.h: parser_yacc.y parser.h
+@@ -191,7 +199,7 @@
  af_names.h: /usr/include/bits/socket.h
        LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n#  define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" $< > $@
        LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)[ \\t]\\+.*/#define AA_AF_MAX \\1\n/p" $< >> $@
  
  cap_names.h: /usr/include/linux/capability.h
        LC_ALL=C sed -n -e "/CAP_EMPTY_SET/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9xa-f]\\+\\)\\(.*\\)\$$/\{\"\\L\\1\", \\UCAP_\\1\},/p" $< > $@
-@@ -214,7 +225,7 @@
+@@ -205,8 +213,8 @@
+ tst_misc: parser_misc.c parser.h parser_yacc.h af_names.h cap_names.h
+       $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
+-tst_regex: parser_regex.c parser.h parser_yacc.h $(PCREOBJECTS)
+-      $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(PCREOBJECTS) $(BUILD_OUTPUT)
++tst_regex: parser_regex.c parser.h parser_yacc.h
++      $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
+ .SILENT: check
+ .PHONY: check
+@@ -214,7 +222,7 @@
  
  .SILENT: tests
  tests: ${TESTS}
        $(Q)make -s -C tst tests
  
  .SILENT: check
+@@ -226,11 +234,6 @@
+ $(AAREOBJECTS):
+       make -C $(AAREDIR)
+-.SILENT: $(PCREOBJECTS)
+-.PHONY: $(PCREOBJECTS)
+-$(PCREOBJECTS):
+-      make -C $(PCREDIR) "CFLAGS=$(EXTRA_CFLAGS)"
+-
+ .PHONY: install-rhel4
+ install-rhel4: install-redhat
+@@ -293,7 +296,6 @@
+       rm -f af_names.h
+       rm -f cap_names.h
+       -rm -rf techdoc.{aux,log,pdf,toc,txt} techdoc/
+-      make -s -C $(PCREDIR) clean
+       make -s -C $(AAREDIR) clean
+       make -s -C po clean
+
+=== modified file 'parser/README'
+--- parser/README      2007-04-25 20:50:21 +0000
++++ parser/README      2010-08-03 17:27:13 +0000
+@@ -11,11 +11,6 @@
+ where we will attempt to conform to the RFP vulnerability disclosure
+ protocol: http://www.wiretrip.net/rfp/policy.html
+-The parser uses the PCRE (Perl Compatible Regular Expression) engine,
+-which was written by Philip Hazel and is copyright by the University
+-of Cambridge, England. For more information on the PCRE engine, see
+-<ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/>
+-
+ Thanks.
+ -- The AppArmor development team
 
 === modified file 'parser/apparmor_parser.pod'
 --- parser/apparmor_parser.pod 2010-01-07 18:03:49 +0000
-+++ parser/apparmor_parser.pod 2010-04-03 23:24:06 +0000
-@@ -154,6 +154,33 @@
++++ parser/apparmor_parser.pod 2010-08-03 17:27:13 +0000
+@@ -80,6 +80,10 @@
+ Writes a binary (cached) profile to stdout (implies -K and -T).
++=item -o file, --ofile file
++
++Writes a binary (cached) profile to the specified file (implies -K and -T)
++
+ =item -b n, --base n
+ Set the base directory for resolving #include directives
+@@ -149,11 +153,43 @@
+ Print the version number and exit.
++=item -p, --preprocess
++
++Dump the input profile to stdout out applying preprocessing flattening
++includes into the output profile.
++
+ =item -d, --debug
  Given once, only checks the profiles to ensure syntactic correctness.
  Given twice, dumps its interpretation of the profile for checking.
  
  
  Give a quick reference guide.
 
+=== modified file 'parser/libapparmor_re/apparmor_re.h'
+--- parser/libapparmor_re/apparmor_re.h        2010-01-28 01:20:13 +0000
++++ parser/libapparmor_re/apparmor_re.h        2010-08-03 17:27:13 +0000
+@@ -35,6 +35,7 @@
+   DFA_DUMP_EQUIV_STATS =      1 << 19,
+   DFA_DUMP_MINIMIZE =         1 << 20,
+   DFA_DUMP_UNREACHABLE =      1 << 22,
++  DFA_DUMP_RULE_EXPR =                1 << 23,
+ } dfaflags_t;
+ #ifdef __cplusplus
+@@ -48,9 +49,9 @@
+ aare_ruleset_t *aare_new_ruleset(int reverse);
+ void aare_delete_ruleset(aare_ruleset_t *rules);
+ int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
+-                uint32_t perms, uint32_t audit);
++                uint32_t perms, uint32_t audit, dfaflags_t flags);
+ int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
+-                    uint32_t audit, int count, char **rulev);
++                    uint32_t audit, int count, char **rulev, dfaflags_t flags);
+ void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
+ void aare_reset_matchflags(void);
+
 === modified file 'parser/libapparmor_re/regexp.y'
 --- parser/libapparmor_re/regexp.y     2010-02-01 07:21:00 +0000
-+++ parser/libapparmor_re/regexp.y     2010-03-13 10:23:23 +0000
-@@ -1715,7 +1715,9 @@
++++ parser/libapparmor_re/regexp.y     2010-08-03 17:27:13 +0000
+@@ -102,6 +102,7 @@
+       /* child 0 is left, child 1 is right */
+       Node *child[2];
++      unsigned int label;     /* unique number for debug etc */
+       /**
+        * We need reference counting for AcceptNodes: sharing AcceptNodes
+        * avoids introducing duplicate States with identical accept values.
+@@ -1208,12 +1209,11 @@
+  * Assign a consecutive number to each node. This is only needed for
+  * pretty-printing the debug output.
+  */
+-map<Node *, int> node_label;
+ void label_nodes(Node *root)
+ {
+     int nodes = 0;
+     for (depth_first_traversal i(root); i; i++)
+-      node_label.insert(make_pair(*i, nodes++));
++       i->label = nodes++;
+ }
+ /**
+@@ -1225,7 +1225,7 @@
+     if (!state.empty()) {
+       State::iterator i = state.begin();
+       for(;;) {
+-          os << node_label[*i];
++         os << (*i)->label;
+           if (++i == state.end())
+               break;
+           os << ',';
+@@ -1240,15 +1240,15 @@
+  */
+ void dump_syntax_tree(ostream& os, Node *node) {
+     for (depth_first_traversal i(node); i; i++) {
+-      os << node_label[*i] << '\t';
++      os << i->label << '\t';
+       if ((*i)->child[0] == 0)
+           os << **i << '\t' << (*i)->followpos << endl;
+       else {
+           if ((*i)->child[1] == 0)
+-              os << node_label[(*i)->child[0]] << **i;
++              os << (*i)->child[0]->label << **i;
+           else
+-              os << node_label[(*i)->child[0]] << **i
+-                 << node_label[(*i)->child[1]];
++              os << (*i)->child[0]->label << **i
++                 << (*i)->child[1]->label;
+           os << '\t' << (*i)->firstpos
+                      << (*i)->lastpos << endl;
+       }
+@@ -1256,22 +1256,17 @@
+     os << endl;
+ }
+-/* Comparison operator for sets of <State *>. */
+-template<class T>
+-class deref_less_than {
+-public:
+-    deref_less_than() { }
+-    bool operator()(T a, T b)
+-    {
+-      return *a < *b;
+-    }
++/* Comparison operator for sets of <State *>.
++ * Do compare pointer comparisom on set of <Node *>, the pointer comparison
++ * allows us to determine which Sets of <Node *> we have seen already from
++ * new ones when constructing the DFA.
++ */
++struct deref_less_than {
++  bool operator()(State * const & lhs, State * const & rhs) const
++  { return *lhs < *rhs; }
+ };
+-/**
+- * States in the DFA. The pointer comparison allows us to tell sets we
+- * have seen already from new ones when constructing the DFA.
+- */
+-typedef set<State *, deref_less_than<State *> > States;
++typedef set<State *, deref_less_than > States;
+ typedef list<State *> Partition;
+ /* Transitions in the DFA. */
+ typedef map<State *, Cases> Trans;
+@@ -1370,6 +1365,13 @@
+               here.cases.insert(*j);
+       }
+     }
++
++    for (depth_first_traversal i(root); i; i++) {
++          (*i)->firstpos.clear();
++          (*i)->lastpos.clear();
++          (*i)->followpos.clear();
++    }
++
+     if (flags & (DFA_DUMP_STATS))
+           fprintf(stderr, "\033[2KCreated dfa: states %ld\tmatching %d\tnonmatching %d\n", states.size(), match_count, nomatch_count);
+@@ -1715,7 +1717,9 @@
                        Trans::iterator j = trans.find(*i);
                        if (j != trans.end())
                                trans.erase(j);
                }
        }
  
+@@ -2097,24 +2101,11 @@
+               /* if it overflows the next_check array it fits in as we will
+                * resize */
+               if (c >= next_check.size())
+-                      goto resize;
++                      return true;
+               if (next_check[c].second)
+                       return false;
+       }
+-      return true;
+-resize:
+-      next_check.resize(base + cases.cases.rbegin()->first + 1);
+-      size_t prev = pos;
+-      size_t x = pos;
+-      /* find last free list entry */
+-      while (x) {
+-              prev = x;
+-              x = free_list[x].second;
+-      }
+-      x = free_list. size();
+-      free_list.resize(base + cases.cases.rbegin()->first + 1);
+-      init_free_list(free_list, prev, x);
+       return true;
+ }
+@@ -2126,6 +2117,7 @@
+ {
+       State *default_state = dfa.nonmatching;
+       size_t base = 0;
++      int resize;
+       Trans::iterator i = dfa.trans.find(from);
+       if (i == dfa.trans.end()) {
+@@ -2142,6 +2134,7 @@
+               goto do_insert;
+ repeat:
++      resize = 0;
+       /* get the first free entry that won't underflow */
+       while (x && (x < c)) {
+               prev = x;
+@@ -2154,15 +2147,24 @@
+               x = free_list[x].second;
+       }
+       if (!x) {
++              resize = 256 - cases.begin()->first;
++              x = free_list.size();
++              /* set prev to last free */
++      } else if (x + 255 - cases.begin()->first >= next_check.size()) {
++              resize = (255 - cases.begin()->first - (next_check.size() - 1 - x));
++              for (size_t y = x; y; y = free_list[y].second)
++                      prev = y;
++      }
++      if (resize) {
+               /* expand next_check and free_list */
+-              x = free_list.size();
+-              size_t range = cases.cases.rbegin()->first - cases.begin()->first + 1;
+-              next_check.resize(next_check.size() + range);
+-              free_list.resize(free_list.size() + range);
+-              init_free_list(free_list, prev, x);
++              size_t old_size = free_list.size();
++              next_check.resize(next_check.size() + resize);
++              free_list.resize(free_list.size() + resize);
++              init_free_list(free_list, prev, old_size);
+               if (!first_free)
+-                      first_free = x;
+-              goto repeat;
++                      first_free = old_size;;
++              if (x == old_size)
++                      goto repeat;
+       }
+       base = x - c;
+@@ -2194,8 +2196,9 @@
+       st.insert(make_pair(i->second, i->first));
+     }
+-    os << "(accept, default, base):" << endl;
++    os << "size=" << default_base.size() << " (accept, default, base):  {state} -> {default state}" << endl;
+     for (size_t i = 0; i < default_base.size(); i++) {
++        os << i << ": ";
+       os << "(" << accept[i] << ", "
+          << num[default_base[i].first] << ", "
+          << default_base[i].second << ")";
+@@ -2206,7 +2209,7 @@
+       os << endl;
+     }
+-    os << "(next, check):" << endl;
++    os << "size=" << next_check.size() << " (next, check): {check state} -> {next state} : offset from base" << endl;
+     for (size_t i = 0; i < next_check.size(); i++) {
+       if (!next_check[i].second)
+           continue;
+@@ -2565,9 +2568,9 @@
+ }
+ extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
+-                           uint32_t perms, uint32_t audit)
++                           uint32_t perms, uint32_t audit,  dfaflags_t flags)
+ {
+-      return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule);
++      return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule, flags);
+ }
+ #define FLAGS_WIDTH 2
+@@ -2598,7 +2601,8 @@
+ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
+                                uint32_t perms, uint32_t audit,
+-                               int count, char **rulev)
++                               int count, char **rulev,
++                               dfaflags_t flags)
+ {
+     Node *tree = NULL, *accept;
+     int exact_match;
+@@ -2715,6 +2719,18 @@
+       }
+     }
++    if (flags & DFA_DUMP_RULE_EXPR) {
++          cerr << "rule: ";
++          cerr << rulev[0];
++          for (int i = 1; i < count; i++) {
++                  cerr << "\\x00";
++                  cerr << rulev[i];
++          }
++          cerr << "  ->  ";
++          tree->dump(cerr);
++          cerr << "\n\n";
++    }
++
+     if (rules->root)
+       rules->root = new AltNode(rules->root, new CatNode(tree, accept));
+     else
 
 === modified file 'parser/parser.h'
 --- parser/parser.h    2010-02-17 20:21:52 +0000
-+++ parser/parser.h    2010-03-12 23:26:32 +0000
++++ parser/parser.h    2010-08-03 17:27:13 +0000
 @@ -4,6 +4,9 @@
   *   Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
   *   NOVELL (All rights reserved)
   *   This program is free software; you can redistribute it and/or
   *   modify it under the terms of version 2 of the GNU General Public
   *   License published by the Free Software Foundation.
-@@ -14,7 +17,8 @@
+@@ -14,12 +17,12 @@
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   */
  
  #include <netinet/in.h>
-@@ -280,12 +284,16 @@
+ #include <sys/resource.h>
+-#include "pcre/internal.h"
+ #include "immunix.h"
+ #include "libapparmor_re/apparmor_re.h"
+@@ -40,7 +43,6 @@
+ struct cod_pattern {
+       char *regex;            // posix regex
+-      pcre *compiled;         // compiled regex, size is compiled->size
+ };
+ struct cod_entry {
+@@ -168,17 +170,20 @@
+ #define OPTION_REMOVE   2
+ #define OPTION_REPLACE  3
+ #define OPTION_STDOUT 4
++#define OPTION_OFILE  5
+ #define AARE_NONE 0
+-#define AARE_PCRE 1
+ #define AARE_DFA 2
++#define BOOL int
++
+ #define FLAG_CHANGEHAT_1_4  2
+ #define FLAG_CHANGEHAT_1_5  3
+ extern int flag_changehat_version;
+ extern int read_implies_exec;
+ extern dfaflags_t dfaflags;
+-
++extern int preprocess_only;
++extern FILE *ofile;
+ #define PATH_CHROOT_REL 0x1
+ #define PATH_NS_REL 0x2
+@@ -280,12 +285,16 @@
  extern void free_cod_entries(struct cod_entry *list);
  
  /* parser_symtab.c */
  extern void dump_symtab(void);
  extern void dump_expanded_symtab(void);
  void free_symtabs(void);
-@@ -312,7 +320,7 @@
+@@ -312,7 +321,7 @@
  extern void add_hat_to_policy(struct codomain *policy, struct codomain *hat);
  extern void add_entry_to_policy(struct codomain *policy, struct cod_entry *entry);
  extern void post_process_nt_entries(struct codomain *cod);
 
 === modified file 'parser/parser_include.c'
 --- parser/parser_include.c    2009-07-24 12:18:12 +0000
-+++ parser/parser_include.c    2010-06-05 01:57:01 +0000
++++ parser/parser_include.c    2010-08-03 17:27:13 +0000
 @@ -1,8 +1,8 @@
 -/* $Id$ */
 -
 +      if (current_filename)
 +              free(current_filename);
 +      current_filename = strdup(filename ? filename : "stdin");
-+      current_lineno   = 0;
++      current_lineno   = 1;
 +}
 +
 +void push_include_stack(char *filename)
 
 === modified file 'parser/parser_include.h'
 --- parser/parser_include.h    2009-07-24 07:35:39 +0000
-+++ parser/parser_include.h    2010-06-05 01:57:01 +0000
++++ parser/parser_include.h    2010-06-05 06:11:20 +0000
 @@ -1,8 +1,8 @@
 -/* $Id$ */
 -
 +
  #endif
 
+=== modified file 'parser/parser_interface.c'
+--- parser/parser_interface.c  2010-03-09 09:38:12 +0000
++++ parser/parser_interface.c  2010-08-03 17:27:13 +0000
+@@ -133,6 +133,9 @@
+                       PERROR(_("%s: Unable to write to stdout\n"),
+                              progname);
+                       break;
++              case OPTION_OFILE:
++                      PERROR(_("%s: Unable to write to output file\n"),
++                             progname);
+               default:
+                       PERROR(_("%s: ASSERT: Invalid option: %d\n"),
+                              progname, option);
+@@ -155,6 +158,7 @@
+                              cod->name);
+                       break;
+               case OPTION_STDOUT:
++              case OPTION_OFILE:
+                       break;
+               default:
+                       PERROR(_("%s: ASSERT: Invalid option: %d\n"),
+@@ -451,57 +455,6 @@
+       return 1;
+ }
+-int sd_serialize_pattern(sd_serialize *p, pcre *pat)
+-{
+-      if (!sd_write_struct(p, "pcre"))
+-              return 0;
+-      if (!sd_write32(p, pat->size - sizeof(pcre)))
+-              return 0;
+-      if (!sd_write32(p, pat->magic_number))
+-              return 0;
+-      if (!sd_write32(p, pat->options))
+-              return 0;
+-      if (!sd_write16(p, pat->top_bracket))
+-              return 0;
+-      if (!sd_write16(p, pat->top_backref))
+-              return 0;
+-      if (!sd_write8(p, pat->first_char))
+-              return 0;
+-      if (!sd_write8(p, pat->req_char))
+-              return 0;
+-      if (!sd_write8(p, pat->code[0]))
+-              return 0;
+-      if (!sd_write_blob(p, &pat->code[1], pat->size - sizeof(pcre), NULL))
+-              return 0;
+-      if (!sd_write_structend(p))
+-              return 0;
+-
+-      return 1;
+-}
+-
+-int sd_serialize_file_entry(sd_serialize *p, struct cod_entry *file_entry)
+-{
+-      PDEBUG("Writing file entry. name '%s'\n", file_entry->name);
+-      if (!sd_write_struct(p, "fe"))
+-              return 0;
+-      if (!sd_write_string(p, file_entry->name, NULL))
+-              return 0;
+-      if (!sd_write32(p, file_entry->mode))
+-              return 0;
+-      if (!sd_write32(p, file_entry->pattern_type))
+-              return 0;
+-      if (file_entry->pattern_type == ePatternRegex) {
+-              if (!sd_write_string(p, file_entry->pat.regex, NULL))
+-                      return 0;
+-              if (!sd_serialize_pattern(p, file_entry->pat.compiled))
+-                      return 0;
+-      }
+-      if (!sd_write_structend(p))
+-              return 0;
+-
+-      return 1;
+-}
+-
+ int sd_serialize_dfa(sd_serialize *p, void *dfa, size_t size)
+ {
+       if (dfa && !sd_write_aligned_blob(p, dfa, size, "aadfa"))
+@@ -594,18 +547,6 @@
+       return count;
+ }
+-int count_pcre_ents(struct cod_entry *list)
+-{
+-      struct cod_entry *entry;
+-      int count = 0;
+-      list_for_each(list, entry) {
+-              if (entry->pattern_type == ePatternRegex) {
+-                      count++;
+-              }
+-      }
+-      return count;
+-}
+-
+ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
+                        int flattened)
+ {
+@@ -724,47 +665,8 @@
+               if (!sd_serialize_xtable(p, profile->exec_table))
+                       return 0;
+       } else {
+-              /* pcre globbing entries */
+-              if (count_pcre_ents(profile->entries)) {
+-                      if (!sd_write_list(p, "pgent"))
+-                              return 0;
+-                      list_for_each(profile->entries, entry) {
+-                              if (entry->pattern_type == ePatternRegex) {
+-                                      if (!sd_serialize_file_entry(p, entry))
+-                                              return 0;
+-                              }
+-                      }
+-                      if (!sd_write_listend(p))
+-                              return 0;
+-              }
+-
+-              /* simple globbing entries */
+-              if (count_tailglob_ents(profile->entries)) {
+-                      if (!sd_write_list(p, "sgent"))
+-                              return 0;
+-                      list_for_each(profile->entries, entry) {
+-                              if (entry->pattern_type == ePatternTailGlob) {
+-                                      if (!sd_serialize_file_entry(p, entry))
+-                                              return 0;
+-                              }
+-                      }
+-                      if (!sd_write_listend(p))
+-                              return 0;
+-              }
+-
+-              /* basic file entries */
+-              if (count_file_ents(profile->entries)) {
+-                      if (!sd_write_list(p, "fent"))
+-                              return 0;
+-                      list_for_each(profile->entries, entry) {
+-                              if (entry->pattern_type == ePatternBasic) {
+-                                      if (!sd_serialize_file_entry(p, entry))
+-                                              return 0;
+-                              }
+-                      }
+-                      if (!sd_write_listend(p))
+-                              return 0;
+-              }
++              PERROR(_("Unknown pattern type\n"));
++              return 1;
+       }
+       if (profile->hat_table && regex_type != AARE_DFA) {
+@@ -812,7 +714,7 @@
+ int cache_fd = -1;
+ int sd_serialize_codomain(int option, struct codomain *cod)
+ {
+-      int fd;
++      int fd = -1;
+       int error = -ENOMEM, size, wsize;
+       sd_serialize *work_area;
+       char *filename = NULL;
+@@ -837,22 +739,29 @@
+               filename = "stdout";
+               fd = dup(1);
+               break;
++      case OPTION_OFILE:
++              fd = dup(fileno(ofile));
++              break;
+       default:
+               error = -EINVAL;
+               goto exit;
+               break;
+       }
+-      if (kernel_load && fd < 0) {
+-              PERROR(_("Unable to open %s - %s\n"), filename,
+-                     strerror(errno));
++      if (fd < 0) {
++              if (kernel_load)
++                      PERROR(_("Unable to open %s - %s\n"), filename,
++                             strerror(errno));
++              else
++                      PERROR(_("Unable to open output file - %s\n"),
++                             strerror(errno));
+               error = -errno;
+               goto exit;
+       }
+       error = 0;
+-      if (option != OPTION_STDOUT)
++      if (option != OPTION_STDOUT && option != OPTION_OFILE)
+               free(filename);
+       if (option == OPTION_REMOVE) {
+@@ -918,7 +827,7 @@
+               }
+               size = work_area->pos - work_area->buffer;
+-              if (kernel_load || option == OPTION_STDOUT) {
++              if (kernel_load || option == OPTION_STDOUT || option == OPTION_OFILE) {
+                       wsize = write(fd, work_area->buffer, size);
+                       if (wsize < 0) {
+                               error = -errno;
+@@ -941,7 +850,7 @@
+               free_sd_serial(work_area);
+       }
+-      if (kernel_load) close(fd);
++      close(fd);
+       if (cod->hat_table && regex_type == AARE_DFA && option != OPTION_REMOVE) {
+               if (load_flattened_hats(cod) != 0)
+@@ -973,7 +882,7 @@
+ int sd_load_buffer(int option, char *buffer, int size)
+ {
+-      int fd;
++      int fd = -1;
+       int error = -ENOMEM, wsize, bsize;
+       char *filename = NULL;
+       char *b;
+
 === modified file 'parser/parser_lex.l'
 --- parser/parser_lex.l        2010-03-09 05:49:16 +0000
-+++ parser/parser_lex.l        2010-06-05 01:57:01 +0000
++++ parser/parser_lex.l        2010-08-03 17:27:13 +0000
 @@ -1,8 +1,8 @@
 -/* $Id$ */
 -
   */
  
  /* Definitions section */
-@@ -49,7 +49,8 @@
+@@ -49,7 +49,12 @@
  #endif
  #define NPDEBUG(fmt, args...) /* Do nothing */
  
 -int current_lineno = 1;
-+int current_lineno     = 0;
++#define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0)
++
++#define YY_NO_INPUT
++
++int current_lineno     = 1;
 +char *current_filename = NULL;
  
  struct ignored_suffix_t {
        char * text;
-@@ -87,7 +88,8 @@
+@@ -80,14 +85,19 @@
+       char *fullpath = NULL;
+       if (search) {
++              if (preprocess_only)
++                      fprintf(yyout, "\n\n##included <%s>\n", filename);
+               include_file = search_path(filename, &fullpath);
+       } else {
++              if (preprocess_only)
++                      fprintf(yyout, "\n\n##included \"%s\"\n", filename);
+               fullpath = strdup(filename);
+               include_file = fopen(fullpath, "r");
        }
  
        if (!include_file)
  
          if (fstat(fileno(include_file), &my_stat))
                yyerror(_("fstat failed for '%s'"), fullpath);
-@@ -95,6 +97,7 @@
+@@ -95,6 +105,7 @@
          if (S_ISREG(my_stat.st_mode)) {
                yyin = include_file;
                PDEBUG("Opened include \"%s\"\n", fullpath);
                yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
          }
  
-@@ -139,8 +142,9 @@
+@@ -139,8 +150,9 @@
                                yyerror(_("stat failed for '%s'"), dirent_path);
                        if (S_ISREG(my_stat.st_mode)) {
                                if (!(yyin = fopen(dirent_path,"r")))
                                yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
                        }
                }
-@@ -227,6 +231,8 @@
+@@ -227,6 +239,8 @@
  }
  
  <<EOF>> {
        yypop_buffer_state();
        if ( !YY_CURRENT_BUFFER ) yyterminate();
  }
-
-=== modified file 'parser/parser_main.c'
---- parser/parser_main.c       2010-01-28 01:20:13 +0000
-+++ parser/parser_main.c       2010-06-05 01:47:44 +0000
-@@ -4,6 +4,9 @@
-  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-  *   NOVELL (All rights reserved)
-  *
-+ *   Copyright (c) 2010
-+ *   Canonical, Ltd. (All rights reserved)
-+ *
-  *   This program is free software; you can redistribute it and/or
-  *   modify it under the terms of version 2 of the GNU General Public
-  *   License published by the Free Software Foundation.
-@@ -14,7 +17,8 @@
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-- *   along with this program; if not, contact Novell, Inc.
-+ *   along with this program; if not, contact Novell, Inc. or Canonical,
-+ *   Ltd.
-  */
+@@ -240,6 +254,7 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id =  processunquoted(yytext, yyleng);
+                       PDEBUG("Found sub name: \"%s\"\n",  yylval.id);
+                       BEGIN(INITIAL);
+@@ -253,6 +268,7 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id = processquoted(yytext, yyleng);
+                       PDEBUG("Found sub name: \"%s\"\n", yylval.id);
+                       BEGIN(INITIAL);
+@@ -260,6 +276,7 @@
+               }
  
- #include <stdio.h>
-@@ -85,8 +89,6 @@
+       [^\n]   {
++                      DUMP_PREPROCESS;
+                       /* Something we didn't expect */
+                       yyerror(_("Found unexpected character: '%s'"), yytext);
+               }
+@@ -274,6 +291,7 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id = processunquoted(yytext, yyleng);
+                       PDEBUG("Found sub name: \"%s\"\n", yylval.id);
+                       BEGIN(INITIAL);
+@@ -287,14 +305,16 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id  = processquoted(yytext, yyleng);
+                       PDEBUG("Found sub name: \"%s\"\n", yylval.id);
+                       BEGIN(INITIAL);
+                       return TOK_ID;
+               }
+-      {WS}+                   {  /* Ignoring whitespace */ }
++      {WS}+   { DUMP_PREPROCESS; /* Ignoring whitespace */ }
+       [^\n]   {
++                      DUMP_PREPROCESS;
+                       /* Something we didn't expect */
+                       yyerror(_("Found unexpected character: '%s'"), yytext);
+               }
+@@ -302,85 +322,99 @@
+ <FLAGS_MODE>{
+       {FLAGOPEN_PAREN}        {
++                      DUMP_PREPROCESS;
+                       PDEBUG("FLag (\n");
+                       return TOK_FLAG_OPENPAREN;
+                       }
+       {FLAGCLOSE_PAREN}       {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Flag )\n");
+                       BEGIN(INITIAL);
+                       return TOK_FLAG_CLOSEPAREN;
+                       }
+-      {WS}+           { /* Eat whitespace */ }
++      {WS}+           { DUMP_PREPROCESS; /* Eat whitespace */ }
+       {FLAGSEP}       {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Flag , \n");
+                       return TOK_FLAG_SEP;
+                       }
+       {EQUALS}        {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Flag = \n");
+                       return TOK_EQUALS;
+                       }
+       {KEYWORD}       {
++                      DUMP_PREPROCESS;
+                       yylval.flag_id = strdup(yytext);
+                       PDEBUG("Found flag: \"%s\"\n", yylval.flag_id);
+                       return TOK_FLAG_ID;
+                       }
+       [^\n]           {
++                      DUMP_PREPROCESS;
+                       /* Something we didn't expect */
+                       yyerror(_("Found unexpected character: '%s'"), yytext);
+                       }
+ }
+ <ASSIGN_MODE>{
+-      {WS}+           { /* Eat whitespace */ }
++      {WS}+           { DUMP_PREPROCESS; /* Eat whitespace */ }
+       {ID}+           {
++                      DUMP_PREPROCESS;
+                       yylval.var_val = processunquoted(yytext, yyleng);
+                       PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val);
+                       return TOK_VALUE;
+                       }
+       {QUOTED_ID}     {
++                      DUMP_PREPROCESS;
+                       yylval.var_val = processquoted(yytext, yyleng);
+                       PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val);
+                       return TOK_VALUE;
+                       }
+-      \\\n            { current_lineno++ ; }
++      \\\n            { DUMP_PREPROCESS; current_lineno++ ; }
+       \r?\n           {
++                      DUMP_PREPROCESS;
+                       current_lineno++;
+                       BEGIN(INITIAL);
+                       }
+ }
+ <NETWORK_MODE>{
+-      {WS}+           { /* Eat whitespace */ }
++      {WS}+           { DUMP_PREPROCESS; /* Eat whitespace */ }
+       {ID}+           {
++                      DUMP_PREPROCESS;
+                       yylval.id = strdup(yytext);
+                       return TOK_ID;
+                       }
+       {END_OF_RULE}   {
++                      DUMP_PREPROCESS;
+                       BEGIN(INITIAL);
+                       return TOK_END_OF_RULE;
+               }
+       [^\n]           {
++                      DUMP_PREPROCESS;
+                         /* Something we didn't expect */
+                       yylval.id = strdup(yytext);
+                       yyerror(_("(network_mode) Found unexpected character: '%s'"), yylval.id);
+                       }
+       \r?\n           {
++                      DUMP_PREPROCESS;
+                       current_lineno++;
+                       }
+ }
+ <CHANGE_PROFILE_MODE>{
+       {ARROW}         {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Matched a change profile arrow\n");
+                       return TOK_ARROW;
+                       }
+@@ -393,6 +427,7 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id = processunquoted(yytext, yyleng);
+                       PDEBUG("Found change profile name: \"%s\"\n", yylval.id);
+                       BEGIN(INITIAL);
+@@ -406,14 +441,16 @@
+                          * a longer match). So now, when I want to
+                          * match any random string, I go into a
+                          * separate state. */
++                      DUMP_PREPROCESS;
+                       yylval.id = processquoted(yytext, yyleng);
+                       PDEBUG("Found change profile quoted name: \"%s\"\n", yylval.id);
+                       BEGIN(INITIAL);
+                       return TOK_ID;
+               }
+-      {WS}+                   {  /* Ignoring whitespace */ }
++      {WS}+                   {  DUMP_PREPROCESS; /* Ignoring whitespace */ }
+       [^\n]   {
++                      DUMP_PREPROCESS;
+                       /* Something we didn't expect */
+                       yyerror(_("Found unexpected character: '%s'"), yytext);
+               }
+@@ -421,127 +458,147 @@
+ #include/.*\r?\n       { /* include */
+                       PDEBUG("Matched #include\n");
+-                      current_lineno++;
+                       BEGIN(INCLUDE);
+                       }
+ #.*\r?\n              { /* normal comment */
++                      DUMP_PREPROCESS;
+                       PDEBUG("comment(%d): %s\n", current_lineno, yytext);
+                       current_lineno++;
+                       BEGIN(INITIAL);
+ }
+-{END_OF_RULE}         { return TOK_END_OF_RULE; }
++{END_OF_RULE}         { DUMP_PREPROCESS; return TOK_END_OF_RULE; }
+ {SEPARATOR}           {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Matched a separator\n");
+                       BEGIN(SUB_NAME);
+                       return TOK_SEP;
+                       }
+ {ARROW}                       {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Matched a arrow\n");
+                       return TOK_ARROW;
+                       }
+ {EQUALS}              {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Matched equals for assignment\n");
+                       BEGIN(ASSIGN_MODE);
+                       return TOK_EQUALS;
+                       }
+ {ADD_ASSIGN}          {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Matched additive value assignment\n");
+                       BEGIN(ASSIGN_MODE);
+                       return TOK_ADD_ASSIGN;
+                       }
+ <RLIMIT_MODE>{
+-      {WS}+           { /* Eat whitespace */ }
++      {WS}+           { DUMP_PREPROCESS; /* Eat whitespace */ }
+       -?{NUMBER}[kKMG]?  {
++                      DUMP_PREPROCESS;
+                       yylval.var_val = strdup(yytext);
+                       return TOK_VALUE;
+                       }
+       {KEYWORD}       {
++                      DUMP_PREPROCESS;
+                       yylval.id = strdup(yytext);
+                       if (strcmp(yytext, "infinity") == 0)
+                               return TOK_VALUE;
+                       return TOK_ID;
+                       }
+-      {LT_EQUAL}      { return TOK_LE; }
++      {LT_EQUAL}      { DUMP_PREPROCESS; return TOK_LE; }
+       {END_OF_RULE}   {
++                      DUMP_PREPROCESS;
+                       BEGIN(INITIAL);
+                       return TOK_END_OF_RULE;
+                       }
+       \\\n            {
++                      DUMP_PREPROCESS;
+                       current_lineno++;
+                       BEGIN(INITIAL);
+                       }
+       \r?\n           {
++                      DUMP_PREPROCESS;
+                       current_lineno++;
+                       BEGIN(INITIAL);
+                       }
+ }
+ {SET_VARIABLE}                {
++                      DUMP_PREPROCESS;
+                       yylval.set_var = strdup(yytext);
+                       PDEBUG("Found set variable %s\n", yylval.set_var);
+                       return TOK_SET_VAR;
+                       }
+ {BOOL_VARIABLE}               {
++                      DUMP_PREPROCESS;
+                       yylval.bool_var = strdup(yytext);
+                       PDEBUG("Found boolean variable %s\n", yylval.bool_var);
+                       return TOK_BOOL_VAR;
+                       }
+ {OPEN_BRACE}          {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Open Brace\n");
+                       return TOK_OPEN;
+                       }
+ {CLOSE_BRACE}         {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Close Brace\n");
+                       return TOK_CLOSE;
+                       }
+ {PATHNAME}            {
++                      DUMP_PREPROCESS;
+                       yylval.id = processunquoted(yytext, yyleng);
+                       PDEBUG("Found id: \"%s\"\n", yylval.id);
+                       return TOK_ID;
+                       }
+ {QPATHNAME}           {
++                      DUMP_PREPROCESS;
+                       yylval.id = processquoted(yytext, yyleng);
+                       PDEBUG("Found id: \"%s\"\n", yylval.id);
+                       return TOK_ID;
+                       }
+ {MODES}                       {
++                      DUMP_PREPROCESS;
+                       yylval.mode = strdup(yytext);
+                       PDEBUG("Found modes: %s\n", yylval.mode);
+                       return TOK_MODE;
+                       }
+ {HAT}                 {
++                      DUMP_PREPROCESS;
+                       BEGIN(SUB_NAME2);
+                       return TOK_HAT;
+                       }
+ {COLON}                       {
++                      DUMP_PREPROCESS;
+                       PDEBUG("Found a colon\n");
+                       return TOK_COLON;
+                       }
+ {FLAGOPEN_PAREN}      {
++                      DUMP_PREPROCESS;
+                       PDEBUG("FLag (\n");
+                       BEGIN(FLAGS_MODE);
+                       return TOK_FLAG_OPENPAREN;
+                       }
+ {VARIABLE_NAME}               {
++                      DUMP_PREPROCESS;
+                       int token = get_keyword_token(yytext);
+                       /* special cases */
+@@ -573,11 +630,13 @@
+                       return token;
+                       }
+-{WS}+                 {  /* Ignoring whitespace */ }
++{WS}+                 {  DUMP_PREPROCESS; /* Ignoring whitespace */ }
+-\r?\n                 { current_lineno++ ; }
++\r?\n                 { DUMP_PREPROCESS; current_lineno++ ; }
+ [^\n]                 {
++                      DUMP_PREPROCESS;
++
+                         /* Something we didn't expect */
+                       yyerror(_("Found unexpected character: '%s'"), yytext);
+                       }
+
+=== modified file 'parser/parser_main.c'
+--- parser/parser_main.c       2010-01-28 01:20:13 +0000
++++ parser/parser_main.c       2010-08-03 17:27:13 +0000
+@@ -4,6 +4,9 @@
+  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+  *   NOVELL (All rights reserved)
+  *
++ *   Copyright (c) 2010
++ *   Canonical, Ltd. (All rights reserved)
++ *
+  *   This program is free software; you can redistribute it and/or
+  *   modify it under the terms of version 2 of the GNU General Public
+  *   License published by the Free Software Foundation.
+@@ -14,7 +17,8 @@
+  *   GNU General Public License for more details.
+  *
+  *   You should have received a copy of the GNU General Public License
+- *   along with this program; if not, contact Novell, Inc.
++ *   along with this program; if not, contact Novell, Inc. or Canonical,
++ *   Ltd.
+  */
+ #include <stdio.h>
+@@ -26,6 +30,7 @@
+ #include <fcntl.h>
+ #include <mntent.h>
+ #include <libintl.h>
++#include <linux/limits.h>
+ #include <locale.h>
+ #define _(s) gettext(s)
+@@ -34,6 +39,8 @@
+ #include <unistd.h>
+ #include <sys/sysctl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
+ #include "parser.h"
+ #include "parser_version.h"
+@@ -47,7 +54,6 @@
+ #define MATCH_STRING "/sys/kernel/security/" MODULE_NAME "/matching"
+ #define FLAGS_FILE "/sys/kernel/security/" MODULE_NAME "/features"
+ #define MOUNTED_FS "/proc/mounts"
+-#define PCRE "pattern=pcre"
+ #define AADFA "pattern=aadfa"
+ #define PRIVILEGED_OPS (write_cache || kernel_load)
+@@ -76,6 +82,8 @@
+ #else
+ int read_implies_exec = 0;
+ #endif
++int preprocess_only = 0;
++int skip_mode_force = 0;
+ char *subdomainbase = NULL;
+ char *match_string = NULL;
+@@ -84,8 +92,7 @@
+ int perms_create = 0;         /* perms contain create flag */
  char *profile_namespace = NULL;
  int flag_changehat_version = FLAG_CHANGEHAT_1_5;
--extern int current_lineno;
 -
+-extern int current_lineno;
++FILE *ofile = NULL;
  /* per-profile settings */
  int force_complain = 0;
- char *profilename = NULL;
-@@ -224,8 +226,10 @@
+@@ -106,6 +113,7 @@
+       {"remove",              0, 0, 'R'},
+       {"names",               0, 0, 'N'},
+       {"stdout",              0, 0, 'S'},
++      {"ofile",               1, 0, 'o'},
+       {"match-string",        1, 0, 'm'},
+       {"quiet",               0, 0, 'q'},
+       {"skip-kernel-load",    0, 0, 'Q'},
+@@ -121,6 +129,7 @@
+       {"Dump",                1, 0, 'D'},
+       {"optimize",            1, 0, 'O'},
+       {"Optimize",            1, 0, 'O'},
++      {"preprocess",          0, 0, 'p'},
+       {NULL, 0, 0, 0},
+ };
+@@ -145,6 +154,7 @@
+              "-B, --binary            Input is precompiled profile\n"
+              "-N, --names             Dump names of profiles in input.\n"
+              "-S, --stdout            Dump compiled profile to stdout\n"
++             "-o n, --ofile n         Write output to file n\n"
+              "-b n, --base n          Set base dir and cwd\n"
+              "-I n, --Include n       Add n to the search path\n"
+              "-f n, --subdomainfs n   Set location of apparmor filesystem\n"
+@@ -160,9 +170,10 @@
+              "-Q, --skip-kernel-load  Do everything except loading into kernel\n"
+              "-V, --version           Display version info and exit\n"
+              "-d, --debug             Debug apparmor definitions\n"
++             "-p, --preprocess        Dump preprocessed profile\n"
+              "-D [n], --dump          Dump internal info for debugging\n"
+              "-O [n], --Optimize      Control dfa optimizations\n"
+-             "-h [command], --help    Display this text or info about command\n"
++             "-h [cmd], --help[=cmd]  Display this text or info about cmd\n"
+              ,command);
+ }
+@@ -175,6 +186,7 @@
+              "no option specified     Dump variables\n"
+              "variables               Dump variables\n"
+              "expanded-variables      Dump expanded variables\n"
++             "rule-exprs              Dump rule to expr tree conversions\n"
+              "expr-stats              Dump stats on expr tree\n"
+              "expr-tree               Dump expression tree\n"
+              "expr-simple             Dump simplified expression tree\n"
+@@ -224,8 +236,10 @@
        if (conf_quiet || names_only || option == OPTION_REMOVE)
                return;
  
                      current_lineno,
                      fmt);
        if (!newfmt)
-@@ -258,7 +262,7 @@
+@@ -244,7 +258,7 @@
+       int count = 0;
+       option = OPTION_ADD;
+-      while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:", long_options, &o)) != -1)
++      while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:po:", long_options, &o)) != -1)
+       {
+               switch (c) {
+               case 0:
+@@ -258,7 +272,7 @@
                        break;
                case 'd':
                        debug++;
                        break;
                case 'h':
                        if (!optarg) {
-@@ -316,7 +320,7 @@
+@@ -285,6 +299,7 @@
+               case 'R':
+                       count++;
+                       option = OPTION_REMOVE;
++                      skip_cache = 1;
+                       break;
+               case 'V':
+                       display_version();
+@@ -297,10 +312,12 @@
+                       set_base_dir(optarg);
+                       break;
+               case 'B':
+-                      binary_input =1;
++                      binary_input = 1;
++                      skip_cache = 1;
+                       break;
+               case 'C':
+                       opt_force_complain = 1;
++                      skip_cache = 1;
+                       break;
+               case 'N':
+                       names_only = 1;
+@@ -312,17 +329,31 @@
+                       skip_read_cache = 1;
+                       kernel_load = 0;
+                       break;
++              case 'o':
++                      count++;
++                      option = OPTION_OFILE;
++                      skip_read_cache = 1;
++                      kernel_load = 0;
++                      ofile = fopen(optarg, "w");
++                      if (!ofile) {
++                              PERROR("%s: Could not open file %s\n",
++                                     progname, optarg);
++                              exit(1);
++                      }
++                      break;
+               case 'f':
                        subdomainbase = strndup(optarg, PATH_MAX);
                        break;
                case 'D':
                        if (!optarg) {
                                dump_vars = 1;
                        } else if (strcmp(optarg, "variables") == 0) {
-@@ -359,7 +363,7 @@
+                               dump_vars = 1;
+                       } else if (strcmp(optarg, "expanded-variables") == 0) {
+                               dump_expanded_vars = 1;
++                      } else if (strcmp(optarg, "rule-exprs") == 0) {
++                              dfaflags |= DFA_DUMP_RULE_EXPR;
+                       } else if (strcmp(optarg, "expr-tree") == 0) {
+                               dfaflags |= DFA_DUMP_TREE;
+                       } else if (strcmp(optarg, "expr-simple") == 0) {
+@@ -359,7 +390,7 @@
                        }
                        break;
                case 'O':
                        if (strcmp(optarg, "0") == 0) {
                                dfaflags |= DFA_CONTROL_NO_TREE_NORMAL |
                                        DFA_CONTROL_NO_TREE_SIMPLE |
-@@ -695,12 +699,13 @@
+@@ -435,6 +466,13 @@
+               case 'Q':
+                       kernel_load = 0;
+                       break;
++              case 'p':
++                      count++;
++                      kernel_load = 0;
++                      skip_cache = 1;
++                      preprocess_only = 1;
++                      skip_mode_force = 1;
++                      break;
+               default:
+                       display_usage(progname);
+                       exit(0);
+@@ -569,9 +607,6 @@
+ out:
+       if (match_string) {
+-              if (strstr(match_string, PCRE))
+-                      regex_type = AARE_PCRE;
+-
+               if (strstr(match_string, AADFA))
+                       regex_type = AARE_DFA;
+@@ -620,20 +655,6 @@
+       return;
+ }
+-/* return 1 --> PCRE should work fine
+-   return 0 --> no PCRE support */
+-static int regex_support(void) {
+-      /* no match string, predates (or postdates?) the split matching
+-      module design */
+-      if (!match_string)
+-              return 1;
+-
+-      if (regex_type != AARE_NONE)
+-              return 1;
+-
+-      return 0;
+-}
+-
+ int process_binary(int option, char *profilename)
+ {
+       char *buffer = NULL;
+@@ -695,12 +716,33 @@
        return retval;
  }
  
        free_policies();
        reset_regex();
 +      reset_include_stack(filename);
++}
++
++int test_for_dir_mode(const char *basename, const char *linkdir)
++{
++      int rc = 0;
++
++      if (!skip_mode_force) {
++              char *target = NULL;
++              if (asprintf(&target, "%s/%s/%s", basedir, linkdir, basename) < 0) {
++                      perror("asprintf");
++                      exit(1);
++              }
++
++              if (access(target, R_OK) == 0)
++                      rc = 1;
++
++              free(target);
++      }
++
++      return rc;
  }
  
  int process_profile(int option, char *profilename)
-@@ -796,7 +801,7 @@
+@@ -727,37 +769,22 @@
+       if (profilename && option != OPTION_REMOVE) {
+               /* make decisions about disabled or complain-mode profiles */
+-              char *target = NULL;
+               char *basename = strrchr(profilename, '/');
+               if (basename)
+                       basename++;
+               else
+                       basename = profilename;
+-              if (asprintf(&target, "%s/%s/%s", basedir, "disable", basename) < 0) {
+-                      perror("asprintf");
+-                      exit(1);
+-              }
+-
+-              if (access(target, R_OK) == 0) {
+-                      if (!conf_quiet)
+-                              PERROR("Skipping profile in %s/disable: %s\n", basedir, basename);
+-                      free(target);
++              if (test_for_dir_mode(basename, "disable")) {
++                      if (!conf_quiet)
++                              PERROR("Skipping profile in %s/disable: %s\n", basedir, basename);
+                       goto out;
+               }
+-              free(target);
+-
+-              if (asprintf(&target, "%s/%s/%s", basedir, "force-complain", basename)<0) {
+-                      perror("asprintf");
+-                      exit(1);
+-              }
+-
+-              if (access(target, R_OK) == 0) {
+-                      if (!conf_quiet)
+-                              PERROR("Warning: found %s in %s/force-complain, forcing complain mode\n", basename, basedir);
+-                      force_complain = 1;
+-              }
+-              free(target);
++
++              if (test_for_dir_mode(basename, "force-complain")) {
++                      PERROR("Warning: found %s in %s/force-complain, forcing complain mode\n", basename, basedir);
++                      force_complain = 1;
++              }
+               if (!force_complain && !skip_cache) {
+                       fstat(fileno(yyin), &stat_text);
+@@ -796,12 +823,15 @@
  
        if (yyin)
                yyrestart(yyin);
  
        retval = yyparse();
        if (retval != 0)
-@@ -812,7 +817,7 @@
+               goto out;
++      if (preprocess_only)
++              goto out;
++
+       if (names_only) {
+               dump_policy_names();
+               goto out;
+@@ -812,7 +842,7 @@
                goto out;
        }
  
        if (retval != 0) {
                PERROR(_("%s: Errors found in file. Aborting.\n"), progname);
                goto out;
+@@ -829,10 +859,6 @@
+               goto out;
+       }
+-      if (!regex_support()) {
+-              die_if_any_regex();
+-      }
+-
+       retval = load_policy(option);
+ out:
+@@ -969,5 +995,8 @@
+               profilename = NULL;
+       }
++      if (ofile)
++              fclose(ofile);
++
+       return retval;
+ }
+
+=== modified file 'parser/parser_misc.c'
+--- parser/parser_misc.c       2010-03-09 04:38:54 +0000
++++ parser/parser_misc.c       2010-08-03 17:27:13 +0000
+@@ -19,6 +19,7 @@
+ /* assistance routines */
++#include <ctype.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+@@ -27,6 +28,7 @@
+ #define _(s) gettext(s)
+ #include <netinet/in.h>
+ #include <linux/socket.h>
++#include <linux/limits.h>
+ #include <arpa/inet.h>
+ #include <linux/capability.h>
+@@ -35,8 +37,10 @@
+ /* #define DEBUG */
+ #ifdef DEBUG
++#undef PDEBUG
+ #define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
+ #else
++#undef PDEBUG
+ #define PDEBUG(fmt, args...)  /* Do nothing */
+ #endif
+ #define NPDEBUG(fmt, args...) /* Do nothing */
+@@ -102,7 +106,7 @@
+ };
+ /* for alpha matches, check for keywords */
+-static int get_table_token(const char *name, struct keyword_table *table,
++static int get_table_token(const char *name __unused, struct keyword_table *table,
+                          const char *keyword)
+ {
+       int i;
+@@ -142,42 +146,6 @@
+       return get_table_token("rlimit", rlimit_table, name);
+ }
+-static struct keyword_table address_family[] = {
+-/*    {"unix",        AF_UNIX},
+-      {"local",       AF_LOCAL},      */
+-      {"inet",        AF_INET},
+-/*    {"ax25",        AF_AX25},
+-      {"ipx",         AF_IPX},
+-      {"appletalk",   AF_APPLETALK},
+-      {"netrom",      AF_NETROM},
+-      {"bridge",      AF_BRIDGE},
+-      {"atmpvc",      AF_ATMPVC},
+-      {"x25",         AF_X25}, */
+-      {"inet6",       AF_INET6},
+-/*    {"rose",        AF_ROSE},
+-      {"decnet",      AF_DECnet},
+-      {"netbeui",     AF_NETBEUI},
+-      {"security",    AF_SECURITY},
+-      {"key",         AF_KEY},
+-      {"netlink",     AF_NETLINK},
+-      {"route",       AF_ROUTE},
+-      {"packet",      AF_PACKET},
+-      {"ash",         AF_ASH},
+-      {"econet",      AF_ECONET},
+-      {"atmsvc",      AF_ATMSVC},
+-      {"sna",         AF_SNA},
+-      {"irda",        AF_IRDA},
+-      {"pppox",       AF_PPPOX},
+-      {"wanpipe",     AF_WANPIPE},
+-      {"llc",         AF_LLC},
+-      {"tipc",        AF_TIPC},
+-      {"bluetooth",   AF_BLUETOOTH},
+-      {"iucv",        AF_IUCV},
+-      {"rxrpc",       AF_RXRPC}, */
+-      /* terminate */
+-      {NULL, 0}
+-};
+-
+ struct network_tuple {
+       char *family_name;
+       unsigned int family;
+@@ -462,7 +430,7 @@
+       }
+ }
+-static int parse_sub_mode(const char *str_mode, const char *mode_desc)
++static int parse_sub_mode(const char *str_mode, const char *mode_desc __unused)
+ {
+ #define IS_DIFF_QUAL(mode, q) (((mode) & AA_MAY_EXEC) && (((mode) & AA_EXEC_TYPE) != ((q) & AA_EXEC_TYPE)))
+@@ -666,7 +634,6 @@
+       entry->pattern_type = ePatternInvalid;
+       entry->pat.regex = NULL;
+-      entry->pat.compiled = NULL;
+       entry->next = NULL;
+@@ -692,7 +659,6 @@
+       /* XXX - need to create copies of the patterns, too */
+       entry->pattern_type = orig->pattern_type;
+       entry->pat.regex = NULL;
+-      entry->pat.compiled = NULL;
+       entry->next = orig->next;
+@@ -713,8 +679,6 @@
+               free(list->link_name);
+       if (list->pat.regex)
+               free(list->pat.regex);
+-      if (list->pat.compiled)
+-              free(list->pat.compiled);
+       free(list);
+ }
 
 === modified file 'parser/parser_policy.c'
 --- parser/parser_policy.c     2009-08-20 15:27:12 +0000
-+++ parser/parser_policy.c     2010-03-12 23:26:32 +0000
++++ parser/parser_policy.c     2010-05-31 18:58:40 +0000
 @@ -4,6 +4,9 @@
   *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
   *   NOVELL (All rights reserved)
  
        return retval;
 
-=== modified file 'parser/parser_symtab.c'
---- parser/parser_symtab.c     2009-07-24 13:24:53 +0000
-+++ parser/parser_symtab.c     2010-03-12 22:41:58 +0000
-@@ -33,11 +33,6 @@
-       sd_set,
- };
--struct set_value {
--      char *val;
--      struct set_value *next;
--};
--
- struct symtab {
-       char *var_name;
-       enum var_type type;
-@@ -288,7 +283,7 @@
- /* returns a pointer to the value list, which should be used as the
-  * argument to the get_next_set_value() function. */
--void *get_set_var(const char *var)
-+struct set_value *get_set_var(const char *var)
- {
-       struct symtab *result;
-       struct set_value *valuelist = NULL;
-@@ -321,16 +316,17 @@
- }
+=== modified file 'parser/parser_regex.c'
+--- parser/parser_regex.c      2010-02-12 21:49:58 +0000
++++ parser/parser_regex.c      2010-08-03 17:27:13 +0000
+@@ -21,6 +21,7 @@
+ #include <stdarg.h>
+ #include <string.h>
+ #include <libintl.h>
++#include <linux/limits.h>
+ #define _(s) gettext(s)
  
- /* iterator to walk the list of set values */
--char *get_next_set_value(void **list)
-+char *get_next_set_value(struct set_value **list)
+ /* #define DEBUG */
+@@ -74,7 +75,6 @@
  {
--      struct set_value **valuelist = (struct set_value **) list;
-+      struct set_value *next;
-       char *ret;
+       char *sptr, *dptr;
+       BOOL seen_slash = 0;
+-      int len;
  
--      if (!valuelist || !(*valuelist))
-+      if (!list || !(*list))
-               return NULL;
+       if (!path || (strlen(path) < 2))
+               return;
+@@ -106,19 +106,6 @@
+               }
+       }
+       *dptr = 0;
+-
+-      if (regex_type != AARE_DFA) {
+-              /* eliminate trailing slashes for versions of apparmor that
+-               * do not use the dfa engine.
+-               * Versions of apparmor which use the dfa engine use the
+-               * trailing / to differentiate between file and directory
+-               * matches
+-               */
+-              len = strlen(path);
+-              if (len > 2 && path[len -1] == '/') {
+-                      path[len - 1] = 0;
+-              }
+-      }
+ }
  
--      ret = (*valuelist)->val;
--      (*valuelist) = (*valuelist)->next;
-+      ret = (*list)->val;
-+      next = (*list)->next;
-+      (*list) = next;
+ static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
+@@ -154,6 +141,9 @@
+       sptr = aare;
+       dptr = pcre;
  
-       return ret;
++      if (dfaflags & DFA_DUMP_RULE_EXPR)
++              fprintf(stderr, "aare: %s   ->   ", aare);
++
+       if (anchor)
+               /* anchor beginning of regular expression */
+               *dptr++ = '^';
+@@ -406,85 +396,13 @@
+ out:
+       if (ret == FALSE)
+               ptype = ePatternInvalid;
++
++      if (dfaflags & DFA_DUMP_RULE_EXPR)
++              fprintf(stderr, "%s\n", pcre);
++
+       return ptype;
  }
-@@ -569,7 +565,7 @@
- {
-       int rc = 0;
-       int retval;
--      void *retptr;
+-static int process_pcre_entry(struct cod_entry *entry)
+-{
+-      char tbuf[PATH_MAX + 3];        /* +3 for ^, $ and \0 */
+-      int ret = TRUE;
+-      pattern_t ptype;
+-      int pos;
+-      if (!entry)             /* shouldn't happen */
+-              return TRUE;
+-
+-      ptype = convert_aaregex_to_pcre(entry->name, 1, tbuf, PATH_MAX+3, &pos);
+-      if (ptype == ePatternInvalid)
+-              return FALSE;
+-
+-      entry->pattern_type = ptype;
+-
+-      /*
+-       * Only use buffer (tbuf) that we built above, if we
+-       * identified a pattern requiring full regex support.
+-       */
+-      if (ptype == ePatternRegex) {
+-              int pattlen = strlen(tbuf);
+-
+-              if ((entry->pat.regex = malloc(pattlen + 1))) {
+-                      const char *errorreason;
+-                      int errpos;
+-
+-                      strcpy(entry->pat.regex, tbuf);
+-
+-                      if ((entry->pat.compiled =
+-                           pcre_compile(entry->pat.regex, 0,
+-                                        &errorreason, &errpos,
+-                                        NULL))) {
+-                              /* NULL out tables, kernel will use a
+-                               * private version
+-                               */
+-                              entry->pat.compiled->tables = NULL;
+-                      } else {
+-                              int i;
+-
+-                              PERROR(_("%s: Failed to compile regex '%s' [original: '%s']\n"),
+-                                     progname, entry->pat.regex,
+-                                     entry->name);
+-
+-                              PERROR(_("%s: error near               "),
+-                                     progname);
+-
+-                              for (i = 0; i < errpos; i++) {
+-                                      fputc('.', stderr);
+-                              }
+-
+-                              fputc('^', stderr);
+-                              fputc('\n', stderr);
+-
+-                              PERROR(_("%s: error reason: '%s'\n"),
+-                                     progname, errorreason);
+-
+-                              free(entry->pat.regex);
+-                              entry->pat.regex = NULL;
+-
+-                              ret = FALSE;
+-                      }
+-              } else {
+-                      PERROR(_("%s: Failed to compile regex '%s' [original: '%s'] - malloc failed\n"),
+-                             progname, entry->pat.regex, entry->name);
+-
+-                      ret = FALSE;
+-              }
+-      } else {
+-              /* not a regex, scan input for any escape characters
+-               * and remove, and reduce double \\ to a single */
+-              filter_escapes(entry->name);
+-      }               /* ptype == ePatternRegex */
+-
+-      return ret;
+-}
+-
+ static const char *local_name(const char *name)
+ {
+       const char *t;
+@@ -519,7 +437,7 @@
+               aare_ruleset_t *rule = aare_new_ruleset(0);
+               if (!rule)
+                       return FALSE;
+-              if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0)) {
++              if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
+                       aare_delete_ruleset(rule);
+                       return FALSE;
+               }
+@@ -529,7 +447,7 @@
+                               int len;
+                               convert_aaregex_to_pcre(alt->name, 0, tbuf,
+                                                       PATH_MAX + 3, &len);
+-                              if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0)) {
++                              if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
+                                       aare_delete_ruleset(rule);
+                                       return FALSE;
+                               }
+@@ -538,7 +456,7 @@
+               cod->xmatch = aare_create_dfa(rule, &cod->xmatch_size,
+                                             dfaflags);
+               aare_delete_ruleset(rule);
+-              if (!cod->xmatch)
++              if (!cod->xmatch)
+                       return FALSE;
+       }
+@@ -581,11 +499,11 @@
+       if (entry->deny && (entry->mode & AA_LINK_BITS)) {
+               if (!aare_add_rule(dfarules, tbuf, entry->deny,
+                                  entry->mode & ~AA_LINK_BITS,
+-                                 entry->audit & ~AA_LINK_BITS))
++                                 entry->audit & ~AA_LINK_BITS, dfaflags))
+                       return FALSE;
+       } else if (entry->mode & ~AA_CHANGE_PROFILE) {
+               if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode,
+-                                 entry->audit))
++                                 entry->audit, dfaflags))
+                       return FALSE;
+       }
+@@ -607,7 +525,7 @@
+                       perms |= LINK_TO_LINK_SUBSET(perms);
+                       vec[1] = "/[^/].*";
+               }
+-              if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec))
++              if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec, dfaflags))
+                       return FALSE;
+       }
+       if (entry->mode & AA_CHANGE_PROFILE) {
+@@ -618,10 +536,10 @@
+                       ptype = convert_aaregex_to_pcre(entry->namespace, 0, lbuf, PATH_MAX + 8, &pos);
+                       vec[0] = lbuf;
+                       vec[1] = tbuf;
+-                      if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec))
++                      if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec, dfaflags))
+                           return FALSE;
+               } else {
+-                      if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0))
++                if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0, dfaflags))
+                               return FALSE;
+               }
+       }
+@@ -631,10 +549,10 @@
+                       char *vec[2];
+                       vec[0] = entry->namespace;
+                       vec[1] = entry->name;
+-                      if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec))
++                      if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec, dfaflags))
+                           return FALSE;
+               } else {
+-                      if (!aare_add_rule(dfarules, entry->name, 0, mode, 0))
++                if (!aare_add_rule(dfarules, entry->name, 0, mode, 0, dfaflags))
+                               return FALSE;
+               }
+       }
+@@ -643,18 +561,13 @@
+ int post_process_entries(struct codomain *cod)
+ {
+-      int ret = TRUE, rc;
++      int ret = TRUE;
+       struct cod_entry *entry;
+       int count = 0;
+       list_for_each(cod->entries, entry) {
+-              if (regex_type == AARE_DFA) {
+-                      rc = process_dfa_entry(cod->dfarules, entry);
+-              } else {
+-                      filter_slashes(entry->name);
+-                      rc = process_pcre_entry(entry);
+-              }
+-              if (!rc)
++              if (regex_type == AARE_DFA &&
++                  !process_dfa_entry(cod->dfarules, entry))
+                       ret = FALSE;
+               count++;
+       }
+@@ -770,7 +683,7 @@
+       test_string = strdup("///foo//////f//oo////////////////");
+       filter_slashes(test_string);
+-      MY_TEST(strcmp(test_string, "/foo/f/oo") == 0, "simple tests");
++      MY_TEST(strcmp(test_string, "/foo/f/oo/") == 0, "simple tests");
+       test_string = strdup("/foo/f/oo");
+       filter_slashes(test_string);
+@@ -802,13 +715,11 @@
+       test_string = strdup("/a/");
+       filter_slashes(test_string);
+-      MY_TEST(strcmp(test_string, "/a") == 0, "simple test for /a/");
++      MY_TEST(strcmp(test_string, "/a/") == 0, "simple test for /a/");
+       return rc;
+ }
+-int regex_type = AARE_PCRE;
+-
+ int main(void)
+ {
+       int rc = 0;
+
+=== modified file 'parser/parser_symtab.c'
+--- parser/parser_symtab.c     2009-07-24 13:24:53 +0000
++++ parser/parser_symtab.c     2010-08-03 17:27:13 +0000
+@@ -23,6 +23,7 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <libintl.h>
++#include <linux/limits.h>
+ #define _(s) gettext(s)
+ #include "immunix.h"
+@@ -33,11 +34,6 @@
+       sd_set,
+ };
+-struct set_value {
+-      char *val;
+-      struct set_value *next;
+-};
+-
+ struct symtab {
+       char *var_name;
+       enum var_type type;
+@@ -288,7 +284,7 @@
+ /* returns a pointer to the value list, which should be used as the
+  * argument to the get_next_set_value() function. */
+-void *get_set_var(const char *var)
++struct set_value *get_set_var(const char *var)
+ {
+       struct symtab *result;
+       struct set_value *valuelist = NULL;
+@@ -321,16 +317,17 @@
+ }
+ /* iterator to walk the list of set values */
+-char *get_next_set_value(void **list)
++char *get_next_set_value(struct set_value **list)
+ {
+-      struct set_value **valuelist = (struct set_value **) list;
++      struct set_value *next;
+       char *ret;
+-      if (!valuelist || !(*valuelist))
++      if (!list || !(*list))
+               return NULL;
+-      ret = (*valuelist)->val;
+-      (*valuelist) = (*valuelist)->next;
++      ret = (*list)->val;
++      next = (*list)->next;
++      (*list) = next;
+       return ret;
+ }
+@@ -429,10 +426,13 @@
+                       for (ref_item = ref->expanded; ref_item; ref_item = ref_item->next) {
+                               char *expanded_string;
+-                              asprintf(&expanded_string, "%s%s%s",
++                              if (!asprintf(&expanded_string, "%s%s%s",
+                                        split->prefix ?  split->prefix : "",
+                                        ref_item->val,
+-                                       split->suffix ?  split->suffix : "");
++                                       split->suffix ?  split->suffix : "")) {
++                                      PERROR("Out of memory\n");
++                                      exit(1);
++                              }
+                               add_to_set(&work_list, expanded_string);
+                               free(expanded_string);
+                       }
+@@ -569,7 +569,7 @@
+ {
+       int rc = 0;
+       int retval;
+-      void *retptr;
 +      struct set_value *retptr;
        struct symtab *a, *b;
  
 
 === modified file 'parser/parser_variable.c'
 --- parser/parser_variable.c   2009-07-24 23:47:46 +0000
-+++ parser/parser_variable.c   2010-03-12 23:20:22 +0000
-@@ -124,7 +124,7 @@
++++ parser/parser_variable.c   2010-08-03 17:27:13 +0000
+@@ -17,10 +17,14 @@
+  *   along with this program; if not, contact Novell, Inc.
+  */
++#include <ctype.h>
++#include <stdlib.h>
+ #include <stdio.h>
+ #include <stdarg.h>
+ #include <string.h>
+ #include <libintl.h>
++#include <linux/limits.h>
++
+ #define _(s) gettext(s)
+ /* #define DEBUG */
+@@ -124,7 +128,7 @@
  
  static int expand_entry_variables(struct cod_entry *entry)
  {
        char *value;
        struct var_string *split_var;
 
-=== modified file 'parser/parser_yacc.y'
---- parser/parser_yacc.y       2010-02-17 20:21:52 +0000
-+++ parser/parser_yacc.y       2010-06-05 01:57:01 +0000
-@@ -1,9 +1,9 @@
- %{
--/* $Id$ */
+=== modified file 'parser/parser_yacc.y'
+--- parser/parser_yacc.y       2010-02-17 20:21:52 +0000
++++ parser/parser_yacc.y       2010-08-03 17:27:13 +0000
+@@ -1,9 +1,9 @@
+ %{
+-/* $Id$ */
+-
+ /*
+  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+  *   NOVELL (All rights reserved)
++ *   Copyright (c) 2010
++ *   Canonical, Ltd.
+  *
+  *   This program is free software; you can redistribute it and/or
+  *   modify it under the terms of version 2 of the GNU General Public
+@@ -15,7 +15,7 @@
+  *   GNU General Public License for more details.
+  *
+  *   You should have received a copy of the GNU General Public License
+- *   along with this program; if not, contact Novell, Inc.
++ *   along with this program; if not, contact Canonical, Ltd.
+  */
+ #define YYERROR_VERBOSE 1
+@@ -32,6 +32,7 @@
+ /* #define DEBUG */
+ #include "parser.h"
++#include "parser_include.h"
+ #include <unistd.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+@@ -63,10 +64,6 @@
+ #define CAP_TO_MASK(x) (1ull << (x))
+-/* from lex_config, for nice error messages */
+-/* extern char *current_file; */
+-extern int current_lineno;
+-
+ struct value_list {
+       char *value;
+       struct value_list *next;
+@@ -415,7 +412,7 @@
+ flagval:      TOK_FLAG_ID
+       {
+-              struct flagval fv = {0, 0, 0};
++              struct flagval fv = { 0, 0, 0, 0 };
+               if (strcmp($1, "debug") == 0) {
+                       yyerror(_("Profile flag 'debug' is no longer valid."));
+               } else if (strcmp($1, "complain") == 0) {
+@@ -1109,10 +1106,15 @@
+       va_end(arg);
+       if (profilename) {
+-              PERROR(_("AppArmor parser error in %s at line %d: %s\n"),
+-                     profilename, current_lineno, buf);
++              PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"),
++                     profilename,
++                     current_filename ? " in " : "",
++                     current_filename ? current_filename : "",
++                     current_lineno, buf);
+       } else {
+-              PERROR(_("AppArmor parser error, line %d: %s\n"),
++              PERROR(_("AppArmor parser error,%s%s line %d: %s\n"),
++                     current_filename ? " in " : "",
++                     current_filename ? current_filename : "",
+                      current_lineno, buf);
+       }
+
+=== removed directory 'parser/pcre'
+=== removed file 'parser/pcre/Makefile'
+--- parser/pcre/Makefile       2007-03-30 16:30:15 +0000
++++ parser/pcre/Makefile       1970-01-01 00:00:00 +0000
+@@ -1,7 +0,0 @@
+-all: pcre.o
+-
+-pcre.o:  pcre.c
+-      $(CC) -c $(CFLAGS) $<
+-
+-clean:
+-      rm -f pcre.o
+
+=== removed file 'parser/pcre/chartables.c'
+--- parser/pcre/chartables.c   2006-04-11 21:52:54 +0000
++++ parser/pcre/chartables.c   1970-01-01 00:00:00 +0000
+@@ -1,183 +0,0 @@
+-/*************************************************
+-*      Perl-Compatible Regular Expressions       *
+-*************************************************/
+-
+-/* This file is automatically written by the dftables auxiliary 
+-program. If you edit it by hand, you might like to edit the Makefile to 
+-prevent its ever being regenerated.
+-
+-This file is #included in the compilation of pcre.c to build the default
+-character tables which are used when no tables are passed to the compile
+-function. */
+-
+-static unsigned char pcre_default_tables[] = {
+-
+-/* This table is a lower casing table. */
+-
+-    0,  1,  2,  3,  4,  5,  6,  7,
+-    8,  9, 10, 11, 12, 13, 14, 15,
+-   16, 17, 18, 19, 20, 21, 22, 23,
+-   24, 25, 26, 27, 28, 29, 30, 31,
+-   32, 33, 34, 35, 36, 37, 38, 39,
+-   40, 41, 42, 43, 44, 45, 46, 47,
+-   48, 49, 50, 51, 52, 53, 54, 55,
+-   56, 57, 58, 59, 60, 61, 62, 63,
+-   64, 97, 98, 99,100,101,102,103,
+-  104,105,106,107,108,109,110,111,
+-  112,113,114,115,116,117,118,119,
+-  120,121,122, 91, 92, 93, 94, 95,
+-   96, 97, 98, 99,100,101,102,103,
+-  104,105,106,107,108,109,110,111,
+-  112,113,114,115,116,117,118,119,
+-  120,121,122,123,124,125,126,127,
+-  128,129,130,131,132,133,134,135,
+-  136,137,138,139,140,141,142,143,
+-  144,145,146,147,148,149,150,151,
+-  152,153,154,155,156,157,158,159,
+-  160,161,162,163,164,165,166,167,
+-  168,169,170,171,172,173,174,175,
+-  176,177,178,179,180,181,182,183,
+-  184,185,186,187,188,189,190,191,
+-  192,193,194,195,196,197,198,199,
+-  200,201,202,203,204,205,206,207,
+-  208,209,210,211,212,213,214,215,
+-  216,217,218,219,220,221,222,223,
+-  224,225,226,227,228,229,230,231,
+-  232,233,234,235,236,237,238,239,
+-  240,241,242,243,244,245,246,247,
+-  248,249,250,251,252,253,254,255,
+-
+-/* This table is a case flipping table. */
+-
+-    0,  1,  2,  3,  4,  5,  6,  7,
+-    8,  9, 10, 11, 12, 13, 14, 15,
+-   16, 17, 18, 19, 20, 21, 22, 23,
+-   24, 25, 26, 27, 28, 29, 30, 31,
+-   32, 33, 34, 35, 36, 37, 38, 39,
+-   40, 41, 42, 43, 44, 45, 46, 47,
+-   48, 49, 50, 51, 52, 53, 54, 55,
+-   56, 57, 58, 59, 60, 61, 62, 63,
+-   64, 97, 98, 99,100,101,102,103,
+-  104,105,106,107,108,109,110,111,
+-  112,113,114,115,116,117,118,119,
+-  120,121,122, 91, 92, 93, 94, 95,
+-   96, 65, 66, 67, 68, 69, 70, 71,
+-   72, 73, 74, 75, 76, 77, 78, 79,
+-   80, 81, 82, 83, 84, 85, 86, 87,
+-   88, 89, 90,123,124,125,126,127,
+-  128,129,130,131,132,133,134,135,
+-  136,137,138,139,140,141,142,143,
+-  144,145,146,147,148,149,150,151,
+-  152,153,154,155,156,157,158,159,
+-  160,161,162,163,164,165,166,167,
+-  168,169,170,171,172,173,174,175,
+-  176,177,178,179,180,181,182,183,
+-  184,185,186,187,188,189,190,191,
+-  192,193,194,195,196,197,198,199,
+-  200,201,202,203,204,205,206,207,
+-  208,209,210,211,212,213,214,215,
+-  216,217,218,219,220,221,222,223,
+-  224,225,226,227,228,229,230,231,
+-  232,233,234,235,236,237,238,239,
+-  240,241,242,243,244,245,246,247,
+-  248,249,250,251,252,253,254,255,
+-
+-/* This table contains bit maps for various character classes.
+-Each map is 32 bytes long and the bits run from the least
+-significant end of each byte. The classes that have their own
+-maps are: space, xdigit, digit, upper, lower, word, graph
+-print, punct, and cntrl. Other classes are built from combinations. */
+-
+-  0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+-  0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+-  0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
+-  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
+-  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
+-  0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-  0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+-
+-/* This table identifies various classes of character by individual bits:
+-  0x01   white space character
+-  0x02   letter
+-  0x04   decimal digit
+-  0x08   hexadecimal digit
+-  0x10   alphanumeric or '_'
+-  0x80   regular expression metacharacter or binary zero
+-*/
+-
+-  0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7 */
+-  0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /*   8- 15 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
+-  0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /*    - '  */
+-  0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /*  ( - /  */
+-  0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /*  0 - 7  */
+-  0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /*  8 - ?  */
+-  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  @ - G  */
+-  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  H - O  */
+-  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  P - W  */
+-  0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /*  X - _  */
+-  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  ` - g  */
+-  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  h - o  */
+-  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  p - w  */
+-  0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /*  x -127 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
+-  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
+-
+-/* End of chartables.c */
+
+=== removed file 'parser/pcre/config.h'
+--- parser/pcre/config.h       2006-04-11 21:52:54 +0000
++++ parser/pcre/config.h       1970-01-01 00:00:00 +0000
+@@ -1,48 +0,0 @@
+-/* config.h.  Generated automatically by configure.  */
+-
+-/* On Unix systems config.in is converted by configure into config.h. PCRE is
+-written in Standard C, but there are a few non-standard things it can cope
+-with, allowing it to run on SunOS4 and other "close to standard" systems.
+-
+-On a non-Unix system you should just copy this file into config.h, and set up
+-the macros the way you need them. You should normally change the definitions of
+-HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because of the way autoconf
+-works, these cannot be made the defaults. If your system has bcopy() and not
+-memmove(), change the definition of HAVE_BCOPY instead of HAVE_MEMMOVE. If your
+-system has neither bcopy() nor memmove(), leave them both as 0; an emulation
+-function will be used. */
+-
+-/* Define to empty if the keyword does not work. */
+-
+-/* #undef const */
+-
+-/* Define to `unsigned' if <stddef.h> doesn't define size_t. */
+-
+-/* #undef size_t */
+-
+-/* The following two definitions are mainly for the benefit of SunOS4, which
+-doesn't have the strerror() or memmove() functions that should be present in
+-all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should
+-normally be defined with the value 1 for other systems, but unfortunately we
+-can't make this the default because "configure" files generated by autoconf
+-will only change 0 to 1; they won't change 1 to 0 if the functions are not
+-found. */
+-
+-#define HAVE_STRERROR 1
+-#define HAVE_MEMMOVE 1
+-
+-/* There are some non-Unix systems that don't even have bcopy(). If this macro
+-is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of
+-HAVE_BCOPY is not relevant. */
+-
+-#define HAVE_BCOPY 1
+-
+-/* The value of NEWLINE determines the newline character. The default is to
+-leave it up to the compiler, but some sites want to force a particular value.
+-On Unix systems, "configure" can be used to override this default. */
+-
+-#ifndef NEWLINE
+-#define NEWLINE '\n'
+-#endif
+-
+-/* End */
+
+=== removed file 'parser/pcre/internal.h'
+--- parser/pcre/internal.h     2006-04-11 21:52:54 +0000
++++ parser/pcre/internal.h     1970-01-01 00:00:00 +0000
+@@ -1,412 +0,0 @@
+-/*************************************************
+-*      Perl-Compatible Regular Expressions       *
+-*************************************************/
+-
+-
+-/* This is a library of functions to support regular expressions whose syntax
+-and semantics are as close as possible to those of the Perl 5 language. See
+-the file Tech.Notes for some information on the internals.
+-
+-Written by: Philip Hazel <ph10@cam.ac.uk>
+-
+-           Copyright (c) 1997-2001 University of Cambridge
+-
+------------------------------------------------------------------------------
+-Permission is granted to anyone to use this software for any purpose on any
+-computer system, and to redistribute it freely, subject to the following
+-restrictions:
+-
+-1. This software 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.
+-
+-2. The origin of this software must not be misrepresented, either by
+-   explicit claim or by omission.
+-
+-3. Altered versions must be plainly marked as such, and must not be
+-   misrepresented as being the original software.
+-
+-4. If PCRE is embedded in any software that is released under the GNU
+-   General Purpose Licence (GPL), then the terms of that licence shall
+-   supersede any condition above with which it is incompatible.
+------------------------------------------------------------------------------
+-*/
+-
+-/* This header contains definitions that are shared between the different
+-modules, but which are not relevant to the outside. */
+-
+-/* Get the definitions provided by running "configure" */
+-
+-#include "config.h"
+-
+-/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(),
+-define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY
+-is set. Otherwise, include an emulating function for those systems that have
+-neither (there some non-Unix environments where this is the case). This assumes
+-that all calls to memmove are moving strings upwards in store, which is the
+-case in PCRE. */
+-
+-#if ! HAVE_MEMMOVE
+-#undef  memmove        /* some systems may have a macro */
+-#if HAVE_BCOPY
+-#define memmove(a, b, c) bcopy(b, a, c)
+-#else
+-void *
+-pcre_memmove(unsigned char *dest, const unsigned char *src, size_t n)
+-{
+-int i;
+-dest += n;
+-src += n;
+-for (i = 0; i < n; ++i) *(--dest) =  *(--src);
+-}
+-#define memmove(a, b, c) pcre_memmove(a, b, c)
+-#endif
+-#endif
+-
+-/* Standard C headers plus the external interface definition */
+-
+-#include <ctype.h>
+-#include <limits.h>
+-#include <stddef.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include "pcre.h"
+-
+-/* In case there is no definition of offsetof() provided - though any proper
+-Standard C system should have one. */
+-
+-#ifndef offsetof
+-#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
+-#endif
+-
+-/* These are the public options that can change during matching. */
+-
+-#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
+-
+-/* Private options flags start at the most significant end of the four bytes,
+-but skip the top bit so we can use ints for convenience without getting tangled
+-with negative values. The public options defined in pcre.h start at the least
+-significant end. Make sure they don't overlap, though now that we have expanded
+-to four bytes there is plenty of space. */
+-
+-#define PCRE_FIRSTSET      0x40000000  /* first_char is set */
+-#define PCRE_REQCHSET      0x20000000  /* req_char is set */
+-#define PCRE_STARTLINE     0x10000000  /* start after \n for multiline */
+-#define PCRE_INGROUP       0x08000000  /* compiling inside a group */
+-#define PCRE_ICHANGED      0x04000000  /* i option changes within regex */
+-
+-/* Options for the "extra" block produced by pcre_study(). */
+-
+-#define PCRE_STUDY_MAPPED   0x01     /* a map of starting chars exists */
+-
+-/* Masks for identifying the public options which are permitted at compile
+-time, run time or study time, respectively. */
+-
+-#define PUBLIC_OPTIONS \
+-  (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \
+-   PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8)
+-
+-#define PUBLIC_EXEC_OPTIONS \
+-  (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY)
+-
+-#define PUBLIC_STUDY_OPTIONS 0   /* None defined */
+-
+-/* Magic number to provide a small check against being handed junk. */
+-
+-#define MAGIC_NUMBER  0x50435245UL   /* 'PCRE' */
+-
+-/* Miscellaneous definitions */
+-
+-typedef int BOOL;
+-
+-#define FALSE   0
+-#define TRUE    1
+-
+-/* Escape items that are just an encoding of a particular data value. Note that
+-ESC_N is defined as yet another macro, which is set in config.h to either \n
+-(the default) or \r (which some people want). */
+-
+-#ifndef ESC_E
+-#define ESC_E 27
+-#endif
+-
+-#ifndef ESC_F
+-#define ESC_F '\f'
+-#endif
+-
+-#ifndef ESC_N
+-#define ESC_N NEWLINE
+-#endif
+-
+-#ifndef ESC_R
+-#define ESC_R '\r'
+-#endif
+-
+-#ifndef ESC_T
+-#define ESC_T '\t'
+-#endif
+-
+-/* These are escaped items that aren't just an encoding of a particular data
+-value such as \n. They must have non-zero values, as check_escape() returns
+-their negation. Also, they must appear in the same order as in the opcode
+-definitions below, up to ESC_z. The final one must be ESC_REF as subsequent
+-values are used for \1, \2, \3, etc. There is a test in the code for an escape
+-greater than ESC_b and less than ESC_Z to detect the types that may be
+-repeated. If any new escapes are put in-between that don't consume a character,
+-that code will have to change. */
+-
+-enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w,
+-       ESC_Z, ESC_z, ESC_REF };
+-
+-/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets
+-that extract substrings. Starting from 1 (i.e. after OP_END), the values up to
+-OP_EOD must correspond in order to the list of escapes immediately above. */
+-
+-enum {
+-  OP_END,            /* End of pattern */
+-
+-  /* Values corresponding to backslashed metacharacters */
+-
+-  OP_SOD,            /* Start of data: \A */
+-  OP_NOT_WORD_BOUNDARY,  /* \B */
+-  OP_WORD_BOUNDARY,      /* \b */
+-  OP_NOT_DIGIT,          /* \D */
+-  OP_DIGIT,              /* \d */
+-  OP_NOT_WHITESPACE,     /* \S */
+-  OP_WHITESPACE,         /* \s */
+-  OP_NOT_WORDCHAR,       /* \W */
+-  OP_WORDCHAR,           /* \w */
+-  OP_EODN,           /* End of data or \n at end of data: \Z. */
+-  OP_EOD,            /* End of data: \z */
+-
+-  OP_OPT,            /* Set runtime options */
+-  OP_CIRC,           /* Start of line - varies with multiline switch */
+-  OP_DOLL,           /* End of line - varies with multiline switch */
+-  OP_ANY,            /* Match any character */
+-  OP_CHARS,          /* Match string of characters */
+-  OP_NOT,            /* Match anything but the following char */
+-
+-  OP_STAR,           /* The maximizing and minimizing versions of */
+-  OP_MINSTAR,        /* all these opcodes must come in pairs, with */
+-  OP_PLUS,           /* the minimizing one second. */
+-  OP_MINPLUS,        /* This first set applies to single characters */
+-  OP_QUERY,
+-  OP_MINQUERY,
+-  OP_UPTO,           /* From 0 to n matches */
+-  OP_MINUPTO,
+-  OP_EXACT,          /* Exactly n matches */
+-
+-  OP_NOTSTAR,        /* The maximizing and minimizing versions of */
+-  OP_NOTMINSTAR,     /* all these opcodes must come in pairs, with */
+-  OP_NOTPLUS,        /* the minimizing one second. */
+-  OP_NOTMINPLUS,     /* This first set applies to "not" single characters */
+-  OP_NOTQUERY,
+-  OP_NOTMINQUERY,
+-  OP_NOTUPTO,        /* From 0 to n matches */
+-  OP_NOTMINUPTO,
+-  OP_NOTEXACT,       /* Exactly n matches */
+-
+-  OP_TYPESTAR,       /* The maximizing and minimizing versions of */
+-  OP_TYPEMINSTAR,    /* all these opcodes must come in pairs, with */
+-  OP_TYPEPLUS,       /* the minimizing one second. These codes must */
+-  OP_TYPEMINPLUS,    /* be in exactly the same order as those above. */
+-  OP_TYPEQUERY,      /* This set applies to character types such as \d */
+-  OP_TYPEMINQUERY,
+-  OP_TYPEUPTO,       /* From 0 to n matches */
+-  OP_TYPEMINUPTO,
+-  OP_TYPEEXACT,      /* Exactly n matches */
+-
+-  OP_CRSTAR,         /* The maximizing and minimizing versions of */
+-  OP_CRMINSTAR,      /* all these opcodes must come in pairs, with */
+-  OP_CRPLUS,         /* the minimizing one second. These codes must */
+-  OP_CRMINPLUS,      /* be in exactly the same order as those above. */
+-  OP_CRQUERY,        /* These are for character classes and back refs */
+-  OP_CRMINQUERY,
+-  OP_CRRANGE,        /* These are different to the three seta above. */
+-  OP_CRMINRANGE,
+-
+-  OP_CLASS,          /* Match a character class */
+-  OP_REF,            /* Match a back reference */
+-  OP_RECURSE,        /* Match this pattern recursively */
+-
+-  OP_ALT,            /* Start of alternation */
+-  OP_KET,            /* End of group that doesn't have an unbounded repeat */
+-  OP_KETRMAX,        /* These two must remain together and in this */
+-  OP_KETRMIN,        /* order. They are for groups the repeat for ever. */
+-
+-  /* The assertions must come before ONCE and COND */
+-
+-  OP_ASSERT,         /* Positive lookahead */
+-  OP_ASSERT_NOT,     /* Negative lookahead */
+-  OP_ASSERTBACK,     /* Positive lookbehind */
+-  OP_ASSERTBACK_NOT, /* Negative lookbehind */
+-  OP_REVERSE,        /* Move pointer back - used in lookbehind assertions */
+-
+-  /* ONCE and COND must come after the assertions, with ONCE first, as there's
+-  a test for >= ONCE for a subpattern that isn't an assertion. */
+-
+-  OP_ONCE,           /* Once matched, don't back up into the subpattern */
+-  OP_COND,           /* Conditional group */
+-  OP_CREF,           /* Used to hold an extraction string number (cond ref) */
+-
+-  OP_BRAZERO,        /* These two must remain together and in this */
+-  OP_BRAMINZERO,     /* order. */
+-
+-  OP_BRANUMBER,      /* Used for extracting brackets whose number is greater
+-                        than can fit into an opcode. */
+-
+-  OP_BRA             /* This and greater values are used for brackets that
+-                        extract substrings up to a basic limit. After that,
+-                        use is made of OP_BRANUMBER. */
+-};
+-
+-/* The highest extraction number before we have to start using additional
+-bytes. (Originally PCRE didn't have support for extraction counts highter than
+-this number.) The value is limited by the number of opcodes left after OP_BRA,
+-i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional
+-opcodes. */
+-
+-#define EXTRACT_BASIC_MAX  150
+-
+-/* The texts of compile-time error messages are defined as macros here so that
+-they can be accessed by the POSIX wrapper and converted into error codes.  Yes,
+-I could have used error codes in the first place, but didn't feel like changing
+-just to accommodate the POSIX wrapper. */
+-
+-#define ERR1  "\\ at end of pattern"
+-#define ERR2  "\\c at end of pattern"
+-#define ERR3  "unrecognized character follows \\"
+-#define ERR4  "numbers out of order in {} quantifier"
+-#define ERR5  "number too big in {} quantifier"
+-#define ERR6  "missing terminating ] for character class"
+-#define ERR7  "invalid escape sequence in character class"
+-#define ERR8  "range out of order in character class"
+-#define ERR9  "nothing to repeat"
+-#define ERR10 "operand of unlimited repeat could match the empty string"
+-#define ERR11 "internal error: unexpected repeat"
+-#define ERR12 "unrecognized character after (?"
+-#define ERR13 "unused error"
+-#define ERR14 "missing )"
+-#define ERR15 "back reference to non-existent subpattern"
+-#define ERR16 "erroffset passed as NULL"
+-#define ERR17 "unknown option bit(s) set"
+-#define ERR18 "missing ) after comment"
+-#define ERR19 "parentheses nested too deeply"
+-#define ERR20 "regular expression too large"
+-#define ERR21 "failed to get memory"
+-#define ERR22 "unmatched parentheses"
+-#define ERR23 "internal error: code overflow"
+-#define ERR24 "unrecognized character after (?<"
+-#define ERR25 "lookbehind assertion is not fixed length"
+-#define ERR26 "malformed number after (?("
+-#define ERR27 "conditional group contains more than two branches"
+-#define ERR28 "assertion expected after (?("
+-#define ERR29 "(?p must be followed by )"
+-#define ERR30 "unknown POSIX class name"
+-#define ERR31 "POSIX collating elements are not supported"
+-#define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support"
+-#define ERR33 "characters with values > 255 are not yet supported in classes"
+-#define ERR34 "character value in \\x{...} sequence is too large"
+-#define ERR35 "invalid condition (?(0)"
+-
+-/* All character handling must be done as unsigned characters. Otherwise there
+-are problems with top-bit-set characters and functions such as isspace().
+-However, we leave the interface to the outside world as char *, because that
+-should make things easier for callers. We define a short type for unsigned char
+-to save lots of typing. I tried "uchar", but it causes problems on Digital
+-Unix, where it is defined in sys/types, so use "uschar" instead. */
+-
+-typedef unsigned char uschar;
+-
+-/* The real format of the start of the pcre block; the actual code vector
+-runs on as long as necessary after the end. */
+-
+-typedef struct real_pcre {
+-  unsigned long int magic_number;
+-  size_t size;
+-  const unsigned char *tables;
+-  unsigned long int options;
+-  unsigned short int top_bracket;
+-  unsigned short int top_backref;
+-  uschar first_char;
+-  uschar req_char;
+-  uschar code[1];
+-} real_pcre;
+-
+-/* The real format of the extra block returned by pcre_study(). */
+-
+-typedef struct real_pcre_extra {
+-  uschar options;
+-  uschar start_bits[32];
+-} real_pcre_extra;
+-
+-
+-/* Structure for passing "static" information around between the functions
+-doing the compiling, so that they are thread-safe. */
+-
+-typedef struct compile_data {
+-  const uschar *lcc;            /* Points to lower casing table */
+-  const uschar *fcc;            /* Points to case-flipping table */
+-  const uschar *cbits;          /* Points to character type table */
+-  const uschar *ctypes;         /* Points to table of type maps */
+-} compile_data;
+-
+-/* Structure for passing "static" information around between the functions
+-doing the matching, so that they are thread-safe. */
+-
+-typedef struct match_data {
+-  int    errorcode;             /* As it says */
+-  int   *offset_vector;         /* Offset vector */
+-  int    offset_end;            /* One past the end */
+-  int    offset_max;            /* The maximum usable for return data */
+-  const uschar *lcc;            /* Points to lower casing table */
+-  const uschar *ctypes;         /* Points to table of type maps */
+-  BOOL   offset_overflow;       /* Set if too many extractions */
+-  BOOL   notbol;                /* NOTBOL flag */
+-  BOOL   noteol;                /* NOTEOL flag */
+-  BOOL   utf8;                  /* UTF8 flag */
+-  BOOL   endonly;               /* Dollar not before final \n */
+-  BOOL   notempty;              /* Empty string match not wanted */
+-  const uschar *start_pattern;  /* For use when recursing */
+-  const uschar *start_subject;  /* Start of the subject string */
+-  const uschar *end_subject;    /* End of the subject string */
+-  const uschar *start_match;    /* Start of this match attempt */
+-  const uschar *end_match_ptr;  /* Subject position at end match */
+-  int    end_offset_top;        /* Highwater mark at end of match */
+-} match_data;
+-
+-/* Bit definitions for entries in the pcre_ctypes table. */
+-
+-#define ctype_space   0x01
+-#define ctype_letter  0x02
+-#define ctype_digit   0x04
+-#define ctype_xdigit  0x08
+-#define ctype_word    0x10   /* alphameric or '_' */
+-#define ctype_meta    0x80   /* regexp meta char or zero (end pattern) */
+-
+-/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
+-of bits for a class map. Some classes are built by combining these tables. */
+-
+-#define cbit_space     0      /* [:space:] or \s */
+-#define cbit_xdigit   32      /* [:xdigit:] */
+-#define cbit_digit    64      /* [:digit:] or \d */
+-#define cbit_upper    96      /* [:upper:] */
+-#define cbit_lower   128      /* [:lower:] */
+-#define cbit_word    160      /* [:word:] or \w */
+-#define cbit_graph   192      /* [:graph:] */
+-#define cbit_print   224      /* [:print:] */
+-#define cbit_punct   256      /* [:punct:] */
+-#define cbit_cntrl   288      /* [:cntrl:] */
+-#define cbit_length  320      /* Length of the cbits table */
+-
+-/* Offsets of the various tables from the base tables pointer, and
+-total length. */
+-
+-#define lcc_offset      0
+-#define fcc_offset    256
+-#define cbits_offset  512
+-#define ctypes_offset (cbits_offset + cbit_length)
+-#define tables_length (ctypes_offset + 256)
+-
+-/* End of internal.h */
+
+=== removed file 'parser/pcre/pcre.c'
+--- parser/pcre/pcre.c 2006-04-11 21:52:54 +0000
++++ parser/pcre/pcre.c 1970-01-01 00:00:00 +0000
+@@ -1,5187 +0,0 @@
+-/*************************************************
+-*      Perl-Compatible Regular Expressions       *
+-*************************************************/
+-
+-/*
+-This is a library of functions to support regular expressions whose syntax
+-and semantics are as close as possible to those of the Perl 5 language. See
+-the file Tech.Notes for some information on the internals.
+-
+-Written by: Philip Hazel <ph10@cam.ac.uk>
+-
+-           Copyright (c) 1997-2001 University of Cambridge
+-
+------------------------------------------------------------------------------
+-Permission is granted to anyone to use this software for any purpose on any
+-computer system, and to redistribute it freely, subject to the following
+-restrictions:
+-
+-1. This software 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.
+-
+-2. The origin of this software must not be misrepresented, either by
+-   explicit claim or by omission.
+-
+-3. Altered versions must be plainly marked as such, and must not be
+-   misrepresented as being the original software.
+-
+-4. If PCRE is embedded in any software that is released under the GNU
+-   General Purpose Licence (GPL), then the terms of that licence shall
+-   supersede any condition above with which it is incompatible.
+------------------------------------------------------------------------------
+-*/
+-
+-
+-/* Define DEBUG to get debugging output on stdout. */
+-
+-/* #define DEBUG */
+-
+-/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
+-inline, and there are *still* stupid compilers about that don't like indented
+-pre-processor statements. I suppose it's only been 10 years... */
+-
+-#ifdef DEBUG
+-#define DPRINTF(p) printf p
+-#else
+-#define DPRINTF(p) /*nothing*/
+-#endif
+-
+-/* Include the internals header, which itself includes Standard C headers plus
+-the external pcre header. */
+-
+-#include "internal.h"
+-
+-
+-/* Allow compilation as C++ source code, should anybody want to do that. */
+-
+-#ifdef __cplusplus
+-#define class pcre_class
+-#endif
+-
+-
+-/* Maximum number of items on the nested bracket stacks at compile time. This
+-applies to the nesting of all kinds of parentheses. It does not limit
+-un-nested, non-capturing parentheses. This number can be made bigger if
+-necessary - it is used to dimension one int and one unsigned char vector at
+-compile time. */
+-
+-#define BRASTACK_SIZE 200
+-
+-
+-/* The number of bytes in a literal character string above which we can't add
+-any more is different when UTF-8 characters may be encountered. */
+-
+-#ifdef SUPPORT_UTF8
+-#define MAXLIT 250
+-#else
+-#define MAXLIT 255
+-#endif
+-
+-
+-/* Min and max values for the common repeats; for the maxima, 0 => infinity */
+-
+-static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
+-static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
+-
+-/* Text forms of OP_ values and things, for debugging (not all used) */
+-
+-#ifdef DEBUG
+-static const char *OP_names[] = {
+-  "End", "\\A", "\\B", "\\b", "\\D", "\\d",
+-  "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z",
+-  "Opt", "^", "$", "Any", "chars", "not",
+-  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",
+-  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",
+-  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",
+-  "*", "*?", "+", "+?", "?", "??", "{", "{",
+-  "class", "Ref", "Recurse",
+-  "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not",
+-  "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref",
+-  "Brazero", "Braminzero", "Branumber", "Bra"
+-};
+-#endif
+-
+-/* Table for handling escaped characters in the range '0'-'z'. Positive returns
+-are simple data values; negative values are for special things like \d and so
+-on. Zero means further processing is needed (for things like \x), or the escape
+-is invalid. */
+-
+-static const short int escapes[] = {
+-    0,      0,      0,      0,      0,      0,      0,      0,   /* 0 - 7 */
+-    0,      0,    ':',    ';',    '<',    '=',    '>',    '?',   /* 8 - ? */
+-  '@', -ESC_A, -ESC_B,      0, -ESC_D,      0,      0,      0,   /* @ - G */
+-    0,      0,      0,      0,      0,      0,      0,      0,   /* H - O */
+-    0,      0,      0, -ESC_S,      0,      0,      0, -ESC_W,   /* P - W */
+-    0,      0, -ESC_Z,    '[',   '\\',    ']',    '^',    '_',   /* X - _ */
+-  '`',      7, -ESC_b,      0, -ESC_d,  ESC_E,  ESC_F,      0,   /* ` - g */
+-    0,      0,      0,      0,      0,      0,  ESC_N,      0,   /* h - o */
+-    0,      0,  ESC_R, -ESC_s,  ESC_T,      0,      0, -ESC_w,   /* p - w */
+-    0,      0, -ESC_z                                            /* x - z */
+-};
+-
+-/* Tables of names of POSIX character classes and their lengths. The list is
+-terminated by a zero length entry. The first three must be alpha, upper, lower,
+-as this is assumed for handling case independence. */
+-
+-static const char *posix_names[] = {
+-  "alpha", "lower", "upper",
+-  "alnum", "ascii", "cntrl", "digit", "graph",
+-  "print", "punct", "space", "word",  "xdigit" };
+-
+-static const uschar posix_name_lengths[] = {
+-  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
+-
+-/* Table of class bit maps for each POSIX class; up to three may be combined
+-to form the class. */
+-
+-static const int posix_class_maps[] = {
+-  cbit_lower, cbit_upper, -1,             /* alpha */
+-  cbit_lower, -1,         -1,             /* lower */
+-  cbit_upper, -1,         -1,             /* upper */
+-  cbit_digit, cbit_lower, cbit_upper,     /* alnum */
+-  cbit_print, cbit_cntrl, -1,             /* ascii */
+-  cbit_cntrl, -1,         -1,             /* cntrl */
+-  cbit_digit, -1,         -1,             /* digit */
+-  cbit_graph, -1,         -1,             /* graph */
+-  cbit_print, -1,         -1,             /* print */
+-  cbit_punct, -1,         -1,             /* punct */
+-  cbit_space, -1,         -1,             /* space */
+-  cbit_word,  -1,         -1,             /* word */
+-  cbit_xdigit,-1,         -1              /* xdigit */
+-};
+-
+-
+-/* Definition to allow mutual recursion */
+-
+-static BOOL
+-  compile_regex(int, int, int *, uschar **, const uschar **, const char **,
+-    BOOL, int, int *, int *, compile_data *);
+-
+-/* Structure for building a chain of data that actually lives on the
+-stack, for holding the values of the subject pointer at the start of each
+-subpattern, so as to detect when an empty string has been matched by a
+-subpattern - to break infinite loops. */
+-
+-typedef struct eptrblock {
+-  struct eptrblock *prev;
+-  const uschar *saved_eptr;
+-} eptrblock;
+-
+-/* Flag bits for the match() function */
+-
+-#define match_condassert   0x01    /* Called to check a condition assertion */
+-#define match_isgroup      0x02    /* Set if start of bracketed group */
+-
+-
+-
+-/*************************************************
+-*               Global variables                 *
+-*************************************************/
+-
+-/* PCRE is thread-clean and doesn't use any global variables in the normal
+-sense. However, it calls memory allocation and free functions via the two
+-indirections below, which are can be changed by the caller, but are shared
+-between all threads. */
+-
+-void *(*pcre_malloc)(size_t) = malloc;
+-void  (*pcre_free)(void *) = free;
+-
+-
+-
+-/*************************************************
+-*    Macros and tables for character handling    *
+-*************************************************/
+-
+-/* When UTF-8 encoding is being used, a character is no longer just a single
+-byte. The macros for character handling generate simple sequences when used in
+-byte-mode, and more complicated ones for UTF-8 characters. */
+-
+-#ifndef SUPPORT_UTF8
+-#define GETCHARINC(c, eptr) c = *eptr++;
+-#define GETCHARLEN(c, eptr, len) c = *eptr;
+-#define BACKCHAR(eptr)
+-
+-#else   /* SUPPORT_UTF8 */
+-
+-/* Get the next UTF-8 character, advancing the pointer */
+-
+-#define GETCHARINC(c, eptr) \
+-  c = *eptr++; \
+-  if (md->utf8 && (c & 0xc0) == 0xc0) \
+-    { \
+-    int a = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+-    int s = 6*a; \
+-    c = (c & utf8_table3[a]) << s; \
+-    while (a-- > 0) \
+-      { \
+-      s -= 6; \
+-      c |= (*eptr++ & 0x3f) << s; \
+-      } \
+-    }
+-
+-/* Get the next UTF-8 character, not advancing the pointer, setting length */
+-
+-#define GETCHARLEN(c, eptr, len) \
+-  c = *eptr; \
+-  len = 1; \
+-  if (md->utf8 && (c & 0xc0) == 0xc0) \
+-    { \
+-    int i; \
+-    int a = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+-    int s = 6*a; \
+-    c = (c & utf8_table3[a]) << s; \
+-    for (i = 1; i <= a; i++) \
+-      { \
+-      s -= 6; \
+-      c |= (eptr[i] & 0x3f) << s; \
+-      } \
+-    len += a; \
+-    }
+-
+-/* If the pointer is not at the start of a character, move it back until
+-it is. */
+-
+-#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--;
+-
+-#endif
+-
+-
+-
+-/*************************************************
+-*             Default character tables           *
+-*************************************************/
+-
+-/* A default set of character tables is included in the PCRE binary. Its source
+-is built by the maketables auxiliary program, which uses the default C ctypes
+-functions, and put in the file chartables.c. These tables are used by PCRE
+-whenever the caller of pcre_compile() does not provide an alternate set of
+-tables. */
+-
+-#include "chartables.c"
+-
+-
+-
+-#ifdef SUPPORT_UTF8
+-/*************************************************
+-*           Tables for UTF-8 support             *
+-*************************************************/
+-
+-/* These are the breakpoints for different numbers of bytes in a UTF-8
+-character. */
+-
+-static int utf8_table1[] = { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
+-
+-/* These are the indicator bits and the mask for the data bits to set in the
+-first byte of a character, indexed by the number of additional bytes. */
+-
+-static int utf8_table2[] = { 0,    0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+-static int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+-
+-/* Table of the number of extra characters, indexed by the first character
+-masked with 0x3f. The highest number for a valid UTF-8 character is in fact
+-0x3d. */
+-
+-static uschar utf8_table4[] = {
+-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+-  3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+-
+-
+-/*************************************************
+-*       Convert character value to UTF-8         *
+-*************************************************/
+-
+-/* This function takes an integer value in the range 0 - 0x7fffffff
+-and encodes it as a UTF-8 character in 0 to 6 bytes.
+-
+-Arguments:
+-  cvalue     the character value
+-  buffer     pointer to buffer for result - at least 6 bytes long
+-
+-Returns:     number of characters placed in the buffer
+-*/
+-
+-static int
+-ord2utf8(int cvalue, uschar *buffer)
+-{
+-register int i, j;
+-for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+-  if (cvalue <= utf8_table1[i]) break;
+-buffer += i;
+-for (j = i; j > 0; j--)
+- {
+- *buffer-- = 0x80 | (cvalue & 0x3f);
+- cvalue >>= 6;
+- }
+-*buffer = utf8_table2[i] | cvalue;
+-return i + 1;
+-}
+-#endif
+-
+-
+-
+-/*************************************************
+-*          Return version string                 *
+-*************************************************/
+-
+-#define STRING(a)  # a
+-#define XSTRING(s) STRING(s)
+-
+-const char *
+-pcre_version(void)
+-{
+-return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE);
+-}
+-
+-
+-
+-
+-/*************************************************
+-* (Obsolete) Return info about compiled pattern  *
+-*************************************************/
+-
+-/* This is the original "info" function. It picks potentially useful data out
+-of the private structure, but its interface was too rigid. It remains for
+-backwards compatibility. The public options are passed back in an int - though
+-the re->options field has been expanded to a long int, all the public options
+-at the low end of it, and so even on 16-bit systems this will still be OK.
+-Therefore, I haven't changed the API for pcre_info().
+-
+-Arguments:
+-  external_re   points to compiled code
+-  optptr        where to pass back the options
+-  first_char    where to pass back the first character,
+-                or -1 if multiline and all branches start ^,
+-                or -2 otherwise
+-
+-Returns:        number of capturing subpatterns
+-                or negative values on error
+-*/
+-
+-int
+-pcre_info(const pcre *external_re, int *optptr, int *first_char)
+-{
+-const real_pcre *re = (const real_pcre *)external_re;
+-if (re == NULL) return PCRE_ERROR_NULL;
+-if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+-if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS);
+-if (first_char != NULL)
+-  *first_char = ((re->options & PCRE_FIRSTSET) != 0)? re->first_char :
+-     ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+-return re->top_bracket;
+-}
+-
+-
+-
+-/*************************************************
+-*        Return info about compiled pattern      *
+-*************************************************/
+-
+-/* This is a newer "info" function which has an extensible interface so
+-that additional items can be added compatibly.
+-
+-Arguments:
+-  external_re      points to compiled code
+-  external_study   points to study data, or NULL
+-  what             what information is required
+-  where            where to put the information
+-
+-Returns:           0 if data returned, negative on error
+-*/
+-
+-int
+-pcre_fullinfo(const pcre *external_re, const pcre_extra *study_data, int what,
+-  void *where)
+-{
+-const real_pcre *re = (const real_pcre *)external_re;
+-const real_pcre_extra *study = (const real_pcre_extra *)study_data;
+-
+-if (re == NULL || where == NULL) return PCRE_ERROR_NULL;
+-if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+-
+-switch (what)
+-  {
+-  case PCRE_INFO_OPTIONS:
+-  *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS;
+-  break;
+-
+-  case PCRE_INFO_SIZE:
+-  *((size_t *)where) = re->size;
+-  break;
+-
+-  case PCRE_INFO_CAPTURECOUNT:
+-  *((int *)where) = re->top_bracket;
+-  break;
+-
+-  case PCRE_INFO_BACKREFMAX:
+-  *((int *)where) = re->top_backref;
+-  break;
+-
+-  case PCRE_INFO_FIRSTCHAR:
+-  *((int *)where) =
+-    ((re->options & PCRE_FIRSTSET) != 0)? re->first_char :
+-    ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+-  break;
+-
+-  case PCRE_INFO_FIRSTTABLE:
+-  *((const uschar **)where) =
+-    (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)?
+-      study->start_bits : NULL;
+-  break;
+-
+-  case PCRE_INFO_LASTLITERAL:
+-  *((int *)where) =
+-    ((re->options & PCRE_REQCHSET) != 0)? re->req_char : -1;
+-  break;
+-
+-  default: return PCRE_ERROR_BADOPTION;
+-  }
+-
+-return 0;
+-}
+-
+-
+-
+-#ifdef DEBUG
+-/*************************************************
+-*        Debugging function to print chars       *
+-*************************************************/
+-
+-/* Print a sequence of chars in printable format, stopping at the end of the
+-subject if the requested.
+-
+-Arguments:
+-  p           points to characters
+-  length      number to print
+-  is_subject  TRUE if printing from within md->start_subject
+-  md          pointer to matching data block, if is_subject is TRUE
+-
+-Returns:     nothing
+-*/
+-
+-static void
+-pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
+-{
+-int c;
+-if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
+-while (length-- > 0)
+-  if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c);
+-}
+-#endif
+-
+-
+-
+-
+-/*************************************************
+-*            Handle escapes                      *
+-*************************************************/
+-
+-/* This function is called when a \ has been encountered. It either returns a
+-positive value for a simple escape such as \n, or a negative value which
+-encodes one of the more complicated things such as \d. When UTF-8 is enabled,
+-a positive value greater than 255 may be returned. On entry, ptr is pointing at
+-the \. On exit, it is on the final character of the escape sequence.
+-
+-Arguments:
+-  ptrptr     points to the pattern position pointer
+-  errorptr   points to the pointer to the error message
+-  bracount   number of previous extracting brackets
+-  options    the options bits
+-  isclass    TRUE if inside a character class
+-  cd         pointer to char tables block
+-
+-Returns:     zero or positive => a data character
+-             negative => a special escape sequence
+-             on error, errorptr is set
+-*/
+-
+-static int
+-check_escape(const uschar **ptrptr, const char **errorptr, int bracount,
+-  int options, BOOL isclass, compile_data *cd)
+-{
+-const uschar *ptr = *ptrptr;
+-int c, i;
+-
+-/* If backslash is at the end of the pattern, it's an error. */
+-
+-c = *(++ptr);
+-if (c == 0) *errorptr = ERR1;
+-
+-/* Digits or letters may have special meaning; all others are literals. */
+-
+-else if (c < '0' || c > 'z') {}
+-
+-/* Do an initial lookup in a table. A non-zero result is something that can be
+-returned immediately. Otherwise further processing may be required. */
+-
+-else if ((i = escapes[c - '0']) != 0) c = i;
+-
+-/* Escapes that need further processing, or are illegal. */
+-
+-else
+-  {
+-  const uschar *oldptr;
+-  switch (c)
+-    {
+-    /* The handling of escape sequences consisting of a string of digits
+-    starting with one that is not zero is not straightforward. By experiment,
+-    the way Perl works seems to be as follows:
+-
+-    Outside a character class, the digits are read as a decimal number. If the
+-    number is less than 10, or if there are that many previous extracting
+-    left brackets, then it is a back reference. Otherwise, up to three octal
+-    digits are read to form an escaped byte. Thus \123 is likely to be octal
+-    123 (cf \0123, which is octal 012 followed by the literal 3). If the octal
+-    value is greater than 377, the least significant 8 bits are taken. Inside a
+-    character class, \ followed by a digit is always an octal number. */
+-
+-    case '1': case '2': case '3': case '4': case '5':
+-    case '6': case '7': case '8': case '9':
+-
+-    if (!isclass)
+-      {
+-      oldptr = ptr;
+-      c -= '0';
+-      while ((cd->ctypes[ptr[1]] & ctype_digit) != 0)
+-        c = c * 10 + *(++ptr) - '0';
+-      if (c < 10 || c <= bracount)
+-        {
+-        c = -(ESC_REF + c);
+-        break;
+-        }
+-      ptr = oldptr;      /* Put the pointer back and fall through */
+-      }
+-
+-    /* Handle an octal number following \. If the first digit is 8 or 9, Perl
+-    generates a binary zero byte and treats the digit as a following literal.
+-    Thus we have to pull back the pointer by one. */
+-
+-    if ((c = *ptr) >= '8')
+-      {
+-      ptr--;
+-      c = 0;
+-      break;
+-      }
+-
+-    /* \0 always starts an octal number, but we may drop through to here with a
+-    larger first octal digit. */
+-
+-    case '0':
+-    c -= '0';
+-    while(i++ < 2 && (cd->ctypes[ptr[1]] & ctype_digit) != 0 &&
+-      ptr[1] != '8' && ptr[1] != '9')
+-        c = c * 8 + *(++ptr) - '0';
+-    c &= 255;     /* Take least significant 8 bits */
+-    break;
+-
+-    /* \x is complicated when UTF-8 is enabled. \x{ddd} is a character number
+-    which can be greater than 0xff, but only if the ddd are hex digits. */
+-
+-    case 'x':
+-#ifdef SUPPORT_UTF8
+-    if (ptr[1] == '{' && (options & PCRE_UTF8) != 0)
+-      {
+-      const uschar *pt = ptr + 2;
+-      register int count = 0;
+-      c = 0;
+-      while ((cd->ctypes[*pt] & ctype_xdigit) != 0)
+-        {
+-        count++;
+-        c = c * 16 + cd->lcc[*pt] -
+-          (((cd->ctypes[*pt] & ctype_digit) != 0)? '0' : 'W');
+-        pt++;
+-        }
+-      if (*pt == '}')
+-        {
+-        if (c < 0 || count > 8) *errorptr = ERR34;
+-        ptr = pt;
+-        break;
+-        }
+-      /* If the sequence of hex digits does not end with '}', then we don't
+-      recognize this construct; fall through to the normal \x handling. */
+-      }
+-#endif
+-
+-    /* Read just a single hex char */
+-
+-    c = 0;
+-    while (i++ < 2 && (cd->ctypes[ptr[1]] & ctype_xdigit) != 0)
+-      {
+-      ptr++;
+-      c = c * 16 + cd->lcc[*ptr] -
+-        (((cd->ctypes[*ptr] & ctype_digit) != 0)? '0' : 'W');
+-      }
+-    break;
+-
+-    /* Other special escapes not starting with a digit are straightforward */
+-
+-    case 'c':
+-    c = *(++ptr);
+-    if (c == 0)
+-      {
+-      *errorptr = ERR2;
+-      return 0;
+-      }
+-
+-    /* A letter is upper-cased; then the 0x40 bit is flipped */
+-
+-    if (c >= 'a' && c <= 'z') c = cd->fcc[c];
+-    c ^= 0x40;
+-    break;
+-
+-    /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any
+-    other alphameric following \ is an error if PCRE_EXTRA was set; otherwise,
+-    for Perl compatibility, it is a literal. This code looks a bit odd, but
+-    there used to be some cases other than the default, and there may be again
+-    in future, so I haven't "optimized" it. */
+-
+-    default:
+-    if ((options & PCRE_EXTRA) != 0) switch(c)
+-      {
+-      default:
+-      *errorptr = ERR3;
+-      break;
+-      }
+-    break;
+-    }
+-  }
+-
+-*ptrptr = ptr;
+-return c;
+-}
+-
+-
+-
+-/*************************************************
+-*            Check for counted repeat            *
+-*************************************************/
+-
+-/* This function is called when a '{' is encountered in a place where it might
+-start a quantifier. It looks ahead to see if it really is a quantifier or not.
+-It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd}
+-where the ddds are digits.
+-
+-Arguments:
+-  p         pointer to the first char after '{'
+-  cd        pointer to char tables block
+-
+-Returns:    TRUE or FALSE
+-*/
+-
+-static BOOL
+-is_counted_repeat(const uschar *p, compile_data *cd)
+-{
+-if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE;
+-while ((cd->ctypes[*p] & ctype_digit) != 0) p++;
+-if (*p == '}') return TRUE;
+-
+-if (*p++ != ',') return FALSE;
+-if (*p == '}') return TRUE;
+-
+-if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE;
+-while ((cd->ctypes[*p] & ctype_digit) != 0) p++;
+-return (*p == '}');
+-}
+-
+-
+-
+-/*************************************************
+-*         Read repeat counts                     *
+-*************************************************/
+-
+-/* Read an item of the form {n,m} and return the values. This is called only
+-after is_counted_repeat() has confirmed that a repeat-count quantifier exists,
+-so the syntax is guaranteed to be correct, but we need to check the values.
+-
+-Arguments:
+-  p          pointer to first char after '{'
+-  minp       pointer to int for min
+-  maxp       pointer to int for max
+-             returned as -1 if no max
+-  errorptr   points to pointer to error message
+-  cd         pointer to character tables clock
+-
+-Returns:     pointer to '}' on success;
+-             current ptr on error, with errorptr set
+-*/
+-
+-static const uschar *
+-read_repeat_counts(const uschar *p, int *minp, int *maxp,
+-  const char **errorptr, compile_data *cd)
+-{
+-int min = 0;
+-int max = -1;
+-
+-/* Read the minimum value and do a paranoid check: a negative value indicates
+-an integer overflow. */
+-
+-while ((cd->ctypes[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0';
+-
+-if (min < 0 || min > 65535) {
+-  *errorptr = ERR5;
+-  return p;
+-}
+-/* Read the maximum value if there is one, and again do a paranoid on its size.
+-Also, max must not be less than min. */
+-
+-if (*p == '}') max = min; else
+-  {
+-  if (*(++p) != '}')
+-    {
+-    max = 0;
+-    while((cd->ctypes[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0';
+-    if (max < 0 || max > 65535)
+-      {
+-      *errorptr = ERR5;
+-      return p;
+-      }
+-    if (max < min)
+-      {
+-      *errorptr = ERR4;
+-      return p;
+-      }
+-    }
+-  }
+-
+-/* Fill in the required variables, and pass back the pointer to the terminating
+-'}'. */
+-
+-*minp = min;
+-*maxp = max;
+-return p;
+-}
+-
+-
+-
+-/*************************************************
+-*        Find the fixed length of a pattern      *
+-*************************************************/
+-
+-/* Scan a pattern and compute the fixed length of subject that will match it,
+-if the length is fixed. This is needed for dealing with backward assertions.
+-
+-Arguments:
+-  code     points to the start of the pattern (the bracket)
+-  options  the compiling options
+-
+-Returns:   the fixed length, or -1 if there is no fixed length
+-*/
+-
+-static int
+-find_fixedlength(uschar *code, int options)
+-{
+-int length = -1;
+-
+-register int branchlength = 0;
+-register uschar *cc = code + 3;
+-
+-/* Scan along the opcodes for this branch. If we get to the end of the
+-branch, check the length against that of the other branches. */
+-
+-for (;;)
+-  {
+-  int d;
+-  register int op = *cc;
+-  if (op >= OP_BRA) op = OP_BRA;
+-
+-  switch (op)
+-    {
+-    case OP_BRA:
+-    case OP_ONCE:
+-    case OP_COND:
+-    d = find_fixedlength(cc, options);
+-    if (d < 0) return -1;
+-    branchlength += d;
+-    do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT);
+-    cc += 3;
+-    break;
+-
+-    /* Reached end of a branch; if it's a ket it is the end of a nested
+-    call. If it's ALT it is an alternation in a nested call. If it is
+-    END it's the end of the outer call. All can be handled by the same code. */
+-
+-    case OP_ALT:
+-    case OP_KET:
+-    case OP_KETRMAX:
+-    case OP_KETRMIN:
+-    case OP_END:
+-    if (length < 0) length = branchlength;
+-      else if (length != branchlength) return -1;
+-    if (*cc != OP_ALT) return length;
+-    cc += 3;
+-    branchlength = 0;
+-    break;
+-
+-    /* Skip over assertive subpatterns */
+-
+-    case OP_ASSERT:
+-    case OP_ASSERT_NOT:
+-    case OP_ASSERTBACK:
+-    case OP_ASSERTBACK_NOT:
+-    do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT);
+-    cc += 3;
+-    break;
+-
+-    /* Skip over things that don't match chars */
+-
+-    case OP_REVERSE:
+-    case OP_BRANUMBER:
+-    case OP_CREF:
+-    cc++;
+-    /* Fall through */
+-
+-    case OP_OPT:
+-    cc++;
+-    /* Fall through */
+-
+-    case OP_SOD:
+-    case OP_EOD:
+-    case OP_EODN:
+-    case OP_CIRC:
+-    case OP_DOLL:
+-    case OP_NOT_WORD_BOUNDARY:
+-    case OP_WORD_BOUNDARY:
+-    cc++;
+-    break;
+-
+-    /* Handle char strings. In UTF-8 mode we must count characters, not bytes.
+-    This requires a scan of the string, unfortunately. We assume valid UTF-8
+-    strings, so all we do is reduce the length by one for byte whose bits are
+-    10xxxxxx. */
+-
+-    case OP_CHARS:
+-    branchlength += *(++cc);
+-#ifdef SUPPORT_UTF8
+-    for (d = 1; d <= *cc; d++)
+-      if ((cc[d] & 0xc0) == 0x80) branchlength--;
+-#endif
+-    cc += *cc + 1;
+-    break;
+-
+-    /* Handle exact repetitions */
+-
+-    case OP_EXACT:
+-    case OP_TYPEEXACT:
+-    branchlength += (cc[1] << 8) + cc[2];
+-    cc += 4;
+-    break;
+-
+-    /* Handle single-char matchers */
+-
+-    case OP_NOT_DIGIT:
+-    case OP_DIGIT:
+-    case OP_NOT_WHITESPACE:
+-    case OP_WHITESPACE:
+-    case OP_NOT_WORDCHAR:
+-    case OP_WORDCHAR:
+-    case OP_ANY:
+-    branchlength++;
+-    cc++;
+-    break;
+-
+-
+-    /* Check a class for variable quantification */
+-
+-    case OP_CLASS:
+-    cc += 33;
+-
+-    switch (*cc)
+-      {
+-      case OP_CRSTAR:
+-      case OP_CRMINSTAR:
+-      case OP_CRQUERY:
+-      case OP_CRMINQUERY:
+-      return -1;
+-
+-      case OP_CRRANGE:
+-      case OP_CRMINRANGE:
+-      if ((cc[1] << 8) + cc[2] != (cc[3] << 8) + cc[4]) return -1;
+-      branchlength += (cc[1] << 8) + cc[2];
+-      cc += 5;
+-      break;
+-
+-      default:
+-      branchlength++;
+-      }
+-    break;
+-
+-    /* Anything else is variable length */
+-
+-    default:
+-    return -1;
+-    }
+-  }
+-/* Control never gets here */
+-}
+-
+-
+-
+-
+-/*************************************************
+-*           Check for POSIX class syntax         *
+-*************************************************/
+-
+-/* This function is called when the sequence "[:" or "[." or "[=" is
+-encountered in a character class. It checks whether this is followed by an
+-optional ^ and then a sequence of letters, terminated by a matching ":]" or
+-".]" or "=]".
+-
+-Argument:
+-  ptr      pointer to the initial [
+-  endptr   where to return the end pointer
+-  cd       pointer to compile data
+-
+-Returns:   TRUE or FALSE
+-*/
+-
+-static BOOL
+-check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd)
+-{
+-int terminator;          /* Don't combine these lines; the Solaris cc */
+-terminator = *(++ptr);   /* compiler warns about "non-constant" initializer. */
+-if (*(++ptr) == '^') ptr++;
+-while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++;
+-if (*ptr == terminator && ptr[1] == ']')
+-  {
+-  *endptr = ptr;
+-  return TRUE;
+-  }
+-return FALSE;
+-}
+-
+-
+-
+-
+-/*************************************************
+-*          Check POSIX class name                *
+-*************************************************/
+-
+-/* This function is called to check the name given in a POSIX-style class entry
+-such as [:alnum:].
+-
+-Arguments:
+-  ptr        points to the first letter
+-  len        the length of the name
+-
+-Returns:     a value representing the name, or -1 if unknown
+-*/
+-
+-static int
+-check_posix_name(const uschar *ptr, int len)
+-{
+-register int yield = 0;
+-while (posix_name_lengths[yield] != 0)
+-  {
+-  if (len == posix_name_lengths[yield] &&
+-    strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield;
+-  yield++;
+-  }
+-return -1;
+-}
+-
+-
+-
+-
+-/*************************************************
+-*           Compile one branch                   *
+-*************************************************/
+-
+-/* Scan the pattern, compiling it into the code vector.
+-
+-Arguments:
+-  options      the option bits
+-  brackets     points to number of extracting brackets used
+-  code         points to the pointer to the current code point
+-  ptrptr       points to the current pattern pointer
+-  errorptr     points to pointer to error message
+-  optchanged   set to the value of the last OP_OPT item compiled
+-  reqchar      set to the last literal character required, else -1
+-  countlits    set to count of mandatory literal characters
+-  cd           contains pointers to tables
+-
+-Returns:       TRUE on success
+-               FALSE, with *errorptr set on error
+-*/
+-
+-static BOOL
+-compile_branch(int options, int *brackets, uschar **codeptr,
+-  const uschar **ptrptr, const char **errorptr, int *optchanged,
+-  int *reqchar, int *countlits, compile_data *cd)
+-{
+-int repeat_type, op_type;
+-int repeat_min, repeat_max;
+-int bravalue, length;
+-int greedy_default, greedy_non_default;
+-int prevreqchar;
+-int condcount = 0;
+-int subcountlits = 0;
+-register int c;
+-register uschar *code = *codeptr;
+-uschar *tempcode;
+-const uschar *ptr = *ptrptr;
+-const uschar *tempptr;
+-uschar *previous = NULL;
+-uschar class[32];
+-
+-/* Set up the default and non-default settings for greediness */
+-
+-greedy_default = ((options & PCRE_UNGREEDY) != 0);
+-greedy_non_default = greedy_default ^ 1;
+-
+-/* Initialize no required char, and count of literals */
+-
+-*reqchar = prevreqchar = -1;
+-*countlits = 0;
+-
+-/* Switch on next character until the end of the branch */
+-
+-for (;; ptr++)
+-  {
+-  BOOL negate_class;
+-  int class_charcount;
+-  int class_lastchar;
+-  int newoptions;
+-  int skipbytes;
+-  int subreqchar;
+-
+-  c = *ptr;
+-  if ((options & PCRE_EXTENDED) != 0)
+-    {
+-    if ((cd->ctypes[c] & ctype_space) != 0) continue;
+-    if (c == '#')
+-      {
+-      /* The space before the ; is to avoid a warning on a silly compiler
+-      on the Macintosh. */
+-      while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+-      continue;
+-      }
+-    }
+-
+-  switch(c)
+-    {
+-    /* The branch terminates at end of string, |, or ). */
+-
+-    case 0:
+-    case '|':
+-    case ')':
+-    *codeptr = code;
+-    *ptrptr = ptr;
+-    return TRUE;
+-
+-    /* Handle single-character metacharacters */
+-
+-    case '^':
+-    previous = NULL;
+-    *code++ = OP_CIRC;
+-    break;
+-
+-    case '$':
+-    previous = NULL;
+-    *code++ = OP_DOLL;
+-    break;
+-
+-    case '.':
+-    previous = code;
+-    *code++ = OP_ANY;
+-    break;
+-
+-    /* Character classes. These always build a 32-byte bitmap of the permitted
+-    characters, except in the special case where there is only one character.
+-    For negated classes, we build the map as usual, then invert it at the end.
+-    */
+-
+-    case '[':
+-    previous = code;
+-    *code++ = OP_CLASS;
+-
+-    /* If the first character is '^', set the negation flag and skip it. */
+-
+-    if ((c = *(++ptr)) == '^')
+-      {
+-      negate_class = TRUE;
+-      c = *(++ptr);
+-      }
+-    else negate_class = FALSE;
+-
+-    /* Keep a count of chars so that we can optimize the case of just a single
+-    character. */
+-
+-    class_charcount = 0;
+-    class_lastchar = -1;
+-
+-    /* Initialize the 32-char bit map to all zeros. We have to build the
+-    map in a temporary bit of store, in case the class contains only 1
+-    character, because in that case the compiled code doesn't use the
+-    bit map. */
+-
+-    memset(class, 0, 32 * sizeof(uschar));
+-
+-    /* Process characters until ] is reached. By writing this as a "do" it
+-    means that an initial ] is taken as a data character. */
+-
+-    do
+-      {
+-      if (c == 0)
+-        {
+-        *errorptr = ERR6;
+-        goto FAILED;
+-        }
+-
+-      /* Handle POSIX class names. Perl allows a negation extension of the
+-      form [:^name]. A square bracket that doesn't match the syntax is
+-      treated as a literal. We also recognize the POSIX constructions
+-      [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
+-      5.6 does. */
+-
+-      if (c == '[' &&
+-          (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') &&
+-          check_posix_syntax(ptr, &tempptr, cd))
+-        {
+-        BOOL local_negate = FALSE;
+-        int posix_class, i;
+-        register const uschar *cbits = cd->cbits;
+-
+-        if (ptr[1] != ':')
+-          {
+-          *errorptr = ERR31;
+-          goto FAILED;
+-          }
+-
+-        ptr += 2;
+-        if (*ptr == '^')
+-          {
+-          local_negate = TRUE;
+-          ptr++;
+-          }
+-
+-        posix_class = check_posix_name(ptr, tempptr - ptr);
+-        if (posix_class < 0)
+-          {
+-          *errorptr = ERR30;
+-          goto FAILED;
+-          }
+-
+-        /* If matching is caseless, upper and lower are converted to
+-        alpha. This relies on the fact that the class table starts with
+-        alpha, lower, upper as the first 3 entries. */
+-
+-        if ((options & PCRE_CASELESS) != 0 && posix_class <= 2)
+-          posix_class = 0;
+-
+-        /* Or into the map we are building up to 3 of the static class
+-        tables, or their negations. */
+-
+-        posix_class *= 3;
+-        for (i = 0; i < 3; i++)
+-          {
+-          int taboffset = posix_class_maps[posix_class + i];
+-          if (taboffset < 0) break;
+-          if (local_negate)
+-            for (c = 0; c < 32; c++) class[c] |= ~cbits[c+taboffset];
+-          else
+-            for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset];
+-          }
+-
+-        ptr = tempptr + 1;
+-        class_charcount = 10;  /* Set > 1; assumes more than 1 per class */
+-        continue;
+-        }
+-
+-      /* Backslash may introduce a single character, or it may introduce one
+-      of the specials, which just set a flag. Escaped items are checked for
+-      validity in the pre-compiling pass. The sequence \b is a special case.
+-      Inside a class (and only there) it is treated as backspace. Elsewhere
+-      it marks a word boundary. Other escapes have preset maps ready to
+-      or into the one we are building. We assume they have more than one
+-      character in them, so set class_count bigger than one. */
+-
+-      if (c == '\\')
+-        {
+-        c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd);
+-        if (-c == ESC_b) c = '\b';
+-        else if (c < 0)
+-          {
+-          register const uschar *cbits = cd->cbits;
+-          class_charcount = 10;
+-          switch (-c)
+-            {
+-            case ESC_d:
+-            for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit];
+-            continue;
+-
+-            case ESC_D:
+-            for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit];
+-            continue;
+-
+-            case ESC_w:
+-            for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_word];
+-            continue;
+-
+-            case ESC_W:
+-            for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_word];
+-            continue;
+-
+-            case ESC_s:
+-            for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space];
+-            continue;
+-
+-            case ESC_S:
+-            for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space];
+-            continue;
+-
+-            default:
+-            *errorptr = ERR7;
+-            goto FAILED;
+-            }
+-          }
+-
+-        /* Fall through if single character, but don't at present allow
+-        chars > 255 in UTF-8 mode. */
+-
+-#ifdef SUPPORT_UTF8
+-        if (c > 255)
+-          {
+-          *errorptr = ERR33;
+-          goto FAILED;
+-          }
+-#endif
+-        }
+-
+-      /* A single character may be followed by '-' to form a range. However,
+-      Perl does not permit ']' to be the end of the range. A '-' character
+-      here is treated as a literal. */
+-
+-      if (ptr[1] == '-' && ptr[2] != ']')
+-        {
+-        int d;
+-        ptr += 2;
+-        d = *ptr;
+-
+-        if (d == 0)
+-          {
+-          *errorptr = ERR6;
+-          goto FAILED;
+-          }
+-
+-        /* The second part of a range can be a single-character escape, but
+-        not any of the other escapes. Perl 5.6 treats a hyphen as a literal
+-        in such circumstances. */
+-
+-        if (d == '\\')
+-          {
+-          const uschar *oldptr = ptr;
+-          d = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd);
+-
+-#ifdef SUPPORT_UTF8
+-          if (d > 255)
+-            {
+-            *errorptr = ERR33;
+-            goto FAILED;
+-            }
+-#endif
+-          /* \b is backslash; any other special means the '-' was literal */
+-
+-          if (d < 0)
+-            {
+-            if (d == -ESC_b) d = '\b'; else
+-              {
+-              ptr = oldptr - 2;
+-              goto SINGLE_CHARACTER;  /* A few lines below */
+-              }
+-            }
+-          }
+-
+-        if (d < c)
+-          {
+-          *errorptr = ERR8;
+-          goto FAILED;
+-          }
+-
+-        for (; c <= d; c++)
+-          {
+-          class[c/8] |= (1 << (c&7));
+-          if ((options & PCRE_CASELESS) != 0)
+-            {
+-            int uc = cd->fcc[c];           /* flip case */
+-            class[uc/8] |= (1 << (uc&7));
+-            }
+-          class_charcount++;                /* in case a one-char range */
+-          class_lastchar = c;
+-          }
+-        continue;   /* Go get the next char in the class */
+-        }
+-
+-      /* Handle a lone single character - we can get here for a normal
+-      non-escape char, or after \ that introduces a single character. */
+-
+-      SINGLE_CHARACTER:
+-
+-      class [c/8] |= (1 << (c&7));
+-      if ((options & PCRE_CASELESS) != 0)
+-        {
+-        c = cd->fcc[c];   /* flip case */
+-        class[c/8] |= (1 << (c&7));
+-        }
+-      class_charcount++;
+-      class_lastchar = c;
+-      }
+-
+-    /* Loop until ']' reached; the check for end of string happens inside the
+-    loop. This "while" is the end of the "do" above. */
+-
+-    while ((c = *(++ptr)) != ']');
+-
+-    /* If class_charcount is 1 and class_lastchar is not negative, we saw
+-    precisely one character. This doesn't need the whole 32-byte bit map.
+-    We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if
+-    it's negative. */
+-
+-    if (class_charcount == 1 && class_lastchar >= 0)
+-      {
+-      if (negate_class)
+-        {
+-        code[-1] = OP_NOT;
+-        }
+-      else
+-        {
+-        code[-1] = OP_CHARS;
+-        *code++ = 1;
+-        }
+-      *code++ = class_lastchar;
+-      }
+-
+-    /* Otherwise, negate the 32-byte map if necessary, and copy it into
+-    the code vector. */
+-
+-    else
+-      {
+-      if (negate_class)
+-        for (c = 0; c < 32; c++) code[c] = ~class[c];
+-      else
+-        memcpy(code, class, 32);
+-      code += 32;
+-      }
+-    break;
+-
+-    /* Various kinds of repeat */
+-
+-    case '{':
+-    if (!is_counted_repeat(ptr+1, cd)) goto NORMAL_CHAR;
+-    ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr, cd);
+-    if (*errorptr != NULL) goto FAILED;
+-    goto REPEAT;
+-
+-    case '*':
+-    repeat_min = 0;
+-    repeat_max = -1;
+-    goto REPEAT;
+-
+-    case '+':
+-    repeat_min = 1;
+-    repeat_max = -1;
+-    goto REPEAT;
+-
+-    case '?':
+-    repeat_min = 0;
+-    repeat_max = 1;
+-
+-    REPEAT:
+-    if (previous == NULL)
+-      {
+-      *errorptr = ERR9;
+-      goto FAILED;
+-      }
+-
+-    /* If the next character is '?' this is a minimizing repeat, by default,
+-    but if PCRE_UNGREEDY is set, it works the other way round. Advance to the
+-    next character. */
+-
+-    if (ptr[1] == '?')
+-      { repeat_type = greedy_non_default; ptr++; }
+-    else repeat_type = greedy_default;
+-
+-    /* If previous was a string of characters, chop off the last one and use it
+-    as the subject of the repeat. If there was only one character, we can
+-    abolish the previous item altogether. A repeat with a zero minimum wipes
+-    out any reqchar setting, backing up to the previous value. We must also
+-    adjust the countlits value. */
+-
+-    if (*previous == OP_CHARS)
+-      {
+-      int len = previous[1];
+-
+-      if (repeat_min == 0) *reqchar = prevreqchar;
+-      *countlits += repeat_min - 1;
+-
+-      if (len == 1)
+-        {
+-        c = previous[2];
+-        code = previous;
+-        }
+-      else
+-        {
+-        c = previous[len+1];
+-        previous[1]--;
+-        code--;
+-        }
+-      op_type = 0;                 /* Use single-char op codes */
+-      goto OUTPUT_SINGLE_REPEAT;   /* Code shared with single character types */
+-      }
+-
+-    /* If previous was a single negated character ([^a] or similar), we use
+-    one of the special opcodes, replacing it. The code is shared with single-
+-    character repeats by adding a suitable offset into repeat_type. */
+-
+-    else if ((int)*previous == OP_NOT)
+-      {
+-      op_type = OP_NOTSTAR - OP_STAR;  /* Use "not" opcodes */
+-      c = previous[1];
+-      code = previous;
+-      goto OUTPUT_SINGLE_REPEAT;
+-      }
+-
+-    /* If previous was a character type match (\d or similar), abolish it and
+-    create a suitable repeat item. The code is shared with single-character
+-    repeats by adding a suitable offset into repeat_type. */
+-
+-    else if ((int)*previous < OP_EODN || *previous == OP_ANY)
+-      {
+-      op_type = OP_TYPESTAR - OP_STAR;  /* Use type opcodes */
+-      c = *previous;
+-      code = previous;
+-
+-      OUTPUT_SINGLE_REPEAT:
+-
+-      /* If the maximum is zero then the minimum must also be zero; Perl allows
+-      this case, so we do too - by simply omitting the item altogether. */
+-
+-      if (repeat_max == 0) goto END_REPEAT;
+-
+-      /* Combine the op_type with the repeat_type */
+-
+-      repeat_type += op_type;
+-
+-      /* A minimum of zero is handled either as the special case * or ?, or as
+-      an UPTO, with the maximum given. */
+-
+-      if (repeat_min == 0)
+-        {
+-        if (repeat_max == -1) *code++ = OP_STAR + repeat_type;
+-          else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
+-        else
+-          {
+-          *code++ = OP_UPTO + repeat_type;
+-          *code++ = repeat_max >> 8;
+-          *code++ = (repeat_max & 255);
+-          }
+-        }
+-
+-      /* The case {1,} is handled as the special case + */
+-
+-      else if (repeat_min == 1 && repeat_max == -1)
+-        *code++ = OP_PLUS + repeat_type;
+-
+-      /* The case {n,n} is just an EXACT, while the general case {n,m} is
+-      handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */
+-
+-      else
+-        {
+-        if (repeat_min != 1)
+-          {
+-          *code++ = OP_EXACT + op_type;  /* NB EXACT doesn't have repeat_type */
+-          *code++ = repeat_min >> 8;
+-          *code++ = (repeat_min & 255);
+-          }
+-
+-        /* If the mininum is 1 and the previous item was a character string,
+-        we either have to put back the item that got cancelled if the string
+-        length was 1, or add the character back onto the end of a longer
+-        string. For a character type nothing need be done; it will just get
+-        put back naturally. Note that the final character is always going to
+-        get added below. */
+-
+-        else if (*previous == OP_CHARS)
+-          {
+-          if (code == previous) code += 2; else previous[1]++;
+-          }
+-
+-        /*  For a single negated character we also have to put back the
+-        item that got cancelled. */
+-
+-        else if (*previous == OP_NOT) code++;
+-
+-        /* If the maximum is unlimited, insert an OP_STAR. */
+-
+-        if (repeat_max < 0)
+-          {
+-          *code++ = c;
+-          *code++ = OP_STAR + repeat_type;
+-          }
+-
+-        /* Else insert an UPTO if the max is greater than the min. */
+-
+-        else if (repeat_max != repeat_min)
+-          {
+-          *code++ = c;
+-          repeat_max -= repeat_min;
+-          *code++ = OP_UPTO + repeat_type;
+-          *code++ = repeat_max >> 8;
+-          *code++ = (repeat_max & 255);
+-          }
+-        }
+-
+-      /* The character or character type itself comes last in all cases. */
+-
+-      *code++ = c;
+-      }
+-
+-    /* If previous was a character class or a back reference, we put the repeat
+-    stuff after it, but just skip the item if the repeat was {0,0}. */
+-
+-    else if (*previous == OP_CLASS || *previous == OP_REF)
+-      {
+-      if (repeat_max == 0)
+-        {
+-        code = previous;
+-        goto END_REPEAT;
+-        }
+-      if (repeat_min == 0 && repeat_max == -1)
+-        *code++ = OP_CRSTAR + repeat_type;
+-      else if (repeat_min == 1 && repeat_max == -1)
+-        *code++ = OP_CRPLUS + repeat_type;
+-      else if (repeat_min == 0 && repeat_max == 1)
+-        *code++ = OP_CRQUERY + repeat_type;
+-      else
+-        {
+-        *code++ = OP_CRRANGE + repeat_type;
+-        *code++ = repeat_min >> 8;
+-        *code++ = repeat_min & 255;
+-        if (repeat_max == -1) repeat_max = 0;  /* 2-byte encoding for max */
+-        *code++ = repeat_max >> 8;
+-        *code++ = repeat_max & 255;
+-        }
+-      }
+-
+-    /* If previous was a bracket group, we may have to replicate it in certain
+-    cases. */
+-
+-    else if ((int)*previous >= OP_BRA || (int)*previous == OP_ONCE ||
+-             (int)*previous == OP_COND)
+-      {
+-      register int i;
+-      int ketoffset = 0;
+-      int len = code - previous;
+-      uschar *bralink = NULL;
+-
+-      /* If the maximum repeat count is unlimited, find the end of the bracket
+-      by scanning through from the start, and compute the offset back to it
+-      from the current code pointer. There may be an OP_OPT setting following
+-      the final KET, so we can't find the end just by going back from the code
+-      pointer. */
+-
+-      if (repeat_max == -1)
+-        {
+-        register uschar *ket = previous;
+-        do ket += (ket[1] << 8) + ket[2]; while (*ket != OP_KET);
+-        ketoffset = code - ket;
+-        }
+-
+-      /* The case of a zero minimum is special because of the need to stick
+-      OP_BRAZERO in front of it, and because the group appears once in the
+-      data, whereas in other cases it appears the minimum number of times. For
+-      this reason, it is simplest to treat this case separately, as otherwise
+-      the code gets far too messy. There are several special subcases when the
+-      minimum is zero. */
+-
+-      if (repeat_min == 0)
+-        {
+-        /* If we set up a required char from the bracket, we must back off
+-        to the previous value and reset the countlits value too. */
+-
+-        if (subcountlits > 0)
+-          {
+-          *reqchar = prevreqchar;
+-          *countlits -= subcountlits;
+-          }
+-
+-        /* If the maximum is also zero, we just omit the group from the output
+-        altogether. */
+-
+-        if (repeat_max == 0)
+-          {
+-          code = previous;
+-          goto END_REPEAT;
+-          }
+-
+-        /* If the maximum is 1 or unlimited, we just have to stick in the
+-        BRAZERO and do no more at this point. */
+-
+-        if (repeat_max <= 1)
+-          {
+-          memmove(previous+1, previous, len);
+-          code++;
+-          *previous++ = OP_BRAZERO + repeat_type;
+-          }
+-
+-        /* If the maximum is greater than 1 and limited, we have to replicate
+-        in a nested fashion, sticking OP_BRAZERO before each set of brackets.
+-        The first one has to be handled carefully because it's the original
+-        copy, which has to be moved up. The remainder can be handled by code
+-        that is common with the non-zero minimum case below. We just have to
+-        adjust the value or repeat_max, since one less copy is required. */
+-
+-        else
+-          {
+-          int offset;
+-          memmove(previous+4, previous, len);
+-          code += 4;
+-          *previous++ = OP_BRAZERO + repeat_type;
+-          *previous++ = OP_BRA;
+-
+-          /* We chain together the bracket offset fields that have to be
+-          filled in later when the ends of the brackets are reached. */
+-
+-          offset = (bralink == NULL)? 0 : previous - bralink;
+-          bralink = previous;
+-          *previous++ = offset >> 8;
+-          *previous++ = offset & 255;
+-          }
+-
+-        repeat_max--;
+-        }
+-
+-      /* If the minimum is greater than zero, replicate the group as many
+-      times as necessary, and adjust the maximum to the number of subsequent
+-      copies that we need. */
+-
+-      else
+-        {
+-        for (i = 1; i < repeat_min; i++)
+-          {
+-          memcpy(code, previous, len);
+-          code += len;
+-          }
+-        if (repeat_max > 0) repeat_max -= repeat_min;
+-        }
+-
+-      /* This code is common to both the zero and non-zero minimum cases. If
+-      the maximum is limited, it replicates the group in a nested fashion,
+-      remembering the bracket starts on a stack. In the case of a zero minimum,
+-      the first one was set up above. In all cases the repeat_max now specifies
+-      the number of additional copies needed. */
+-
+-      if (repeat_max >= 0)
+-        {
+-        for (i = repeat_max - 1; i >= 0; i--)
+-          {
+-          *code++ = OP_BRAZERO + repeat_type;
+-
+-          /* All but the final copy start a new nesting, maintaining the
+-          chain of brackets outstanding. */
+-
+-          if (i != 0)
+-            {
+-            int offset;
+-            *code++ = OP_BRA;
+-            offset = (bralink == NULL)? 0 : code - bralink;
+-            bralink = code;
+-            *code++ = offset >> 8;
+-            *code++ = offset & 255;
+-            }
+-
+-          memcpy(code, previous, len);
+-          code += len;
+-          }
+-
+-        /* Now chain through the pending brackets, and fill in their length
+-        fields (which are holding the chain links pro tem). */
+-
+-        while (bralink != NULL)
+-          {
+-          int oldlinkoffset;
+-          int offset = code - bralink + 1;
+-          uschar *bra = code - offset;
+-          oldlinkoffset = (bra[1] << 8) + bra[2];
+-          bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
+-          *code++ = OP_KET;
+-          *code++ = bra[1] = offset >> 8;
+-          *code++ = bra[2] = (offset & 255);
+-          }
+-        }
+-
+-      /* If the maximum is unlimited, set a repeater in the final copy. We
+-      can't just offset backwards from the current code point, because we
+-      don't know if there's been an options resetting after the ket. The
+-      correct offset was computed above. */
+-
+-      else code[-ketoffset] = OP_KETRMAX + repeat_type;
+-      }
+-
+-    /* Else there's some kind of shambles */
+-
+-    else
+-      {
+-      *errorptr = ERR11;
+-      goto FAILED;
+-      }
+-
+-    /* In all case we no longer have a previous item. */
+-
+-    END_REPEAT:
+-    previous = NULL;
+-    break;
+-
+-
+-    /* Start of nested bracket sub-expression, or comment or lookahead or
+-    lookbehind or option setting or condition. First deal with special things
+-    that can come after a bracket; all are introduced by ?, and the appearance
+-    of any of them means that this is not a referencing group. They were
+-    checked for validity in the first pass over the string, so we don't have to
+-    check for syntax errors here.  */
+-
+-    case '(':
+-    newoptions = options;
+-    skipbytes = 0;
+-
+-    if (*(++ptr) == '?')
+-      {
+-      int set, unset;
+-      int *optset;
+-
+-      switch (*(++ptr))
+-        {
+-        case '#':                 /* Comment; skip to ket */
+-        ptr++;
+-        while (*ptr != ')') ptr++;
+-        continue;
+-
+-        case ':':                 /* Non-extracting bracket */
+-        bravalue = OP_BRA;
+-        ptr++;
+-        break;
+-
+-        case '(':
+-        bravalue = OP_COND;       /* Conditional group */
+-        if ((cd->ctypes[*(++ptr)] & ctype_digit) != 0)
+-          {
+-          int condref = *ptr - '0';
+-          while (*(++ptr) != ')') condref = condref*10 + *ptr - '0';
+-          if (condref == 0)
+-            {
+-            *errorptr = ERR35;
+-            goto FAILED;
+-            }
+-          ptr++;
+-          code[3] = OP_CREF;
+-          code[4] = condref >> 8;
+-          code[5] = condref & 255;
+-          skipbytes = 3;
+-          }
+-        else ptr--;
+-        break;
+-
+-        case '=':                 /* Positive lookahead */
+-        bravalue = OP_ASSERT;
+-        ptr++;
+-        break;
+-
+-        case '!':                 /* Negative lookahead */
+-        bravalue = OP_ASSERT_NOT;
+-        ptr++;
+-        break;
+-
+-        case '<':                 /* Lookbehinds */
+-        switch (*(++ptr))
+-          {
+-          case '=':               /* Positive lookbehind */
+-          bravalue = OP_ASSERTBACK;
+-          ptr++;
+-          break;
+-
+-          case '!':               /* Negative lookbehind */
+-          bravalue = OP_ASSERTBACK_NOT;
+-          ptr++;
+-          break;
+-
+-          default:                /* Syntax error */
+-          *errorptr = ERR24;
+-          goto FAILED;
+-          }
+-        break;
+-
+-        case '>':                 /* One-time brackets */
+-        bravalue = OP_ONCE;
+-        ptr++;
+-        break;
+-
+-        case 'R':                 /* Pattern recursion */
+-        *code++ = OP_RECURSE;
+-        ptr++;
+-        continue;
+-
+-        default:                  /* Option setting */
+-        set = unset = 0;
+-        optset = &set;
+-
+-        while (*ptr != ')' && *ptr != ':')
+-          {
+-          switch (*ptr++)
+-            {
+-            case '-': optset = &unset; break;
+-
+-            case 'i': *optset |= PCRE_CASELESS; break;
+-            case 'm': *optset |= PCRE_MULTILINE; break;
+-            case 's': *optset |= PCRE_DOTALL; break;
+-            case 'x': *optset |= PCRE_EXTENDED; break;
+-            case 'U': *optset |= PCRE_UNGREEDY; break;
+-            case 'X': *optset |= PCRE_EXTRA; break;
+-
+-            default:
+-            *errorptr = ERR12;
+-            goto FAILED;
+-            }
+-          }
+-
+-        /* Set up the changed option bits, but don't change anything yet. */
+-
+-        newoptions = (options | set) & (~unset);
+-
+-        /* If the options ended with ')' this is not the start of a nested
+-        group with option changes, so the options change at this level. At top
+-        level there is nothing else to be done (the options will in fact have
+-        been set from the start of compiling as a result of the first pass) but
+-        at an inner level we must compile code to change the ims options if
+-        necessary, and pass the new setting back so that it can be put at the
+-        start of any following branches, and when this group ends, a resetting
+-        item can be compiled. */
+-
+-        if (*ptr == ')')
+-          {
+-          if ((options & PCRE_INGROUP) != 0 &&
+-              (options & PCRE_IMS) != (newoptions & PCRE_IMS))
+-            {
+-            *code++ = OP_OPT;
+-            *code++ = *optchanged = newoptions & PCRE_IMS;
+-            }
+-          options = newoptions;  /* Change options at this level */
+-          previous = NULL;       /* This item can't be repeated */
+-          continue;              /* It is complete */
+-          }
+-
+-        /* If the options ended with ':' we are heading into a nested group
+-        with possible change of options. Such groups are non-capturing and are
+-        not assertions of any kind. All we need to do is skip over the ':';
+-        the newoptions value is handled below. */
+-
+-        bravalue = OP_BRA;
+-        ptr++;
+-        }
+-      }
+-
+-    /* Else we have a referencing group; adjust the opcode. If the bracket
+-    number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and
+-    arrange for the true number to follow later, in an OP_BRANUMBER item. */
+-
+-    else
+-      {
+-      if (++(*brackets) > EXTRACT_BASIC_MAX)
+-        {
+-        bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1;
+-        code[3] = OP_BRANUMBER;
+-        code[4] = *brackets >> 8;
+-        code[5] = *brackets & 255;
+-        skipbytes = 3;
+-        }
+-      else bravalue = OP_BRA + *brackets;
+-      }
+-
+-    /* Process nested bracketed re. Assertions may not be repeated, but other
+-    kinds can be. We copy code into a non-register variable in order to be able
+-    to pass its address because some compilers complain otherwise. Pass in a
+-    new setting for the ims options if they have changed. */
+-
+-    previous = (bravalue >= OP_ONCE)? code : NULL;
+-    *code = bravalue;
+-    tempcode = code;
+-
+-    if (!compile_regex(
+-         options | PCRE_INGROUP,       /* Set for all nested groups */
+-         ((options & PCRE_IMS) != (newoptions & PCRE_IMS))?
+-           newoptions & PCRE_IMS : -1, /* Pass ims options if changed */
+-         brackets,                     /* Extracting bracket count */
+-         &tempcode,                    /* Where to put code (updated) */
+-         &ptr,                         /* Input pointer (updated) */
+-         errorptr,                     /* Where to put an error message */
+-         (bravalue == OP_ASSERTBACK ||
+-          bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */
+-         skipbytes,                    /* Skip over OP_COND/OP_BRANUMBER */
+-         &subreqchar,                  /* For possible last char */
+-         &subcountlits,                /* For literal count */
+-         cd))                          /* Tables block */
+-      goto FAILED;
+-
+-    /* At the end of compiling, code is still pointing to the start of the
+-    group, while tempcode has been updated to point past the end of the group
+-    and any option resetting that may follow it. The pattern pointer (ptr)
+-    is on the bracket. */
+-
+-    /* If this is a conditional bracket, check that there are no more than
+-    two branches in the group. */
+-
+-    else if (bravalue == OP_COND)
+-      {
+-      uschar *tc = code;
+-      condcount = 0;
+-
+-      do {
+-         condcount++;
+-         tc += (tc[1] << 8) | tc[2];
+-         }
+-      while (*tc != OP_KET);
+-
+-      if (condcount > 2)
+-        {
+-        *errorptr = ERR27;
+-        goto FAILED;
+-        }
+-      }
+-
+-    /* Handle updating of the required character. If the subpattern didn't
+-    set one, leave it as it was. Otherwise, update it for normal brackets of
+-    all kinds, forward assertions, and conditions with two branches. Don't
+-    update the literal count for forward assertions, however. If the bracket
+-    is followed by a quantifier with zero repeat, we have to back off. Hence
+-    the definition of prevreqchar and subcountlits outside the main loop so
+-    that they can be accessed for the back off. */
+-
+-    if (subreqchar > 0 &&
+-         (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_ASSERT ||
+-         (bravalue == OP_COND && condcount == 2)))
+-      {
+-      prevreqchar = *reqchar;
+-      *reqchar = subreqchar;
+-      if (bravalue != OP_ASSERT) *countlits += subcountlits;
+-      }
+-
+-    /* Now update the main code pointer to the end of the group. */
+-
+-    code = tempcode;
+-
+-    /* Error if hit end of pattern */
+-
+-    if (*ptr != ')')
+-      {
+-      *errorptr = ERR14;
+-      goto FAILED;
+-      }
+-    break;
+-
+-    /* Check \ for being a real metacharacter; if not, fall through and handle
+-    it as a data character at the start of a string. Escape items are checked
+-    for validity in the pre-compiling pass. */
+-
+-    case '\\':
+-    tempptr = ptr;
+-    c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd);
+-
+-    /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values
+-    are arranged to be the negation of the corresponding OP_values. For the
+-    back references, the values are ESC_REF plus the reference number. Only
+-    back references and those types that consume a character may be repeated.
+-    We can test for values between ESC_b and ESC_Z for the latter; this may
+-    have to change if any new ones are ever created. */
+-
+-    if (c < 0)
+-      {
+-      if (-c >= ESC_REF)
+-        {
+-        int number = -c - ESC_REF;
+-        previous = code;
+-        *code++ = OP_REF;
+-        *code++ = number >> 8;
+-        *code++ = number & 255;
+-        }
+-      else
+-        {
+-        previous = (-c > ESC_b && -c < ESC_Z)? code : NULL;
+-        *code++ = -c;
+-        }
+-      continue;
+-      }
+-
+-    /* Data character: reset and fall through */
+-
+-    ptr = tempptr;
+-    c = '\\';
+-
+-    /* Handle a run of data characters until a metacharacter is encountered.
+-    The first character is guaranteed not to be whitespace or # when the
+-    extended flag is set. */
+-
+-    NORMAL_CHAR:
+-    default:
+-    previous = code;
+-    *code = OP_CHARS;
+-    code += 2;
+-    length = 0;
+-
+-    do
+-      {
+-      if ((options & PCRE_EXTENDED) != 0)
+-        {
+-        if ((cd->ctypes[c] & ctype_space) != 0) continue;
+-        if (c == '#')
+-          {
+-          /* The space before the ; is to avoid a warning on a silly compiler
+-          on the Macintosh. */
+-          while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+-          if (c == 0) break;
+-          continue;
+-          }
+-        }
+-
+-      /* Backslash may introduce a data char or a metacharacter. Escaped items
+-      are checked for validity in the pre-compiling pass. Stop the string
+-      before a metaitem. */
+-
+-      if (c == '\\')
+-        {
+-        tempptr = ptr;
+-        c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd);
+-        if (c < 0) { ptr = tempptr; break; }
+-
+-        /* If a character is > 127 in UTF-8 mode, we have to turn it into
+-        two or more characters in the UTF-8 encoding. */
+-
+-#ifdef SUPPORT_UTF8
+-        if (c > 127 && (options & PCRE_UTF8) != 0)
+-          {
+-          uschar buffer[8];
+-          int len = ord2utf8(c, buffer);
+-          for (c = 0; c < len; c++) *code++ = buffer[c];
+-          length += len;
+-          continue;
+-          }
+-#endif
+-        }
+-
+-      /* Ordinary character or single-char escape */
+-
+-      *code++ = c;
+-      length++;
+-      }
+-
+-    /* This "while" is the end of the "do" above. */
+-
+-    while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0);
+-
+-    /* Update the last character and the count of literals */
+-
+-    prevreqchar = (length > 1)? code[-2] : *reqchar;
+-    *reqchar = code[-1];
+-    *countlits += length;
+-
+-    /* Compute the length and set it in the data vector, and advance to
+-    the next state. */
+-
+-    previous[1] = length;
+-    if (length < MAXLIT) ptr--;
+-    break;
+-    }
+-  }                   /* end of big loop */
+-
+-/* Control never reaches here by falling through, only by a goto for all the
+-error states. Pass back the position in the pattern so that it can be displayed
+-to the user for diagnosing the error. */
+-
+-FAILED:
+-*ptrptr = ptr;
+-return FALSE;
+-}
+-
+-
+-
+-
+-/*************************************************
+-*     Compile sequence of alternatives           *
+-*************************************************/
+-
+-/* On entry, ptr is pointing past the bracket character, but on return
+-it points to the closing bracket, or vertical bar, or end of string.
+-The code variable is pointing at the byte into which the BRA operator has been
+-stored. If the ims options are changed at the start (for a (?ims: group) or
+-during any branch, we need to insert an OP_OPT item at the start of every
+-following branch to ensure they get set correctly at run time, and also pass
+-the new options into every subsequent branch compile.
+-
+-Argument:
+-  options     the option bits
+-  optchanged  new ims options to set as if (?ims) were at the start, or -1
+-               for no change
+-  brackets    -> int containing the number of extracting brackets used
+-  codeptr     -> the address of the current code pointer
+-  ptrptr      -> the address of the current pattern pointer
+-  errorptr    -> pointer to error message
+-  lookbehind  TRUE if this is a lookbehind assertion
+-  skipbytes   skip this many bytes at start (for OP_COND, OP_BRANUMBER)
+-  reqchar     -> place to put the last required character, or a negative number
+-  countlits   -> place to put the shortest literal count of any branch
+-  cd          points to the data block with tables pointers
+-
+-Returns:      TRUE on success
+-*/
+-
+-static BOOL
+-compile_regex(int options, int optchanged, int *brackets, uschar **codeptr,
+-  const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int skipbytes,
+-  int *reqchar, int *countlits, compile_data *cd)
+-{
+-const uschar *ptr = *ptrptr;
+-uschar *code = *codeptr;
+-uschar *last_branch = code;
+-uschar *start_bracket = code;
+-uschar *reverse_count = NULL;
+-int oldoptions = options & PCRE_IMS;
+-int branchreqchar, branchcountlits;
+-
+-*reqchar = -1;
+-*countlits = INT_MAX;
+-code += 3 + skipbytes;
+-
+-/* Loop for each alternative branch */
+-
+-for (;;)
+-  {
+-  int length;
+-
+-  /* Handle change of options */
+-
+-  if (optchanged >= 0)
+-    {
+-    *code++ = OP_OPT;
+-    *code++ = optchanged;
+-    options = (options & ~PCRE_IMS) | optchanged;
+-    }
+-
+-  /* Set up dummy OP_REVERSE if lookbehind assertion */
+-
+-  if (lookbehind)
+-    {
+-    *code++ = OP_REVERSE;
+-    reverse_count = code;
+-    *code++ = 0;
+-    *code++ = 0;
+-    }
+-
+-  /* Now compile the branch */
+-
+-  if (!compile_branch(options, brackets, &code, &ptr, errorptr, &optchanged,
+-      &branchreqchar, &branchcountlits, cd))
+-    {
+-    *ptrptr = ptr;
+-    return FALSE;
+-    }
+-
+-  /* Fill in the length of the last branch */
+-
+-  length = code - last_branch;
+-  last_branch[1] = length >> 8;
+-  last_branch[2] = length & 255;
+-
+-  /* Save the last required character if all branches have the same; a current
+-  value of -1 means unset, while -2 means "previous branch had no last required
+-  char".  */
+-
+-  if (*reqchar != -2)
+-    {
+-    if (branchreqchar >= 0)
+-      {
+-      if (*reqchar == -1) *reqchar = branchreqchar;
+-      else if (*reqchar != branchreqchar) *reqchar = -2;
+-      }
+-    else *reqchar = -2;
+-    }
+-
+-  /* Keep the shortest literal count */
+-
+-  if (branchcountlits < *countlits) *countlits = branchcountlits;
+-  DPRINTF(("literal count = %d min=%d\n", branchcountlits, *countlits));
+-
+-  /* If lookbehind, check that this branch matches a fixed-length string,
+-  and put the length into the OP_REVERSE item. Temporarily mark the end of
+-  the branch with OP_END. */
+-
+-  if (lookbehind)
+-    {
+-    *code = OP_END;
+-    length = find_fixedlength(last_branch, options);
+-    DPRINTF(("fixed length = %d\n", length));
+-    if (length < 0)
+-      {
+-      *errorptr = ERR25;
+-      *ptrptr = ptr;
+-      return FALSE;
+-      }
+-    reverse_count[0] = (length >> 8);
+-    reverse_count[1] = length & 255;
+-    }
+-
+-  /* Reached end of expression, either ')' or end of pattern. Insert a
+-  terminating ket and the length of the whole bracketed item, and return,
+-  leaving the pointer at the terminating char. If any of the ims options
+-  were changed inside the group, compile a resetting op-code following. */
+-
+-  if (*ptr != '|')
+-    {
+-    length = code - start_bracket;
+-    *code++ = OP_KET;
+-    *code++ = length >> 8;
+-    *code++ = length & 255;
+-    if (optchanged >= 0)
+-      {
+-      *code++ = OP_OPT;
+-      *code++ = oldoptions;
+-      }
+-    *codeptr = code;
+-    *ptrptr = ptr;
+-    return TRUE;
+-    }
+-
+-  /* Another branch follows; insert an "or" node and advance the pointer. */
+-
+-  *code = OP_ALT;
+-  last_branch = code;
+-  code += 3;
+-  ptr++;
+-  }
+-/* Control never reaches here */
+-}
+-
+-
+-
+-
+-/*************************************************
+-*      Find first significant op code            *
+-*************************************************/
+-
+-/* This is called by several functions that scan a compiled expression looking
+-for a fixed first character, or an anchoring op code etc. It skips over things
+-that do not influence this. For one application, a change of caseless option is
+-important.
+-
+-Arguments:
+-  code       pointer to the start of the group
+-  options    pointer to external options
+-  optbit     the option bit whose changing is significant, or
+-             zero if none are
+-  optstop    TRUE to return on option change, otherwise change the options
+-               value and continue
+-
+-Returns:     pointer to the first significant opcode
+-*/
+-
+-static const uschar*
+-first_significant_code(const uschar *code, int *options, int optbit,
+-  BOOL optstop)
+-{
+-for (;;)
+-  {
+-  switch ((int)*code)
+-    {
+-    case OP_OPT:
+-    if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit))
+-      {
+-      if (optstop) return code;
+-      *options = (int)code[1];
+-      }
+-    code += 2;
+-    break;
+-
+-    case OP_CREF:
+-    case OP_BRANUMBER:
+-    code += 3;
+-    break;
+-
+-    case OP_WORD_BOUNDARY:
+-    case OP_NOT_WORD_BOUNDARY:
+-    code++;
+-    break;
+-
+-    case OP_ASSERT_NOT:
+-    case OP_ASSERTBACK:
+-    case OP_ASSERTBACK_NOT:
+-    do code += (code[1] << 8) + code[2]; while (*code == OP_ALT);
+-    code += 3;
+-    break;
+-
+-    default:
+-    return code;
+-    }
+-  }
+-/* Control never reaches here */
+-}
+-
+-
+-
+-
+-/*************************************************
+-*          Check for anchored expression         *
+-*************************************************/
+-
+-/* Try to find out if this is an anchored regular expression. Consider each
+-alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
+-all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
+-it's anchored. However, if this is a multiline pattern, then only OP_SOD
+-counts, since OP_CIRC can match in the middle.
+-
+-A branch is also implicitly anchored if it starts with .* and DOTALL is set,
+-because that will try the rest of the pattern at all possible matching points,
+-so there is no point trying them again.
+-
+-Arguments:
+-  code       points to start of expression (the bracket)
+-  options    points to the options setting
+-
+-Returns:     TRUE or FALSE
+-*/
+-
+-static BOOL
+-is_anchored(register const uschar *code, int *options)
+-{
+-do {
+-   const uschar *scode = first_significant_code(code + 3, options,
+-     PCRE_MULTILINE, FALSE);
+-   register int op = *scode;
+-   if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+-     { if (!is_anchored(scode, options)) return FALSE; }
+-   else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) &&
+-            (*options & PCRE_DOTALL) != 0)
+-     { if (scode[1] != OP_ANY) return FALSE; }
+-   else if (op != OP_SOD &&
+-           ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC))
+-     return FALSE;
+-   code += (code[1] << 8) + code[2];
+-   }
+-while (*code == OP_ALT);
+-return TRUE;
+-}
+-
+-
+-
+-/*************************************************
+-*         Check for starting with ^ or .*        *
+-*************************************************/
+-
+-/* This is called to find out if every branch starts with ^ or .* so that
+-"first char" processing can be done to speed things up in multiline
+-matching and for non-DOTALL patterns that start with .* (which must start at
+-the beginning or after \n).
+-
+-Argument:  points to start of expression (the bracket)
+-Returns:   TRUE or FALSE
+-*/
+-
+-static BOOL
+-is_startline(const uschar *code)
+-{
+-do {
+-   const uschar *scode = first_significant_code(code + 3, NULL, 0, FALSE);
+-   register int op = *scode;
+-   if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+-     { if (!is_startline(scode)) return FALSE; }
+-   else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR)
+-     { if (scode[1] != OP_ANY) return FALSE; }
+-   else if (op != OP_CIRC) return FALSE;
+-   code += (code[1] << 8) + code[2];
+-   }
+-while (*code == OP_ALT);
+-return TRUE;
+-}
+-
+-
+-
+-/*************************************************
+-*          Check for fixed first char            *
+-*************************************************/
+-
+-/* Try to find out if there is a fixed first character. This is called for
+-unanchored expressions, as it speeds up their processing quite considerably.
+-Consider each alternative branch. If they all start with the same char, or with
+-a bracket all of whose alternatives start with the same char (recurse ad lib),
+-then we return that char, otherwise -1.
+-
+-Arguments:
+-  code       points to start of expression (the bracket)
+-  options    pointer to the options (used to check casing changes)
+-
+-Returns:     -1 or the fixed first char
+-*/
+-
+-static int
+-find_firstchar(const uschar *code, int *options)
+-{
+-register int c = -1;
+-do {
+-   int d;
+-   const uschar *scode = first_significant_code(code + 3, options,
+-     PCRE_CASELESS, TRUE);
+-   register int op = *scode;
+-
+-   if (op >= OP_BRA) op = OP_BRA;
+-
+-   switch(op)
+-     {
+-     default:
+-     return -1;
+-
+-     case OP_BRA:
+-     case OP_ASSERT:
+-     case OP_ONCE:
+-     case OP_COND:
+-     if ((d = find_firstchar(scode, options)) < 0) return -1;
+-     if (c < 0) c = d; else if (c != d) return -1;
+-     break;
+-
+-     case OP_EXACT:       /* Fall through */
+-     scode++;
+-
+-     case OP_CHARS:       /* Fall through */
+-     scode++;
+-
+-     case OP_PLUS:
+-     case OP_MINPLUS:
+-     if (c < 0) c = scode[1]; else if (c != scode[1]) return -1;
+-     break;
+-     }
+-
+-   code += (code[1] << 8) + code[2];
+-   }
+-while (*code == OP_ALT);
+-return c;
+-}
+-
+-
+-
+-
+-
+-/*************************************************
+-*        Compile a Regular Expression            *
+-*************************************************/
+-
+-/* This function takes a string and returns a pointer to a block of store
+-holding a compiled version of the expression.
+-
+-Arguments:
+-  pattern      the regular expression
+-  options      various option bits
+-  errorptr     pointer to pointer to error text
+-  erroroffset  ptr offset in pattern where error was detected
+-  tables       pointer to character tables or NULL
+-
+-Returns:       pointer to compiled data block, or NULL on error,
+-               with errorptr and erroroffset set
+-*/
+-
+-pcre *
+-pcre_compile(const char *pattern, int options, const char **errorptr,
+-  int *erroroffset, const unsigned char *tables)
+-{
+-real_pcre *re;
+-int length = 3;      /* For initial BRA plus length */
+-int runlength;
+-int c, reqchar, countlits;
+-int bracount = 0;
+-int top_backref = 0;
+-int branch_extra = 0;
+-int branch_newextra;
+-unsigned int brastackptr = 0;
+-size_t size;
+-uschar *code;
+-const uschar *ptr;
+-compile_data compile_block;
+-int brastack[BRASTACK_SIZE];
+-uschar bralenstack[BRASTACK_SIZE];
+-
+-#ifdef DEBUG
+-uschar *code_base, *code_end;
+-#endif
+-
+-/* Can't support UTF8 unless PCRE has been compiled to include the code. */
+-
+-#ifndef SUPPORT_UTF8
+-if ((options & PCRE_UTF8) != 0)
+-  {
+-  *errorptr = ERR32;
+-  return NULL;
+-  }
+-#endif
+-
+-/* We can't pass back an error message if errorptr is NULL; I guess the best we
+-can do is just return NULL. */
+-
+-if (errorptr == NULL) return NULL;
+-*errorptr = NULL;
+-
+-/* However, we can give a message for this error */
+-
+-if (erroroffset == NULL)
+-  {
+-  *errorptr = ERR16;
+-  return NULL;
+-  }
+-*erroroffset = 0;
+-
+-if ((options & ~PUBLIC_OPTIONS) != 0)
+-  {
+-  *errorptr = ERR17;
+-  return NULL;
+-  }
+-
+-/* Set up pointers to the individual character tables */
+-
+-if (tables == NULL) tables = pcre_default_tables;
+-compile_block.lcc = tables + lcc_offset;
+-compile_block.fcc = tables + fcc_offset;
+-compile_block.cbits = tables + cbits_offset;
+-compile_block.ctypes = tables + ctypes_offset;
+-
+-/* Reflect pattern for debugging output */
+-
+-DPRINTF(("------------------------------------------------------------------\n"));
+-DPRINTF(("%s\n", pattern));
+-
+-/* The first thing to do is to make a pass over the pattern to compute the
+-amount of store required to hold the compiled code. This does not have to be
+-perfect as long as errors are overestimates. At the same time we can detect any
+-internal flag settings. Make an attempt to correct for any counted white space
+-if an "extended" flag setting appears late in the pattern. We can't be so
+-clever for #-comments. */
+-
+-ptr = (const uschar *)(pattern - 1);
+-while ((c = *(++ptr)) != 0)
+-  {
+-  int min, max;
+-  int class_charcount;
+-  int bracket_length;
+-
+-  if ((options & PCRE_EXTENDED) != 0)
+-    {
+-    if ((compile_block.ctypes[c] & ctype_space) != 0) continue;
+-    if (c == '#')
+-      {
+-      /* The space before the ; is to avoid a warning on a silly compiler
+-      on the Macintosh. */
+-      while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+-      continue;
+-      }
+-    }
+-
+-  switch(c)
+-    {
+-    /* A backslashed item may be an escaped "normal" character or a
+-    character type. For a "normal" character, put the pointers and
+-    character back so that tests for whitespace etc. in the input
+-    are done correctly. */
+-
+-    case '\\':
+-      {
+-      const uschar *save_ptr = ptr;
+-      c = check_escape(&ptr, errorptr, bracount, options, FALSE, &compile_block);
+-      if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-      if (c >= 0)
+-        {
+-        ptr = save_ptr;
+-        c = '\\';
+-        goto NORMAL_CHAR;
+-        }
+-      }
+-    length++;
+-
+-    /* A back reference needs an additional 2 bytes, plus either one or 5
+-    bytes for a repeat. We also need to keep the value of the highest
+-    back reference. */
+-
+-    if (c <= -ESC_REF)
+-      {
+-      int refnum = -c - ESC_REF;
+-      if (refnum > top_backref) top_backref = refnum;
+-      length += 2;   /* For single back reference */
+-      if (ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block))
+-        {
+-        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block);
+-        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-        if ((min == 0 && (max == 1 || max == -1)) ||
+-          (min == 1 && max == -1))
+-            length++;
+-        else length += 5;
+-        if (ptr[1] == '?') ptr++;
+-        }
+-      }
+-    continue;
+-
+-    case '^':
+-    case '.':
+-    case '$':
+-    case '*':     /* These repeats won't be after brackets; */
+-    case '+':     /* those are handled separately */
+-    case '?':
+-    length++;
+-    continue;
+-
+-    /* This covers the cases of repeats after a single char, metachar, class,
+-    or back reference. */
+-
+-    case '{':
+-    if (!is_counted_repeat(ptr+1, &compile_block)) goto NORMAL_CHAR;
+-    ptr = read_repeat_counts(ptr+1, &min, &max, errorptr, &compile_block);
+-    if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-    if ((min == 0 && (max == 1 || max == -1)) ||
+-      (min == 1 && max == -1))
+-        length++;
+-    else
+-      {
+-      length--;   /* Uncount the original char or metachar */
+-      if (min == 1) length++; else if (min > 0) length += 4;
+-      if (max > 0) length += 4; else length += 2;
+-      }
+-    if (ptr[1] == '?') ptr++;
+-    continue;
+-
+-    /* An alternation contains an offset to the next branch or ket. If any ims
+-    options changed in the previous branch(es), and/or if we are in a
+-    lookbehind assertion, extra space will be needed at the start of the
+-    branch. This is handled by branch_extra. */
+-
+-    case '|':
+-    length += 3 + branch_extra;
+-    continue;
+-
+-    /* A character class uses 33 characters. Don't worry about character types
+-    that aren't allowed in classes - they'll get picked up during the compile.
+-    A character class that contains only one character uses 2 or 3 bytes,
+-    depending on whether it is negated or not. Notice this where we can. */
+-
+-    case '[':
+-    class_charcount = 0;
+-    if (*(++ptr) == '^') ptr++;
+-    do
+-      {
+-      if (*ptr == '\\')
+-        {
+-        int ch = check_escape(&ptr, errorptr, bracount, options, TRUE,
+-          &compile_block);
+-        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-        if (-ch == ESC_b) class_charcount++; else class_charcount = 10;
+-        }
+-      else class_charcount++;
+-      ptr++;
+-      }
+-    while (*ptr != 0 && *ptr != ']');
+-
+-    /* Repeats for negated single chars are handled by the general code */
+-
+-    if (class_charcount == 1) length += 3; else
+-      {
+-      length += 33;
+-
+-      /* A repeat needs either 1 or 5 bytes. */
+-
+-      if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block))
+-        {
+-        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block);
+-        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-        if ((min == 0 && (max == 1 || max == -1)) ||
+-          (min == 1 && max == -1))
+-            length++;
+-        else length += 5;
+-        if (ptr[1] == '?') ptr++;
+-        }
+-      }
+-    continue;
+-
+-    /* Brackets may be genuine groups or special things */
+-
+-    case '(':
+-    branch_newextra = 0;
+-    bracket_length = 3;
+-
+-    /* Handle special forms of bracket, which all start (? */
+-
+-    if (ptr[1] == '?')
+-      {
+-      int set, unset;
+-      int *optset;
+-
+-      switch (c = ptr[2])
+-        {
+-        /* Skip over comments entirely */
+-        case '#':
+-        ptr += 3;
+-        while (*ptr != 0 && *ptr != ')') ptr++;
+-        if (*ptr == 0)
+-          {
+-          *errorptr = ERR18;
+-          goto PCRE_ERROR_RETURN;
+-          }
+-        continue;
+-
+-        /* Non-referencing groups and lookaheads just move the pointer on, and
+-        then behave like a non-special bracket, except that they don't increment
+-        the count of extracting brackets. Ditto for the "once only" bracket,
+-        which is in Perl from version 5.005. */
+-
+-        case ':':
+-        case '=':
+-        case '!':
+-        case '>':
+-        ptr += 2;
+-        break;
+-
+-        /* A recursive call to the regex is an extension, to provide the
+-        facility which can be obtained by $(?p{perl-code}) in Perl 5.6. */
+-
+-        case 'R':
+-        if (ptr[3] != ')')
+-          {
+-          *errorptr = ERR29;
+-          goto PCRE_ERROR_RETURN;
+-          }
+-        ptr += 3;
+-        length += 1;
+-        break;
+-
+-        /* Lookbehinds are in Perl from version 5.005 */
+-
+-        case '<':
+-        if (ptr[3] == '=' || ptr[3] == '!')
+-          {
+-          ptr += 3;
+-          branch_newextra = 3;
+-          length += 3;         /* For the first branch */
+-          break;
+-          }
+-        *errorptr = ERR24;
+-        goto PCRE_ERROR_RETURN;
+-
+-        /* Conditionals are in Perl from version 5.005. The bracket must either
+-        be followed by a number (for bracket reference) or by an assertion
+-        group. */
+-
+-        case '(':
+-        if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0)
+-          {
+-          ptr += 4;
+-          length += 3;
+-          while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) ptr++;
+-          if (*ptr != ')')
+-            {
+-            *errorptr = ERR26;
+-            goto PCRE_ERROR_RETURN;
+-            }
+-          }
+-        else   /* An assertion must follow */
+-          {
+-          ptr++;   /* Can treat like ':' as far as spacing is concerned */
+-          if (ptr[2] != '?' ||
+-             (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<') )
+-            {
+-            ptr += 2;    /* To get right offset in message */
+-            *errorptr = ERR28;
+-            goto PCRE_ERROR_RETURN;
+-            }
+-          }
+-        break;
+-
+-        /* Else loop checking valid options until ) is met. Anything else is an
+-        error. If we are without any brackets, i.e. at top level, the settings
+-        act as if specified in the options, so massage the options immediately.
+-        This is for backward compatibility with Perl 5.004. */
+-
+-        default:
+-        set = unset = 0;
+-        optset = &set;
+-        ptr += 2;
+-
+-        for (;; ptr++)
+-          {
+-          c = *ptr;
+-          switch (c)
+-            {
+-            case 'i':
+-            *optset |= PCRE_CASELESS;
+-            continue;
+-
+-            case 'm':
+-            *optset |= PCRE_MULTILINE;
+-            continue;
+-
+-            case 's':
+-            *optset |= PCRE_DOTALL;
+-            continue;
+-
+-            case 'x':
+-            *optset |= PCRE_EXTENDED;
+-            continue;
+-
+-            case 'X':
+-            *optset |= PCRE_EXTRA;
+-            continue;
+-
+-            case 'U':
+-            *optset |= PCRE_UNGREEDY;
+-            continue;
+-
+-            case '-':
+-            optset = &unset;
+-            continue;
+-
+-            /* A termination by ')' indicates an options-setting-only item;
+-            this is global at top level; otherwise nothing is done here and
+-            it is handled during the compiling process on a per-bracket-group
+-            basis. */
+-
+-            case ')':
+-            if (brastackptr == 0)
+-              {
+-              options = (options | set) & (~unset);
+-              set = unset = 0;     /* To save length */
+-              }
+-            /* Fall through */
+-
+-            /* A termination by ':' indicates the start of a nested group with
+-            the given options set. This is again handled at compile time, but
+-            we must allow for compiled space if any of the ims options are
+-            set. We also have to allow for resetting space at the end of
+-            the group, which is why 4 is added to the length and not just 2.
+-            If there are several changes of options within the same group, this
+-            will lead to an over-estimate on the length, but this shouldn't
+-            matter very much. We also have to allow for resetting options at
+-            the start of any alternations, which we do by setting
+-            branch_newextra to 2. Finally, we record whether the case-dependent
+-            flag ever changes within the regex. This is used by the "required
+-            character" code. */
+-
+-            case ':':
+-            if (((set|unset) & PCRE_IMS) != 0)
+-              {
+-              length += 4;
+-              branch_newextra = 2;
+-              if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED;
+-              }
+-            goto END_OPTIONS;
+-
+-            /* Unrecognized option character */
+-
+-            default:
+-            *errorptr = ERR12;
+-            goto PCRE_ERROR_RETURN;
+-            }
+-          }
+-
+-        /* If we hit a closing bracket, that's it - this is a freestanding
+-        option-setting. We need to ensure that branch_extra is updated if
+-        necessary. The only values branch_newextra can have here are 0 or 2.
+-        If the value is 2, then branch_extra must either be 2 or 5, depending
+-        on whether this is a lookbehind group or not. */
+-
+-        END_OPTIONS:
+-        if (c == ')')
+-          {
+-          if (branch_newextra == 2 && (branch_extra == 0 || branch_extra == 3))
+-            branch_extra += branch_newextra;
+-          continue;
+-          }
+-
+-        /* If options were terminated by ':' control comes here. Fall through
+-        to handle the group below. */
+-        }
+-      }
+-
+-    /* Extracting brackets must be counted so we can process escapes in a
+-    Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to
+-    need an additional 3 bytes of store per extracting bracket. */
+-
+-    else
+-      {
+-      bracount++;
+-      if (bracount > EXTRACT_BASIC_MAX) bracket_length += 3;
+-      }
+-
+-    /* Save length for computing whole length at end if there's a repeat that
+-    requires duplication of the group. Also save the current value of
+-    branch_extra, and start the new group with the new value. If non-zero, this
+-    will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */
+-
+-    if (brastackptr >= sizeof(brastack)/sizeof(int))
+-      {
+-      *errorptr = ERR19;
+-      goto PCRE_ERROR_RETURN;
+-      }
+-
+-    bralenstack[brastackptr] = branch_extra;
+-    branch_extra = branch_newextra;
+-
+-    brastack[brastackptr++] = length;
+-    length += bracket_length;
+-    continue;
+-
+-    /* Handle ket. Look for subsequent max/min; for certain sets of values we
+-    have to replicate this bracket up to that many times. If brastackptr is
+-    0 this is an unmatched bracket which will generate an error, but take care
+-    not to try to access brastack[-1] when computing the length and restoring
+-    the branch_extra value. */
+-
+-    case ')':
+-    length += 3;
+-      {
+-      int minval = 1;
+-      int maxval = 1;
+-      int duplength;
+-
+-      if (brastackptr > 0)
+-        {
+-        duplength = length - brastack[--brastackptr];
+-        branch_extra = bralenstack[brastackptr];
+-        }
+-      else duplength = 0;
+-
+-      /* Leave ptr at the final char; for read_repeat_counts this happens
+-      automatically; for the others we need an increment. */
+-
+-      if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2, &compile_block))
+-        {
+-        ptr = read_repeat_counts(ptr+2, &minval, &maxval, errorptr,
+-          &compile_block);
+-        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-        }
+-      else if (c == '*') { minval = 0; maxval = -1; ptr++; }
+-      else if (c == '+') { maxval = -1; ptr++; }
+-      else if (c == '?') { minval = 0; ptr++; }
+-
+-      /* If the minimum is zero, we have to allow for an OP_BRAZERO before the
+-      group, and if the maximum is greater than zero, we have to replicate
+-      maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting
+-      bracket set - hence the 7. */
+-
+-      if (minval == 0)
+-        {
+-        length++;
+-        if (maxval > 0) length += (maxval - 1) * (duplength + 7);
+-        }
+-
+-      /* When the minimum is greater than zero, 1 we have to replicate up to
+-      minval-1 times, with no additions required in the copies. Then, if
+-      there is a limited maximum we have to replicate up to maxval-1 times
+-      allowing for a BRAZERO item before each optional copy and nesting
+-      brackets for all but one of the optional copies. */
+-
+-      else
+-        {
+-        length += (minval - 1) * duplength;
+-        if (maxval > minval)   /* Need this test as maxval=-1 means no limit */
+-          length += (maxval - minval) * (duplength + 7) - 6;
+-        }
+-      }
+-    continue;
+-
+-    /* Non-special character. For a run of such characters the length required
+-    is the number of characters + 2, except that the maximum run length is 255.
+-    We won't get a skipped space or a non-data escape or the start of a #
+-    comment as the first character, so the length can't be zero. */
+-
+-    NORMAL_CHAR:
+-    default:
+-    length += 2;
+-    runlength = 0;
+-    do
+-      {
+-      if ((options & PCRE_EXTENDED) != 0)
+-        {
+-        if ((compile_block.ctypes[c] & ctype_space) != 0) continue;
+-        if (c == '#')
+-          {
+-          /* The space before the ; is to avoid a warning on a silly compiler
+-          on the Macintosh. */
+-          while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+-          continue;
+-          }
+-        }
+-
+-      /* Backslash may introduce a data char or a metacharacter; stop the
+-      string before the latter. */
+-
+-      if (c == '\\')
+-        {
+-        const uschar *saveptr = ptr;
+-        c = check_escape(&ptr, errorptr, bracount, options, FALSE,
+-          &compile_block);
+-        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+-        if (c < 0) { ptr = saveptr; break; }
+-
+-#ifdef SUPPORT_UTF8
+-        if (c > 127 && (options & PCRE_UTF8) != 0)
+-          {
+-          int i;
+-          for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+-            if (c <= utf8_table1[i]) break;
+-          runlength += i;
+-          }
+-#endif
+-        }
+-
+-      /* Ordinary character or single-char escape */
+-
+-      runlength++;
+-      }
+-
+-    /* This "while" is the end of the "do" above. */
+-
+-    while (runlength < MAXLIT &&
+-      (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0);
+-
+-    ptr--;
+-    length += runlength;
+-    continue;
+-    }
+-  }
+-
+-length += 4;    /* For final KET and END */
+-
+-if (length > 65539)
+-  {
+-  *errorptr = ERR20;
+-  return NULL;
+-  }
+-
+-/* Compute the size of data block needed and get it, either from malloc or
+-externally provided function. We specify "code[0]" in the offsetof() expression
+-rather than just "code", because it has been reported that one broken compiler
+-fails on "code" because it is also an independent variable. It should make no
+-difference to the value of the offsetof(). */
+-
+-size = length + offsetof(real_pcre, code[0]);
+-re = (real_pcre *)(pcre_malloc)(size);
+-
+-if (re == NULL)
+-  {
+-  *errorptr = ERR21;
+-  return NULL;
+-  }
+-
+-/* size of re is sloppily computed, memset to get consistent output */
+-memset(re, 0, size);
+-
+-/* Put in the magic number, and save the size, options, and table pointer */
+-
+-re->magic_number = MAGIC_NUMBER;
+-re->size = size;
+-re->options = options;
+-re->tables = tables;
+-
+-/* Set up a starting, non-extracting bracket, then compile the expression. On
+-error, *errorptr will be set non-NULL, so we don't need to look at the result
+-of the function here. */
+-
+-ptr = (const uschar *)pattern;
+-code = re->code;
+-*code = OP_BRA;
+-bracount = 0;
+-(void)compile_regex(options, -1, &bracount, &code, &ptr, errorptr, FALSE, 0,
+-  &reqchar, &countlits, &compile_block);
+-re->top_bracket = bracount;
+-re->top_backref = top_backref;
+-
+-/* If not reached end of pattern on success, there's an excess bracket. */
+-
+-if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22;
+-
+-/* Fill in the terminating state and check for disastrous overflow, but
+-if debugging, leave the test till after things are printed out. */
+-
+-*code++ = OP_END;
+-
+-#ifndef DEBUG
+-if (code - re->code > length) *errorptr = ERR23;
+-#endif
+-
+-/* Give an error if there's back reference to a non-existent capturing
+-subpattern. */
+-
+-if (top_backref > re->top_bracket) *errorptr = ERR15;
+-
+-/* Failed to compile */
+-
+-if (*errorptr != NULL)
+-  {
+-  (pcre_free)(re);
+-  PCRE_ERROR_RETURN:
+-  *erroroffset = ptr - (const uschar *)pattern;
+-  return NULL;
+-  }
+-
+-/* If the anchored option was not passed, set flag if we can determine that the
+-pattern is anchored by virtue of ^ characters or \A or anything else (such as
+-starting with .* when DOTALL is set).
+-
+-Otherwise, see if we can determine what the first character has to be, because
+-that speeds up unanchored matches no end. If not, see if we can set the
+-PCRE_STARTLINE flag. This is helpful for multiline matches when all branches
+-start with ^. and also when all branches start with .* for non-DOTALL matches.
+-*/
+-
+-if ((options & PCRE_ANCHORED) == 0)
+-  {
+-  int temp_options = options;
+-  if (is_anchored(re->code, &temp_options))
+-    re->options |= PCRE_ANCHORED;
+-  else
+-    {
+-    int ch = find_firstchar(re->code, &temp_options);
+-    if (ch >= 0)
+-      {
+-      re->first_char = ch;
+-      re->options |= PCRE_FIRSTSET;
+-      }
+-    else if (is_startline(re->code))
+-      re->options |= PCRE_STARTLINE;
+-    }
+-  }
+-
+-/* Save the last required character if there are at least two literal
+-characters on all paths, or if there is no first character setting. */
+-
+-if (reqchar >= 0 && (countlits > 1 || (re->options & PCRE_FIRSTSET) == 0))
+-  {
+-  re->req_char = reqchar;
+-  re->options |= PCRE_REQCHSET;
+-  }
+-
+-/* Print out the compiled data for debugging */
+-
+-#ifdef DEBUG
+-
+-printf("Length = %d top_bracket = %d top_backref = %d\n",
+-  length, re->top_bracket, re->top_backref);
+-
+-if (re->options != 0)
+-  {
+-  printf("%s%s%s%s%s%s%s%s%s\n",
+-    ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "",
+-    ((re->options & PCRE_CASELESS) != 0)? "caseless " : "",
+-    ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "",
+-    ((re->options & PCRE_EXTENDED) != 0)? "extended " : "",
+-    ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "",
+-    ((re->options & PCRE_DOTALL) != 0)? "dotall " : "",
+-    ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "",
+-    ((re->options & PCRE_EXTRA) != 0)? "extra " : "",
+-    ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : "");
+-  }
+-
+-if ((re->options & PCRE_FIRSTSET) != 0)
+-  {
+-  if (isprint(re->first_char)) printf("First char = %c\n", re->first_char);
+-    else printf("First char = \\x%02x\n", re->first_char);
+-  }
+-
+-if ((re->options & PCRE_REQCHSET) != 0)
+-  {
+-  if (isprint(re->req_char)) printf("Req char = %c\n", re->req_char);
+-    else printf("Req char = \\x%02x\n", re->req_char);
+-  }
+-
+-code_end = code;
+-code_base = code = re->code;
+-
+-while (code < code_end)
+-  {
+-  int charlength;
+-
+-  printf("%3d ", code - code_base);
+-
+-  if (*code >= OP_BRA)
+-    {
+-    if (*code - OP_BRA > EXTRACT_BASIC_MAX)
+-      printf("%3d Bra extra", (code[1] << 8) + code[2]);
+-    else
+-      printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA);
+-    code += 2;
+-    }
+-
+-  else switch(*code)
+-    {
+-    case OP_OPT:
+-    printf(" %.2x %s", code[1], OP_names[*code]);
+-    code++;
+-    break;
+-
+-    case OP_CHARS:
+-    charlength = *(++code);
+-    printf("%3d ", charlength);
+-    while (charlength-- > 0)
+-      if (isprint(c = *(++code))) printf("%c", c); else printf("\\x%02x", c);
+-    break;
+-
+-    case OP_KETRMAX:
+-    case OP_KETRMIN:
+-    case OP_ALT:
+-    case OP_KET:
+-    case OP_ASSERT:
+-    case OP_ASSERT_NOT:
+-    case OP_ASSERTBACK:
+-    case OP_ASSERTBACK_NOT:
+-    case OP_ONCE:
+-    case OP_REVERSE:
+-    case OP_BRANUMBER:
+-    case OP_COND:
+-    case OP_CREF:
+-    printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]);
+-    code += 2;
+-    break;
+-
+-    case OP_STAR:
+-    case OP_MINSTAR:
+-    case OP_PLUS:
+-    case OP_MINPLUS:
+-    case OP_QUERY:
+-    case OP_MINQUERY:
+-    case OP_TYPESTAR:
+-    case OP_TYPEMINSTAR:
+-    case OP_TYPEPLUS:
+-    case OP_TYPEMINPLUS:
+-    case OP_TYPEQUERY:
+-    case OP_TYPEMINQUERY:
+-    if (*code >= OP_TYPESTAR)
+-      printf("    %s", OP_names[code[1]]);
+-    else if (isprint(c = code[1])) printf("    %c", c);
+-      else printf("    \\x%02x", c);
+-    printf("%s", OP_names[*code++]);
+-    break;
+-
+-    case OP_EXACT:
+-    case OP_UPTO:
+-    case OP_MINUPTO:
+-    if (isprint(c = code[3])) printf("    %c{", c);
+-      else printf("    \\x%02x{", c);
+-    if (*code != OP_EXACT) printf("0,");
+-    printf("%d}", (code[1] << 8) + code[2]);
+-    if (*code == OP_MINUPTO) printf("?");
+-    code += 3;
+-    break;
+-
+-    case OP_TYPEEXACT:
+-    case OP_TYPEUPTO:
+-    case OP_TYPEMINUPTO:
+-    printf("    %s{", OP_names[code[3]]);
+-    if (*code != OP_TYPEEXACT) printf(",");
+-    printf("%d}", (code[1] << 8) + code[2]);
+-    if (*code == OP_TYPEMINUPTO) printf("?");
+-    code += 3;
+-    break;
+-
+-    case OP_NOT:
+-    if (isprint(c = *(++code))) printf("    [^%c]", c);
+-      else printf("    [^\\x%02x]", c);
+-    break;
+-
+-    case OP_NOTSTAR:
+-    case OP_NOTMINSTAR:
+-    case OP_NOTPLUS:
+-    case OP_NOTMINPLUS:
+-    case OP_NOTQUERY:
+-    case OP_NOTMINQUERY:
+-    if (isprint(c = code[1])) printf("    [^%c]", c);
+-      else printf("    [^\\x%02x]", c);
+-    printf("%s", OP_names[*code++]);
+-    break;
+-
+-    case OP_NOTEXACT:
+-    case OP_NOTUPTO:
+-    case OP_NOTMINUPTO:
+-    if (isprint(c = code[3])) printf("    [^%c]{", c);
+-      else printf("    [^\\x%02x]{", c);
+-    if (*code != OP_NOTEXACT) printf(",");
+-    printf("%d}", (code[1] << 8) + code[2]);
+-    if (*code == OP_NOTMINUPTO) printf("?");
+-    code += 3;
+-    break;
+-
+-    case OP_REF:
+-    printf("    \\%d", (code[1] << 8) | code[2]);
+-    code += 3;
+-    goto CLASS_REF_REPEAT;
+-
+-    case OP_CLASS:
+-      {
+-      int i, min, max;
+-      code++;
+-      printf("    [");
+-
+-      for (i = 0; i < 256; i++)
+-        {
+-        if ((code[i/8] & (1 << (i&7))) != 0)
+-          {
+-          int j;
+-          for (j = i+1; j < 256; j++)
+-            if ((code[j/8] & (1 << (j&7))) == 0) break;
+-          if (i == '-' || i == ']') printf("\\");
+-          if (isprint(i)) printf("%c", i); else printf("\\x%02x", i);
+-          if (--j > i)
+-            {
+-            printf("-");
+-            if (j == '-' || j == ']') printf("\\");
+-            if (isprint(j)) printf("%c", j); else printf("\\x%02x", j);
+-            }
+-          i = j;
+-          }
+-        }
+-      printf("]");
+-      code += 32;
+-
+-      CLASS_REF_REPEAT:
+-
+-      switch(*code)
+-        {
+-        case OP_CRSTAR:
+-        case OP_CRMINSTAR:
+-        case OP_CRPLUS:
+-        case OP_CRMINPLUS:
+-        case OP_CRQUERY:
+-        case OP_CRMINQUERY:
+-        printf("%s", OP_names[*code]);
+-        break;
+-
+-        case OP_CRRANGE:
+-        case OP_CRMINRANGE:
+-        min = (code[1] << 8) + code[2];
+-        max = (code[3] << 8) + code[4];
+-        if (max == 0) printf("{%d,}", min);
+-        else printf("{%d,%d}", min, max);
+-        if (*code == OP_CRMINRANGE) printf("?");
+-        code += 4;
+-        break;
+-
+-        default:
+-        code--;
+-        }
+-      }
+-    break;
+-
+-    /* Anything else is just a one-node item */
+-
+-    default:
+-    printf("    %s", OP_names[*code]);
+-    break;
+-    }
+-
+-  code++;
+-  printf("\n");
+-  }
+-printf("------------------------------------------------------------------\n");
+-
+-/* This check is done here in the debugging case so that the code that
+-was compiled can be seen. */
+-
+-if (code - re->code > length)
+-  {
+-  *errorptr = ERR23;
+-  (pcre_free)(re);
+-  *erroroffset = ptr - (uschar *)pattern;
+-  return NULL;
+-  }
+-#endif
+-
+-return (pcre *)re;
+-}
+-
+-
+-
+-/*************************************************
+-*          Match a back-reference                *
+-*************************************************/
+-
+-/* If a back reference hasn't been set, the length that is passed is greater
+-than the number of characters left in the string, so the match fails.
+-
+-Arguments:
+-  offset      index into the offset vector
+-  eptr        points into the subject
+-  length      length to be matched
+-  md          points to match data block
+-  ims         the ims flags
+-
+-Returns:      TRUE if matched
+-*/
+-
+-static BOOL
+-match_ref(int offset, register const uschar *eptr, int length, match_data *md,
+-  unsigned long int ims)
+-{
+-const uschar *p = md->start_subject + md->offset_vector[offset];
+-
+-#ifdef DEBUG
+-if (eptr >= md->end_subject)
+-  printf("matching subject <null>");
+-else
+-  {
+-  printf("matching subject ");
+-  pchars(eptr, length, TRUE, md);
+-  }
+-printf(" against backref ");
+-pchars(p, length, FALSE, md);
+-printf("\n");
+-#endif
+-
+-/* Always fail if not enough characters left */
+-
+-if (length > md->end_subject - eptr) return FALSE;
+-
+-/* Separate the caselesss case for speed */
+-
+-if ((ims & PCRE_CASELESS) != 0)
+-  {
+-  while (length-- > 0)
+-    if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE;
+-  }
+-else
+-  { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
+-
+-return TRUE;
+-}
+-
+-
+-
+-/*************************************************
+-*         Match from current position            *
+-*************************************************/
+-
+-/* On entry ecode points to the first opcode, and eptr to the first character
+-in the subject string, while eptrb holds the value of eptr at the start of the
+-last bracketed group - used for breaking infinite loops matching zero-length
+-strings.
+-
+-Arguments:
+-   eptr        pointer in subject
+-   ecode       position in code
+-   offset_top  current top pointer
+-   md          pointer to "static" info for the match
+-   ims         current /i, /m, and /s options
+-   eptrb       pointer to chain of blocks containing eptr at start of
+-                 brackets - for testing for empty matches
+-   flags       can contain
+-                 match_condassert - this is an assertion condition
+-                 match_isgroup - this is the start of a bracketed group
+-
+-Returns:       TRUE if matched
+-*/
+-
+-static BOOL
+-match(register const uschar *eptr, register const uschar *ecode,
+-  int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb,
+-  int flags)
+-{
+-unsigned long int original_ims = ims;   /* Save for resetting on ')' */
+-eptrblock newptrb;
+-
+-/* At the start of a bracketed group, add the current subject pointer to the
+-stack of such pointers, to be re-instated at the end of the group when we hit
+-the closing ket. When match() is called in other circumstances, we don't add to
+-the stack. */
+-
+-if ((flags & match_isgroup) != 0)
+-  {
+-  newptrb.prev = eptrb;
+-  newptrb.saved_eptr = eptr;
+-  eptrb = &newptrb;
+-  }
+-
+-/* Now start processing the operations. */
+-
+-for (;;)
+-  {
+-  int op = (int)*ecode;
+-  int min, max, ctype;
+-  register int i;
+-  register int c;
+-  BOOL minimize = FALSE;
+-
+-  /* Opening capturing bracket. If there is space in the offset vector, save
+-  the current subject position in the working slot at the top of the vector. We
+-  mustn't change the current values of the data slot, because they may be set
+-  from a previous iteration of this group, and be referred to by a reference
+-  inside the group.
+-
+-  If the bracket fails to match, we need to restore this value and also the
+-  values of the final offsets, in case they were set by a previous iteration of
+-  the same bracket.
+-
+-  If there isn't enough space in the offset vector, treat this as if it were a
+-  non-capturing bracket. Don't worry about setting the flag for the error case
+-  here; that is handled in the code for KET. */
+-
+-  if (op > OP_BRA)
+-    {
+-    int offset;
+-    int number = op - OP_BRA;
+-
+-    /* For extended extraction brackets (large number), we have to fish out the
+-    number from a dummy opcode at the start. */
+-
+-    if (number > EXTRACT_BASIC_MAX) number = (ecode[4] << 8) | ecode[5];
+-    offset = number << 1;
+-
+-#ifdef DEBUG
+-    printf("start bracket %d subject=", number);
+-    pchars(eptr, 16, TRUE, md);
+-    printf("\n");
+-#endif
+-
+-    if (offset < md->offset_max)
+-      {
+-      int save_offset1 = md->offset_vector[offset];
+-      int save_offset2 = md->offset_vector[offset+1];
+-      int save_offset3 = md->offset_vector[md->offset_end - number];
+-
+-      DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
+-      md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
+-
+-      do
+-        {
+-        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
+-          return TRUE;
+-        ecode += (ecode[1] << 8) + ecode[2];
+-        }
+-      while (*ecode == OP_ALT);
+-
+-      DPRINTF(("bracket %d failed\n", number));
+-
+-      md->offset_vector[offset] = save_offset1;
+-      md->offset_vector[offset+1] = save_offset2;
+-      md->offset_vector[md->offset_end - number] = save_offset3;
+-
+-      return FALSE;
+-      }
+-
+-    /* Insufficient room for saving captured contents */
+-
+-    else op = OP_BRA;
+-    }
+-
+-  /* Other types of node can be handled by a switch */
+-
+-  switch(op)
+-    {
+-    case OP_BRA:     /* Non-capturing bracket: optimized */
+-    DPRINTF(("start bracket 0\n"));
+-    do
+-      {
+-      if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
+-        return TRUE;
+-      ecode += (ecode[1] << 8) + ecode[2];
+-      }
+-    while (*ecode == OP_ALT);
+-    DPRINTF(("bracket 0 failed\n"));
+-    return FALSE;
+-
+-    /* Conditional group: compilation checked that there are no more than
+-    two branches. If the condition is false, skipping the first branch takes us
+-    past the end if there is only one branch, but that's OK because that is
+-    exactly what going to the ket would do. */
+-
+-    case OP_COND:
+-    if (ecode[3] == OP_CREF)         /* Condition is extraction test */
+-      {
+-      int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */
+-      return match(eptr,
+-        ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)?
+-          6 : 3 + (ecode[1] << 8) + ecode[2]),
+-        offset_top, md, ims, eptrb, match_isgroup);
+-      }
+-
+-    /* The condition is an assertion. Call match() to evaluate it - setting
+-    the final argument TRUE causes it to stop at the end of an assertion. */
+-
+-    else
+-      {
+-      if (match(eptr, ecode+3, offset_top, md, ims, NULL,
+-          match_condassert | match_isgroup))
+-        {
+-        ecode += 3 + (ecode[4] << 8) + ecode[5];
+-        while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2];
+-        }
+-      else ecode += (ecode[1] << 8) + ecode[2];
+-      return match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup);
+-      }
+-    /* Control never reaches here */
+-
+-    /* Skip over conditional reference or large extraction number data if
+-    encountered. */
+-
+-    case OP_CREF:
+-    case OP_BRANUMBER:
+-    ecode += 3;
+-    break;
+-
+-    /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched
+-    an empty string - recursion will then try other alternatives, if any. */
+-
+-    case OP_END:
+-    if (md->notempty && eptr == md->start_match) return FALSE;
+-    md->end_match_ptr = eptr;          /* Record where we ended */
+-    md->end_offset_top = offset_top;   /* and how many extracts were taken */
+-    return TRUE;
+-
+-    /* Change option settings */
+-
+-    case OP_OPT:
+-    ims = ecode[1];
+-    ecode += 2;
+-    DPRINTF(("ims set to %02lx\n", ims));
+-    break;
+-
+-    /* Assertion brackets. Check the alternative branches in turn - the
+-    matching won't pass the KET for an assertion. If any one branch matches,
+-    the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
+-    start of each branch to move the current point backwards, so the code at
+-    this level is identical to the lookahead case. */
+-
+-    case OP_ASSERT:
+-    case OP_ASSERTBACK:
+-    do
+-      {
+-      if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) break;
+-      ecode += (ecode[1] << 8) + ecode[2];
+-      }
+-    while (*ecode == OP_ALT);
+-    if (*ecode == OP_KET) return FALSE;
+-
+-    /* If checking an assertion for a condition, return TRUE. */
+-
+-    if ((flags & match_condassert) != 0) return TRUE;
+-
+-    /* Continue from after the assertion, updating the offsets high water
+-    mark, since extracts may have been taken during the assertion. */
+-
+-    do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
+-    ecode += 3;
+-    offset_top = md->end_offset_top;
+-    continue;
+-
+-    /* Negative assertion: all branches must fail to match */
+-
+-    case OP_ASSERT_NOT:
+-    case OP_ASSERTBACK_NOT:
+-    do
+-      {
+-      if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup))
+-        return FALSE;
+-      ecode += (ecode[1] << 8) + ecode[2];
+-      }
+-    while (*ecode == OP_ALT);
+-
+-    if ((flags & match_condassert) != 0) return TRUE;
+-
+-    ecode += 3;
+-    continue;
+-
+-    /* Move the subject pointer back. This occurs only at the start of
+-    each branch of a lookbehind assertion. If we are too close to the start to
+-    move back, this match function fails. When working with UTF-8 we move
+-    back a number of characters, not bytes. */
+-
+-    case OP_REVERSE:
+-#ifdef SUPPORT_UTF8
+-    c = (ecode[1] << 8) + ecode[2];
+-    for (i = 0; i < c; i++)
+-      {
+-      eptr--;
+-      BACKCHAR(eptr)
+-      }
+-#else
+-    eptr -= (ecode[1] << 8) + ecode[2];
+-#endif
+-
+-    if (eptr < md->start_subject) return FALSE;
+-    ecode += 3;
+-    break;
+-
+-    /* Recursion matches the current regex, nested. If there are any capturing
+-    brackets started but not finished, we have to save their starting points
+-    and reinstate them after the recursion. However, we don't know how many
+-    such there are (offset_top records the completed total) so we just have
+-    to save all the potential data. There may be up to 99 such values, which
+-    is a bit large to put on the stack, but using malloc for small numbers
+-    seems expensive. As a compromise, the stack is used when there are fewer
+-    than 16 values to store; otherwise malloc is used. A problem is what to do
+-    if the malloc fails ... there is no way of returning to the top level with
+-    an error. Save the top 15 values on the stack, and accept that the rest
+-    may be wrong. */
+-
+-    case OP_RECURSE:
+-      {
+-      BOOL rc;
+-      int *save;
+-      int stacksave[15];
+-
+-      c = md->offset_max;
+-
+-      if (c < 16) save = stacksave; else
+-        {
+-        save = (int *)(pcre_malloc)((c+1) * sizeof(int));
+-        if (save == NULL)
+-          {
+-          save = stacksave;
+-          c = 15;
+-          }
+-        }
+-
+-      for (i = 1; i <= c; i++)
+-        save[i] = md->offset_vector[md->offset_end - i];
+-      rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb,
+-        match_isgroup);
+-      for (i = 1; i <= c; i++)
+-        md->offset_vector[md->offset_end - i] = save[i];
+-      if (save != stacksave) (pcre_free)(save);
+-      if (!rc) return FALSE;
+-
+-      /* In case the recursion has set more capturing values, save the final
+-      number, then move along the subject till after the recursive match,
+-      and advance one byte in the pattern code. */
+-
+-      offset_top = md->end_offset_top;
+-      eptr = md->end_match_ptr;
+-      ecode++;
+-      }
+-    break;
+-
+-    /* "Once" brackets are like assertion brackets except that after a match,
+-    the point in the subject string is not moved back. Thus there can never be
+-    a move back into the brackets. Check the alternative branches in turn - the
+-    matching won't pass the KET for this kind of subpattern. If any one branch
+-    matches, we carry on as at the end of a normal bracket, leaving the subject
+-    pointer. */
+-
+-    case OP_ONCE:
+-      {
+-      const uschar *prev = ecode;
+-      const uschar *saved_eptr = eptr;
+-
+-      do
+-        {
+-        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
+-          break;
+-        ecode += (ecode[1] << 8) + ecode[2];
+-        }
+-      while (*ecode == OP_ALT);
+-
+-      /* If hit the end of the group (which could be repeated), fail */
+-
+-      if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE;
+-
+-      /* Continue as from after the assertion, updating the offsets high water
+-      mark, since extracts may have been taken. */
+-
+-      do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
+-
+-      offset_top = md->end_offset_top;
+-      eptr = md->end_match_ptr;
+-
+-      /* For a non-repeating ket, just continue at this level. This also
+-      happens for a repeating ket if no characters were matched in the group.
+-      This is the forcible breaking of infinite loops as implemented in Perl
+-      5.005. If there is an options reset, it will get obeyed in the normal
+-      course of events. */
+-
+-      if (*ecode == OP_KET || eptr == saved_eptr)
+-        {
+-        ecode += 3;
+-        break;
+-        }
+-
+-      /* The repeating kets try the rest of the pattern or restart from the
+-      preceding bracket, in the appropriate order. We need to reset any options
+-      that changed within the bracket before re-running it, so check the next
+-      opcode. */
+-
+-      if (ecode[3] == OP_OPT)
+-        {
+-        ims = (ims & ~PCRE_IMS) | ecode[4];
+-        DPRINTF(("ims set to %02lx at group repeat\n", ims));
+-        }
+-
+-      if (*ecode == OP_KETRMIN)
+-        {
+-        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
+-            match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
+-              return TRUE;
+-        }
+-      else  /* OP_KETRMAX */
+-        {
+-        if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
+-            match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
+-        }
+-      }
+-    return FALSE;
+-
+-    /* An alternation is the end of a branch; scan along to find the end of the
+-    bracketed group and go to there. */
+-
+-    case OP_ALT:
+-    do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
+-    break;
+-
+-    /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating
+-    that it may occur zero times. It may repeat infinitely, or not at all -
+-    i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper
+-    repeat limits are compiled as a number of copies, with the optional ones
+-    preceded by BRAZERO or BRAMINZERO. */
+-
+-    case OP_BRAZERO:
+-      {
+-      const uschar *next = ecode+1;
+-      if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup))
+-        return TRUE;
+-      do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
+-      ecode = next + 3;
+-      }
+-    break;
+-
+-    case OP_BRAMINZERO:
+-      {
+-      const uschar *next = ecode+1;
+-      do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
+-      if (match(eptr, next+3, offset_top, md, ims, eptrb, match_isgroup))
+-        return TRUE;
+-      ecode++;
+-      }
+-    break;
+-
+-    /* End of a group, repeated or non-repeating. If we are at the end of
+-    an assertion "group", stop matching and return TRUE, but record the
+-    current high water mark for use by positive assertions. Do this also
+-    for the "once" (not-backup up) groups. */
+-
+-    case OP_KET:
+-    case OP_KETRMIN:
+-    case OP_KETRMAX:
+-      {
+-      const uschar *prev = ecode - (ecode[1] << 8) - ecode[2];
+-      const uschar *saved_eptr = eptrb->saved_eptr;
+-
+-      eptrb = eptrb->prev;    /* Back up the stack of bracket start pointers */
+-
+-      if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
+-          *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
+-          *prev == OP_ONCE)
+-        {
+-        md->end_match_ptr = eptr;      /* For ONCE */
+-        md->end_offset_top = offset_top;
+-        return TRUE;
+-        }
+-
+-      /* In all other cases except a conditional group we have to check the
+-      group number back at the start and if necessary complete handling an
+-      extraction by setting the offsets and bumping the high water mark. */
+-
+-      if (*prev != OP_COND)
+-        {
+-        int offset;
+-        int number = *prev - OP_BRA;
+-
+-        /* For extended extraction brackets (large number), we have to fish out
+-        the number from a dummy opcode at the start. */
+-
+-        if (number > EXTRACT_BASIC_MAX) number = (prev[4] << 8) | prev[5];
+-        offset = number << 1;
+-
+-#ifdef DEBUG
+-        printf("end bracket %d", number);
+-        printf("\n");
+-#endif
+-
+-        if (number > 0)
+-          {
+-          if (offset >= md->offset_max) md->offset_overflow = TRUE; else
+-            {
+-            md->offset_vector[offset] =
+-              md->offset_vector[md->offset_end - number];
+-            md->offset_vector[offset+1] = eptr - md->start_subject;
+-            if (offset_top <= offset) offset_top = offset + 2;
+-            }
+-          }
+-        }
+-
+-      /* Reset the value of the ims flags, in case they got changed during
+-      the group. */
+-
+-      ims = original_ims;
+-      DPRINTF(("ims reset to %02lx\n", ims));
+-
+-      /* For a non-repeating ket, just continue at this level. This also
+-      happens for a repeating ket if no characters were matched in the group.
+-      This is the forcible breaking of infinite loops as implemented in Perl
+-      5.005. If there is an options reset, it will get obeyed in the normal
+-      course of events. */
+-
+-      if (*ecode == OP_KET || eptr == saved_eptr)
+-        {
+-        ecode += 3;
+-        break;
+-        }
+-
+-      /* The repeating kets try the rest of the pattern or restart from the
+-      preceding bracket, in the appropriate order. */
+-
+-      if (*ecode == OP_KETRMIN)
+-        {
+-        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
+-            match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
+-              return TRUE;
+-        }
+-      else  /* OP_KETRMAX */
+-        {
+-        if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
+-            match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
+-        }
+-      }
+-    return FALSE;
+-
+-    /* Start of subject unless notbol, or after internal newline if multiline */
+-
+-    case OP_CIRC:
+-    if (md->notbol && eptr == md->start_subject) return FALSE;
+-    if ((ims & PCRE_MULTILINE) != 0)
+-      {
+-      if (eptr != md->start_subject && eptr[-1] != NEWLINE) return FALSE;
+-      ecode++;
+-      break;
+-      }
+-    /* ... else fall through */
+-
+-    /* Start of subject assertion */
+-
+-    case OP_SOD:
+-    if (eptr != md->start_subject) return FALSE;
+-    ecode++;
+-    break;
+-
+-    /* Assert before internal newline if multiline, or before a terminating
+-    newline unless endonly is set, else end of subject unless noteol is set. */
+-
+-    case OP_DOLL:
+-    if ((ims & PCRE_MULTILINE) != 0)
+-      {
+-      if (eptr < md->end_subject) { if (*eptr != NEWLINE) return FALSE; }
+-        else { if (md->noteol) return FALSE; }
+-      ecode++;
+-      break;
+-      }
+-    else
+-      {
+-      if (md->noteol) return FALSE;
+-      if (!md->endonly)
+-        {
+-        if (eptr < md->end_subject - 1 ||
+-           (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
+-
+-        ecode++;
+-        break;
+-        }
+-      }
+-    /* ... else fall through */
+-
+-    /* End of subject assertion (\z) */
+-
+-    case OP_EOD:
+-    if (eptr < md->end_subject) return FALSE;
+-    ecode++;
+-    break;
+-
+-    /* End of subject or ending \n assertion (\Z) */
+-
+-    case OP_EODN:
+-    if (eptr < md->end_subject - 1 ||
+-       (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
+-    ecode++;
+-    break;
+-
+-    /* Word boundary assertions */
+-
+-    case OP_NOT_WORD_BOUNDARY:
+-    case OP_WORD_BOUNDARY:
+-      {
+-      BOOL prev_is_word = (eptr != md->start_subject) &&
+-        ((md->ctypes[eptr[-1]] & ctype_word) != 0);
+-      BOOL cur_is_word = (eptr < md->end_subject) &&
+-        ((md->ctypes[*eptr] & ctype_word) != 0);
+-      if ((*ecode++ == OP_WORD_BOUNDARY)?
+-           cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+-        return FALSE;
+-      }
+-    break;
+-
+-    /* Match a single character type; inline for speed */
+-
+-    case OP_ANY:
+-    if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE)
+-      return FALSE;
+-    if (eptr++ >= md->end_subject) return FALSE;
+-#ifdef SUPPORT_UTF8
+-    if (md->utf8)
+-      while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+-#endif
+-    ecode++;
+-    break;
+-
+-    case OP_NOT_DIGIT:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_digit) != 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    case OP_DIGIT:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_digit) == 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    case OP_NOT_WHITESPACE:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_space) != 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    case OP_WHITESPACE:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_space) == 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    case OP_NOT_WORDCHAR:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_word) != 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    case OP_WORDCHAR:
+-    if (eptr >= md->end_subject ||
+-       (md->ctypes[*eptr++] & ctype_word) == 0)
+-      return FALSE;
+-    ecode++;
+-    break;
+-
+-    /* Match a back reference, possibly repeatedly. Look past the end of the
+-    item to see if there is repeat information following. The code is similar
+-    to that for character classes, but repeated for efficiency. Then obey
+-    similar code to character type repeats - written out again for speed.
+-    However, if the referenced string is the empty string, always treat
+-    it as matched, any number of times (otherwise there could be infinite
+-    loops). */
+-
+-    case OP_REF:
+-      {
+-      int length;
+-      int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */
+-      ecode += 3;                                     /* Advance past item */
+-
+-      /* If the reference is unset, set the length to be longer than the amount
+-      of subject left; this ensures that every attempt at a match fails. We
+-      can't just fail here, because of the possibility of quantifiers with zero
+-      minima. */
+-
+-      length = (offset >= offset_top || md->offset_vector[offset] < 0)?
+-        md->end_subject - eptr + 1 :
+-        md->offset_vector[offset+1] - md->offset_vector[offset];
+-
+-      /* Set up for repetition, or handle the non-repeated case */
+-
+-      switch (*ecode)
+-        {
+-        case OP_CRSTAR:
+-        case OP_CRMINSTAR:
+-        case OP_CRPLUS:
+-        case OP_CRMINPLUS:
+-        case OP_CRQUERY:
+-        case OP_CRMINQUERY:
+-        c = *ecode++ - OP_CRSTAR;
+-        minimize = (c & 1) != 0;
+-        min = rep_min[c];                 /* Pick up values from tables; */
+-        max = rep_max[c];                 /* zero for max => infinity */
+-        if (max == 0) max = INT_MAX;
+-        break;
+-
+-        case OP_CRRANGE:
+-        case OP_CRMINRANGE:
+-        minimize = (*ecode == OP_CRMINRANGE);
+-        min = (ecode[1] << 8) + ecode[2];
+-        max = (ecode[3] << 8) + ecode[4];
+-        if (max == 0) max = INT_MAX;
+-        ecode += 5;
+-        break;
+-
+-        default:               /* No repeat follows */
+-        if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
+-        eptr += length;
+-        continue;              /* With the main loop */
+-        }
+-
+-      /* If the length of the reference is zero, just continue with the
+-      main loop. */
+-
+-      if (length == 0) continue;
+-
+-      /* First, ensure the minimum number of matches are present. We get back
+-      the length of the reference string explicitly rather than passing the
+-      address of eptr, so that eptr can be a register variable. */
+-
+-      for (i = 1; i <= min; i++)
+-        {
+-        if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
+-        eptr += length;
+-        }
+-
+-      /* If min = max, continue at the same level without recursion.
+-      They are not both allowed to be zero. */
+-
+-      if (min == max) continue;
+-
+-      /* If minimizing, keep trying and advancing the pointer */
+-
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || !match_ref(offset, eptr, length, md, ims))
+-            return FALSE;
+-          eptr += length;
+-          }
+-        /* Control never gets here */
+-        }
+-
+-      /* If maximizing, find the longest string and work backwards */
+-
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        for (i = min; i < max; i++)
+-          {
+-          if (!match_ref(offset, eptr, length, md, ims)) break;
+-          eptr += length;
+-          }
+-        while (eptr >= pp)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          eptr -= length;
+-          }
+-        return FALSE;
+-        }
+-      }
+-    /* Control never gets here */
+-
+-
+-
+-    /* Match a character class, possibly repeatedly. Look past the end of the
+-    item to see if there is repeat information following. Then obey similar
+-    code to character type repeats - written out again for speed. */
+-
+-    case OP_CLASS:
+-      {
+-      const uschar *data = ecode + 1;  /* Save for matching */
+-      ecode += 33;                     /* Advance past the item */
+-
+-      switch (*ecode)
+-        {
+-        case OP_CRSTAR:
+-        case OP_CRMINSTAR:
+-        case OP_CRPLUS:
+-        case OP_CRMINPLUS:
+-        case OP_CRQUERY:
+-        case OP_CRMINQUERY:
+-        c = *ecode++ - OP_CRSTAR;
+-        minimize = (c & 1) != 0;
+-        min = rep_min[c];                 /* Pick up values from tables; */
+-        max = rep_max[c];                 /* zero for max => infinity */
+-        if (max == 0) max = INT_MAX;
+-        break;
+-
+-        case OP_CRRANGE:
+-        case OP_CRMINRANGE:
+-        minimize = (*ecode == OP_CRMINRANGE);
+-        min = (ecode[1] << 8) + ecode[2];
+-        max = (ecode[3] << 8) + ecode[4];
+-        if (max == 0) max = INT_MAX;
+-        ecode += 5;
+-        break;
+-
+-        default:               /* No repeat follows */
+-        min = max = 1;
+-        break;
+-        }
+-
+-      /* First, ensure the minimum number of matches are present. */
+-
+-      for (i = 1; i <= min; i++)
+-        {
+-        if (eptr >= md->end_subject) return FALSE;
+-        GETCHARINC(c, eptr)         /* Get character; increment eptr */
+-
+-#ifdef SUPPORT_UTF8
+-        /* We do not yet support class members > 255 */
+-        if (c > 255) return FALSE;
+-#endif
+-
+-        if ((data[c/8] & (1 << (c&7))) != 0) continue;
+-        return FALSE;
+-        }
+-
+-      /* If max == min we can continue with the main loop without the
+-      need to recurse. */
+-
+-      if (min == max) continue;
+-
+-      /* If minimizing, keep testing the rest of the expression and advancing
+-      the pointer while it matches the class. */
+-
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || eptr >= md->end_subject) return FALSE;
+-          GETCHARINC(c, eptr)       /* Get character; increment eptr */
+-
+-#ifdef SUPPORT_UTF8
+-          /* We do not yet support class members > 255 */
+-          if (c > 255) return FALSE;
+-#endif
+-          if ((data[c/8] & (1 << (c&7))) != 0) continue;
+-          return FALSE;
+-          }
+-        /* Control never gets here */
+-        }
+-
+-      /* If maximizing, find the longest possible run, then work backwards. */
+-
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        int len = 1;
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject) break;
+-          GETCHARLEN(c, eptr, len)  /* Get character, set length if UTF-8 */
+-
+-#ifdef SUPPORT_UTF8
+-          /* We do not yet support class members > 255 */
+-          if (c > 255) break;
+-#endif
+-          if ((data[c/8] & (1 << (c&7))) == 0) break;
+-          eptr += len;
+-          }
+-
+-        while (eptr >= pp)
+-          {
+-          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-
+-#ifdef SUPPORT_UTF8
+-          BACKCHAR(eptr)
+-#endif
+-          }
+-        return FALSE;
+-        }
+-      }
+-    /* Control never gets here */
+-
+-    /* Match a run of characters */
+-
+-    case OP_CHARS:
+-      {
+-      register int length = ecode[1];
+-      ecode += 2;
+-
+-#ifdef DEBUG    /* Sigh. Some compilers never learn. */
+-      if (eptr >= md->end_subject)
+-        printf("matching subject <null> against pattern ");
+-      else
+-        {
+-        printf("matching subject ");
+-        pchars(eptr, length, TRUE, md);
+-        printf(" against pattern ");
+-        }
+-      pchars(ecode, length, FALSE, md);
+-      printf("\n");
+-#endif
+-
+-      if (length > md->end_subject - eptr) return FALSE;
+-      if ((ims & PCRE_CASELESS) != 0)
+-        {
+-        while (length-- > 0)
+-          if (md->lcc[*ecode++] != md->lcc[*eptr++])
+-            return FALSE;
+-        }
+-      else
+-        {
+-        while (length-- > 0) if (*ecode++ != *eptr++) return FALSE;
+-        }
+-      }
+-    break;
+-
+-    /* Match a single character repeatedly; different opcodes share code. */
+-
+-    case OP_EXACT:
+-    min = max = (ecode[1] << 8) + ecode[2];
+-    ecode += 3;
+-    goto REPEATCHAR;
+-
+-    case OP_UPTO:
+-    case OP_MINUPTO:
+-    min = 0;
+-    max = (ecode[1] << 8) + ecode[2];
+-    minimize = *ecode == OP_MINUPTO;
+-    ecode += 3;
+-    goto REPEATCHAR;
+-
+-    case OP_STAR:
+-    case OP_MINSTAR:
+-    case OP_PLUS:
+-    case OP_MINPLUS:
+-    case OP_QUERY:
+-    case OP_MINQUERY:
+-    c = *ecode++ - OP_STAR;
+-    minimize = (c & 1) != 0;
+-    min = rep_min[c];                 /* Pick up values from tables; */
+-    max = rep_max[c];                 /* zero for max => infinity */
+-    if (max == 0) max = INT_MAX;
+-
+-    /* Common code for all repeated single-character matches. We can give
+-    up quickly if there are fewer than the minimum number of characters left in
+-    the subject. */
+-
+-    REPEATCHAR:
+-    if (min > md->end_subject - eptr) return FALSE;
+-    c = *ecode++;
+-
+-    /* The code is duplicated for the caseless and caseful cases, for speed,
+-    since matching characters is likely to be quite common. First, ensure the
+-    minimum number of matches are present. If min = max, continue at the same
+-    level without recursing. Otherwise, if minimizing, keep trying the rest of
+-    the expression and advancing one matching character if failing, up to the
+-    maximum. Alternatively, if maximizing, find the maximum number of
+-    characters and work backwards. */
+-
+-    DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max,
+-      max, eptr));
+-
+-    if ((ims & PCRE_CASELESS) != 0)
+-      {
+-      c = md->lcc[c];
+-      for (i = 1; i <= min; i++)
+-        if (c != md->lcc[*eptr++]) return FALSE;
+-      if (min == max) continue;
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || eptr >= md->end_subject ||
+-              c != md->lcc[*eptr++])
+-            return FALSE;
+-          }
+-        /* Control never gets here */
+-        }
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || c != md->lcc[*eptr]) break;
+-          eptr++;
+-          }
+-        while (eptr >= pp)
+-          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-        return FALSE;
+-        }
+-      /* Control never gets here */
+-      }
+-
+-    /* Caseful comparisons */
+-
+-    else
+-      {
+-      for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE;
+-      if (min == max) continue;
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE;
+-          }
+-        /* Control never gets here */
+-        }
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || c != *eptr) break;
+-          eptr++;
+-          }
+-        while (eptr >= pp)
+-         if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-           return TRUE;
+-        return FALSE;
+-        }
+-      }
+-    /* Control never gets here */
+-
+-    /* Match a negated single character */
+-
+-    case OP_NOT:
+-    if (eptr >= md->end_subject) return FALSE;
+-    ecode++;
+-    if ((ims & PCRE_CASELESS) != 0)
+-      {
+-      if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE;
+-      }
+-    else
+-      {
+-      if (*ecode++ == *eptr++) return FALSE;
+-      }
+-    break;
+-
+-    /* Match a negated single character repeatedly. This is almost a repeat of
+-    the code for a repeated single character, but I haven't found a nice way of
+-    commoning these up that doesn't require a test of the positive/negative
+-    option for each character match. Maybe that wouldn't add very much to the
+-    time taken, but character matching *is* what this is all about... */
+-
+-    case OP_NOTEXACT:
+-    min = max = (ecode[1] << 8) + ecode[2];
+-    ecode += 3;
+-    goto REPEATNOTCHAR;
+-
+-    case OP_NOTUPTO:
+-    case OP_NOTMINUPTO:
+-    min = 0;
+-    max = (ecode[1] << 8) + ecode[2];
+-    minimize = *ecode == OP_NOTMINUPTO;
+-    ecode += 3;
+-    goto REPEATNOTCHAR;
+-
+-    case OP_NOTSTAR:
+-    case OP_NOTMINSTAR:
+-    case OP_NOTPLUS:
+-    case OP_NOTMINPLUS:
+-    case OP_NOTQUERY:
+-    case OP_NOTMINQUERY:
+-    c = *ecode++ - OP_NOTSTAR;
+-    minimize = (c & 1) != 0;
+-    min = rep_min[c];                 /* Pick up values from tables; */
+-    max = rep_max[c];                 /* zero for max => infinity */
+-    if (max == 0) max = INT_MAX;
+-
+-    /* Common code for all repeated single-character matches. We can give
+-    up quickly if there are fewer than the minimum number of characters left in
+-    the subject. */
+-
+-    REPEATNOTCHAR:
+-    if (min > md->end_subject - eptr) return FALSE;
+-    c = *ecode++;
+-
+-    /* The code is duplicated for the caseless and caseful cases, for speed,
+-    since matching characters is likely to be quite common. First, ensure the
+-    minimum number of matches are present. If min = max, continue at the same
+-    level without recursing. Otherwise, if minimizing, keep trying the rest of
+-    the expression and advancing one matching character if failing, up to the
+-    maximum. Alternatively, if maximizing, find the maximum number of
+-    characters and work backwards. */
+-
+-    DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max,
+-      max, eptr));
+-
+-    if ((ims & PCRE_CASELESS) != 0)
+-      {
+-      c = md->lcc[c];
+-      for (i = 1; i <= min; i++)
+-        if (c == md->lcc[*eptr++]) return FALSE;
+-      if (min == max) continue;
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || eptr >= md->end_subject ||
+-              c == md->lcc[*eptr++])
+-            return FALSE;
+-          }
+-        /* Control never gets here */
+-        }
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || c == md->lcc[*eptr]) break;
+-          eptr++;
+-          }
+-        while (eptr >= pp)
+-          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-        return FALSE;
+-        }
+-      /* Control never gets here */
+-      }
+-
+-    /* Caseful comparisons */
+-
+-    else
+-      {
+-      for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE;
+-      if (min == max) continue;
+-      if (minimize)
+-        {
+-        for (i = min;; i++)
+-          {
+-          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
+-            return TRUE;
+-          if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE;
+-          }
+-        /* Control never gets here */
+-        }
+-      else
+-        {
+-        const uschar *pp = eptr;
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || c == *eptr) break;
+-          eptr++;
+-          }
+-        while (eptr >= pp)
+-         if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-           return TRUE;
+-        return FALSE;
+-        }
+-      }
+-    /* Control never gets here */
+-
+-    /* Match a single character type repeatedly; several different opcodes
+-    share code. This is very similar to the code for single characters, but we
+-    repeat it in the interests of efficiency. */
+-
+-    case OP_TYPEEXACT:
+-    min = max = (ecode[1] << 8) + ecode[2];
+-    minimize = TRUE;
+-    ecode += 3;
+-    goto REPEATTYPE;
+-
+-    case OP_TYPEUPTO:
+-    case OP_TYPEMINUPTO:
+-    min = 0;
+-    max = (ecode[1] << 8) + ecode[2];
+-    minimize = *ecode == OP_TYPEMINUPTO;
+-    ecode += 3;
+-    goto REPEATTYPE;
+-
+-    case OP_TYPESTAR:
+-    case OP_TYPEMINSTAR:
+-    case OP_TYPEPLUS:
+-    case OP_TYPEMINPLUS:
+-    case OP_TYPEQUERY:
+-    case OP_TYPEMINQUERY:
+-    c = *ecode++ - OP_TYPESTAR;
+-    minimize = (c & 1) != 0;
+-    min = rep_min[c];                 /* Pick up values from tables; */
+-    max = rep_max[c];                 /* zero for max => infinity */
+-    if (max == 0) max = INT_MAX;
+-
+-    /* Common code for all repeated single character type matches */
+-
+-    REPEATTYPE:
+-    ctype = *ecode++;      /* Code for the character type */
+-
+-    /* First, ensure the minimum number of matches are present. Use inline
+-    code for maximizing the speed, and do the type test once at the start
+-    (i.e. keep it out of the loop). Also we can test that there are at least
+-    the minimum number of bytes before we start, except when doing '.' in
+-    UTF8 mode. Leave the test in in all cases; in the special case we have
+-    to test after each character. */
+-
+-    if (min > md->end_subject - eptr) return FALSE;
+-    if (min > 0) switch(ctype)
+-      {
+-      case OP_ANY:
+-#ifdef SUPPORT_UTF8
+-      if (md->utf8)
+-        {
+-        for (i = 1; i <= min; i++)
+-          {
+-          if (eptr >= md->end_subject ||
+-             (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0))
+-            return FALSE;
+-          while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+-          }
+-        break;
+-        }
+-#endif
+-      /* Non-UTF8 can be faster */
+-      if ((ims & PCRE_DOTALL) == 0)
+-        { for (i = 1; i <= min; i++) if (*eptr++ == NEWLINE) return FALSE; }
+-      else eptr += min;
+-      break;
+-
+-      case OP_NOT_DIGIT:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE;
+-      break;
+-
+-      case OP_DIGIT:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE;
+-      break;
+-
+-      case OP_NOT_WHITESPACE:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE;
+-      break;
+-
+-      case OP_WHITESPACE:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE;
+-      break;
+-
+-      case OP_NOT_WORDCHAR:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_word) != 0)
+-          return FALSE;
+-      break;
+-
+-      case OP_WORDCHAR:
+-      for (i = 1; i <= min; i++)
+-        if ((md->ctypes[*eptr++] & ctype_word) == 0)
+-          return FALSE;
+-      break;
+-      }
+-
+-    /* If min = max, continue at the same level without recursing */
+-
+-    if (min == max) continue;
+-
+-    /* If minimizing, we have to test the rest of the pattern before each
+-    subsequent match. */
+-
+-    if (minimize)
+-      {
+-      for (i = min;; i++)
+-        {
+-        if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) return TRUE;
+-        if (i >= max || eptr >= md->end_subject) return FALSE;
+-
+-        c = *eptr++;
+-        switch(ctype)
+-          {
+-          case OP_ANY:
+-          if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return FALSE;
+-#ifdef SUPPORT_UTF8
+-          if (md->utf8)
+-            while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+-#endif
+-          break;
+-
+-          case OP_NOT_DIGIT:
+-          if ((md->ctypes[c] & ctype_digit) != 0) return FALSE;
+-          break;
+-
+-          case OP_DIGIT:
+-          if ((md->ctypes[c] & ctype_digit) == 0) return FALSE;
+-          break;
+-
+-          case OP_NOT_WHITESPACE:
+-          if ((md->ctypes[c] & ctype_space) != 0) return FALSE;
+-          break;
+-
+-          case OP_WHITESPACE:
+-          if  ((md->ctypes[c] & ctype_space) == 0) return FALSE;
+-          break;
+-
+-          case OP_NOT_WORDCHAR:
+-          if ((md->ctypes[c] & ctype_word) != 0) return FALSE;
+-          break;
+-
+-          case OP_WORDCHAR:
+-          if ((md->ctypes[c] & ctype_word) == 0) return FALSE;
+-          break;
+-          }
+-        }
+-      /* Control never gets here */
+-      }
+-
+-    /* If maximizing it is worth using inline code for speed, doing the type
+-    test once at the start (i.e. keep it out of the loop). */
+-
+-    else
+-      {
+-      const uschar *pp = eptr;
+-      switch(ctype)
+-        {
+-        case OP_ANY:
+-
+-        /* Special code is required for UTF8, but when the maximum is unlimited
+-        we don't need it. */
+-
+-#ifdef SUPPORT_UTF8
+-        if (md->utf8 && max < INT_MAX)
+-          {
+-          if ((ims & PCRE_DOTALL) == 0)
+-            {
+-            for (i = min; i < max; i++)
+-              {
+-              if (eptr >= md->end_subject || *eptr++ == NEWLINE) break;
+-              while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+-              }
+-            }
+-          else
+-            {
+-            for (i = min; i < max; i++)
+-              {
+-              eptr++;
+-              while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+-              }
+-            }
+-          break;
+-          }
+-#endif
+-        /* Non-UTF8 can be faster */
+-        if ((ims & PCRE_DOTALL) == 0)
+-          {
+-          for (i = min; i < max; i++)
+-            {
+-            if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+-            eptr++;
+-            }
+-          }
+-        else
+-          {
+-          c = max - min;
+-          if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+-          eptr += c;
+-          }
+-        break;
+-
+-        case OP_NOT_DIGIT:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-
+-        case OP_DIGIT:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-
+-        case OP_NOT_WHITESPACE:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-
+-        case OP_WHITESPACE:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-
+-        case OP_NOT_WORDCHAR:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-
+-        case OP_WORDCHAR:
+-        for (i = min; i < max; i++)
+-          {
+-          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0)
+-            break;
+-          eptr++;
+-          }
+-        break;
+-        }
+-
+-      while (eptr >= pp)
+-        {
+-        if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
+-          return TRUE;
+-#ifdef SUPPORT_UTF8
+-        if (md->utf8)
+-          while (eptr > pp && (*eptr & 0xc0) == 0x80) eptr--;
+-#endif
+-        }
+-      return FALSE;
+-      }
+-    /* Control never gets here */
+-
+-    /* There's been some horrible disaster. */
+-
+-    default:
+-    DPRINTF(("Unknown opcode %d\n", *ecode));
+-    md->errorcode = PCRE_ERROR_UNKNOWN_NODE;
+-    return FALSE;
+-    }
+-
+-  /* Do not stick any code in here without much thought; it is assumed
+-  that "continue" in the code above comes out to here to repeat the main
+-  loop. */
+-
+-  }             /* End of main loop */
+-/* Control never reaches here */
+-}
+-
+-
+-
+-
+-/*************************************************
+-*         Execute a Regular Expression           *
+-*************************************************/
+-
+-/* This function applies a compiled re to a subject string and picks out
+-portions of the string if it matches. Two elements in the vector are set for
+-each substring: the offsets to the start and end of the substring.
+-
+-Arguments:
+-  external_re     points to the compiled expression
+-  external_extra  points to "hints" from pcre_study() or is NULL
+-  subject         points to the subject string
+-  length          length of subject string (may contain binary zeros)
+-  start_offset    where to start in the subject string
+-  options         option bits
+-  offsets         points to a vector of ints to be filled in with offsets
+-  offsetcount     the number of elements in the vector
+-
+-Returns:          > 0 => success; value is the number of elements filled in
+-                  = 0 => success, but offsets is not big enough
+-                   -1 => failed to match
+-                 < -1 => some kind of unexpected problem
+-*/
+-
+-int
+-pcre_exec(const pcre *external_re, const pcre_extra *external_extra,
+-  const char *subject, int length, int start_offset, int options, int *offsets,
+-  int offsetcount)
+-{
+-int resetcount, ocount;
+-int first_char = -1;
+-int req_char = -1;
+-int req_char2 = -1;
+-unsigned long int ims = 0;
+-match_data match_block;
+-const uschar *start_bits = NULL;
+-const uschar *start_match = (const uschar *)subject + start_offset;
+-const uschar *end_subject;
+-const uschar *req_char_ptr = start_match - 1;
+-const real_pcre *re = (const real_pcre *)external_re;
+-const real_pcre_extra *extra = (const real_pcre_extra *)external_extra;
+-BOOL using_temporary_offsets = FALSE;
+-BOOL anchored;
+-BOOL startline;
+-
+-if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
+-
+-if (re == NULL || subject == NULL ||
+-   (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
+-if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+-
+-anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
+-startline = (re->options & PCRE_STARTLINE) != 0;
+-
+-match_block.start_pattern = re->code;
+-match_block.start_subject = (const uschar *)subject;
+-match_block.end_subject = match_block.start_subject + length;
+-end_subject = match_block.end_subject;
+-
+-match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
+-match_block.utf8 = (re->options & PCRE_UTF8) != 0;
+-
+-match_block.notbol = (options & PCRE_NOTBOL) != 0;
+-match_block.noteol = (options & PCRE_NOTEOL) != 0;
+-match_block.notempty = (options & PCRE_NOTEMPTY) != 0;
+-
+-match_block.errorcode = PCRE_ERROR_NOMATCH;     /* Default error */
+-
+-match_block.lcc = re->tables + lcc_offset;
+-match_block.ctypes = re->tables + ctypes_offset;
+-
+-/* The ims options can vary during the matching as a result of the presence
+-of (?ims) items in the pattern. They are kept in a local variable so that
+-restoring at the exit of a group is easy. */
+-
+-ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
+-
+-/* If the expression has got more back references than the offsets supplied can
+-hold, we get a temporary bit of working store to use during the matching.
+-Otherwise, we can use the vector supplied, rounding down its size to a multiple
+-of 3. */
+-
+-ocount = offsetcount - (offsetcount % 3);
+-
+-if (re->top_backref > 0 && re->top_backref >= ocount/3)
+-  {
+-  ocount = re->top_backref * 3 + 3;
+-  match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
+-  if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
+-  using_temporary_offsets = TRUE;
+-  DPRINTF(("Got memory to hold back references\n"));
+-  }
+-else match_block.offset_vector = offsets;
+-
+-match_block.offset_end = ocount;
+-match_block.offset_max = (2*ocount)/3;
+-match_block.offset_overflow = FALSE;
+-
+-/* Compute the minimum number of offsets that we need to reset each time. Doing
+-this makes a huge difference to execution time when there aren't many brackets
+-in the pattern. */
+-
+-resetcount = 2 + re->top_bracket * 2;
+-if (resetcount > offsetcount) resetcount = ocount;
+-
+-/* Reset the working variable associated with each extraction. These should
+-never be used unless previously set, but they get saved and restored, and so we
+-initialize them to avoid reading uninitialized locations. */
+-
+-if (match_block.offset_vector != NULL)
+-  {
+-  register int *iptr = match_block.offset_vector + ocount;
+-  register int *iend = iptr - resetcount/2 + 1;
+-  while (--iptr >= iend) *iptr = -1;
+-  }
+-
+-/* Set up the first character to match, if available. The first_char value is
+-never set for an anchored regular expression, but the anchoring may be forced
+-at run time, so we have to test for anchoring. The first char may be unset for
+-an unanchored pattern, of course. If there's no first char and the pattern was
+-studied, there may be a bitmap of possible first characters. */
+-
+-if (!anchored)
+-  {
+-  if ((re->options & PCRE_FIRSTSET) != 0)
+-    {
+-    first_char = re->first_char;
+-    if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char];
+-    }
+-  else
+-    if (!startline && extra != NULL &&
+-      (extra->options & PCRE_STUDY_MAPPED) != 0)
+-        start_bits = extra->start_bits;
+-  }
+-
+-/* For anchored or unanchored matches, there may be a "last known required
+-character" set. If the PCRE_CASELESS is set, implying that the match starts
+-caselessly, or if there are any changes of this flag within the regex, set up
+-both cases of the character. Otherwise set the two values the same, which will
+-avoid duplicate testing (which takes significant time). This covers the vast
+-majority of cases. It will be suboptimal when the case flag changes in a regex
+-and the required character in fact is caseful. */
+-
+-if ((re->options & PCRE_REQCHSET) != 0)
+-  {
+-  req_char = re->req_char;
+-  req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)?
+-    (re->tables + fcc_offset)[req_char] : req_char;
+-  }
+-
+-/* Loop for handling unanchored repeated matching attempts; for anchored regexs
+-the loop runs just once. */
+-
+-do
+-  {
+-  int rc;
+-  register int *iptr = match_block.offset_vector;
+-  register int *iend = iptr + resetcount;
+-
+-  /* Reset the maximum number of extractions we might see. */
+-
+-  while (iptr < iend) *iptr++ = -1;
+-
+-  /* Advance to a unique first char if possible */
+-
+-  if (first_char >= 0)
+-    {
+-    if ((ims & PCRE_CASELESS) != 0)
+-      while (start_match < end_subject &&
+-             match_block.lcc[*start_match] != first_char)
+-        start_match++;
+-    else
+-      while (start_match < end_subject && *start_match != first_char)
+-        start_match++;
+-    }
+-
+-  /* Or to just after \n for a multiline match if possible */
+-
+-  else if (startline)
+-    {
+-    if (start_match > match_block.start_subject + start_offset)
+-      {
+-      while (start_match < end_subject && start_match[-1] != NEWLINE)
+-        start_match++;
+-      }
+-    }
+-
+-  /* Or to a non-unique first char after study */
+-
+-  else if (start_bits != NULL)
+-    {
+-    while (start_match < end_subject)
+-      {
+-      register int c = *start_match;
+-      if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break;
+-      }
+-    }
+-
+-#ifdef DEBUG  /* Sigh. Some compilers never learn. */
+-  printf(">>>> Match against: ");
+-  pchars(start_match, end_subject - start_match, TRUE, &match_block);
+-  printf("\n");
+-#endif
+-
+-  /* If req_char is set, we know that that character must appear in the subject
+-  for the match to succeed. If the first character is set, req_char must be
+-  later in the subject; otherwise the test starts at the match point. This
+-  optimization can save a huge amount of backtracking in patterns with nested
+-  unlimited repeats that aren't going to match. We don't know what the state of
+-  case matching may be when this character is hit, so test for it in both its
+-  cases if necessary. However, the different cased versions will not be set up
+-  unless PCRE_CASELESS was given or the casing state changes within the regex.
+-  Writing separate code makes it go faster, as does using an autoincrement and
+-  backing off on a match. */
+-
+-  if (req_char >= 0)
+-    {
+-    register const uschar *p = start_match + ((first_char >= 0)? 1 : 0);
+-
+-    /* We don't need to repeat the search if we haven't yet reached the
+-    place we found it at last time. */
+-
+-    if (p > req_char_ptr)
+-      {
+-      /* Do a single test if no case difference is set up */
+-
+-      if (req_char == req_char2)
+-        {
+-        while (p < end_subject)
+-          {
+-          if (*p++ == req_char) { p--; break; }
+-          }
+-        }
+-
+-      /* Otherwise test for either case */
+-
+-      else
+-        {
+-        while (p < end_subject)
+-          {
+-          register int pp = *p++;
+-          if (pp == req_char || pp == req_char2) { p--; break; }
+-          }
+-        }
+-
+-      /* If we can't find the required character, break the matching loop */
+-
+-      if (p >= end_subject) break;
+-
+-      /* If we have found the required character, save the point where we
+-      found it, so that we don't search again next time round the loop if
+-      the start hasn't passed this character yet. */
+-
+-      req_char_ptr = p;
+-      }
+-    }
+-
+-  /* When a match occurs, substrings will be set for all internal extractions;
+-  we just need to set up the whole thing as substring 0 before returning. If
+-  there were too many extractions, set the return code to zero. In the case
+-  where we had to get some local store to hold offsets for backreferences, copy
+-  those back references that we can. In this case there need not be overflow
+-  if certain parts of the pattern were not used. */
+-
+-  match_block.start_match = start_match;
+-  if (!match(start_match, re->code, 2, &match_block, ims, NULL, match_isgroup))
+-    continue;
+-
+-  /* Copy the offset information from temporary store if necessary */
+-
+-  if (using_temporary_offsets)
+-    {
+-    if (offsetcount >= 4)
+-      {
+-      memcpy(offsets + 2, match_block.offset_vector + 2,
+-        (offsetcount - 2) * sizeof(int));
+-      DPRINTF(("Copied offsets from temporary memory\n"));
+-      }
+-    if (match_block.end_offset_top > offsetcount)
+-      match_block.offset_overflow = TRUE;
+-
+-    DPRINTF(("Freeing temporary memory\n"));
+-    (pcre_free)(match_block.offset_vector);
+-    }
+-
+-  rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2;
+-
+-  if (offsetcount < 2) rc = 0; else
+-    {
+-    offsets[0] = start_match - match_block.start_subject;
+-    offsets[1] = match_block.end_match_ptr - match_block.start_subject;
+-    }
+-
+-  DPRINTF((">>>> returning %d\n", rc));
+-  return rc;
+-  }
+-
+-/* This "while" is the end of the "do" above */
+-
+-while (!anchored &&
+-       match_block.errorcode == PCRE_ERROR_NOMATCH &&
+-       start_match++ < end_subject);
+-
+-if (using_temporary_offsets)
+-  {
+-  DPRINTF(("Freeing temporary memory\n"));
+-  (pcre_free)(match_block.offset_vector);
+-  }
+-
+-DPRINTF((">>>> returning %d\n", match_block.errorcode));
+-
+-return match_block.errorcode;
+-}
+-
+-/* End of pcre.c */
+
+=== removed file 'parser/pcre/pcre.h'
+--- parser/pcre/pcre.h 2006-04-11 21:52:54 +0000
++++ parser/pcre/pcre.h 1970-01-01 00:00:00 +0000
+@@ -1,113 +0,0 @@
+-/*************************************************
+-*       Perl-Compatible Regular Expressions      *
+-*************************************************/
 -
- /*
-  *   Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-  *   NOVELL (All rights reserved)
-+ *   Copyright (c) 2010
-+ *   Canonical, Ltd.
-  *
-  *   This program is free software; you can redistribute it and/or
-  *   modify it under the terms of version 2 of the GNU General Public
-@@ -15,7 +15,7 @@
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-- *   along with this program; if not, contact Novell, Inc.
-+ *   along with this program; if not, contact Canonical, Ltd.
-  */
- #define YYERROR_VERBOSE 1
-@@ -32,6 +32,7 @@
- /* #define DEBUG */
- #include "parser.h"
-+#include "parser_include.h"
- #include <unistd.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-@@ -63,10 +64,6 @@
- #define CAP_TO_MASK(x) (1ull << (x))
--/* from lex_config, for nice error messages */
--/* extern char *current_file; */
--extern int current_lineno;
+-/* Copyright (c) 1997-2001 University of Cambridge */
 -
- struct value_list {
-       char *value;
-       struct value_list *next;
-@@ -1109,10 +1106,15 @@
-       va_end(arg);
-       if (profilename) {
--              PERROR(_("AppArmor parser error in %s at line %d: %s\n"),
--                     profilename, current_lineno, buf);
-+              PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"),
-+                     profilename,
-+                     current_filename ? " in " : "",
-+                     current_filename ? current_filename : "",
-+                     current_lineno, buf);
-       } else {
--              PERROR(_("AppArmor parser error, line %d: %s\n"),
-+              PERROR(_("AppArmor parser error,%s%s line %d: %s\n"),
-+                     current_filename ? " in " : "",
-+                     current_filename ? current_filename : "",
-                      current_lineno, buf);
-       }
+-#ifndef _PCRE_H
+-#define _PCRE_H
+-
+-/* The file pcre.h is build by "configure". Do not edit it; instead
+-make changes to pcre.in. */
+-
+-#define PCRE_MAJOR          3
+-#define PCRE_MINOR          9
+-#define PCRE_DATE           02-Jan-2002
+-
+-/* Win32 uses DLL by default */
+-
+-#ifdef _WIN32
+-# ifdef STATIC
+-#  define PCRE_DL_IMPORT
+-# else
+-#  define PCRE_DL_IMPORT __declspec(dllimport)
+-# endif
+-#else
+-# define PCRE_DL_IMPORT
+-#endif
+-
+-/* Have to include stdlib.h in order to ensure that size_t is defined;
+-it is needed here for malloc. */
+-
+-#include <stdlib.h>
+-
+-/* Allow for C++ users */
+-
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
+-
+-/* Options */
+-
+-#define PCRE_CASELESS        0x0001
+-#define PCRE_MULTILINE       0x0002
+-#define PCRE_DOTALL          0x0004
+-#define PCRE_EXTENDED        0x0008
+-#define PCRE_ANCHORED        0x0010
+-#define PCRE_DOLLAR_ENDONLY  0x0020
+-#define PCRE_EXTRA           0x0040
+-#define PCRE_NOTBOL          0x0080
+-#define PCRE_NOTEOL          0x0100
+-#define PCRE_UNGREEDY        0x0200
+-#define PCRE_NOTEMPTY        0x0400
+-#define PCRE_UTF8            0x0800
+-
+-/* Exec-time and get-time error codes */
+-
+-#define PCRE_ERROR_NOMATCH        (-1)
+-#define PCRE_ERROR_NULL           (-2)
+-#define PCRE_ERROR_BADOPTION      (-3)
+-#define PCRE_ERROR_BADMAGIC       (-4)
+-#define PCRE_ERROR_UNKNOWN_NODE   (-5)
+-#define PCRE_ERROR_NOMEMORY       (-6)
+-#define PCRE_ERROR_NOSUBSTRING    (-7)
+-
+-/* Request types for pcre_fullinfo() */
+-
+-#define PCRE_INFO_OPTIONS         0
+-#define PCRE_INFO_SIZE            1
+-#define PCRE_INFO_CAPTURECOUNT    2
+-#define PCRE_INFO_BACKREFMAX      3
+-#define PCRE_INFO_FIRSTCHAR       4
+-#define PCRE_INFO_FIRSTTABLE      5
+-#define PCRE_INFO_LASTLITERAL     6
+-
+-/* Types */
+-
+-struct real_pcre;        /* declaration; the definition is private  */
+-struct real_pcre_extra;  /* declaration; the definition is private */
+-
+-typedef struct real_pcre pcre;
+-typedef struct real_pcre_extra pcre_extra;
+-
+-/* Store get and free functions. These can be set to alternative malloc/free
+-functions if required. Some magic is required for Win32 DLL; it is null on
+-other OS. */
+-
+-PCRE_DL_IMPORT extern void *(*pcre_malloc)(size_t);
+-PCRE_DL_IMPORT extern void  (*pcre_free)(void *);
+-
+-#undef PCRE_DL_IMPORT
+-
+-/* Functions */
+-
+-extern pcre *pcre_compile(const char *, int, const char **, int *,
+-              const unsigned char *);
+-extern int  pcre_copy_substring(const char *, int *, int, int, char *, int);
+-extern int  pcre_exec(const pcre *, const pcre_extra *, const char *,
+-              int, int, int, int *, int);
+-extern void pcre_free_substring(const char *);
+-extern void pcre_free_substring_list(const char **);
+-extern int  pcre_get_substring(const char *, int *, int, int, const char **);
+-extern int  pcre_get_substring_list(const char *, int *, int, const char ***);
+-extern int  pcre_info(const pcre *, int *, int *);
+-extern int  pcre_fullinfo(const pcre *, const pcre_extra *, int, void *);
+-extern const unsigned char *pcre_maketables(void);
+-extern pcre_extra *pcre_study(const pcre *, int, const char **);
+-extern const char *pcre_version(void);
+-
+-#ifdef __cplusplus
+-}  /* extern "C" */
+-#endif
+-
+-#endif /* End of pcre.h */
 
 === modified file 'parser/tst/Makefile'
 --- parser/tst/Makefile        2006-12-15 08:10:25 +0000
-+++ parser/tst/Makefile        2010-06-05 01:47:44 +0000
++++ parser/tst/Makefile        2010-08-03 17:27:13 +0000
 @@ -1,8 +1,9 @@
  #
 -# $Id$
  
  ifeq ($(VERBOSE),1)
    PROVE_ARG=-v
-@@ -10,9 +11,19 @@
+@@ -10,9 +11,23 @@
  
  all: tests
  
 +              grep -q "AppArmor parser error for errors/single.sd in errors/single.sd at line 3: Could not open 'failure'"
 +      LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/double.sd | \
 +              grep -q "AppArmor parser error for errors/double.sd in errors/includes/busted at line 67: Could not open 'does-not-exist'"
++      LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/modefail.sd | \
++              grep -q "AppArmor parser error for errors/modefail.sd in errors/modefail.sd at line 6: syntax error"
++      LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/multi_include.sd | \
++              grep -q "AppArmor parser error for errors/multi_include.sd in errors/multi_include.sd at line 12: Could not open 'failure'"
 +      @echo "Error Output: PASS"
 +
 +parser_sanity: $(PARSER)
 
 === modified file 'parser/tst/README'
 --- parser/tst/README  2006-06-01 17:02:28 +0000
-+++ parser/tst/README  2010-06-05 01:47:44 +0000
++++ parser/tst/README  2010-06-05 06:11:20 +0000
 @@ -64,5 +64,3 @@
      loop.
  
 === added directory 'parser/tst/errors'
 === added file 'parser/tst/errors/double.sd'
 --- parser/tst/errors/double.sd        1970-01-01 00:00:00 +0000
-+++ parser/tst/errors/double.sd        2010-06-05 01:47:44 +0000
++++ parser/tst/errors/double.sd        2010-06-05 06:11:20 +0000
 @@ -0,0 +1,5 @@
 +#
 +/does/not/exist {
 === added directory 'parser/tst/errors/includes'
 === added file 'parser/tst/errors/includes/base'
 --- parser/tst/errors/includes/base    1970-01-01 00:00:00 +0000
-+++ parser/tst/errors/includes/base    2010-06-05 01:47:44 +0000
++++ parser/tst/errors/includes/base    2010-06-05 06:11:20 +0000
 @@ -0,0 +1,81 @@
 +# $Id$
 +# ------------------------------------------------------------------
 
 === added file 'parser/tst/errors/includes/busted'
 --- parser/tst/errors/includes/busted  1970-01-01 00:00:00 +0000
-+++ parser/tst/errors/includes/busted  2010-06-05 01:47:44 +0000
++++ parser/tst/errors/includes/busted  2010-06-05 06:11:20 +0000
 @@ -0,0 +1,83 @@
 +# $Id$
 +# ------------------------------------------------------------------
 +  /proc/stat                     r,
 +  /proc/cpuinfo                  r,
 
+=== added file 'parser/tst/errors/modefail.sd'
+--- parser/tst/errors/modefail.sd      1970-01-01 00:00:00 +0000
++++ parser/tst/errors/modefail.sd      2010-08-03 17:27:13 +0000
+@@ -0,0 +1,7 @@
++#     1
++#     2
++#     3
++/does/not/exist {     # 4
++  /lib/lib*.so rm,    # 5
++  /fail abcdefgh,     # 6
++}                     # 7
+
+=== added file 'parser/tst/errors/multi_include.sd'
+--- parser/tst/errors/multi_include.sd 1970-01-01 00:00:00 +0000
++++ parser/tst/errors/multi_include.sd 2010-08-03 17:27:13 +0000
+@@ -0,0 +1,13 @@
++#
++#
++#
++/does/not/exist {
++  #include <includes/base>
++  #include <includes/base>
++  #include <includes/base>
++  #include <includes/base>
++
++  /bin/true rix,
++
++  #include <failure>
++}
+
 === added file 'parser/tst/errors/okay.sd'
 --- parser/tst/errors/okay.sd  1970-01-01 00:00:00 +0000
-+++ parser/tst/errors/okay.sd  2010-06-05 01:47:44 +0000
++++ parser/tst/errors/okay.sd  2010-06-05 06:11:20 +0000
 @@ -0,0 +1,4 @@
 +#
 +/does/not/exist {
 
 === added file 'parser/tst/errors/single.sd'
 --- parser/tst/errors/single.sd        1970-01-01 00:00:00 +0000
-+++ parser/tst/errors/single.sd        2010-06-05 01:47:44 +0000
++++ parser/tst/errors/single.sd        2010-06-05 06:11:20 +0000
 @@ -0,0 +1,7 @@
 +#
 +#
 
 === modified file 'profiles/apparmor.d/abstractions/base'
 --- profiles/apparmor.d/abstractions/base      2010-01-03 21:16:38 +0000
-+++ profiles/apparmor.d/abstractions/base      2010-06-05 00:43:11 +0000
++++ profiles/apparmor.d/abstractions/base      2010-06-05 06:11:20 +0000
 @@ -85,6 +85,9 @@
    # some applications will display license information
    /usr/share/common-licenses/**  r,
    # filesystems generally. This does not appreciably decrease security with
    # Ubuntu profiles because the user is expected to have access to files owned
 
+=== modified file 'profiles/apparmor.d/abstractions/dbus'
+--- profiles/apparmor.d/abstractions/dbus      2009-11-04 20:25:42 +0000
++++ profiles/apparmor.d/abstractions/dbus      2010-08-03 17:27:13 +0000
+@@ -2,7 +2,7 @@
+ # $Id$
+ # ------------------------------------------------------------------
+ #
+-#    Copyright (C) 2009 Canonical Ltd.
++#    Copyright (C) 2009-2010 Canonical Ltd.
+ #
+ #    This program is free software; you can redistribute it and/or
+ #    modify it under the terms of version 2 of the GNU General Public
+@@ -10,8 +10,5 @@
+ #
+ # ------------------------------------------------------------------
+-  # System socket
++  # System socket. Be careful when including this abstraction.
+   /var/run/dbus/system_bus_socket w,
+-
+-  # Machine id
+-  /var/lib/dbus/machine-id r,
+
 === added file 'profiles/apparmor.d/abstractions/dbus-session'
 --- profiles/apparmor.d/abstractions/dbus-session      1970-01-01 00:00:00 +0000
-+++ profiles/apparmor.d/abstractions/dbus-session      2010-04-19 17:38:17 +0000
++++ profiles/apparmor.d/abstractions/dbus-session      2010-08-03 17:27:13 +0000
 @@ -0,0 +1,14 @@
 +# vim:syntax=apparmor
 +# $Id$
 +#
 +# ------------------------------------------------------------------
 +
-+  #include <abstractions/dbus>
-+  /usr/bin/dbus-launch Uxr,
++  /usr/bin/dbus-launch Pix,
++  /var/lib/dbus/machine-id r,
 
 === modified file 'profiles/apparmor.d/abstractions/fonts'
 --- profiles/apparmor.d/abstractions/fonts     2009-11-04 20:25:42 +0000
-+++ profiles/apparmor.d/abstractions/fonts     2010-06-05 00:44:30 +0000
++++ profiles/apparmor.d/abstractions/fonts     2010-06-05 06:11:20 +0000
 @@ -15,6 +15,7 @@
  
    /usr/lib/xorg/modules/fonts/**.so*    mr,
 
 === modified file 'profiles/apparmor.d/abstractions/freedesktop.org'
 --- profiles/apparmor.d/abstractions/freedesktop.org   2009-11-04 20:25:42 +0000
-+++ profiles/apparmor.d/abstractions/freedesktop.org   2010-06-05 00:44:30 +0000
++++ profiles/apparmor.d/abstractions/freedesktop.org   2010-06-05 06:11:20 +0000
 @@ -27,3 +27,7 @@
    @{HOME}/.icons/                 r,
    @{HOME}/.recently-used.xbel*    rw,
 +  @{HOME}/.local/share/mime/      r,
 +  @{HOME}/.local/share/mime/**    r,
 
+=== modified file 'profiles/apparmor.d/abstractions/gnome'
+--- profiles/apparmor.d/abstractions/gnome     2009-11-10 20:04:26 +0000
++++ profiles/apparmor.d/abstractions/gnome     2010-08-03 17:27:13 +0000
+@@ -3,7 +3,7 @@
+ # ------------------------------------------------------------------
+ #
+ #    Copyright (C) 2002-2009 Novell/SUSE
+-#    Copyright (C) 2009 Canonical Ltd.
++#    Copyright (C) 2009-2010 Canonical Ltd.
+ #
+ #    This program is free software; you can redistribute it and/or
+ #    modify it under the terms of version 2 of the GNU General Public
+@@ -31,6 +31,7 @@
+   /etc/pango/*                    r,
+   /usr/lib{,32,64}/pango/**       mr,
+   /usr/lib{,32,64}/gtk-*/**       mr,
++  /usr/lib{,32,64}/gdk-pixbuf-*/** mr,
+   # per-user gtk configuration
+   @{HOME}/.gnome/Gnome            r,
+
 === modified file 'profiles/apparmor.d/abstractions/nameservice'
 --- profiles/apparmor.d/abstractions/nameservice       2009-11-04 20:25:42 +0000
-+++ profiles/apparmor.d/abstractions/nameservice       2010-06-05 00:44:59 +0000
++++ profiles/apparmor.d/abstractions/nameservice       2010-06-05 06:11:20 +0000
 @@ -77,3 +77,5 @@
    network inet  dgram,
    network inet6 dgram,
 
 === modified file 'profiles/apparmor.d/abstractions/php5'
 --- profiles/apparmor.d/abstractions/php5      2010-01-03 21:16:38 +0000
-+++ profiles/apparmor.d/abstractions/php5      2010-03-30 17:34:32 +0000
++++ profiles/apparmor.d/abstractions/php5      2010-05-31 18:58:40 +0000
 @@ -2,7 +2,7 @@
  # ------------------------------------------------------------------
  #
 
 === modified file 'profiles/apparmor.d/abstractions/samba'
 --- profiles/apparmor.d/abstractions/samba     2009-11-04 20:25:42 +0000
-+++ profiles/apparmor.d/abstractions/samba     2010-03-25 23:13:00 +0000
++++ profiles/apparmor.d/abstractions/samba     2010-05-31 18:58:40 +0000
 @@ -2,7 +2,7 @@
  # $Id$
  # ------------------------------------------------------------------
    /var/log/samba/log.* w,
    /var/run/samba/*.tdb rw,
 
+=== modified file 'profiles/apparmor.d/abstractions/ubuntu-email'
+--- profiles/apparmor.d/abstractions/ubuntu-email      2009-11-11 19:42:30 +0000
++++ profiles/apparmor.d/abstractions/ubuntu-email      2010-08-03 17:27:13 +0000
+@@ -15,5 +15,5 @@
+   /usr/bin/tkrat Ux,
+   /usr/lib/thunderbird/thunderbird Ux,
+-
++  /usr/lib/thunderbird-3*/thunderbird Ux,
+
+=== modified file 'profiles/apparmor.d/abstractions/ubuntu-media-players'
+--- profiles/apparmor.d/abstractions/ubuntu-media-players      2010-03-08 19:50:25 +0000
++++ profiles/apparmor.d/abstractions/ubuntu-media-players      2010-08-03 17:27:13 +0000
+@@ -24,6 +24,7 @@
+   # mplayer
+   /etc/mplayerplug-in.conf r,
++  /usr/bin/gmplayer Uxr,
+   /usr/bin/gnome-mplayer Uxr,
+   /usr/bin/kmplayer Uxr,
+   /usr/bin/mplayer Uxr,
+
 === modified file 'profiles/apparmor.d/abstractions/user-tmp'
 --- profiles/apparmor.d/abstractions/user-tmp  2009-11-04 20:25:42 +0000
-+++ profiles/apparmor.d/abstractions/user-tmp  2010-05-12 08:52:23 +0000
++++ profiles/apparmor.d/abstractions/user-tmp  2010-05-31 18:58:40 +0000
 @@ -2,7 +2,7 @@
  # ------------------------------------------------------------------
  #
 +  owner /tmp/**         rwkl,
 +  owner /tmp/           rw,
 
-=== modified file 'tests/regression/subdomain/prologue.inc'
+=== renamed directory 'tests/regression/subdomain' => 'tests/regression/apparmor'
+=== modified file 'tests/regression/apparmor/Makefile'
+--- tests/regression/subdomain/Makefile        2009-08-21 20:39:45 +0000
++++ tests/regression/apparmor/Makefile 2010-08-03 17:27:13 +0000
+@@ -22,6 +22,7 @@
+     chmod.c \
+     chown.c \
+     clone.c \
++    coredump.c \
+     deleted.c \
+     environ.c \
+     env_check.c \
+@@ -113,6 +114,7 @@
+       changehat_misc \
+       chdir \
+       clone \
++      coredump \
+       deleted \
+       environ \
+       exec \
+@@ -158,27 +160,39 @@
+ tests: all
+       @if [ `whoami` = "root" ] ;\
+       then \
++              rc=0; \
+               for i in $(TESTS) ;\
+               do \
+                       echo ;\
+                       echo "running $$i" ;\
+                       bash $$i.sh ;\
++                      if [ $$? -ne 0 ] ; then \
++                              rc=1;\
++                      fi;\
+               done ;\
++              exit $$rc;\
+       else \
+               echo "must be root to run tests" ;\
++              exit 1;\
+       fi
+ alltests: all
+       @if [ `whoami` = "root" ] ;\
+       then \
++              rc=0; \
+               for i in $(TESTS) $(RISKY_TESTS) ;\
+               do \
+                       echo ;\
+                       echo "running $$i" ;\
+                       bash $$i.sh ;\
++                      if [ $$? -ne 0 ] ; then \
++                              rc=1;\
++                      fi;\
+               done ;\
++              exit $$rc;\
+       else \
+               echo "must be root to run tests" ;\
++              exit 1;\
+       fi
+ clean:
+
+=== modified file 'tests/regression/apparmor/README'
+--- tests/regression/subdomain/README  2006-09-15 22:39:59 +0000
++++ tests/regression/apparmor/README   2010-08-03 17:27:13 +0000
+@@ -1,10 +1,10 @@
+ Running tests
+ =============
+-Type "make tests" at the shell prompt, this will make the subprograms
+-and run the tests.
++Type "sudo make tests" at the shell prompt, this will make the
++subprograms and run the tests.
+-You must be root to execute make tests (a requirement of subdomain).
++You must be root to execute "make tests" (a requirement of AppArmor).
+ (There is also a 'make alltests', which adds a test for bug that, when
+ triggered, would cause the kernel to crash.)
+@@ -12,10 +12,13 @@
+ Test output
+ ===========
+-No output is displayed for a passing test.  The makefile will output
++By default, no output is displayed for a passing test.  The makefile will
++output:
+       running <testname> for each test.
+-Output other than this indicates a problem.
++To have verbose output with each subtest reporting successes, set the
++environment variable VERBOSE=1:
++      sudo VERBOSE=1 make tests
+ There are three typical failure scenarios:
+       - Test failed when it was expected to pass
+@@ -29,7 +32,7 @@
+ Common user changeable environment variables are stored in the file
+ 'uservars.inc'.  Currently the path to the tmp directory, the path
+-to the subdomain_parser executable, and any additional arguments to give
++to the apparmor_parser executable, and any additional arguments to give
+ to the parser are specified in this configuration file.
+ (Note: the tmp directory specified in uservars.inc will have an added
+@@ -66,7 +69,7 @@
+ directory will contain the files for the failed subtest.
+ It may be necessary to create certain temp files in this directory in order to 
+-have the test function correctly, see the subdomain profile 'profile' in the 
++have the test function correctly, see the AppArmor profile 'profile' in the 
+ directory in order to determine which files may need to be created to support
+ the executable.
+@@ -155,7 +158,7 @@
+       <requirement placed on the shell script author by prologue.inc>
+       bin=$pwd
+       
+-      <prologie.inc must be included before running any tests>
++      <prologue.inc must be included before running any tests>
+       . $bin/prologue.inc
+       
+       <variable definitions used by this script?
+@@ -174,7 +177,7 @@
+       
+       # NOLINK PERMTEST
+       <generate a new profile allowing only r access to /bin/true>
+-      <subdomain_parser will automatically be invoked in -r mode>
++      <apparmor_parser will automatically be invoked in -r mode>
+       genprofile $file:$badperm
+       <run this test (exec) passing /bin/true as argv[1]>
+@@ -183,16 +186,11 @@
+       <Thats it. Exit status $rc is automatically returned by epilogue.inc>
+-Additional documentation
+-========================
+-
+-See the file 'subdomain_test.txt'
+-
+ Supporting files
+ ================
+ strace.sh     Not a test harness, used to support strace testing.
+-mkprofile.sh  Not a test harness, used to generate subdomain profiles.
++mkprofile.sh  Not a test harness, used to generate AppArmor profiles.
+ prologue.inc  Must be dotted (included) into the test harness. Provides
+               support routines.
+ epilogue.inc  Cleanup support, automatically called upon successful or
+@@ -222,17 +220,3 @@
+    This is not an error, rather a sign that bash noticed the kernel had killed 
+    a process which was attempting to use a bogus MAGIC number.  Alas, there is 
+    no way to get bash to not print this diagnostic
+-
+-3) Ptrace
+-      Error: open passed. Test 'STRACE OPEN (x confinement)' 
+-      was expected to 'fail'
+- 
+-   Regression from 2.4.18 to 2.4.20. (We aren't sure on the first
+-   endpoint, and the problem still happens in 2.4.20-20_imnx_10smp.)
+-
+-4) Open
+-      Error: open passed. Test 'OPEN W (create)' was expected to 'fail'
+-
+-   LSM issue.  Flags passed to inode_permission are 0 if O_CREAT is used to
+-   open file.  Need to submit a patch to inode_create hook to receive the
+-   O_RDWR flags. See https://bugs.wirex.com/show_bug.cgi?id=2885
+
+=== modified file 'tests/regression/apparmor/changehat_misc.sh'
+--- tests/regression/subdomain/changehat_misc.sh       2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/changehat_misc.sh        2010-08-03 17:27:13 +0000
+@@ -64,7 +64,7 @@
+ echo "*** A 'Killed' message from bash is expected for the following test"
+ runchecktest "CHANGEHAT (subprofile->subprofile w/ bad magic)" signal9 $subtest $subtest2 badmagic $file
+-# 1. ATTEMPT TO CHANGEGAT TO AN INVALUD PROFILE, SHOULD PUT US INTO A NULL
++# 1. ATTEMPT TO CHANGEHAT TO AN INVALID PROFILE, SHOULD PUT US INTO A NULL
+ #    PROFILE
+ # 2. ATTEMPT TO CHANGEHAT OUT WITH BAD TOKEN
+ settest changehat_fail
+
+=== modified file 'tests/regression/apparmor/coredump.c'
+--- tests/regression/subdomain/coredump.c      2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/coredump.c       2010-08-03 17:27:13 +0000
+@@ -1,7 +1,9 @@
++#include <stdio.h>
+ int *ptr;
+ /*
+  *    Copyright (C) 2002-2005 Novell/SUSE
++ *    Copyright (C) 2010 Canonical, Ltd
+  *
+  *    This program is free software; you can redistribute it and/or
+  *    modify it under the terms of the GNU General Public License as
+@@ -9,7 +11,7 @@
+  *    License.
+  */
+-main()
++int main(int argc, char *argv[])
+ {
+       printf("This will cause a sigsegv\n");
+
+=== modified file 'tests/regression/apparmor/coredump.sh'
+--- tests/regression/subdomain/coredump.sh     2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/coredump.sh      2010-08-03 17:27:13 +0000
+@@ -1,7 +1,6 @@
+ #! /bin/bash
+-# $Id$
+-
+ #     Copyright (C) 2002-2005 Novell/SUSE
++#     Copyright (C) 2010 Canonical, Ltd
+ #
+ #     This program is free software; you can redistribute it and/or
+ #     modify it under the terms of the GNU General Public License as
+@@ -11,26 +10,52 @@
+ #=NAME coredump
+ #=DESCRIPTION coredump test
++cleancorefile()
++{
++      rm -f core core.*
++}
++
+ checkcorefile()
+ {
+-_corefilelist=`echo core.*`
+-if [ "$_corefilelist" = "core.*" ]
+-then
+-      _corefile=no
+-else
+-      _corefile=yes
+-fi
+-
+-if [ "$1" = "yes" -a "$_corefile" = "no" ]
+-then
+-      echo "Error: corefile expected but not present - $2"
+-elif [ "$1" = "no" -a "$_corefile"  = "yes" ]
+-then
+-      echo "Error: corefile present when not expected -- $2"
+-fi
+-
+-unset _corefile _corefilelist
+-rm -f core.*
++      # global _testdesc _pfmode _known outfile
++      if [ ${1:0:1} == "x" ] ; then
++              requirement=${1#x}
++              _known=" (known problem)"
++        else
++              requirement=$1
++              _known=""
++        fi
++
++      _corefilelist=`echo core.*`
++      if [ ! -f core ] && [ "$_corefilelist" = "core.*" ]
++      then
++              _corefile=no
++      else
++              _corefile=yes
++      fi
++
++      if [ "$requirement" = "yes" -a "$_corefile" = "no" ] ; then
++              if [ -n $_known ] ; then
++                      echo -n "XFAIL: "
++              fi
++              echo "Error: corefile expected but not present - $2"
++              if [ -z $_known ] ; then
++                      cat $profile
++                      testfailed
++              fi
++      elif [ "$requirement" = "no" -a "$_corefile"  = "yes" ] ; then
++              if [ -n "$_known" ] ; then
++                      echo -n "XFAIL: "
++              fi
++              echo "Error: corefile present when not expected -- $2"
++              if [ -z "$_known" ] ; then
++                      cat $profile
++                      testfailed
++              fi
++      fi
++
++      unset _corefile _corefilelist
++      cleancorefile
+ }
+ pwd=`dirname $0`
+@@ -45,15 +70,18 @@
+ # enable coredumps
+ ulimit -c 1000000
++cleancorefile
++checkcorefile no "COREDUMP (starting with clean slate)"
+ # PASS TEST, no confinement
++cleancorefile
+ echo "*** A 'Segmentation Fault' message from bash is expected for the following test"
+ runchecktest "COREDUMP (no confinement)" signal11
+ checkcorefile yes "COREDUMP (no confinement)"
+ # PASS TEST, with r confinement
+-genprofile $test:$coreperm
+-cat $profile
++cleancorefile
++genprofile image=$test:$coreperm
+ echo
+ echo "*** A 'Segmentation Fault' message from bash is expected for the following test"
+@@ -61,10 +89,10 @@
+ checkcorefile yes "COREDUMP ($coreperm confinement)"
+ # FAIL TEST, with x confinement
+-genprofile $test:$nocoreperm
+-cat $profile
++cleancorefile
++genprofile image=$test:$nocoreperm
+ echo
+ echo "*** A 'Segmentation Fault' message from bash is expected for the following test"
+ runchecktest "COREDUMP ($nocoreperm confinement)" signal11
+-checkcorefile no "COREDUMP ($nocoreperm confinement)"
++checkcorefile xno "COREDUMP ($nocoreperm confinement)"
+
+=== modified file 'tests/regression/apparmor/mkprofile.pl'
+--- tests/regression/subdomain/mkprofile.pl    2009-11-11 18:44:26 +0000
++++ tests/regression/apparmor/mkprofile.pl     2010-08-03 17:27:13 +0000
+@@ -5,7 +5,7 @@
+ #
+ # Gawd, I hate writing perl. It shows, too.
+ #
+-my $__VERSION__='$Id$';
++my $__VERSION__=$0;
+ use strict;
+ use Getopt::Long;
+
+=== modified file 'tests/regression/apparmor/prologue.inc'
 --- tests/regression/subdomain/prologue.inc    2010-02-07 07:04:57 +0000
-+++ tests/regression/subdomain/prologue.inc    2010-04-27 09:37:30 +0000
-@@ -93,8 +93,10 @@
++++ tests/regression/apparmor/prologue.inc     2010-08-03 17:27:13 +0000
+@@ -1,9 +1,14 @@
+ # vim:syntax=sh
+ #
+-# prologue.inc
+-#
+ # Test infrastructure support.
+ #
++# Copyright 2010 Canonical, Ltd.
++#
++#     This program 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, version 2 of the
++#     License.
++#
+ # This file should be included by each test case
+ # It does a lot of hidden 'magic',  Downside is that
+ # this magic makes debugging fauling tests more difficult.
+@@ -93,8 +98,10 @@
  
        while [ -h ${link} ]
        do
                else 
                        # I'm sure there's a more perlish way to do this
                        target=$( perl -e "printf (\"%s\n\", readlink(\"${link}\"));") 
+@@ -251,6 +258,7 @@
+                       then
+                               echo "Error: ${testname} passed. Test '${_testdesc}' was expected to '${_pfmode}'"
+                               testfailed
++                              return
+                       elif [ "$_pfmode" == "pass" -a -n "${_known}" ]
+                       then
+                               echo "Alert: ${testname} passed. Test '${_testdesc}' was marked as expected pass but known problem (xpass)"
+@@ -260,6 +268,7 @@
+                       then
+                               echo "Error: ${testname} failed. Test '${_testdesc}' was expected to '${_pfmode}'. Reason for failure '${ret}'"
+                               testfailed
++                              return
+                       elif [ "$_pfmode" == "fail" -a -n "${_known}" ]
+                       then
+                               echo "Alert: ${testname} failed. Test '${_testdesc}' was marked as expected fail but known problem (xfail)."
+@@ -272,16 +281,23 @@
+                               then
+                                       echo "Error: ${testname} failed. Test '${_testdesc}' was expected to terminate with signal ${expectedsig}${_known}. Instead it terminated with signal ${killedsig}"
+                                       testfailed
++                                      return
+                               fi
+                               ;;
+                       *)      echo "Error: ${testname} failed. Test '${_testdesc}' was expected to '${_pfmode}'${_known}. Reason for failure 'killed by signal ${killedsig}'"
+                               testfailed
++                              return
+                               ;;      
+                       esac
+                       ;;
+               *)      testerror
++                      return
+                       ;;
+       esac
++
++      if [ -n "$VERBOSE" ]; then
++              echo "ok: ${_testdesc}"
++      fi
+ }
+ runchecktest()
+@@ -399,22 +415,12 @@
+               # it is most often used after --, in fact it is basically
+               # mandatory after --
+               case "$1" in
+-                      profile=*) imagename=`echo $1 | sed 's/^profile=[rix]*//'`
+-                               perm=`echo $1 | sed -n 's/^profile=\([rix]*\).*$/\1/p'`
+-                               if [ -n "$perm" ]
+-                               then
+-                                      imageperm=$perm
+-                               fi
+-                               num_emitted=0
+-                               shift
+-                               ;;
+-
+-                      image=*) imagename=`echo $1 | sed 's/^image=[rix]*//'`
++                      image=*) imagename=`echo $1 | sed 's/^image=\([^:]*\).*$/\1/'`
+                                if [ ! -x "$imagename" ]
+                                then
+                                       fatalerror "invalid imagename specified in input '$1'"
+                                fi
+-                               perm=`echo $1 | sed -n 's/^image=\([rix]*\).*$/\1/p'`
++                               perm=`echo $1 | sed -n 's/^image=[^:]*:\(.*\)$/\1/p'`
+                                if [ -n "$perm" ]
+                                then
+                                       imageperm=$perm
+
+=== modified file 'tests/regression/apparmor/pwrite.sh'
+--- tests/regression/subdomain/pwrite.sh       2007-12-23 00:58:47 +0000
++++ tests/regression/apparmor/pwrite.sh        2010-08-03 17:27:13 +0000
+@@ -27,7 +27,7 @@
+ genprofile $file:$okperm
+-runtestbg "PWRITE with w" pass $file
++runtestbg "PREAD/PWRITE with rw" pass $file
+ sleep 2
+
+=== modified file 'tests/regression/apparmor/swap.sh'
+--- tests/regression/subdomain/swap.sh 2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/swap.sh  2010-08-03 17:27:13 +0000
+@@ -32,7 +32,7 @@
+ swap_file=$tmpdir/swapfile
+ dd if=/dev/zero of=${swap_file} bs=1024 count=512 2> /dev/null
+-/sbin/mkswap ${swap_file} > /dev/null
++/sbin/mkswap -f ${swap_file} > /dev/null
+ # TEST 1.  Make sure can enable and disable swap unconfined
+
+=== modified file 'tests/regression/apparmor/syscall.sh'
+--- tests/regression/subdomain/syscall.sh      2007-12-23 01:02:50 +0000
++++ tests/regression/apparmor/syscall.sh       2010-08-03 17:27:13 +0000
+@@ -1,7 +1,7 @@
+ #! /bin/bash
+-# $Id$
+-
++#
+ #     Copyright (C) 2002-2005 Novell/SUSE
++#     Copyright (C) 2010 Canonical, Ltd.
+ #
+ #     This program is free software; you can redistribute it and/or
+ #     modify it under the terms of the GNU General Public License as
+@@ -114,9 +114,9 @@
+ runchecktest "MKNOD sock (permissions)" fail s $mknod_file
+ ##
+-## D. SETHOSTNAME
++## C. SYSCTL
+ ##
+-sh syscall_sysctl.sh
++bash syscall_sysctl.sh
+ ##
+ ## D. SETHOSTNAME 
+
+=== modified file 'tests/regression/apparmor/syscall_mknod.c'
+--- tests/regression/subdomain/syscall_mknod.c 2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/syscall_mknod.c  2010-08-03 17:27:13 +0000
+@@ -19,7 +19,7 @@
+ int main(int argc, char *argv[])
+ {
+-int fd, mode;
++      int mode;
+       if (argc != 3){
+               fprintf(stderr, "usage: %s b|c|f|s|r file\n",
+@@ -49,8 +49,6 @@
+               return 1;
+       }
+-      close(fd);
+-
+       printf("PASS\n");
+       return 0;
+
+=== modified file 'tests/regression/apparmor/unix_fd_server.c'
+--- tests/regression/subdomain/unix_fd_server.c        2006-05-19 17:32:14 +0000
++++ tests/regression/apparmor/unix_fd_server.c 2010-08-03 17:27:13 +0000
+@@ -2,6 +2,7 @@
+ /*
+  *    Copyright (C) 2002-2005 Novell/SUSE
++ *    Copyright (C) 2010 Canonical, Ltd.
+  *
+  *    This program is free software; you can redistribute it and/or
+  *    modify it under the terms of the GNU General Public License as
+@@ -134,6 +135,7 @@
+       }
+       /* Check for info re: reading the file */
++      memset(inbound_buffer, 0, sizeof(inbound_buffer));
+       if (recv(in_sock, inbound_buffer, 16,0) == -1 ) {
+               fprintf(stderr, "FAIL - recv %s\n",
+                       strerror(errno));
 
 === modified file 'tests/stress/parser/stress.rb'
 --- tests/stress/parser/stress.rb      2008-11-26 22:16:48 +0000
-+++ tests/stress/parser/stress.rb      2010-03-15 18:31:38 +0000
++++ tests/stress/parser/stress.rb      2010-05-31 18:58:40 +0000
 @@ -14,10 +14,27 @@
    return sprintf("%0#{len}x", rand(2 ** (4 * len)))
  end
 
 === modified file 'utils/SubDomain.pm'
 --- utils/SubDomain.pm 2010-03-10 23:30:06 +0000
-+++ utils/SubDomain.pm 2010-03-26 13:51:21 +0000
-@@ -6612,10 +6612,14 @@
++++ utils/SubDomain.pm 2010-08-03 17:27:13 +0000
+@@ -2,6 +2,7 @@
+ #
+ # ----------------------------------------------------------------------
+ #    Copyright (c) 2006 Novell, Inc. All Rights Reserved.
++#    Copyright (c) 2010 Canonical, Ltd.
+ #
+ #    This program is free software; you can redistribute it and/or
+ #    modify it under the terms of version 2 of the GNU General Public
+@@ -2413,10 +2414,13 @@
+ our $seenmark;
+ my $RE_LOG_v2_0_syslog = qr/SubDomain/;
+ my $RE_LOG_v2_1_syslog = qr/kernel:\s+(\[[\d\.\s]+\]\s+)?(audit\([\d\.\:]+\):\s+)?type=150[1-6]/;
++my $RE_LOG_v2_6_syslog = qr/kernel:\s+(\[[\d\.\s]+\]\s+)?type=\d+\s+audit\([\d\.\:]+\):\s+apparmor=/;
+ my $RE_LOG_v2_0_audit  =
+     qr/type=(APPARMOR|UNKNOWN\[1500\]) msg=audit\([\d\.\:]+\):/;
+ my $RE_LOG_v2_1_audit  =
+     qr/type=(UNKNOWN\[150[1-6]\]|APPARMOR_(AUDIT|ALLOWED|DENIED|HINT|STATUS|ERROR))/;
++my $RE_LOG_v2_6_audit =
++    qr/type=AVC\s+audit\([\d\.\:]+\):\s+apparmor=/;
+ sub prefetch_next_log_entry {
+     # if we already have an existing cache entry, something's broken
+@@ -2434,6 +2438,8 @@
+         $RE_LOG_v2_0_audit  |
+         $RE_LOG_v2_1_audit  |
+         $RE_LOG_v2_1_syslog |
++        $RE_LOG_v2_6_syslog |
++        $RE_LOG_v2_6_audit  |
+         $logmark
+     }x);
+ }
+@@ -6612,10 +6618,14 @@
      LibAppArmor::free_record($event);
  
      #map new c and d to w as logprof doesn't support them yet
 
 === modified file 'utils/apparmor_notify'
 --- utils/apparmor_notify      2010-03-10 16:11:26 +0000
-+++ utils/apparmor_notify      2010-05-27 14:08:12 +0000
++++ utils/apparmor_notify      2010-05-31 18:58:40 +0000
 @@ -30,8 +30,7 @@
  require Time::Local;
  require File::Basename;
 
 === modified file 'utils/apparmor_notify.pod'
 --- utils/apparmor_notify.pod  2010-02-12 16:25:02 +0000
-+++ utils/apparmor_notify.pod  2010-05-12 08:46:22 +0000
++++ utils/apparmor_notify.pod  2010-05-31 18:58:40 +0000
 @@ -40,24 +40,37 @@
  
  =over 4
index cb29b371aaab5d699ea9d04de4b530f253920e46..79cd87558657cdac1fcba9584e7fc001e613107f 100644 (file)
@@ -5,7 +5,7 @@ Summary:        AppArmor userlevel parser utility
 Summary(pl.UTF-8):     Narzędzie przestrzeni użytkownika do przetwarzania AppArmor
 Name:          apparmor-parser
 Version:       2.5
-Release:       5
+Release:       6
 Epoch:         1
 License:       GPL
 Group:         Applications/System
This page took 0.714588 seconds and 4 git commands to generate.