[Pcre-svn] [1003] code/trunk: Fix a number of issues in pcre…

Startseite
Nachricht löschen
Autor: Subversion repository
Datum:  
To: pcre-svn
Betreff: [Pcre-svn] [1003] code/trunk: Fix a number of issues in pcregrep.
Revision: 1003
          http://vcs.pcre.org/viewvc?view=rev&revision=1003
Author:   ph10
Date:     2012-08-15 17:45:36 +0100 (Wed, 15 Aug 2012)


Log Message:
-----------
Fix a number of issues in pcregrep.

Modified Paths:
--------------
    code/trunk/ChangeLog
    code/trunk/NEWS
    code/trunk/RunGrepTest
    code/trunk/doc/pcregrep.1
    code/trunk/pcregrep.c
    code/trunk/testdata/grepoutput


Property Changed:
----------------
    code/trunk/



Property changes on: code/trunk
___________________________________________________________________
Name: svn:ignore
- .deps
.libs
CMakeCache.txt
CMakeFiles
DartTestfile.txt
INSTALL
Makefile
Makefile.in
Testing
aclocal.m4
autom4te.cache
cmake_install.cmake
config.guess
config.h
config.h.generic
config.h.in
config.log
config.status
config.sub
configure
depcomp
dftables
install-sh
libpcre.pc
libpcre16.pc
libpcre.so
libpcrecpp.pc
libpcrecpp.so
libpcreposix.pc
libpcreposix.so
libtool
ltmain.sh
m4
missing
pcre.h
pcre.h.generic
pcre_chartables.c
pcre-config
pcre_jit_test
pcre_scanner_unittest
pcre_stringpiece.h
pcre_stringpiece_unittest
pcrecpparg.h
pcrecpp_unittest
pcredemo
pcregrep
pcretest
progress.make
stamp-h1
test3input
test3output
testNinput
testsavedregex
teststderr
teststdout
testtry

+ .deps
.libs
CMakeCache.txt
CMakeFiles
DartTestfile.txt
INSTALL
Makefile
Makefile.in
Testing
aclocal.m4
autom4te.cache
cmake_install.cmake
config.guess
config.h
config.h.generic
config.h.in
config.log
config.status
config.sub
configure
depcomp
dftables
install-sh
libpcre.pc
libpcre16.pc
libpcre.so
libpcrecpp.pc
libpcrecpp.so
libpcreposix.pc
libpcreposix.so
libtool
ltmain.sh
m4
missing
pcre.h
pcre.h.generic
pcre_chartables.c
pcre-config
pcre_jit_test
pcre_scanner_unittest
pcre_stringpiece.h
pcre_stringpiece_unittest
pcrecpparg.h
pcrecpp_unittest
pcredemo
pcregrep
pcretest
progress.make
stamp-h1
test3input
test3output
testNinput
testsavedregex
teststderr
teststdout
testtemp1
testtemp2
testtry


Modified: code/trunk/ChangeLog
===================================================================
--- code/trunk/ChangeLog    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/ChangeLog    2012-08-15 16:45:36 UTC (rev 1003)
@@ -24,6 +24,29 @@


 7.  Improved instruction cache flush for POWER/PowerPC.
     Patch by Daniel Richard G.
+    
+8.  Fixed a number of issues in pcregrep, making it more compatible with GNU
+    grep:
+    
+    (a) There is now no limit to the number of patterns to be matched.
+    
+    (b) An error is given if a pattern is too long.
+    
+    (c) Multiple uses of --exclude, --exclude-dir, --include, and --include-dir
+        are now supported.
+        
+    (d) --exclude-from and --include-from (multiple use) have been added.
+    
+    (e) Exclusions and inclusions now apply to all files and directories, not
+        just to those obtained from scanning a directory recursively.   
+        
+    (f) Multiple uses of -f and --file-list are now supported.
+    
+    (g) In a Windows environment, the default for -d has been changed from 
+        "read" (the GNU grep default) to "skip", because otherwise the presence 
+        of a directory in the file list provokes an error.
+        
+    (h) The documentation has been revised and clarified in places.   



Version 8.31 06-July-2012

Modified: code/trunk/NEWS
===================================================================
--- code/trunk/NEWS    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/NEWS    2012-08-15 16:45:36 UTC (rev 1003)
@@ -1,6 +1,16 @@
 News about PCRE releases
 ------------------------


+Release 8.32 xx-xxxx-2012
+-------------------------
+
+. A number of issues in pcregrep have been fixed, making it more compatible
+ with GNU grep. In particular, --exclude and --include (and variants) apply
+ to all files now, not just those obtained from scanning a directory
+ recursively. In Windows environments, the default action for directories is
+ now "skip" instead of "read" (which provokes an error).
+
+
Release 8.31 06-July-2012
-------------------------


Modified: code/trunk/RunGrepTest
===================================================================
--- code/trunk/RunGrepTest    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/RunGrepTest    2012-08-15 16:45:36 UTC (rev 1003)
@@ -119,7 +119,8 @@
 echo "RC=$?" >>testtry


echo "---------------------------- Test 13 -----------------------------" >>testtry
-(cd $srcdir; $valgrind $pcregrep -f./testdata/greplist ./testdata/grepinputx) >>testtry
+echo seventeen >testtemp1
+(cd $srcdir; $valgrind $pcregrep -f./testdata/greplist -f testtemp1 ./testdata/grepinputx) >>testtry
echo "RC=$?" >>testtry

echo "---------------------------- Test 14 -----------------------------" >>testtry
@@ -207,11 +208,11 @@
echo "RC=$?" >>testtry

echo "---------------------------- Test 35 -----------------------------" >>testtry
-(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinputx --exclude-dir='^\.' 'fox' ./testdata) >>testtry
+(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinputx --include grepinput8 --exclude-dir='^\.' 'fox' ./testdata) >>testtry
echo "RC=$?" >>testtry

echo "---------------------------- Test 36 -----------------------------" >>testtry
-(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinput --exclude 'grepinput$' --exclude_dir='^\.' 'fox' ./testdata | sort) >>testtry
+(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinput --exclude 'grepinput$' --exclude=grepinput8 --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtry
echo "RC=$?" >>testtry

echo "---------------------------- Test 37 -----------------------------" >>testtry
@@ -408,7 +409,8 @@
echo "RC=$?" >>testtry

echo "---------------------------- Test 84 -----------------------------" >>testtry
-(cd $srcdir; $valgrind $pcregrep --file-list ./testdata/grepfilelist "fox|complete") >>testtry 2>&1
+echo testdata/grepinput3 >testtemp1
+(cd $srcdir; $valgrind $pcregrep --file-list ./testdata/grepfilelist --file-list testtemp1 "fox|complete|t7") >>testtry 2>&1
echo "RC=$?" >>testtry

echo "---------------------------- Test 85 -----------------------------" >>testtry
@@ -447,6 +449,36 @@
(cd $srcdir; $valgrind $pcregrep --text "dog" ./testdata/grepbinary) >>testtry 2>&1
echo "RC=$?" >>testtry

+echo "---------------------------- Test 94 -----------------------------" >>testtry
+(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinputx --include grepinput8 'fox' ./testdata/grepinput*) >>testtry
+echo "RC=$?" >>testtry
+
+echo "---------------------------- Test 95 -----------------------------" >>testtry
+(cd $srcdir; $valgrind $pcregrep --file-list ./testdata/grepfilelist --exclude grepinputv "fox|complete") >>testtry 2>&1
+echo "RC=$?" >>testtry
+
+echo "---------------------------- Test 96 -----------------------------" >>testtry
+(cd $srcdir; $valgrind $pcregrep -L -r --include-dir=testdata --exclude '^(?!grepinput)' 'fox' ./test*) >>testtry
+echo "RC=$?" >>testtry
+
+echo "---------------------------- Test 97 -----------------------------" >>testtry
+echo "grepinput$" >testtemp1
+echo "grepinput8" >>testtemp1
+(cd $srcdir; $valgrind $pcregrep -L -r --include=grepinput --exclude-from testtemp1 --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtry
+echo "RC=$?" >>testtry
+
+echo "---------------------------- Test 98 -----------------------------" >>testtry
+echo "grepinput$" >testtemp1
+echo "grepinput8" >>testtemp1
+(cd $srcdir; $valgrind $pcregrep -L -r --exclude=grepinput3 --include=grepinput --exclude-from testtemp1 --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtry
+echo "RC=$?" >>testtry
+
+echo "---------------------------- Test 99 -----------------------------" >>testtry
+echo "grepinput$" >testtemp1
+echo "grepinput8" >testtemp2
+(cd $srcdir; $valgrind $pcregrep -L -r --include grepinput --exclude-from testtemp1 --exclude-from=testtemp2 --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtry
+echo "RC=$?" >>testtry
+
# Now compare the results.

$cf $srcdir/testdata/grepoutput testtry

Modified: code/trunk/doc/pcregrep.1
===================================================================
--- code/trunk/doc/pcregrep.1    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/doc/pcregrep.1    2012-08-15 16:45:36 UTC (rev 1003)
@@ -1,4 +1,4 @@
-.TH PCREGREP 1 "04 March 2012" "PCRE 8.31"
+.TH PCREGREP 1 "15 August 2012" "PCRE 8.32"
 .SH NAME
 pcregrep - a grep with Perl-compatible regular expressions.
 .SH SYNOPSIS
@@ -24,7 +24,7 @@
 If you attempt to use delimiters (for example, by surrounding a pattern with
 slashes, as is common in Perl scripts), they are interpreted as part of the
 pattern. Quotes can of course be used to delimit patterns on the command line
-because they are interpreted by the shell, and indeed they are required if a
+because they are interpreted by the shell, and indeed quotes are required if a
 pattern contains white space or shell metacharacters.
 .P
 The first argument that follows any option settings is treated as the single
@@ -53,25 +53,26 @@
 used (to allow for buffering "before" and "after" lines). An error occurs if a
 line overflows the buffer.
 .P
-Patterns are limited to 8K or BUFSIZ bytes, whichever is the greater. BUFSIZ is
-defined in \fB<stdio.h>\fP. When there is more than one pattern (specified by
-the use of \fB-e\fP and/or \fB-f\fP), each pattern is applied to each line in
-the order in which they are defined, except that all the \fB-e\fP patterns are
-tried before the \fB-f\fP patterns.
+Patterns can be no longer than 8K or BUFSIZ bytes, whichever is the greater.
+BUFSIZ is defined in \fB<stdio.h>\fP. When there is more than one pattern
+(specified by the use of \fB-e\fP and/or \fB-f\fP), each pattern is applied to
+each line in the order in which they are defined, except that all the \fB-e\fP
+patterns are tried before the \fB-f\fP patterns.
 .P
-By default, as soon as one pattern matches (or fails to match when \fB-v\fP is
-used), no further patterns are considered. However, if \fB--colour\fP (or
-\fB--color\fP) is used to colour the matching substrings, or if
-\fB--only-matching\fP, \fB--file-offsets\fP, or \fB--line-offsets\fP is used to
-output only the part of the line that matched (either shown literally, or as an
-offset), scanning resumes immediately following the match, so that further
-matches on the same line can be found. If there are multiple patterns, they are
-all tried on the remainder of the line, but patterns that follow the one that
-matched are not tried on the earlier part of the line.
+By default, as soon as one pattern matches a line, no further patterns are
+considered. However, if \fB--colour\fP (or \fB--color\fP) is used to colour the
+matching substrings, or if \fB--only-matching\fP, \fB--file-offsets\fP, or
+\fB--line-offsets\fP is used to output only the part of the line that matched
+(either shown literally, or as an offset), scanning resumes immediately
+following the match, so that further matches on the same line can be found. If
+there are multiple patterns, they are all tried on the remainder of the line,
+but patterns that follow the one that matched are not tried on the earlier part
+of the line.
 .P
-This is the same behaviour as GNU grep, but it does mean that the order in
-which multiple patterns are specified can affect the output when one of the
-above options is used.
+This behaviour means that the order in which multiple patterns are specified
+can affect the output when one of the above options is used. This is no longer 
+the same behaviour as GNU grep, which now manages to display earlier matches 
+for later patterns (as long as there is no overlap).
 .P
 Patterns that can match an empty string are accepted, but empty string
 matches are never recognized. An example is the pattern "(super)?(man)?", in
@@ -110,8 +111,9 @@
 The order in which some of the options appear can affect the output. For
 example, both the \fB-h\fP and \fB-l\fP options affect the printing of file
 names. Whichever comes later in the command line will be the one that takes
-effect. Numerical values for options may be followed by K or M, to signify
-multiplication by 1024 or 1024*1024 respectively.
+effect. Similarly, except where noted below, if an option is given twice, the
+later setting is used. Numerical values for options may be followed by K or M,
+to signify multiplication by 1024 or 1024*1024 respectively.
 .TP 10
 \fB--\fP
 This terminates the list of options. It is useful if the next item on the
@@ -194,75 +196,98 @@
 .TP
 \fB-d\fP \fIaction\fP, \fB--directories=\fP\fIaction\fP
 If an input path is a directory, "action" specifies how it is to be processed.
-Valid values are "read" (the default), "recurse" (equivalent to the \fB-r\fP
-option), or "skip" (silently skip the path). In the default case, directories
-are read as if they were ordinary files. In some operating systems the effect
-of reading a directory like this is an immediate end-of-file.
+Valid values are "read" (the default in non-Windows environments, for
+compatibility with GNU grep), "recurse" (equivalent to the \fB-r\fP option), or
+"skip" (silently skip the path, the default in Windows environments). In the
+"read" case, directories are read as if they were ordinary files. In some
+operating systems the effect of reading a directory like this is an immediate
+end-of-file; in others it may provoke an error.
 .TP
 \fB-e\fP \fIpattern\fP, \fB--regex=\fP\fIpattern\fP, \fB--regexp=\fP\fIpattern\fP
 Specify a pattern to be matched. This option can be used multiple times in
 order to specify several patterns. It can also be used as a way of specifying a
 single pattern that starts with a hyphen. When \fB-e\fP is used, no argument
 pattern is taken from the command line; all arguments are treated as file
-names. There is an overall maximum of 100 patterns. They are applied to each
-line in the order in which they are defined until one matches (or fails to
-match if \fB-v\fP is used). If \fB-f\fP is used with \fB-e\fP, the command line
-patterns are matched first, followed by the patterns from the file, independent
-of the order in which these options are specified. Note that multiple use of
-\fB-e\fP is not the same as a single pattern with alternatives. For example,
-X|Y finds the first character in a line that is X or Y, whereas if the two
-patterns are given separately, \fBpcregrep\fP finds X if it is present, even if
-it follows Y in the line. It finds Y only if there is no X in the line. This
-really matters only if you are using \fB-o\fP to show the part(s) of the line
-that matched.
+names. There is no limit to the number of patterns. They are applied to each
+line in the order in which they are defined until one matches. 
+.sp
+If \fB-f\fP is used with \fB-e\fP, the command line patterns are matched first,
+followed by the patterns from the file(s), independent of the order in which
+these options are specified. Note that multiple use of \fB-e\fP is not the same
+as a single pattern with alternatives. For example, X|Y finds the first
+character in a line that is X or Y, whereas if the two patterns are given
+separately, with X first, \fBpcregrep\fP finds X if it is present, even if it
+follows Y in the line. It finds Y only if there is no X in the line. This
+matters only if you are using \fB-o\fP or \fB--colo(u)r\fP to show the part(s)
+of the line that matched.
 .TP
 \fB--exclude\fP=\fIpattern\fP
-When \fBpcregrep\fP is searching the files in a directory as a consequence of
-the \fB-r\fP (recursive search) option, any regular files whose names match the
-pattern are excluded. Subdirectories are not excluded by this option; they are
-searched recursively, subject to the \fB--exclude-dir\fP and
-\fB--include_dir\fP options. The pattern is a PCRE regular expression, and is
-matched against the final component of the file name (not the entire path). If
-a file name matches both \fB--include\fP and \fB--exclude\fP, it is excluded.
-There is no short form for this option.
+Files (but not directories) whose names match the pattern are skipped without
+being processed. This applies to all files, whether listed on the command line,
+obtained from \fB--file-list\fP, or by scanning a directory. The pattern is a
+PCRE regular expression, and is matched against the final component of the file
+name, not the entire path. The \fB-F\fP, \fB-w\fP, and \fB-x\fP options do not
+apply to this pattern. The option may be given any number of times in order to 
+specify multiple patterns. If a file name matches both an \fB--include\fP
+and an \fB--exclude\fP pattern, it is excluded. There is no short form for this
+option.
 .TP
+\fB--exclude-from=\fP\fIfilename\fP
+Treat each non-empty line of the file as the data for an \fB--exclude\fP
+option. What constitutes a newline when reading the file is the operating
+system's default. The \fB--newline\fP option has no effect on this option. This
+option may be given more than once in order to specify a number of files to
+read.
+.TP
 \fB--exclude-dir\fP=\fIpattern\fP
-When \fBpcregrep\fP is searching the contents of a directory as a consequence
-of the \fB-r\fP (recursive search) option, any subdirectories whose names match
-the pattern are excluded. (Note that the \fP--exclude\fP option does not affect
-subdirectories.) The pattern is a PCRE regular expression, and is matched
-against the final component of the name (not the entire path). If a
-subdirectory name matches both \fB--include-dir\fP and \fB--exclude-dir\fP, it
-is excluded. There is no short form for this option.
+Directories whose names match the pattern are skipped without being processed,
+whatever the setting of the \fB--recursive\fP option. This applies to all
+directories, whether listed on the command line, obtained from
+\fB--file-list\fP, or by scanning a parent directory. The pattern is a PCRE
+regular expression, and is matched against the final component of the directory
+name, not the entire path. The \fB-F\fP, \fB-w\fP, and \fB-x\fP options do not
+apply to this pattern. The option may be given any number of times in order to
+specify more than one pattern. If a directory matches both \fB--include-dir\fP
+and \fB--exclude-dir\fP, it is excluded. There is no short form for this
+option.
 .TP
 \fB-F\fP, \fB--fixed-strings\fP
-Interpret each pattern as a list of fixed strings, separated by newlines,
-instead of as a regular expression. The \fB-w\fP (match as a word) and \fB-x\fP
-(match whole line) options can be used with \fB-F\fP. They apply to each of the
-fixed strings. A line is selected if any of the fixed strings are found in it
-(subject to \fB-w\fP or \fB-x\fP, if present).
+Interpret each data-matching pattern as a list of fixed strings, separated by
+newlines, instead of as a regular expression. What constitutes a newline for
+this purpose is controlled by the \fB--newline\fP option. The \fB-w\fP (match
+as a word) and \fB-x\fP (match whole line) options can be used with \fB-F\fP.
+They apply to each of the fixed strings. A line is selected if any of the fixed
+strings are found in it (subject to \fB-w\fP or \fB-x\fP, if present). This
+option applies only to the patterns that are matched against the contents of
+files; it does not apply to patterns specified by any of the \fB--include\fP or
+\fB--exclude\fP options.
 .TP
 \fB-f\fP \fIfilename\fP, \fB--file=\fP\fIfilename\fP
-Read a number of patterns from the file, one per line, and match them against
-each line of input. A data line is output if any of the patterns match it. The
-filename can be given as "-" to refer to the standard input. When \fB-f\fP is
-used, patterns specified on the command line using \fB-e\fP may also be
-present; they are tested before the file's patterns. However, no other pattern
-is taken from the command line; all arguments are treated as the names of paths
-to be searched. There is an overall maximum of 100 patterns. Trailing white
-space is removed from each line, and blank lines are ignored. An empty file
-contains no patterns and therefore matches nothing. See also the comments about
-multiple patterns versus a single pattern with alternatives in the description
-of \fB-e\fP above.
+Read patterns from the file, one per line, and match them against
+each line of input. What constitutes a newline when reading the file is the
+operating system's default. The \fB--newline\fP option has no effect on this
+option. Trailing white space is removed from each line, and blank lines are
+ignored. An empty file contains no patterns and therefore matches nothing. See
+also the comments about multiple patterns versus a single pattern with
+alternatives in the description of \fB-e\fP above.
+.sp
+If this option is given more than once, all the specified files are
+read. A data line is output if any of the patterns match it. A filename can
+be given as "-" to refer to the standard input. When \fB-f\fP is used, patterns
+specified on the command line using \fB-e\fP may also be present; they are
+tested before the file's patterns. However, no other pattern is taken from the
+command line; all arguments are treated as the names of paths to be searched.
 .TP
 \fB--file-list\fP=\fIfilename\fP
-Read a list of files to be searched from the given file, one per line. Trailing
-white space is removed from each line, and blank lines are ignored. These files
-are searched before any others that may be listed on the command line. The
-filename can be given as "-" to refer to the standard input. If \fB--file\fP
-and \fB--file-list\fP are both specified as "-", patterns are read first. This
-is useful only when the standard input is a terminal, from which further lines
-(the list of files) can be read after an end-of-file indication.
+Read a list of files and/or directories that are to be scanned from the given
+file, one per line. Trailing white space is removed from each line, and blank
+lines are ignored. These paths are processed before any that are listed on the
+command line. The filename can be given as "-" to refer to the standard input.
+If \fB--file\fP and \fB--file-list\fP are both specified as "-", patterns are
+read first. This is useful only when the standard input is a terminal, from
+which further lines (the list of files) can be read after an end-of-file
+indication. If this option is given more than once, all the specified files are
+read.
 .TP
 \fB--file-offsets\fP
 Instead of showing lines or parts of lines that match, show each match as an
@@ -287,7 +312,8 @@
 .TP
 \fB--help\fP
 Output a help message, giving brief details of the command options and file
-type support, and then exit.
+type support, and then exit. Anything else on the command line is
+ignored.
 .TP
 \fB-I\fP
 Treat binary files as never matching. This is equivalent to
@@ -297,23 +323,33 @@
 Ignore upper/lower case distinctions during comparisons.
 .TP
 \fB--include\fP=\fIpattern\fP
-When \fBpcregrep\fP is searching the files in a directory as a consequence of
-the \fB-r\fP (recursive search) option, only those regular files whose names
-match the pattern are included. Subdirectories are always included and searched
-recursively, subject to the \fP--include-dir\fP and \fB--exclude-dir\fP
-options. The pattern is a PCRE regular expression, and is matched against the
-final component of the file name (not the entire path). If a file name matches
-both \fB--include\fP and \fB--exclude\fP, it is excluded. There is no short
-form for this option.
+If any \fB--include\fP patterns are specified, the only files that are
+processed are those that match one of the patterns (and do not match an
+\fB--exclude\fP pattern). This option does not affect directories, but it
+applies to all files, whether listed on the command line, obtained from
+\fB--file-list\fP, or by scanning a directory. The pattern is a PCRE regular
+expression, and is matched against the final component of the file name, not
+the entire path. The \fB-F\fP, \fB-w\fP, and \fB-x\fP options do not apply to
+this pattern. The option may be given any number of times. If a file name
+matches both an \fB--include\fP and an \fB--exclude\fP pattern, it is excluded.
+There is no short form for this option.
 .TP
+\fB--include-from=\fP\fIfilename\fP
+Treat each non-empty line of the file as the data for an \fB--include\fP
+option. What constitutes a newline for this purpose is the operating system's
+default. The \fB--newline\fP option has no effect on this option. This option 
+may be given any number of times; all the files are read.
+.TP
 \fB--include-dir\fP=\fIpattern\fP
-When \fBpcregrep\fP is searching the contents of a directory as a consequence
-of the \fB-r\fP (recursive search) option, only those subdirectories whose
-names match the pattern are included. (Note that the \fB--include\fP option
-does not affect subdirectories.) The pattern is a PCRE regular expression, and
-is matched against the final component of the name (not the entire path). If a
-subdirectory name matches both \fB--include-dir\fP and \fB--exclude-dir\fP, it
-is excluded. There is no short form for this option.
+If any \fB--include-dir\fP patterns are specified, the only directories that
+are processed are those that match one of the patterns (and do not match an
+\fB--exclude-dir\fP pattern). This applies to all directories, whether listed
+on the command line, obtained from \fB--file-list\fP, or by scanning a parent
+directory. The pattern is a PCRE regular expression, and is matched against the
+final component of the directory name, not the entire path. The \fB-F\fP,
+\fB-w\fP, and \fB-x\fP options do not apply to this pattern. The option may be
+given any number of times. If a directory matches both \fB--include-dir\fP and
+\fB--exclude-dir\fP, it is excluded. There is no short form for this option.
 .TP
 \fB-L\fP, \fB--files-without-match\fP
 Instead of outputting lines from the files, just output the names of the files
@@ -338,7 +374,7 @@
 When this option is given, input is read and processed line by line, and the
 output is flushed after each write. By default, input is read in large chunks,
 unless \fBpcregrep\fP can determine that it is reading from a terminal (which
-is currently possible only in Unix environments). Output to terminal is
+is currently possible only in Unix-like environments). Output to terminal is
 normally automatically flushed by the operating system. This option can be
 useful when the input or output is attached to a pipe and you do not want
 \fBpcregrep\fP to buffer up large amounts of data. However, its use will affect
@@ -415,10 +451,13 @@
 This is normally the standard sequence for the operating system. Unless
 otherwise specified by this option, \fBpcregrep\fP uses the library's default.
 The possible values for this option are CR, LF, CRLF, ANYCRLF, or ANY. This
-makes it possible to use \fBpcregrep\fP on files that have come from other
+makes it possible to use \fBpcregrep\fP to scan files that have come from other
 environments without having to modify their line endings. If the data that is
 being scanned does not agree with the convention set by this option,
-\fBpcregrep\fP may behave in strange ways.
+\fBpcregrep\fP may behave in strange ways. Note that this option does not 
+apply to files specified by the \fB-f\fP, \fB--exclude-from\fP, or 
+\fB--include-from\fP options, which are expected to use the operating system's
+standard newline sequence.
 .TP
 \fB-n\fP, \fB--line-number\fP
 Precede each output line by its line number in the file, followed by a colon
@@ -475,12 +514,14 @@
 .TP
 \fB-u\fP, \fB--utf-8\fP
 Operate in UTF-8 mode. This option is available only if PCRE has been compiled
-with UTF-8 support. Both patterns and subject lines must be valid strings of
-UTF-8 characters.
+with UTF-8 support. All patterns (including those for any \fB--exclude\fP and 
+\fB--include\fP options) and all subject lines that are scanned must be valid
+strings of UTF-8 characters.
 .TP
 \fB-V\fP, \fB--version\fP
-Write the version numbers of \fBpcregrep\fP and the PCRE library that is being
-used to the standard error stream.
+Write the version numbers of \fBpcregrep\fP and the PCRE library to the
+standard output and then exit. Anything else on the command line is
+ignored.
 .TP
 \fB-v\fP, \fB--invert-match\fP
 Invert the sense of the match, so that lines which do \fInot\fP match any of
@@ -488,13 +529,17 @@
 .TP
 \fB-w\fP, \fB--word-regex\fP, \fB--word-regexp\fP
 Force the patterns to match only whole words. This is equivalent to having \eb
-at the start and end of the pattern.
+at the start and end of the pattern. This option applies only to the patterns
+that are matched against the contents of files; it does not apply to patterns
+specified by any of the \fB--include\fP or \fB--exclude\fP options.
 .TP
 \fB-x\fP, \fB--line-regex\fP, \fB--line-regexp\fP
 Force the patterns to be anchored (each must start matching at the beginning of
-a line) and in addition, require them to match entire lines. This is
-equivalent to having ^ and $ characters at the start and end of each
-alternative branch in every pattern.
+a line) and in addition, require them to match entire lines. This is equivalent
+to having ^ and $ characters at the start and end of each alternative branch in
+every pattern. This option applies only to the patterns that are matched
+against the contents of files; it does not apply to patterns specified by any
+of the \fB--include\fP or \fB--exclude\fP options.
 .
 .
 .SH "ENVIRONMENT VARIABLES"
@@ -510,11 +555,15 @@
 .rs
 .sp
 The \fB-N\fP (\fB--newline\fP) option allows \fBpcregrep\fP to scan files with
-different newline conventions from the default. However, the setting of this
-option does not affect the way in which \fBpcregrep\fP writes information to
-the standard error and output streams. It uses the string "\en" in C
-\fBprintf()\fP calls to indicate newlines, relying on the C I/O library to
-convert this to an appropriate sequence if the output is sent to a file.
+different newline conventions from the default. Any parts of the input files
+that are written to the standard output are copied identically, with whatever
+newline sequences they have in the input. However, the setting of this option
+does not affect the interpretation of files specified by the \fB-f\fP,
+\fB--exclude-from\fP, or \fB--include-from\fP options, which are assumed to use
+the operating system's standard newline sequence, nor does it affect the way in
+which \fBpcregrep\fP writes informational messages to the standard error and
+output streams. For these it uses the string "\en" to indicate newlines,
+relying on the C I/O library to convert this to an appropriate sequence.
 .
 .
 .SH "OPTIONS COMPATIBILITY"
@@ -599,7 +648,7 @@
 .SH "SEE ALSO"
 .rs
 .sp
-\fBpcrepattern\fP(3), \fBpcretest\fP(1).
+\fBpcrepattern\fP(3), \fBpcresyntax\fP(3), \fBpcretest\fP(1).
 .
 .
 .SH AUTHOR
@@ -616,6 +665,6 @@
 .rs
 .sp
 .nf
-Last updated: 04 March 2012
+Last updated: 15 August 2012
 Copyright (c) 1997-2012 University of Cambridge.
 .fi


Modified: code/trunk/pcregrep.c
===================================================================
--- code/trunk/pcregrep.c    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/pcregrep.c    2012-08-15 16:45:36 UTC (rev 1003)
@@ -70,15 +70,16 @@


typedef int BOOL;

-#define MAX_PATTERN_COUNT 100
#define OFFSET_SIZE 99

#if BUFSIZ > 8192
-#define PATBUFSIZE BUFSIZ
+#define MAXPATLEN BUFSIZ
#else
-#define PATBUFSIZE 8192
+#define MAXPATLEN 8192
#endif

+#define PATBUFSIZE (MAXPATLEN + 10) /* Allows for prefix+suffix */
+
/* Values for the "filenames" variable, which specifies options for file name
output. The order is important; it is assumed that a file name is wanted for
all values greater than FN_DEFAULT. */
@@ -139,40 +140,31 @@
static char *colour_option = NULL;
static char *dee_option = NULL;
static char *DEE_option = NULL;
+static char *locale = NULL;
static char *main_buffer = NULL;
static char *newline = NULL;
-static char *pattern_filename = NULL;
static char *stdin_name = (char *)"(standard input)";
-static char *locale = NULL;

static const unsigned char *pcretables = NULL;

-static int pattern_count = 0;
-static pcre **pattern_list = NULL;
-static pcre_extra **hints_list = NULL;
-
-static char *file_list = NULL;
-static char *include_pattern = NULL;
-static char *exclude_pattern = NULL;
-static char *include_dir_pattern = NULL;
-static char *exclude_dir_pattern = NULL;
-
-static pcre *include_compiled = NULL;
-static pcre *exclude_compiled = NULL;
-static pcre *include_dir_compiled = NULL;
-static pcre *exclude_dir_compiled = NULL;
-
static int after_context = 0;
static int before_context = 0;
static int binary_files = BIN_BINARY;
static int both_context = 0;
static int bufthird = PCREGREP_BUFSIZE;
static int bufsize = 3*PCREGREP_BUFSIZE;
+
+#if defined HAVE_WINDOWS_H && HAVE_WINDOWS_H
+static int dee_action = dee_SKIP;
+#else
static int dee_action = dee_READ;
+#endif
+
static int DEE_action = DEE_READ;
static int error_count = 0;
static int filenames = FN_DEFAULT;
static int only_matching = -1;
+static int pcre_options = 0;
static int process_options = 0;

#ifdef SUPPORT_PCREGREP_JIT
@@ -199,10 +191,80 @@
static BOOL silent = FALSE;
static BOOL utf8 = FALSE;

+/* Structure for list of file names (for -f and --{in,ex}clude-from) */
+
+typedef struct fnstr {
+  struct fnstr *next;
+  char *name;
+} fnstr;
+
+static fnstr *exclude_from = NULL;
+static fnstr *exclude_from_last = NULL;
+static fnstr *include_from = NULL;
+static fnstr *include_from_last = NULL;
+
+static fnstr *file_lists = NULL;
+static fnstr *file_lists_last = NULL;
+static fnstr *pattern_files = NULL;
+static fnstr *pattern_files_last = NULL;
+
+/* Structure for holding the two variables that describe a file name chain. */
+
+typedef struct fndatastr {
+  fnstr **anchor;
+  fnstr **lastptr;
+} fndatastr;
+
+static fndatastr exclude_from_data = { &exclude_from, &exclude_from_last };
+static fndatastr include_from_data = { &include_from, &include_from_last };
+static fndatastr file_lists_data = { &file_lists, &file_lists_last };
+static fndatastr pattern_files_data = { &pattern_files, &pattern_files_last };
+
+/* Structure for pattern and its compiled form; used for matching patterns and
+also for include/exclude patterns. */
+
+typedef struct patstr {
+  struct patstr *next;
+  char *string;
+  pcre *compiled;
+  pcre_extra *hint;
+} patstr;
+
+static patstr *patterns = NULL;
+static patstr *patterns_last = NULL;
+static patstr *include_patterns = NULL;
+static patstr *include_patterns_last = NULL;
+static patstr *exclude_patterns = NULL;
+static patstr *exclude_patterns_last = NULL;
+static patstr *include_dir_patterns = NULL;
+static patstr *include_dir_patterns_last = NULL;
+static patstr *exclude_dir_patterns = NULL;
+static patstr *exclude_dir_patterns_last = NULL;
+
+/* Structure holding the two variables that describe a pattern chain. A pointer
+to such structures is used for each appropriate option. */
+
+typedef struct patdatastr {
+  patstr **anchor;
+  patstr **lastptr;
+} patdatastr;
+
+static patdatastr match_patdata = { &patterns, &patterns_last };
+static patdatastr include_patdata = { &include_patterns, &include_patterns_last };
+static patdatastr exclude_patdata = { &exclude_patterns, &exclude_patterns_last };
+static patdatastr include_dir_patdata = { &include_dir_patterns, &include_dir_patterns_last };
+static patdatastr exclude_dir_patdata = { &exclude_dir_patterns, &exclude_dir_patterns_last };
+
+static patstr **incexlist[4] = { &include_patterns, &exclude_patterns,
+                                 &include_dir_patterns, &exclude_dir_patterns };
+
+static const char *incexname[4] = { "--include", "--exclude",
+                                    "--include-dir", "--exclude-dir" };
+
 /* Structure for options and list of them */


 enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_LONGNUMBER,
-       OP_OP_NUMBER, OP_PATLIST, OP_BINFILES };
+       OP_OP_NUMBER, OP_PATLIST, OP_FILELIST, OP_BINFILES };


 typedef struct option_item {
   int type;
@@ -233,6 +295,8 @@
 #define N_NOJIT        (-16)
 #define N_FILE_LIST    (-17)
 #define N_BINARY_FILES (-18)
+#define N_EXCLUDE_FROM (-19)
+#define N_INCLUDE_FROM (-20)


 static option_item optionlist[] = {
   { OP_NODATA,     N_NULL,   NULL,              "",              "terminate options" },
@@ -248,10 +312,10 @@
   { OP_NODATA,     'c',      NULL,              "count",         "print only a count of matching lines per FILE" },
   { OP_STRING,     'D',      &DEE_option,       "devices=action","how to handle devices, FIFOs, and sockets" },
   { OP_STRING,     'd',      &dee_option,       "directories=action", "how to handle directories" },
-  { OP_PATLIST,    'e',      NULL,              "regex(p)=pattern", "specify pattern (may be used more than once)" },
+  { OP_PATLIST,    'e',      &match_patdata,    "regex(p)=pattern", "specify pattern (may be used more than once)" },
   { OP_NODATA,     'F',      NULL,              "fixed-strings", "patterns are sets of newline-separated strings" },
-  { OP_STRING,     'f',      &pattern_filename, "file=path",     "read patterns from file" },
-  { OP_STRING,     N_FILE_LIST, &file_list,     "file-list=path","read files to search from file" },
+  { OP_FILELIST,   'f',      &pattern_files_data, "file=path",   "read patterns from file" },
+  { OP_FILELIST,   N_FILE_LIST, &file_lists_data, "file-list=path","read files to search from file" },
   { OP_NODATA,     N_FOFFSETS, NULL,            "file-offsets",  "output file offsets, not text" },
   { OP_NODATA,     'H',      NULL,              "with-filename", "force the prefixing filename on output" },
   { OP_NODATA,     'h',      NULL,              "no-filename",   "suppress the prefixing filename on output" },
@@ -276,18 +340,20 @@
   { OP_OP_NUMBER,  'o',      &only_matching,    "only-matching=n", "show only the part of the line that matched" },
   { OP_NODATA,     'q',      NULL,              "quiet",         "suppress output, just set return code" },
   { OP_NODATA,     'r',      NULL,              "recursive",     "recursively scan sub-directories" },
-  { OP_STRING,     N_EXCLUDE,&exclude_pattern,  "exclude=pattern","exclude matching files when recursing" },
-  { OP_STRING,     N_INCLUDE,&include_pattern,  "include=pattern","include matching files when recursing" },
-  { OP_STRING,     N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude-dir=pattern","exclude matching directories when recursing" },
-  { OP_STRING,     N_INCLUDE_DIR,&include_dir_pattern, "include-dir=pattern","include matching directories when recursing" },
+  { OP_PATLIST,    N_EXCLUDE,&exclude_patdata,  "exclude=pattern","exclude matching files when recursing" },
+  { OP_PATLIST,    N_INCLUDE,&include_patdata,  "include=pattern","include matching files when recursing" },
+  { OP_PATLIST,    N_EXCLUDE_DIR,&exclude_dir_patdata, "exclude-dir=pattern","exclude matching directories when recursing" },
+  { OP_PATLIST,    N_INCLUDE_DIR,&include_dir_patdata, "include-dir=pattern","include matching directories when recursing" },
+  { OP_FILELIST,   N_EXCLUDE_FROM,&exclude_from_data, "exclude-from=path", "read exclude list from file" },
+  { OP_FILELIST,   N_INCLUDE_FROM,&include_from_data, "include-from=path", "read include list from file" },


/* These two were accidentally implemented with underscores instead of
hyphens in the option names. As this was not discovered for several releases,
the incorrect versions are left in the table for compatibility. However, the
--help function misses out any option that has an underscore in its name. */

-  { OP_STRING,    N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude_dir=pattern","exclude matching directories when recursing" },
-  { OP_STRING,    N_INCLUDE_DIR,&include_dir_pattern, "include_dir=pattern","include matching directories when recursing" },
+  { OP_PATLIST,   N_EXCLUDE_DIR,&exclude_dir_patdata, "exclude_dir=pattern","exclude matching directories when recursing" },
+  { OP_PATLIST,   N_INCLUDE_DIR,&include_dir_patdata, "include_dir=pattern","include matching directories when recursing" },


 #ifdef JFRIEDL_DEBUG
   { OP_OP_NUMBER, 'S',      &S_arg,            "jeffS",         "replace matched (sub)string with X" },
@@ -304,7 +370,9 @@
 /* Tables for prefixing and suffixing patterns, according to the -w, -x, and -F
 options. These set the 1, 2, and 4 bits in process_options, respectively. Note
 that the combination of -w and -x has the same effect as -x on its own, so we
-can treat them as the same. */
+can treat them as the same. Note that the MAXPATLEN macro assumes the longest
+prefix+suffix is 10 characters; if anything longer is added, it must be
+adjusted. */


static const char *prefix[] = {
"", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" };
@@ -325,6 +393,94 @@


 /*************************************************
+*          Add item to chain of patterns         *
+*************************************************/
+
+/* Used to add an item onto a chain, or just return an unconnected item if the
+"after" argument is NULL.
+
+Arguments:
+  s          pattern string to add
+  after      if not NULL points to item to insert after
+
+Returns:     new pattern block, or NULL after malloc failure
+*/
+
+static patstr *
+add_pattern(char *s, patstr *after)
+{
+patstr *p = (patstr *)malloc(sizeof(patstr));
+if (p == NULL)
+  {
+  fprintf(stderr, "pcregrep: malloc failed\n");
+  return NULL;
+  }
+if (strlen(s) > MAXPATLEN)
+  {
+  fprintf(stderr, "pcregrep: pattern is too long (limit is %d bytes)\n",
+    MAXPATLEN);
+  return NULL;
+  }
+p->next = NULL;
+p->string = s;
+p->compiled = NULL;
+p->hint = NULL;
+
+if (after != NULL)
+  {
+  p->next = after->next;
+  after->next = p;
+  }
+return p;
+}
+
+
+/*************************************************
+*           Free chain of patterns               *
+*************************************************/
+
+/* Used for several chains of patterns.
+
+Argument: pointer to start of chain
+Returns:  nothing
+*/
+
+static void
+free_pattern_chain(patstr *pc)
+{
+while (pc != NULL)
+  {
+  patstr *p = pc;
+  pc = p->next;
+  if (p->hint != NULL) pcre_free_study(p->hint);
+  if (p->compiled != NULL) pcre_free(p->compiled);
+  free(p);
+  }
+}
+
+
+/*************************************************
+*           Free chain of file names             *
+*************************************************/
+
+/*
+Argument: pointer to start of chain
+Returns:  nothing
+*/
+
+static void
+free_file_chain(fnstr *fn)
+{
+while (fn != NULL)
+  {
+  fnstr *f = fn;
+  fn = f->next;
+  free(f);
+  }
+}
+
+
+/*************************************************
 *         Exit from the program                  *
 *************************************************/


@@ -365,6 +521,7 @@
#include <dirent.h>

typedef DIR directory_type;
+#define FILESEP '/'

 static int
 isdirectory(char *filename)
@@ -372,7 +529,7 @@
 struct stat statbuf;
 if (stat(filename, &statbuf) < 0)
   return 0;        /* In the expectation that opening as a file will fail */
-return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0;
+return (statbuf.st_mode & S_IFMT) == S_IFDIR;
 }


static directory_type *
@@ -459,13 +616,15 @@
WIN32_FIND_DATA data;
} directory_type;

+#DEFINE FILESEP '/'
+
int
isdirectory(char *filename)
{
DWORD attr = GetFileAttributes(filename);
if (attr == INVALID_FILE_ATTRIBUTES)
return 0;
-return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) ? '/' : 0;
+return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

directory_type *
@@ -476,8 +635,8 @@
directory_type *dir;
DWORD err;
len = strlen(filename);
-pattern = (char *) malloc(len + 3);
-dir = (directory_type *) malloc(sizeof(*dir));
+pattern = (char *)malloc(len + 3);
+dir = (directory_type *)malloc(sizeof(*dir));
if ((pattern == NULL) || (dir == NULL))
{
fprintf(stderr, "pcregrep: malloc failed\n");
@@ -563,6 +722,7 @@

#else

+#DEFINE FILESEP 0;
typedef void directory_type;

int isdirectory(char *filename) { return 0; }
@@ -619,6 +779,45 @@


 /*************************************************
+*            Test exclude/includes               *
+*************************************************/
+
+/* If any exclude pattern matches, the path is excluded. Otherwise, unless
+there are no includes, the path must match an include pattern.
+
+Arguments:
+  path      the path to be matched
+  ip        the chain of include patterns
+  ep        the chain of exclude patterns
+
+Returns:    TRUE if the path is not excluded
+*/
+
+static BOOL
+test_incexc(char *path, patstr *ip, patstr *ep)
+{
+int plen = strlen(path);
+
+for (; ep != NULL; ep = ep->next)
+  {
+  if (pcre_exec(ep->compiled, NULL, path, plen, 0, 0, NULL, 0) >= 0)
+    return FALSE;
+  }
+
+if (ip == NULL) return TRUE;
+
+for (; ip != NULL; ip = ip->next)
+  {
+  if (pcre_exec(ip->compiled, NULL, path, plen, 0, 0, NULL, 0) >= 0)
+    return TRUE;
+  }
+
+return FALSE;
+}
+
+
+
+/*************************************************
 *            Read one line of input              *
 *************************************************/


@@ -935,8 +1134,9 @@
 Returns:            nothing
 */


-static void do_after_lines(int lastmatchnumber, char *lastmatchrestart,
-  char *endptr, char *printname)
+static void
+do_after_lines(int lastmatchnumber, char *lastmatchrestart, char *endptr,
+  char *printname)
 {
 if (after_context > 0 && lastmatchnumber > 0)
   {
@@ -983,20 +1183,22 @@
 {
 int i;
 size_t slen = length;
+patstr *p = patterns;
 const char *msg = "this text:\n\n";
+
 if (slen > 200)
   {
   slen = 200;
   msg = "text that starts:\n\n";
   }
-for (i = 0; i < pattern_count; i++)
+for (i = 1; p != NULL; p = p->next, i++)
   {
-  *mrc = pcre_exec(pattern_list[i], hints_list[i], matchptr, (int)length,
+  *mrc = pcre_exec(p->compiled, p->hint, matchptr, (int)length,
     startoffset, PCRE_NOTEMPTY, offsets, OFFSET_SIZE);
   if (*mrc >= 0) return TRUE;
   if (*mrc == PCRE_ERROR_NOMATCH) continue;
   fprintf(stderr, "pcregrep: pcre_exec() gave error %d while matching ", *mrc);
-  if (pattern_count > 1) fprintf(stderr, "pattern number %d to ", i+1);
+  if (patterns->next != NULL) fprintf(stderr, "pattern number %d to ", i);
   fprintf(stderr, "%s", msg);
   FWRITE(matchptr, 1, slen, stderr);   /* In case binary zero included */
   fprintf(stderr, "\n\n");
@@ -1191,7 +1393,7 @@



       for (i = 0; i < jfriedl_XR; i++)
-          match = (pcre_exec(pattern_list[0], hints_list[0], ptr, length, 0,
+          match = (pcre_exec(patterns->compiled, patterns->hint, ptr, length, 0,
               PCRE_NOTEMPTY, offsets, OFFSET_SIZE) >= 0);


       if (gettimeofday(&end_time, &dummy) != 0)
@@ -1594,7 +1796,8 @@
   dir_recurse       TRUE if recursing is wanted (-r or -drecurse)
   only_one_at_top   TRUE if the path is the only one at toplevel


-Returns:   0 if there was at least one match
+Returns:  -1 the file/directory was skipped
+           0 if there was at least one match
            1 if there were no matches
            2 there was some kind of error


@@ -1605,9 +1808,9 @@
 grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top)
 {
 int rc = 1;
-int sep;
 int frtype;
 void *handle;
+char *lastcomp;
 FILE *in = NULL;           /* Ensure initialized */


 #ifdef SUPPORT_LIBZ
@@ -1631,14 +1834,23 @@
       stdin_name : NULL);
   }


-/* If the file is a directory, skip if skipping or if we are recursing, scan
-each file and directory within it, subject to any include or exclude patterns
-that were set. The scanning code is localized so it can be made
-system-specific. */
+/* Inclusion and exclusion: --include-dir and --exclude-dir apply only to
+directories, whereas --include and --exclude apply to everything else. The test
+is against the final component of the path. */

-if ((sep = isdirectory(pathname)) != 0)
+lastcomp = strrchr(pathname, FILESEP);
+lastcomp = (lastcomp == NULL)? pathname : lastcomp + 1;
+
+/* If the file is a directory, skip if not recursing or if explicitly excluded.
+Otherwise, scan the directory and recurse for each path within it. The scanning
+code is localized so it can be made system-specific. */
+
+if (isdirectory(pathname))
   {
-  if (dee_action == dee_SKIP) return 1;
+  if (dee_action == dee_SKIP ||
+      !test_incexc(lastcomp, include_dir_patterns, exclude_dir_patterns))
+    return -1;
+
   if (dee_action == dee_RECURSE)
     {
     char buffer[1024];
@@ -1655,31 +1867,8 @@


     while ((nextfile = readdirectory(dir)) != NULL)
       {
-      int frc, nflen;
-      sprintf(buffer, "%.512s%c%.128s", pathname, sep, nextfile);
-      nflen = (int)(strlen(nextfile));
-
-      if (isdirectory(buffer))
-        {
-        if (exclude_dir_compiled != NULL &&
-            pcre_exec(exclude_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0)
-          continue;
-
-        if (include_dir_compiled != NULL &&
-            pcre_exec(include_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0)
-          continue;
-        }
-      else
-        {
-        if (exclude_compiled != NULL &&
-            pcre_exec(exclude_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0)
-          continue;
-
-        if (include_compiled != NULL &&
-            pcre_exec(include_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0)
-          continue;
-        }
-
+      int frc;
+      sprintf(buffer, "%.512s%c%.128s", pathname, FILESEP, nextfile);
       frc = grep_or_recurse(buffer, dir_recurse, FALSE);
       if (frc > 1) rc = frc;
        else if (frc == 0 && rc == 1) rc = 0;
@@ -1691,9 +1880,11 @@
   }


/* If the file is not a directory and not a regular file, skip it if that's
-been requested. */
+been requested. Otherwise, check for explicit include/exclude. */

-else if (!isregfile(pathname) && DEE_action == DEE_SKIP) return 1;
+else if ((!isregfile(pathname) && DEE_action == DEE_SKIP) ||
+          !test_incexc(lastcomp, include_patterns, exclude_patterns))
+        return -1;


/* Control reaches here if we have a regular file, or if we have a directory
and recursion or skipping was not requested, or if we have anything else and
@@ -1894,8 +2085,7 @@
printf("The default value for --buffer-size is %d.\n", PCREGREP_BUFSIZE);
printf("When reading patterns or file names from a file, trailing white\n");
printf("space is removed and blank lines are ignored.\n");
-printf("There is a maximum of %d patterns, each of maximum size %d bytes.\n",
- MAX_PATTERN_COUNT, PATBUFSIZE);
+printf("The maximum size of any pattern is %d bytes.\n", MAXPATLEN);

printf("\nWith no FILEs, read standard input. If fewer than two FILEs given, assume -h.\n");
printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n");
@@ -1939,7 +2129,7 @@
case 'x': process_options |= PO_LINE_MATCH; break;

case 'V':
- fprintf(stderr, "pcregrep version %s\n", pcre_version());
+ fprintf(stdout, "pcregrep version %s\n", pcre_version());
pcregrep_exit(0);
break;

@@ -1983,13 +2173,20 @@
 *          Compile a single pattern              *
 *************************************************/


-/* When the -F option has been used, this is called for each substring.
-Otherwise it's called for each supplied pattern.
+/* Do nothing if the pattern has already been compiled. This is the case for
+include/exclude patterns read from a file.

+When the -F option has been used, each "pattern" may be a list of strings,
+separated by line breaks. They will be matched literally. We split such a
+string and compile the first substring, inserting an additional block into the
+pattern chain.
+
 Arguments:
-  pattern        the pattern string
+  p              points to the pattern block
   options        the PCRE options
-  filename       the file name, or NULL for a command-line pattern
+  popts          the processing options
+  fromfile       TRUE if the pattern was read from a file
+  fromtext       file name or identifying text (e.g. "include")
   count          0 if this is the only command line pattern, or
                  number of the command line pattern, or
                  linenumber for a pattern from a file
@@ -1998,47 +2195,52 @@
 */


static BOOL
-compile_single_pattern(char *pattern, int options, char *filename, int count)
+compile_pattern(patstr *p, int options, int popts, int fromfile,
+ const char *fromtext, int count)
{
char buffer[PATBUFSIZE];
const char *error;
+char *ps = p->string;
+int patlen = strlen(ps);
int errptr;

-if (pattern_count >= MAX_PATTERN_COUNT)
+if (p->compiled != NULL) return TRUE;
+
+if ((popts & PO_FIXED_STRINGS) != 0)
   {
-  fprintf(stderr, "pcregrep: Too many %spatterns (max %d)\n",
-    (filename == NULL)? "command-line " : "", MAX_PATTERN_COUNT);
-  return FALSE;
-  }
+  int ellength;
+  char *eop = ps + patlen;
+  char *pe = end_of_line(ps, eop, &ellength);


-sprintf(buffer, "%s%.*s%s", prefix[process_options], bufthird, pattern,
-  suffix[process_options]);
-pattern_list[pattern_count] =
-  pcre_compile(buffer, options, &error, &errptr, pcretables);
-if (pattern_list[pattern_count] != NULL)
-  {
-  pattern_count++;
-  return TRUE;
+  if (ellength != 0)
+    {
+    if (add_pattern(pe, p) == NULL) return FALSE;
+    patlen = (int)(pe - ps - ellength);
+    }
   }


+sprintf(buffer, "%s%.*s%s", prefix[popts], patlen, ps, suffix[popts]);
+p->compiled = pcre_compile(buffer, options, &error, &errptr, pcretables);
+if (p->compiled != NULL) return TRUE;
+
/* Handle compile errors */

-errptr -= (int)strlen(prefix[process_options]);
-if (errptr > (int)strlen(pattern)) errptr = (int)strlen(pattern);
+errptr -= (int)strlen(prefix[popts]);
+if (errptr > patlen) errptr = patlen;

-if (filename == NULL)
+if (fromfile)
   {
-  if (count == 0)
-    fprintf(stderr, "pcregrep: Error in command-line regex "
-      "at offset %d: %s\n", errptr, error);
-  else
-    fprintf(stderr, "pcregrep: Error in %s command-line regex "
-      "at offset %d: %s\n", ordin(count), errptr, error);
+  fprintf(stderr, "pcregrep: Error in regex in line %d of %s "
+    "at offset %d: %s\n", count, fromtext, errptr, error);
   }
 else
   {
-  fprintf(stderr, "pcregrep: Error in regex in line %d of %s "
-    "at offset %d: %s\n", count, filename, errptr, error);
+  if (count == 0)
+    fprintf(stderr, "pcregrep: Error in %s regex at offset %d: %s\n",
+      fromtext, errptr, error);
+  else
+    fprintf(stderr, "pcregrep: Error in %s %s regex at offset %d: %s\n",
+      ordin(count), fromtext, errptr, error);
   }


return FALSE;
@@ -2047,43 +2249,79 @@


 /*************************************************
-*           Compile one supplied pattern         *
+*     Read and compile a file of patterns        *
 *************************************************/


-/* When the -F option has been used, each string may be a list of strings,
-separated by line breaks. They will be matched literally.
+/* This is used for --filelist, --include-from, and --exclude-from.

 Arguments:
-  pattern        the pattern string
-  options        the PCRE options
-  filename       the file name, or NULL for a command-line pattern
-  count          0 if this is the only command line pattern, or
-                 number of the command line pattern, or
-                 linenumber for a pattern from a file
+  name         the name of the file; "-" is stdin
+  patptr       pointer to the pattern chain anchor
+  patlastptr   pointer to the last pattern pointer
+  popts        the process options to pass to pattern_compile()


-Returns:         TRUE on success, FALSE after an error
+Returns:       TRUE if all went well
 */


 static BOOL
-compile_pattern(char *pattern, int options, char *filename, int count)
+read_pattern_file(char *name, patstr **patptr, patstr **patlastptr, int popts)
 {
-if ((process_options & PO_FIXED_STRINGS) != 0)
+int linenumber = 0;
+FILE *f;
+char *filename;
+char buffer[PATBUFSIZE];
+
+if (strcmp(name, "-") == 0)
   {
-  char *eop = pattern + strlen(pattern);
-  char buffer[PATBUFSIZE];
+  f = stdin;
+  filename = stdin_name;
+  }
+else
+  {
+  f = fopen(name, "r");
+  if (f == NULL)
+    {
+    fprintf(stderr, "pcregrep: Failed to open %s: %s\n", name, strerror(errno));
+    return FALSE;
+    }
+  filename = name;
+  }
+
+while (fgets(buffer, PATBUFSIZE, f) != NULL)
+  {
+  char *s = buffer + (int)strlen(buffer);
+  while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
+  *s = 0;
+  linenumber++;
+  if (buffer[0] == 0) continue;   /* Skip blank lines */
+
+  /* Note: this call to add_pattern() puts a pointer to the local variable
+  "buffer" into the pattern chain. However, that pointer is used only when
+  compiling the pattern, which happens immediately below, so we flatten it
+  afterwards, as a precaution against any later code trying to use it. */
+
+  *patlastptr = add_pattern(buffer, *patlastptr);
+  if (*patlastptr == NULL) return FALSE;
+  if (*patptr == NULL) *patptr = *patlastptr;
+
+  /* This loop is needed because compiling a "pattern" when -F is set may add
+  on additional literal patterns if the original contains a newline. In the
+  common case, it never will, because fgets() stops at a newline. However,
+  the -N option can be used to give pcregrep a different newline setting. */
+
   for(;;)
     {
-    int ellength;
-    char *p = end_of_line(pattern, eop, &ellength);
-    if (ellength == 0)
-      return compile_single_pattern(pattern, options, filename, count);
-    sprintf(buffer, "%.*s", (int)(p - pattern - ellength), pattern);
-    pattern = p;
-    if (!compile_single_pattern(buffer, options, filename, count))
+    if (!compile_pattern(*patlastptr, pcre_options, popts, TRUE, filename,
+        linenumber))
       return FALSE;
+    (*patlastptr)->string = NULL;            /* Insurance */
+    if ((*patlastptr)->next == NULL) break;
+    *patlastptr = (*patlastptr)->next;
     }
   }
-else return compile_single_pattern(pattern, options, filename, count);
+
+if (f != stdin) fclose(f);
+return TRUE;
 }



@@ -2099,12 +2337,9 @@
{
int i, j;
int rc = 1;
-int pcre_options = 0;
-int cmd_pattern_count = 0;
-int hint_count = 0;
-int errptr;
BOOL only_one_at_top;
-char *patterns[MAX_PATTERN_COUNT];
+patstr *cp;
+fnstr *fn;
const char *locale_from = "--locale";
const char *error;

@@ -2144,7 +2379,7 @@

   if (argv[i][1] == 0)
     {
-    if (pattern_filename != NULL || pattern_count > 0) break;
+    if (pattern_files != NULL || patterns != NULL) break;
       else pcregrep_exit(usage(2));
     }


@@ -2354,18 +2589,37 @@
     option_data = argv[++i];
     }


- /* If the option type is OP_PATLIST, it's the -e option, which can be called
- multiple times to create a list of patterns. */
+ /* If the option type is OP_PATLIST, it's the -e option, or one of the
+ include/exclude options, which can be called multiple times to create lists
+ of patterns. */

   if (op->type == OP_PATLIST)
+     {
+     patdatastr *pd = (patdatastr *)op->dataptr;
+     *(pd->lastptr) = add_pattern(option_data, *(pd->lastptr));
+     if (*(pd->lastptr) == NULL) goto EXIT2;
+     if (*(pd->anchor) == NULL) *(pd->anchor) = *(pd->lastptr);
+     }
+
+  /* If the option type is OP_FILELIST, it's one of the options that names a
+  file. */
+
+  else if (op->type == OP_FILELIST)
     {
-    if (cmd_pattern_count >= MAX_PATTERN_COUNT)
+    fndatastr *fd = (fndatastr *)op->dataptr;
+    fn = (fnstr *)malloc(sizeof(fnstr));
+    if (fn == NULL)
       {
-      fprintf(stderr, "pcregrep: Too many command-line patterns (max %d)\n",
-        MAX_PATTERN_COUNT);
-      return 2;
+      fprintf(stderr, "pcregrep: malloc failed\n");
+      goto EXIT2;
       }
-    patterns[cmd_pattern_count++] = option_data;
+    fn->next = NULL;
+    fn->name = option_data;
+    if (*(fd->anchor) == NULL)
+      *(fd->anchor) = fn;
+    else
+      (*(fd->lastptr))->next = fn;
+    *(fd->lastptr) = fn;
     }


/* Handle OP_BINARY_FILES */
@@ -2580,78 +2834,48 @@
}
#endif

-/* Get memory for the main buffer, and to store the pattern and hints lists. */
+/* Get memory for the main buffer. */

bufsize = 3*bufthird;
main_buffer = (char *)malloc(bufsize);
-pattern_list = (pcre **)malloc(MAX_PATTERN_COUNT * sizeof(pcre *));
-hints_list = (pcre_extra **)malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *));

-if (main_buffer == NULL || pattern_list == NULL || hints_list == NULL)
+if (main_buffer == NULL)
{
fprintf(stderr, "pcregrep: malloc failed\n");
goto EXIT2;
}

-/* If no patterns were provided by -e, and there is no file provided by -f,
+/* If no patterns were provided by -e, and there are no files provided by -f,
the first argument is the one and only pattern, and it must exist. */

-if (cmd_pattern_count == 0 && pattern_filename == NULL)
+if (patterns == NULL && pattern_files == NULL)
{
if (i >= argc) return usage(2);
- patterns[cmd_pattern_count++] = argv[i++];
+ patterns = patterns_last = add_pattern(argv[i++], NULL);
+ if (patterns == NULL) goto EXIT2;
}

/* Compile the patterns that were provided on the command line, either by
-multiple uses of -e or as a single unkeyed pattern. */
+multiple uses of -e or as a single unkeyed pattern. We cannot do this until
+after all the command-line options are read so that we know which PCRE options
+to use. When -F is used, compile_pattern() may add another block into the
+chain, so we must not access the next pointer till after the compile. */

-for (j = 0; j < cmd_pattern_count; j++)
+for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next)
   {
-  if (!compile_pattern(patterns[j], pcre_options, NULL,
-       (j == 0 && cmd_pattern_count == 1)? 0 : j + 1))
+  if (!compile_pattern(cp, pcre_options, process_options, FALSE, "command-line",
+       (j == 1 && patterns->next == NULL)? 0 : j))
     goto EXIT2;
   }


-/* Compile the regular expressions that are provided in a file. */
+/* Read and compile the regular expressions that are provided in files. */

-if (pattern_filename != NULL)
+for (fn = pattern_files; fn != NULL; fn = fn->next)
   {
-  int linenumber = 0;
-  FILE *f;
-  char *filename;
-  char buffer[PATBUFSIZE];
+  if (!read_pattern_file(fn->name, &patterns, &patterns_last, process_options))
+    goto EXIT2;
+  } 


-  if (strcmp(pattern_filename, "-") == 0)
-    {
-    f = stdin;
-    filename = stdin_name;
-    }
-  else
-    {
-    f = fopen(pattern_filename, "r");
-    if (f == NULL)
-      {
-      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename,
-        strerror(errno));
-      goto EXIT2;
-      }
-    filename = pattern_filename;
-    }
-
-  while (fgets(buffer, PATBUFSIZE, f) != NULL)
-    {
-    char *s = buffer + (int)strlen(buffer);
-    while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
-    *s = 0;
-    linenumber++;
-    if (buffer[0] == 0) continue;   /* Skip blank lines */
-    if (!compile_pattern(buffer, pcre_options, filename, linenumber))
-      goto EXIT2;
-    }
-
-  if (f != stdin) fclose(f);
-  }
-
 /* Study the regular expressions, as we will be running them many times. Unless
 JIT has been explicitly disabled, arrange a stack for it to use. */


@@ -2660,20 +2884,19 @@
jit_stack = pcre_jit_stack_alloc(32*1024, 1024*1024);
#endif

-for (j = 0; j < pattern_count; j++)
+for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next)
   {
-  hints_list[j] = pcre_study(pattern_list[j], study_options, &error);
+  cp->hint = pcre_study(cp->compiled, study_options, &error);
   if (error != NULL)
     {
     char s[16];
-    if (pattern_count == 1) s[0] = 0; else sprintf(s, " number %d", j);
+    if (patterns->next == NULL) s[0] = 0; else sprintf(s, " number %d", j);
     fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error);
     goto EXIT2;
     }
-  hint_count++;
 #ifdef SUPPORT_PCREGREP_JIT
-  if (jit_stack != NULL && hints_list[j] != NULL)
-    pcre_assign_jit_stack(hints_list[j], NULL, jit_stack);
+  if (jit_stack != NULL && cp->hint != NULL)
+    pcre_assign_jit_stack(cp->hint, NULL, jit_stack);
 #endif
   }


@@ -2682,12 +2905,12 @@

 if (match_limit > 0 || match_limit_recursion > 0)
   {
-  for (j = 0; j < pattern_count; j++)
+  for (cp = patterns; cp != NULL; cp = cp->next)
     {
-    if (hints_list[j] == NULL)
+    if (cp->hint == NULL)
       {
-      hints_list[j] = malloc(sizeof(pcre_extra));
-      if (hints_list[j] == NULL)
+      cp->hint = (pcre_extra *)malloc(sizeof(pcre_extra));
+      if (cp->hint == NULL)
         {
         fprintf(stderr, "pcregrep: malloc failed\n");
         pcregrep_exit(2);
@@ -2695,81 +2918,69 @@
       }
     if (match_limit > 0)
       {
-      hints_list[j]->flags |= PCRE_EXTRA_MATCH_LIMIT;
-      hints_list[j]->match_limit = match_limit;
+      cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT;
+      cp->hint->match_limit = match_limit;
       }
     if (match_limit_recursion > 0)
       {
-      hints_list[j]->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
-      hints_list[j]->match_limit_recursion = match_limit_recursion;
+      cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+      cp->hint->match_limit_recursion = match_limit_recursion;
       }
     }
   }


-/* If there are include or exclude patterns, compile them. */
+/* If there are include or exclude patterns read from the command line, compile
+them. -F, -w, and -x do not apply, so the third argument of compile_pattern is
+0. */

-if (exclude_pattern != NULL)
+for (j = 0; j < 4; j++)
   {
-  exclude_compiled = pcre_compile(exclude_pattern, 0, &error, &errptr,
-    pcretables);
-  if (exclude_compiled == NULL)
+  int k;
+  for (k = 1, cp = *(incexlist[j]); cp != NULL; k++, cp = cp->next)
     {
-    fprintf(stderr, "pcregrep: Error in 'exclude' regex at offset %d: %s\n",
-      errptr, error);
-    goto EXIT2;
+    if (!compile_pattern(cp, pcre_options, 0, FALSE, incexname[j],
+         (k == 1 && cp->next == NULL)? 0 : k))
+      goto EXIT2;
     }
   }


-if (include_pattern != NULL)
+/* Read and compile include/exclude patterns from files. */
+
+for (fn = include_from; fn != NULL; fn = fn->next)
   {
-  include_compiled = pcre_compile(include_pattern, 0, &error, &errptr,
-    pcretables);
-  if (include_compiled == NULL)
-    {
-    fprintf(stderr, "pcregrep: Error in 'include' regex at offset %d: %s\n",
-      errptr, error);
+  if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last, 0))
     goto EXIT2;
-    }
   }


-if (exclude_dir_pattern != NULL)
+for (fn = exclude_from; fn != NULL; fn = fn->next)
   {
-  exclude_dir_compiled = pcre_compile(exclude_dir_pattern, 0, &error, &errptr,
-    pcretables);
-  if (exclude_dir_compiled == NULL)
-    {
-    fprintf(stderr, "pcregrep: Error in 'exclude_dir' regex at offset %d: %s\n",
-      errptr, error);
+  if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last, 0))
     goto EXIT2;
-    }
   }


-if (include_dir_pattern != NULL)
+/* If there are no files that contain lists of files to search, and there are
+no file arguments, search stdin, and then exit. */
+
+if (file_lists == NULL && i >= argc)
   {
-  include_dir_compiled = pcre_compile(include_dir_pattern, 0, &error, &errptr,
-    pcretables);
-  if (include_dir_compiled == NULL)
-    {
-    fprintf(stderr, "pcregrep: Error in 'include_dir' regex at offset %d: %s\n",
-      errptr, error);
-    goto EXIT2;
-    }
+  rc = pcregrep(stdin, FR_PLAIN, stdin_name,
+    (filenames > FN_DEFAULT)? stdin_name : NULL);
+  goto EXIT;
   }


-/* If a file that contains a list of files to search has been specified, read
-it line by line and search the given files. Otherwise, if there are no further
-arguments, do the business on stdin and exit. */
+/* If any files that contains a list of files to search have been specified,
+read them line by line and search the given files. */

-if (file_list != NULL)
+for (fn = file_lists; fn != NULL; fn = fn->next)
   {
   char buffer[PATBUFSIZE];
   FILE *fl;
-  if (strcmp(file_list, "-") == 0) fl = stdin; else
+  if (strcmp(fn->name, "-") == 0) fl = stdin; else
     {
-    fl = fopen(file_list, "rb");
+    fl = fopen(fn->name, "rb");
     if (fl == NULL)
       {
-      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", file_list,
+      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", fn->name,
         strerror(errno));
       goto EXIT2;
       }
@@ -2787,25 +2998,15 @@
         else if (frc == 0 && rc == 1) rc = 0;
       }
     }
-  if (fl != stdin) fclose (fl);
+  if (fl != stdin) fclose(fl);
   }


-/* Do this only if there was no file list (and no file arguments). */
+/* After handling file-list, work through remaining arguments. Pass in the fact
+that there is only one argument at top level - this suppresses the file name if
+the argument is not a directory and filenames are not otherwise forced. */

-else if (i >= argc)
-  {
-  rc = pcregrep(stdin, FR_PLAIN, stdin_name,
-    (filenames > FN_DEFAULT)? stdin_name : NULL);
-  goto EXIT;
-  }
+only_one_at_top = i == argc - 1 && file_lists == NULL;


-/* After handling file-list or if there are remaining arguments, work through
-them as files or directories. Pass in the fact that there is only one argument
-at top level - this suppresses the file name if the argument is not a directory
-and filenames are not otherwise forced. */
-
-only_one_at_top = i == argc - 1 && file_list == NULL;
-
 for (; i < argc; i++)
   {
   int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE,
@@ -2818,20 +3019,20 @@
 #ifdef SUPPORT_PCREGREP_JIT
 if (jit_stack != NULL) pcre_jit_stack_free(jit_stack);
 #endif
+
 if (main_buffer != NULL) free(main_buffer);
-if (pattern_list != NULL)
-  {
-  for (i = 0; i < pattern_count; i++) free(pattern_list[i]);
-  free(pattern_list);
-  }
-if (hints_list != NULL)
-  {
-  for (i = 0; i < hint_count; i++)
-    {
-    if (hints_list[i] != NULL) pcre_free_study(hints_list[i]);
-    }
-  free(hints_list);
-  }
+
+free_pattern_chain(patterns);
+free_pattern_chain(include_patterns);
+free_pattern_chain(include_dir_patterns);
+free_pattern_chain(exclude_patterns);
+free_pattern_chain(exclude_dir_patterns);
+
+free_file_chain(exclude_from);
+free_file_chain(include_from);
+free_file_chain(pattern_files);
+free_file_chain(file_lists);
+
 pcregrep_exit(rc);


EXIT2:

Modified: code/trunk/testdata/grepoutput
===================================================================
--- code/trunk/testdata/grepoutput    2012-08-14 09:31:00 UTC (rev 1002)
+++ code/trunk/testdata/grepoutput    2012-08-15 16:45:36 UTC (rev 1003)
@@ -93,6 +93,7 @@
 ---------------------------- Test 13 -----------------------------
 Here is the pattern again.
 That time it was on a line by itself.
+seventeen
 This line contains pattern not on a line by itself.
 RC=0
 ---------------------------- Test 14 -----------------------------
@@ -370,11 +371,11 @@
 ---------------------------- Test 34 -----------------------------
 RC=2
 ---------------------------- Test 35 -----------------------------
+./testdata/grepinput8
 ./testdata/grepinputx
 RC=0
 ---------------------------- Test 36 -----------------------------
 ./testdata/grepinput3
-./testdata/grepinput8
 ./testdata/grepinputx
 RC=0
 ---------------------------- Test 37 -----------------------------
@@ -643,6 +644,7 @@
 testdata/grepinputx:complete pair
 testdata/grepinputx:That was a complete pair
 testdata/grepinputx:complete pair
+testdata/grepinput3:triple:    t7_txt    s1_tag    s_txt    p_tag    p_txt    o_tag    o_txt
 RC=0
 ---------------------------- Test 85 -----------------------------
 ./testdata/grepinput3:Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
@@ -668,3 +670,28 @@
 ---------------------------- Test 93 -----------------------------
 The quick brown f?x jumps over the lazy dog.
 RC=0
+---------------------------- Test 94 -----------------------------
+./testdata/grepinput8
+./testdata/grepinputx
+RC=0
+---------------------------- Test 95 -----------------------------
+testdata/grepinputx:complete pair
+testdata/grepinputx:That was a complete pair
+testdata/grepinputx:complete pair
+RC=0
+---------------------------- Test 96 -----------------------------
+./testdata/grepinput3
+./testdata/grepinput8
+./testdata/grepinputx
+RC=0
+---------------------------- Test 97 -----------------------------
+./testdata/grepinput3
+./testdata/grepinputx
+RC=0
+---------------------------- Test 98 -----------------------------
+./testdata/grepinputx
+RC=0
+---------------------------- Test 99 -----------------------------
+./testdata/grepinput3
+./testdata/grepinputx
+RC=0